metamaps/metamaps

View on GitHub

Showing 2,044 of 2,044 total issues

TODO found
Open

    eval("/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\nvar logging = __webpack_require__(460).log;\n\n// Expose public methods.\nmodule.exports = function() {\n  var constraintsToChrome_ = function(c) {\n    if (typeof c !== 'object' || c.mandatory || c.optional) {\n      return c;\n    }\n    var cc = {};\n    Object.keys(c).forEach(function(key) {\n      if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n        return;\n      }\n      var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};\n      if (r.exact !== undefined && typeof r.exact === 'number') {\n        r.min = r.max = r.exact;\n      }\n      var oldname_ = function(prefix, name) {\n        if (prefix) {\n          return prefix + name.charAt(0).toUpperCase() + name.slice(1);\n        }\n        return (name === 'deviceId') ? 'sourceId' : name;\n      };\n      if (r.ideal !== undefined) {\n        cc.optional = cc.optional || [];\n        var oc = {};\n        if (typeof r.ideal === 'number') {\n          oc[oldname_('min', key)] = r.ideal;\n          cc.optional.push(oc);\n          oc = {};\n          oc[oldname_('max', key)] = r.ideal;\n          cc.optional.push(oc);\n        } else {\n          oc[oldname_('', key)] = r.ideal;\n          cc.optional.push(oc);\n        }\n      }\n      if (r.exact !== undefined && typeof r.exact !== 'number') {\n        cc.mandatory = cc.mandatory || {};\n        cc.mandatory[oldname_('', key)] = r.exact;\n      } else {\n        ['min', 'max'].forEach(function(mix) {\n          if (r[mix] !== undefined) {\n            cc.mandatory = cc.mandatory || {};\n            cc.mandatory[oldname_(mix, key)] = r[mix];\n          }\n        });\n      }\n    });\n    if (c.advanced) {\n      cc.optional = (cc.optional || []).concat(c.advanced);\n    }\n    return cc;\n  };\n\n  var shimConstraints_ = function(constraints, func) {\n    constraints = JSON.parse(JSON.stringify(constraints));\n    if (constraints && constraints.audio) {\n      constraints.audio = constraintsToChrome_(constraints.audio);\n    }\n    if (constraints && typeof constraints.video === 'object') {\n      // Shim facingMode for mobile, where it defaults to \"user\".\n      var face = constraints.video.facingMode;\n      face = face && ((typeof face === 'object') ? face : {ideal: face});\n\n      if ((face && (face.exact === 'user' || face.exact === 'environment' ||\n                    face.ideal === 'user' || face.ideal === 'environment')) &&\n          !(navigator.mediaDevices.getSupportedConstraints &&\n            navigator.mediaDevices.getSupportedConstraints().facingMode)) {\n        delete constraints.video.facingMode;\n        if (face.exact === 'environment' || face.ideal === 'environment') {\n          // Look for \"back\" in label, or use last cam (typically back cam).\n          return navigator.mediaDevices.enumerateDevices()\n          .then(function(devices) {\n            devices = devices.filter(function(d) {\n              return d.kind === 'videoinput';\n            });\n            var back = devices.find(function(d) {\n              return d.label.toLowerCase().indexOf('back') !== -1;\n            }) || (devices.length && devices[devices.length - 1]);\n            if (back) {\n              constraints.video.deviceId = face.exact ? {exact: back.deviceId} :\n                                                        {ideal: back.deviceId};\n            }\n            constraints.video = constraintsToChrome_(constraints.video);\n            logging('chrome: ' + JSON.stringify(constraints));\n            return func(constraints);\n          });\n        }\n      }\n      constraints.video = constraintsToChrome_(constraints.video);\n    }\n    logging('chrome: ' + JSON.stringify(constraints));\n    return func(constraints);\n  };\n\n  var shimError_ = function(e) {\n    return {\n      name: {\n        PermissionDeniedError: 'NotAllowedError',\n        ConstraintNotSatisfiedError: 'OverconstrainedError'\n      }[e.name] || e.name,\n      message: e.message,\n      constraint: e.constraintName,\n      toString: function() {\n        return this.name + (this.message && ': ') + this.message;\n      }\n    };\n  };\n\n  var getUserMedia_ = function(constraints, onSuccess, onError) {\n    shimConstraints_(constraints, function(c) {\n      navigator.webkitGetUserMedia(c, onSuccess, function(e) {\n        onError(shimError_(e));\n      });\n    });\n  };\n\n  navigator.getUserMedia = getUserMedia_;\n\n  // Returns the result of getUserMedia as a Promise.\n  var getUserMediaPromise_ = function(constraints) {\n    return new Promise(function(resolve, reject) {\n      navigator.getUserMedia(constraints, resolve, reject);\n    });\n  };\n\n  if (!navigator.mediaDevices) {\n    navigator.mediaDevices = {\n      getUserMedia: getUserMediaPromise_,\n      enumerateDevices: function() {\n        return new Promise(function(resolve) {\n          var kinds = {audio: 'audioinput', video: 'videoinput'};\n          return MediaStreamTrack.getSources(function(devices) {\n            resolve(devices.map(function(device) {\n              return {label: device.label,\n                      kind: kinds[device.kind],\n                      deviceId: device.id,\n                      groupId: ''};\n            }));\n          });\n        });\n      }\n    };\n  }\n\n  // A shim for getUserMedia method on the mediaDevices object.\n  // TODO(KaptenJansson) remove once implemented in Chrome stable.\n  if (!navigator.mediaDevices.getUserMedia) {\n    navigator.mediaDevices.getUserMedia = function(constraints) {\n      return getUserMediaPromise_(constraints);\n    };\n  } else {\n    // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia\n    // function which returns a Promise, it does not accept spec-style\n    // constraints.\n    var origGetUserMedia = navigator.mediaDevices.getUserMedia.\n        bind(navigator.mediaDevices);\n    navigator.mediaDevices.getUserMedia = function(cs) {\n      return shimConstraints_(cs, function(c) {\n        return origGetUserMedia(c).then(function(stream) {\n          if (c.audio && !stream.getAudioTracks().length ||\n              c.video && !stream.getVideoTracks().length) {\n            stream.getTracks().forEach(function(track) {\n              track.stop();\n            });\n            throw new DOMException('', 'NotFoundError');\n          }\n          return stream;\n        }, function(e) {\n          return Promise.reject(shimError_(e));\n        });\n      });\n    };\n  }\n\n  // Dummy devicechange event methods.\n  // TODO(KaptenJansson) remove once implemented in Chrome stable.\n  if (typeof navigator.mediaDevices.addEventListener === 'undefined') {\n    navigator.mediaDevices.addEventListener = function() {\n      logging('Dummy mediaDevices.addEventListener called.');\n    };\n  }\n  if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {\n    navigator.mediaDevices.removeEventListener = function() {\n      logging('Dummy mediaDevices.removeEventListener called.');\n    };\n  }\n};\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDYyLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy93ZWJydGMtYWRhcHRlci9zcmMvanMvY2hyb21lL2dldHVzZXJtZWRpYS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogIENvcHlyaWdodCAoYykgMjAxNiBUaGUgV2ViUlRDIHByb2plY3QgYXV0aG9ycy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiAgVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYSBCU0Qtc3R5bGUgbGljZW5zZVxuICogIHRoYXQgY2FuIGJlIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3Qgb2YgdGhlIHNvdXJjZVxuICogIHRyZWUuXG4gKi9cbiAvKiBlc2xpbnQtZW52IG5vZGUgKi9cbid1c2Ugc3RyaWN0JztcbnZhciBsb2dnaW5nID0gcmVxdWlyZSgnLi4vdXRpbHMuanMnKS5sb2c7XG5cbi8vIEV4cG9zZSBwdWJsaWMgbWV0aG9kcy5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG4gIHZhciBjb25zdHJhaW50c1RvQ2hyb21lXyA9IGZ1bmN0aW9uKGMpIHtcbiAgICBpZiAodHlwZW9mIGMgIT09ICdvYmplY3QnIHx8IGMubWFuZGF0b3J5IHx8IGMub3B0aW9uYWwpIHtcbiAgICAgIHJldHVybiBjO1xuICAgIH1cbiAgICB2YXIgY2MgPSB7fTtcbiAgICBPYmplY3Qua2V5cyhjKS5mb3JFYWNoKGZ1bmN0aW9uKGtleSkge1xuICAgICAgaWYgKGtleSA9PT0gJ3JlcXVpcmUnIHx8IGtleSA9PT0gJ2FkdmFuY2VkJyB8fCBrZXkgPT09ICdtZWRpYVNvdXJjZScpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdmFyIHIgPSAodHlwZW9mIGNba2V5XSA9PT0gJ29iamVjdCcpID8gY1trZXldIDoge2lkZWFsOiBjW2tleV19O1xuICAgICAgaWYgKHIuZXhhY3QgIT09IHVuZGVmaW5lZCAmJiB0eXBlb2Ygci5leGFjdCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgci5taW4gPSByLm1heCA9IHIuZXhhY3Q7XG4gICAgICB9XG4gICAgICB2YXIgb2xkbmFtZV8gPSBmdW5jdGlvbihwcmVmaXgsIG5hbWUpIHtcbiAgICAgICAgaWYgKHByZWZpeCkge1xuICAgICAgICAgIHJldHVybiBwcmVmaXggKyBuYW1lLmNoYXJBdCgwKS50b1VwcGVyQ2FzZSgpICsgbmFtZS5zbGljZSgxKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gKG5hbWUgPT09ICdkZXZpY2VJZCcpID8gJ3NvdXJjZUlkJyA6IG5hbWU7XG4gICAgICB9O1xuICAgICAgaWYgKHIuaWRlYWwgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjYy5vcHRpb25hbCA9IGNjLm9wdGlvbmFsIHx8IFtdO1xuICAgICAgICB2YXIgb2MgPSB7fTtcbiAgICAgICAgaWYgKHR5cGVvZiByLmlkZWFsID09PSAnbnVtYmVyJykge1xuICAgICAgICAgIG9jW29sZG5hbWVfKCdtaW4nLCBrZXkpXSA9IHIuaWRlYWw7XG4gICAgICAgICAgY2Mub3B0aW9uYWwucHVzaChvYyk7XG4gICAgICAgICAgb2MgPSB7fTtcbiAgICAgICAgICBvY1tvbGRuYW1lXygnbWF4Jywga2V5KV0gPSByLmlkZWFsO1xuICAgICAgICAgIGNjLm9wdGlvbmFsLnB1c2gob2MpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG9jW29sZG5hbWVfKCcnLCBrZXkpXSA9IHIuaWRlYWw7XG4gICAgICAgICAgY2Mub3B0aW9uYWwucHVzaChvYyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChyLmV4YWN0ICE9PSB1bmRlZmluZWQgJiYgdHlwZW9mIHIuZXhhY3QgIT09ICdudW1iZXInKSB7XG4gICAgICAgIGNjLm1hbmRhdG9yeSA9IGNjLm1hbmRhdG9yeSB8fCB7fTtcbiAgICAgICAgY2MubWFuZGF0b3J5W29sZG5hbWVfKCcnLCBrZXkpXSA9IHIuZXhhY3Q7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBbJ21pbicsICdtYXgnXS5mb3JFYWNoKGZ1bmN0aW9uKG1peCkge1xuICAgICAgICAgIGlmIChyW21peF0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgY2MubWFuZGF0b3J5ID0gY2MubWFuZGF0b3J5IHx8IHt9O1xuICAgICAgICAgICAgY2MubWFuZGF0b3J5W29sZG5hbWVfKG1peCwga2V5KV0gPSByW21peF07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBpZiAoYy5hZHZhbmNlZCkge1xuICAgICAgY2Mub3B0aW9uYWwgPSAoY2Mub3B0aW9uYWwgfHwgW10pLmNvbmNhdChjLmFkdmFuY2VkKTtcbiAgICB9XG4gICAgcmV0dXJuIGNjO1xuICB9O1xuXG4gIHZhciBzaGltQ29uc3RyYWludHNfID0gZnVuY3Rpb24oY29uc3RyYWludHMsIGZ1bmMpIHtcbiAgICBjb25zdHJhaW50cyA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkoY29uc3RyYWludHMpKTtcbiAgICBpZiAoY29uc3RyYWludHMgJiYgY29uc3RyYWludHMuYXVkaW8pIHtcbiAgICAgIGNvbnN0cmFpbnRzLmF1ZGlvID0gY29uc3RyYWludHNUb0Nocm9tZV8oY29uc3RyYWludHMuYXVkaW8pO1xuICAgIH1cbiAgICBpZiAoY29uc3RyYWludHMgJiYgdHlwZW9mIGNvbnN0cmFpbnRzLnZpZGVvID09PSAnb2JqZWN0Jykge1xuICAgICAgLy8gU2hpbSBmYWNpbmdNb2RlIGZvciBtb2JpbGUsIHdoZXJlIGl0IGRlZmF1bHRzIHRvIFwidXNlclwiLlxuICAgICAgdmFyIGZhY2UgPSBjb25zdHJhaW50cy52aWRlby5mYWNpbmdNb2RlO1xuICAgICAgZmFjZSA9IGZhY2UgJiYgKCh0eXBlb2YgZmFjZSA9PT0gJ29iamVjdCcpID8gZmFjZSA6IHtpZGVhbDogZmFjZX0pO1xuXG4gICAgICBpZiAoKGZhY2UgJiYgKGZhY2UuZXhhY3QgPT09ICd1c2VyJyB8fCBmYWNlLmV4YWN0ID09PSAnZW52aXJvbm1lbnQnIHx8XG4gICAgICAgICAgICAgICAgICAgIGZhY2UuaWRlYWwgPT09ICd1c2VyJyB8fCBmYWNlLmlkZWFsID09PSAnZW52aXJvbm1lbnQnKSkgJiZcbiAgICAgICAgICAhKG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0U3VwcG9ydGVkQ29uc3RyYWludHMgJiZcbiAgICAgICAgICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0U3VwcG9ydGVkQ29uc3RyYWludHMoKS5mYWNpbmdNb2RlKSkge1xuICAgICAgICBkZWxldGUgY29uc3RyYWludHMudmlkZW8uZmFjaW5nTW9kZTtcbiAgICAgICAgaWYgKGZhY2UuZXhhY3QgPT09ICdlbnZpcm9ubWVudCcgfHwgZmFjZS5pZGVhbCA9PT0gJ2Vudmlyb25tZW50Jykge1xuICAgICAgICAgIC8vIExvb2sgZm9yIFwiYmFja1wiIGluIGxhYmVsLCBvciB1c2UgbGFzdCBjYW0gKHR5cGljYWxseSBiYWNrIGNhbSkuXG4gICAgICAgICAgcmV0dXJuIG5hdmlnYXRvci5tZWRpYURldmljZXMuZW51bWVyYXRlRGV2aWNlcygpXG4gICAgICAgICAgLnRoZW4oZnVuY3Rpb24oZGV2aWNlcykge1xuICAgICAgICAgICAgZGV2aWNlcyA9IGRldmljZXMuZmlsdGVyKGZ1bmN0aW9uKGQpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIGQua2luZCA9PT0gJ3ZpZGVvaW5wdXQnO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB2YXIgYmFjayA9IGRldmljZXMuZmluZChmdW5jdGlvbihkKSB7XG4gICAgICAgICAgICAgIHJldHVybiBkLmxhYmVsLnRvTG93ZXJDYXNlKCkuaW5kZXhPZignYmFjaycpICE9PSAtMTtcbiAgICAgICAgICAgIH0pIHx8IChkZXZpY2VzLmxlbmd0aCAmJiBkZXZpY2VzW2RldmljZXMubGVuZ3RoIC0gMV0pO1xuICAgICAgICAgICAgaWYgKGJhY2spIHtcbiAgICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8uZGV2aWNlSWQgPSBmYWNlLmV4YWN0ID8ge2V4YWN0OiBiYWNrLmRldmljZUlkfSA6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtpZGVhbDogYmFjay5kZXZpY2VJZH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdHJhaW50cy52aWRlbyA9IGNvbnN0cmFpbnRzVG9DaHJvbWVfKGNvbnN0cmFpbnRzLnZpZGVvKTtcbiAgICAgICAgICAgIGxvZ2dpbmcoJ2Nocm9tZTogJyArIEpTT04uc3RyaW5naWZ5KGNvbnN0cmFpbnRzKSk7XG4gICAgICAgICAgICByZXR1cm4gZnVuYyhjb25zdHJhaW50cyk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNvbnN0cmFpbnRzLnZpZGVvID0gY29uc3RyYWludHNUb0Nocm9tZV8oY29uc3RyYWludHMudmlkZW8pO1xuICAgIH1cbiAgICBsb2dnaW5nKCdjaHJvbWU6ICcgKyBKU09OLnN0cmluZ2lmeShjb25zdHJhaW50cykpO1xuICAgIHJldHVybiBmdW5jKGNvbnN0cmFpbnRzKTtcbiAgfTtcblxuICB2YXIgc2hpbUVycm9yXyA9IGZ1bmN0aW9uKGUpIHtcbiAgICByZXR1cm4ge1xuICAgICAgbmFtZToge1xuICAgICAgICBQZXJtaXNzaW9uRGVuaWVkRXJyb3I6ICdOb3RBbGxvd2VkRXJyb3InLFxuICAgICAgICBDb25zdHJhaW50Tm90U2F0aXNmaWVkRXJyb3I6ICdPdmVyY29uc3RyYWluZWRFcnJvcidcbiAgICAgIH1bZS5uYW1lXSB8fCBlLm5hbWUsXG4gICAgICBtZXNzYWdlOiBlLm1lc3NhZ2UsXG4gICAgICBjb25zdHJhaW50OiBlLmNvbnN0cmFpbnROYW1lLFxuICAgICAgdG9TdHJpbmc6IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5uYW1lICsgKHRoaXMubWVzc2FnZSAmJiAnOiAnKSArIHRoaXMubWVzc2FnZTtcbiAgICAgIH1cbiAgICB9O1xuICB9O1xuXG4gIHZhciBnZXRVc2VyTWVkaWFfID0gZnVuY3Rpb24oY29uc3RyYWludHMsIG9uU3VjY2Vzcywgb25FcnJvcikge1xuICAgIHNoaW1Db25zdHJhaW50c18oY29uc3RyYWludHMsIGZ1bmN0aW9uKGMpIHtcbiAgICAgIG5hdmlnYXRvci53ZWJraXRHZXRVc2VyTWVkaWEoYywgb25TdWNjZXNzLCBmdW5jdGlvbihlKSB7XG4gICAgICAgIG9uRXJyb3Ioc2hpbUVycm9yXyhlKSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfTtcblxuICBuYXZpZ2F0b3IuZ2V0VXNlck1lZGlhID0gZ2V0VXNlck1lZGlhXztcblxuICAvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2YgZ2V0VXNlck1lZGlhIGFzIGEgUHJvbWlzZS5cbiAgdmFyIGdldFVzZXJNZWRpYVByb21pc2VfID0gZnVuY3Rpb24oY29uc3RyYWludHMpIHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSwgcmVqZWN0KSB7XG4gICAgICBuYXZpZ2F0b3IuZ2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzLCByZXNvbHZlLCByZWplY3QpO1xuICAgIH0pO1xuICB9O1xuXG4gIGlmICghbmF2aWdhdG9yLm1lZGlhRGV2aWNlcykge1xuICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMgPSB7XG4gICAgICBnZXRVc2VyTWVkaWE6IGdldFVzZXJNZWRpYVByb21pc2VfLFxuICAgICAgZW51bWVyYXRlRGV2aWNlczogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlKSB7XG4gICAgICAgICAgdmFyIGtpbmRzID0ge2F1ZGlvOiAnYXVkaW9pbnB1dCcsIHZpZGVvOiAndmlkZW9pbnB1dCd9O1xuICAgICAgICAgIHJldHVybiBNZWRpYVN0cmVhbVRyYWNrLmdldFNvdXJjZXMoZnVuY3Rpb24oZGV2aWNlcykge1xuICAgICAgICAgICAgcmVzb2x2ZShkZXZpY2VzLm1hcChmdW5jdGlvbihkZXZpY2UpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHtsYWJlbDogZGV2aWNlLmxhYmVsLFxuICAgICAgICAgICAgICAgICAgICAgIGtpbmQ6IGtpbmRzW2RldmljZS5raW5kXSxcbiAgICAgICAgICAgICAgICAgICAgICBkZXZpY2VJZDogZGV2aWNlLmlkLFxuICAgICAgICAgICAgICAgICAgICAgIGdyb3VwSWQ6ICcnfTtcbiAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIC8vIEEgc2hpbSBmb3IgZ2V0VXNlck1lZGlhIG1ldGhvZCBvbiB0aGUgbWVkaWFEZXZpY2VzIG9iamVjdC5cbiAgLy8gVE9ETyhLYXB0ZW5KYW5zc29uKSByZW1vdmUgb25jZSBpbXBsZW1lbnRlZCBpbiBDaHJvbWUgc3RhYmxlLlxuICBpZiAoIW5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKSB7XG4gICAgbmF2aWdhdG9yLm1lZGlhRGV2aWNlcy5nZXRVc2VyTWVkaWEgPSBmdW5jdGlvbihjb25zdHJhaW50cykge1xuICAgICAgcmV0dXJuIGdldFVzZXJNZWRpYVByb21pc2VfKGNvbnN0cmFpbnRzKTtcbiAgICB9O1xuICB9IGVsc2Uge1xuICAgIC8vIEV2ZW4gdGhvdWdoIENocm9tZSA0NSBoYXMgbmF2aWdhdG9yLm1lZGlhRGV2aWNlcyBhbmQgYSBnZXRVc2VyTWVkaWFcbiAgICAvLyBmdW5jdGlvbiB3aGljaCByZXR1cm5zIGEgUHJvbWlzZSwgaXQgZG9lcyBub3QgYWNjZXB0IHNwZWMtc3R5bGVcbiAgICAvLyBjb25zdHJhaW50cy5cbiAgICB2YXIgb3JpZ0dldFVzZXJNZWRpYSA9IG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhLlxuICAgICAgICBiaW5kKG5hdmlnYXRvci5tZWRpYURldmljZXMpO1xuICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhID0gZnVuY3Rpb24oY3MpIHtcbiAgICAgIHJldHVybiBzaGltQ29uc3RyYWludHNfKGNzLCBmdW5jdGlvbihjKSB7XG4gICAgICAgIHJldHVybiBvcmlnR2V0VXNlck1lZGlhKGMpLnRoZW4oZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICAgICAgaWYgKGMuYXVkaW8gJiYgIXN0cmVhbS5nZXRBdWRpb1RyYWNrcygpLmxlbmd0aCB8fFxuICAgICAgICAgICAgICBjLnZpZGVvICYmICFzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGgpIHtcbiAgICAgICAgICAgIHN0cmVhbS5nZXRUcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uKHRyYWNrKSB7XG4gICAgICAgICAgICAgIHRyYWNrLnN0b3AoKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhyb3cgbmV3IERPTUV4Y2VwdGlvbignJywgJ05vdEZvdW5kRXJyb3InKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHN0cmVhbTtcbiAgICAgICAgfSwgZnVuY3Rpb24oZSkge1xuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChzaGltRXJyb3JfKGUpKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9O1xuICB9XG5cbiAgLy8gRHVtbXkgZGV2aWNlY2hhbmdlIGV2ZW50IG1ldGhvZHMuXG4gIC8vIFRPRE8oS2FwdGVuSmFuc3NvbikgcmVtb3ZlIG9uY2UgaW1wbGVtZW50ZWQgaW4gQ2hyb21lIHN0YWJsZS5cbiAgaWYgKHR5cGVvZiBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmFkZEV2ZW50TGlzdGVuZXIgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgbmF2aWdhdG9yLm1lZGlhRGV2aWNlcy5hZGRFdmVudExpc3RlbmVyID0gZnVuY3Rpb24oKSB7XG4gICAgICBsb2dnaW5nKCdEdW1teSBtZWRpYURldmljZXMuYWRkRXZlbnRMaXN0ZW5lciBjYWxsZWQuJyk7XG4gICAgfTtcbiAgfVxuICBpZiAodHlwZW9mIG5hdmlnYXRvci5tZWRpYURldmljZXMucmVtb3ZlRXZlbnRMaXN0ZW5lciA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLnJlbW92ZUV2ZW50TGlzdGVuZXIgPSBmdW5jdGlvbigpIHtcbiAgICAgIGxvZ2dpbmcoJ0R1bW15IG1lZGlhRGV2aWNlcy5yZW1vdmVFdmVudExpc3RlbmVyIGNhbGxlZC4nKTtcbiAgICB9O1xuICB9XG59O1xuXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9+L3dlYnJ0Yy1hZGFwdGVyL3NyYy9qcy9jaHJvbWUvZ2V0dXNlcm1lZGlhLmpzXG4vLyBtb2R1bGUgaWQgPSA0NjJcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9");

TODO found
Open

    // TODO: make these dynamic values so that the ContextMenu can

TODO found
Open

    eval("/**\n * Copyright 2013-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n *\n */\n\n/* global hasOwnProperty:true */\n\n'use strict';\n\nvar _prodInvariant = __webpack_require__(208),\n    _assign = __webpack_require__(176);\n\nvar AutoFocusUtils = __webpack_require__(266);\nvar CSSPropertyOperations = __webpack_require__(268);\nvar DOMLazyTree = __webpack_require__(254);\nvar DOMNamespaces = __webpack_require__(255);\nvar DOMProperty = __webpack_require__(209);\nvar DOMPropertyOperations = __webpack_require__(276);\nvar EventPluginHub = __webpack_require__(215);\nvar EventPluginRegistry = __webpack_require__(216);\nvar ReactBrowserEventEmitter = __webpack_require__(278);\nvar ReactDOMComponentFlags = __webpack_require__(210);\nvar ReactDOMComponentTree = __webpack_require__(207);\nvar ReactDOMInput = __webpack_require__(281);\nvar ReactDOMOption = __webpack_require__(284);\nvar ReactDOMSelect = __webpack_require__(285);\nvar ReactDOMTextarea = __webpack_require__(286);\nvar ReactInstrumentation = __webpack_require__(235);\nvar ReactMultiChild = __webpack_require__(287);\nvar ReactServerRenderingTransaction = __webpack_require__(306);\n\nvar emptyFunction = __webpack_require__(184);\nvar escapeTextContentForBrowser = __webpack_require__(259);\nvar invariant = __webpack_require__(180);\nvar isEventSupported = __webpack_require__(243);\nvar shallowEqual = __webpack_require__(296);\nvar validateDOMNesting = __webpack_require__(309);\nvar warning = __webpack_require__(183);\n\nvar Flags = ReactDOMComponentFlags;\nvar deleteListener = EventPluginHub.deleteListener;\nvar getNode = ReactDOMComponentTree.getNodeFromInstance;\nvar listenTo = ReactBrowserEventEmitter.listenTo;\nvar registrationNameModules = EventPluginRegistry.registrationNameModules;\n\n// For quickly matching children type, to test if can be treated as content.\nvar CONTENT_TYPES = { 'string': true, 'number': true };\n\nvar STYLE = 'style';\nvar HTML = '__html';\nvar RESERVED_PROPS = {\n  children: null,\n  dangerouslySetInnerHTML: null,\n  suppressContentEditableWarning: null\n};\n\n// Node type for document fragments (Node.DOCUMENT_FRAGMENT_NODE).\nvar DOC_FRAGMENT_TYPE = 11;\n\nfunction getDeclarationErrorAddendum(internalInstance) {\n  if (internalInstance) {\n    var owner = internalInstance._currentElement._owner || null;\n    if (owner) {\n      var name = owner.getName();\n      if (name) {\n        return ' This DOM node was rendered by `' + name + '`.';\n      }\n    }\n  }\n  return '';\n}\n\nfunction friendlyStringify(obj) {\n  if (typeof obj === 'object') {\n    if (Array.isArray(obj)) {\n      return '[' + obj.map(friendlyStringify).join(', ') + ']';\n    } else {\n      var pairs = [];\n      for (var key in obj) {\n        if (Object.prototype.hasOwnProperty.call(obj, key)) {\n          var keyEscaped = /^[a-z$_][\\w$_]*$/i.test(key) ? key : JSON.stringify(key);\n          pairs.push(keyEscaped + ': ' + friendlyStringify(obj[key]));\n        }\n      }\n      return '{' + pairs.join(', ') + '}';\n    }\n  } else if (typeof obj === 'string') {\n    return JSON.stringify(obj);\n  } else if (typeof obj === 'function') {\n    return '[function object]';\n  }\n  // Differs from JSON.stringify in that undefined because undefined and that\n  // inf and nan don't become null\n  return String(obj);\n}\n\nvar styleMutationWarning = {};\n\nfunction checkAndWarnForMutatedStyle(style1, style2, component) {\n  if (style1 == null || style2 == null) {\n    return;\n  }\n  if (shallowEqual(style1, style2)) {\n    return;\n  }\n\n  var componentName = component._tag;\n  var owner = component._currentElement._owner;\n  var ownerName;\n  if (owner) {\n    ownerName = owner.getName();\n  }\n\n  var hash = ownerName + '|' + componentName;\n\n  if (styleMutationWarning.hasOwnProperty(hash)) {\n    return;\n  }\n\n  styleMutationWarning[hash] = true;\n\n   true ? warning(false, '`%s` was passed a style object that has previously been mutated. ' + 'Mutating `style` is deprecated. Consider cloning it beforehand. Check ' + 'the `render` %s. Previous style: %s. Mutated style: %s.', componentName, owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>', friendlyStringify(style1), friendlyStringify(style2)) : void 0;\n}\n\n/**\n * @param {object} component\n * @param {?object} props\n */\nfunction assertValidProps(component, props) {\n  if (!props) {\n    return;\n  }\n  // Note the use of `==` which checks for null or undefined.\n  if (voidElementTags[component._tag]) {\n    !(props.children == null && props.dangerouslySetInnerHTML == null) ?  true ? invariant(false, '%s is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.%s', component._tag, component._currentElement._owner ? ' Check the render method of ' + component._currentElement._owner.getName() + '.' : '') : _prodInvariant('137', component._tag, component._currentElement._owner ? ' Check the render method of ' + component._currentElement._owner.getName() + '.' : '') : void 0;\n  }\n  if (props.dangerouslySetInnerHTML != null) {\n    !(props.children == null) ?  true ? invariant(false, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.') : _prodInvariant('60') : void 0;\n    !(typeof props.dangerouslySetInnerHTML === 'object' && HTML in props.dangerouslySetInnerHTML) ?  true ? invariant(false, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit https://fb.me/react-invariant-dangerously-set-inner-html for more information.') : _prodInvariant('61') : void 0;\n  }\n  if (true) {\n     true ? warning(props.innerHTML == null, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.') : void 0;\n     true ? warning(props.suppressContentEditableWarning || !props.contentEditable || props.children == null, 'A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.') : void 0;\n     true ? warning(props.onFocusIn == null && props.onFocusOut == null, 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' + 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' + 'are not needed/supported by React.') : void 0;\n  }\n  !(props.style == null || typeof props.style === 'object') ?  true ? invariant(false, 'The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + \\'em\\'}} when using JSX.%s', getDeclarationErrorAddendum(component)) : _prodInvariant('62', getDeclarationErrorAddendum(component)) : void 0;\n}\n\nfunction enqueuePutListener(inst, registrationName, listener, transaction) {\n  if (transaction instanceof ReactServerRenderingTransaction) {\n    return;\n  }\n  if (true) {\n    // IE8 has no API for event capturing and the `onScroll` event doesn't\n    // bubble.\n     true ? warning(registrationName !== 'onScroll' || isEventSupported('scroll', true), 'This browser doesn\\'t support the `onScroll` event') : void 0;\n  }\n  var containerInfo = inst._hostContainerInfo;\n  var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;\n  var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;\n  listenTo(registrationName, doc);\n  transaction.getReactMountReady().enqueue(putListener, {\n    inst: inst,\n    registrationName: registrationName,\n    listener: listener\n  });\n}\n\nfunction putListener() {\n  var listenerToPut = this;\n  EventPluginHub.putListener(listenerToPut.inst, listenerToPut.registrationName, listenerToPut.listener);\n}\n\nfunction inputPostMount() {\n  var inst = this;\n  ReactDOMInput.postMountWrapper(inst);\n}\n\nfunction textareaPostMount() {\n  var inst = this;\n  ReactDOMTextarea.postMountWrapper(inst);\n}\n\nfunction optionPostMount() {\n  var inst = this;\n  ReactDOMOption.postMountWrapper(inst);\n}\n\nvar setAndValidateContentChildDev = emptyFunction;\nif (true) {\n  setAndValidateContentChildDev = function (content) {\n    var hasExistingContent = this._contentDebugID != null;\n    var debugID = this._debugID;\n    // This ID represents the inlined child that has no backing instance:\n    var contentDebugID = -debugID;\n\n    if (content == null) {\n      if (hasExistingContent) {\n        ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);\n      }\n      this._contentDebugID = null;\n      return;\n    }\n\n    validateDOMNesting(null, String(content), this, this._ancestorInfo);\n    this._contentDebugID = contentDebugID;\n    if (hasExistingContent) {\n      ReactInstrumentation.debugTool.onBeforeUpdateComponent(contentDebugID, content);\n      ReactInstrumentation.debugTool.onUpdateComponent(contentDebugID);\n    } else {\n      ReactInstrumentation.debugTool.onBeforeMountComponent(contentDebugID, content, debugID);\n      ReactInstrumentation.debugTool.onMountComponent(contentDebugID);\n      ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]);\n    }\n  };\n}\n\n// There are so many media events, it makes sense to just\n// maintain a list rather than create a `trapBubbledEvent` for each\nvar mediaEvents = {\n  topAbort: 'abort',\n  topCanPlay: 'canplay',\n  topCanPlayThrough: 'canplaythrough',\n  topDurationChange: 'durationchange',\n  topEmptied: 'emptied',\n  topEncrypted: 'encrypted',\n  topEnded: 'ended',\n  topError: 'error',\n  topLoadedData: 'loadeddata',\n  topLoadedMetadata: 'loadedmetadata',\n  topLoadStart: 'loadstart',\n  topPause: 'pause',\n  topPlay: 'play',\n  topPlaying: 'playing',\n  topProgress: 'progress',\n  topRateChange: 'ratechange',\n  topSeeked: 'seeked',\n  topSeeking: 'seeking',\n  topStalled: 'stalled',\n  topSuspend: 'suspend',\n  topTimeUpdate: 'timeupdate',\n  topVolumeChange: 'volumechange',\n  topWaiting: 'waiting'\n};\n\nfunction trapBubbledEventsLocal() {\n  var inst = this;\n  // If a component renders to null or if another component fatals and causes\n  // the state of the tree to be corrupted, `node` here can be null.\n  !inst._rootNodeID ?  true ? invariant(false, 'Must be mounted to trap events') : _prodInvariant('63') : void 0;\n  var node = getNode(inst);\n  !node ?  true ? invariant(false, 'trapBubbledEvent(...): Requires node to be rendered.') : _prodInvariant('64') : void 0;\n\n  switch (inst._tag) {\n    case 'iframe':\n    case 'object':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topLoad', 'load', node)];\n      break;\n    case 'video':\n    case 'audio':\n\n      inst._wrapperState.listeners = [];\n      // Create listener for each media event\n      for (var event in mediaEvents) {\n        if (mediaEvents.hasOwnProperty(event)) {\n          inst._wrapperState.listeners.push(ReactBrowserEventEmitter.trapBubbledEvent(event, mediaEvents[event], node));\n        }\n      }\n      break;\n    case 'source':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topError', 'error', node)];\n      break;\n    case 'img':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topError', 'error', node), ReactBrowserEventEmitter.trapBubbledEvent('topLoad', 'load', node)];\n      break;\n    case 'form':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topReset', 'reset', node), ReactBrowserEventEmitter.trapBubbledEvent('topSubmit', 'submit', node)];\n      break;\n    case 'input':\n    case 'select':\n    case 'textarea':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topInvalid', 'invalid', node)];\n      break;\n  }\n}\n\nfunction postUpdateSelectWrapper() {\n  ReactDOMSelect.postUpdateWrapper(this);\n}\n\n// For HTML, certain tags should omit their close tag. We keep a whitelist for\n// those special-case tags.\n\nvar omittedCloseTags = {\n  'area': true,\n  'base': true,\n  'br': true,\n  'col': true,\n  'embed': true,\n  'hr': true,\n  'img': true,\n  'input': true,\n  'keygen': true,\n  'link': true,\n  'meta': true,\n  'param': true,\n  'source': true,\n  'track': true,\n  'wbr': true\n};\n\nvar newlineEatingTags = {\n  'listing': true,\n  'pre': true,\n  'textarea': true\n};\n\n// For HTML, certain tags cannot have children. This has the same purpose as\n// `omittedCloseTags` except that `menuitem` should still have its closing tag.\n\nvar voidElementTags = _assign({\n  'menuitem': true\n}, omittedCloseTags);\n\n// We accept any tag to be rendered but since this gets injected into arbitrary\n// HTML, we want to make sure that it's a safe tag.\n// http://www.w3.org/TR/REC-xml/#NT-Name\n\nvar VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\\.\\-\\d]*$/; // Simplified subset\nvar validatedTagCache = {};\nvar hasOwnProperty = {}.hasOwnProperty;\n\nfunction validateDangerousTag(tag) {\n  if (!hasOwnProperty.call(validatedTagCache, tag)) {\n    !VALID_TAG_REGEX.test(tag) ?  true ? invariant(false, 'Invalid tag: %s', tag) : _prodInvariant('65', tag) : void 0;\n    validatedTagCache[tag] = true;\n  }\n}\n\nfunction isCustomComponent(tagName, props) {\n  return tagName.indexOf('-') >= 0 || props.is != null;\n}\n\nvar globalIdCounter = 1;\n\n/**\n * Creates a new React class that is idempotent and capable of containing other\n * React components. It accepts event listeners and DOM properties that are\n * valid according to `DOMProperty`.\n *\n *  - Event listeners: `onClick`, `onMouseDown`, etc.\n *  - DOM properties: `className`, `name`, `title`, etc.\n *\n * The `style` property functions differently from the DOM API. It accepts an\n * object mapping of style properties to values.\n *\n * @constructor ReactDOMComponent\n * @extends ReactMultiChild\n */\nfunction ReactDOMComponent(element) {\n  var tag = element.type;\n  validateDangerousTag(tag);\n  this._currentElement = element;\n  this._tag = tag.toLowerCase();\n  this._namespaceURI = null;\n  this._renderedChildren = null;\n  this._previousStyle = null;\n  this._previousStyleCopy = null;\n  this._hostNode = null;\n  this._hostParent = null;\n  this._rootNodeID = 0;\n  this._domID = 0;\n  this._hostContainerInfo = null;\n  this._wrapperState = null;\n  this._topLevelWrapper = null;\n  this._flags = 0;\n  if (true) {\n    this._ancestorInfo = null;\n    setAndValidateContentChildDev.call(this, null);\n  }\n}\n\nReactDOMComponent.displayName = 'ReactDOMComponent';\n\nReactDOMComponent.Mixin = {\n\n  /**\n   * Generates root tag markup then recurses. This method has side effects and\n   * is not idempotent.\n   *\n   * @internal\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {?ReactDOMComponent} the parent component instance\n   * @param {?object} info about the host container\n   * @param {object} context\n   * @return {string} The computed markup.\n   */\n  mountComponent: function (transaction, hostParent, hostContainerInfo, context) {\n    this._rootNodeID = globalIdCounter++;\n    this._domID = hostContainerInfo._idCounter++;\n    this._hostParent = hostParent;\n    this._hostContainerInfo = hostContainerInfo;\n\n    var props = this._currentElement.props;\n\n    switch (this._tag) {\n      case 'audio':\n      case 'form':\n      case 'iframe':\n      case 'img':\n      case 'link':\n      case 'object':\n      case 'source':\n      case 'video':\n        this._wrapperState = {\n          listeners: null\n        };\n        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);\n        break;\n      case 'input':\n        ReactDOMInput.mountWrapper(this, props, hostParent);\n        props = ReactDOMInput.getHostProps(this, props);\n        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);\n        break;\n      case 'option':\n        ReactDOMOption.mountWrapper(this, props, hostParent);\n        props = ReactDOMOption.getHostProps(this, props);\n        break;\n      case 'select':\n        ReactDOMSelect.mountWrapper(this, props, hostParent);\n        props = ReactDOMSelect.getHostProps(this, props);\n        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);\n        break;\n      case 'textarea':\n        ReactDOMTextarea.mountWrapper(this, props, hostParent);\n        props = ReactDOMTextarea.getHostProps(this, props);\n        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);\n        break;\n    }\n\n    assertValidProps(this, props);\n\n    // We create tags in the namespace of their parent container, except HTML\n    // tags get no namespace.\n    var namespaceURI;\n    var parentTag;\n    if (hostParent != null) {\n      namespaceURI = hostParent._namespaceURI;\n      parentTag = hostParent._tag;\n    } else if (hostContainerInfo._tag) {\n      namespaceURI = hostContainerInfo._namespaceURI;\n      parentTag = hostContainerInfo._tag;\n    }\n    if (namespaceURI == null || namespaceURI === DOMNamespaces.svg && parentTag === 'foreignobject') {\n      namespaceURI = DOMNamespaces.html;\n    }\n    if (namespaceURI === DOMNamespaces.html) {\n      if (this._tag === 'svg') {\n        namespaceURI = DOMNamespaces.svg;\n      } else if (this._tag === 'math') {\n        namespaceURI = DOMNamespaces.mathml;\n      }\n    }\n    this._namespaceURI = namespaceURI;\n\n    if (true) {\n      var parentInfo;\n      if (hostParent != null) {\n        parentInfo = hostParent._ancestorInfo;\n      } else if (hostContainerInfo._tag) {\n        parentInfo = hostContainerInfo._ancestorInfo;\n      }\n      if (parentInfo) {\n        // parentInfo should always be present except for the top-level\n        // component when server rendering\n        validateDOMNesting(this._tag, null, this, parentInfo);\n      }\n      this._ancestorInfo = validateDOMNesting.updatedAncestorInfo(parentInfo, this._tag, this);\n    }\n\n    var mountImage;\n    if (transaction.useCreateElement) {\n      var ownerDocument = hostContainerInfo._ownerDocument;\n      var el;\n      if (namespaceURI === DOMNamespaces.html) {\n        if (this._tag === 'script') {\n          // Create the script via .innerHTML so its \"parser-inserted\" flag is\n          // set to true and it does not execute\n          var div = ownerDocument.createElement('div');\n          var type = this._currentElement.type;\n          div.innerHTML = '<' + type + '></' + type + '>';\n          el = div.removeChild(div.firstChild);\n        } else if (props.is) {\n          el = ownerDocument.createElement(this._currentElement.type, props.is);\n        } else {\n          // Separate else branch instead of using `props.is || undefined` above becuase of a Firefox bug.\n          // See discussion in https://github.com/facebook/react/pull/6896\n          // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240\n          el = ownerDocument.createElement(this._currentElement.type);\n        }\n      } else {\n        el = ownerDocument.createElementNS(namespaceURI, this._currentElement.type);\n      }\n      ReactDOMComponentTree.precacheNode(this, el);\n      this._flags |= Flags.hasCachedChildNodes;\n      if (!this._hostParent) {\n        DOMPropertyOperations.setAttributeForRoot(el);\n      }\n      this._updateDOMProperties(null, props, transaction);\n      var lazyTree = DOMLazyTree(el);\n      this._createInitialChildren(transaction, props, context, lazyTree);\n      mountImage = lazyTree;\n    } else {\n      var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);\n      var tagContent = this._createContentMarkup(transaction, props, context);\n      if (!tagContent && omittedCloseTags[this._tag]) {\n        mountImage = tagOpen + '/>';\n      } else {\n        mountImage = tagOpen + '>' + tagContent + '</' + this._currentElement.type + '>';\n      }\n    }\n\n    switch (this._tag) {\n      case 'input':\n        transaction.getReactMountReady().enqueue(inputPostMount, this);\n        if (props.autoFocus) {\n          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);\n        }\n        break;\n      case 'textarea':\n        transaction.getReactMountReady().enqueue(textareaPostMount, this);\n        if (props.autoFocus) {\n          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);\n        }\n        break;\n      case 'select':\n        if (props.autoFocus) {\n          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);\n        }\n        break;\n      case 'button':\n        if (props.autoFocus) {\n          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);\n        }\n        break;\n      case 'option':\n        transaction.getReactMountReady().enqueue(optionPostMount, this);\n        break;\n    }\n\n    return mountImage;\n  },\n\n  /**\n   * Creates markup for the open tag and all attributes.\n   *\n   * This method has side effects because events get registered.\n   *\n   * Iterating over object properties is faster than iterating over arrays.\n   * @see http://jsperf.com/obj-vs-arr-iteration\n   *\n   * @private\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {object} props\n   * @return {string} Markup of opening tag.\n   */\n  _createOpenTagMarkupAndPutListeners: function (transaction, props) {\n    var ret = '<' + this._currentElement.type;\n\n    for (var propKey in props) {\n      if (!props.hasOwnProperty(propKey)) {\n        continue;\n      }\n      var propValue = props[propKey];\n      if (propValue == null) {\n        continue;\n      }\n      if (registrationNameModules.hasOwnProperty(propKey)) {\n        if (propValue) {\n          enqueuePutListener(this, propKey, propValue, transaction);\n        }\n      } else {\n        if (propKey === STYLE) {\n          if (propValue) {\n            if (true) {\n              // See `_updateDOMProperties`. style block\n              this._previousStyle = propValue;\n            }\n            propValue = this._previousStyleCopy = _assign({}, props.style);\n          }\n          propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this);\n        }\n        var markup = null;\n        if (this._tag != null && isCustomComponent(this._tag, props)) {\n          if (!RESERVED_PROPS.hasOwnProperty(propKey)) {\n            markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey, propValue);\n          }\n        } else {\n          markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);\n        }\n        if (markup) {\n          ret += ' ' + markup;\n        }\n      }\n    }\n\n    // For static pages, no need to put React ID and checksum. Saves lots of\n    // bytes.\n    if (transaction.renderToStaticMarkup) {\n      return ret;\n    }\n\n    if (!this._hostParent) {\n      ret += ' ' + DOMPropertyOperations.createMarkupForRoot();\n    }\n    ret += ' ' + DOMPropertyOperations.createMarkupForID(this._domID);\n    return ret;\n  },\n\n  /**\n   * Creates markup for the content between the tags.\n   *\n   * @private\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {object} props\n   * @param {object} context\n   * @return {string} Content markup.\n   */\n  _createContentMarkup: function (transaction, props, context) {\n    var ret = '';\n\n    // Intentional use of != to avoid catching zero/false.\n    var innerHTML = props.dangerouslySetInnerHTML;\n    if (innerHTML != null) {\n      if (innerHTML.__html != null) {\n        ret = innerHTML.__html;\n      }\n    } else {\n      var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;\n      var childrenToUse = contentToUse != null ? null : props.children;\n      if (contentToUse != null) {\n        // TODO: Validate that text is allowed as a child of this node\n        ret = escapeTextContentForBrowser(contentToUse);\n        if (true) {\n          setAndValidateContentChildDev.call(this, contentToUse);\n        }\n      } else if (childrenToUse != null) {\n        var mountImages = this.mountChildren(childrenToUse, transaction, context);\n        ret = mountImages.join('');\n      }\n    }\n    if (newlineEatingTags[this._tag] && ret.charAt(0) === '\\n') {\n      // text/html ignores the first character in these tags if it's a newline\n      // Prefer to break application/xml over text/html (for now) by adding\n      // a newline specifically to get eaten by the parser. (Alternately for\n      // textareas, replacing \"^\\n\" with \"\\r\\n\" doesn't get eaten, and the first\n      // \\r is normalized out by HTMLTextAreaElement#value.)\n      // See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>\n      // See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>\n      // See: <http://www.w3.org/TR/html5/syntax.html#newlines>\n      // See: Parsing of \"textarea\" \"listing\" and \"pre\" elements\n      //  from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>\n      return '\\n' + ret;\n    } else {\n      return ret;\n    }\n  },\n\n  _createInitialChildren: function (transaction, props, context, lazyTree) {\n    // Intentional use of != to avoid catching zero/false.\n    var innerHTML = props.dangerouslySetInnerHTML;\n    if (innerHTML != null) {\n      if (innerHTML.__html != null) {\n        DOMLazyTree.queueHTML(lazyTree, innerHTML.__html);\n      }\n    } else {\n      var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;\n      var childrenToUse = contentToUse != null ? null : props.children;\n      // TODO: Validate that text is allowed as a child of this node\n      if (contentToUse != null) {\n        // Avoid setting textContent when the text is empty. In IE11 setting\n        // textContent on a text area will cause the placeholder to not\n        // show within the textarea until it has been focused and blurred again.\n        // https://github.com/facebook/react/issues/6731#issuecomment-254874553\n        if (contentToUse !== '') {\n          if (true) {\n            setAndValidateContentChildDev.call(this, contentToUse);\n          }\n          DOMLazyTree.queueText(lazyTree, contentToUse);\n        }\n      } else if (childrenToUse != null) {\n        var mountImages = this.mountChildren(childrenToUse, transaction, context);\n        for (var i = 0; i < mountImages.length; i++) {\n          DOMLazyTree.queueChild(lazyTree, mountImages[i]);\n        }\n      }\n    }\n  },\n\n  /**\n   * Receives a next element and updates the component.\n   *\n   * @internal\n   * @param {ReactElement} nextElement\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {object} context\n   */\n  receiveComponent: function (nextElement, transaction, context) {\n    var prevElement = this._currentElement;\n    this._currentElement = nextElement;\n    this.updateComponent(transaction, prevElement, nextElement, context);\n  },\n\n  /**\n   * Updates a DOM component after it has already been allocated and\n   * attached to the DOM. Reconciles the root DOM node, then recurses.\n   *\n   * @param {ReactReconcileTransaction} transaction\n   * @param {ReactElement} prevElement\n   * @param {ReactElement} nextElement\n   * @internal\n   * @overridable\n   */\n  updateComponent: function (transaction, prevElement, nextElement, context) {\n    var lastProps = prevElement.props;\n    var nextProps = this._currentElement.props;\n\n    switch (this._tag) {\n      case 'input':\n        lastProps = ReactDOMInput.getHostProps(this, lastProps);\n        nextProps = ReactDOMInput.getHostProps(this, nextProps);\n        break;\n      case 'option':\n        lastProps = ReactDOMOption.getHostProps(this, lastProps);\n        nextProps = ReactDOMOption.getHostProps(this, nextProps);\n        break;\n      case 'select':\n        lastProps = ReactDOMSelect.getHostProps(this, lastProps);\n        nextProps = ReactDOMSelect.getHostProps(this, nextProps);\n        break;\n      case 'textarea':\n        lastProps = ReactDOMTextarea.getHostProps(this, lastProps);\n        nextProps = ReactDOMTextarea.getHostProps(this, nextProps);\n        break;\n    }\n\n    assertValidProps(this, nextProps);\n    this._updateDOMProperties(lastProps, nextProps, transaction);\n    this._updateDOMChildren(lastProps, nextProps, transaction, context);\n\n    switch (this._tag) {\n      case 'input':\n        // Update the wrapper around inputs *after* updating props. This has to\n        // happen after `_updateDOMProperties`. Otherwise HTML5 input validations\n        // raise warnings and prevent the new value from being assigned.\n        ReactDOMInput.updateWrapper(this);\n        break;\n      case 'textarea':\n        ReactDOMTextarea.updateWrapper(this);\n        break;\n      case 'select':\n        // <select> value update needs to occur after <option> children\n        // reconciliation\n        transaction.getReactMountReady().enqueue(postUpdateSelectWrapper, this);\n        break;\n    }\n  },\n\n  /**\n   * Reconciles the properties by detecting differences in property values and\n   * updating the DOM as necessary. This function is probably the single most\n   * critical path for performance optimization.\n   *\n   * TODO: Benchmark whether checking for changed values in memory actually\n   *       improves performance (especially statically positioned elements).\n   * TODO: Benchmark the effects of putting this at the top since 99% of props\n   *       do not change for a given reconciliation.\n   * TODO: Benchmark areas that can be improved with caching.\n   *\n   * @private\n   * @param {object} lastProps\n   * @param {object} nextProps\n   * @param {?DOMElement} node\n   */\n  _updateDOMProperties: function (lastProps, nextProps, transaction) {\n    var propKey;\n    var styleName;\n    var styleUpdates;\n    for (propKey in lastProps) {\n      if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) {\n        continue;\n      }\n      if (propKey === STYLE) {\n        var lastStyle = this._previousStyleCopy;\n        for (styleName in lastStyle) {\n          if (lastStyle.hasOwnProperty(styleName)) {\n            styleUpdates = styleUpdates || {};\n            styleUpdates[styleName] = '';\n          }\n        }\n        this._previousStyleCopy = null;\n      } else if (registrationNameModules.hasOwnProperty(propKey)) {\n        if (lastProps[propKey]) {\n          // Only call deleteListener if there was a listener previously or\n          // else willDeleteListener gets called when there wasn't actually a\n          // listener (e.g., onClick={null})\n          deleteListener(this, propKey);\n        }\n      } else if (isCustomComponent(this._tag, lastProps)) {\n        if (!RESERVED_PROPS.hasOwnProperty(propKey)) {\n          DOMPropertyOperations.deleteValueForAttribute(getNode(this), propKey);\n        }\n      } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {\n        DOMPropertyOperations.deleteValueForProperty(getNode(this), propKey);\n      }\n    }\n    for (propKey in nextProps) {\n      var nextProp = nextProps[propKey];\n      var lastProp = propKey === STYLE ? this._previousStyleCopy : lastProps != null ? lastProps[propKey] : undefined;\n      if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp == null && lastProp == null) {\n        continue;\n      }\n      if (propKey === STYLE) {\n        if (nextProp) {\n          if (true) {\n            checkAndWarnForMutatedStyle(this._previousStyleCopy, this._previousStyle, this);\n            this._previousStyle = nextProp;\n          }\n          nextProp = this._previousStyleCopy = _assign({}, nextProp);\n        } else {\n          this._previousStyleCopy = null;\n        }\n        if (lastProp) {\n          // Unset styles on `lastProp` but not on `nextProp`.\n          for (styleName in lastProp) {\n            if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) {\n              styleUpdates = styleUpdates || {};\n              styleUpdates[styleName] = '';\n            }\n          }\n          // Update styles that changed since `lastProp`.\n          for (styleName in nextProp) {\n            if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) {\n              styleUpdates = styleUpdates || {};\n              styleUpdates[styleName] = nextProp[styleName];\n            }\n          }\n        } else {\n          // Relies on `updateStylesByID` not mutating `styleUpdates`.\n          styleUpdates = nextProp;\n        }\n      } else if (registrationNameModules.hasOwnProperty(propKey)) {\n        if (nextProp) {\n          enqueuePutListener(this, propKey, nextProp, transaction);\n        } else if (lastProp) {\n          deleteListener(this, propKey);\n        }\n      } else if (isCustomComponent(this._tag, nextProps)) {\n        if (!RESERVED_PROPS.hasOwnProperty(propKey)) {\n          DOMPropertyOperations.setValueForAttribute(getNode(this), propKey, nextProp);\n        }\n      } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {\n        var node = getNode(this);\n        // If we're updating to null or undefined, we should remove the property\n        // from the DOM node instead of inadvertently setting to a string. This\n        // brings us in line with the same behavior we have on initial render.\n        if (nextProp != null) {\n          DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);\n        } else {\n          DOMPropertyOperations.deleteValueForProperty(node, propKey);\n        }\n      }\n    }\n    if (styleUpdates) {\n      CSSPropertyOperations.setValueForStyles(getNode(this), styleUpdates, this);\n    }\n  },\n\n  /**\n   * Reconciles the children with the various properties that affect the\n   * children content.\n   *\n   * @param {object} lastProps\n   * @param {object} nextProps\n   * @param {ReactReconcileTransaction} transaction\n   * @param {object} context\n   */\n  _updateDOMChildren: function (lastProps, nextProps, transaction, context) {\n    var lastContent = CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;\n    var nextContent = CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;\n\n    var lastHtml = lastProps.dangerouslySetInnerHTML && lastProps.dangerouslySetInnerHTML.__html;\n    var nextHtml = nextProps.dangerouslySetInnerHTML && nextProps.dangerouslySetInnerHTML.__html;\n\n    // Note the use of `!=` which checks for null or undefined.\n    var lastChildren = lastContent != null ? null : lastProps.children;\n    var nextChildren = nextContent != null ? null : nextProps.children;\n\n    // If we're switching from children to content/html or vice versa, remove\n    // the old content\n    var lastHasContentOrHtml = lastContent != null || lastHtml != null;\n    var nextHasContentOrHtml = nextContent != null || nextHtml != null;\n    if (lastChildren != null && nextChildren == null) {\n      this.updateChildren(null, transaction, context);\n    } else if (lastHasContentOrHtml && !nextHasContentOrHtml) {\n      this.updateTextContent('');\n      if (true) {\n        ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);\n      }\n    }\n\n    if (nextContent != null) {\n      if (lastContent !== nextContent) {\n        this.updateTextContent('' + nextContent);\n        if (true) {\n          setAndValidateContentChildDev.call(this, nextContent);\n        }\n      }\n    } else if (nextHtml != null) {\n      if (lastHtml !== nextHtml) {\n        this.updateMarkup('' + nextHtml);\n      }\n      if (true) {\n        ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);\n      }\n    } else if (nextChildren != null) {\n      if (true) {\n        setAndValidateContentChildDev.call(this, null);\n      }\n\n      this.updateChildren(nextChildren, transaction, context);\n    }\n  },\n\n  getHostNode: function () {\n    return getNode(this);\n  },\n\n  /**\n   * Destroys all event registrations for this instance. Does not remove from\n   * the DOM. That must be done by the parent.\n   *\n   * @internal\n   */\n  unmountComponent: function (safely) {\n    switch (this._tag) {\n      case 'audio':\n      case 'form':\n      case 'iframe':\n      case 'img':\n      case 'link':\n      case 'object':\n      case 'source':\n      case 'video':\n        var listeners = this._wrapperState.listeners;\n        if (listeners) {\n          for (var i = 0; i < listeners.length; i++) {\n            listeners[i].remove();\n          }\n        }\n        break;\n      case 'html':\n      case 'head':\n      case 'body':\n        /**\n         * Components like <html> <head> and <body> can't be removed or added\n         * easily in a cross-browser way, however it's valuable to be able to\n         * take advantage of React's reconciliation for styling and <title>\n         * management. So we just document it and throw in dangerous cases.\n         */\n         true ?  true ? invariant(false, '<%s> tried to unmount. Because of cross-browser quirks it is impossible to unmount some top-level components (eg <html>, <head>, and <body>) reliably and efficiently. To fix this, have a single top-level component that never unmounts render these elements.', this._tag) : _prodInvariant('66', this._tag) : void 0;\n        break;\n    }\n\n    this.unmountChildren(safely);\n    ReactDOMComponentTree.uncacheNode(this);\n    EventPluginHub.deleteAllListeners(this);\n    this._rootNodeID = 0;\n    this._domID = 0;\n    this._wrapperState = null;\n\n    if (true) {\n      setAndValidateContentChildDev.call(this, null);\n    }\n  },\n\n  getPublicInstance: function () {\n    return getNode(this);\n  }\n\n};\n\n_assign(ReactDOMComponent.prototype, ReactDOMComponent.Mixin, ReactMultiChild.Mixin);\n\nmodule.exports = ReactDOMComponent;//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMjY1LmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9yZWFjdC1kb20vbGliL1JlYWN0RE9NQ29tcG9uZW50LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29weXJpZ2h0IDIwMTMtcHJlc2VudCwgRmFjZWJvb2ssIEluYy5cbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKlxuICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgQlNELXN0eWxlIGxpY2Vuc2UgZm91bmQgaW4gdGhlXG4gKiBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuIEFuIGFkZGl0aW9uYWwgZ3JhbnRcbiAqIG9mIHBhdGVudCByaWdodHMgY2FuIGJlIGZvdW5kIGluIHRoZSBQQVRFTlRTIGZpbGUgaW4gdGhlIHNhbWUgZGlyZWN0b3J5LlxuICpcbiAqL1xuXG4vKiBnbG9iYWwgaGFzT3duUHJvcGVydHk6dHJ1ZSAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBfcHJvZEludmFyaWFudCA9IHJlcXVpcmUoJy4vcmVhY3RQcm9kSW52YXJpYW50JyksXG4gICAgX2Fzc2lnbiA9IHJlcXVpcmUoJ29iamVjdC1hc3NpZ24nKTtcblxudmFyIEF1dG9Gb2N1c1V0aWxzID0gcmVxdWlyZSgnLi9BdXRvRm9jdXNVdGlscycpO1xudmFyIENTU1Byb3BlcnR5T3BlcmF0aW9ucyA9IHJlcXVpcmUoJy4vQ1NTUHJvcGVydHlPcGVyYXRpb25zJyk7XG52YXIgRE9NTGF6eVRyZWUgPSByZXF1aXJlKCcuL0RPTUxhenlUcmVlJyk7XG52YXIgRE9NTmFtZXNwYWNlcyA9IHJlcXVpcmUoJy4vRE9NTmFtZXNwYWNlcycpO1xudmFyIERPTVByb3BlcnR5ID0gcmVxdWlyZSgnLi9ET01Qcm9wZXJ0eScpO1xudmFyIERPTVByb3BlcnR5T3BlcmF0aW9ucyA9IHJlcXVpcmUoJy4vRE9NUHJvcGVydHlPcGVyYXRpb25zJyk7XG52YXIgRXZlbnRQbHVnaW5IdWIgPSByZXF1aXJlKCcuL0V2ZW50UGx1Z2luSHViJyk7XG52YXIgRXZlbnRQbHVnaW5SZWdpc3RyeSA9IHJlcXVpcmUoJy4vRXZlbnRQbHVnaW5SZWdpc3RyeScpO1xudmFyIFJlYWN0QnJvd3NlckV2ZW50RW1pdHRlciA9IHJlcXVpcmUoJy4vUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyJyk7XG52YXIgUmVhY3RET01Db21wb25lbnRGbGFncyA9IHJlcXVpcmUoJy4vUmVhY3RET01Db21wb25lbnRGbGFncycpO1xudmFyIFJlYWN0RE9NQ29tcG9uZW50VHJlZSA9IHJlcXVpcmUoJy4vUmVhY3RET01Db21wb25lbnRUcmVlJyk7XG52YXIgUmVhY3RET01JbnB1dCA9IHJlcXVpcmUoJy4vUmVhY3RET01JbnB1dCcpO1xudmFyIFJlYWN0RE9NT3B0aW9uID0gcmVxdWlyZSgnLi9SZWFjdERPTU9wdGlvbicpO1xudmFyIFJlYWN0RE9NU2VsZWN0ID0gcmVxdWlyZSgnLi9SZWFjdERPTVNlbGVjdCcpO1xudmFyIFJlYWN0RE9NVGV4dGFyZWEgPSByZXF1aXJlKCcuL1JlYWN0RE9NVGV4dGFyZWEnKTtcbnZhciBSZWFjdEluc3RydW1lbnRhdGlvbiA9IHJlcXVpcmUoJy4vUmVhY3RJbnN0cnVtZW50YXRpb24nKTtcbnZhciBSZWFjdE11bHRpQ2hpbGQgPSByZXF1aXJlKCcuL1JlYWN0TXVsdGlDaGlsZCcpO1xudmFyIFJlYWN0U2VydmVyUmVuZGVyaW5nVHJhbnNhY3Rpb24gPSByZXF1aXJlKCcuL1JlYWN0U2VydmVyUmVuZGVyaW5nVHJhbnNhY3Rpb24nKTtcblxudmFyIGVtcHR5RnVuY3Rpb24gPSByZXF1aXJlKCdmYmpzL2xpYi9lbXB0eUZ1bmN0aW9uJyk7XG52YXIgZXNjYXBlVGV4dENvbnRlbnRGb3JCcm93c2VyID0gcmVxdWlyZSgnLi9lc2NhcGVUZXh0Q29udGVudEZvckJyb3dzZXInKTtcbnZhciBpbnZhcmlhbnQgPSByZXF1aXJlKCdmYmpzL2xpYi9pbnZhcmlhbnQnKTtcbnZhciBpc0V2ZW50U3VwcG9ydGVkID0gcmVxdWlyZSgnLi9pc0V2ZW50U3VwcG9ydGVkJyk7XG52YXIgc2hhbGxvd0VxdWFsID0gcmVxdWlyZSgnZmJqcy9saWIvc2hhbGxvd0VxdWFsJyk7XG52YXIgdmFsaWRhdGVET01OZXN0aW5nID0gcmVxdWlyZSgnLi92YWxpZGF0ZURPTU5lc3RpbmcnKTtcbnZhciB3YXJuaW5nID0gcmVxdWlyZSgnZmJqcy9saWIvd2FybmluZycpO1xuXG52YXIgRmxhZ3MgPSBSZWFjdERPTUNvbXBvbmVudEZsYWdzO1xudmFyIGRlbGV0ZUxpc3RlbmVyID0gRXZlbnRQbHVnaW5IdWIuZGVsZXRlTGlzdGVuZXI7XG52YXIgZ2V0Tm9kZSA9IFJlYWN0RE9NQ29tcG9uZW50VHJlZS5nZXROb2RlRnJvbUluc3RhbmNlO1xudmFyIGxpc3RlblRvID0gUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyLmxpc3RlblRvO1xudmFyIHJlZ2lzdHJhdGlvbk5hbWVNb2R1bGVzID0gRXZlbnRQbHVnaW5SZWdpc3RyeS5yZWdpc3RyYXRpb25OYW1lTW9kdWxlcztcblxuLy8gRm9yIHF1aWNrbHkgbWF0Y2hpbmcgY2hpbGRyZW4gdHlwZSwgdG8gdGVzdCBpZiBjYW4gYmUgdHJlYXRlZCBhcyBjb250ZW50LlxudmFyIENPTlRFTlRfVFlQRVMgPSB7ICdzdHJpbmcnOiB0cnVlLCAnbnVtYmVyJzogdHJ1ZSB9O1xuXG52YXIgU1RZTEUgPSAnc3R5bGUnO1xudmFyIEhUTUwgPSAnX19odG1sJztcbnZhciBSRVNFUlZFRF9QUk9QUyA9IHtcbiAgY2hpbGRyZW46IG51bGwsXG4gIGRhbmdlcm91c2x5U2V0SW5uZXJIVE1MOiBudWxsLFxuICBzdXBwcmVzc0NvbnRlbnRFZGl0YWJsZVdhcm5pbmc6IG51bGxcbn07XG5cbi8vIE5vZGUgdHlwZSBmb3IgZG9jdW1lbnQgZnJhZ21lbnRzIChOb2RlLkRPQ1VNRU5UX0ZSQUdNRU5UX05PREUpLlxudmFyIERPQ19GUkFHTUVOVF9UWVBFID0gMTE7XG5cbmZ1bmN0aW9uIGdldERlY2xhcmF0aW9uRXJyb3JBZGRlbmR1bShpbnRlcm5hbEluc3RhbmNlKSB7XG4gIGlmIChpbnRlcm5hbEluc3RhbmNlKSB7XG4gICAgdmFyIG93bmVyID0gaW50ZXJuYWxJbnN0YW5jZS5fY3VycmVudEVsZW1lbnQuX293bmVyIHx8IG51bGw7XG4gICAgaWYgKG93bmVyKSB7XG4gICAgICB2YXIgbmFtZSA9IG93bmVyLmdldE5hbWUoKTtcbiAgICAgIGlmIChuYW1lKSB7XG4gICAgICAgIHJldHVybiAnIFRoaXMgRE9NIG5vZGUgd2FzIHJlbmRlcmVkIGJ5IGAnICsgbmFtZSArICdgLic7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiAnJztcbn1cblxuZnVuY3Rpb24gZnJpZW5kbHlTdHJpbmdpZnkob2JqKSB7XG4gIGlmICh0eXBlb2Ygb2JqID09PSAnb2JqZWN0Jykge1xuICAgIGlmIChBcnJheS5pc0FycmF5KG9iaikpIHtcbiAgICAgIHJldHVybiAnWycgKyBvYmoubWFwKGZyaWVuZGx5U3RyaW5naWZ5KS5qb2luKCcsICcpICsgJ10nO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgcGFpcnMgPSBbXTtcbiAgICAgIGZvciAodmFyIGtleSBpbiBvYmopIHtcbiAgICAgICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIGtleSkpIHtcbiAgICAgICAgICB2YXIga2V5RXNjYXBlZCA9IC9eW2EteiRfXVtcXHckX10qJC9pLnRlc3Qoa2V5KSA/IGtleSA6IEpTT04uc3RyaW5naWZ5KGtleSk7XG4gICAgICAgICAgcGFpcnMucHVzaChrZXlFc2NhcGVkICsgJzogJyArIGZyaWVuZGx5U3RyaW5naWZ5KG9ialtrZXldKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiAneycgKyBwYWlycy5qb2luKCcsICcpICsgJ30nO1xuICAgIH1cbiAgfSBlbHNlIGlmICh0eXBlb2Ygb2JqID09PSAnc3RyaW5nJykge1xuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShvYmopO1xuICB9IGVsc2UgaWYgKHR5cGVvZiBvYmogPT09ICdmdW5jdGlvbicpIHtcbiAgICByZXR1cm4gJ1tmdW5jdGlvbiBvYmplY3RdJztcbiAgfVxuICAvLyBEaWZmZXJzIGZyb20gSlNPTi5zdHJpbmdpZnkgaW4gdGhhdCB1bmRlZmluZWQgYmVjYXVzZSB1bmRlZmluZWQgYW5kIHRoYXRcbiAgLy8gaW5mIGFuZCBuYW4gZG9uJ3QgYmVjb21lIG51bGxcbiAgcmV0dXJuIFN0cmluZyhvYmopO1xufVxuXG52YXIgc3R5bGVNdXRhdGlvbldhcm5pbmcgPSB7fTtcblxuZnVuY3Rpb24gY2hlY2tBbmRXYXJuRm9yTXV0YXRlZFN0eWxlKHN0eWxlMSwgc3R5bGUyLCBjb21wb25lbnQpIHtcbiAgaWYgKHN0eWxlMSA9PSBudWxsIHx8IHN0eWxlMiA9PSBudWxsKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGlmIChzaGFsbG93RXF1YWwoc3R5bGUxLCBzdHlsZTIpKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdmFyIGNvbXBvbmVudE5hbWUgPSBjb21wb25lbnQuX3RhZztcbiAgdmFyIG93bmVyID0gY29tcG9uZW50Ll9jdXJyZW50RWxlbWVudC5fb3duZXI7XG4gIHZhciBvd25lck5hbWU7XG4gIGlmIChvd25lcikge1xuICAgIG93bmVyTmFtZSA9IG93bmVyLmdldE5hbWUoKTtcbiAgfVxuXG4gIHZhciBoYXNoID0gb3duZXJOYW1lICsgJ3wnICsgY29tcG9uZW50TmFtZTtcblxuICBpZiAoc3R5bGVNdXRhdGlvbldhcm5pbmcuaGFzT3duUHJvcGVydHkoaGFzaCkpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBzdHlsZU11dGF0aW9uV2FybmluZ1toYXNoXSA9IHRydWU7XG5cbiAgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IHdhcm5pbmcoZmFsc2UsICdgJXNgIHdhcyBwYXNzZWQgYSBzdHlsZSBvYmplY3QgdGhhdCBoYXMgcHJldmlvdXNseSBiZWVuIG11dGF0ZWQuICcgKyAnTXV0YXRpbmcgYHN0eWxlYCBpcyBkZXByZWNhdGVkLiBDb25zaWRlciBjbG9uaW5nIGl0IGJlZm9yZWhhbmQuIENoZWNrICcgKyAndGhlIGByZW5kZXJgICVzLiBQcmV2aW91cyBzdHlsZTogJXMuIE11dGF0ZWQgc3R5bGU6ICVzLicsIGNvbXBvbmVudE5hbWUsIG93bmVyID8gJ29mIGAnICsgb3duZXJOYW1lICsgJ2AnIDogJ3VzaW5nIDwnICsgY29tcG9uZW50TmFtZSArICc+JywgZnJpZW5kbHlTdHJpbmdpZnkoc3R5bGUxKSwgZnJpZW5kbHlTdHJpbmdpZnkoc3R5bGUyKSkgOiB2b2lkIDA7XG59XG5cbi8qKlxuICogQHBhcmFtIHtvYmplY3R9IGNvbXBvbmVudFxuICogQHBhcmFtIHs/b2JqZWN0fSBwcm9wc1xuICovXG5mdW5jdGlvbiBhc3NlcnRWYWxpZFByb3BzKGNvbXBvbmVudCwgcHJvcHMpIHtcbiAgaWYgKCFwcm9wcykge1xuICAgIHJldHVybjtcbiAgfVxuICAvLyBOb3RlIHRoZSB1c2Ugb2YgYD09YCB3aGljaCBjaGVja3MgZm9yIG51bGwgb3IgdW5kZWZpbmVkLlxuICBpZiAodm9pZEVsZW1lbnRUYWdzW2NvbXBvbmVudC5fdGFnXSkge1xuICAgICEocHJvcHMuY2hpbGRyZW4gPT0gbnVsbCAmJiBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCA9PSBudWxsKSA/IHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicgPyBpbnZhcmlhbnQoZmFsc2UsICclcyBpcyBhIHZvaWQgZWxlbWVudCB0YWcgYW5kIG11c3QgbmVpdGhlciBoYXZlIGBjaGlsZHJlbmAgbm9yIHVzZSBgZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUxgLiVzJywgY29tcG9uZW50Ll90YWcsIGNvbXBvbmVudC5fY3VycmVudEVsZW1lbnQuX293bmVyID8gJyBDaGVjayB0aGUgcmVuZGVyIG1ldGhvZCBvZiAnICsgY29tcG9uZW50Ll9jdXJyZW50RWxlbWVudC5fb3duZXIuZ2V0TmFtZSgpICsgJy4nIDogJycpIDogX3Byb2RJbnZhcmlhbnQoJzEzNycsIGNvbXBvbmVudC5fdGFnLCBjb21wb25lbnQuX2N1cnJlbnRFbGVtZW50Ll9vd25lciA/ICcgQ2hlY2sgdGhlIHJlbmRlciBtZXRob2Qgb2YgJyArIGNvbXBvbmVudC5fY3VycmVudEVsZW1lbnQuX293bmVyLmdldE5hbWUoKSArICcuJyA6ICcnKSA6IHZvaWQgMDtcbiAgfVxuICBpZiAocHJvcHMuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwgIT0gbnVsbCkge1xuICAgICEocHJvcHMuY2hpbGRyZW4gPT0gbnVsbCkgPyBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gaW52YXJpYW50KGZhbHNlLCAnQ2FuIG9ubHkgc2V0IG9uZSBvZiBgY2hpbGRyZW5gIG9yIGBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTGAuJykgOiBfcHJvZEludmFyaWFudCgnNjAnKSA6IHZvaWQgMDtcbiAgICAhKHR5cGVvZiBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCA9PT0gJ29iamVjdCcgJiYgSFRNTCBpbiBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCkgPyBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gaW52YXJpYW50KGZhbHNlLCAnYHByb3BzLmRhbmdlcm91c2x5U2V0SW5uZXJIVE1MYCBtdXN0IGJlIGluIHRoZSBmb3JtIGB7X19odG1sOiAuLi59YC4gUGxlYXNlIHZpc2l0IGh0dHBzOi8vZmIubWUvcmVhY3QtaW52YXJpYW50LWRhbmdlcm91c2x5LXNldC1pbm5lci1odG1sIGZvciBtb3JlIGluZm9ybWF0aW9uLicpIDogX3Byb2RJbnZhcmlhbnQoJzYxJykgOiB2b2lkIDA7XG4gIH1cbiAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gd2FybmluZyhwcm9wcy5pbm5lckhUTUwgPT0gbnVsbCwgJ0RpcmVjdGx5IHNldHRpbmcgcHJvcGVydHkgYGlubmVySFRNTGAgaXMgbm90IHBlcm1pdHRlZC4gJyArICdGb3IgbW9yZSBpbmZvcm1hdGlvbiwgbG9va3VwIGRvY3VtZW50YXRpb24gb24gYGRhbmdlcm91c2x5U2V0SW5uZXJIVE1MYC4nKSA6IHZvaWQgMDtcbiAgICBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gd2FybmluZyhwcm9wcy5zdXBwcmVzc0NvbnRlbnRFZGl0YWJsZVdhcm5pbmcgfHwgIXByb3BzLmNvbnRlbnRFZGl0YWJsZSB8fCBwcm9wcy5jaGlsZHJlbiA9PSBudWxsLCAnQSBjb21wb25lbnQgaXMgYGNvbnRlbnRFZGl0YWJsZWAgYW5kIGNvbnRhaW5zIGBjaGlsZHJlbmAgbWFuYWdlZCBieSAnICsgJ1JlYWN0LiBJdCBpcyBub3cgeW91ciByZXNwb25zaWJpbGl0eSB0byBndWFyYW50ZWUgdGhhdCBub25lIG9mICcgKyAndGhvc2Ugbm9kZXMgYXJlIHVuZXhwZWN0ZWRseSBtb2RpZmllZCBvciBkdXBsaWNhdGVkLiBUaGlzIGlzICcgKyAncHJvYmFibHkgbm90IGludGVudGlvbmFsLicpIDogdm9pZCAwO1xuICAgIHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicgPyB3YXJuaW5nKHByb3BzLm9uRm9jdXNJbiA9PSBudWxsICYmIHByb3BzLm9uRm9jdXNPdXQgPT0gbnVsbCwgJ1JlYWN0IHVzZXMgb25Gb2N1cyBhbmQgb25CbHVyIGluc3RlYWQgb2Ygb25Gb2N1c0luIGFuZCBvbkZvY3VzT3V0LiAnICsgJ0FsbCBSZWFjdCBldmVudHMgYXJlIG5vcm1hbGl6ZWQgdG8gYnViYmxlLCBzbyBvbkZvY3VzSW4gYW5kIG9uRm9jdXNPdXQgJyArICdhcmUgbm90IG5lZWRlZC9zdXBwb3J0ZWQgYnkgUmVhY3QuJykgOiB2b2lkIDA7XG4gIH1cbiAgIShwcm9wcy5zdHlsZSA9PSBudWxsIHx8IHR5cGVvZiBwcm9wcy5zdHlsZSA9PT0gJ29iamVjdCcpID8gcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IGludmFyaWFudChmYWxzZSwgJ1RoZSBgc3R5bGVgIHByb3AgZXhwZWN0cyBhIG1hcHBpbmcgZnJvbSBzdHlsZSBwcm9wZXJ0aWVzIHRvIHZhbHVlcywgbm90IGEgc3RyaW5nLiBGb3IgZXhhbXBsZSwgc3R5bGU9e3ttYXJnaW5SaWdodDogc3BhY2luZyArIFxcJ2VtXFwnfX0gd2hlbiB1c2luZyBKU1guJXMnLCBnZXREZWNsYXJhdGlvbkVycm9yQWRkZW5kdW0oY29tcG9uZW50KSkgOiBfcHJvZEludmFyaWFudCgnNjInLCBnZXREZWNsYXJhdGlvbkVycm9yQWRkZW5kdW0oY29tcG9uZW50KSkgOiB2b2lkIDA7XG59XG5cbmZ1bmN0aW9uIGVucXVldWVQdXRMaXN0ZW5lcihpbnN0LCByZWdpc3RyYXRpb25OYW1lLCBsaXN0ZW5lciwgdHJhbnNhY3Rpb24pIHtcbiAgaWYgKHRyYW5zYWN0aW9uIGluc3RhbmNlb2YgUmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbikge1xuICAgIHJldHVybjtcbiAgfVxuICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJykge1xuICAgIC8vIElFOCBoYXMgbm8gQVBJIGZvciBldmVudCBjYXB0dXJpbmcgYW5kIHRoZSBgb25TY3JvbGxgIGV2ZW50IGRvZXNuJ3RcbiAgICAvLyBidWJibGUuXG4gICAgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IHdhcm5pbmcocmVnaXN0cmF0aW9uTmFtZSAhPT0gJ29uU2Nyb2xsJyB8fCBpc0V2ZW50U3VwcG9ydGVkKCdzY3JvbGwnLCB0cnVlKSwgJ1RoaXMgYnJvd3NlciBkb2VzblxcJ3Qgc3VwcG9ydCB0aGUgYG9uU2Nyb2xsYCBldmVudCcpIDogdm9pZCAwO1xuICB9XG4gIHZhciBjb250YWluZXJJbmZvID0gaW5zdC5faG9zdENvbnRhaW5lckluZm87XG4gIHZhciBpc0RvY3VtZW50RnJhZ21lbnQgPSBjb250YWluZXJJbmZvLl9ub2RlICYmIGNvbnRhaW5lckluZm8uX25vZGUubm9kZVR5cGUgPT09IERPQ19GUkFHTUVOVF9UWVBFO1xuICB2YXIgZG9jID0gaXNEb2N1bWVudEZyYWdtZW50ID8gY29udGFpbmVySW5mby5fbm9kZSA6IGNvbnRhaW5lckluZm8uX293bmVyRG9jdW1lbnQ7XG4gIGxpc3RlblRvKHJlZ2lzdHJhdGlvbk5hbWUsIGRvYyk7XG4gIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUocHV0TGlzdGVuZXIsIHtcbiAgICBpbnN0OiBpbnN0LFxuICAgIHJlZ2lzdHJhdGlvbk5hbWU6IHJlZ2lzdHJhdGlvbk5hbWUsXG4gICAgbGlzdGVuZXI6IGxpc3RlbmVyXG4gIH0pO1xufVxuXG5mdW5jdGlvbiBwdXRMaXN0ZW5lcigpIHtcbiAgdmFyIGxpc3RlbmVyVG9QdXQgPSB0aGlzO1xuICBFdmVudFBsdWdpbkh1Yi5wdXRMaXN0ZW5lcihsaXN0ZW5lclRvUHV0Lmluc3QsIGxpc3RlbmVyVG9QdXQucmVnaXN0cmF0aW9uTmFtZSwgbGlzdGVuZXJUb1B1dC5saXN0ZW5lcik7XG59XG5cbmZ1bmN0aW9uIGlucHV0UG9zdE1vdW50KCkge1xuICB2YXIgaW5zdCA9IHRoaXM7XG4gIFJlYWN0RE9NSW5wdXQucG9zdE1vdW50V3JhcHBlcihpbnN0KTtcbn1cblxuZnVuY3Rpb24gdGV4dGFyZWFQb3N0TW91bnQoKSB7XG4gIHZhciBpbnN0ID0gdGhpcztcbiAgUmVhY3RET01UZXh0YXJlYS5wb3N0TW91bnRXcmFwcGVyKGluc3QpO1xufVxuXG5mdW5jdGlvbiBvcHRpb25Qb3N0TW91bnQoKSB7XG4gIHZhciBpbnN0ID0gdGhpcztcbiAgUmVhY3RET01PcHRpb24ucG9zdE1vdW50V3JhcHBlcihpbnN0KTtcbn1cblxudmFyIHNldEFuZFZhbGlkYXRlQ29udGVudENoaWxkRGV2ID0gZW1wdHlGdW5jdGlvbjtcbmlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gIHNldEFuZFZhbGlkYXRlQ29udGVudENoaWxkRGV2ID0gZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICB2YXIgaGFzRXhpc3RpbmdDb250ZW50ID0gdGhpcy5fY29udGVudERlYnVnSUQgIT0gbnVsbDtcbiAgICB2YXIgZGVidWdJRCA9IHRoaXMuX2RlYnVnSUQ7XG4gICAgLy8gVGhpcyBJRCByZXByZXNlbnRzIHRoZSBpbmxpbmVkIGNoaWxkIHRoYXQgaGFzIG5vIGJhY2tpbmcgaW5zdGFuY2U6XG4gICAgdmFyIGNvbnRlbnREZWJ1Z0lEID0gLWRlYnVnSUQ7XG5cbiAgICBpZiAoY29udGVudCA9PSBudWxsKSB7XG4gICAgICBpZiAoaGFzRXhpc3RpbmdDb250ZW50KSB7XG4gICAgICAgIFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vblVubW91bnRDb21wb25lbnQodGhpcy5fY29udGVudERlYnVnSUQpO1xuICAgICAgfVxuICAgICAgdGhpcy5fY29udGVudERlYnVnSUQgPSBudWxsO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhbGlkYXRlRE9NTmVzdGluZyhudWxsLCBTdHJpbmcoY29udGVudCksIHRoaXMsIHRoaXMuX2FuY2VzdG9ySW5mbyk7XG4gICAgdGhpcy5fY29udGVudERlYnVnSUQgPSBjb250ZW50RGVidWdJRDtcbiAgICBpZiAoaGFzRXhpc3RpbmdDb250ZW50KSB7XG4gICAgICBSZWFjdEluc3RydW1lbnRhdGlvbi5kZWJ1Z1Rvb2wub25CZWZvcmVVcGRhdGVDb21wb25lbnQoY29udGVudERlYnVnSUQsIGNvbnRlbnQpO1xuICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uVXBkYXRlQ29tcG9uZW50KGNvbnRlbnREZWJ1Z0lEKTtcbiAgICB9IGVsc2Uge1xuICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uQmVmb3JlTW91bnRDb21wb25lbnQoY29udGVudERlYnVnSUQsIGNvbnRlbnQsIGRlYnVnSUQpO1xuICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uTW91bnRDb21wb25lbnQoY29udGVudERlYnVnSUQpO1xuICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uU2V0Q2hpbGRyZW4oZGVidWdJRCwgW2NvbnRlbnREZWJ1Z0lEXSk7XG4gICAgfVxuICB9O1xufVxuXG4vLyBUaGVyZSBhcmUgc28gbWFueSBtZWRpYSBldmVudHMsIGl0IG1ha2VzIHNlbnNlIHRvIGp1c3Rcbi8vIG1haW50YWluIGEgbGlzdCByYXRoZXIgdGhhbiBjcmVhdGUgYSBgdHJhcEJ1YmJsZWRFdmVudGAgZm9yIGVhY2hcbnZhciBtZWRpYUV2ZW50cyA9IHtcbiAgdG9wQWJvcnQ6ICdhYm9ydCcsXG4gIHRvcENhblBsYXk6ICdjYW5wbGF5JyxcbiAgdG9wQ2FuUGxheVRocm91Z2g6ICdjYW5wbGF5dGhyb3VnaCcsXG4gIHRvcER1cmF0aW9uQ2hhbmdlOiAnZHVyYXRpb25jaGFuZ2UnLFxuICB0b3BFbXB0aWVkOiAnZW1wdGllZCcsXG4gIHRvcEVuY3J5cHRlZDogJ2VuY3J5cHRlZCcsXG4gIHRvcEVuZGVkOiAnZW5kZWQnLFxuICB0b3BFcnJvcjogJ2Vycm9yJyxcbiAgdG9wTG9hZGVkRGF0YTogJ2xvYWRlZGRhdGEnLFxuICB0b3BMb2FkZWRNZXRhZGF0YTogJ2xvYWRlZG1ldGFkYXRhJyxcbiAgdG9wTG9hZFN0YXJ0OiAnbG9hZHN0YXJ0JyxcbiAgdG9wUGF1c2U6ICdwYXVzZScsXG4gIHRvcFBsYXk6ICdwbGF5JyxcbiAgdG9wUGxheWluZzogJ3BsYXlpbmcnLFxuICB0b3BQcm9ncmVzczogJ3Byb2dyZXNzJyxcbiAgdG9wUmF0ZUNoYW5nZTogJ3JhdGVjaGFuZ2UnLFxuICB0b3BTZWVrZWQ6ICdzZWVrZWQnLFxuICB0b3BTZWVraW5nOiAnc2Vla2luZycsXG4gIHRvcFN0YWxsZWQ6ICdzdGFsbGVkJyxcbiAgdG9wU3VzcGVuZDogJ3N1c3BlbmQnLFxuICB0b3BUaW1lVXBkYXRlOiAndGltZXVwZGF0ZScsXG4gIHRvcFZvbHVtZUNoYW5nZTogJ3ZvbHVtZWNoYW5nZScsXG4gIHRvcFdhaXRpbmc6ICd3YWl0aW5nJ1xufTtcblxuZnVuY3Rpb24gdHJhcEJ1YmJsZWRFdmVudHNMb2NhbCgpIHtcbiAgdmFyIGluc3QgPSB0aGlzO1xuICAvLyBJZiBhIGNvbXBvbmVudCByZW5kZXJzIHRvIG51bGwgb3IgaWYgYW5vdGhlciBjb21wb25lbnQgZmF0YWxzIGFuZCBjYXVzZXNcbiAgLy8gdGhlIHN0YXRlIG9mIHRoZSB0cmVlIHRvIGJlIGNvcnJ1cHRlZCwgYG5vZGVgIGhlcmUgY2FuIGJlIG51bGwuXG4gICFpbnN0Ll9yb290Tm9kZUlEID8gcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IGludmFyaWFudChmYWxzZSwgJ011c3QgYmUgbW91bnRlZCB0byB0cmFwIGV2ZW50cycpIDogX3Byb2RJbnZhcmlhbnQoJzYzJykgOiB2b2lkIDA7XG4gIHZhciBub2RlID0gZ2V0Tm9kZShpbnN0KTtcbiAgIW5vZGUgPyBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gaW52YXJpYW50KGZhbHNlLCAndHJhcEJ1YmJsZWRFdmVudCguLi4pOiBSZXF1aXJlcyBub2RlIHRvIGJlIHJlbmRlcmVkLicpIDogX3Byb2RJbnZhcmlhbnQoJzY0JykgOiB2b2lkIDA7XG5cbiAgc3dpdGNoIChpbnN0Ll90YWcpIHtcbiAgICBjYXNlICdpZnJhbWUnOlxuICAgIGNhc2UgJ29iamVjdCc6XG4gICAgICBpbnN0Ll93cmFwcGVyU3RhdGUubGlzdGVuZXJzID0gW1JlYWN0QnJvd3NlckV2ZW50RW1pdHRlci50cmFwQnViYmxlZEV2ZW50KCd0b3BMb2FkJywgJ2xvYWQnLCBub2RlKV07XG4gICAgICBicmVhaztcbiAgICBjYXNlICd2aWRlbyc6XG4gICAgY2FzZSAnYXVkaW8nOlxuXG4gICAgICBpbnN0Ll93cmFwcGVyU3RhdGUubGlzdGVuZXJzID0gW107XG4gICAgICAvLyBDcmVhdGUgbGlzdGVuZXIgZm9yIGVhY2ggbWVkaWEgZXZlbnRcbiAgICAgIGZvciAodmFyIGV2ZW50IGluIG1lZGlhRXZlbnRzKSB7XG4gICAgICAgIGlmIChtZWRpYUV2ZW50cy5oYXNPd25Qcm9wZXJ0eShldmVudCkpIHtcbiAgICAgICAgICBpbnN0Ll93cmFwcGVyU3RhdGUubGlzdGVuZXJzLnB1c2goUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyLnRyYXBCdWJibGVkRXZlbnQoZXZlbnQsIG1lZGlhRXZlbnRzW2V2ZW50XSwgbm9kZSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBicmVhaztcbiAgICBjYXNlICdzb3VyY2UnOlxuICAgICAgaW5zdC5fd3JhcHBlclN0YXRlLmxpc3RlbmVycyA9IFtSZWFjdEJyb3dzZXJFdmVudEVtaXR0ZXIudHJhcEJ1YmJsZWRFdmVudCgndG9wRXJyb3InLCAnZXJyb3InLCBub2RlKV07XG4gICAgICBicmVhaztcbiAgICBjYXNlICdpbWcnOlxuICAgICAgaW5zdC5fd3JhcHBlclN0YXRlLmxpc3RlbmVycyA9IFtSZWFjdEJyb3dzZXJFdmVudEVtaXR0ZXIudHJhcEJ1YmJsZWRFdmVudCgndG9wRXJyb3InLCAnZXJyb3InLCBub2RlKSwgUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyLnRyYXBCdWJibGVkRXZlbnQoJ3RvcExvYWQnLCAnbG9hZCcsIG5vZGUpXTtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgJ2Zvcm0nOlxuICAgICAgaW5zdC5fd3JhcHBlclN0YXRlLmxpc3RlbmVycyA9IFtSZWFjdEJyb3dzZXJFdmVudEVtaXR0ZXIudHJhcEJ1YmJsZWRFdmVudCgndG9wUmVzZXQnLCAncmVzZXQnLCBub2RlKSwgUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyLnRyYXBCdWJibGVkRXZlbnQoJ3RvcFN1Ym1pdCcsICdzdWJtaXQnLCBub2RlKV07XG4gICAgICBicmVhaztcbiAgICBjYXNlICdpbnB1dCc6XG4gICAgY2FzZSAnc2VsZWN0JzpcbiAgICBjYXNlICd0ZXh0YXJlYSc6XG4gICAgICBpbnN0Ll93cmFwcGVyU3RhdGUubGlzdGVuZXJzID0gW1JlYWN0QnJvd3NlckV2ZW50RW1pdHRlci50cmFwQnViYmxlZEV2ZW50KCd0b3BJbnZhbGlkJywgJ2ludmFsaWQnLCBub2RlKV07XG4gICAgICBicmVhaztcbiAgfVxufVxuXG5mdW5jdGlvbiBwb3N0VXBkYXRlU2VsZWN0V3JhcHBlcigpIHtcbiAgUmVhY3RET01TZWxlY3QucG9zdFVwZGF0ZVdyYXBwZXIodGhpcyk7XG59XG5cbi8vIEZvciBIVE1MLCBjZXJ0YWluIHRhZ3Mgc2hvdWxkIG9taXQgdGhlaXIgY2xvc2UgdGFnLiBXZSBrZWVwIGEgd2hpdGVsaXN0IGZvclxuLy8gdGhvc2Ugc3BlY2lhbC1jYXNlIHRhZ3MuXG5cbnZhciBvbWl0dGVkQ2xvc2VUYWdzID0ge1xuICAnYXJlYSc6IHRydWUsXG4gICdiYXNlJzogdHJ1ZSxcbiAgJ2JyJzogdHJ1ZSxcbiAgJ2NvbCc6IHRydWUsXG4gICdlbWJlZCc6IHRydWUsXG4gICdocic6IHRydWUsXG4gICdpbWcnOiB0cnVlLFxuICAnaW5wdXQnOiB0cnVlLFxuICAna2V5Z2VuJzogdHJ1ZSxcbiAgJ2xpbmsnOiB0cnVlLFxuICAnbWV0YSc6IHRydWUsXG4gICdwYXJhbSc6IHRydWUsXG4gICdzb3VyY2UnOiB0cnVlLFxuICAndHJhY2snOiB0cnVlLFxuICAnd2JyJzogdHJ1ZVxufTtcblxudmFyIG5ld2xpbmVFYXRpbmdUYWdzID0ge1xuICAnbGlzdGluZyc6IHRydWUsXG4gICdwcmUnOiB0cnVlLFxuICAndGV4dGFyZWEnOiB0cnVlXG59O1xuXG4vLyBGb3IgSFRNTCwgY2VydGFpbiB0YWdzIGNhbm5vdCBoYXZlIGNoaWxkcmVuLiBUaGlzIGhhcyB0aGUgc2FtZSBwdXJwb3NlIGFzXG4vLyBgb21pdHRlZENsb3NlVGFnc2AgZXhjZXB0IHRoYXQgYG1lbnVpdGVtYCBzaG91bGQgc3RpbGwgaGF2ZSBpdHMgY2xvc2luZyB0YWcuXG5cbnZhciB2b2lkRWxlbWVudFRhZ3MgPSBfYXNzaWduKHtcbiAgJ21lbnVpdGVtJzogdHJ1ZVxufSwgb21pdHRlZENsb3NlVGFncyk7XG5cbi8vIFdlIGFjY2VwdCBhbnkgdGFnIHRvIGJlIHJlbmRlcmVkIGJ1dCBzaW5jZSB0aGlzIGdldHMgaW5qZWN0ZWQgaW50byBhcmJpdHJhcnlcbi8vIEhUTUwsIHdlIHdhbnQgdG8gbWFrZSBzdXJlIHRoYXQgaXQncyBhIHNhZmUgdGFnLlxuLy8gaHR0cDovL3d3dy53My5vcmcvVFIvUkVDLXhtbC8jTlQtTmFtZVxuXG52YXIgVkFMSURfVEFHX1JFR0VYID0gL15bYS16QS1aXVthLXpBLVo6X1xcLlxcLVxcZF0qJC87IC8vIFNpbXBsaWZpZWQgc3Vic2V0XG52YXIgdmFsaWRhdGVkVGFnQ2FjaGUgPSB7fTtcbnZhciBoYXNPd25Qcm9wZXJ0eSA9IHt9Lmhhc093blByb3BlcnR5O1xuXG5mdW5jdGlvbiB2YWxpZGF0ZURhbmdlcm91c1RhZyh0YWcpIHtcbiAgaWYgKCFoYXNPd25Qcm9wZXJ0eS5jYWxsKHZhbGlkYXRlZFRhZ0NhY2hlLCB0YWcpKSB7XG4gICAgIVZBTElEX1RBR19SRUdFWC50ZXN0KHRhZykgPyBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gaW52YXJpYW50KGZhbHNlLCAnSW52YWxpZCB0YWc6ICVzJywgdGFnKSA6IF9wcm9kSW52YXJpYW50KCc2NScsIHRhZykgOiB2b2lkIDA7XG4gICAgdmFsaWRhdGVkVGFnQ2FjaGVbdGFnXSA9IHRydWU7XG4gIH1cbn1cblxuZnVuY3Rpb24gaXNDdXN0b21Db21wb25lbnQodGFnTmFtZSwgcHJvcHMpIHtcbiAgcmV0dXJuIHRhZ05hbWUuaW5kZXhPZignLScpID49IDAgfHwgcHJvcHMuaXMgIT0gbnVsbDtcbn1cblxudmFyIGdsb2JhbElkQ291bnRlciA9IDE7XG5cbi8qKlxuICogQ3JlYXRlcyBhIG5ldyBSZWFjdCBjbGFzcyB0aGF0IGlzIGlkZW1wb3RlbnQgYW5kIGNhcGFibGUgb2YgY29udGFpbmluZyBvdGhlclxuICogUmVhY3QgY29tcG9uZW50cy4gSXQgYWNjZXB0cyBldmVudCBsaXN0ZW5lcnMgYW5kIERPTSBwcm9wZXJ0aWVzIHRoYXQgYXJlXG4gKiB2YWxpZCBhY2NvcmRpbmcgdG8gYERPTVByb3BlcnR5YC5cbiAqXG4gKiAgLSBFdmVudCBsaXN0ZW5lcnM6IGBvbkNsaWNrYCwgYG9uTW91c2VEb3duYCwgZXRjLlxuICogIC0gRE9NIHByb3BlcnRpZXM6IGBjbGFzc05hbWVgLCBgbmFtZWAsIGB0aXRsZWAsIGV0Yy5cbiAqXG4gKiBUaGUgYHN0eWxlYCBwcm9wZXJ0eSBmdW5jdGlvbnMgZGlmZmVyZW50bHkgZnJvbSB0aGUgRE9NIEFQSS4gSXQgYWNjZXB0cyBhblxuICogb2JqZWN0IG1hcHBpbmcgb2Ygc3R5bGUgcHJvcGVydGllcyB0byB2YWx1ZXMuXG4gKlxuICogQGNvbnN0cnVjdG9yIFJlYWN0RE9NQ29tcG9uZW50XG4gKiBAZXh0ZW5kcyBSZWFjdE11bHRpQ2hpbGRcbiAqL1xuZnVuY3Rpb24gUmVhY3RET01Db21wb25lbnQoZWxlbWVudCkge1xuICB2YXIgdGFnID0gZWxlbWVudC50eXBlO1xuICB2YWxpZGF0ZURhbmdlcm91c1RhZyh0YWcpO1xuICB0aGlzLl9jdXJyZW50RWxlbWVudCA9IGVsZW1lbnQ7XG4gIHRoaXMuX3RhZyA9IHRhZy50b0xvd2VyQ2FzZSgpO1xuICB0aGlzLl9uYW1lc3BhY2VVUkkgPSBudWxsO1xuICB0aGlzLl9yZW5kZXJlZENoaWxkcmVuID0gbnVsbDtcbiAgdGhpcy5fcHJldmlvdXNTdHlsZSA9IG51bGw7XG4gIHRoaXMuX3ByZXZpb3VzU3R5bGVDb3B5ID0gbnVsbDtcbiAgdGhpcy5faG9zdE5vZGUgPSBudWxsO1xuICB0aGlzLl9ob3N0UGFyZW50ID0gbnVsbDtcbiAgdGhpcy5fcm9vdE5vZGVJRCA9IDA7XG4gIHRoaXMuX2RvbUlEID0gMDtcbiAgdGhpcy5faG9zdENvbnRhaW5lckluZm8gPSBudWxsO1xuICB0aGlzLl93cmFwcGVyU3RhdGUgPSBudWxsO1xuICB0aGlzLl90b3BMZXZlbFdyYXBwZXIgPSBudWxsO1xuICB0aGlzLl9mbGFncyA9IDA7XG4gIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgdGhpcy5fYW5jZXN0b3JJbmZvID0gbnVsbDtcbiAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIG51bGwpO1xuICB9XG59XG5cblJlYWN0RE9NQ29tcG9uZW50LmRpc3BsYXlOYW1lID0gJ1JlYWN0RE9NQ29tcG9uZW50JztcblxuUmVhY3RET01Db21wb25lbnQuTWl4aW4gPSB7XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlcyByb290IHRhZyBtYXJrdXAgdGhlbiByZWN1cnNlcy4gVGhpcyBtZXRob2QgaGFzIHNpZGUgZWZmZWN0cyBhbmRcbiAgICogaXMgbm90IGlkZW1wb3RlbnQuXG4gICAqXG4gICAqIEBpbnRlcm5hbFxuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb258UmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHs/UmVhY3RET01Db21wb25lbnR9IHRoZSBwYXJlbnQgY29tcG9uZW50IGluc3RhbmNlXG4gICAqIEBwYXJhbSB7P29iamVjdH0gaW5mbyBhYm91dCB0aGUgaG9zdCBjb250YWluZXJcbiAgICogQHBhcmFtIHtvYmplY3R9IGNvbnRleHRcbiAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgY29tcHV0ZWQgbWFya3VwLlxuICAgKi9cbiAgbW91bnRDb21wb25lbnQ6IGZ1bmN0aW9uICh0cmFuc2FjdGlvbiwgaG9zdFBhcmVudCwgaG9zdENvbnRhaW5lckluZm8sIGNvbnRleHQpIHtcbiAgICB0aGlzLl9yb290Tm9kZUlEID0gZ2xvYmFsSWRDb3VudGVyKys7XG4gICAgdGhpcy5fZG9tSUQgPSBob3N0Q29udGFpbmVySW5mby5faWRDb3VudGVyKys7XG4gICAgdGhpcy5faG9zdFBhcmVudCA9IGhvc3RQYXJlbnQ7XG4gICAgdGhpcy5faG9zdENvbnRhaW5lckluZm8gPSBob3N0Q29udGFpbmVySW5mbztcblxuICAgIHZhciBwcm9wcyA9IHRoaXMuX2N1cnJlbnRFbGVtZW50LnByb3BzO1xuXG4gICAgc3dpdGNoICh0aGlzLl90YWcpIHtcbiAgICAgIGNhc2UgJ2F1ZGlvJzpcbiAgICAgIGNhc2UgJ2Zvcm0nOlxuICAgICAgY2FzZSAnaWZyYW1lJzpcbiAgICAgIGNhc2UgJ2ltZyc6XG4gICAgICBjYXNlICdsaW5rJzpcbiAgICAgIGNhc2UgJ29iamVjdCc6XG4gICAgICBjYXNlICdzb3VyY2UnOlxuICAgICAgY2FzZSAndmlkZW8nOlxuICAgICAgICB0aGlzLl93cmFwcGVyU3RhdGUgPSB7XG4gICAgICAgICAgbGlzdGVuZXJzOiBudWxsXG4gICAgICAgIH07XG4gICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUodHJhcEJ1YmJsZWRFdmVudHNMb2NhbCwgdGhpcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnaW5wdXQnOlxuICAgICAgICBSZWFjdERPTUlucHV0Lm1vdW50V3JhcHBlcih0aGlzLCBwcm9wcywgaG9zdFBhcmVudCk7XG4gICAgICAgIHByb3BzID0gUmVhY3RET01JbnB1dC5nZXRIb3N0UHJvcHModGhpcywgcHJvcHMpO1xuICAgICAgICB0cmFuc2FjdGlvbi5nZXRSZWFjdE1vdW50UmVhZHkoKS5lbnF1ZXVlKHRyYXBCdWJibGVkRXZlbnRzTG9jYWwsIHRoaXMpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ29wdGlvbic6XG4gICAgICAgIFJlYWN0RE9NT3B0aW9uLm1vdW50V3JhcHBlcih0aGlzLCBwcm9wcywgaG9zdFBhcmVudCk7XG4gICAgICAgIHByb3BzID0gUmVhY3RET01PcHRpb24uZ2V0SG9zdFByb3BzKHRoaXMsIHByb3BzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdzZWxlY3QnOlxuICAgICAgICBSZWFjdERPTVNlbGVjdC5tb3VudFdyYXBwZXIodGhpcywgcHJvcHMsIGhvc3RQYXJlbnQpO1xuICAgICAgICBwcm9wcyA9IFJlYWN0RE9NU2VsZWN0LmdldEhvc3RQcm9wcyh0aGlzLCBwcm9wcyk7XG4gICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUodHJhcEJ1YmJsZWRFdmVudHNMb2NhbCwgdGhpcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGV4dGFyZWEnOlxuICAgICAgICBSZWFjdERPTVRleHRhcmVhLm1vdW50V3JhcHBlcih0aGlzLCBwcm9wcywgaG9zdFBhcmVudCk7XG4gICAgICAgIHByb3BzID0gUmVhY3RET01UZXh0YXJlYS5nZXRIb3N0UHJvcHModGhpcywgcHJvcHMpO1xuICAgICAgICB0cmFuc2FjdGlvbi5nZXRSZWFjdE1vdW50UmVhZHkoKS5lbnF1ZXVlKHRyYXBCdWJibGVkRXZlbnRzTG9jYWwsIHRoaXMpO1xuICAgICAgICBicmVhaztcbiAgICB9XG5cbiAgICBhc3NlcnRWYWxpZFByb3BzKHRoaXMsIHByb3BzKTtcblxuICAgIC8vIFdlIGNyZWF0ZSB0YWdzIGluIHRoZSBuYW1lc3BhY2Ugb2YgdGhlaXIgcGFyZW50IGNvbnRhaW5lciwgZXhjZXB0IEhUTUxcbiAgICAvLyB0YWdzIGdldCBubyBuYW1lc3BhY2UuXG4gICAgdmFyIG5hbWVzcGFjZVVSSTtcbiAgICB2YXIgcGFyZW50VGFnO1xuICAgIGlmIChob3N0UGFyZW50ICE9IG51bGwpIHtcbiAgICAgIG5hbWVzcGFjZVVSSSA9IGhvc3RQYXJlbnQuX25hbWVzcGFjZVVSSTtcbiAgICAgIHBhcmVudFRhZyA9IGhvc3RQYXJlbnQuX3RhZztcbiAgICB9IGVsc2UgaWYgKGhvc3RDb250YWluZXJJbmZvLl90YWcpIHtcbiAgICAgIG5hbWVzcGFjZVVSSSA9IGhvc3RDb250YWluZXJJbmZvLl9uYW1lc3BhY2VVUkk7XG4gICAgICBwYXJlbnRUYWcgPSBob3N0Q29udGFpbmVySW5mby5fdGFnO1xuICAgIH1cbiAgICBpZiAobmFtZXNwYWNlVVJJID09IG51bGwgfHwgbmFtZXNwYWNlVVJJID09PSBET01OYW1lc3BhY2VzLnN2ZyAmJiBwYXJlbnRUYWcgPT09ICdmb3JlaWdub2JqZWN0Jykge1xuICAgICAgbmFtZXNwYWNlVVJJID0gRE9NTmFtZXNwYWNlcy5odG1sO1xuICAgIH1cbiAgICBpZiAobmFtZXNwYWNlVVJJID09PSBET01OYW1lc3BhY2VzLmh0bWwpIHtcbiAgICAgIGlmICh0aGlzLl90YWcgPT09ICdzdmcnKSB7XG4gICAgICAgIG5hbWVzcGFjZVVSSSA9IERPTU5hbWVzcGFjZXMuc3ZnO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLl90YWcgPT09ICdtYXRoJykge1xuICAgICAgICBuYW1lc3BhY2VVUkkgPSBET01OYW1lc3BhY2VzLm1hdGhtbDtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5fbmFtZXNwYWNlVVJJID0gbmFtZXNwYWNlVVJJO1xuXG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgIHZhciBwYXJlbnRJbmZvO1xuICAgICAgaWYgKGhvc3RQYXJlbnQgIT0gbnVsbCkge1xuICAgICAgICBwYXJlbnRJbmZvID0gaG9zdFBhcmVudC5fYW5jZXN0b3JJbmZvO1xuICAgICAgfSBlbHNlIGlmIChob3N0Q29udGFpbmVySW5mby5fdGFnKSB7XG4gICAgICAgIHBhcmVudEluZm8gPSBob3N0Q29udGFpbmVySW5mby5fYW5jZXN0b3JJbmZvO1xuICAgICAgfVxuICAgICAgaWYgKHBhcmVudEluZm8pIHtcbiAgICAgICAgLy8gcGFyZW50SW5mbyBzaG91bGQgYWx3YXlzIGJlIHByZXNlbnQgZXhjZXB0IGZvciB0aGUgdG9wLWxldmVsXG4gICAgICAgIC8vIGNvbXBvbmVudCB3aGVuIHNlcnZlciByZW5kZXJpbmdcbiAgICAgICAgdmFsaWRhdGVET01OZXN0aW5nKHRoaXMuX3RhZywgbnVsbCwgdGhpcywgcGFyZW50SW5mbyk7XG4gICAgICB9XG4gICAgICB0aGlzLl9hbmNlc3RvckluZm8gPSB2YWxpZGF0ZURPTU5lc3RpbmcudXBkYXRlZEFuY2VzdG9ySW5mbyhwYXJlbnRJbmZvLCB0aGlzLl90YWcsIHRoaXMpO1xuICAgIH1cblxuICAgIHZhciBtb3VudEltYWdlO1xuICAgIGlmICh0cmFuc2FjdGlvbi51c2VDcmVhdGVFbGVtZW50KSB7XG4gICAgICB2YXIgb3duZXJEb2N1bWVudCA9IGhvc3RDb250YWluZXJJbmZvLl9vd25lckRvY3VtZW50O1xuICAgICAgdmFyIGVsO1xuICAgICAgaWYgKG5hbWVzcGFjZVVSSSA9PT0gRE9NTmFtZXNwYWNlcy5odG1sKSB7XG4gICAgICAgIGlmICh0aGlzLl90YWcgPT09ICdzY3JpcHQnKSB7XG4gICAgICAgICAgLy8gQ3JlYXRlIHRoZSBzY3JpcHQgdmlhIC5pbm5lckhUTUwgc28gaXRzIFwicGFyc2VyLWluc2VydGVkXCIgZmxhZyBpc1xuICAgICAgICAgIC8vIHNldCB0byB0cnVlIGFuZCBpdCBkb2VzIG5vdCBleGVjdXRlXG4gICAgICAgICAgdmFyIGRpdiA9IG93bmVyRG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgICAgdmFyIHR5cGUgPSB0aGlzLl9jdXJyZW50RWxlbWVudC50eXBlO1xuICAgICAgICAgIGRpdi5pbm5lckhUTUwgPSAnPCcgKyB0eXBlICsgJz48LycgKyB0eXBlICsgJz4nO1xuICAgICAgICAgIGVsID0gZGl2LnJlbW92ZUNoaWxkKGRpdi5maXJzdENoaWxkKTtcbiAgICAgICAgfSBlbHNlIGlmIChwcm9wcy5pcykge1xuICAgICAgICAgIGVsID0gb3duZXJEb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuX2N1cnJlbnRFbGVtZW50LnR5cGUsIHByb3BzLmlzKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBTZXBhcmF0ZSBlbHNlIGJyYW5jaCBpbnN0ZWFkIG9mIHVzaW5nIGBwcm9wcy5pcyB8fCB1bmRlZmluZWRgIGFib3ZlIGJlY3Vhc2Ugb2YgYSBGaXJlZm94IGJ1Zy5cbiAgICAgICAgICAvLyBTZWUgZGlzY3Vzc2lvbiBpbiBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2svcmVhY3QvcHVsbC82ODk2XG4gICAgICAgICAgLy8gYW5kIGRpc2N1c3Npb24gaW4gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTI3NjI0MFxuICAgICAgICAgIGVsID0gb3duZXJEb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuX2N1cnJlbnRFbGVtZW50LnR5cGUpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBlbCA9IG93bmVyRG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKG5hbWVzcGFjZVVSSSwgdGhpcy5fY3VycmVudEVsZW1lbnQudHlwZSk7XG4gICAgICB9XG4gICAgICBSZWFjdERPTUNvbXBvbmVudFRyZWUucHJlY2FjaGVOb2RlKHRoaXMsIGVsKTtcbiAgICAgIHRoaXMuX2ZsYWdzIHw9IEZsYWdzLmhhc0NhY2hlZENoaWxkTm9kZXM7XG4gICAgICBpZiAoIXRoaXMuX2hvc3RQYXJlbnQpIHtcbiAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLnNldEF0dHJpYnV0ZUZvclJvb3QoZWwpO1xuICAgICAgfVxuICAgICAgdGhpcy5fdXBkYXRlRE9NUHJvcGVydGllcyhudWxsLCBwcm9wcywgdHJhbnNhY3Rpb24pO1xuICAgICAgdmFyIGxhenlUcmVlID0gRE9NTGF6eVRyZWUoZWwpO1xuICAgICAgdGhpcy5fY3JlYXRlSW5pdGlhbENoaWxkcmVuKHRyYW5zYWN0aW9uLCBwcm9wcywgY29udGV4dCwgbGF6eVRyZWUpO1xuICAgICAgbW91bnRJbWFnZSA9IGxhenlUcmVlO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgdGFnT3BlbiA9IHRoaXMuX2NyZWF0ZU9wZW5UYWdNYXJrdXBBbmRQdXRMaXN0ZW5lcnModHJhbnNhY3Rpb24sIHByb3BzKTtcbiAgICAgIHZhciB0YWdDb250ZW50ID0gdGhpcy5fY3JlYXRlQ29udGVudE1hcmt1cCh0cmFuc2FjdGlvbiwgcHJvcHMsIGNvbnRleHQpO1xuICAgICAgaWYgKCF0YWdDb250ZW50ICYmIG9taXR0ZWRDbG9zZVRhZ3NbdGhpcy5fdGFnXSkge1xuICAgICAgICBtb3VudEltYWdlID0gdGFnT3BlbiArICcvPic7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBtb3VudEltYWdlID0gdGFnT3BlbiArICc+JyArIHRhZ0NvbnRlbnQgKyAnPC8nICsgdGhpcy5fY3VycmVudEVsZW1lbnQudHlwZSArICc+JztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBzd2l0Y2ggKHRoaXMuX3RhZykge1xuICAgICAgY2FzZSAnaW5wdXQnOlxuICAgICAgICB0cmFuc2FjdGlvbi5nZXRSZWFjdE1vdW50UmVhZHkoKS5lbnF1ZXVlKGlucHV0UG9zdE1vdW50LCB0aGlzKTtcbiAgICAgICAgaWYgKHByb3BzLmF1dG9Gb2N1cykge1xuICAgICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoQXV0b0ZvY3VzVXRpbHMuZm9jdXNET01Db21wb25lbnQsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGV4dGFyZWEnOlxuICAgICAgICB0cmFuc2FjdGlvbi5nZXRSZWFjdE1vdW50UmVhZHkoKS5lbnF1ZXVlKHRleHRhcmVhUG9zdE1vdW50LCB0aGlzKTtcbiAgICAgICAgaWYgKHByb3BzLmF1dG9Gb2N1cykge1xuICAgICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoQXV0b0ZvY3VzVXRpbHMuZm9jdXNET01Db21wb25lbnQsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnc2VsZWN0JzpcbiAgICAgICAgaWYgKHByb3BzLmF1dG9Gb2N1cykge1xuICAgICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoQXV0b0ZvY3VzVXRpbHMuZm9jdXNET01Db21wb25lbnQsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnYnV0dG9uJzpcbiAgICAgICAgaWYgKHByb3BzLmF1dG9Gb2N1cykge1xuICAgICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoQXV0b0ZvY3VzVXRpbHMuZm9jdXNET01Db21wb25lbnQsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnb3B0aW9uJzpcbiAgICAgICAgdHJhbnNhY3Rpb24uZ2V0UmVhY3RNb3VudFJlYWR5KCkuZW5xdWV1ZShvcHRpb25Qb3N0TW91bnQsIHRoaXMpO1xuICAgICAgICBicmVhaztcbiAgICB9XG5cbiAgICByZXR1cm4gbW91bnRJbWFnZTtcbiAgfSxcblxuICAvKipcbiAgICogQ3JlYXRlcyBtYXJrdXAgZm9yIHRoZSBvcGVuIHRhZyBhbmQgYWxsIGF0dHJpYnV0ZXMuXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGhhcyBzaWRlIGVmZmVjdHMgYmVjYXVzZSBldmVudHMgZ2V0IHJlZ2lzdGVyZWQuXG4gICAqXG4gICAqIEl0ZXJhdGluZyBvdmVyIG9iamVjdCBwcm9wZXJ0aWVzIGlzIGZhc3RlciB0aGFuIGl0ZXJhdGluZyBvdmVyIGFycmF5cy5cbiAgICogQHNlZSBodHRwOi8vanNwZXJmLmNvbS9vYmotdnMtYXJyLWl0ZXJhdGlvblxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb258UmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtvYmplY3R9IHByb3BzXG4gICAqIEByZXR1cm4ge3N0cmluZ30gTWFya3VwIG9mIG9wZW5pbmcgdGFnLlxuICAgKi9cbiAgX2NyZWF0ZU9wZW5UYWdNYXJrdXBBbmRQdXRMaXN0ZW5lcnM6IGZ1bmN0aW9uICh0cmFuc2FjdGlvbiwgcHJvcHMpIHtcbiAgICB2YXIgcmV0ID0gJzwnICsgdGhpcy5fY3VycmVudEVsZW1lbnQudHlwZTtcblxuICAgIGZvciAodmFyIHByb3BLZXkgaW4gcHJvcHMpIHtcbiAgICAgIGlmICghcHJvcHMuaGFzT3duUHJvcGVydHkocHJvcEtleSkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICB2YXIgcHJvcFZhbHVlID0gcHJvcHNbcHJvcEtleV07XG4gICAgICBpZiAocHJvcFZhbHVlID09IG51bGwpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAocmVnaXN0cmF0aW9uTmFtZU1vZHVsZXMuaGFzT3duUHJvcGVydHkocHJvcEtleSkpIHtcbiAgICAgICAgaWYgKHByb3BWYWx1ZSkge1xuICAgICAgICAgIGVucXVldWVQdXRMaXN0ZW5lcih0aGlzLCBwcm9wS2V5LCBwcm9wVmFsdWUsIHRyYW5zYWN0aW9uKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHByb3BLZXkgPT09IFNUWUxFKSB7XG4gICAgICAgICAgaWYgKHByb3BWYWx1ZSkge1xuICAgICAgICAgICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgICAgICAgICAgLy8gU2VlIGBfdXBkYXRlRE9NUHJvcGVydGllc2AuIHN0eWxlIGJsb2NrXG4gICAgICAgICAgICAgIHRoaXMuX3ByZXZpb3VzU3R5bGUgPSBwcm9wVmFsdWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBwcm9wVmFsdWUgPSB0aGlzLl9wcmV2aW91c1N0eWxlQ29weSA9IF9hc3NpZ24oe30sIHByb3BzLnN0eWxlKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcHJvcFZhbHVlID0gQ1NTUHJvcGVydHlPcGVyYXRpb25zLmNyZWF0ZU1hcmt1cEZvclN0eWxlcyhwcm9wVmFsdWUsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBtYXJrdXAgPSBudWxsO1xuICAgICAgICBpZiAodGhpcy5fdGFnICE9IG51bGwgJiYgaXNDdXN0b21Db21wb25lbnQodGhpcy5fdGFnLCBwcm9wcykpIHtcbiAgICAgICAgICBpZiAoIVJFU0VSVkVEX1BST1BTLmhhc093blByb3BlcnR5KHByb3BLZXkpKSB7XG4gICAgICAgICAgICBtYXJrdXAgPSBET01Qcm9wZXJ0eU9wZXJhdGlvbnMuY3JlYXRlTWFya3VwRm9yQ3VzdG9tQXR0cmlidXRlKHByb3BLZXksIHByb3BWYWx1ZSk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG1hcmt1cCA9IERPTVByb3BlcnR5T3BlcmF0aW9ucy5jcmVhdGVNYXJrdXBGb3JQcm9wZXJ0eShwcm9wS2V5LCBwcm9wVmFsdWUpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChtYXJrdXApIHtcbiAgICAgICAgICByZXQgKz0gJyAnICsgbWFya3VwO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRm9yIHN0YXRpYyBwYWdlcywgbm8gbmVlZCB0byBwdXQgUmVhY3QgSUQgYW5kIGNoZWNrc3VtLiBTYXZlcyBsb3RzIG9mXG4gICAgLy8gYnl0ZXMuXG4gICAgaWYgKHRyYW5zYWN0aW9uLnJlbmRlclRvU3RhdGljTWFya3VwKSB7XG4gICAgICByZXR1cm4gcmV0O1xuICAgIH1cblxuICAgIGlmICghdGhpcy5faG9zdFBhcmVudCkge1xuICAgICAgcmV0ICs9ICcgJyArIERPTVByb3BlcnR5T3BlcmF0aW9ucy5jcmVhdGVNYXJrdXBGb3JSb290KCk7XG4gICAgfVxuICAgIHJldCArPSAnICcgKyBET01Qcm9wZXJ0eU9wZXJhdGlvbnMuY3JlYXRlTWFya3VwRm9ySUQodGhpcy5fZG9tSUQpO1xuICAgIHJldHVybiByZXQ7XG4gIH0sXG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgbWFya3VwIGZvciB0aGUgY29udGVudCBiZXR3ZWVuIHRoZSB0YWdzLlxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb258UmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtvYmplY3R9IHByb3BzXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBjb250ZXh0XG4gICAqIEByZXR1cm4ge3N0cmluZ30gQ29udGVudCBtYXJrdXAuXG4gICAqL1xuICBfY3JlYXRlQ29udGVudE1hcmt1cDogZnVuY3Rpb24gKHRyYW5zYWN0aW9uLCBwcm9wcywgY29udGV4dCkge1xuICAgIHZhciByZXQgPSAnJztcblxuICAgIC8vIEludGVudGlvbmFsIHVzZSBvZiAhPSB0byBhdm9pZCBjYXRjaGluZyB6ZXJvL2ZhbHNlLlxuICAgIHZhciBpbm5lckhUTUwgPSBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTDtcbiAgICBpZiAoaW5uZXJIVE1MICE9IG51bGwpIHtcbiAgICAgIGlmIChpbm5lckhUTUwuX19odG1sICE9IG51bGwpIHtcbiAgICAgICAgcmV0ID0gaW5uZXJIVE1MLl9faHRtbDtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIGNvbnRlbnRUb1VzZSA9IENPTlRFTlRfVFlQRVNbdHlwZW9mIHByb3BzLmNoaWxkcmVuXSA/IHByb3BzLmNoaWxkcmVuIDogbnVsbDtcbiAgICAgIHZhciBjaGlsZHJlblRvVXNlID0gY29udGVudFRvVXNlICE9IG51bGwgPyBudWxsIDogcHJvcHMuY2hpbGRyZW47XG4gICAgICBpZiAoY29udGVudFRvVXNlICE9IG51bGwpIHtcbiAgICAgICAgLy8gVE9ETzogVmFsaWRhdGUgdGhhdCB0ZXh0IGlzIGFsbG93ZWQgYXMgYSBjaGlsZCBvZiB0aGlzIG5vZGVcbiAgICAgICAgcmV0ID0gZXNjYXBlVGV4dENvbnRlbnRGb3JCcm93c2VyKGNvbnRlbnRUb1VzZSk7XG4gICAgICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICAgICAgc2V0QW5kVmFsaWRhdGVDb250ZW50Q2hpbGREZXYuY2FsbCh0aGlzLCBjb250ZW50VG9Vc2UpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGNoaWxkcmVuVG9Vc2UgIT0gbnVsbCkge1xuICAgICAgICB2YXIgbW91bnRJbWFnZXMgPSB0aGlzLm1vdW50Q2hpbGRyZW4oY2hpbGRyZW5Ub1VzZSwgdHJhbnNhY3Rpb24sIGNvbnRleHQpO1xuICAgICAgICByZXQgPSBtb3VudEltYWdlcy5qb2luKCcnKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKG5ld2xpbmVFYXRpbmdUYWdzW3RoaXMuX3RhZ10gJiYgcmV0LmNoYXJBdCgwKSA9PT0gJ1xcbicpIHtcbiAgICAgIC8vIHRleHQvaHRtbCBpZ25vcmVzIHRoZSBmaXJzdCBjaGFyYWN0ZXIgaW4gdGhlc2UgdGFncyBpZiBpdCdzIGEgbmV3bGluZVxuICAgICAgLy8gUHJlZmVyIHRvIGJyZWFrIGFwcGxpY2F0aW9uL3htbCBvdmVyIHRleHQvaHRtbCAoZm9yIG5vdykgYnkgYWRkaW5nXG4gICAgICAvLyBhIG5ld2xpbmUgc3BlY2lmaWNhbGx5IHRvIGdldCBlYXRlbiBieSB0aGUgcGFyc2VyLiAoQWx0ZXJuYXRlbHkgZm9yXG4gICAgICAvLyB0ZXh0YXJlYXMsIHJlcGxhY2luZyBcIl5cXG5cIiB3aXRoIFwiXFxyXFxuXCIgZG9lc24ndCBnZXQgZWF0ZW4sIGFuZCB0aGUgZmlyc3RcbiAgICAgIC8vIFxcciBpcyBub3JtYWxpemVkIG91dCBieSBIVE1MVGV4dEFyZWFFbGVtZW50I3ZhbHVlLilcbiAgICAgIC8vIFNlZTogPGh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWwtcG9seWdsb3QvI25ld2xpbmVzLWluLXRleHRhcmVhLWFuZC1wcmU+XG4gICAgICAvLyBTZWU6IDxodHRwOi8vd3d3LnczLm9yZy9UUi9odG1sNS9zeW50YXguaHRtbCNlbGVtZW50LXJlc3RyaWN0aW9ucz5cbiAgICAgIC8vIFNlZTogPGh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWw1L3N5bnRheC5odG1sI25ld2xpbmVzPlxuICAgICAgLy8gU2VlOiBQYXJzaW5nIG9mIFwidGV4dGFyZWFcIiBcImxpc3RpbmdcIiBhbmQgXCJwcmVcIiBlbGVtZW50c1xuICAgICAgLy8gIGZyb20gPGh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWw1L3N5bnRheC5odG1sI3BhcnNpbmctbWFpbi1pbmJvZHk+XG4gICAgICByZXR1cm4gJ1xcbicgKyByZXQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiByZXQ7XG4gICAgfVxuICB9LFxuXG4gIF9jcmVhdGVJbml0aWFsQ2hpbGRyZW46IGZ1bmN0aW9uICh0cmFuc2FjdGlvbiwgcHJvcHMsIGNvbnRleHQsIGxhenlUcmVlKSB7XG4gICAgLy8gSW50ZW50aW9uYWwgdXNlIG9mICE9IHRvIGF2b2lkIGNhdGNoaW5nIHplcm8vZmFsc2UuXG4gICAgdmFyIGlubmVySFRNTCA9IHByb3BzLmRhbmdlcm91c2x5U2V0SW5uZXJIVE1MO1xuICAgIGlmIChpbm5lckhUTUwgIT0gbnVsbCkge1xuICAgICAgaWYgKGlubmVySFRNTC5fX2h0bWwgIT0gbnVsbCkge1xuICAgICAgICBET01MYXp5VHJlZS5xdWV1ZUhUTUwobGF6eVRyZWUsIGlubmVySFRNTC5fX2h0bWwpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgY29udGVudFRvVXNlID0gQ09OVEVOVF9UWVBFU1t0eXBlb2YgcHJvcHMuY2hpbGRyZW5dID8gcHJvcHMuY2hpbGRyZW4gOiBudWxsO1xuICAgICAgdmFyIGNoaWxkcmVuVG9Vc2UgPSBjb250ZW50VG9Vc2UgIT0gbnVsbCA/IG51bGwgOiBwcm9wcy5jaGlsZHJlbjtcbiAgICAgIC8vIFRPRE86IFZhbGlkYXRlIHRoYXQgdGV4dCBpcyBhbGxvd2VkIGFzIGEgY2hpbGQgb2YgdGhpcyBub2RlXG4gICAgICBpZiAoY29udGVudFRvVXNlICE9IG51bGwpIHtcbiAgICAgICAgLy8gQXZvaWQgc2V0dGluZyB0ZXh0Q29udGVudCB3aGVuIHRoZSB0ZXh0IGlzIGVtcHR5LiBJbiBJRTExIHNldHRpbmdcbiAgICAgICAgLy8gdGV4dENvbnRlbnQgb24gYSB0ZXh0IGFyZWEgd2lsbCBjYXVzZSB0aGUgcGxhY2Vob2xkZXIgdG8gbm90XG4gICAgICAgIC8vIHNob3cgd2l0aGluIHRoZSB0ZXh0YXJlYSB1bnRpbCBpdCBoYXMgYmVlbiBmb2N1c2VkIGFuZCBibHVycmVkIGFnYWluLlxuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2svcmVhY3QvaXNzdWVzLzY3MzEjaXNzdWVjb21tZW50LTI1NDg3NDU1M1xuICAgICAgICBpZiAoY29udGVudFRvVXNlICE9PSAnJykge1xuICAgICAgICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICAgICAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIGNvbnRlbnRUb1VzZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIERPTUxhenlUcmVlLnF1ZXVlVGV4dChsYXp5VHJlZSwgY29udGVudFRvVXNlKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChjaGlsZHJlblRvVXNlICE9IG51bGwpIHtcbiAgICAgICAgdmFyIG1vdW50SW1hZ2VzID0gdGhpcy5tb3VudENoaWxkcmVuKGNoaWxkcmVuVG9Vc2UsIHRyYW5zYWN0aW9uLCBjb250ZXh0KTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBtb3VudEltYWdlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIERPTUxhenlUcmVlLnF1ZXVlQ2hpbGQobGF6eVRyZWUsIG1vdW50SW1hZ2VzW2ldKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICAvKipcbiAgICogUmVjZWl2ZXMgYSBuZXh0IGVsZW1lbnQgYW5kIHVwZGF0ZXMgdGhlIGNvbXBvbmVudC5cbiAgICpcbiAgICogQGludGVybmFsXG4gICAqIEBwYXJhbSB7UmVhY3RFbGVtZW50fSBuZXh0RWxlbWVudFxuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb258UmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtvYmplY3R9IGNvbnRleHRcbiAgICovXG4gIHJlY2VpdmVDb21wb25lbnQ6IGZ1bmN0aW9uIChuZXh0RWxlbWVudCwgdHJhbnNhY3Rpb24sIGNvbnRleHQpIHtcbiAgICB2YXIgcHJldkVsZW1lbnQgPSB0aGlzLl9jdXJyZW50RWxlbWVudDtcbiAgICB0aGlzLl9jdXJyZW50RWxlbWVudCA9IG5leHRFbGVtZW50O1xuICAgIHRoaXMudXBkYXRlQ29tcG9uZW50KHRyYW5zYWN0aW9uLCBwcmV2RWxlbWVudCwgbmV4dEVsZW1lbnQsIGNvbnRleHQpO1xuICB9LFxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIGEgRE9NIGNvbXBvbmVudCBhZnRlciBpdCBoYXMgYWxyZWFkeSBiZWVuIGFsbG9jYXRlZCBhbmRcbiAgICogYXR0YWNoZWQgdG8gdGhlIERPTS4gUmVjb25jaWxlcyB0aGUgcm9vdCBET00gbm9kZSwgdGhlbiByZWN1cnNlcy5cbiAgICpcbiAgICogQHBhcmFtIHtSZWFjdFJlY29uY2lsZVRyYW5zYWN0aW9ufSB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge1JlYWN0RWxlbWVudH0gcHJldkVsZW1lbnRcbiAgICogQHBhcmFtIHtSZWFjdEVsZW1lbnR9IG5leHRFbGVtZW50XG4gICAqIEBpbnRlcm5hbFxuICAgKiBAb3ZlcnJpZGFibGVcbiAgICovXG4gIHVwZGF0ZUNvbXBvbmVudDogZnVuY3Rpb24gKHRyYW5zYWN0aW9uLCBwcmV2RWxlbWVudCwgbmV4dEVsZW1lbnQsIGNvbnRleHQpIHtcbiAgICB2YXIgbGFzdFByb3BzID0gcHJldkVsZW1lbnQucHJvcHM7XG4gICAgdmFyIG5leHRQcm9wcyA9IHRoaXMuX2N1cnJlbnRFbGVtZW50LnByb3BzO1xuXG4gICAgc3dpdGNoICh0aGlzLl90YWcpIHtcbiAgICAgIGNhc2UgJ2lucHV0JzpcbiAgICAgICAgbGFzdFByb3BzID0gUmVhY3RET01JbnB1dC5nZXRIb3N0UHJvcHModGhpcywgbGFzdFByb3BzKTtcbiAgICAgICAgbmV4dFByb3BzID0gUmVhY3RET01JbnB1dC5nZXRIb3N0UHJvcHModGhpcywgbmV4dFByb3BzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdvcHRpb24nOlxuICAgICAgICBsYXN0UHJvcHMgPSBSZWFjdERPTU9wdGlvbi5nZXRIb3N0UHJvcHModGhpcywgbGFzdFByb3BzKTtcbiAgICAgICAgbmV4dFByb3BzID0gUmVhY3RET01PcHRpb24uZ2V0SG9zdFByb3BzKHRoaXMsIG5leHRQcm9wcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnc2VsZWN0JzpcbiAgICAgICAgbGFzdFByb3BzID0gUmVhY3RET01TZWxlY3QuZ2V0SG9zdFByb3BzKHRoaXMsIGxhc3RQcm9wcyk7XG4gICAgICAgIG5leHRQcm9wcyA9IFJlYWN0RE9NU2VsZWN0LmdldEhvc3RQcm9wcyh0aGlzLCBuZXh0UHJvcHMpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3RleHRhcmVhJzpcbiAgICAgICAgbGFzdFByb3BzID0gUmVhY3RET01UZXh0YXJlYS5nZXRIb3N0UHJvcHModGhpcywgbGFzdFByb3BzKTtcbiAgICAgICAgbmV4dFByb3BzID0gUmVhY3RET01UZXh0YXJlYS5nZXRIb3N0UHJvcHModGhpcywgbmV4dFByb3BzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgYXNzZXJ0VmFsaWRQcm9wcyh0aGlzLCBuZXh0UHJvcHMpO1xuICAgIHRoaXMuX3VwZGF0ZURPTVByb3BlcnRpZXMobGFzdFByb3BzLCBuZXh0UHJvcHMsIHRyYW5zYWN0aW9uKTtcbiAgICB0aGlzLl91cGRhdGVET01DaGlsZHJlbihsYXN0UHJvcHMsIG5leHRQcm9wcywgdHJhbnNhY3Rpb24sIGNvbnRleHQpO1xuXG4gICAgc3dpdGNoICh0aGlzLl90YWcpIHtcbiAgICAgIGNhc2UgJ2lucHV0JzpcbiAgICAgICAgLy8gVXBkYXRlIHRoZSB3cmFwcGVyIGFyb3VuZCBpbnB1dHMgKmFmdGVyKiB1cGRhdGluZyBwcm9wcy4gVGhpcyBoYXMgdG9cbiAgICAgICAgLy8gaGFwcGVuIGFmdGVyIGBfdXBkYXRlRE9NUHJvcGVydGllc2AuIE90aGVyd2lzZSBIVE1MNSBpbnB1dCB2YWxpZGF0aW9uc1xuICAgICAgICAvLyByYWlzZSB3YXJuaW5ncyBhbmQgcHJldmVudCB0aGUgbmV3IHZhbHVlIGZyb20gYmVpbmcgYXNzaWduZWQuXG4gICAgICAgIFJlYWN0RE9NSW5wdXQudXBkYXRlV3JhcHBlcih0aGlzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd0ZXh0YXJlYSc6XG4gICAgICAgIFJlYWN0RE9NVGV4dGFyZWEudXBkYXRlV3JhcHBlcih0aGlzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdzZWxlY3QnOlxuICAgICAgICAvLyA8c2VsZWN0PiB2YWx1ZSB1cGRhdGUgbmVlZHMgdG8gb2NjdXIgYWZ0ZXIgPG9wdGlvbj4gY2hpbGRyZW5cbiAgICAgICAgLy8gcmVjb25jaWxpYXRpb25cbiAgICAgICAgdHJhbnNhY3Rpb24uZ2V0UmVhY3RNb3VudFJlYWR5KCkuZW5xdWV1ZShwb3N0VXBkYXRlU2VsZWN0V3JhcHBlciwgdGhpcyk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfSxcblxuICAvKipcbiAgICogUmVjb25jaWxlcyB0aGUgcHJvcGVydGllcyBieSBkZXRlY3RpbmcgZGlmZmVyZW5jZXMgaW4gcHJvcGVydHkgdmFsdWVzIGFuZFxuICAgKiB1cGRhdGluZyB0aGUgRE9NIGFzIG5lY2Vzc2FyeS4gVGhpcyBmdW5jdGlvbiBpcyBwcm9iYWJseSB0aGUgc2luZ2xlIG1vc3RcbiAgICogY3JpdGljYWwgcGF0aCBmb3IgcGVyZm9ybWFuY2Ugb3B0aW1pemF0aW9uLlxuICAgKlxuICAgKiBUT0RPOiBCZW5jaG1hcmsgd2hldGhlciBjaGVja2luZyBmb3IgY2hhbmdlZCB2YWx1ZXMgaW4gbWVtb3J5IGFjdHVhbGx5XG4gICAqICAgICAgIGltcHJvdmVzIHBlcmZvcm1hbmNlIChlc3BlY2lhbGx5IHN0YXRpY2FsbHkgcG9zaXRpb25lZCBlbGVtZW50cykuXG4gICAqIFRPRE86IEJlbmNobWFyayB0aGUgZWZmZWN0cyBvZiBwdXR0aW5nIHRoaXMgYXQgdGhlIHRvcCBzaW5jZSA5OSUgb2YgcHJvcHNcbiAgICogICAgICAgZG8gbm90IGNoYW5nZSBmb3IgYSBnaXZlbiByZWNvbmNpbGlhdGlvbi5cbiAgICogVE9ETzogQmVuY2htYXJrIGFyZWFzIHRoYXQgY2FuIGJlIGltcHJvdmVkIHdpdGggY2FjaGluZy5cbiAgICpcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtvYmplY3R9IGxhc3RQcm9wc1xuICAgKiBAcGFyYW0ge29iamVjdH0gbmV4dFByb3BzXG4gICAqIEBwYXJhbSB7P0RPTUVsZW1lbnR9IG5vZGVcbiAgICovXG4gIF91cGRhdGVET01Qcm9wZXJ0aWVzOiBmdW5jdGlvbiAobGFzdFByb3BzLCBuZXh0UHJvcHMsIHRyYW5zYWN0aW9uKSB7XG4gICAgdmFyIHByb3BLZXk7XG4gICAgdmFyIHN0eWxlTmFtZTtcbiAgICB2YXIgc3R5bGVVcGRhdGVzO1xuICAgIGZvciAocHJvcEtleSBpbiBsYXN0UHJvcHMpIHtcbiAgICAgIGlmIChuZXh0UHJvcHMuaGFzT3duUHJvcGVydHkocHJvcEtleSkgfHwgIWxhc3RQcm9wcy5oYXNPd25Qcm9wZXJ0eShwcm9wS2V5KSB8fCBsYXN0UHJvcHNbcHJvcEtleV0gPT0gbnVsbCkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmIChwcm9wS2V5ID09PSBTVFlMRSkge1xuICAgICAgICB2YXIgbGFzdFN0eWxlID0gdGhpcy5fcHJldmlvdXNTdHlsZUNvcHk7XG4gICAgICAgIGZvciAoc3R5bGVOYW1lIGluIGxhc3RTdHlsZSkge1xuICAgICAgICAgIGlmIChsYXN0U3R5bGUuaGFzT3duUHJvcGVydHkoc3R5bGVOYW1lKSkge1xuICAgICAgICAgICAgc3R5bGVVcGRhdGVzID0gc3R5bGVVcGRhdGVzIHx8IHt9O1xuICAgICAgICAgICAgc3R5bGVVcGRhdGVzW3N0eWxlTmFtZV0gPSAnJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fcHJldmlvdXNTdHlsZUNvcHkgPSBudWxsO1xuICAgICAgfSBlbHNlIGlmIChyZWdpc3RyYXRpb25OYW1lTW9kdWxlcy5oYXNPd25Qcm9wZXJ0eShwcm9wS2V5KSkge1xuICAgICAgICBpZiAobGFzdFByb3BzW3Byb3BLZXldKSB7XG4gICAgICAgICAgLy8gT25seSBjYWxsIGRlbGV0ZUxpc3RlbmVyIGlmIHRoZXJlIHdhcyBhIGxpc3RlbmVyIHByZXZpb3VzbHkgb3JcbiAgICAgICAgICAvLyBlbHNlIHdpbGxEZWxldGVMaXN0ZW5lciBnZXRzIGNhbGxlZCB3aGVuIHRoZXJlIHdhc24ndCBhY3R1YWxseSBhXG4gICAgICAgICAgLy8gbGlzdGVuZXIgKGUuZy4sIG9uQ2xpY2s9e251bGx9KVxuICAgICAgICAgIGRlbGV0ZUxpc3RlbmVyKHRoaXMsIHByb3BLZXkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGlzQ3VzdG9tQ29tcG9uZW50KHRoaXMuX3RhZywgbGFzdFByb3BzKSkge1xuICAgICAgICBpZiAoIVJFU0VSVkVEX1BST1BTLmhhc093blByb3BlcnR5KHByb3BLZXkpKSB7XG4gICAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLmRlbGV0ZVZhbHVlRm9yQXR0cmlidXRlKGdldE5vZGUodGhpcyksIHByb3BLZXkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKERPTVByb3BlcnR5LnByb3BlcnRpZXNbcHJvcEtleV0gfHwgRE9NUHJvcGVydHkuaXNDdXN0b21BdHRyaWJ1dGUocHJvcEtleSkpIHtcbiAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLmRlbGV0ZVZhbHVlRm9yUHJvcGVydHkoZ2V0Tm9kZSh0aGlzKSwgcHJvcEtleSk7XG4gICAgICB9XG4gICAgfVxuICAgIGZvciAocHJvcEtleSBpbiBuZXh0UHJvcHMpIHtcbiAgICAgIHZhciBuZXh0UHJvcCA9IG5leHRQcm9wc1twcm9wS2V5XTtcbiAgICAgIHZhciBsYXN0UHJvcCA9IHByb3BLZXkgPT09IFNUWUxFID8gdGhpcy5fcHJldmlvdXNTdHlsZUNvcHkgOiBsYXN0UHJvcHMgIT0gbnVsbCA/IGxhc3RQcm9wc1twcm9wS2V5XSA6IHVuZGVmaW5lZDtcbiAgICAgIGlmICghbmV4dFByb3BzLmhhc093blByb3BlcnR5KHByb3BLZXkpIHx8IG5leHRQcm9wID09PSBsYXN0UHJvcCB8fCBuZXh0UHJvcCA9PSBudWxsICYmIGxhc3RQcm9wID09IG51bGwpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAocHJvcEtleSA9PT0gU1RZTEUpIHtcbiAgICAgICAgaWYgKG5leHRQcm9wKSB7XG4gICAgICAgICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgICAgICAgIGNoZWNrQW5kV2FybkZvck11dGF0ZWRTdHlsZSh0aGlzLl9wcmV2aW91c1N0eWxlQ29weSwgdGhpcy5fcHJldmlvdXNTdHlsZSwgdGhpcyk7XG4gICAgICAgICAgICB0aGlzLl9wcmV2aW91c1N0eWxlID0gbmV4dFByb3A7XG4gICAgICAgICAgfVxuICAgICAgICAgIG5leHRQcm9wID0gdGhpcy5fcHJldmlvdXNTdHlsZUNvcHkgPSBfYXNzaWduKHt9LCBuZXh0UHJvcCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5fcHJldmlvdXNTdHlsZUNvcHkgPSBudWxsO1xuICAgICAgICB9XG4gICAgICAgIGlmIChsYXN0UHJvcCkge1xuICAgICAgICAgIC8vIFVuc2V0IHN0eWxlcyBvbiBgbGFzdFByb3BgIGJ1dCBub3Qgb24gYG5leHRQcm9wYC5cbiAgICAgICAgICBmb3IgKHN0eWxlTmFtZSBpbiBsYXN0UHJvcCkge1xuICAgICAgICAgICAgaWYgKGxhc3RQcm9wLmhhc093blByb3BlcnR5KHN0eWxlTmFtZSkgJiYgKCFuZXh0UHJvcCB8fCAhbmV4dFByb3AuaGFzT3duUHJvcGVydHkoc3R5bGVOYW1lKSkpIHtcbiAgICAgICAgICAgICAgc3R5bGVVcGRhdGVzID0gc3R5bGVVcGRhdGVzIHx8IHt9O1xuICAgICAgICAgICAgICBzdHlsZVVwZGF0ZXNbc3R5bGVOYW1lXSA9ICcnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBVcGRhdGUgc3R5bGVzIHRoYXQgY2hhbmdlZCBzaW5jZSBgbGFzdFByb3BgLlxuICAgICAgICAgIGZvciAoc3R5bGVOYW1lIGluIG5leHRQcm9wKSB7XG4gICAgICAgICAgICBpZiAobmV4dFByb3AuaGFzT3duUHJvcGVydHkoc3R5bGVOYW1lKSAmJiBsYXN0UHJvcFtzdHlsZU5hbWVdICE9PSBuZXh0UHJvcFtzdHlsZU5hbWVdKSB7XG4gICAgICAgICAgICAgIHN0eWxlVXBkYXRlcyA9IHN0eWxlVXBkYXRlcyB8fCB7fTtcbiAgICAgICAgICAgICAgc3R5bGVVcGRhdGVzW3N0eWxlTmFtZV0gPSBuZXh0UHJvcFtzdHlsZU5hbWVdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBSZWxpZXMgb24gYHVwZGF0ZVN0eWxlc0J5SURgIG5vdCBtdXRhdGluZyBgc3R5bGVVcGRhdGVzYC5cbiAgICAgICAgICBzdHlsZVVwZGF0ZXMgPSBuZXh0UHJvcDtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChyZWdpc3RyYXRpb25OYW1lTW9kdWxlcy5oYXNPd25Qcm9wZXJ0eShwcm9wS2V5KSkge1xuICAgICAgICBpZiAobmV4dFByb3ApIHtcbiAgICAgICAgICBlbnF1ZXVlUHV0TGlzdGVuZXIodGhpcywgcHJvcEtleSwgbmV4dFByb3AsIHRyYW5zYWN0aW9uKTtcbiAgICAgICAgfSBlbHNlIGlmIChsYXN0UHJvcCkge1xuICAgICAgICAgIGRlbGV0ZUxpc3RlbmVyKHRoaXMsIHByb3BLZXkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGlzQ3VzdG9tQ29tcG9uZW50KHRoaXMuX3RhZywgbmV4dFByb3BzKSkge1xuICAgICAgICBpZiAoIVJFU0VSVkVEX1BST1BTLmhhc093blByb3BlcnR5KHByb3BLZXkpKSB7XG4gICAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLnNldFZhbHVlRm9yQXR0cmlidXRlKGdldE5vZGUodGhpcyksIHByb3BLZXksIG5leHRQcm9wKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChET01Qcm9wZXJ0eS5wcm9wZXJ0aWVzW3Byb3BLZXldIHx8IERPTVByb3BlcnR5LmlzQ3VzdG9tQXR0cmlidXRlKHByb3BLZXkpKSB7XG4gICAgICAgIHZhciBub2RlID0gZ2V0Tm9kZSh0aGlzKTtcbiAgICAgICAgLy8gSWYgd2UncmUgdXBkYXRpbmcgdG8gbnVsbCBvciB1bmRlZmluZWQsIHdlIHNob3VsZCByZW1vdmUgdGhlIHByb3BlcnR5XG4gICAgICAgIC8vIGZyb20gdGhlIERPTSBub2RlIGluc3RlYWQgb2YgaW5hZHZlcnRlbnRseSBzZXR0aW5nIHRvIGEgc3RyaW5nLiBUaGlzXG4gICAgICAgIC8vIGJyaW5ncyB1cyBpbiBsaW5lIHdpdGggdGhlIHNhbWUgYmVoYXZpb3Igd2UgaGF2ZSBvbiBpbml0aWFsIHJlbmRlci5cbiAgICAgICAgaWYgKG5leHRQcm9wICE9IG51bGwpIHtcbiAgICAgICAgICBET01Qcm9wZXJ0eU9wZXJhdGlvbnMuc2V0VmFsdWVGb3JQcm9wZXJ0eShub2RlLCBwcm9wS2V5LCBuZXh0UHJvcCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLmRlbGV0ZVZhbHVlRm9yUHJvcGVydHkobm9kZSwgcHJvcEtleSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHN0eWxlVXBkYXRlcykge1xuICAgICAgQ1NTUHJvcGVydHlPcGVyYXRpb25zLnNldFZhbHVlRm9yU3R5bGVzKGdldE5vZGUodGhpcyksIHN0eWxlVXBkYXRlcywgdGhpcyk7XG4gICAgfVxuICB9LFxuXG4gIC8qKlxuICAgKiBSZWNvbmNpbGVzIHRoZSBjaGlsZHJlbiB3aXRoIHRoZSB2YXJpb3VzIHByb3BlcnRpZXMgdGhhdCBhZmZlY3QgdGhlXG4gICAqIGNoaWxkcmVuIGNvbnRlbnQuXG4gICAqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBsYXN0UHJvcHNcbiAgICogQHBhcmFtIHtvYmplY3R9IG5leHRQcm9wc1xuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb259IHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBjb250ZXh0XG4gICAqL1xuICBfdXBkYXRlRE9NQ2hpbGRyZW46IGZ1bmN0aW9uIChsYXN0UHJvcHMsIG5leHRQcm9wcywgdHJhbnNhY3Rpb24sIGNvbnRleHQpIHtcbiAgICB2YXIgbGFzdENvbnRlbnQgPSBDT05URU5UX1RZUEVTW3R5cGVvZiBsYXN0UHJvcHMuY2hpbGRyZW5dID8gbGFzdFByb3BzLmNoaWxkcmVuIDogbnVsbDtcbiAgICB2YXIgbmV4dENvbnRlbnQgPSBDT05URU5UX1RZUEVTW3R5cGVvZiBuZXh0UHJvcHMuY2hpbGRyZW5dID8gbmV4dFByb3BzLmNoaWxkcmVuIDogbnVsbDtcblxuICAgIHZhciBsYXN0SHRtbCA9IGxhc3RQcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCAmJiBsYXN0UHJvcHMuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwuX19odG1sO1xuICAgIHZhciBuZXh0SHRtbCA9IG5leHRQcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCAmJiBuZXh0UHJvcHMuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwuX19odG1sO1xuXG4gICAgLy8gTm90ZSB0aGUgdXNlIG9mIGAhPWAgd2hpY2ggY2hlY2tzIGZvciBudWxsIG9yIHVuZGVmaW5lZC5cbiAgICB2YXIgbGFzdENoaWxkcmVuID0gbGFzdENvbnRlbnQgIT0gbnVsbCA/IG51bGwgOiBsYXN0UHJvcHMuY2hpbGRyZW47XG4gICAgdmFyIG5leHRDaGlsZHJlbiA9IG5leHRDb250ZW50ICE9IG51bGwgPyBudWxsIDogbmV4dFByb3BzLmNoaWxkcmVuO1xuXG4gICAgLy8gSWYgd2UncmUgc3dpdGNoaW5nIGZyb20gY2hpbGRyZW4gdG8gY29udGVudC9odG1sIG9yIHZpY2UgdmVyc2EsIHJlbW92ZVxuICAgIC8vIHRoZSBvbGQgY29udGVudFxuICAgIHZhciBsYXN0SGFzQ29udGVudE9ySHRtbCA9IGxhc3RDb250ZW50ICE9IG51bGwgfHwgbGFzdEh0bWwgIT0gbnVsbDtcbiAgICB2YXIgbmV4dEhhc0NvbnRlbnRPckh0bWwgPSBuZXh0Q29udGVudCAhPSBudWxsIHx8IG5leHRIdG1sICE9IG51bGw7XG4gICAgaWYgKGxhc3RDaGlsZHJlbiAhPSBudWxsICYmIG5leHRDaGlsZHJlbiA9PSBudWxsKSB7XG4gICAgICB0aGlzLnVwZGF0ZUNoaWxkcmVuKG51bGwsIHRyYW5zYWN0aW9uLCBjb250ZXh0KTtcbiAgICB9IGVsc2UgaWYgKGxhc3RIYXNDb250ZW50T3JIdG1sICYmICFuZXh0SGFzQ29udGVudE9ySHRtbCkge1xuICAgICAgdGhpcy51cGRhdGVUZXh0Q29udGVudCgnJyk7XG4gICAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJykge1xuICAgICAgICBSZWFjdEluc3RydW1lbnRhdGlvbi5kZWJ1Z1Rvb2wub25TZXRDaGlsZHJlbih0aGlzLl9kZWJ1Z0lELCBbXSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG5leHRDb250ZW50ICE9IG51bGwpIHtcbiAgICAgIGlmIChsYXN0Q29udGVudCAhPT0gbmV4dENvbnRlbnQpIHtcbiAgICAgICAgdGhpcy51cGRhdGVUZXh0Q29udGVudCgnJyArIG5leHRDb250ZW50KTtcbiAgICAgICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgICAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIG5leHRDb250ZW50KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAobmV4dEh0bWwgIT0gbnVsbCkge1xuICAgICAgaWYgKGxhc3RIdG1sICE9PSBuZXh0SHRtbCkge1xuICAgICAgICB0aGlzLnVwZGF0ZU1hcmt1cCgnJyArIG5leHRIdG1sKTtcbiAgICAgIH1cbiAgICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICAgIFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vblNldENoaWxkcmVuKHRoaXMuX2RlYnVnSUQsIFtdKTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKG5leHRDaGlsZHJlbiAhPSBudWxsKSB7XG4gICAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJykge1xuICAgICAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIG51bGwpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLnVwZGF0ZUNoaWxkcmVuKG5leHRDaGlsZHJlbiwgdHJhbnNhY3Rpb24sIGNvbnRleHQpO1xuICAgIH1cbiAgfSxcblxuICBnZXRIb3N0Tm9kZTogZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiBnZXROb2RlKHRoaXMpO1xuICB9LFxuXG4gIC8qKlxuICAgKiBEZXN0cm95cyBhbGwgZXZlbnQgcmVnaXN0cmF0aW9ucyBmb3IgdGhpcyBpbnN0YW5jZS4gRG9lcyBub3QgcmVtb3ZlIGZyb21cbiAgICogdGhlIERPTS4gVGhhdCBtdXN0IGJlIGRvbmUgYnkgdGhlIHBhcmVudC5cbiAgICpcbiAgICogQGludGVybmFsXG4gICAqL1xuICB1bm1vdW50Q29tcG9uZW50OiBmdW5jdGlvbiAoc2FmZWx5KSB7XG4gICAgc3dpdGNoICh0aGlzLl90YWcpIHtcbiAgICAgIGNhc2UgJ2F1ZGlvJzpcbiAgICAgIGNhc2UgJ2Zvcm0nOlxuICAgICAgY2FzZSAnaWZyYW1lJzpcbiAgICAgIGNhc2UgJ2ltZyc6XG4gICAgICBjYXNlICdsaW5rJzpcbiAgICAgIGNhc2UgJ29iamVjdCc6XG4gICAgICBjYXNlICdzb3VyY2UnOlxuICAgICAgY2FzZSAndmlkZW8nOlxuICAgICAgICB2YXIgbGlzdGVuZXJzID0gdGhpcy5fd3JhcHBlclN0YXRlLmxpc3RlbmVycztcbiAgICAgICAgaWYgKGxpc3RlbmVycykge1xuICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGlzdGVuZXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBsaXN0ZW5lcnNbaV0ucmVtb3ZlKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnaHRtbCc6XG4gICAgICBjYXNlICdoZWFkJzpcbiAgICAgIGNhc2UgJ2JvZHknOlxuICAgICAgICAvKipcbiAgICAgICAgICogQ29tcG9uZW50cyBsaWtlIDxodG1sPiA8aGVhZD4gYW5kIDxib2R5PiBjYW4ndCBiZSByZW1vdmVkIG9yIGFkZGVkXG4gICAgICAgICAqIGVhc2lseSBpbiBhIGNyb3NzLWJyb3dzZXIgd2F5LCBob3dldmVyIGl0J3MgdmFsdWFibGUgdG8gYmUgYWJsZSB0b1xuICAgICAgICAgKiB0YWtlIGFkdmFudGFnZSBvZiBSZWFjdCdzIHJlY29uY2lsaWF0aW9uIGZvciBzdHlsaW5nIGFuZCA8dGl0bGU+XG4gICAgICAgICAqIG1hbmFnZW1lbnQuIFNvIHdlIGp1c3QgZG9jdW1lbnQgaXQgYW5kIHRocm93IGluIGRhbmdlcm91cyBjYXNlcy5cbiAgICAgICAgICovXG4gICAgICAgICFmYWxzZSA/IHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicgPyBpbnZhcmlhbnQoZmFsc2UsICc8JXM+IHRyaWVkIHRvIHVubW91bnQuIEJlY2F1c2Ugb2YgY3Jvc3MtYnJvd3NlciBxdWlya3MgaXQgaXMgaW1wb3NzaWJsZSB0byB1bm1vdW50IHNvbWUgdG9wLWxldmVsIGNvbXBvbmVudHMgKGVnIDxodG1sPiwgPGhlYWQ+LCBhbmQgPGJvZHk+KSByZWxpYWJseSBhbmQgZWZmaWNpZW50bHkuIFRvIGZpeCB0aGlzLCBoYXZlIGEgc2luZ2xlIHRvcC1sZXZlbCBjb21wb25lbnQgdGhhdCBuZXZlciB1bm1vdW50cyByZW5kZXIgdGhlc2UgZWxlbWVudHMuJywgdGhpcy5fdGFnKSA6IF9wcm9kSW52YXJpYW50KCc2NicsIHRoaXMuX3RhZykgOiB2b2lkIDA7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIHRoaXMudW5tb3VudENoaWxkcmVuKHNhZmVseSk7XG4gICAgUmVhY3RET01Db21wb25lbnRUcmVlLnVuY2FjaGVOb2RlKHRoaXMpO1xuICAgIEV2ZW50UGx1Z2luSHViLmRlbGV0ZUFsbExpc3RlbmVycyh0aGlzKTtcbiAgICB0aGlzLl9yb290Tm9kZUlEID0gMDtcbiAgICB0aGlzLl9kb21JRCA9IDA7XG4gICAgdGhpcy5fd3JhcHBlclN0YXRlID0gbnVsbDtcblxuICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIG51bGwpO1xuICAgIH1cbiAgfSxcblxuICBnZXRQdWJsaWNJbnN0YW5jZTogZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiBnZXROb2RlKHRoaXMpO1xuICB9XG5cbn07XG5cbl9hc3NpZ24oUmVhY3RET01Db21wb25lbnQucHJvdG90eXBlLCBSZWFjdERPTUNvbXBvbmVudC5NaXhpbiwgUmVhY3RNdWx0aUNoaWxkLk1peGluKTtcblxubW9kdWxlLmV4cG9ydHMgPSBSZWFjdERPTUNvbXBvbmVudDtcblxuXG4vLy8vLy8vLy8vLy8vLy8vLy9cbi8vIFdFQlBBQ0sgRk9PVEVSXG4vLyAuL34vcmVhY3QtZG9tL2xpYi9SZWFjdERPTUNvbXBvbmVudC5qc1xuLy8gbW9kdWxlIGlkID0gMjY1XG4vLyBtb2R1bGUgY2h1bmtzID0gMCJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=");

FIXME found
Open

    eval(" /* eslint-env node */\n'use strict';\n\n// SDP helpers.\nvar 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(function(line) {\n    return line.trim();\n  });\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n  var parts = blob.split('\\nm=');\n  return parts.map(function(part, index) {\n    return (index > 0 ? 'm=' + part : part).trim() + '\\r\\n';\n  });\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n  return SDPUtils.splitLines(blob).filter(function(line) {\n    return line.indexOf(prefix) === 0;\n  });\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\"\nSDPUtils.parseCandidate = function(line) {\n  var 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  var candidate = {\n    foundation: parts[0],\n    component: parts[1],\n    protocol: parts[2].toLowerCase(),\n    priority: parseInt(parts[3], 10),\n    ip: parts[4],\n    port: parseInt(parts[5], 10),\n    // skip parts[6] == 'typ'\n    type: parts[7]\n  };\n\n  for (var 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      default: // Unknown extensions are silently ignored.\n        break;\n    }\n  }\n  return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\nSDPUtils.writeCandidate = function(candidate) {\n  var sdp = [];\n  sdp.push(candidate.foundation);\n  sdp.push(candidate.component);\n  sdp.push(candidate.protocol.toUpperCase());\n  sdp.push(candidate.priority);\n  sdp.push(candidate.ip);\n  sdp.push(candidate.port);\n\n  var 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); // was: relAddr\n    sdp.push('rport');\n    sdp.push(candidate.relatedPort); // was: relPort\n  }\n  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n    sdp.push('tcptype');\n    sdp.push(candidate.tcpType);\n  }\n  return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n  var parts = line.substr(9).split(' ');\n  var 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  // was: channels\n  parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n  return parsed;\n};\n\n// Generate an a=rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n      (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\\r\\n';\n};\n\n// Parses an a=extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n  var parts = line.substr(9).split(' ');\n  return {\n    id: parseInt(parts[0], 10),\n    uri: parts[1]\n  };\n};\n\n// Generates a=extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n       ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses an ftmp 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  var parsed = {};\n  var kv;\n  var parts = line.substr(line.indexOf(' ') + 1).split(';');\n  for (var 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 an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n  var line = '';\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  if (codec.parameters && Object.keys(codec.parameters).length) {\n    var params = [];\n    Object.keys(codec.parameters).forEach(function(param) {\n      params.push(param + '=' + codec.parameters[param]);\n    });\n    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n  }\n  return line;\n};\n\n// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n  var parts = line.substr(line.indexOf(' ') + 1).split(' ');\n  return {\n    type: parts.shift(),\n    parameter: parts.join(' ')\n  };\n};\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n  var lines = '';\n  var 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(function(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 an RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n  var sp = line.indexOf(' ');\n  var parts = {\n    ssrc: parseInt(line.substr(7, sp - 7), 10)\n  };\n  var 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// 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  var lines = SDPUtils.splitLines(mediaSection);\n  // Search in session part, too.\n  lines = lines.concat(SDPUtils.splitLines(sessionpart));\n  var fpLine = lines.filter(function(line) {\n    return line.indexOf('a=fingerprint:') === 0;\n  })[0].substr(14);\n  // Note: a=setup line is ignored since we use the 'auto' role.\n  var dtlsParameters = {\n    role: 'auto',\n    fingerprints: [{\n      algorithm: fpLine.split(' ')[0],\n      value: fpLine.split(' ')[1]\n    }]\n  };\n  return dtlsParameters;\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n  var sdp = 'a=setup:' + setupType + '\\r\\n';\n  params.fingerprints.forEach(function(fp) {\n    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n  });\n  return sdp;\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  var lines = SDPUtils.splitLines(mediaSection);\n  // Search in session part, too.\n  lines = lines.concat(SDPUtils.splitLines(sessionpart));\n  var iceParameters = {\n    usernameFragment: lines.filter(function(line) {\n      return line.indexOf('a=ice-ufrag:') === 0;\n    })[0].substr(12),\n    password: lines.filter(function(line) {\n      return line.indexOf('a=ice-pwd:') === 0;\n    })[0].substr(10)\n  };\n  return iceParameters;\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n  return 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n      'a=ice-pwd:' + params.password + '\\r\\n';\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n  var description = {\n    codecs: [],\n    headerExtensions: [],\n    fecMechanisms: [],\n    rtcp: []\n  };\n  var lines = SDPUtils.splitLines(mediaSection);\n  var mline = lines[0].split(' ');\n  for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n    var pt = mline[i];\n    var rtpmapline = SDPUtils.matchPrefix(\n        mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n    if (rtpmapline) {\n      var codec = SDPUtils.parseRtpMap(rtpmapline);\n      var fmtps = SDPUtils.matchPrefix(\n          mediaSection, 'a=fmtp:' + pt + ' ');\n      // Only the first a=fmtp:<pt> 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(function(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  var 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(function(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(function(codec) {\n    sdp += SDPUtils.writeRtpMap(codec);\n    sdp += SDPUtils.writeFmtp(codec);\n    sdp += SDPUtils.writeRtcpFb(codec);\n  });\n  sdp += 'a=rtcp-mux\\r\\n';\n\n  caps.headerExtensions.forEach(function(extension) {\n    sdp += SDPUtils.writeExtmap(extension);\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  var encodingParameters = [];\n  var description = SDPUtils.parseRtpParameters(mediaSection);\n  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n  // filter a=ssrc:... cname:, ignore PlanB-msid\n  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n  .map(function(line) {\n    return SDPUtils.parseSsrcMedia(line);\n  })\n  .filter(function(parts) {\n    return parts.attribute === 'cname';\n  });\n  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n  var secondarySsrc;\n\n  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n  .map(function(line) {\n    var parts = line.split(' ');\n    parts.shift();\n    return parts.map(function(part) {\n      return parseInt(part, 10);\n    });\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(function(codec) {\n    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n      var encParam = {\n        ssrc: primarySsrc,\n        codecPayloadType: parseInt(codec.parameters.apt, 10),\n        rtx: {\n          payloadType: codec.payloadType,\n          ssrc: secondarySsrc\n        }\n      };\n      encodingParameters.push(encParam);\n      if (hasRed) {\n        encParam = JSON.parse(JSON.stringify(encParam));\n        encParam.fec = {\n          ssrc: secondarySsrc,\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  var 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      bandwidth = parseInt(bandwidth[0].substr(5), 10);\n    }\n    encodingParameters.forEach(function(params) {\n      params.maxBitrate = bandwidth;\n    });\n  }\n  return encodingParameters;\n};\n\nSDPUtils.writeSessionBoilerplate = function() {\n  // FIXME: sess-id should be an NTP timestamp.\n  return 'v=0\\r\\n' +\n      'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\\r\\n' +\n      's=-\\r\\n' +\n      't=0 0\\r\\n';\n};\n\nSDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {\n  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);\n\n  // Map ICE parameters (ufrag, pwd) to SDP.\n  sdp += SDPUtils.writeIceParameters(\n      transceiver.iceGatherer.getLocalParameters());\n\n  // Map DTLS parameters to SDP.\n  sdp += SDPUtils.writeDtlsParameters(\n      transceiver.dtlsTransport.getLocalParameters(),\n      type === 'offer' ? 'actpass' : 'active');\n\n  sdp += 'a=mid:' + transceiver.mid + '\\r\\n';\n\n  if (transceiver.rtpSender && transceiver.rtpReceiver) {\n    sdp += 'a=sendrecv\\r\\n';\n  } else if (transceiver.rtpSender) {\n    sdp += 'a=sendonly\\r\\n';\n  } else if (transceiver.rtpReceiver) {\n    sdp += 'a=recvonly\\r\\n';\n  } else {\n    sdp += 'a=inactive\\r\\n';\n  }\n\n  // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.\n  if (transceiver.rtpSender) {\n    var msid = 'msid:' + stream.id + ' ' +\n        transceiver.rtpSender.track.id + '\\r\\n';\n    sdp += 'a=' + msid;\n    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n        ' ' + msid;\n  }\n  // FIXME: this should be written by writeRtpDescription.\n  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n      ' cname:' + SDPUtils.localCName + '\\r\\n';\n  return sdp;\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  var lines = SDPUtils.splitLines(mediaSection);\n  for (var 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\n// Expose public methods.\nmodule.exports = SDPUtils;\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDUyLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9zZHAvc2RwLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIiAvKiBlc2xpbnQtZW52IG5vZGUgKi9cbid1c2Ugc3RyaWN0JztcblxuLy8gU0RQIGhlbHBlcnMuXG52YXIgU0RQVXRpbHMgPSB7fTtcblxuLy8gR2VuZXJhdGUgYW4gYWxwaGFudW1lcmljIGlkZW50aWZpZXIgZm9yIGNuYW1lIG9yIG1pZHMuXG4vLyBUT0RPOiB1c2UgVVVJRHMgaW5zdGVhZD8gaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vamVkLzk4Mjg4M1xuU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTApO1xufTtcblxuLy8gVGhlIFJUQ1AgQ05BTUUgdXNlZCBieSBhbGwgcGVlcmNvbm5lY3Rpb25zIGZyb20gdGhlIHNhbWUgSlMuXG5TRFBVdGlscy5sb2NhbENOYW1lID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG5cbi8vIFNwbGl0cyBTRFAgaW50byBsaW5lcywgZGVhbGluZyB3aXRoIGJvdGggQ1JMRiBhbmQgTEYuXG5TRFBVdGlscy5zcGxpdExpbmVzID0gZnVuY3Rpb24oYmxvYikge1xuICByZXR1cm4gYmxvYi50cmltKCkuc3BsaXQoJ1xcbicpLm1hcChmdW5jdGlvbihsaW5lKSB7XG4gICAgcmV0dXJuIGxpbmUudHJpbSgpO1xuICB9KTtcbn07XG4vLyBTcGxpdHMgU0RQIGludG8gc2Vzc2lvbnBhcnQgYW5kIG1lZGlhc2VjdGlvbnMuIEVuc3VyZXMgQ1JMRi5cblNEUFV0aWxzLnNwbGl0U2VjdGlvbnMgPSBmdW5jdGlvbihibG9iKSB7XG4gIHZhciBwYXJ0cyA9IGJsb2Iuc3BsaXQoJ1xcbm09Jyk7XG4gIHJldHVybiBwYXJ0cy5tYXAoZnVuY3Rpb24ocGFydCwgaW5kZXgpIHtcbiAgICByZXR1cm4gKGluZGV4ID4gMCA/ICdtPScgKyBwYXJ0IDogcGFydCkudHJpbSgpICsgJ1xcclxcbic7XG4gIH0pO1xufTtcblxuLy8gUmV0dXJucyBsaW5lcyB0aGF0IHN0YXJ0IHdpdGggYSBjZXJ0YWluIHByZWZpeC5cblNEUFV0aWxzLm1hdGNoUHJlZml4ID0gZnVuY3Rpb24oYmxvYiwgcHJlZml4KSB7XG4gIHJldHVybiBTRFBVdGlscy5zcGxpdExpbmVzKGJsb2IpLmZpbHRlcihmdW5jdGlvbihsaW5lKSB7XG4gICAgcmV0dXJuIGxpbmUuaW5kZXhPZihwcmVmaXgpID09PSAwO1xuICB9KTtcbn07XG5cbi8vIFBhcnNlcyBhbiBJQ0UgY2FuZGlkYXRlIGxpbmUuIFNhbXBsZSBpbnB1dDpcbi8vIGNhbmRpZGF0ZTo3MDI3ODYzNTAgMiB1ZHAgNDE4MTk5MDIgOC44LjguOCA2MDc2OSB0eXAgcmVsYXkgcmFkZHIgOC44LjguOFxuLy8gcnBvcnQgNTU5OTZcIlxuU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBwYXJ0cztcbiAgLy8gUGFyc2UgYm90aCB2YXJpYW50cy5cbiAgaWYgKGxpbmUuaW5kZXhPZignYT1jYW5kaWRhdGU6JykgPT09IDApIHtcbiAgICBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDEyKS5zcGxpdCgnICcpO1xuICB9IGVsc2Uge1xuICAgIHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMTApLnNwbGl0KCcgJyk7XG4gIH1cblxuICB2YXIgY2FuZGlkYXRlID0ge1xuICAgIGZvdW5kYXRpb246IHBhcnRzWzBdLFxuICAgIGNvbXBvbmVudDogcGFydHNbMV0sXG4gICAgcHJvdG9jb2w6IHBhcnRzWzJdLnRvTG93ZXJDYXNlKCksXG4gICAgcHJpb3JpdHk6IHBhcnNlSW50KHBhcnRzWzNdLCAxMCksXG4gICAgaXA6IHBhcnRzWzRdLFxuICAgIHBvcnQ6IHBhcnNlSW50KHBhcnRzWzVdLCAxMCksXG4gICAgLy8gc2tpcCBwYXJ0c1s2XSA9PSAndHlwJ1xuICAgIHR5cGU6IHBhcnRzWzddXG4gIH07XG5cbiAgZm9yICh2YXIgaSA9IDg7IGkgPCBwYXJ0cy5sZW5ndGg7IGkgKz0gMikge1xuICAgIHN3aXRjaCAocGFydHNbaV0pIHtcbiAgICAgIGNhc2UgJ3JhZGRyJzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3Jwb3J0JzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRQb3J0ID0gcGFyc2VJbnQocGFydHNbaSArIDFdLCAxMCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgIGNhbmRpZGF0ZS50Y3BUeXBlID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGRlZmF1bHQ6IC8vIFVua25vd24gZXh0ZW5zaW9ucyBhcmUgc2lsZW50bHkgaWdub3JlZC5cbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG4gIHJldHVybiBjYW5kaWRhdGU7XG59O1xuXG4vLyBUcmFuc2xhdGVzIGEgY2FuZGlkYXRlIG9iamVjdCBpbnRvIFNEUCBjYW5kaWRhdGUgYXR0cmlidXRlLlxuU0RQVXRpbHMud3JpdGVDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgdmFyIHNkcCA9IFtdO1xuICBzZHAucHVzaChjYW5kaWRhdGUuZm91bmRhdGlvbik7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5jb21wb25lbnQpO1xuICBzZHAucHVzaChjYW5kaWRhdGUucHJvdG9jb2wudG9VcHBlckNhc2UoKSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wcmlvcml0eSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5pcCk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wb3J0KTtcblxuICB2YXIgdHlwZSA9IGNhbmRpZGF0ZS50eXBlO1xuICBzZHAucHVzaCgndHlwJyk7XG4gIHNkcC5wdXNoKHR5cGUpO1xuICBpZiAodHlwZSAhPT0gJ2hvc3QnICYmIGNhbmRpZGF0ZS5yZWxhdGVkQWRkcmVzcyAmJlxuICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRQb3J0KSB7XG4gICAgc2RwLnB1c2goJ3JhZGRyJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzKTsgLy8gd2FzOiByZWxBZGRyXG4gICAgc2RwLnB1c2goJ3Jwb3J0Jyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnJlbGF0ZWRQb3J0KTsgLy8gd2FzOiByZWxQb3J0XG4gIH1cbiAgaWYgKGNhbmRpZGF0ZS50Y3BUeXBlICYmIGNhbmRpZGF0ZS5wcm90b2NvbC50b0xvd2VyQ2FzZSgpID09PSAndGNwJykge1xuICAgIHNkcC5wdXNoKCd0Y3B0eXBlJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnRjcFR5cGUpO1xuICB9XG4gIHJldHVybiAnY2FuZGlkYXRlOicgKyBzZHAuam9pbignICcpO1xufTtcblxuLy8gUGFyc2VzIGFuIHJ0cG1hcCBsaW5lLCByZXR1cm5zIFJUQ1J0cENvZGRlY1BhcmFtZXRlcnMuIFNhbXBsZSBpbnB1dDpcbi8vIGE9cnRwbWFwOjExMSBvcHVzLzQ4MDAwLzJcblNEUFV0aWxzLnBhcnNlUnRwTWFwID0gZnVuY3Rpb24obGluZSkge1xuICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cig5KS5zcGxpdCgnICcpO1xuICB2YXIgcGFyc2VkID0ge1xuICAgIHBheWxvYWRUeXBlOiBwYXJzZUludChwYXJ0cy5zaGlmdCgpLCAxMCkgLy8gd2FzOiBpZFxuICB9O1xuXG4gIHBhcnRzID0gcGFydHNbMF0uc3BsaXQoJy8nKTtcblxuICBwYXJzZWQubmFtZSA9IHBhcnRzWzBdO1xuICBwYXJzZWQuY2xvY2tSYXRlID0gcGFyc2VJbnQocGFydHNbMV0sIDEwKTsgLy8gd2FzOiBjbG9ja3JhdGVcbiAgLy8gd2FzOiBjaGFubmVsc1xuICBwYXJzZWQubnVtQ2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPT09IDMgPyBwYXJzZUludChwYXJ0c1syXSwgMTApIDogMTtcbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlIGFuIGE9cnRwbWFwIGxpbmUgZnJvbSBSVENSdHBDb2RlY0NhcGFiaWxpdHkgb3Jcbi8vIFJUQ1J0cENvZGVjUGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlUnRwTWFwID0gZnVuY3Rpb24oY29kZWMpIHtcbiAgdmFyIHB0ID0gY29kZWMucGF5bG9hZFR5cGU7XG4gIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcHQgPSBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgfVxuICByZXR1cm4gJ2E9cnRwbWFwOicgKyBwdCArICcgJyArIGNvZGVjLm5hbWUgKyAnLycgKyBjb2RlYy5jbG9ja1JhdGUgK1xuICAgICAgKGNvZGVjLm51bUNoYW5uZWxzICE9PSAxID8gJy8nICsgY29kZWMubnVtQ2hhbm5lbHMgOiAnJykgKyAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyBhbiBhPWV4dG1hcCBsaW5lIChoZWFkZXJleHRlbnNpb24gZnJvbSBSRkMgNTI4NSkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9ZXh0bWFwOjIgdXJuOmlldGY6cGFyYW1zOnJ0cC1oZHJleHQ6dG9mZnNldFxuU0RQVXRpbHMucGFyc2VFeHRtYXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyKDkpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgaWQ6IHBhcnNlSW50KHBhcnRzWzBdLCAxMCksXG4gICAgdXJpOiBwYXJ0c1sxXVxuICB9O1xufTtcblxuLy8gR2VuZXJhdGVzIGE9ZXh0bWFwIGxpbmUgZnJvbSBSVENSdHBIZWFkZXJFeHRlbnNpb25QYXJhbWV0ZXJzIG9yXG4vLyBSVENSdHBIZWFkZXJFeHRlbnNpb24uXG5TRFBVdGlscy53cml0ZUV4dG1hcCA9IGZ1bmN0aW9uKGhlYWRlckV4dGVuc2lvbikge1xuICByZXR1cm4gJ2E9ZXh0bWFwOicgKyAoaGVhZGVyRXh0ZW5zaW9uLmlkIHx8IGhlYWRlckV4dGVuc2lvbi5wcmVmZXJyZWRJZCkgK1xuICAgICAgICcgJyArIGhlYWRlckV4dGVuc2lvbi51cmkgKyAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyBhbiBmdG1wIGxpbmUsIHJldHVybnMgZGljdGlvbmFyeS4gU2FtcGxlIGlucHV0OlxuLy8gYT1mbXRwOjk2IHZicj1vbjtjbmc9b25cbi8vIEFsc28gZGVhbHMgd2l0aCB2YnI9b247IGNuZz1vblxuU0RQVXRpbHMucGFyc2VGbXRwID0gZnVuY3Rpb24obGluZSkge1xuICB2YXIgcGFyc2VkID0ge307XG4gIHZhciBrdjtcbiAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIobGluZS5pbmRleE9mKCcgJykgKyAxKS5zcGxpdCgnOycpO1xuICBmb3IgKHZhciBqID0gMDsgaiA8IHBhcnRzLmxlbmd0aDsgaisrKSB7XG4gICAga3YgPSBwYXJ0c1tqXS50cmltKCkuc3BsaXQoJz0nKTtcbiAgICBwYXJzZWRba3ZbMF0udHJpbSgpXSA9IGt2WzFdO1xuICB9XG4gIHJldHVybiBwYXJzZWQ7XG59O1xuXG4vLyBHZW5lcmF0ZXMgYW4gYT1mdG1wIGxpbmUgZnJvbSBSVENSdHBDb2RlY0NhcGFiaWxpdHkgb3IgUlRDUnRwQ29kZWNQYXJhbWV0ZXJzLlxuU0RQVXRpbHMud3JpdGVGbXRwID0gZnVuY3Rpb24oY29kZWMpIHtcbiAgdmFyIGxpbmUgPSAnJztcbiAgdmFyIHB0ID0gY29kZWMucGF5bG9hZFR5cGU7XG4gIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcHQgPSBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgfVxuICBpZiAoY29kZWMucGFyYW1ldGVycyAmJiBPYmplY3Qua2V5cyhjb2RlYy5wYXJhbWV0ZXJzKS5sZW5ndGgpIHtcbiAgICB2YXIgcGFyYW1zID0gW107XG4gICAgT2JqZWN0LmtleXMoY29kZWMucGFyYW1ldGVycykuZm9yRWFjaChmdW5jdGlvbihwYXJhbSkge1xuICAgICAgcGFyYW1zLnB1c2gocGFyYW0gKyAnPScgKyBjb2RlYy5wYXJhbWV0ZXJzW3BhcmFtXSk7XG4gICAgfSk7XG4gICAgbGluZSArPSAnYT1mbXRwOicgKyBwdCArICcgJyArIHBhcmFtcy5qb2luKCc7JykgKyAnXFxyXFxuJztcbiAgfVxuICByZXR1cm4gbGluZTtcbn07XG5cbi8vIFBhcnNlcyBhbiBydGNwLWZiIGxpbmUsIHJldHVybnMgUlRDUFJ0Y3BGZWVkYmFjayBvYmplY3QuIFNhbXBsZSBpbnB1dDpcbi8vIGE9cnRjcC1mYjo5OCBuYWNrIHJwc2lcblNEUFV0aWxzLnBhcnNlUnRjcEZiID0gZnVuY3Rpb24obGluZSkge1xuICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cihsaW5lLmluZGV4T2YoJyAnKSArIDEpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgdHlwZTogcGFydHMuc2hpZnQoKSxcbiAgICBwYXJhbWV0ZXI6IHBhcnRzLmpvaW4oJyAnKVxuICB9O1xufTtcbi8vIEdlbmVyYXRlIGE9cnRjcC1mYiBsaW5lcyBmcm9tIFJUQ1J0cENvZGVjQ2FwYWJpbGl0eSBvciBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZVJ0Y3BGYiA9IGZ1bmN0aW9uKGNvZGVjKSB7XG4gIHZhciBsaW5lcyA9ICcnO1xuICB2YXIgcHQgPSBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgaWYgKGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICBwdCA9IGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICB9XG4gIGlmIChjb2RlYy5ydGNwRmVlZGJhY2sgJiYgY29kZWMucnRjcEZlZWRiYWNrLmxlbmd0aCkge1xuICAgIC8vIEZJWE1FOiBzcGVjaWFsIGhhbmRsaW5nIGZvciB0cnItaW50P1xuICAgIGNvZGVjLnJ0Y3BGZWVkYmFjay5mb3JFYWNoKGZ1bmN0aW9uKGZiKSB7XG4gICAgICBsaW5lcyArPSAnYT1ydGNwLWZiOicgKyBwdCArICcgJyArIGZiLnR5cGUgK1xuICAgICAgKGZiLnBhcmFtZXRlciAmJiBmYi5wYXJhbWV0ZXIubGVuZ3RoID8gJyAnICsgZmIucGFyYW1ldGVyIDogJycpICtcbiAgICAgICAgICAnXFxyXFxuJztcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gbGluZXM7XG59O1xuXG4vLyBQYXJzZXMgYW4gUkZDIDU1NzYgc3NyYyBtZWRpYSBhdHRyaWJ1dGUuIFNhbXBsZSBpbnB1dDpcbi8vIGE9c3NyYzozNzM1OTI4NTU5IGNuYW1lOnNvbWV0aGluZ1xuU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBzcCA9IGxpbmUuaW5kZXhPZignICcpO1xuICB2YXIgcGFydHMgPSB7XG4gICAgc3NyYzogcGFyc2VJbnQobGluZS5zdWJzdHIoNywgc3AgLSA3KSwgMTApXG4gIH07XG4gIHZhciBjb2xvbiA9IGxpbmUuaW5kZXhPZignOicsIHNwKTtcbiAgaWYgKGNvbG9uID4gLTEpIHtcbiAgICBwYXJ0cy5hdHRyaWJ1dGUgPSBsaW5lLnN1YnN0cihzcCArIDEsIGNvbG9uIC0gc3AgLSAxKTtcbiAgICBwYXJ0cy52YWx1ZSA9IGxpbmUuc3Vic3RyKGNvbG9uICsgMSk7XG4gIH0gZWxzZSB7XG4gICAgcGFydHMuYXR0cmlidXRlID0gbGluZS5zdWJzdHIoc3AgKyAxKTtcbiAgfVxuICByZXR1cm4gcGFydHM7XG59O1xuXG4vLyBFeHRyYWN0cyBEVExTIHBhcmFtZXRlcnMgZnJvbSBTRFAgbWVkaWEgc2VjdGlvbiBvciBzZXNzaW9ucGFydC5cbi8vIEZJWE1FOiBmb3IgY29uc2lzdGVuY3kgd2l0aCBvdGhlciBmdW5jdGlvbnMgdGhpcyBzaG91bGQgb25seVxuLy8gICBnZXQgdGhlIGZpbmdlcnByaW50IGxpbmUgYXMgaW5wdXQuIFNlZSBhbHNvIGdldEljZVBhcmFtZXRlcnMuXG5TRFBVdGlscy5nZXREdGxzUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAvLyBTZWFyY2ggaW4gc2Vzc2lvbiBwYXJ0LCB0b28uXG4gIGxpbmVzID0gbGluZXMuY29uY2F0KFNEUFV0aWxzLnNwbGl0TGluZXMoc2Vzc2lvbnBhcnQpKTtcbiAgdmFyIGZwTGluZSA9IGxpbmVzLmZpbHRlcihmdW5jdGlvbihsaW5lKSB7XG4gICAgcmV0dXJuIGxpbmUuaW5kZXhPZignYT1maW5nZXJwcmludDonKSA9PT0gMDtcbiAgfSlbMF0uc3Vic3RyKDE0KTtcbiAgLy8gTm90ZTogYT1zZXR1cCBsaW5lIGlzIGlnbm9yZWQgc2luY2Ugd2UgdXNlIHRoZSAnYXV0bycgcm9sZS5cbiAgdmFyIGR0bHNQYXJhbWV0ZXJzID0ge1xuICAgIHJvbGU6ICdhdXRvJyxcbiAgICBmaW5nZXJwcmludHM6IFt7XG4gICAgICBhbGdvcml0aG06IGZwTGluZS5zcGxpdCgnICcpWzBdLFxuICAgICAgdmFsdWU6IGZwTGluZS5zcGxpdCgnICcpWzFdXG4gICAgfV1cbiAgfTtcbiAgcmV0dXJuIGR0bHNQYXJhbWV0ZXJzO1xufTtcblxuLy8gU2VyaWFsaXplcyBEVExTIHBhcmFtZXRlcnMgdG8gU0RQLlxuU0RQVXRpbHMud3JpdGVEdGxzUGFyYW1ldGVycyA9IGZ1bmN0aW9uKHBhcmFtcywgc2V0dXBUeXBlKSB7XG4gIHZhciBzZHAgPSAnYT1zZXR1cDonICsgc2V0dXBUeXBlICsgJ1xcclxcbic7XG4gIHBhcmFtcy5maW5nZXJwcmludHMuZm9yRWFjaChmdW5jdGlvbihmcCkge1xuICAgIHNkcCArPSAnYT1maW5nZXJwcmludDonICsgZnAuYWxnb3JpdGhtICsgJyAnICsgZnAudmFsdWUgKyAnXFxyXFxuJztcbiAgfSk7XG4gIHJldHVybiBzZHA7XG59O1xuLy8gUGFyc2VzIElDRSBpbmZvcm1hdGlvbiBmcm9tIFNEUCBtZWRpYSBzZWN0aW9uIG9yIHNlc3Npb25wYXJ0LlxuLy8gRklYTUU6IGZvciBjb25zaXN0ZW5jeSB3aXRoIG90aGVyIGZ1bmN0aW9ucyB0aGlzIHNob3VsZCBvbmx5XG4vLyAgIGdldCB0aGUgaWNlLXVmcmFnIGFuZCBpY2UtcHdkIGxpbmVzIGFzIGlucHV0LlxuU0RQVXRpbHMuZ2V0SWNlUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAvLyBTZWFyY2ggaW4gc2Vzc2lvbiBwYXJ0LCB0b28uXG4gIGxpbmVzID0gbGluZXMuY29uY2F0KFNEUFV0aWxzLnNwbGl0TGluZXMoc2Vzc2lvbnBhcnQpKTtcbiAgdmFyIGljZVBhcmFtZXRlcnMgPSB7XG4gICAgdXNlcm5hbWVGcmFnbWVudDogbGluZXMuZmlsdGVyKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgIHJldHVybiBsaW5lLmluZGV4T2YoJ2E9aWNlLXVmcmFnOicpID09PSAwO1xuICAgIH0pWzBdLnN1YnN0cigxMiksXG4gICAgcGFzc3dvcmQ6IGxpbmVzLmZpbHRlcihmdW5jdGlvbihsaW5lKSB7XG4gICAgICByZXR1cm4gbGluZS5pbmRleE9mKCdhPWljZS1wd2Q6JykgPT09IDA7XG4gICAgfSlbMF0uc3Vic3RyKDEwKVxuICB9O1xuICByZXR1cm4gaWNlUGFyYW1ldGVycztcbn07XG5cbi8vIFNlcmlhbGl6ZXMgSUNFIHBhcmFtZXRlcnMgdG8gU0RQLlxuU0RQVXRpbHMud3JpdGVJY2VQYXJhbWV0ZXJzID0gZnVuY3Rpb24ocGFyYW1zKSB7XG4gIHJldHVybiAnYT1pY2UtdWZyYWc6JyArIHBhcmFtcy51c2VybmFtZUZyYWdtZW50ICsgJ1xcclxcbicgK1xuICAgICAgJ2E9aWNlLXB3ZDonICsgcGFyYW1zLnBhc3N3b3JkICsgJ1xcclxcbic7XG59O1xuXG4vLyBQYXJzZXMgdGhlIFNEUCBtZWRpYSBzZWN0aW9uIGFuZCByZXR1cm5zIFJUQ1J0cFBhcmFtZXRlcnMuXG5TRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgdmFyIGRlc2NyaXB0aW9uID0ge1xuICAgIGNvZGVjczogW10sXG4gICAgaGVhZGVyRXh0ZW5zaW9uczogW10sXG4gICAgZmVjTWVjaGFuaXNtczogW10sXG4gICAgcnRjcDogW11cbiAgfTtcbiAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICB2YXIgbWxpbmUgPSBsaW5lc1swXS5zcGxpdCgnICcpO1xuICBmb3IgKHZhciBpID0gMzsgaSA8IG1saW5lLmxlbmd0aDsgaSsrKSB7IC8vIGZpbmQgYWxsIGNvZGVjcyBmcm9tIG1saW5lWzMuLl1cbiAgICB2YXIgcHQgPSBtbGluZVtpXTtcbiAgICB2YXIgcnRwbWFwbGluZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KFxuICAgICAgICBtZWRpYVNlY3Rpb24sICdhPXJ0cG1hcDonICsgcHQgKyAnICcpWzBdO1xuICAgIGlmIChydHBtYXBsaW5lKSB7XG4gICAgICB2YXIgY29kZWMgPSBTRFBVdGlscy5wYXJzZVJ0cE1hcChydHBtYXBsaW5lKTtcbiAgICAgIHZhciBmbXRwcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KFxuICAgICAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9Zm10cDonICsgcHQgKyAnICcpO1xuICAgICAgLy8gT25seSB0aGUgZmlyc3QgYT1mbXRwOjxwdD4gaXMgY29uc2lkZXJlZC5cbiAgICAgIGNvZGVjLnBhcmFtZXRlcnMgPSBmbXRwcy5sZW5ndGggPyBTRFBVdGlscy5wYXJzZUZtdHAoZm10cHNbMF0pIDoge307XG4gICAgICBjb2RlYy5ydGNwRmVlZGJhY2sgPSBTRFBVdGlscy5tYXRjaFByZWZpeChcbiAgICAgICAgICBtZWRpYVNlY3Rpb24sICdhPXJ0Y3AtZmI6JyArIHB0ICsgJyAnKVxuICAgICAgICAubWFwKFNEUFV0aWxzLnBhcnNlUnRjcEZiKTtcbiAgICAgIGRlc2NyaXB0aW9uLmNvZGVjcy5wdXNoKGNvZGVjKTtcbiAgICAgIC8vIHBhcnNlIEZFQyBtZWNoYW5pc21zIGZyb20gcnRwbWFwIGxpbmVzLlxuICAgICAgc3dpdGNoIChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkpIHtcbiAgICAgICAgY2FzZSAnUkVEJzpcbiAgICAgICAgY2FzZSAnVUxQRkVDJzpcbiAgICAgICAgICBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLnB1c2goY29kZWMubmFtZS50b1VwcGVyQ2FzZSgpKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDogLy8gb25seSBSRUQgYW5kIFVMUEZFQyBhcmUgcmVjb2duaXplZCBhcyBGRUMgbWVjaGFuaXNtcy5cbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1leHRtYXA6JykuZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgZGVzY3JpcHRpb24uaGVhZGVyRXh0ZW5zaW9ucy5wdXNoKFNEUFV0aWxzLnBhcnNlRXh0bWFwKGxpbmUpKTtcbiAgfSk7XG4gIC8vIEZJWE1FOiBwYXJzZSBydGNwLlxuICByZXR1cm4gZGVzY3JpcHRpb247XG59O1xuXG4vLyBHZW5lcmF0ZXMgcGFydHMgb2YgdGhlIFNEUCBtZWRpYSBzZWN0aW9uIGRlc2NyaWJpbmcgdGhlIGNhcGFiaWxpdGllcyAvXG4vLyBwYXJhbWV0ZXJzLlxuU0RQVXRpbHMud3JpdGVSdHBEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uKGtpbmQsIGNhcHMpIHtcbiAgdmFyIHNkcCA9ICcnO1xuXG4gIC8vIEJ1aWxkIHRoZSBtbGluZS5cbiAgc2RwICs9ICdtPScgKyBraW5kICsgJyAnO1xuICBzZHAgKz0gY2Fwcy5jb2RlY3MubGVuZ3RoID4gMCA/ICc5JyA6ICcwJzsgLy8gcmVqZWN0IGlmIG5vIGNvZGVjcy5cbiAgc2RwICs9ICcgVURQL1RMUy9SVFAvU0FWUEYgJztcbiAgc2RwICs9IGNhcHMuY29kZWNzLm1hcChmdW5jdGlvbihjb2RlYykge1xuICAgIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gICAgfVxuICAgIHJldHVybiBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgfSkuam9pbignICcpICsgJ1xcclxcbic7XG5cbiAgc2RwICs9ICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJztcbiAgc2RwICs9ICdhPXJ0Y3A6OSBJTiBJUDQgMC4wLjAuMFxcclxcbic7XG5cbiAgLy8gQWRkIGE9cnRwbWFwIGxpbmVzIGZvciBlYWNoIGNvZGVjLiBBbHNvIGZtdHAgYW5kIHJ0Y3AtZmIuXG4gIGNhcHMuY29kZWNzLmZvckVhY2goZnVuY3Rpb24oY29kZWMpIHtcbiAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVSdHBNYXAoY29kZWMpO1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZUZtdHAoY29kZWMpO1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZVJ0Y3BGYihjb2RlYyk7XG4gIH0pO1xuICBzZHAgKz0gJ2E9cnRjcC1tdXhcXHJcXG4nO1xuXG4gIGNhcHMuaGVhZGVyRXh0ZW5zaW9ucy5mb3JFYWNoKGZ1bmN0aW9uKGV4dGVuc2lvbikge1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZUV4dG1hcChleHRlbnNpb24pO1xuICB9KTtcbiAgLy8gRklYTUU6IHdyaXRlIGZlY01lY2hhbmlzbXMuXG4gIHJldHVybiBzZHA7XG59O1xuXG4vLyBQYXJzZXMgdGhlIFNEUCBtZWRpYSBzZWN0aW9uIGFuZCByZXR1cm5zIGFuIGFycmF5IG9mXG4vLyBSVENSdHBFbmNvZGluZ1BhcmFtZXRlcnMuXG5TRFBVdGlscy5wYXJzZVJ0cEVuY29kaW5nUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICB2YXIgZW5jb2RpbmdQYXJhbWV0ZXJzID0gW107XG4gIHZhciBkZXNjcmlwdGlvbiA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICB2YXIgaGFzUmVkID0gZGVzY3JpcHRpb24uZmVjTWVjaGFuaXNtcy5pbmRleE9mKCdSRUQnKSAhPT0gLTE7XG4gIHZhciBoYXNVbHBmZWMgPSBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLmluZGV4T2YoJ1VMUEZFQycpICE9PSAtMTtcblxuICAvLyBmaWx0ZXIgYT1zc3JjOi4uLiBjbmFtZTosIGlnbm9yZSBQbGFuQi1tc2lkXG4gIHZhciBzc3JjcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAubWFwKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICByZXR1cm4gU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEobGluZSk7XG4gIH0pXG4gIC5maWx0ZXIoZnVuY3Rpb24ocGFydHMpIHtcbiAgICByZXR1cm4gcGFydHMuYXR0cmlidXRlID09PSAnY25hbWUnO1xuICB9KTtcbiAgdmFyIHByaW1hcnlTc3JjID0gc3NyY3MubGVuZ3RoID4gMCAmJiBzc3Jjc1swXS5zc3JjO1xuICB2YXIgc2Vjb25kYXJ5U3NyYztcblxuICB2YXIgZmxvd3MgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNzcmMtZ3JvdXA6RklEJylcbiAgLm1hcChmdW5jdGlvbihsaW5lKSB7XG4gICAgdmFyIHBhcnRzID0gbGluZS5zcGxpdCgnICcpO1xuICAgIHBhcnRzLnNoaWZ0KCk7XG4gICAgcmV0dXJuIHBhcnRzLm1hcChmdW5jdGlvbihwYXJ0KSB7XG4gICAgICByZXR1cm4gcGFyc2VJbnQocGFydCwgMTApO1xuICAgIH0pO1xuICB9KTtcbiAgaWYgKGZsb3dzLmxlbmd0aCA+IDAgJiYgZmxvd3NbMF0ubGVuZ3RoID4gMSAmJiBmbG93c1swXVswXSA9PT0gcHJpbWFyeVNzcmMpIHtcbiAgICBzZWNvbmRhcnlTc3JjID0gZmxvd3NbMF1bMV07XG4gIH1cblxuICBkZXNjcmlwdGlvbi5jb2RlY3MuZm9yRWFjaChmdW5jdGlvbihjb2RlYykge1xuICAgIGlmIChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkgPT09ICdSVFgnICYmIGNvZGVjLnBhcmFtZXRlcnMuYXB0KSB7XG4gICAgICB2YXIgZW5jUGFyYW0gPSB7XG4gICAgICAgIHNzcmM6IHByaW1hcnlTc3JjLFxuICAgICAgICBjb2RlY1BheWxvYWRUeXBlOiBwYXJzZUludChjb2RlYy5wYXJhbWV0ZXJzLmFwdCwgMTApLFxuICAgICAgICBydHg6IHtcbiAgICAgICAgICBwYXlsb2FkVHlwZTogY29kZWMucGF5bG9hZFR5cGUsXG4gICAgICAgICAgc3NyYzogc2Vjb25kYXJ5U3NyY1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgZW5jb2RpbmdQYXJhbWV0ZXJzLnB1c2goZW5jUGFyYW0pO1xuICAgICAgaWYgKGhhc1JlZCkge1xuICAgICAgICBlbmNQYXJhbSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkoZW5jUGFyYW0pKTtcbiAgICAgICAgZW5jUGFyYW0uZmVjID0ge1xuICAgICAgICAgIHNzcmM6IHNlY29uZGFyeVNzcmMsXG4gICAgICAgICAgbWVjaGFuaXNtOiBoYXNVbHBmZWMgPyAncmVkK3VscGZlYycgOiAncmVkJ1xuICAgICAgICB9O1xuICAgICAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaChlbmNQYXJhbSk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcbiAgaWYgKGVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGggPT09IDAgJiYgcHJpbWFyeVNzcmMpIHtcbiAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaCh7XG4gICAgICBzc3JjOiBwcmltYXJ5U3NyY1xuICAgIH0pO1xuICB9XG5cbiAgLy8gd2Ugc3VwcG9ydCBib3RoIGI9QVMgYW5kIGI9VElBUyBidXQgaW50ZXJwcmV0IEFTIGFzIFRJQVMuXG4gIHZhciBiYW5kd2lkdGggPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdiPScpO1xuICBpZiAoYmFuZHdpZHRoLmxlbmd0aCkge1xuICAgIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1USUFTOicpID09PSAwKSB7XG4gICAgICBiYW5kd2lkdGggPSBwYXJzZUludChiYW5kd2lkdGhbMF0uc3Vic3RyKDcpLCAxMCk7XG4gICAgfSBlbHNlIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1BUzonKSA9PT0gMCkge1xuICAgICAgYmFuZHdpZHRoID0gcGFyc2VJbnQoYmFuZHdpZHRoWzBdLnN1YnN0cig1KSwgMTApO1xuICAgIH1cbiAgICBlbmNvZGluZ1BhcmFtZXRlcnMuZm9yRWFjaChmdW5jdGlvbihwYXJhbXMpIHtcbiAgICAgIHBhcmFtcy5tYXhCaXRyYXRlID0gYmFuZHdpZHRoO1xuICAgIH0pO1xuICB9XG4gIHJldHVybiBlbmNvZGluZ1BhcmFtZXRlcnM7XG59O1xuXG5TRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSA9IGZ1bmN0aW9uKCkge1xuICAvLyBGSVhNRTogc2Vzcy1pZCBzaG91bGQgYmUgYW4gTlRQIHRpbWVzdGFtcC5cbiAgcmV0dXJuICd2PTBcXHJcXG4nICtcbiAgICAgICdvPXRoaXNpc2FkYXB0ZXJvcnRjIDgxNjk2Mzk5MTU2NDY5NDMxMzcgMiBJTiBJUDQgMTI3LjAuMC4xXFxyXFxuJyArXG4gICAgICAncz0tXFxyXFxuJyArXG4gICAgICAndD0wIDBcXHJcXG4nO1xufTtcblxuU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24gPSBmdW5jdGlvbih0cmFuc2NlaXZlciwgY2FwcywgdHlwZSwgc3RyZWFtKSB7XG4gIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVJ0cERlc2NyaXB0aW9uKHRyYW5zY2VpdmVyLmtpbmQsIGNhcHMpO1xuXG4gIC8vIE1hcCBJQ0UgcGFyYW1ldGVycyAodWZyYWcsIHB3ZCkgdG8gU0RQLlxuICBzZHAgKz0gU0RQVXRpbHMud3JpdGVJY2VQYXJhbWV0ZXJzKFxuICAgICAgdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIuZ2V0TG9jYWxQYXJhbWV0ZXJzKCkpO1xuXG4gIC8vIE1hcCBEVExTIHBhcmFtZXRlcnMgdG8gU0RQLlxuICBzZHAgKz0gU0RQVXRpbHMud3JpdGVEdGxzUGFyYW1ldGVycyhcbiAgICAgIHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQuZ2V0TG9jYWxQYXJhbWV0ZXJzKCksXG4gICAgICB0eXBlID09PSAnb2ZmZXInID8gJ2FjdHBhc3MnIDogJ2FjdGl2ZScpO1xuXG4gIHNkcCArPSAnYT1taWQ6JyArIHRyYW5zY2VpdmVyLm1pZCArICdcXHJcXG4nO1xuXG4gIGlmICh0cmFuc2NlaXZlci5ydHBTZW5kZXIgJiYgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIpIHtcbiAgICBzZHAgKz0gJ2E9c2VuZHJlY3ZcXHJcXG4nO1xuICB9IGVsc2UgaWYgKHRyYW5zY2VpdmVyLnJ0cFNlbmRlcikge1xuICAgIHNkcCArPSAnYT1zZW5kb25seVxcclxcbic7XG4gIH0gZWxzZSBpZiAodHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIpIHtcbiAgICBzZHAgKz0gJ2E9cmVjdm9ubHlcXHJcXG4nO1xuICB9IGVsc2Uge1xuICAgIHNkcCArPSAnYT1pbmFjdGl2ZVxcclxcbic7XG4gIH1cblxuICAvLyBGSVhNRTogZm9yIFJUWCB0aGVyZSBtaWdodCBiZSBtdWx0aXBsZSBTU1JDcy4gTm90IGltcGxlbWVudGVkIGluIEVkZ2UgeWV0LlxuICBpZiAodHJhbnNjZWl2ZXIucnRwU2VuZGVyKSB7XG4gICAgdmFyIG1zaWQgPSAnbXNpZDonICsgc3RyZWFtLmlkICsgJyAnICtcbiAgICAgICAgdHJhbnNjZWl2ZXIucnRwU2VuZGVyLnRyYWNrLmlkICsgJ1xcclxcbic7XG4gICAgc2RwICs9ICdhPScgKyBtc2lkO1xuICAgIHNkcCArPSAnYT1zc3JjOicgKyB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmMgK1xuICAgICAgICAnICcgKyBtc2lkO1xuICB9XG4gIC8vIEZJWE1FOiB0aGlzIHNob3VsZCBiZSB3cml0dGVuIGJ5IHdyaXRlUnRwRGVzY3JpcHRpb24uXG4gIHNkcCArPSAnYT1zc3JjOicgKyB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmMgK1xuICAgICAgJyBjbmFtZTonICsgU0RQVXRpbHMubG9jYWxDTmFtZSArICdcXHJcXG4nO1xuICByZXR1cm4gc2RwO1xufTtcblxuLy8gR2V0cyB0aGUgZGlyZWN0aW9uIGZyb20gdGhlIG1lZGlhU2VjdGlvbiBvciB0aGUgc2Vzc2lvbnBhcnQuXG5TRFBVdGlscy5nZXREaXJlY3Rpb24gPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIC8vIExvb2sgZm9yIHNlbmRyZWN2LCBzZW5kb25seSwgcmVjdm9ubHksIGluYWN0aXZlLCBkZWZhdWx0IHRvIHNlbmRyZWN2LlxuICB2YXIgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKG1lZGlhU2VjdGlvbik7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICBzd2l0Y2ggKGxpbmVzW2ldKSB7XG4gICAgICBjYXNlICdhPXNlbmRyZWN2JzpcbiAgICAgIGNhc2UgJ2E9c2VuZG9ubHknOlxuICAgICAgY2FzZSAnYT1yZWN2b25seSc6XG4gICAgICBjYXNlICdhPWluYWN0aXZlJzpcbiAgICAgICAgcmV0dXJuIGxpbmVzW2ldLnN1YnN0cigyKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIC8vIEZJWE1FOiBXaGF0IHNob3VsZCBoYXBwZW4gaGVyZT9cbiAgICB9XG4gIH1cbiAgaWYgKHNlc3Npb25wYXJ0KSB7XG4gICAgcmV0dXJuIFNEUFV0aWxzLmdldERpcmVjdGlvbihzZXNzaW9ucGFydCk7XG4gIH1cbiAgcmV0dXJuICdzZW5kcmVjdic7XG59O1xuXG4vLyBFeHBvc2UgcHVibGljIG1ldGhvZHMuXG5tb2R1bGUuZXhwb3J0cyA9IFNEUFV0aWxzO1xuXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9+L3NkcC9zZHAuanNcbi8vIG1vZHVsZSBpZCA9IDQ1MlxuLy8gbW9kdWxlIGNodW5rcyA9IDAiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=");

FIXME found
Open

    eval(" /* eslint-env node */\n'use strict';\n\n// SDP helpers.\nvar 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(function(line) {\n    return line.trim();\n  });\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n  var parts = blob.split('\\nm=');\n  return parts.map(function(part, index) {\n    return (index > 0 ? 'm=' + part : part).trim() + '\\r\\n';\n  });\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n  return SDPUtils.splitLines(blob).filter(function(line) {\n    return line.indexOf(prefix) === 0;\n  });\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\"\nSDPUtils.parseCandidate = function(line) {\n  var 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  var candidate = {\n    foundation: parts[0],\n    component: parts[1],\n    protocol: parts[2].toLowerCase(),\n    priority: parseInt(parts[3], 10),\n    ip: parts[4],\n    port: parseInt(parts[5], 10),\n    // skip parts[6] == 'typ'\n    type: parts[7]\n  };\n\n  for (var 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      default: // Unknown extensions are silently ignored.\n        break;\n    }\n  }\n  return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\nSDPUtils.writeCandidate = function(candidate) {\n  var sdp = [];\n  sdp.push(candidate.foundation);\n  sdp.push(candidate.component);\n  sdp.push(candidate.protocol.toUpperCase());\n  sdp.push(candidate.priority);\n  sdp.push(candidate.ip);\n  sdp.push(candidate.port);\n\n  var 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); // was: relAddr\n    sdp.push('rport');\n    sdp.push(candidate.relatedPort); // was: relPort\n  }\n  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n    sdp.push('tcptype');\n    sdp.push(candidate.tcpType);\n  }\n  return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n  var parts = line.substr(9).split(' ');\n  var 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  // was: channels\n  parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n  return parsed;\n};\n\n// Generate an a=rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n      (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\\r\\n';\n};\n\n// Parses an a=extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n  var parts = line.substr(9).split(' ');\n  return {\n    id: parseInt(parts[0], 10),\n    uri: parts[1]\n  };\n};\n\n// Generates a=extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n       ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses an ftmp 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  var parsed = {};\n  var kv;\n  var parts = line.substr(line.indexOf(' ') + 1).split(';');\n  for (var 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 an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n  var line = '';\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  if (codec.parameters && Object.keys(codec.parameters).length) {\n    var params = [];\n    Object.keys(codec.parameters).forEach(function(param) {\n      params.push(param + '=' + codec.parameters[param]);\n    });\n    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n  }\n  return line;\n};\n\n// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n  var parts = line.substr(line.indexOf(' ') + 1).split(' ');\n  return {\n    type: parts.shift(),\n    parameter: parts.join(' ')\n  };\n};\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n  var lines = '';\n  var 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(function(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 an RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n  var sp = line.indexOf(' ');\n  var parts = {\n    ssrc: parseInt(line.substr(7, sp - 7), 10)\n  };\n  var 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// 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  var lines = SDPUtils.splitLines(mediaSection);\n  // Search in session part, too.\n  lines = lines.concat(SDPUtils.splitLines(sessionpart));\n  var fpLine = lines.filter(function(line) {\n    return line.indexOf('a=fingerprint:') === 0;\n  })[0].substr(14);\n  // Note: a=setup line is ignored since we use the 'auto' role.\n  var dtlsParameters = {\n    role: 'auto',\n    fingerprints: [{\n      algorithm: fpLine.split(' ')[0],\n      value: fpLine.split(' ')[1]\n    }]\n  };\n  return dtlsParameters;\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n  var sdp = 'a=setup:' + setupType + '\\r\\n';\n  params.fingerprints.forEach(function(fp) {\n    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n  });\n  return sdp;\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  var lines = SDPUtils.splitLines(mediaSection);\n  // Search in session part, too.\n  lines = lines.concat(SDPUtils.splitLines(sessionpart));\n  var iceParameters = {\n    usernameFragment: lines.filter(function(line) {\n      return line.indexOf('a=ice-ufrag:') === 0;\n    })[0].substr(12),\n    password: lines.filter(function(line) {\n      return line.indexOf('a=ice-pwd:') === 0;\n    })[0].substr(10)\n  };\n  return iceParameters;\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n  return 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n      'a=ice-pwd:' + params.password + '\\r\\n';\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n  var description = {\n    codecs: [],\n    headerExtensions: [],\n    fecMechanisms: [],\n    rtcp: []\n  };\n  var lines = SDPUtils.splitLines(mediaSection);\n  var mline = lines[0].split(' ');\n  for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n    var pt = mline[i];\n    var rtpmapline = SDPUtils.matchPrefix(\n        mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n    if (rtpmapline) {\n      var codec = SDPUtils.parseRtpMap(rtpmapline);\n      var fmtps = SDPUtils.matchPrefix(\n          mediaSection, 'a=fmtp:' + pt + ' ');\n      // Only the first a=fmtp:<pt> 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(function(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  var 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(function(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(function(codec) {\n    sdp += SDPUtils.writeRtpMap(codec);\n    sdp += SDPUtils.writeFmtp(codec);\n    sdp += SDPUtils.writeRtcpFb(codec);\n  });\n  sdp += 'a=rtcp-mux\\r\\n';\n\n  caps.headerExtensions.forEach(function(extension) {\n    sdp += SDPUtils.writeExtmap(extension);\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  var encodingParameters = [];\n  var description = SDPUtils.parseRtpParameters(mediaSection);\n  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n  // filter a=ssrc:... cname:, ignore PlanB-msid\n  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n  .map(function(line) {\n    return SDPUtils.parseSsrcMedia(line);\n  })\n  .filter(function(parts) {\n    return parts.attribute === 'cname';\n  });\n  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n  var secondarySsrc;\n\n  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n  .map(function(line) {\n    var parts = line.split(' ');\n    parts.shift();\n    return parts.map(function(part) {\n      return parseInt(part, 10);\n    });\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(function(codec) {\n    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n      var encParam = {\n        ssrc: primarySsrc,\n        codecPayloadType: parseInt(codec.parameters.apt, 10),\n        rtx: {\n          payloadType: codec.payloadType,\n          ssrc: secondarySsrc\n        }\n      };\n      encodingParameters.push(encParam);\n      if (hasRed) {\n        encParam = JSON.parse(JSON.stringify(encParam));\n        encParam.fec = {\n          ssrc: secondarySsrc,\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  var 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      bandwidth = parseInt(bandwidth[0].substr(5), 10);\n    }\n    encodingParameters.forEach(function(params) {\n      params.maxBitrate = bandwidth;\n    });\n  }\n  return encodingParameters;\n};\n\nSDPUtils.writeSessionBoilerplate = function() {\n  // FIXME: sess-id should be an NTP timestamp.\n  return 'v=0\\r\\n' +\n      'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\\r\\n' +\n      's=-\\r\\n' +\n      't=0 0\\r\\n';\n};\n\nSDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {\n  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);\n\n  // Map ICE parameters (ufrag, pwd) to SDP.\n  sdp += SDPUtils.writeIceParameters(\n      transceiver.iceGatherer.getLocalParameters());\n\n  // Map DTLS parameters to SDP.\n  sdp += SDPUtils.writeDtlsParameters(\n      transceiver.dtlsTransport.getLocalParameters(),\n      type === 'offer' ? 'actpass' : 'active');\n\n  sdp += 'a=mid:' + transceiver.mid + '\\r\\n';\n\n  if (transceiver.rtpSender && transceiver.rtpReceiver) {\n    sdp += 'a=sendrecv\\r\\n';\n  } else if (transceiver.rtpSender) {\n    sdp += 'a=sendonly\\r\\n';\n  } else if (transceiver.rtpReceiver) {\n    sdp += 'a=recvonly\\r\\n';\n  } else {\n    sdp += 'a=inactive\\r\\n';\n  }\n\n  // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.\n  if (transceiver.rtpSender) {\n    var msid = 'msid:' + stream.id + ' ' +\n        transceiver.rtpSender.track.id + '\\r\\n';\n    sdp += 'a=' + msid;\n    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n        ' ' + msid;\n  }\n  // FIXME: this should be written by writeRtpDescription.\n  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n      ' cname:' + SDPUtils.localCName + '\\r\\n';\n  return sdp;\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  var lines = SDPUtils.splitLines(mediaSection);\n  for (var 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\n// Expose public methods.\nmodule.exports = SDPUtils;\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDUyLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9zZHAvc2RwLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIiAvKiBlc2xpbnQtZW52IG5vZGUgKi9cbid1c2Ugc3RyaWN0JztcblxuLy8gU0RQIGhlbHBlcnMuXG52YXIgU0RQVXRpbHMgPSB7fTtcblxuLy8gR2VuZXJhdGUgYW4gYWxwaGFudW1lcmljIGlkZW50aWZpZXIgZm9yIGNuYW1lIG9yIG1pZHMuXG4vLyBUT0RPOiB1c2UgVVVJRHMgaW5zdGVhZD8gaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vamVkLzk4Mjg4M1xuU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTApO1xufTtcblxuLy8gVGhlIFJUQ1AgQ05BTUUgdXNlZCBieSBhbGwgcGVlcmNvbm5lY3Rpb25zIGZyb20gdGhlIHNhbWUgSlMuXG5TRFBVdGlscy5sb2NhbENOYW1lID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG5cbi8vIFNwbGl0cyBTRFAgaW50byBsaW5lcywgZGVhbGluZyB3aXRoIGJvdGggQ1JMRiBhbmQgTEYuXG5TRFBVdGlscy5zcGxpdExpbmVzID0gZnVuY3Rpb24oYmxvYikge1xuICByZXR1cm4gYmxvYi50cmltKCkuc3BsaXQoJ1xcbicpLm1hcChmdW5jdGlvbihsaW5lKSB7XG4gICAgcmV0dXJuIGxpbmUudHJpbSgpO1xuICB9KTtcbn07XG4vLyBTcGxpdHMgU0RQIGludG8gc2Vzc2lvbnBhcnQgYW5kIG1lZGlhc2VjdGlvbnMuIEVuc3VyZXMgQ1JMRi5cblNEUFV0aWxzLnNwbGl0U2VjdGlvbnMgPSBmdW5jdGlvbihibG9iKSB7XG4gIHZhciBwYXJ0cyA9IGJsb2Iuc3BsaXQoJ1xcbm09Jyk7XG4gIHJldHVybiBwYXJ0cy5tYXAoZnVuY3Rpb24ocGFydCwgaW5kZXgpIHtcbiAgICByZXR1cm4gKGluZGV4ID4gMCA/ICdtPScgKyBwYXJ0IDogcGFydCkudHJpbSgpICsgJ1xcclxcbic7XG4gIH0pO1xufTtcblxuLy8gUmV0dXJucyBsaW5lcyB0aGF0IHN0YXJ0IHdpdGggYSBjZXJ0YWluIHByZWZpeC5cblNEUFV0aWxzLm1hdGNoUHJlZml4ID0gZnVuY3Rpb24oYmxvYiwgcHJlZml4KSB7XG4gIHJldHVybiBTRFBVdGlscy5zcGxpdExpbmVzKGJsb2IpLmZpbHRlcihmdW5jdGlvbihsaW5lKSB7XG4gICAgcmV0dXJuIGxpbmUuaW5kZXhPZihwcmVmaXgpID09PSAwO1xuICB9KTtcbn07XG5cbi8vIFBhcnNlcyBhbiBJQ0UgY2FuZGlkYXRlIGxpbmUuIFNhbXBsZSBpbnB1dDpcbi8vIGNhbmRpZGF0ZTo3MDI3ODYzNTAgMiB1ZHAgNDE4MTk5MDIgOC44LjguOCA2MDc2OSB0eXAgcmVsYXkgcmFkZHIgOC44LjguOFxuLy8gcnBvcnQgNTU5OTZcIlxuU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBwYXJ0cztcbiAgLy8gUGFyc2UgYm90aCB2YXJpYW50cy5cbiAgaWYgKGxpbmUuaW5kZXhPZignYT1jYW5kaWRhdGU6JykgPT09IDApIHtcbiAgICBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDEyKS5zcGxpdCgnICcpO1xuICB9IGVsc2Uge1xuICAgIHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMTApLnNwbGl0KCcgJyk7XG4gIH1cblxuICB2YXIgY2FuZGlkYXRlID0ge1xuICAgIGZvdW5kYXRpb246IHBhcnRzWzBdLFxuICAgIGNvbXBvbmVudDogcGFydHNbMV0sXG4gICAgcHJvdG9jb2w6IHBhcnRzWzJdLnRvTG93ZXJDYXNlKCksXG4gICAgcHJpb3JpdHk6IHBhcnNlSW50KHBhcnRzWzNdLCAxMCksXG4gICAgaXA6IHBhcnRzWzRdLFxuICAgIHBvcnQ6IHBhcnNlSW50KHBhcnRzWzVdLCAxMCksXG4gICAgLy8gc2tpcCBwYXJ0c1s2XSA9PSAndHlwJ1xuICAgIHR5cGU6IHBhcnRzWzddXG4gIH07XG5cbiAgZm9yICh2YXIgaSA9IDg7IGkgPCBwYXJ0cy5sZW5ndGg7IGkgKz0gMikge1xuICAgIHN3aXRjaCAocGFydHNbaV0pIHtcbiAgICAgIGNhc2UgJ3JhZGRyJzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3Jwb3J0JzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRQb3J0ID0gcGFyc2VJbnQocGFydHNbaSArIDFdLCAxMCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgIGNhbmRpZGF0ZS50Y3BUeXBlID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGRlZmF1bHQ6IC8vIFVua25vd24gZXh0ZW5zaW9ucyBhcmUgc2lsZW50bHkgaWdub3JlZC5cbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG4gIHJldHVybiBjYW5kaWRhdGU7XG59O1xuXG4vLyBUcmFuc2xhdGVzIGEgY2FuZGlkYXRlIG9iamVjdCBpbnRvIFNEUCBjYW5kaWRhdGUgYXR0cmlidXRlLlxuU0RQVXRpbHMud3JpdGVDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgdmFyIHNkcCA9IFtdO1xuICBzZHAucHVzaChjYW5kaWRhdGUuZm91bmRhdGlvbik7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5jb21wb25lbnQpO1xuICBzZHAucHVzaChjYW5kaWRhdGUucHJvdG9jb2wudG9VcHBlckNhc2UoKSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wcmlvcml0eSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5pcCk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wb3J0KTtcblxuICB2YXIgdHlwZSA9IGNhbmRpZGF0ZS50eXBlO1xuICBzZHAucHVzaCgndHlwJyk7XG4gIHNkcC5wdXNoKHR5cGUpO1xuICBpZiAodHlwZSAhPT0gJ2hvc3QnICYmIGNhbmRpZGF0ZS5yZWxhdGVkQWRkcmVzcyAmJlxuICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRQb3J0KSB7XG4gICAgc2RwLnB1c2goJ3JhZGRyJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzKTsgLy8gd2FzOiByZWxBZGRyXG4gICAgc2RwLnB1c2goJ3Jwb3J0Jyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnJlbGF0ZWRQb3J0KTsgLy8gd2FzOiByZWxQb3J0XG4gIH1cbiAgaWYgKGNhbmRpZGF0ZS50Y3BUeXBlICYmIGNhbmRpZGF0ZS5wcm90b2NvbC50b0xvd2VyQ2FzZSgpID09PSAndGNwJykge1xuICAgIHNkcC5wdXNoKCd0Y3B0eXBlJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnRjcFR5cGUpO1xuICB9XG4gIHJldHVybiAnY2FuZGlkYXRlOicgKyBzZHAuam9pbignICcpO1xufTtcblxuLy8gUGFyc2VzIGFuIHJ0cG1hcCBsaW5lLCByZXR1cm5zIFJUQ1J0cENvZGRlY1BhcmFtZXRlcnMuIFNhbXBsZSBpbnB1dDpcbi8vIGE9cnRwbWFwOjExMSBvcHVzLzQ4MDAwLzJcblNEUFV0aWxzLnBhcnNlUnRwTWFwID0gZnVuY3Rpb24obGluZSkge1xuICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cig5KS5zcGxpdCgnICcpO1xuICB2YXIgcGFyc2VkID0ge1xuICAgIHBheWxvYWRUeXBlOiBwYXJzZUludChwYXJ0cy5zaGlmdCgpLCAxMCkgLy8gd2FzOiBpZFxuICB9O1xuXG4gIHBhcnRzID0gcGFydHNbMF0uc3BsaXQoJy8nKTtcblxuICBwYXJzZWQubmFtZSA9IHBhcnRzWzBdO1xuICBwYXJzZWQuY2xvY2tSYXRlID0gcGFyc2VJbnQocGFydHNbMV0sIDEwKTsgLy8gd2FzOiBjbG9ja3JhdGVcbiAgLy8gd2FzOiBjaGFubmVsc1xuICBwYXJzZWQubnVtQ2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPT09IDMgPyBwYXJzZUludChwYXJ0c1syXSwgMTApIDogMTtcbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlIGFuIGE9cnRwbWFwIGxpbmUgZnJvbSBSVENSdHBDb2RlY0NhcGFiaWxpdHkgb3Jcbi8vIFJUQ1J0cENvZGVjUGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlUnRwTWFwID0gZnVuY3Rpb24oY29kZWMpIHtcbiAgdmFyIHB0ID0gY29kZWMucGF5bG9hZFR5cGU7XG4gIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcHQgPSBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgfVxuICByZXR1cm4gJ2E9cnRwbWFwOicgKyBwdCArICcgJyArIGNvZGVjLm5hbWUgKyAnLycgKyBjb2RlYy5jbG9ja1JhdGUgK1xuICAgICAgKGNvZGVjLm51bUNoYW5uZWxzICE9PSAxID8gJy8nICsgY29kZWMubnVtQ2hhbm5lbHMgOiAnJykgKyAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyBhbiBhPWV4dG1hcCBsaW5lIChoZWFkZXJleHRlbnNpb24gZnJvbSBSRkMgNTI4NSkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9ZXh0bWFwOjIgdXJuOmlldGY6cGFyYW1zOnJ0cC1oZHJleHQ6dG9mZnNldFxuU0RQVXRpbHMucGFyc2VFeHRtYXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyKDkpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgaWQ6IHBhcnNlSW50KHBhcnRzWzBdLCAxMCksXG4gICAgdXJpOiBwYXJ0c1sxXVxuICB9O1xufTtcblxuLy8gR2VuZXJhdGVzIGE9ZXh0bWFwIGxpbmUgZnJvbSBSVENSdHBIZWFkZXJFeHRlbnNpb25QYXJhbWV0ZXJzIG9yXG4vLyBSVENSdHBIZWFkZXJFeHRlbnNpb24uXG5TRFBVdGlscy53cml0ZUV4dG1hcCA9IGZ1bmN0aW9uKGhlYWRlckV4dGVuc2lvbikge1xuICByZXR1cm4gJ2E9ZXh0bWFwOicgKyAoaGVhZGVyRXh0ZW5zaW9uLmlkIHx8IGhlYWRlckV4dGVuc2lvbi5wcmVmZXJyZWRJZCkgK1xuICAgICAgICcgJyArIGhlYWRlckV4dGVuc2lvbi51cmkgKyAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyBhbiBmdG1wIGxpbmUsIHJldHVybnMgZGljdGlvbmFyeS4gU2FtcGxlIGlucHV0OlxuLy8gYT1mbXRwOjk2IHZicj1vbjtjbmc9b25cbi8vIEFsc28gZGVhbHMgd2l0aCB2YnI9b247IGNuZz1vblxuU0RQVXRpbHMucGFyc2VGbXRwID0gZnVuY3Rpb24obGluZSkge1xuICB2YXIgcGFyc2VkID0ge307XG4gIHZhciBrdjtcbiAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIobGluZS5pbmRleE9mKCcgJykgKyAxKS5zcGxpdCgnOycpO1xuICBmb3IgKHZhciBqID0gMDsgaiA8IHBhcnRzLmxlbmd0aDsgaisrKSB7XG4gICAga3YgPSBwYXJ0c1tqXS50cmltKCkuc3BsaXQoJz0nKTtcbiAgICBwYXJzZWRba3ZbMF0udHJpbSgpXSA9IGt2WzFdO1xuICB9XG4gIHJldHVybiBwYXJzZWQ7XG59O1xuXG4vLyBHZW5lcmF0ZXMgYW4gYT1mdG1wIGxpbmUgZnJvbSBSVENSdHBDb2RlY0NhcGFiaWxpdHkgb3IgUlRDUnRwQ29kZWNQYXJhbWV0ZXJzLlxuU0RQVXRpbHMud3JpdGVGbXRwID0gZnVuY3Rpb24oY29kZWMpIHtcbiAgdmFyIGxpbmUgPSAnJztcbiAgdmFyIHB0ID0gY29kZWMucGF5bG9hZFR5cGU7XG4gIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcHQgPSBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgfVxuICBpZiAoY29kZWMucGFyYW1ldGVycyAmJiBPYmplY3Qua2V5cyhjb2RlYy5wYXJhbWV0ZXJzKS5sZW5ndGgpIHtcbiAgICB2YXIgcGFyYW1zID0gW107XG4gICAgT2JqZWN0LmtleXMoY29kZWMucGFyYW1ldGVycykuZm9yRWFjaChmdW5jdGlvbihwYXJhbSkge1xuICAgICAgcGFyYW1zLnB1c2gocGFyYW0gKyAnPScgKyBjb2RlYy5wYXJhbWV0ZXJzW3BhcmFtXSk7XG4gICAgfSk7XG4gICAgbGluZSArPSAnYT1mbXRwOicgKyBwdCArICcgJyArIHBhcmFtcy5qb2luKCc7JykgKyAnXFxyXFxuJztcbiAgfVxuICByZXR1cm4gbGluZTtcbn07XG5cbi8vIFBhcnNlcyBhbiBydGNwLWZiIGxpbmUsIHJldHVybnMgUlRDUFJ0Y3BGZWVkYmFjayBvYmplY3QuIFNhbXBsZSBpbnB1dDpcbi8vIGE9cnRjcC1mYjo5OCBuYWNrIHJwc2lcblNEUFV0aWxzLnBhcnNlUnRjcEZiID0gZnVuY3Rpb24obGluZSkge1xuICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cihsaW5lLmluZGV4T2YoJyAnKSArIDEpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgdHlwZTogcGFydHMuc2hpZnQoKSxcbiAgICBwYXJhbWV0ZXI6IHBhcnRzLmpvaW4oJyAnKVxuICB9O1xufTtcbi8vIEdlbmVyYXRlIGE9cnRjcC1mYiBsaW5lcyBmcm9tIFJUQ1J0cENvZGVjQ2FwYWJpbGl0eSBvciBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZVJ0Y3BGYiA9IGZ1bmN0aW9uKGNvZGVjKSB7XG4gIHZhciBsaW5lcyA9ICcnO1xuICB2YXIgcHQgPSBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgaWYgKGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICBwdCA9IGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICB9XG4gIGlmIChjb2RlYy5ydGNwRmVlZGJhY2sgJiYgY29kZWMucnRjcEZlZWRiYWNrLmxlbmd0aCkge1xuICAgIC8vIEZJWE1FOiBzcGVjaWFsIGhhbmRsaW5nIGZvciB0cnItaW50P1xuICAgIGNvZGVjLnJ0Y3BGZWVkYmFjay5mb3JFYWNoKGZ1bmN0aW9uKGZiKSB7XG4gICAgICBsaW5lcyArPSAnYT1ydGNwLWZiOicgKyBwdCArICcgJyArIGZiLnR5cGUgK1xuICAgICAgKGZiLnBhcmFtZXRlciAmJiBmYi5wYXJhbWV0ZXIubGVuZ3RoID8gJyAnICsgZmIucGFyYW1ldGVyIDogJycpICtcbiAgICAgICAgICAnXFxyXFxuJztcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gbGluZXM7XG59O1xuXG4vLyBQYXJzZXMgYW4gUkZDIDU1NzYgc3NyYyBtZWRpYSBhdHRyaWJ1dGUuIFNhbXBsZSBpbnB1dDpcbi8vIGE9c3NyYzozNzM1OTI4NTU5IGNuYW1lOnNvbWV0aGluZ1xuU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBzcCA9IGxpbmUuaW5kZXhPZignICcpO1xuICB2YXIgcGFydHMgPSB7XG4gICAgc3NyYzogcGFyc2VJbnQobGluZS5zdWJzdHIoNywgc3AgLSA3KSwgMTApXG4gIH07XG4gIHZhciBjb2xvbiA9IGxpbmUuaW5kZXhPZignOicsIHNwKTtcbiAgaWYgKGNvbG9uID4gLTEpIHtcbiAgICBwYXJ0cy5hdHRyaWJ1dGUgPSBsaW5lLnN1YnN0cihzcCArIDEsIGNvbG9uIC0gc3AgLSAxKTtcbiAgICBwYXJ0cy52YWx1ZSA9IGxpbmUuc3Vic3RyKGNvbG9uICsgMSk7XG4gIH0gZWxzZSB7XG4gICAgcGFydHMuYXR0cmlidXRlID0gbGluZS5zdWJzdHIoc3AgKyAxKTtcbiAgfVxuICByZXR1cm4gcGFydHM7XG59O1xuXG4vLyBFeHRyYWN0cyBEVExTIHBhcmFtZXRlcnMgZnJvbSBTRFAgbWVkaWEgc2VjdGlvbiBvciBzZXNzaW9ucGFydC5cbi8vIEZJWE1FOiBmb3IgY29uc2lzdGVuY3kgd2l0aCBvdGhlciBmdW5jdGlvbnMgdGhpcyBzaG91bGQgb25seVxuLy8gICBnZXQgdGhlIGZpbmdlcnByaW50IGxpbmUgYXMgaW5wdXQuIFNlZSBhbHNvIGdldEljZVBhcmFtZXRlcnMuXG5TRFBVdGlscy5nZXREdGxzUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAvLyBTZWFyY2ggaW4gc2Vzc2lvbiBwYXJ0LCB0b28uXG4gIGxpbmVzID0gbGluZXMuY29uY2F0KFNEUFV0aWxzLnNwbGl0TGluZXMoc2Vzc2lvbnBhcnQpKTtcbiAgdmFyIGZwTGluZSA9IGxpbmVzLmZpbHRlcihmdW5jdGlvbihsaW5lKSB7XG4gICAgcmV0dXJuIGxpbmUuaW5kZXhPZignYT1maW5nZXJwcmludDonKSA9PT0gMDtcbiAgfSlbMF0uc3Vic3RyKDE0KTtcbiAgLy8gTm90ZTogYT1zZXR1cCBsaW5lIGlzIGlnbm9yZWQgc2luY2Ugd2UgdXNlIHRoZSAnYXV0bycgcm9sZS5cbiAgdmFyIGR0bHNQYXJhbWV0ZXJzID0ge1xuICAgIHJvbGU6ICdhdXRvJyxcbiAgICBmaW5nZXJwcmludHM6IFt7XG4gICAgICBhbGdvcml0aG06IGZwTGluZS5zcGxpdCgnICcpWzBdLFxuICAgICAgdmFsdWU6IGZwTGluZS5zcGxpdCgnICcpWzFdXG4gICAgfV1cbiAgfTtcbiAgcmV0dXJuIGR0bHNQYXJhbWV0ZXJzO1xufTtcblxuLy8gU2VyaWFsaXplcyBEVExTIHBhcmFtZXRlcnMgdG8gU0RQLlxuU0RQVXRpbHMud3JpdGVEdGxzUGFyYW1ldGVycyA9IGZ1bmN0aW9uKHBhcmFtcywgc2V0dXBUeXBlKSB7XG4gIHZhciBzZHAgPSAnYT1zZXR1cDonICsgc2V0dXBUeXBlICsgJ1xcclxcbic7XG4gIHBhcmFtcy5maW5nZXJwcmludHMuZm9yRWFjaChmdW5jdGlvbihmcCkge1xuICAgIHNkcCArPSAnYT1maW5nZXJwcmludDonICsgZnAuYWxnb3JpdGhtICsgJyAnICsgZnAudmFsdWUgKyAnXFxyXFxuJztcbiAgfSk7XG4gIHJldHVybiBzZHA7XG59O1xuLy8gUGFyc2VzIElDRSBpbmZvcm1hdGlvbiBmcm9tIFNEUCBtZWRpYSBzZWN0aW9uIG9yIHNlc3Npb25wYXJ0LlxuLy8gRklYTUU6IGZvciBjb25zaXN0ZW5jeSB3aXRoIG90aGVyIGZ1bmN0aW9ucyB0aGlzIHNob3VsZCBvbmx5XG4vLyAgIGdldCB0aGUgaWNlLXVmcmFnIGFuZCBpY2UtcHdkIGxpbmVzIGFzIGlucHV0LlxuU0RQVXRpbHMuZ2V0SWNlUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAvLyBTZWFyY2ggaW4gc2Vzc2lvbiBwYXJ0LCB0b28uXG4gIGxpbmVzID0gbGluZXMuY29uY2F0KFNEUFV0aWxzLnNwbGl0TGluZXMoc2Vzc2lvbnBhcnQpKTtcbiAgdmFyIGljZVBhcmFtZXRlcnMgPSB7XG4gICAgdXNlcm5hbWVGcmFnbWVudDogbGluZXMuZmlsdGVyKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgIHJldHVybiBsaW5lLmluZGV4T2YoJ2E9aWNlLXVmcmFnOicpID09PSAwO1xuICAgIH0pWzBdLnN1YnN0cigxMiksXG4gICAgcGFzc3dvcmQ6IGxpbmVzLmZpbHRlcihmdW5jdGlvbihsaW5lKSB7XG4gICAgICByZXR1cm4gbGluZS5pbmRleE9mKCdhPWljZS1wd2Q6JykgPT09IDA7XG4gICAgfSlbMF0uc3Vic3RyKDEwKVxuICB9O1xuICByZXR1cm4gaWNlUGFyYW1ldGVycztcbn07XG5cbi8vIFNlcmlhbGl6ZXMgSUNFIHBhcmFtZXRlcnMgdG8gU0RQLlxuU0RQVXRpbHMud3JpdGVJY2VQYXJhbWV0ZXJzID0gZnVuY3Rpb24ocGFyYW1zKSB7XG4gIHJldHVybiAnYT1pY2UtdWZyYWc6JyArIHBhcmFtcy51c2VybmFtZUZyYWdtZW50ICsgJ1xcclxcbicgK1xuICAgICAgJ2E9aWNlLXB3ZDonICsgcGFyYW1zLnBhc3N3b3JkICsgJ1xcclxcbic7XG59O1xuXG4vLyBQYXJzZXMgdGhlIFNEUCBtZWRpYSBzZWN0aW9uIGFuZCByZXR1cm5zIFJUQ1J0cFBhcmFtZXRlcnMuXG5TRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgdmFyIGRlc2NyaXB0aW9uID0ge1xuICAgIGNvZGVjczogW10sXG4gICAgaGVhZGVyRXh0ZW5zaW9uczogW10sXG4gICAgZmVjTWVjaGFuaXNtczogW10sXG4gICAgcnRjcDogW11cbiAgfTtcbiAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICB2YXIgbWxpbmUgPSBsaW5lc1swXS5zcGxpdCgnICcpO1xuICBmb3IgKHZhciBpID0gMzsgaSA8IG1saW5lLmxlbmd0aDsgaSsrKSB7IC8vIGZpbmQgYWxsIGNvZGVjcyBmcm9tIG1saW5lWzMuLl1cbiAgICB2YXIgcHQgPSBtbGluZVtpXTtcbiAgICB2YXIgcnRwbWFwbGluZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KFxuICAgICAgICBtZWRpYVNlY3Rpb24sICdhPXJ0cG1hcDonICsgcHQgKyAnICcpWzBdO1xuICAgIGlmIChydHBtYXBsaW5lKSB7XG4gICAgICB2YXIgY29kZWMgPSBTRFBVdGlscy5wYXJzZVJ0cE1hcChydHBtYXBsaW5lKTtcbiAgICAgIHZhciBmbXRwcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KFxuICAgICAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9Zm10cDonICsgcHQgKyAnICcpO1xuICAgICAgLy8gT25seSB0aGUgZmlyc3QgYT1mbXRwOjxwdD4gaXMgY29uc2lkZXJlZC5cbiAgICAgIGNvZGVjLnBhcmFtZXRlcnMgPSBmbXRwcy5sZW5ndGggPyBTRFBVdGlscy5wYXJzZUZtdHAoZm10cHNbMF0pIDoge307XG4gICAgICBjb2RlYy5ydGNwRmVlZGJhY2sgPSBTRFBVdGlscy5tYXRjaFByZWZpeChcbiAgICAgICAgICBtZWRpYVNlY3Rpb24sICdhPXJ0Y3AtZmI6JyArIHB0ICsgJyAnKVxuICAgICAgICAubWFwKFNEUFV0aWxzLnBhcnNlUnRjcEZiKTtcbiAgICAgIGRlc2NyaXB0aW9uLmNvZGVjcy5wdXNoKGNvZGVjKTtcbiAgICAgIC8vIHBhcnNlIEZFQyBtZWNoYW5pc21zIGZyb20gcnRwbWFwIGxpbmVzLlxuICAgICAgc3dpdGNoIChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkpIHtcbiAgICAgICAgY2FzZSAnUkVEJzpcbiAgICAgICAgY2FzZSAnVUxQRkVDJzpcbiAgICAgICAgICBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLnB1c2goY29kZWMubmFtZS50b1VwcGVyQ2FzZSgpKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDogLy8gb25seSBSRUQgYW5kIFVMUEZFQyBhcmUgcmVjb2duaXplZCBhcyBGRUMgbWVjaGFuaXNtcy5cbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1leHRtYXA6JykuZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgZGVzY3JpcHRpb24uaGVhZGVyRXh0ZW5zaW9ucy5wdXNoKFNEUFV0aWxzLnBhcnNlRXh0bWFwKGxpbmUpKTtcbiAgfSk7XG4gIC8vIEZJWE1FOiBwYXJzZSBydGNwLlxuICByZXR1cm4gZGVzY3JpcHRpb247XG59O1xuXG4vLyBHZW5lcmF0ZXMgcGFydHMgb2YgdGhlIFNEUCBtZWRpYSBzZWN0aW9uIGRlc2NyaWJpbmcgdGhlIGNhcGFiaWxpdGllcyAvXG4vLyBwYXJhbWV0ZXJzLlxuU0RQVXRpbHMud3JpdGVSdHBEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uKGtpbmQsIGNhcHMpIHtcbiAgdmFyIHNkcCA9ICcnO1xuXG4gIC8vIEJ1aWxkIHRoZSBtbGluZS5cbiAgc2RwICs9ICdtPScgKyBraW5kICsgJyAnO1xuICBzZHAgKz0gY2Fwcy5jb2RlY3MubGVuZ3RoID4gMCA/ICc5JyA6ICcwJzsgLy8gcmVqZWN0IGlmIG5vIGNvZGVjcy5cbiAgc2RwICs9ICcgVURQL1RMUy9SVFAvU0FWUEYgJztcbiAgc2RwICs9IGNhcHMuY29kZWNzLm1hcChmdW5jdGlvbihjb2RlYykge1xuICAgIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gICAgfVxuICAgIHJldHVybiBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgfSkuam9pbignICcpICsgJ1xcclxcbic7XG5cbiAgc2RwICs9ICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJztcbiAgc2RwICs9ICdhPXJ0Y3A6OSBJTiBJUDQgMC4wLjAuMFxcclxcbic7XG5cbiAgLy8gQWRkIGE9cnRwbWFwIGxpbmVzIGZvciBlYWNoIGNvZGVjLiBBbHNvIGZtdHAgYW5kIHJ0Y3AtZmIuXG4gIGNhcHMuY29kZWNzLmZvckVhY2goZnVuY3Rpb24oY29kZWMpIHtcbiAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVSdHBNYXAoY29kZWMpO1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZUZtdHAoY29kZWMpO1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZVJ0Y3BGYihjb2RlYyk7XG4gIH0pO1xuICBzZHAgKz0gJ2E9cnRjcC1tdXhcXHJcXG4nO1xuXG4gIGNhcHMuaGVhZGVyRXh0ZW5zaW9ucy5mb3JFYWNoKGZ1bmN0aW9uKGV4dGVuc2lvbikge1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZUV4dG1hcChleHRlbnNpb24pO1xuICB9KTtcbiAgLy8gRklYTUU6IHdyaXRlIGZlY01lY2hhbmlzbXMuXG4gIHJldHVybiBzZHA7XG59O1xuXG4vLyBQYXJzZXMgdGhlIFNEUCBtZWRpYSBzZWN0aW9uIGFuZCByZXR1cm5zIGFuIGFycmF5IG9mXG4vLyBSVENSdHBFbmNvZGluZ1BhcmFtZXRlcnMuXG5TRFBVdGlscy5wYXJzZVJ0cEVuY29kaW5nUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICB2YXIgZW5jb2RpbmdQYXJhbWV0ZXJzID0gW107XG4gIHZhciBkZXNjcmlwdGlvbiA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICB2YXIgaGFzUmVkID0gZGVzY3JpcHRpb24uZmVjTWVjaGFuaXNtcy5pbmRleE9mKCdSRUQnKSAhPT0gLTE7XG4gIHZhciBoYXNVbHBmZWMgPSBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLmluZGV4T2YoJ1VMUEZFQycpICE9PSAtMTtcblxuICAvLyBmaWx0ZXIgYT1zc3JjOi4uLiBjbmFtZTosIGlnbm9yZSBQbGFuQi1tc2lkXG4gIHZhciBzc3JjcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAubWFwKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICByZXR1cm4gU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEobGluZSk7XG4gIH0pXG4gIC5maWx0ZXIoZnVuY3Rpb24ocGFydHMpIHtcbiAgICByZXR1cm4gcGFydHMuYXR0cmlidXRlID09PSAnY25hbWUnO1xuICB9KTtcbiAgdmFyIHByaW1hcnlTc3JjID0gc3NyY3MubGVuZ3RoID4gMCAmJiBzc3Jjc1swXS5zc3JjO1xuICB2YXIgc2Vjb25kYXJ5U3NyYztcblxuICB2YXIgZmxvd3MgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNzcmMtZ3JvdXA6RklEJylcbiAgLm1hcChmdW5jdGlvbihsaW5lKSB7XG4gICAgdmFyIHBhcnRzID0gbGluZS5zcGxpdCgnICcpO1xuICAgIHBhcnRzLnNoaWZ0KCk7XG4gICAgcmV0dXJuIHBhcnRzLm1hcChmdW5jdGlvbihwYXJ0KSB7XG4gICAgICByZXR1cm4gcGFyc2VJbnQocGFydCwgMTApO1xuICAgIH0pO1xuICB9KTtcbiAgaWYgKGZsb3dzLmxlbmd0aCA+IDAgJiYgZmxvd3NbMF0ubGVuZ3RoID4gMSAmJiBmbG93c1swXVswXSA9PT0gcHJpbWFyeVNzcmMpIHtcbiAgICBzZWNvbmRhcnlTc3JjID0gZmxvd3NbMF1bMV07XG4gIH1cblxuICBkZXNjcmlwdGlvbi5jb2RlY3MuZm9yRWFjaChmdW5jdGlvbihjb2RlYykge1xuICAgIGlmIChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkgPT09ICdSVFgnICYmIGNvZGVjLnBhcmFtZXRlcnMuYXB0KSB7XG4gICAgICB2YXIgZW5jUGFyYW0gPSB7XG4gICAgICAgIHNzcmM6IHByaW1hcnlTc3JjLFxuICAgICAgICBjb2RlY1BheWxvYWRUeXBlOiBwYXJzZUludChjb2RlYy5wYXJhbWV0ZXJzLmFwdCwgMTApLFxuICAgICAgICBydHg6IHtcbiAgICAgICAgICBwYXlsb2FkVHlwZTogY29kZWMucGF5bG9hZFR5cGUsXG4gICAgICAgICAgc3NyYzogc2Vjb25kYXJ5U3NyY1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgZW5jb2RpbmdQYXJhbWV0ZXJzLnB1c2goZW5jUGFyYW0pO1xuICAgICAgaWYgKGhhc1JlZCkge1xuICAgICAgICBlbmNQYXJhbSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkoZW5jUGFyYW0pKTtcbiAgICAgICAgZW5jUGFyYW0uZmVjID0ge1xuICAgICAgICAgIHNzcmM6IHNlY29uZGFyeVNzcmMsXG4gICAgICAgICAgbWVjaGFuaXNtOiBoYXNVbHBmZWMgPyAncmVkK3VscGZlYycgOiAncmVkJ1xuICAgICAgICB9O1xuICAgICAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaChlbmNQYXJhbSk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcbiAgaWYgKGVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGggPT09IDAgJiYgcHJpbWFyeVNzcmMpIHtcbiAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaCh7XG4gICAgICBzc3JjOiBwcmltYXJ5U3NyY1xuICAgIH0pO1xuICB9XG5cbiAgLy8gd2Ugc3VwcG9ydCBib3RoIGI9QVMgYW5kIGI9VElBUyBidXQgaW50ZXJwcmV0IEFTIGFzIFRJQVMuXG4gIHZhciBiYW5kd2lkdGggPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdiPScpO1xuICBpZiAoYmFuZHdpZHRoLmxlbmd0aCkge1xuICAgIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1USUFTOicpID09PSAwKSB7XG4gICAgICBiYW5kd2lkdGggPSBwYXJzZUludChiYW5kd2lkdGhbMF0uc3Vic3RyKDcpLCAxMCk7XG4gICAgfSBlbHNlIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1BUzonKSA9PT0gMCkge1xuICAgICAgYmFuZHdpZHRoID0gcGFyc2VJbnQoYmFuZHdpZHRoWzBdLnN1YnN0cig1KSwgMTApO1xuICAgIH1cbiAgICBlbmNvZGluZ1BhcmFtZXRlcnMuZm9yRWFjaChmdW5jdGlvbihwYXJhbXMpIHtcbiAgICAgIHBhcmFtcy5tYXhCaXRyYXRlID0gYmFuZHdpZHRoO1xuICAgIH0pO1xuICB9XG4gIHJldHVybiBlbmNvZGluZ1BhcmFtZXRlcnM7XG59O1xuXG5TRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSA9IGZ1bmN0aW9uKCkge1xuICAvLyBGSVhNRTogc2Vzcy1pZCBzaG91bGQgYmUgYW4gTlRQIHRpbWVzdGFtcC5cbiAgcmV0dXJuICd2PTBcXHJcXG4nICtcbiAgICAgICdvPXRoaXNpc2FkYXB0ZXJvcnRjIDgxNjk2Mzk5MTU2NDY5NDMxMzcgMiBJTiBJUDQgMTI3LjAuMC4xXFxyXFxuJyArXG4gICAgICAncz0tXFxyXFxuJyArXG4gICAgICAndD0wIDBcXHJcXG4nO1xufTtcblxuU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24gPSBmdW5jdGlvbih0cmFuc2NlaXZlciwgY2FwcywgdHlwZSwgc3RyZWFtKSB7XG4gIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVJ0cERlc2NyaXB0aW9uKHRyYW5zY2VpdmVyLmtpbmQsIGNhcHMpO1xuXG4gIC8vIE1hcCBJQ0UgcGFyYW1ldGVycyAodWZyYWcsIHB3ZCkgdG8gU0RQLlxuICBzZHAgKz0gU0RQVXRpbHMud3JpdGVJY2VQYXJhbWV0ZXJzKFxuICAgICAgdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIuZ2V0TG9jYWxQYXJhbWV0ZXJzKCkpO1xuXG4gIC8vIE1hcCBEVExTIHBhcmFtZXRlcnMgdG8gU0RQLlxuICBzZHAgKz0gU0RQVXRpbHMud3JpdGVEdGxzUGFyYW1ldGVycyhcbiAgICAgIHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQuZ2V0TG9jYWxQYXJhbWV0ZXJzKCksXG4gICAgICB0eXBlID09PSAnb2ZmZXInID8gJ2FjdHBhc3MnIDogJ2FjdGl2ZScpO1xuXG4gIHNkcCArPSAnYT1taWQ6JyArIHRyYW5zY2VpdmVyLm1pZCArICdcXHJcXG4nO1xuXG4gIGlmICh0cmFuc2NlaXZlci5ydHBTZW5kZXIgJiYgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIpIHtcbiAgICBzZHAgKz0gJ2E9c2VuZHJlY3ZcXHJcXG4nO1xuICB9IGVsc2UgaWYgKHRyYW5zY2VpdmVyLnJ0cFNlbmRlcikge1xuICAgIHNkcCArPSAnYT1zZW5kb25seVxcclxcbic7XG4gIH0gZWxzZSBpZiAodHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIpIHtcbiAgICBzZHAgKz0gJ2E9cmVjdm9ubHlcXHJcXG4nO1xuICB9IGVsc2Uge1xuICAgIHNkcCArPSAnYT1pbmFjdGl2ZVxcclxcbic7XG4gIH1cblxuICAvLyBGSVhNRTogZm9yIFJUWCB0aGVyZSBtaWdodCBiZSBtdWx0aXBsZSBTU1JDcy4gTm90IGltcGxlbWVudGVkIGluIEVkZ2UgeWV0LlxuICBpZiAodHJhbnNjZWl2ZXIucnRwU2VuZGVyKSB7XG4gICAgdmFyIG1zaWQgPSAnbXNpZDonICsgc3RyZWFtLmlkICsgJyAnICtcbiAgICAgICAgdHJhbnNjZWl2ZXIucnRwU2VuZGVyLnRyYWNrLmlkICsgJ1xcclxcbic7XG4gICAgc2RwICs9ICdhPScgKyBtc2lkO1xuICAgIHNkcCArPSAnYT1zc3JjOicgKyB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmMgK1xuICAgICAgICAnICcgKyBtc2lkO1xuICB9XG4gIC8vIEZJWE1FOiB0aGlzIHNob3VsZCBiZSB3cml0dGVuIGJ5IHdyaXRlUnRwRGVzY3JpcHRpb24uXG4gIHNkcCArPSAnYT1zc3JjOicgKyB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmMgK1xuICAgICAgJyBjbmFtZTonICsgU0RQVXRpbHMubG9jYWxDTmFtZSArICdcXHJcXG4nO1xuICByZXR1cm4gc2RwO1xufTtcblxuLy8gR2V0cyB0aGUgZGlyZWN0aW9uIGZyb20gdGhlIG1lZGlhU2VjdGlvbiBvciB0aGUgc2Vzc2lvbnBhcnQuXG5TRFBVdGlscy5nZXREaXJlY3Rpb24gPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIC8vIExvb2sgZm9yIHNlbmRyZWN2LCBzZW5kb25seSwgcmVjdm9ubHksIGluYWN0aXZlLCBkZWZhdWx0IHRvIHNlbmRyZWN2LlxuICB2YXIgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKG1lZGlhU2VjdGlvbik7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICBzd2l0Y2ggKGxpbmVzW2ldKSB7XG4gICAgICBjYXNlICdhPXNlbmRyZWN2JzpcbiAgICAgIGNhc2UgJ2E9c2VuZG9ubHknOlxuICAgICAgY2FzZSAnYT1yZWN2b25seSc6XG4gICAgICBjYXNlICdhPWluYWN0aXZlJzpcbiAgICAgICAgcmV0dXJuIGxpbmVzW2ldLnN1YnN0cigyKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIC8vIEZJWE1FOiBXaGF0IHNob3VsZCBoYXBwZW4gaGVyZT9cbiAgICB9XG4gIH1cbiAgaWYgKHNlc3Npb25wYXJ0KSB7XG4gICAgcmV0dXJuIFNEUFV0aWxzLmdldERpcmVjdGlvbihzZXNzaW9ucGFydCk7XG4gIH1cbiAgcmV0dXJuICdzZW5kcmVjdic7XG59O1xuXG4vLyBFeHBvc2UgcHVibGljIG1ldGhvZHMuXG5tb2R1bGUuZXhwb3J0cyA9IFNEUFV0aWxzO1xuXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9+L3NkcC9zZHAuanNcbi8vIG1vZHVsZSBpZCA9IDQ1MlxuLy8gbW9kdWxlIGNodW5rcyA9IDAiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=");

FIXME found
Open

    eval("/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar SDPUtils = __webpack_require__(452);\nvar browserDetails = __webpack_require__(460).browserDetails;\n\nvar edgeShim = {\n  shimPeerConnection: function() {\n    if (window.RTCIceGatherer) {\n      // ORTC defines an RTCIceCandidate object but no constructor.\n      // Not implemented in Edge.\n      if (!window.RTCIceCandidate) {\n        window.RTCIceCandidate = function(args) {\n          return args;\n        };\n      }\n      // ORTC does not have a session description object but\n      // other browsers (i.e. Chrome) that will support both PC and ORTC\n      // in the future might have this defined already.\n      if (!window.RTCSessionDescription) {\n        window.RTCSessionDescription = function(args) {\n          return args;\n        };\n      }\n      // this adds an additional event listener to MediaStrackTrack that signals\n      // when a tracks enabled property was changed.\n      var origMSTEnabled = Object.getOwnPropertyDescriptor(\n          MediaStreamTrack.prototype, 'enabled');\n      Object.defineProperty(MediaStreamTrack.prototype, 'enabled', {\n        set: function(value) {\n          origMSTEnabled.set.call(this, value);\n          var ev = new Event('enabled');\n          ev.enabled = value;\n          this.dispatchEvent(ev);\n        }\n      });\n    }\n\n    window.RTCPeerConnection = function(config) {\n      var self = this;\n\n      var _eventTarget = document.createDocumentFragment();\n      ['addEventListener', 'removeEventListener', 'dispatchEvent']\n          .forEach(function(method) {\n            self[method] = _eventTarget[method].bind(_eventTarget);\n          });\n\n      this.onicecandidate = null;\n      this.onaddstream = null;\n      this.ontrack = null;\n      this.onremovestream = null;\n      this.onsignalingstatechange = null;\n      this.oniceconnectionstatechange = null;\n      this.onnegotiationneeded = null;\n      this.ondatachannel = null;\n\n      this.localStreams = [];\n      this.remoteStreams = [];\n      this.getLocalStreams = function() {\n        return self.localStreams;\n      };\n      this.getRemoteStreams = function() {\n        return self.remoteStreams;\n      };\n\n      this.localDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.remoteDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.signalingState = 'stable';\n      this.iceConnectionState = 'new';\n      this.iceGatheringState = 'new';\n\n      this.iceOptions = {\n        gatherPolicy: 'all',\n        iceServers: []\n      };\n      if (config && config.iceTransportPolicy) {\n        switch (config.iceTransportPolicy) {\n          case 'all':\n          case 'relay':\n            this.iceOptions.gatherPolicy = config.iceTransportPolicy;\n            break;\n          case 'none':\n            // FIXME: remove once implementation and spec have added this.\n            throw new TypeError('iceTransportPolicy \"none\" not supported');\n          default:\n            // don't set iceTransportPolicy.\n            break;\n        }\n      }\n      this.usingBundle = config && config.bundlePolicy === 'max-bundle';\n\n      if (config && config.iceServers) {\n        // Edge does not like\n        // 1) stun:\n        // 2) turn: that does not have all of turn:host:port?transport=udp\n        // 3) turn: with ipv6 addresses\n        var iceServers = JSON.parse(JSON.stringify(config.iceServers));\n        this.iceOptions.iceServers = iceServers.filter(function(server) {\n          if (server && server.urls) {\n            var urls = server.urls;\n            if (typeof urls === 'string') {\n              urls = [urls];\n            }\n            urls = urls.filter(function(url) {\n              return (url.indexOf('turn:') === 0 &&\n                  url.indexOf('transport=udp') !== -1 &&\n                  url.indexOf('turn:[') === -1) ||\n                  (url.indexOf('stun:') === 0 &&\n                    browserDetails.version >= 14393);\n            })[0];\n            return !!urls;\n          }\n          return false;\n        });\n      }\n      this._config = config;\n\n      // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n      // everything that is needed to describe a SDP m-line.\n      this.transceivers = [];\n\n      // since the iceGatherer is currently created in createOffer but we\n      // must not emit candidates until after setLocalDescription we buffer\n      // them in this array.\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {\n      var self = this;\n      var sections = SDPUtils.splitSections(self.localDescription.sdp);\n      // FIXME: need to apply ice candidates in a way which is async but\n      // in-order\n      this._localIceCandidatesBuffer.forEach(function(event) {\n        var end = !event.candidate || Object.keys(event.candidate).length === 0;\n        if (end) {\n          for (var j = 1; j < sections.length; j++) {\n            if (sections[j].indexOf('\\r\\na=end-of-candidates\\r\\n') === -1) {\n              sections[j] += 'a=end-of-candidates\\r\\n';\n            }\n          }\n        } else if (event.candidate.candidate.indexOf('typ endOfCandidates')\n            === -1) {\n          sections[event.candidate.sdpMLineIndex + 1] +=\n              'a=' + event.candidate.candidate + '\\r\\n';\n        }\n        self.localDescription.sdp = sections.join('');\n        self.dispatchEvent(event);\n        if (self.onicecandidate !== null) {\n          self.onicecandidate(event);\n        }\n        if (!event.candidate && self.iceGatheringState !== 'complete') {\n          var complete = self.transceivers.every(function(transceiver) {\n            return transceiver.iceGatherer &&\n                transceiver.iceGatherer.state === 'completed';\n          });\n          if (complete) {\n            self.iceGatheringState = 'complete';\n          }\n        }\n      });\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype.getConfiguration = function() {\n      return this._config;\n    };\n\n    window.RTCPeerConnection.prototype.addStream = function(stream) {\n      // Clone is necessary for local demos mostly, attaching directly\n      // to two different senders does not work (build 10547).\n      var clonedStream = stream.clone();\n      stream.getTracks().forEach(function(track, idx) {\n        var clonedTrack = clonedStream.getTracks()[idx];\n        track.addEventListener('enabled', function(event) {\n          clonedTrack.enabled = event.enabled;\n        });\n      });\n      this.localStreams.push(clonedStream);\n      this._maybeFireNegotiationNeeded();\n    };\n\n    window.RTCPeerConnection.prototype.removeStream = function(stream) {\n      var idx = this.localStreams.indexOf(stream);\n      if (idx > -1) {\n        this.localStreams.splice(idx, 1);\n        this._maybeFireNegotiationNeeded();\n      }\n    };\n\n    window.RTCPeerConnection.prototype.getSenders = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpSender;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpSender;\n      });\n    };\n\n    window.RTCPeerConnection.prototype.getReceivers = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpReceiver;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpReceiver;\n      });\n    };\n\n    // Determines the intersection of local and remote capabilities.\n    window.RTCPeerConnection.prototype._getCommonCapabilities =\n        function(localCapabilities, remoteCapabilities) {\n          var commonCapabilities = {\n            codecs: [],\n            headerExtensions: [],\n            fecMechanisms: []\n          };\n          localCapabilities.codecs.forEach(function(lCodec) {\n            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n              var rCodec = remoteCapabilities.codecs[i];\n              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n                  lCodec.clockRate === rCodec.clockRate) {\n                // number of channels is the highest common number of channels\n                rCodec.numChannels = Math.min(lCodec.numChannels,\n                    rCodec.numChannels);\n                // push rCodec so we reply with offerer payload type\n                commonCapabilities.codecs.push(rCodec);\n\n                // determine common feedback mechanisms\n                rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {\n                  for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {\n                    if (lCodec.rtcpFeedback[j].type === fb.type &&\n                        lCodec.rtcpFeedback[j].parameter === fb.parameter) {\n                      return true;\n                    }\n                  }\n                  return false;\n                });\n                // FIXME: also need to determine .parameters\n                //  see https://github.com/openpeer/ortc/issues/569\n                break;\n              }\n            }\n          });\n\n          localCapabilities.headerExtensions\n              .forEach(function(lHeaderExtension) {\n                for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n                     i++) {\n                  var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n                  if (lHeaderExtension.uri === rHeaderExtension.uri) {\n                    commonCapabilities.headerExtensions.push(rHeaderExtension);\n                    break;\n                  }\n                }\n              });\n\n          // FIXME: fecMechanisms\n          return commonCapabilities;\n        };\n\n    // Create ICE gatherer, ICE transport and DTLS transport.\n    window.RTCPeerConnection.prototype._createIceAndDtlsTransports =\n        function(mid, sdpMLineIndex) {\n          var self = this;\n          var iceGatherer = new RTCIceGatherer(self.iceOptions);\n          var iceTransport = new RTCIceTransport(iceGatherer);\n          iceGatherer.onlocalcandidate = function(evt) {\n            var event = new Event('icecandidate');\n            event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n            var cand = evt.candidate;\n            var end = !cand || Object.keys(cand).length === 0;\n            // Edge emits an empty object for RTCIceCandidateComplete‥\n            if (end) {\n              // polyfill since RTCIceGatherer.state is not implemented in\n              // Edge 10547 yet.\n              if (iceGatherer.state === undefined) {\n                iceGatherer.state = 'completed';\n              }\n\n              // Emit a candidate with type endOfCandidates to make the samples\n              // work. Edge requires addIceCandidate with this empty candidate\n              // to start checking. The real solution is to signal\n              // end-of-candidates to the other side when getting the null\n              // candidate but some apps (like the samples) don't do that.\n              event.candidate.candidate =\n                  'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';\n            } else {\n              // RTCIceCandidate doesn't have a component, needs to be added\n              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;\n              event.candidate.candidate = SDPUtils.writeCandidate(cand);\n            }\n\n            // update local description.\n            var sections = SDPUtils.splitSections(self.localDescription.sdp);\n            if (event.candidate.candidate.indexOf('typ endOfCandidates')\n                === -1) {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=' + event.candidate.candidate + '\\r\\n';\n            } else {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=end-of-candidates\\r\\n';\n            }\n            self.localDescription.sdp = sections.join('');\n\n            var complete = self.transceivers.every(function(transceiver) {\n              return transceiver.iceGatherer &&\n                  transceiver.iceGatherer.state === 'completed';\n            });\n\n            // Emit candidate if localDescription is set.\n            // Also emits null candidate when all gatherers are complete.\n            switch (self.iceGatheringState) {\n              case 'new':\n                self._localIceCandidatesBuffer.push(event);\n                if (end && complete) {\n                  self._localIceCandidatesBuffer.push(\n                      new Event('icecandidate'));\n                }\n                break;\n              case 'gathering':\n                self._emitBufferedCandidates();\n                self.dispatchEvent(event);\n                if (self.onicecandidate !== null) {\n                  self.onicecandidate(event);\n                }\n                if (complete) {\n                  self.dispatchEvent(new Event('icecandidate'));\n                  if (self.onicecandidate !== null) {\n                    self.onicecandidate(new Event('icecandidate'));\n                  }\n                  self.iceGatheringState = 'complete';\n                }\n                break;\n              case 'complete':\n                // should not happen... currently!\n                break;\n              default: // no-op.\n                break;\n            }\n          };\n          iceTransport.onicestatechange = function() {\n            self._updateConnectionState();\n          };\n\n          var dtlsTransport = new RTCDtlsTransport(iceTransport);\n          dtlsTransport.ondtlsstatechange = function() {\n            self._updateConnectionState();\n          };\n          dtlsTransport.onerror = function() {\n            // onerror does not set state to failed by itself.\n            dtlsTransport.state = 'failed';\n            self._updateConnectionState();\n          };\n\n          return {\n            iceGatherer: iceGatherer,\n            iceTransport: iceTransport,\n            dtlsTransport: dtlsTransport\n          };\n        };\n\n    // Start the RTP Sender and Receiver for a transceiver.\n    window.RTCPeerConnection.prototype._transceive = function(transceiver,\n        send, recv) {\n      var params = this._getCommonCapabilities(transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n      if (send && transceiver.rtpSender) {\n        params.encodings = transceiver.sendEncodingParameters;\n        params.rtcp = {\n          cname: SDPUtils.localCName\n        };\n        if (transceiver.recvEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpSender.send(params);\n      }\n      if (recv && transceiver.rtpReceiver) {\n        // remove RTX field in Edge 14942\n        if (transceiver.kind === 'video'\n            && transceiver.recvEncodingParameters) {\n          transceiver.recvEncodingParameters.forEach(function(p) {\n            delete p.rtx;\n          });\n        }\n        params.encodings = transceiver.recvEncodingParameters;\n        params.rtcp = {\n          cname: transceiver.cname\n        };\n        if (transceiver.sendEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpReceiver.receive(params);\n      }\n    };\n\n    window.RTCPeerConnection.prototype.setLocalDescription =\n        function(description) {\n          var self = this;\n          var sections;\n          var sessionpart;\n          if (description.type === 'offer') {\n            // FIXME: What was the purpose of this empty if statement?\n            // if (!this._pendingOffer) {\n            // } else {\n            if (this._pendingOffer) {\n              // VERY limited support for SDP munging. Limited to:\n              // * changing the order of codecs\n              sections = SDPUtils.splitSections(description.sdp);\n              sessionpart = sections.shift();\n              sections.forEach(function(mediaSection, sdpMLineIndex) {\n                var caps = SDPUtils.parseRtpParameters(mediaSection);\n                self._pendingOffer[sdpMLineIndex].localCapabilities = caps;\n              });\n              this.transceivers = this._pendingOffer;\n              delete this._pendingOffer;\n            }\n          } else if (description.type === 'answer') {\n            sections = SDPUtils.splitSections(self.remoteDescription.sdp);\n            sessionpart = sections.shift();\n            var isIceLite = SDPUtils.matchPrefix(sessionpart,\n                'a=ice-lite').length > 0;\n            sections.forEach(function(mediaSection, sdpMLineIndex) {\n              var transceiver = self.transceivers[sdpMLineIndex];\n              var iceGatherer = transceiver.iceGatherer;\n              var iceTransport = transceiver.iceTransport;\n              var dtlsTransport = transceiver.dtlsTransport;\n              var localCapabilities = transceiver.localCapabilities;\n              var remoteCapabilities = transceiver.remoteCapabilities;\n\n              var rejected = mediaSection.split('\\n', 1)[0]\n                  .split(' ', 2)[1] === '0';\n\n              if (!rejected && !transceiver.isDatachannel) {\n                var remoteIceParameters = SDPUtils.getIceParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                  .map(function(cand) {\n                    return SDPUtils.parseCandidate(cand);\n                  })\n                  .filter(function(cand) {\n                    return cand.component === '1';\n                  });\n                  // ice-lite only includes host candidates in the SDP so we can\n                  // use setRemoteCandidates (which implies an\n                  // RTCIceCandidateComplete)\n                  if (cands.length) {\n                    iceTransport.setRemoteCandidates(cands);\n                  }\n                }\n                var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  remoteDtlsParameters.role = 'server';\n                }\n\n                if (!self.usingBundle || sdpMLineIndex === 0) {\n                  iceTransport.start(iceGatherer, remoteIceParameters,\n                      isIceLite ? 'controlling' : 'controlled');\n                  dtlsTransport.start(remoteDtlsParameters);\n                }\n\n                // Calculate intersection of capabilities.\n                var params = self._getCommonCapabilities(localCapabilities,\n                    remoteCapabilities);\n\n                // Start the RTCRtpSender. The RTCRtpReceiver for this\n                // transceiver has already been started in setRemoteDescription.\n                self._transceive(transceiver,\n                    params.codecs.length > 0,\n                    false);\n              }\n            });\n          }\n\n          this.localDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-local-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n\n          // If a success callback was provided, emit ICE candidates after it\n          // has been executed. Otherwise, emit callback after the Promise is\n          // resolved.\n          var hasCallback = arguments.length > 1 &&\n            typeof arguments[1] === 'function';\n          if (hasCallback) {\n            var cb = arguments[1];\n            window.setTimeout(function() {\n              cb();\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              self._emitBufferedCandidates();\n            }, 0);\n          }\n          var p = Promise.resolve();\n          p.then(function() {\n            if (!hasCallback) {\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              // Usually candidates will be emitted earlier.\n              window.setTimeout(self._emitBufferedCandidates.bind(self), 500);\n            }\n          });\n          return p;\n        };\n\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n        function(description) {\n          var self = this;\n          var stream = new MediaStream();\n          var receiverList = [];\n          var sections = SDPUtils.splitSections(description.sdp);\n          var sessionpart = sections.shift();\n          var isIceLite = SDPUtils.matchPrefix(sessionpart,\n              'a=ice-lite').length > 0;\n          this.usingBundle = SDPUtils.matchPrefix(sessionpart,\n              'a=group:BUNDLE ').length > 0;\n          sections.forEach(function(mediaSection, sdpMLineIndex) {\n            var lines = SDPUtils.splitLines(mediaSection);\n            var mline = lines[0].substr(2).split(' ');\n            var kind = mline[0];\n            var rejected = mline[1] === '0';\n            var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n\n            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');\n            if (mid.length) {\n              mid = mid[0].substr(6);\n            } else {\n              mid = SDPUtils.generateIdentifier();\n            }\n\n            // Reject datachannels which are not implemented yet.\n            if (kind === 'application' && mline[2] === 'DTLS/SCTP') {\n              self.transceivers[sdpMLineIndex] = {\n                mid: mid,\n                isDatachannel: true\n              };\n              return;\n            }\n\n            var transceiver;\n            var iceGatherer;\n            var iceTransport;\n            var dtlsTransport;\n            var rtpSender;\n            var rtpReceiver;\n            var sendEncodingParameters;\n            var recvEncodingParameters;\n            var localCapabilities;\n\n            var track;\n            // FIXME: ensure the mediaSection has rtcp-mux set.\n            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n            var remoteIceParameters;\n            var remoteDtlsParameters;\n            if (!rejected) {\n              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters.role = 'client';\n            }\n            recvEncodingParameters =\n                SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n            var cname;\n            // Gets the first SSRC. Note that with RTX there might be multiple\n            // SSRCs.\n            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n                .map(function(line) {\n                  return SDPUtils.parseSsrcMedia(line);\n                })\n                .filter(function(obj) {\n                  return obj.attribute === 'cname';\n                })[0];\n            if (remoteSsrc) {\n              cname = remoteSsrc.value;\n            }\n\n            var isComplete = SDPUtils.matchPrefix(mediaSection,\n                'a=end-of-candidates', sessionpart).length > 0;\n            var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                .map(function(cand) {\n                  return SDPUtils.parseCandidate(cand);\n                })\n                .filter(function(cand) {\n                  return cand.component === '1';\n                });\n            if (description.type === 'offer' && !rejected) {\n              var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n                iceGatherer: self.transceivers[0].iceGatherer,\n                iceTransport: self.transceivers[0].iceTransport,\n                dtlsTransport: self.transceivers[0].dtlsTransport\n              } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n              if (isComplete) {\n                transports.iceTransport.setRemoteCandidates(cands);\n              }\n\n              localCapabilities = RTCRtpReceiver.getCapabilities(kind);\n\n              // filter RTX until additional stuff needed for RTX is implemented\n              // in adapter.js\n              localCapabilities.codecs = localCapabilities.codecs.filter(\n                  function(codec) {\n                    return codec.name !== 'rtx';\n                  });\n\n              sendEncodingParameters = [{\n                ssrc: (2 * sdpMLineIndex + 2) * 1001\n              }];\n\n              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n\n              track = rtpReceiver.track;\n              receiverList.push([track, rtpReceiver]);\n              // FIXME: not correct when there are multiple streams but that is\n              // not currently supported in this shim.\n              stream.addTrack(track);\n\n              // FIXME: look at direction.\n              if (self.localStreams.length > 0 &&\n                  self.localStreams[0].getTracks().length >= sdpMLineIndex) {\n                var localTrack;\n                if (kind === 'audio') {\n                  localTrack = self.localStreams[0].getAudioTracks()[0];\n                } else if (kind === 'video') {\n                  localTrack = self.localStreams[0].getVideoTracks()[0];\n                }\n                if (localTrack) {\n                  rtpSender = new RTCRtpSender(localTrack,\n                      transports.dtlsTransport);\n                }\n              }\n\n              self.transceivers[sdpMLineIndex] = {\n                iceGatherer: transports.iceGatherer,\n                iceTransport: transports.iceTransport,\n                dtlsTransport: transports.dtlsTransport,\n                localCapabilities: localCapabilities,\n                remoteCapabilities: remoteCapabilities,\n                rtpSender: rtpSender,\n                rtpReceiver: rtpReceiver,\n                kind: kind,\n                mid: mid,\n                cname: cname,\n                sendEncodingParameters: sendEncodingParameters,\n                recvEncodingParameters: recvEncodingParameters\n              };\n              // Start the RTCRtpReceiver now. The RTPSender is started in\n              // setLocalDescription.\n              self._transceive(self.transceivers[sdpMLineIndex],\n                  false,\n                  direction === 'sendrecv' || direction === 'sendonly');\n            } else if (description.type === 'answer' && !rejected) {\n              transceiver = self.transceivers[sdpMLineIndex];\n              iceGatherer = transceiver.iceGatherer;\n              iceTransport = transceiver.iceTransport;\n              dtlsTransport = transceiver.dtlsTransport;\n              rtpSender = transceiver.rtpSender;\n              rtpReceiver = transceiver.rtpReceiver;\n              sendEncodingParameters = transceiver.sendEncodingParameters;\n              localCapabilities = transceiver.localCapabilities;\n\n              self.transceivers[sdpMLineIndex].recvEncodingParameters =\n                  recvEncodingParameters;\n              self.transceivers[sdpMLineIndex].remoteCapabilities =\n                  remoteCapabilities;\n              self.transceivers[sdpMLineIndex].cname = cname;\n\n              if ((isIceLite || isComplete) && cands.length) {\n                iceTransport.setRemoteCandidates(cands);\n              }\n              if (!self.usingBundle || sdpMLineIndex === 0) {\n                iceTransport.start(iceGatherer, remoteIceParameters,\n                    'controlling');\n                dtlsTransport.start(remoteDtlsParameters);\n              }\n\n              self._transceive(transceiver,\n                  direction === 'sendrecv' || direction === 'recvonly',\n                  direction === 'sendrecv' || direction === 'sendonly');\n\n              if (rtpReceiver &&\n                  (direction === 'sendrecv' || direction === 'sendonly')) {\n                track = rtpReceiver.track;\n                receiverList.push([track, rtpReceiver]);\n                stream.addTrack(track);\n              } else {\n                // FIXME: actually the receiver should be created later.\n                delete transceiver.rtpReceiver;\n              }\n            }\n          });\n\n          this.remoteDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-remote-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n          if (stream.getTracks().length) {\n            self.remoteStreams.push(stream);\n            window.setTimeout(function() {\n              var event = new Event('addstream');\n              event.stream = stream;\n              self.dispatchEvent(event);\n              if (self.onaddstream !== null) {\n                window.setTimeout(function() {\n                  self.onaddstream(event);\n                }, 0);\n              }\n\n              receiverList.forEach(function(item) {\n                var track = item[0];\n                var receiver = item[1];\n                var trackEvent = new Event('track');\n                trackEvent.track = track;\n                trackEvent.receiver = receiver;\n                trackEvent.streams = [stream];\n                self.dispatchEvent(event);\n                if (self.ontrack !== null) {\n                  window.setTimeout(function() {\n                    self.ontrack(trackEvent);\n                  }, 0);\n                }\n              });\n            }, 0);\n          }\n          if (arguments.length > 1 && typeof arguments[1] === 'function') {\n            window.setTimeout(arguments[1], 0);\n          }\n          return Promise.resolve();\n        };\n\n    window.RTCPeerConnection.prototype.close = function() {\n      this.transceivers.forEach(function(transceiver) {\n        /* not yet\n        if (transceiver.iceGatherer) {\n          transceiver.iceGatherer.close();\n        }\n        */\n        if (transceiver.iceTransport) {\n          transceiver.iceTransport.stop();\n        }\n        if (transceiver.dtlsTransport) {\n          transceiver.dtlsTransport.stop();\n        }\n        if (transceiver.rtpSender) {\n          transceiver.rtpSender.stop();\n        }\n        if (transceiver.rtpReceiver) {\n          transceiver.rtpReceiver.stop();\n        }\n      });\n      // FIXME: clean up tracks, local streams, remote streams, etc\n      this._updateSignalingState('closed');\n    };\n\n    // Update the signaling state.\n    window.RTCPeerConnection.prototype._updateSignalingState =\n        function(newState) {\n          this.signalingState = newState;\n          var event = new Event('signalingstatechange');\n          this.dispatchEvent(event);\n          if (this.onsignalingstatechange !== null) {\n            this.onsignalingstatechange(event);\n          }\n        };\n\n    // Determine whether to fire the negotiationneeded event.\n    window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =\n        function() {\n          // Fire away (for now).\n          var event = new Event('negotiationneeded');\n          this.dispatchEvent(event);\n          if (this.onnegotiationneeded !== null) {\n            this.onnegotiationneeded(event);\n          }\n        };\n\n    // Update the connection state.\n    window.RTCPeerConnection.prototype._updateConnectionState = function() {\n      var self = this;\n      var newState;\n      var states = {\n        'new': 0,\n        closed: 0,\n        connecting: 0,\n        checking: 0,\n        connected: 0,\n        completed: 0,\n        failed: 0\n      };\n      this.transceivers.forEach(function(transceiver) {\n        states[transceiver.iceTransport.state]++;\n        states[transceiver.dtlsTransport.state]++;\n      });\n      // ICETransport.completed and connected are the same for this purpose.\n      states.connected += states.completed;\n\n      newState = 'new';\n      if (states.failed > 0) {\n        newState = 'failed';\n      } else if (states.connecting > 0 || states.checking > 0) {\n        newState = 'connecting';\n      } else if (states.disconnected > 0) {\n        newState = 'disconnected';\n      } else if (states.new > 0) {\n        newState = 'new';\n      } else if (states.connected > 0 || states.completed > 0) {\n        newState = 'connected';\n      }\n\n      if (newState !== self.iceConnectionState) {\n        self.iceConnectionState = newState;\n        var event = new Event('iceconnectionstatechange');\n        this.dispatchEvent(event);\n        if (this.oniceconnectionstatechange !== null) {\n          this.oniceconnectionstatechange(event);\n        }\n      }\n    };\n\n    window.RTCPeerConnection.prototype.createOffer = function() {\n      var self = this;\n      if (this._pendingOffer) {\n        throw new Error('createOffer called while there is a pending offer.');\n      }\n      var offerOptions;\n      if (arguments.length === 1 && typeof arguments[0] !== 'function') {\n        offerOptions = arguments[0];\n      } else if (arguments.length === 3) {\n        offerOptions = arguments[2];\n      }\n\n      var tracks = [];\n      var numAudioTracks = 0;\n      var numVideoTracks = 0;\n      // Default to sendrecv.\n      if (this.localStreams.length) {\n        numAudioTracks = this.localStreams[0].getAudioTracks().length;\n        numVideoTracks = this.localStreams[0].getVideoTracks().length;\n      }\n      // Determine number of audio and video tracks we need to send/recv.\n      if (offerOptions) {\n        // Reject Chrome legacy constraints.\n        if (offerOptions.mandatory || offerOptions.optional) {\n          throw new TypeError(\n              'Legacy mandatory/optional constraints not supported.');\n        }\n        if (offerOptions.offerToReceiveAudio !== undefined) {\n          numAudioTracks = offerOptions.offerToReceiveAudio;\n        }\n        if (offerOptions.offerToReceiveVideo !== undefined) {\n          numVideoTracks = offerOptions.offerToReceiveVideo;\n        }\n      }\n      if (this.localStreams.length) {\n        // Push local streams.\n        this.localStreams[0].getTracks().forEach(function(track) {\n          tracks.push({\n            kind: track.kind,\n            track: track,\n            wantReceive: track.kind === 'audio' ?\n                numAudioTracks > 0 : numVideoTracks > 0\n          });\n          if (track.kind === 'audio') {\n            numAudioTracks--;\n          } else if (track.kind === 'video') {\n            numVideoTracks--;\n          }\n        });\n      }\n      // Create M-lines for recvonly streams.\n      while (numAudioTracks > 0 || numVideoTracks > 0) {\n        if (numAudioTracks > 0) {\n          tracks.push({\n            kind: 'audio',\n            wantReceive: true\n          });\n          numAudioTracks--;\n        }\n        if (numVideoTracks > 0) {\n          tracks.push({\n            kind: 'video',\n            wantReceive: true\n          });\n          numVideoTracks--;\n        }\n      }\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      var transceivers = [];\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        // For each track, create an ice gatherer, ice transport,\n        // dtls transport, potentially rtpsender and rtpreceiver.\n        var track = mline.track;\n        var kind = mline.kind;\n        var mid = SDPUtils.generateIdentifier();\n\n        var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n          iceGatherer: transceivers[0].iceGatherer,\n          iceTransport: transceivers[0].iceTransport,\n          dtlsTransport: transceivers[0].dtlsTransport\n        } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n        var localCapabilities = RTCRtpSender.getCapabilities(kind);\n        // filter RTX until additional stuff needed for RTX is implemented\n        // in adapter.js\n        localCapabilities.codecs = localCapabilities.codecs.filter(\n            function(codec) {\n              return codec.name !== 'rtx';\n            });\n        localCapabilities.codecs.forEach(function(codec) {\n          // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552\n          // by adding level-asymmetry-allowed=1\n          if (codec.name === 'H264' &&\n              codec.parameters['level-asymmetry-allowed'] === undefined) {\n            codec.parameters['level-asymmetry-allowed'] = '1';\n          }\n        });\n\n        var rtpSender;\n        var rtpReceiver;\n\n        // generate an ssrc now, to be used later in rtpSender.send\n        var sendEncodingParameters = [{\n          ssrc: (2 * sdpMLineIndex + 1) * 1001\n        }];\n        if (track) {\n          rtpSender = new RTCRtpSender(track, transports.dtlsTransport);\n        }\n\n        if (mline.wantReceive) {\n          rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n        }\n\n        transceivers[sdpMLineIndex] = {\n          iceGatherer: transports.iceGatherer,\n          iceTransport: transports.iceTransport,\n          dtlsTransport: transports.dtlsTransport,\n          localCapabilities: localCapabilities,\n          remoteCapabilities: null,\n          rtpSender: rtpSender,\n          rtpReceiver: rtpReceiver,\n          kind: kind,\n          mid: mid,\n          sendEncodingParameters: sendEncodingParameters,\n          recvEncodingParameters: null\n        };\n      });\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        var transceiver = transceivers[sdpMLineIndex];\n        sdp += SDPUtils.writeMediaSection(transceiver,\n            transceiver.localCapabilities, 'offer', self.localStreams[0]);\n      });\n\n      this._pendingOffer = transceivers;\n      var desc = new RTCSessionDescription({\n        type: 'offer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.createAnswer = function() {\n      var self = this;\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      this.transceivers.forEach(function(transceiver) {\n        if (transceiver.isDatachannel) {\n          sdp += 'm=application 0 DTLS/SCTP 5000\\r\\n' +\n              'c=IN IP4 0.0.0.0\\r\\n' +\n              'a=mid:' + transceiver.mid + '\\r\\n';\n          return;\n        }\n        // Calculate intersection of capabilities.\n        var commonCapabilities = self._getCommonCapabilities(\n            transceiver.localCapabilities,\n            transceiver.remoteCapabilities);\n\n        sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,\n            'answer', self.localStreams[0]);\n      });\n\n      var desc = new RTCSessionDescription({\n        type: 'answer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n      if (!candidate) {\n        this.transceivers.forEach(function(transceiver) {\n          transceiver.iceTransport.addRemoteCandidate({});\n        });\n      } else {\n        var mLineIndex = candidate.sdpMLineIndex;\n        if (candidate.sdpMid) {\n          for (var i = 0; i < this.transceivers.length; i++) {\n            if (this.transceivers[i].mid === candidate.sdpMid) {\n              mLineIndex = i;\n              break;\n            }\n          }\n        }\n        var transceiver = this.transceivers[mLineIndex];\n        if (transceiver) {\n          var cand = Object.keys(candidate.candidate).length > 0 ?\n              SDPUtils.parseCandidate(candidate.candidate) : {};\n          // Ignore Chrome's invalid candidates since Edge does not like them.\n          if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {\n            return;\n          }\n          // Ignore RTCP candidates, we assume RTCP-MUX.\n          if (cand.component !== '1') {\n            return;\n          }\n          // A dirty hack to make samples work.\n          if (cand.type === 'endOfCandidates') {\n            cand = {};\n          }\n          transceiver.iceTransport.addRemoteCandidate(cand);\n\n          // update the remoteDescription.\n          var sections = SDPUtils.splitSections(this.remoteDescription.sdp);\n          sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()\n              : 'a=end-of-candidates') + '\\r\\n';\n          this.remoteDescription.sdp = sections.join('');\n        }\n      }\n      if (arguments.length > 1 && typeof arguments[1] === 'function') {\n        window.setTimeout(arguments[1], 0);\n      }\n      return Promise.resolve();\n    };\n\n    window.RTCPeerConnection.prototype.getStats = function() {\n      var promises = [];\n      this.transceivers.forEach(function(transceiver) {\n        ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n            'dtlsTransport'].forEach(function(method) {\n              if (transceiver[method]) {\n                promises.push(transceiver[method].getStats());\n              }\n            });\n      });\n      var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&\n          arguments[1];\n      return new Promise(function(resolve) {\n        // shim getStats with maplike support\n        var results = new Map();\n        Promise.all(promises).then(function(res) {\n          res.forEach(function(result) {\n            Object.keys(result).forEach(function(id) {\n              results.set(id, result[id]);\n              results[id] = result[id];\n            });\n          });\n          if (cb) {\n            window.setTimeout(cb, 0, results);\n          }\n          resolve(results);\n        });\n      });\n    };\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimPeerConnection: edgeShim.shimPeerConnection,\n  shimGetUserMedia: __webpack_require__(464)\n};\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDYzLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy93ZWJydGMtYWRhcHRlci9zcmMvanMvZWRnZS9lZGdlX3NoaW0uanMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFdlYlJUQyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG4gLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbnZhciBTRFBVdGlscyA9IHJlcXVpcmUoJ3NkcCcpO1xudmFyIGJyb3dzZXJEZXRhaWxzID0gcmVxdWlyZSgnLi4vdXRpbHMnKS5icm93c2VyRGV0YWlscztcblxudmFyIGVkZ2VTaGltID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGZ1bmN0aW9uKCkge1xuICAgIGlmICh3aW5kb3cuUlRDSWNlR2F0aGVyZXIpIHtcbiAgICAgIC8vIE9SVEMgZGVmaW5lcyBhbiBSVENJY2VDYW5kaWRhdGUgb2JqZWN0IGJ1dCBubyBjb25zdHJ1Y3Rvci5cbiAgICAgIC8vIE5vdCBpbXBsZW1lbnRlZCBpbiBFZGdlLlxuICAgICAgaWYgKCF3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlKSB7XG4gICAgICAgIHdpbmRvdy5SVENJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbihhcmdzKSB7XG4gICAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICAvLyBPUlRDIGRvZXMgbm90IGhhdmUgYSBzZXNzaW9uIGRlc2NyaXB0aW9uIG9iamVjdCBidXRcbiAgICAgIC8vIG90aGVyIGJyb3dzZXJzIChpLmUuIENocm9tZSkgdGhhdCB3aWxsIHN1cHBvcnQgYm90aCBQQyBhbmQgT1JUQ1xuICAgICAgLy8gaW4gdGhlIGZ1dHVyZSBtaWdodCBoYXZlIHRoaXMgZGVmaW5lZCBhbHJlYWR5LlxuICAgICAgaWYgKCF3aW5kb3cuUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKSB7XG4gICAgICAgIHdpbmRvdy5SVENTZXNzaW9uRGVzY3JpcHRpb24gPSBmdW5jdGlvbihhcmdzKSB7XG4gICAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICAvLyB0aGlzIGFkZHMgYW4gYWRkaXRpb25hbCBldmVudCBsaXN0ZW5lciB0byBNZWRpYVN0cmFja1RyYWNrIHRoYXQgc2lnbmFsc1xuICAgICAgLy8gd2hlbiBhIHRyYWNrcyBlbmFibGVkIHByb3BlcnR5IHdhcyBjaGFuZ2VkLlxuICAgICAgdmFyIG9yaWdNU1RFbmFibGVkID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihcbiAgICAgICAgICBNZWRpYVN0cmVhbVRyYWNrLnByb3RvdHlwZSwgJ2VuYWJsZWQnKTtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShNZWRpYVN0cmVhbVRyYWNrLnByb3RvdHlwZSwgJ2VuYWJsZWQnLCB7XG4gICAgICAgIHNldDogZnVuY3Rpb24odmFsdWUpIHtcbiAgICAgICAgICBvcmlnTVNURW5hYmxlZC5zZXQuY2FsbCh0aGlzLCB2YWx1ZSk7XG4gICAgICAgICAgdmFyIGV2ID0gbmV3IEV2ZW50KCdlbmFibGVkJyk7XG4gICAgICAgICAgZXYuZW5hYmxlZCA9IHZhbHVlO1xuICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiA9IGZ1bmN0aW9uKGNvbmZpZykge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgICB2YXIgX2V2ZW50VGFyZ2V0ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xuICAgICAgWydhZGRFdmVudExpc3RlbmVyJywgJ3JlbW92ZUV2ZW50TGlzdGVuZXInLCAnZGlzcGF0Y2hFdmVudCddXG4gICAgICAgICAgLmZvckVhY2goZnVuY3Rpb24obWV0aG9kKSB7XG4gICAgICAgICAgICBzZWxmW21ldGhvZF0gPSBfZXZlbnRUYXJnZXRbbWV0aG9kXS5iaW5kKF9ldmVudFRhcmdldCk7XG4gICAgICAgICAgfSk7XG5cbiAgICAgIHRoaXMub25pY2VjYW5kaWRhdGUgPSBudWxsO1xuICAgICAgdGhpcy5vbmFkZHN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9udHJhY2sgPSBudWxsO1xuICAgICAgdGhpcy5vbnJlbW92ZXN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBudWxsO1xuICAgICAgdGhpcy5vbmRhdGFjaGFubmVsID0gbnVsbDtcblxuICAgICAgdGhpcy5sb2NhbFN0cmVhbXMgPSBbXTtcbiAgICAgIHRoaXMucmVtb3RlU3RyZWFtcyA9IFtdO1xuICAgICAgdGhpcy5nZXRMb2NhbFN0cmVhbXMgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYubG9jYWxTdHJlYW1zO1xuICAgICAgfTtcbiAgICAgIHRoaXMuZ2V0UmVtb3RlU3RyZWFtcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gc2VsZi5yZW1vdGVTdHJlYW1zO1xuICAgICAgfTtcblxuICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICcnLFxuICAgICAgICBzZHA6ICcnXG4gICAgICB9KTtcbiAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24gPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJycsXG4gICAgICAgIHNkcDogJydcbiAgICAgIH0pO1xuICAgICAgdGhpcy5zaWduYWxpbmdTdGF0ZSA9ICdzdGFibGUnO1xuICAgICAgdGhpcy5pY2VDb25uZWN0aW9uU3RhdGUgPSAnbmV3JztcbiAgICAgIHRoaXMuaWNlR2F0aGVyaW5nU3RhdGUgPSAnbmV3JztcblxuICAgICAgdGhpcy5pY2VPcHRpb25zID0ge1xuICAgICAgICBnYXRoZXJQb2xpY3k6ICdhbGwnLFxuICAgICAgICBpY2VTZXJ2ZXJzOiBbXVxuICAgICAgfTtcbiAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmljZVRyYW5zcG9ydFBvbGljeSkge1xuICAgICAgICBzd2l0Y2ggKGNvbmZpZy5pY2VUcmFuc3BvcnRQb2xpY3kpIHtcbiAgICAgICAgICBjYXNlICdhbGwnOlxuICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgIHRoaXMuaWNlT3B0aW9ucy5nYXRoZXJQb2xpY3kgPSBjb25maWcuaWNlVHJhbnNwb3J0UG9saWN5O1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnbm9uZSc6XG4gICAgICAgICAgICAvLyBGSVhNRTogcmVtb3ZlIG9uY2UgaW1wbGVtZW50YXRpb24gYW5kIHNwZWMgaGF2ZSBhZGRlZCB0aGlzLlxuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignaWNlVHJhbnNwb3J0UG9saWN5IFwibm9uZVwiIG5vdCBzdXBwb3J0ZWQnKTtcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgLy8gZG9uJ3Qgc2V0IGljZVRyYW5zcG9ydFBvbGljeS5cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLnVzaW5nQnVuZGxlID0gY29uZmlnICYmIGNvbmZpZy5idW5kbGVQb2xpY3kgPT09ICdtYXgtYnVuZGxlJztcblxuICAgICAgaWYgKGNvbmZpZyAmJiBjb25maWcuaWNlU2VydmVycykge1xuICAgICAgICAvLyBFZGdlIGRvZXMgbm90IGxpa2VcbiAgICAgICAgLy8gMSkgc3R1bjpcbiAgICAgICAgLy8gMikgdHVybjogdGhhdCBkb2VzIG5vdCBoYXZlIGFsbCBvZiB0dXJuOmhvc3Q6cG9ydD90cmFuc3BvcnQ9dWRwXG4gICAgICAgIC8vIDMpIHR1cm46IHdpdGggaXB2NiBhZGRyZXNzZXNcbiAgICAgICAgdmFyIGljZVNlcnZlcnMgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGNvbmZpZy5pY2VTZXJ2ZXJzKSk7XG4gICAgICAgIHRoaXMuaWNlT3B0aW9ucy5pY2VTZXJ2ZXJzID0gaWNlU2VydmVycy5maWx0ZXIoZnVuY3Rpb24oc2VydmVyKSB7XG4gICAgICAgICAgaWYgKHNlcnZlciAmJiBzZXJ2ZXIudXJscykge1xuICAgICAgICAgICAgdmFyIHVybHMgPSBzZXJ2ZXIudXJscztcbiAgICAgICAgICAgIGlmICh0eXBlb2YgdXJscyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgdXJscyA9IFt1cmxzXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHVybHMgPSB1cmxzLmZpbHRlcihmdW5jdGlvbih1cmwpIHtcbiAgICAgICAgICAgICAgcmV0dXJuICh1cmwuaW5kZXhPZigndHVybjonKSA9PT0gMCAmJlxuICAgICAgICAgICAgICAgICAgdXJsLmluZGV4T2YoJ3RyYW5zcG9ydD11ZHAnKSAhPT0gLTEgJiZcbiAgICAgICAgICAgICAgICAgIHVybC5pbmRleE9mKCd0dXJuOlsnKSA9PT0gLTEpIHx8XG4gICAgICAgICAgICAgICAgICAodXJsLmluZGV4T2YoJ3N0dW46JykgPT09IDAgJiZcbiAgICAgICAgICAgICAgICAgICAgYnJvd3NlckRldGFpbHMudmVyc2lvbiA+PSAxNDM5Myk7XG4gICAgICAgICAgICB9KVswXTtcbiAgICAgICAgICAgIHJldHVybiAhIXVybHM7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aGlzLl9jb25maWcgPSBjb25maWc7XG5cbiAgICAgIC8vIHBlci10cmFjayBpY2VHYXRoZXJzLCBpY2VUcmFuc3BvcnRzLCBkdGxzVHJhbnNwb3J0cywgcnRwU2VuZGVycywgLi4uXG4gICAgICAvLyBldmVyeXRoaW5nIHRoYXQgaXMgbmVlZGVkIHRvIGRlc2NyaWJlIGEgU0RQIG0tbGluZS5cbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzID0gW107XG5cbiAgICAgIC8vIHNpbmNlIHRoZSBpY2VHYXRoZXJlciBpcyBjdXJyZW50bHkgY3JlYXRlZCBpbiBjcmVhdGVPZmZlciBidXQgd2VcbiAgICAgIC8vIG11c3Qgbm90IGVtaXQgY2FuZGlkYXRlcyB1bnRpbCBhZnRlciBzZXRMb2NhbERlc2NyaXB0aW9uIHdlIGJ1ZmZlclxuICAgICAgLy8gdGhlbSBpbiB0aGlzIGFycmF5LlxuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAvLyBGSVhNRTogbmVlZCB0byBhcHBseSBpY2UgY2FuZGlkYXRlcyBpbiBhIHdheSB3aGljaCBpcyBhc3luYyBidXRcbiAgICAgIC8vIGluLW9yZGVyXG4gICAgICB0aGlzLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIuZm9yRWFjaChmdW5jdGlvbihldmVudCkge1xuICAgICAgICB2YXIgZW5kID0gIWV2ZW50LmNhbmRpZGF0ZSB8fCBPYmplY3Qua2V5cyhldmVudC5jYW5kaWRhdGUpLmxlbmd0aCA9PT0gMDtcbiAgICAgICAgaWYgKGVuZCkge1xuICAgICAgICAgIGZvciAodmFyIGogPSAxOyBqIDwgc2VjdGlvbnMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChzZWN0aW9uc1tqXS5pbmRleE9mKCdcXHJcXG5hPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJykgPT09IC0xKSB7XG4gICAgICAgICAgICAgIHNlY3Rpb25zW2pdICs9ICdhPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZS5pbmRleE9mKCd0eXAgZW5kT2ZDYW5kaWRhdGVzJylcbiAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgIHNlY3Rpb25zW2V2ZW50LmNhbmRpZGF0ZS5zZHBNTGluZUluZGV4ICsgMV0gKz1cbiAgICAgICAgICAgICAgJ2E9JyArIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgKyAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgICAgICBzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgc2VsZi5vbmljZWNhbmRpZGF0ZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFldmVudC5jYW5kaWRhdGUgJiYgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSAhPT0gJ2NvbXBsZXRlJykge1xuICAgICAgICAgIHZhciBjb21wbGV0ZSA9IHNlbGYudHJhbnNjZWl2ZXJzLmV2ZXJ5KGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5zdGF0ZSA9PT0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKGNvbXBsZXRlKSB7XG4gICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0Q29uZmlndXJhdGlvbiA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMuX2NvbmZpZztcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgIC8vIENsb25lIGlzIG5lY2Vzc2FyeSBmb3IgbG9jYWwgZGVtb3MgbW9zdGx5LCBhdHRhY2hpbmcgZGlyZWN0bHlcbiAgICAgIC8vIHRvIHR3byBkaWZmZXJlbnQgc2VuZGVycyBkb2VzIG5vdCB3b3JrIChidWlsZCAxMDU0NykuXG4gICAgICB2YXIgY2xvbmVkU3RyZWFtID0gc3RyZWFtLmNsb25lKCk7XG4gICAgICBzdHJlYW0uZ2V0VHJhY2tzKCkuZm9yRWFjaChmdW5jdGlvbih0cmFjaywgaWR4KSB7XG4gICAgICAgIHZhciBjbG9uZWRUcmFjayA9IGNsb25lZFN0cmVhbS5nZXRUcmFja3MoKVtpZHhdO1xuICAgICAgICB0cmFjay5hZGRFdmVudExpc3RlbmVyKCdlbmFibGVkJywgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICBjbG9uZWRUcmFjay5lbmFibGVkID0gZXZlbnQuZW5hYmxlZDtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnB1c2goY2xvbmVkU3RyZWFtKTtcbiAgICAgIHRoaXMuX21heWJlRmlyZU5lZ290aWF0aW9uTmVlZGVkKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID0gZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICB2YXIgaWR4ID0gdGhpcy5sb2NhbFN0cmVhbXMuaW5kZXhPZihzdHJlYW0pO1xuICAgICAgaWYgKGlkeCA+IC0xKSB7XG4gICAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnNwbGljZShpZHgsIDEpO1xuICAgICAgICB0aGlzLl9tYXliZUZpcmVOZWdvdGlhdGlvbk5lZWRlZCgpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFNlbmRlcnMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiB0aGlzLnRyYW5zY2VpdmVycy5maWx0ZXIoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuICEhdHJhbnNjZWl2ZXIucnRwU2VuZGVyO1xuICAgICAgfSlcbiAgICAgIC5tYXAoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuIHRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFJlY2VpdmVycyA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMudHJhbnNjZWl2ZXJzLmZpbHRlcihmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICByZXR1cm4gISF0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgIH0pXG4gICAgICAubWFwKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIHJldHVybiB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvLyBEZXRlcm1pbmVzIHRoZSBpbnRlcnNlY3Rpb24gb2YgbG9jYWwgYW5kIHJlbW90ZSBjYXBhYmlsaXRpZXMuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzID1cbiAgICAgICAgZnVuY3Rpb24obG9jYWxDYXBhYmlsaXRpZXMsIHJlbW90ZUNhcGFiaWxpdGllcykge1xuICAgICAgICAgIHZhciBjb21tb25DYXBhYmlsaXRpZXMgPSB7XG4gICAgICAgICAgICBjb2RlY3M6IFtdLFxuICAgICAgICAgICAgaGVhZGVyRXh0ZW5zaW9uczogW10sXG4gICAgICAgICAgICBmZWNNZWNoYW5pc21zOiBbXVxuICAgICAgICAgIH07XG4gICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzLmZvckVhY2goZnVuY3Rpb24obENvZGVjKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJlbW90ZUNhcGFiaWxpdGllcy5jb2RlY3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgdmFyIHJDb2RlYyA9IHJlbW90ZUNhcGFiaWxpdGllcy5jb2RlY3NbaV07XG4gICAgICAgICAgICAgIGlmIChsQ29kZWMubmFtZS50b0xvd2VyQ2FzZSgpID09PSByQ29kZWMubmFtZS50b0xvd2VyQ2FzZSgpICYmXG4gICAgICAgICAgICAgICAgICBsQ29kZWMuY2xvY2tSYXRlID09PSByQ29kZWMuY2xvY2tSYXRlKSB7XG4gICAgICAgICAgICAgICAgLy8gbnVtYmVyIG9mIGNoYW5uZWxzIGlzIHRoZSBoaWdoZXN0IGNvbW1vbiBudW1iZXIgb2YgY2hhbm5lbHNcbiAgICAgICAgICAgICAgICByQ29kZWMubnVtQ2hhbm5lbHMgPSBNYXRoLm1pbihsQ29kZWMubnVtQ2hhbm5lbHMsXG4gICAgICAgICAgICAgICAgICAgIHJDb2RlYy5udW1DaGFubmVscyk7XG4gICAgICAgICAgICAgICAgLy8gcHVzaCByQ29kZWMgc28gd2UgcmVwbHkgd2l0aCBvZmZlcmVyIHBheWxvYWQgdHlwZVxuICAgICAgICAgICAgICAgIGNvbW1vbkNhcGFiaWxpdGllcy5jb2RlY3MucHVzaChyQ29kZWMpO1xuXG4gICAgICAgICAgICAgICAgLy8gZGV0ZXJtaW5lIGNvbW1vbiBmZWVkYmFjayBtZWNoYW5pc21zXG4gICAgICAgICAgICAgICAgckNvZGVjLnJ0Y3BGZWVkYmFjayA9IHJDb2RlYy5ydGNwRmVlZGJhY2suZmlsdGVyKGZ1bmN0aW9uKGZiKSB7XG4gICAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxDb2RlYy5ydGNwRmVlZGJhY2subGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGxDb2RlYy5ydGNwRmVlZGJhY2tbal0udHlwZSA9PT0gZmIudHlwZSAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgbENvZGVjLnJ0Y3BGZWVkYmFja1tqXS5wYXJhbWV0ZXIgPT09IGZiLnBhcmFtZXRlcikge1xuICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGFsc28gbmVlZCB0byBkZXRlcm1pbmUgLnBhcmFtZXRlcnNcbiAgICAgICAgICAgICAgICAvLyAgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVucGVlci9vcnRjL2lzc3Vlcy81NjlcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9uc1xuICAgICAgICAgICAgICAuZm9yRWFjaChmdW5jdGlvbihsSGVhZGVyRXh0ZW5zaW9uKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZW1vdGVDYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9ucy5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICBpKyspIHtcbiAgICAgICAgICAgICAgICAgIHZhciBySGVhZGVyRXh0ZW5zaW9uID0gcmVtb3RlQ2FwYWJpbGl0aWVzLmhlYWRlckV4dGVuc2lvbnNbaV07XG4gICAgICAgICAgICAgICAgICBpZiAobEhlYWRlckV4dGVuc2lvbi51cmkgPT09IHJIZWFkZXJFeHRlbnNpb24udXJpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbW1vbkNhcGFiaWxpdGllcy5oZWFkZXJFeHRlbnNpb25zLnB1c2gockhlYWRlckV4dGVuc2lvbik7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAvLyBGSVhNRTogZmVjTWVjaGFuaXNtc1xuICAgICAgICAgIHJldHVybiBjb21tb25DYXBhYmlsaXRpZXM7XG4gICAgICAgIH07XG5cbiAgICAvLyBDcmVhdGUgSUNFIGdhdGhlcmVyLCBJQ0UgdHJhbnNwb3J0IGFuZCBEVExTIHRyYW5zcG9ydC5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyA9XG4gICAgICAgIGZ1bmN0aW9uKG1pZCwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICB2YXIgaWNlR2F0aGVyZXIgPSBuZXcgUlRDSWNlR2F0aGVyZXIoc2VsZi5pY2VPcHRpb25zKTtcbiAgICAgICAgICB2YXIgaWNlVHJhbnNwb3J0ID0gbmV3IFJUQ0ljZVRyYW5zcG9ydChpY2VHYXRoZXJlcik7XG4gICAgICAgICAgaWNlR2F0aGVyZXIub25sb2NhbGNhbmRpZGF0ZSA9IGZ1bmN0aW9uKGV2dCkge1xuICAgICAgICAgICAgdmFyIGV2ZW50ID0gbmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKTtcbiAgICAgICAgICAgIGV2ZW50LmNhbmRpZGF0ZSA9IHtzZHBNaWQ6IG1pZCwgc2RwTUxpbmVJbmRleDogc2RwTUxpbmVJbmRleH07XG5cbiAgICAgICAgICAgIHZhciBjYW5kID0gZXZ0LmNhbmRpZGF0ZTtcbiAgICAgICAgICAgIHZhciBlbmQgPSAhY2FuZCB8fCBPYmplY3Qua2V5cyhjYW5kKS5sZW5ndGggPT09IDA7XG4gICAgICAgICAgICAvLyBFZGdlIGVtaXRzIGFuIGVtcHR5IG9iamVjdCBmb3IgUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGXigKVcbiAgICAgICAgICAgIGlmIChlbmQpIHtcbiAgICAgICAgICAgICAgLy8gcG9seWZpbGwgc2luY2UgUlRDSWNlR2F0aGVyZXIuc3RhdGUgaXMgbm90IGltcGxlbWVudGVkIGluXG4gICAgICAgICAgICAgIC8vIEVkZ2UgMTA1NDcgeWV0LlxuICAgICAgICAgICAgICBpZiAoaWNlR2F0aGVyZXIuc3RhdGUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyLnN0YXRlID0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAvLyBFbWl0IGEgY2FuZGlkYXRlIHdpdGggdHlwZSBlbmRPZkNhbmRpZGF0ZXMgdG8gbWFrZSB0aGUgc2FtcGxlc1xuICAgICAgICAgICAgICAvLyB3b3JrLiBFZGdlIHJlcXVpcmVzIGFkZEljZUNhbmRpZGF0ZSB3aXRoIHRoaXMgZW1wdHkgY2FuZGlkYXRlXG4gICAgICAgICAgICAgIC8vIHRvIHN0YXJ0IGNoZWNraW5nLiBUaGUgcmVhbCBzb2x1dGlvbiBpcyB0byBzaWduYWxcbiAgICAgICAgICAgICAgLy8gZW5kLW9mLWNhbmRpZGF0ZXMgdG8gdGhlIG90aGVyIHNpZGUgd2hlbiBnZXR0aW5nIHRoZSBudWxsXG4gICAgICAgICAgICAgIC8vIGNhbmRpZGF0ZSBidXQgc29tZSBhcHBzIChsaWtlIHRoZSBzYW1wbGVzKSBkb24ndCBkbyB0aGF0LlxuICAgICAgICAgICAgICBldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlID1cbiAgICAgICAgICAgICAgICAgICdjYW5kaWRhdGU6MSAxIHVkcCAxIDAuMC4wLjAgOSB0eXAgZW5kT2ZDYW5kaWRhdGVzJztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIFJUQ0ljZUNhbmRpZGF0ZSBkb2Vzbid0IGhhdmUgYSBjb21wb25lbnQsIG5lZWRzIHRvIGJlIGFkZGVkXG4gICAgICAgICAgICAgIGNhbmQuY29tcG9uZW50ID0gaWNlVHJhbnNwb3J0LmNvbXBvbmVudCA9PT0gJ1JUQ1AnID8gMiA6IDE7XG4gICAgICAgICAgICAgIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgPSBTRFBVdGlscy53cml0ZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gdXBkYXRlIGxvY2FsIGRlc2NyaXB0aW9uLlxuICAgICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICAgIGlmIChldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlLmluZGV4T2YoJ3R5cCBlbmRPZkNhbmRpZGF0ZXMnKVxuICAgICAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgICAgICBzZWN0aW9uc1tldmVudC5jYW5kaWRhdGUuc2RwTUxpbmVJbmRleCArIDFdICs9XG4gICAgICAgICAgICAgICAgICAnYT0nICsgZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc2VjdGlvbnNbZXZlbnQuY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXggKyAxXSArPVxuICAgICAgICAgICAgICAgICAgJ2E9ZW5kLW9mLWNhbmRpZGF0ZXNcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCA9IHNlY3Rpb25zLmpvaW4oJycpO1xuXG4gICAgICAgICAgICB2YXIgY29tcGxldGUgPSBzZWxmLnRyYW5zY2VpdmVycy5ldmVyeShmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICAgIHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyLnN0YXRlID09PSAnY29tcGxldGVkJztcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBFbWl0IGNhbmRpZGF0ZSBpZiBsb2NhbERlc2NyaXB0aW9uIGlzIHNldC5cbiAgICAgICAgICAgIC8vIEFsc28gZW1pdHMgbnVsbCBjYW5kaWRhdGUgd2hlbiBhbGwgZ2F0aGVyZXJzIGFyZSBjb21wbGV0ZS5cbiAgICAgICAgICAgIHN3aXRjaCAoc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSkge1xuICAgICAgICAgICAgICBjYXNlICduZXcnOlxuICAgICAgICAgICAgICAgIHNlbGYuX2xvY2FsSWNlQ2FuZGlkYXRlc0J1ZmZlci5wdXNoKGV2ZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZW5kICYmIGNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIucHVzaChcbiAgICAgICAgICAgICAgICAgICAgICBuZXcgRXZlbnQoJ2ljZWNhbmRpZGF0ZScpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgJ2dhdGhlcmluZyc6XG4gICAgICAgICAgICAgICAgc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcygpO1xuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYub25pY2VjYW5kaWRhdGUgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUoZXZlbnQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoY29tcGxldGUpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2ljZWNhbmRpZGF0ZScpKTtcbiAgICAgICAgICAgICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUobmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgJ2NvbXBsZXRlJzpcbiAgICAgICAgICAgICAgICAvLyBzaG91bGQgbm90IGhhcHBlbi4uLiBjdXJyZW50bHkhXG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGRlZmF1bHQ6IC8vIG5vLW9wLlxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH07XG4gICAgICAgICAgaWNlVHJhbnNwb3J0Lm9uaWNlc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHNlbGYuX3VwZGF0ZUNvbm5lY3Rpb25TdGF0ZSgpO1xuICAgICAgICAgIH07XG5cbiAgICAgICAgICB2YXIgZHRsc1RyYW5zcG9ydCA9IG5ldyBSVENEdGxzVHJhbnNwb3J0KGljZVRyYW5zcG9ydCk7XG4gICAgICAgICAgZHRsc1RyYW5zcG9ydC5vbmR0bHNzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgc2VsZi5fdXBkYXRlQ29ubmVjdGlvblN0YXRlKCk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICBkdGxzVHJhbnNwb3J0Lm9uZXJyb3IgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIC8vIG9uZXJyb3IgZG9lcyBub3Qgc2V0IHN0YXRlIHRvIGZhaWxlZCBieSBpdHNlbGYuXG4gICAgICAgICAgICBkdGxzVHJhbnNwb3J0LnN0YXRlID0gJ2ZhaWxlZCc7XG4gICAgICAgICAgICBzZWxmLl91cGRhdGVDb25uZWN0aW9uU3RhdGUoKTtcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGljZUdhdGhlcmVyOiBpY2VHYXRoZXJlcixcbiAgICAgICAgICAgIGljZVRyYW5zcG9ydDogaWNlVHJhbnNwb3J0LFxuICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogZHRsc1RyYW5zcG9ydFxuICAgICAgICAgIH07XG4gICAgICAgIH07XG5cbiAgICAvLyBTdGFydCB0aGUgUlRQIFNlbmRlciBhbmQgUmVjZWl2ZXIgZm9yIGEgdHJhbnNjZWl2ZXIuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdHJhbnNjZWl2ZSA9IGZ1bmN0aW9uKHRyYW5zY2VpdmVyLFxuICAgICAgICBzZW5kLCByZWN2KSB7XG4gICAgICB2YXIgcGFyYW1zID0gdGhpcy5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgIHRyYW5zY2VpdmVyLnJlbW90ZUNhcGFiaWxpdGllcyk7XG4gICAgICBpZiAoc2VuZCAmJiB0cmFuc2NlaXZlci5ydHBTZW5kZXIpIHtcbiAgICAgICAgcGFyYW1zLmVuY29kaW5ncyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgIHBhcmFtcy5ydGNwID0ge1xuICAgICAgICAgIGNuYW1lOiBTRFBVdGlscy5sb2NhbENOYW1lXG4gICAgICAgIH07XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzLmxlbmd0aCkge1xuICAgICAgICAgIHBhcmFtcy5ydGNwLnNzcmMgPSB0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmM7XG4gICAgICAgIH1cbiAgICAgICAgdHJhbnNjZWl2ZXIucnRwU2VuZGVyLnNlbmQocGFyYW1zKTtcbiAgICAgIH1cbiAgICAgIGlmIChyZWN2ICYmIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyKSB7XG4gICAgICAgIC8vIHJlbW92ZSBSVFggZmllbGQgaW4gRWRnZSAxNDk0MlxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIua2luZCA9PT0gJ3ZpZGVvJ1xuICAgICAgICAgICAgJiYgdHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVycykge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMuZm9yRWFjaChmdW5jdGlvbihwKSB7XG4gICAgICAgICAgICBkZWxldGUgcC5ydHg7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcGFyYW1zLmVuY29kaW5ncyA9IHRyYW5zY2VpdmVyLnJlY3ZFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgIHBhcmFtcy5ydGNwID0ge1xuICAgICAgICAgIGNuYW1lOiB0cmFuc2NlaXZlci5jbmFtZVxuICAgICAgICB9O1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuc2VuZEVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGgpIHtcbiAgICAgICAgICBwYXJhbXMucnRjcC5zc3JjID0gdHJhbnNjZWl2ZXIuc2VuZEVuY29kaW5nUGFyYW1ldGVyc1swXS5zc3JjO1xuICAgICAgICB9XG4gICAgICAgIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyLnJlY2VpdmUocGFyYW1zKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRMb2NhbERlc2NyaXB0aW9uID1cbiAgICAgICAgZnVuY3Rpb24oZGVzY3JpcHRpb24pIHtcbiAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgdmFyIHNlY3Rpb25zO1xuICAgICAgICAgIHZhciBzZXNzaW9ucGFydDtcbiAgICAgICAgICBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ29mZmVyJykge1xuICAgICAgICAgICAgLy8gRklYTUU6IFdoYXQgd2FzIHRoZSBwdXJwb3NlIG9mIHRoaXMgZW1wdHkgaWYgc3RhdGVtZW50P1xuICAgICAgICAgICAgLy8gaWYgKCF0aGlzLl9wZW5kaW5nT2ZmZXIpIHtcbiAgICAgICAgICAgIC8vIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAodGhpcy5fcGVuZGluZ09mZmVyKSB7XG4gICAgICAgICAgICAgIC8vIFZFUlkgbGltaXRlZCBzdXBwb3J0IGZvciBTRFAgbXVuZ2luZy4gTGltaXRlZCB0bzpcbiAgICAgICAgICAgICAgLy8gKiBjaGFuZ2luZyB0aGUgb3JkZXIgb2YgY29kZWNzXG4gICAgICAgICAgICAgIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhkZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgICBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgICAgIHNlY3Rpb25zLmZvckVhY2goZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZHBNTGluZUluZGV4KSB7XG4gICAgICAgICAgICAgICAgdmFyIGNhcHMgPSBTRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMobWVkaWFTZWN0aW9uKTtcbiAgICAgICAgICAgICAgICBzZWxmLl9wZW5kaW5nT2ZmZXJbc2RwTUxpbmVJbmRleF0ubG9jYWxDYXBhYmlsaXRpZXMgPSBjYXBzO1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgdGhpcy50cmFuc2NlaXZlcnMgPSB0aGlzLl9wZW5kaW5nT2ZmZXI7XG4gICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9wZW5kaW5nT2ZmZXI7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmIChkZXNjcmlwdGlvbi50eXBlID09PSAnYW5zd2VyJykge1xuICAgICAgICAgICAgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKHNlbGYucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICAgIHNlc3Npb25wYXJ0ID0gc2VjdGlvbnMuc2hpZnQoKTtcbiAgICAgICAgICAgIHZhciBpc0ljZUxpdGUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChzZXNzaW9ucGFydCxcbiAgICAgICAgICAgICAgICAnYT1pY2UtbGl0ZScpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XTtcbiAgICAgICAgICAgICAgdmFyIGljZUdhdGhlcmVyID0gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXI7XG4gICAgICAgICAgICAgIHZhciBpY2VUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIHZhciBkdGxzVHJhbnNwb3J0ID0gdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydDtcbiAgICAgICAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzID0gdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXM7XG4gICAgICAgICAgICAgIHZhciByZW1vdGVDYXBhYmlsaXRpZXMgPSB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWVkaWFTZWN0aW9uLnNwbGl0KCdcXG4nLCAxKVswXVxuICAgICAgICAgICAgICAgICAgLnNwbGl0KCcgJywgMilbMV0gPT09ICcwJztcblxuICAgICAgICAgICAgICBpZiAoIXJlamVjdGVkICYmICF0cmFuc2NlaXZlci5pc0RhdGFjaGFubmVsKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKFxuICAgICAgICAgICAgICAgICAgICBtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgICBpZiAoaXNJY2VMaXRlKSB7XG4gICAgICAgICAgICAgICAgICB2YXIgY2FuZHMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWNhbmRpZGF0ZTonKVxuICAgICAgICAgICAgICAgICAgLm1hcChmdW5jdGlvbihjYW5kKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAuZmlsdGVyKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhbmQuY29tcG9uZW50ID09PSAnMSc7XG4gICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgIC8vIGljZS1saXRlIG9ubHkgaW5jbHVkZXMgaG9zdCBjYW5kaWRhdGVzIGluIHRoZSBTRFAgc28gd2UgY2FuXG4gICAgICAgICAgICAgICAgICAvLyB1c2Ugc2V0UmVtb3RlQ2FuZGlkYXRlcyAod2hpY2ggaW1wbGllcyBhblxuICAgICAgICAgICAgICAgICAgLy8gUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGUpXG4gICAgICAgICAgICAgICAgICBpZiAoY2FuZHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUR0bHNQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMoXG4gICAgICAgICAgICAgICAgICAgIG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpO1xuICAgICAgICAgICAgICAgIGlmIChpc0ljZUxpdGUpIHtcbiAgICAgICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnc2VydmVyJztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgaWNlVHJhbnNwb3J0LnN0YXJ0KGljZUdhdGhlcmVyLCByZW1vdGVJY2VQYXJhbWV0ZXJzLFxuICAgICAgICAgICAgICAgICAgICAgIGlzSWNlTGl0ZSA/ICdjb250cm9sbGluZycgOiAnY29udHJvbGxlZCcpO1xuICAgICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIGludGVyc2VjdGlvbiBvZiBjYXBhYmlsaXRpZXMuXG4gICAgICAgICAgICAgICAgdmFyIHBhcmFtcyA9IHNlbGYuX2dldENvbW1vbkNhcGFiaWxpdGllcyhsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzKTtcblxuICAgICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBTZW5kZXIuIFRoZSBSVENSdHBSZWNlaXZlciBmb3IgdGhpc1xuICAgICAgICAgICAgICAgIC8vIHRyYW5zY2VpdmVyIGhhcyBhbHJlYWR5IGJlZW4gc3RhcnRlZCBpbiBzZXRSZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgICBwYXJhbXMuY29kZWNzLmxlbmd0aCA+IDAsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0ge1xuICAgICAgICAgICAgdHlwZTogZGVzY3JpcHRpb24udHlwZSxcbiAgICAgICAgICAgIHNkcDogZGVzY3JpcHRpb24uc2RwXG4gICAgICAgICAgfTtcbiAgICAgICAgICBzd2l0Y2ggKGRlc2NyaXB0aW9uLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ29mZmVyJzpcbiAgICAgICAgICAgICAgdGhpcy5fdXBkYXRlU2lnbmFsaW5nU3RhdGUoJ2hhdmUtbG9jYWwtb2ZmZXInKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbnN3ZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnc3RhYmxlJyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigndW5zdXBwb3J0ZWQgdHlwZSBcIicgKyBkZXNjcmlwdGlvbi50eXBlICtcbiAgICAgICAgICAgICAgICAgICdcIicpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIElmIGEgc3VjY2VzcyBjYWxsYmFjayB3YXMgcHJvdmlkZWQsIGVtaXQgSUNFIGNhbmRpZGF0ZXMgYWZ0ZXIgaXRcbiAgICAgICAgICAvLyBoYXMgYmVlbiBleGVjdXRlZC4gT3RoZXJ3aXNlLCBlbWl0IGNhbGxiYWNrIGFmdGVyIHRoZSBQcm9taXNlIGlzXG4gICAgICAgICAgLy8gcmVzb2x2ZWQuXG4gICAgICAgICAgdmFyIGhhc0NhbGxiYWNrID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiZcbiAgICAgICAgICAgIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbic7XG4gICAgICAgICAgaWYgKGhhc0NhbGxiYWNrKSB7XG4gICAgICAgICAgICB2YXIgY2IgPSBhcmd1bWVudHNbMV07XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgY2IoKTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHNlbGYuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMoKTtcbiAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB2YXIgcCA9IFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgICAgIHAudGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmICghaGFzQ2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFVzdWFsbHkgY2FuZGlkYXRlcyB3aWxsIGJlIGVtaXR0ZWQgZWFybGllci5cbiAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcy5iaW5kKHNlbGYpLCA1MDApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybiBwO1xuICAgICAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9XG4gICAgICAgIGZ1bmN0aW9uKGRlc2NyaXB0aW9uKSB7XG4gICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgIHZhciBzdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICAgICAgICB2YXIgcmVjZWl2ZXJMaXN0ID0gW107XG4gICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhkZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgIHZhciBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgdmFyIGlzSWNlTGl0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1pY2UtbGl0ZScpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgdGhpcy51c2luZ0J1bmRsZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1ncm91cDpCVU5ETEUgJykubGVuZ3RoID4gMDtcbiAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIG1saW5lID0gbGluZXNbMF0uc3Vic3RyKDIpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICB2YXIga2luZCA9IG1saW5lWzBdO1xuICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWxpbmVbMV0gPT09ICcwJztcbiAgICAgICAgICAgIHZhciBkaXJlY3Rpb24gPSBTRFBVdGlscy5nZXREaXJlY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCk7XG5cbiAgICAgICAgICAgIHZhciBtaWQgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPW1pZDonKTtcbiAgICAgICAgICAgIGlmIChtaWQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgIG1pZCA9IG1pZFswXS5zdWJzdHIoNik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBtaWQgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUmVqZWN0IGRhdGFjaGFubmVscyB3aGljaCBhcmUgbm90IGltcGxlbWVudGVkIHlldC5cbiAgICAgICAgICAgIGlmIChraW5kID09PSAnYXBwbGljYXRpb24nICYmIG1saW5lWzJdID09PSAnRFRMUy9TQ1RQJykge1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgICAgICBpc0RhdGFjaGFubmVsOiB0cnVlXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHRyYW5zY2VpdmVyO1xuICAgICAgICAgICAgdmFyIGljZUdhdGhlcmVyO1xuICAgICAgICAgICAgdmFyIGljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgIHZhciBkdGxzVHJhbnNwb3J0O1xuICAgICAgICAgICAgdmFyIHJ0cFNlbmRlcjtcbiAgICAgICAgICAgIHZhciBydHBSZWNlaXZlcjtcbiAgICAgICAgICAgIHZhciBzZW5kRW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgdmFyIHJlY3ZFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgICAgICB2YXIgbG9jYWxDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgIHZhciB0cmFjaztcbiAgICAgICAgICAgIC8vIEZJWE1FOiBlbnN1cmUgdGhlIG1lZGlhU2VjdGlvbiBoYXMgcnRjcC1tdXggc2V0LlxuICAgICAgICAgICAgdmFyIHJlbW90ZUNhcGFiaWxpdGllcyA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnM7XG4gICAgICAgICAgICB2YXIgcmVtb3RlRHRsc1BhcmFtZXRlcnM7XG4gICAgICAgICAgICBpZiAoIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAgIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgcmVtb3RlRHRsc1BhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXREdGxzUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24sXG4gICAgICAgICAgICAgICAgICBzZXNzaW9ucGFydCk7XG4gICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnY2xpZW50JztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMgPVxuICAgICAgICAgICAgICAgIFNEUFV0aWxzLnBhcnNlUnRwRW5jb2RpbmdQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbik7XG5cbiAgICAgICAgICAgIHZhciBjbmFtZTtcbiAgICAgICAgICAgIC8vIEdldHMgdGhlIGZpcnN0IFNTUkMuIE5vdGUgdGhhdCB3aXRoIFJUWCB0aGVyZSBtaWdodCBiZSBtdWx0aXBsZVxuICAgICAgICAgICAgLy8gU1NSQ3MuXG4gICAgICAgICAgICB2YXIgcmVtb3RlU3NyYyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgICAgICAgICAgICAgIC5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbHRlcihmdW5jdGlvbihvYmopIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBvYmouYXR0cmlidXRlID09PSAnY25hbWUnO1xuICAgICAgICAgICAgICAgIH0pWzBdO1xuICAgICAgICAgICAgaWYgKHJlbW90ZVNzcmMpIHtcbiAgICAgICAgICAgICAgY25hbWUgPSByZW1vdGVTc3JjLnZhbHVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgaXNDb21wbGV0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAnYT1lbmQtb2YtY2FuZGlkYXRlcycsIHNlc3Npb25wYXJ0KS5sZW5ndGggPiAwO1xuICAgICAgICAgICAgdmFyIGNhbmRzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1jYW5kaWRhdGU6JylcbiAgICAgICAgICAgICAgICAubWFwKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC5maWx0ZXIoZnVuY3Rpb24oY2FuZCkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhbmQuY29tcG9uZW50ID09PSAnMSc7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ29mZmVyJyAmJiAhcmVqZWN0ZWQpIHtcbiAgICAgICAgICAgICAgdmFyIHRyYW5zcG9ydHMgPSBzZWxmLnVzaW5nQnVuZGxlICYmIHNkcE1MaW5lSW5kZXggPiAwID8ge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyOiBzZWxmLnRyYW5zY2VpdmVyc1swXS5pY2VHYXRoZXJlcixcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHNlbGYudHJhbnNjZWl2ZXJzWzBdLmljZVRyYW5zcG9ydCxcbiAgICAgICAgICAgICAgICBkdGxzVHJhbnNwb3J0OiBzZWxmLnRyYW5zY2VpdmVyc1swXS5kdGxzVHJhbnNwb3J0XG4gICAgICAgICAgICAgIH0gOiBzZWxmLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyhtaWQsIHNkcE1MaW5lSW5kZXgpO1xuXG4gICAgICAgICAgICAgIGlmIChpc0NvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgdHJhbnNwb3J0cy5pY2VUcmFuc3BvcnQuc2V0UmVtb3RlQ2FuZGlkYXRlcyhjYW5kcyk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllcyA9IFJUQ1J0cFJlY2VpdmVyLmdldENhcGFiaWxpdGllcyhraW5kKTtcblxuICAgICAgICAgICAgICAvLyBmaWx0ZXIgUlRYIHVudGlsIGFkZGl0aW9uYWwgc3R1ZmYgbmVlZGVkIGZvciBSVFggaXMgaW1wbGVtZW50ZWRcbiAgICAgICAgICAgICAgLy8gaW4gYWRhcHRlci5qc1xuICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MgPSBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MuZmlsdGVyKFxuICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oY29kZWMpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNvZGVjLm5hbWUgIT09ICdydHgnO1xuICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IFt7XG4gICAgICAgICAgICAgICAgc3NyYzogKDIgKiBzZHBNTGluZUluZGV4ICsgMikgKiAxMDAxXG4gICAgICAgICAgICAgIH1dO1xuXG4gICAgICAgICAgICAgIHJ0cFJlY2VpdmVyID0gbmV3IFJUQ1J0cFJlY2VpdmVyKHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCwga2luZCk7XG5cbiAgICAgICAgICAgICAgdHJhY2sgPSBydHBSZWNlaXZlci50cmFjaztcbiAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LnB1c2goW3RyYWNrLCBydHBSZWNlaXZlcl0pO1xuICAgICAgICAgICAgICAvLyBGSVhNRTogbm90IGNvcnJlY3Qgd2hlbiB0aGVyZSBhcmUgbXVsdGlwbGUgc3RyZWFtcyBidXQgdGhhdCBpc1xuICAgICAgICAgICAgICAvLyBub3QgY3VycmVudGx5IHN1cHBvcnRlZCBpbiB0aGlzIHNoaW0uXG4gICAgICAgICAgICAgIHN0cmVhbS5hZGRUcmFjayh0cmFjayk7XG5cbiAgICAgICAgICAgICAgLy8gRklYTUU6IGxvb2sgYXQgZGlyZWN0aW9uLlxuICAgICAgICAgICAgICBpZiAoc2VsZi5sb2NhbFN0cmVhbXMubGVuZ3RoID4gMCAmJlxuICAgICAgICAgICAgICAgICAgc2VsZi5sb2NhbFN0cmVhbXNbMF0uZ2V0VHJhY2tzKCkubGVuZ3RoID49IHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgICAgICAgICB2YXIgbG9jYWxUcmFjaztcbiAgICAgICAgICAgICAgICBpZiAoa2luZCA9PT0gJ2F1ZGlvJykge1xuICAgICAgICAgICAgICAgICAgbG9jYWxUcmFjayA9IHNlbGYubG9jYWxTdHJlYW1zWzBdLmdldEF1ZGlvVHJhY2tzKClbMF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChraW5kID09PSAndmlkZW8nKSB7XG4gICAgICAgICAgICAgICAgICBsb2NhbFRyYWNrID0gc2VsZi5sb2NhbFN0cmVhbXNbMF0uZ2V0VmlkZW9UcmFja3MoKVswXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGxvY2FsVHJhY2spIHtcbiAgICAgICAgICAgICAgICAgIHJ0cFNlbmRlciA9IG5ldyBSVENSdHBTZW5kZXIobG9jYWxUcmFjayxcbiAgICAgICAgICAgICAgICAgICAgICB0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdID0ge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyOiB0cmFuc3BvcnRzLmljZUdhdGhlcmVyLFxuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydDogdHJhbnNwb3J0cy5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LFxuICAgICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzOiBsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICByZW1vdGVDYXBhYmlsaXRpZXM6IHJlbW90ZUNhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICBydHBTZW5kZXI6IHJ0cFNlbmRlcixcbiAgICAgICAgICAgICAgICBydHBSZWNlaXZlcjogcnRwUmVjZWl2ZXIsXG4gICAgICAgICAgICAgICAga2luZDoga2luZCxcbiAgICAgICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgICAgICBjbmFtZTogY25hbWUsXG4gICAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVyczogc2VuZEVuY29kaW5nUGFyYW1ldGVycyxcbiAgICAgICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzOiByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBSZWNlaXZlciBub3cuIFRoZSBSVFBTZW5kZXIgaXMgc3RhcnRlZCBpblxuICAgICAgICAgICAgICAvLyBzZXRMb2NhbERlc2NyaXB0aW9uLlxuICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdLFxuICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPT09ICdzZW5kcmVjdicgfHwgZGlyZWN0aW9uID09PSAnc2VuZG9ubHknKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ2Fuc3dlcicgJiYgIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHRyYW5zY2VpdmVyID0gc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF07XG4gICAgICAgICAgICAgIGljZUdhdGhlcmVyID0gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXI7XG4gICAgICAgICAgICAgIGljZVRyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIHJ0cFNlbmRlciA9IHRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgICAgICAgICAgcnRwUmVjZWl2ZXIgPSB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzID0gdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgICAgc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF0ucmVjdkVuY29kaW5nUGFyYW1ldGVycyA9XG4gICAgICAgICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XS5yZW1vdGVDYXBhYmlsaXRpZXMgPVxuICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzO1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XS5jbmFtZSA9IGNuYW1lO1xuXG4gICAgICAgICAgICAgIGlmICgoaXNJY2VMaXRlIHx8IGlzQ29tcGxldGUpICYmIGNhbmRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zdGFydChpY2VHYXRoZXJlciwgcmVtb3RlSWNlUGFyYW1ldGVycyxcbiAgICAgICAgICAgICAgICAgICAgJ2NvbnRyb2xsaW5nJyk7XG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID09PSAnc2VuZHJlY3YnIHx8IGRpcmVjdGlvbiA9PT0gJ3JlY3Zvbmx5JyxcbiAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdzZW5kb25seScpO1xuXG4gICAgICAgICAgICAgIGlmIChydHBSZWNlaXZlciAmJlxuICAgICAgICAgICAgICAgICAgKGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdzZW5kb25seScpKSB7XG4gICAgICAgICAgICAgICAgdHJhY2sgPSBydHBSZWNlaXZlci50cmFjaztcbiAgICAgICAgICAgICAgICByZWNlaXZlckxpc3QucHVzaChbdHJhY2ssIHJ0cFJlY2VpdmVyXSk7XG4gICAgICAgICAgICAgICAgc3RyZWFtLmFkZFRyYWNrKHRyYWNrKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBGSVhNRTogYWN0dWFsbHkgdGhlIHJlY2VpdmVyIHNob3VsZCBiZSBjcmVhdGVkIGxhdGVyLlxuICAgICAgICAgICAgICAgIGRlbGV0ZSB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgdGhpcy5yZW1vdGVEZXNjcmlwdGlvbiA9IHtcbiAgICAgICAgICAgIHR5cGU6IGRlc2NyaXB0aW9uLnR5cGUsXG4gICAgICAgICAgICBzZHA6IGRlc2NyaXB0aW9uLnNkcFxuICAgICAgICAgIH07XG4gICAgICAgICAgc3dpdGNoIChkZXNjcmlwdGlvbi50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdvZmZlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdoYXZlLXJlbW90ZS1vZmZlcicpO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2Fuc3dlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdzdGFibGUnKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCd1bnN1cHBvcnRlZCB0eXBlIFwiJyArIGRlc2NyaXB0aW9uLnR5cGUgK1xuICAgICAgICAgICAgICAgICAgJ1wiJyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChzdHJlYW0uZ2V0VHJhY2tzKCkubGVuZ3RoKSB7XG4gICAgICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbXMucHVzaChzdHJlYW0pO1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnYWRkc3RyZWFtJyk7XG4gICAgICAgICAgICAgIGV2ZW50LnN0cmVhbSA9IHN0cmVhbTtcbiAgICAgICAgICAgICAgc2VsZi5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYub25hZGRzdHJlYW0gIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYub25hZGRzdHJlYW0oZXZlbnQpO1xuICAgICAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LmZvckVhY2goZnVuY3Rpb24oaXRlbSkge1xuICAgICAgICAgICAgICAgIHZhciB0cmFjayA9IGl0ZW1bMF07XG4gICAgICAgICAgICAgICAgdmFyIHJlY2VpdmVyID0gaXRlbVsxXTtcbiAgICAgICAgICAgICAgICB2YXIgdHJhY2tFdmVudCA9IG5ldyBFdmVudCgndHJhY2snKTtcbiAgICAgICAgICAgICAgICB0cmFja0V2ZW50LnRyYWNrID0gdHJhY2s7XG4gICAgICAgICAgICAgICAgdHJhY2tFdmVudC5yZWNlaXZlciA9IHJlY2VpdmVyO1xuICAgICAgICAgICAgICAgIHRyYWNrRXZlbnQuc3RyZWFtcyA9IFtzdHJlYW1dO1xuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYub250cmFjayAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYub250cmFjayh0cmFja0V2ZW50KTtcbiAgICAgICAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICAgICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY2xvc2UgPSBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgLyogbm90IHlldFxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgICovXG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5kdGxzVHJhbnNwb3J0KSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnJ0cFNlbmRlcikge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJ0cFNlbmRlci5zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyKSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIC8vIEZJWE1FOiBjbGVhbiB1cCB0cmFja3MsIGxvY2FsIHN0cmVhbXMsIHJlbW90ZSBzdHJlYW1zLCBldGNcbiAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdjbG9zZWQnKTtcbiAgICB9O1xuXG4gICAgLy8gVXBkYXRlIHRoZSBzaWduYWxpbmcgc3RhdGUuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdXBkYXRlU2lnbmFsaW5nU3RhdGUgPVxuICAgICAgICBmdW5jdGlvbihuZXdTdGF0ZSkge1xuICAgICAgICAgIHRoaXMuc2lnbmFsaW5nU3RhdGUgPSBuZXdTdGF0ZTtcbiAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ3NpZ25hbGluZ3N0YXRlY2hhbmdlJyk7XG4gICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICBpZiAodGhpcy5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UoZXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIC8vIERldGVybWluZSB3aGV0aGVyIHRvIGZpcmUgdGhlIG5lZ290aWF0aW9ubmVlZGVkIGV2ZW50LlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX21heWJlRmlyZU5lZ290aWF0aW9uTmVlZGVkID1cbiAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgLy8gRmlyZSBhd2F5IChmb3Igbm93KS5cbiAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ25lZ290aWF0aW9ubmVlZGVkJyk7XG4gICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICBpZiAodGhpcy5vbm5lZ290aWF0aW9ubmVlZGVkICE9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQoZXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIC8vIFVwZGF0ZSB0aGUgY29ubmVjdGlvbiBzdGF0ZS5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl91cGRhdGVDb25uZWN0aW9uU3RhdGUgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBuZXdTdGF0ZTtcbiAgICAgIHZhciBzdGF0ZXMgPSB7XG4gICAgICAgICduZXcnOiAwLFxuICAgICAgICBjbG9zZWQ6IDAsXG4gICAgICAgIGNvbm5lY3Rpbmc6IDAsXG4gICAgICAgIGNoZWNraW5nOiAwLFxuICAgICAgICBjb25uZWN0ZWQ6IDAsXG4gICAgICAgIGNvbXBsZXRlZDogMCxcbiAgICAgICAgZmFpbGVkOiAwXG4gICAgICB9O1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBzdGF0ZXNbdHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0LnN0YXRlXSsrO1xuICAgICAgICBzdGF0ZXNbdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5zdGF0ZV0rKztcbiAgICAgIH0pO1xuICAgICAgLy8gSUNFVHJhbnNwb3J0LmNvbXBsZXRlZCBhbmQgY29ubmVjdGVkIGFyZSB0aGUgc2FtZSBmb3IgdGhpcyBwdXJwb3NlLlxuICAgICAgc3RhdGVzLmNvbm5lY3RlZCArPSBzdGF0ZXMuY29tcGxldGVkO1xuXG4gICAgICBuZXdTdGF0ZSA9ICduZXcnO1xuICAgICAgaWYgKHN0YXRlcy5mYWlsZWQgPiAwKSB7XG4gICAgICAgIG5ld1N0YXRlID0gJ2ZhaWxlZCc7XG4gICAgICB9IGVsc2UgaWYgKHN0YXRlcy5jb25uZWN0aW5nID4gMCB8fCBzdGF0ZXMuY2hlY2tpbmcgPiAwKSB7XG4gICAgICAgIG5ld1N0YXRlID0gJ2Nvbm5lY3RpbmcnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuZGlzY29ubmVjdGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdkaXNjb25uZWN0ZWQnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMubmV3ID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICduZXcnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuY29ubmVjdGVkID4gMCB8fCBzdGF0ZXMuY29tcGxldGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdjb25uZWN0ZWQnO1xuICAgICAgfVxuXG4gICAgICBpZiAobmV3U3RhdGUgIT09IHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgIHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlID0gbmV3U3RhdGU7XG4gICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlJyk7XG4gICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmICh0aGlzLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVPZmZlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgaWYgKHRoaXMuX3BlbmRpbmdPZmZlcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2NyZWF0ZU9mZmVyIGNhbGxlZCB3aGlsZSB0aGVyZSBpcyBhIHBlbmRpbmcgb2ZmZXIuJyk7XG4gICAgICB9XG4gICAgICB2YXIgb2ZmZXJPcHRpb25zO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDEgJiYgdHlwZW9mIGFyZ3VtZW50c1swXSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBvZmZlck9wdGlvbnMgPSBhcmd1bWVudHNbMF07XG4gICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDMpIHtcbiAgICAgICAgb2ZmZXJPcHRpb25zID0gYXJndW1lbnRzWzJdO1xuICAgICAgfVxuXG4gICAgICB2YXIgdHJhY2tzID0gW107XG4gICAgICB2YXIgbnVtQXVkaW9UcmFja3MgPSAwO1xuICAgICAgdmFyIG51bVZpZGVvVHJhY2tzID0gMDtcbiAgICAgIC8vIERlZmF1bHQgdG8gc2VuZHJlY3YuXG4gICAgICBpZiAodGhpcy5sb2NhbFN0cmVhbXMubGVuZ3RoKSB7XG4gICAgICAgIG51bUF1ZGlvVHJhY2tzID0gdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0QXVkaW9UcmFja3MoKS5sZW5ndGg7XG4gICAgICAgIG51bVZpZGVvVHJhY2tzID0gdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGg7XG4gICAgICB9XG4gICAgICAvLyBEZXRlcm1pbmUgbnVtYmVyIG9mIGF1ZGlvIGFuZCB2aWRlbyB0cmFja3Mgd2UgbmVlZCB0byBzZW5kL3JlY3YuXG4gICAgICBpZiAob2ZmZXJPcHRpb25zKSB7XG4gICAgICAgIC8vIFJlamVjdCBDaHJvbWUgbGVnYWN5IGNvbnN0cmFpbnRzLlxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm1hbmRhdG9yeSB8fCBvZmZlck9wdGlvbnMub3B0aW9uYWwpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFxuICAgICAgICAgICAgICAnTGVnYWN5IG1hbmRhdG9yeS9vcHRpb25hbCBjb25zdHJhaW50cyBub3Qgc3VwcG9ydGVkLicpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVBdWRpbyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgbnVtQXVkaW9UcmFja3MgPSBvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVBdWRpbztcbiAgICAgICAgfVxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlVmlkZW8gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIG51bVZpZGVvVHJhY2tzID0gb2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlVmlkZW87XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLmxvY2FsU3RyZWFtcy5sZW5ndGgpIHtcbiAgICAgICAgLy8gUHVzaCBsb2NhbCBzdHJlYW1zLlxuICAgICAgICB0aGlzLmxvY2FsU3RyZWFtc1swXS5nZXRUcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uKHRyYWNrKSB7XG4gICAgICAgICAgdHJhY2tzLnB1c2goe1xuICAgICAgICAgICAga2luZDogdHJhY2sua2luZCxcbiAgICAgICAgICAgIHRyYWNrOiB0cmFjayxcbiAgICAgICAgICAgIHdhbnRSZWNlaXZlOiB0cmFjay5raW5kID09PSAnYXVkaW8nID9cbiAgICAgICAgICAgICAgICBudW1BdWRpb1RyYWNrcyA+IDAgOiBudW1WaWRlb1RyYWNrcyA+IDBcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpZiAodHJhY2sua2luZCA9PT0gJ2F1ZGlvJykge1xuICAgICAgICAgICAgbnVtQXVkaW9UcmFja3MtLTtcbiAgICAgICAgICB9IGVsc2UgaWYgKHRyYWNrLmtpbmQgPT09ICd2aWRlbycpIHtcbiAgICAgICAgICAgIG51bVZpZGVvVHJhY2tzLS07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIC8vIENyZWF0ZSBNLWxpbmVzIGZvciByZWN2b25seSBzdHJlYW1zLlxuICAgICAgd2hpbGUgKG51bUF1ZGlvVHJhY2tzID4gMCB8fCBudW1WaWRlb1RyYWNrcyA+IDApIHtcbiAgICAgICAgaWYgKG51bUF1ZGlvVHJhY2tzID4gMCkge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6ICdhdWRpbycsXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG51bUF1ZGlvVHJhY2tzLS07XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG51bVZpZGVvVHJhY2tzID4gMCkge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6ICd2aWRlbycsXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG51bVZpZGVvVHJhY2tzLS07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdmFyIHNkcCA9IFNEUFV0aWxzLndyaXRlU2Vzc2lvbkJvaWxlcnBsYXRlKCk7XG4gICAgICB2YXIgdHJhbnNjZWl2ZXJzID0gW107XG4gICAgICB0cmFja3MuZm9yRWFjaChmdW5jdGlvbihtbGluZSwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAvLyBGb3IgZWFjaCB0cmFjaywgY3JlYXRlIGFuIGljZSBnYXRoZXJlciwgaWNlIHRyYW5zcG9ydCxcbiAgICAgICAgLy8gZHRscyB0cmFuc3BvcnQsIHBvdGVudGlhbGx5IHJ0cHNlbmRlciBhbmQgcnRwcmVjZWl2ZXIuXG4gICAgICAgIHZhciB0cmFjayA9IG1saW5lLnRyYWNrO1xuICAgICAgICB2YXIga2luZCA9IG1saW5lLmtpbmQ7XG4gICAgICAgIHZhciBtaWQgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcblxuICAgICAgICB2YXIgdHJhbnNwb3J0cyA9IHNlbGYudXNpbmdCdW5kbGUgJiYgc2RwTUxpbmVJbmRleCA+IDAgPyB7XG4gICAgICAgICAgaWNlR2F0aGVyZXI6IHRyYW5zY2VpdmVyc1swXS5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zY2VpdmVyc1swXS5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNjZWl2ZXJzWzBdLmR0bHNUcmFuc3BvcnRcbiAgICAgICAgfSA6IHNlbGYuX2NyZWF0ZUljZUFuZER0bHNUcmFuc3BvcnRzKG1pZCwgc2RwTUxpbmVJbmRleCk7XG5cbiAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzID0gUlRDUnRwU2VuZGVyLmdldENhcGFiaWxpdGllcyhraW5kKTtcbiAgICAgICAgLy8gZmlsdGVyIFJUWCB1bnRpbCBhZGRpdGlvbmFsIHN0dWZmIG5lZWRlZCBmb3IgUlRYIGlzIGltcGxlbWVudGVkXG4gICAgICAgIC8vIGluIGFkYXB0ZXIuanNcbiAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzID0gbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzLmZpbHRlcihcbiAgICAgICAgICAgIGZ1bmN0aW9uKGNvZGVjKSB7XG4gICAgICAgICAgICAgIHJldHVybiBjb2RlYy5uYW1lICE9PSAncnR4JztcbiAgICAgICAgICAgIH0pO1xuICAgICAgICBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MuZm9yRWFjaChmdW5jdGlvbihjb2RlYykge1xuICAgICAgICAgIC8vIHdvcmsgYXJvdW5kIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD02NTUyXG4gICAgICAgICAgLy8gYnkgYWRkaW5nIGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTFcbiAgICAgICAgICBpZiAoY29kZWMubmFtZSA9PT0gJ0gyNjQnICYmXG4gICAgICAgICAgICAgIGNvZGVjLnBhcmFtZXRlcnNbJ2xldmVsLWFzeW1tZXRyeS1hbGxvd2VkJ10gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgY29kZWMucGFyYW1ldGVyc1snbGV2ZWwtYXN5bW1ldHJ5LWFsbG93ZWQnXSA9ICcxJztcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHZhciBydHBTZW5kZXI7XG4gICAgICAgIHZhciBydHBSZWNlaXZlcjtcblxuICAgICAgICAvLyBnZW5lcmF0ZSBhbiBzc3JjIG5vdywgdG8gYmUgdXNlZCBsYXRlciBpbiBydHBTZW5kZXIuc2VuZFxuICAgICAgICB2YXIgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IFt7XG4gICAgICAgICAgc3NyYzogKDIgKiBzZHBNTGluZUluZGV4ICsgMSkgKiAxMDAxXG4gICAgICAgIH1dO1xuICAgICAgICBpZiAodHJhY2spIHtcbiAgICAgICAgICBydHBTZW5kZXIgPSBuZXcgUlRDUnRwU2VuZGVyKHRyYWNrLCB0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG1saW5lLndhbnRSZWNlaXZlKSB7XG4gICAgICAgICAgcnRwUmVjZWl2ZXIgPSBuZXcgUlRDUnRwUmVjZWl2ZXIodHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LCBraW5kKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICBpY2VHYXRoZXJlcjogdHJhbnNwb3J0cy5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuaWNlVHJhbnNwb3J0LFxuICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCxcbiAgICAgICAgICBsb2NhbENhcGFiaWxpdGllczogbG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzOiBudWxsLFxuICAgICAgICAgIHJ0cFNlbmRlcjogcnRwU2VuZGVyLFxuICAgICAgICAgIHJ0cFJlY2VpdmVyOiBydHBSZWNlaXZlcixcbiAgICAgICAgICBraW5kOiBraW5kLFxuICAgICAgICAgIG1pZDogbWlkLFxuICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnM6IHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMsXG4gICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVyczogbnVsbFxuICAgICAgICB9O1xuICAgICAgfSk7XG4gICAgICBpZiAodGhpcy51c2luZ0J1bmRsZSkge1xuICAgICAgICBzZHAgKz0gJ2E9Z3JvdXA6QlVORExFICcgKyB0cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdHJhY2tzLmZvckVhY2goZnVuY3Rpb24obWxpbmUsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgdmFyIHRyYW5zY2VpdmVyID0gdHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24odHJhbnNjZWl2ZXIsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcywgJ29mZmVyJywgc2VsZi5sb2NhbFN0cmVhbXNbMF0pO1xuICAgICAgfSk7XG5cbiAgICAgIHRoaXMuX3BlbmRpbmdPZmZlciA9IHRyYW5zY2VpdmVycztcbiAgICAgIHZhciBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICdvZmZlcicsXG4gICAgICAgIHNkcDogc2RwXG4gICAgICB9KTtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoYXJndW1lbnRzWzBdLCAwLCBkZXNjKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGVzYyk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlQW5zd2VyID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAgIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSgpO1xuICAgICAgaWYgKHRoaXMudXNpbmdCdW5kbGUpIHtcbiAgICAgICAgc2RwICs9ICdhPWdyb3VwOkJVTkRMRSAnICsgdGhpcy50cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaXNEYXRhY2hhbm5lbCkge1xuICAgICAgICAgIHNkcCArPSAnbT1hcHBsaWNhdGlvbiAwIERUTFMvU0NUUCA1MDAwXFxyXFxuJyArXG4gICAgICAgICAgICAgICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJyArXG4gICAgICAgICAgICAgICdhPW1pZDonICsgdHJhbnNjZWl2ZXIubWlkICsgJ1xcclxcbic7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIENhbGN1bGF0ZSBpbnRlcnNlY3Rpb24gb2YgY2FwYWJpbGl0aWVzLlxuICAgICAgICB2YXIgY29tbW9uQ2FwYWJpbGl0aWVzID0gc2VsZi5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKFxuICAgICAgICAgICAgdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXMpO1xuXG4gICAgICAgIHNkcCArPSBTRFBVdGlscy53cml0ZU1lZGlhU2VjdGlvbih0cmFuc2NlaXZlciwgY29tbW9uQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgICAgJ2Fuc3dlcicsIHNlbGYubG9jYWxTdHJlYW1zWzBdKTtcbiAgICAgIH0pO1xuXG4gICAgICB2YXIgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiAnYW5zd2VyJyxcbiAgICAgICAgc2RwOiBzZHBcbiAgICAgIH0pO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggJiYgdHlwZW9mIGFyZ3VtZW50c1swXSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChhcmd1bWVudHNbMF0sIDAsIGRlc2MpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShkZXNjKTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgICAgIGlmICghY2FuZGlkYXRlKSB7XG4gICAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKHt9KTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgbUxpbmVJbmRleCA9IGNhbmRpZGF0ZS5zZHBNTGluZUluZGV4O1xuICAgICAgICBpZiAoY2FuZGlkYXRlLnNkcE1pZCkge1xuICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy50cmFuc2NlaXZlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnRyYW5zY2VpdmVyc1tpXS5taWQgPT09IGNhbmRpZGF0ZS5zZHBNaWQpIHtcbiAgICAgICAgICAgICAgbUxpbmVJbmRleCA9IGk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSB0aGlzLnRyYW5zY2VpdmVyc1ttTGluZUluZGV4XTtcbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgdmFyIGNhbmQgPSBPYmplY3Qua2V5cyhjYW5kaWRhdGUuY2FuZGlkYXRlKS5sZW5ndGggPiAwID9cbiAgICAgICAgICAgICAgU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUoY2FuZGlkYXRlLmNhbmRpZGF0ZSkgOiB7fTtcbiAgICAgICAgICAvLyBJZ25vcmUgQ2hyb21lJ3MgaW52YWxpZCBjYW5kaWRhdGVzIHNpbmNlIEVkZ2UgZG9lcyBub3QgbGlrZSB0aGVtLlxuICAgICAgICAgIGlmIChjYW5kLnByb3RvY29sID09PSAndGNwJyAmJiAoY2FuZC5wb3J0ID09PSAwIHx8IGNhbmQucG9ydCA9PT0gOSkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gSWdub3JlIFJUQ1AgY2FuZGlkYXRlcywgd2UgYXNzdW1lIFJUQ1AtTVVYLlxuICAgICAgICAgIGlmIChjYW5kLmNvbXBvbmVudCAhPT0gJzEnKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIEEgZGlydHkgaGFjayB0byBtYWtlIHNhbXBsZXMgd29yay5cbiAgICAgICAgICBpZiAoY2FuZC50eXBlID09PSAnZW5kT2ZDYW5kaWRhdGVzJykge1xuICAgICAgICAgICAgY2FuZCA9IHt9O1xuICAgICAgICAgIH1cbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKGNhbmQpO1xuXG4gICAgICAgICAgLy8gdXBkYXRlIHRoZSByZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICB2YXIgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICBzZWN0aW9uc1ttTGluZUluZGV4ICsgMV0gKz0gKGNhbmQudHlwZSA/IGNhbmRpZGF0ZS5jYW5kaWRhdGUudHJpbSgpXG4gICAgICAgICAgICAgIDogJ2E9ZW5kLW9mLWNhbmRpZGF0ZXMnKSArICdcXHJcXG4nO1xuICAgICAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSAmJiB0eXBlb2YgYXJndW1lbnRzWzFdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0U3RhdHMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBwcm9taXNlcyA9IFtdO1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBbJ3J0cFNlbmRlcicsICdydHBSZWNlaXZlcicsICdpY2VHYXRoZXJlcicsICdpY2VUcmFuc3BvcnQnLFxuICAgICAgICAgICAgJ2R0bHNUcmFuc3BvcnQnXS5mb3JFYWNoKGZ1bmN0aW9uKG1ldGhvZCkge1xuICAgICAgICAgICAgICBpZiAodHJhbnNjZWl2ZXJbbWV0aG9kXSkge1xuICAgICAgICAgICAgICAgIHByb21pc2VzLnB1c2godHJhbnNjZWl2ZXJbbWV0aG9kXS5nZXRTdGF0cygpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICAgIHZhciBjYiA9IGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicgJiZcbiAgICAgICAgICBhcmd1bWVudHNbMV07XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSkge1xuICAgICAgICAvLyBzaGltIGdldFN0YXRzIHdpdGggbWFwbGlrZSBzdXBwb3J0XG4gICAgICAgIHZhciByZXN1bHRzID0gbmV3IE1hcCgpO1xuICAgICAgICBQcm9taXNlLmFsbChwcm9taXNlcykudGhlbihmdW5jdGlvbihyZXMpIHtcbiAgICAgICAgICByZXMuZm9yRWFjaChmdW5jdGlvbihyZXN1bHQpIHtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHJlc3VsdCkuZm9yRWFjaChmdW5jdGlvbihpZCkge1xuICAgICAgICAgICAgICByZXN1bHRzLnNldChpZCwgcmVzdWx0W2lkXSk7XG4gICAgICAgICAgICAgIHJlc3VsdHNbaWRdID0gcmVzdWx0W2lkXTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIGlmIChjYikge1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoY2IsIDAsIHJlc3VsdHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXNvbHZlKHJlc3VsdHMpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH07XG4gIH1cbn07XG5cbi8vIEV4cG9zZSBwdWJsaWMgbWV0aG9kcy5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGVkZ2VTaGltLnNoaW1QZWVyQ29ubmVjdGlvbixcbiAgc2hpbUdldFVzZXJNZWRpYTogcmVxdWlyZSgnLi9nZXR1c2VybWVkaWEnKVxufTtcblxuXG5cbi8vLy8vLy8vLy8vLy8vLy8vL1xuLy8gV0VCUEFDSyBGT09URVJcbi8vIC4vfi93ZWJydGMtYWRhcHRlci9zcmMvanMvZWRnZS9lZGdlX3NoaW0uanNcbi8vIG1vZHVsZSBpZCA9IDQ2M1xuLy8gbW9kdWxlIGNodW5rcyA9IDAiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9");

FIXME found
Open

    eval("/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar SDPUtils = __webpack_require__(452);\nvar logging = __webpack_require__(479).log;\n\nvar edgeShim = {\n  shimPeerConnection: function() {\n    if (window.RTCIceGatherer) {\n      // ORTC defines an RTCIceCandidate object but no constructor.\n      // Not implemented in Edge.\n      if (!window.RTCIceCandidate) {\n        window.RTCIceCandidate = function(args) {\n          return args;\n        };\n      }\n      // ORTC does not have a session description object but\n      // other browsers (i.e. Chrome) that will support both PC and ORTC\n      // in the future might have this defined already.\n      if (!window.RTCSessionDescription) {\n        window.RTCSessionDescription = function(args) {\n          return args;\n        };\n      }\n    }\n\n    window.RTCPeerConnection = function(config) {\n      var self = this;\n\n      var _eventTarget = document.createDocumentFragment();\n      ['addEventListener', 'removeEventListener', 'dispatchEvent']\n          .forEach(function(method) {\n            self[method] = _eventTarget[method].bind(_eventTarget);\n          });\n\n      this.onicecandidate = null;\n      this.onaddstream = null;\n      this.ontrack = null;\n      this.onremovestream = null;\n      this.onsignalingstatechange = null;\n      this.oniceconnectionstatechange = null;\n      this.onnegotiationneeded = null;\n      this.ondatachannel = null;\n\n      this.localStreams = [];\n      this.remoteStreams = [];\n      this.getLocalStreams = function() {\n        return self.localStreams;\n      };\n      this.getRemoteStreams = function() {\n        return self.remoteStreams;\n      };\n\n      this.localDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.remoteDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.signalingState = 'stable';\n      this.iceConnectionState = 'new';\n      this.iceGatheringState = 'new';\n\n      this.iceOptions = {\n        gatherPolicy: 'all',\n        iceServers: []\n      };\n      if (config && config.iceTransportPolicy) {\n        switch (config.iceTransportPolicy) {\n          case 'all':\n          case 'relay':\n            this.iceOptions.gatherPolicy = config.iceTransportPolicy;\n            break;\n          case 'none':\n            // FIXME: remove once implementation and spec have added this.\n            throw new TypeError('iceTransportPolicy \"none\" not supported');\n          default:\n            // don't set iceTransportPolicy.\n            break;\n        }\n      }\n      this.usingBundle = config && config.bundlePolicy === 'max-bundle';\n\n      if (config && config.iceServers) {\n        // Edge does not like\n        // 1) stun:\n        // 2) turn: that does not have all of turn:host:port?transport=udp\n        var iceServers = JSON.parse(JSON.stringify(config.iceServers));\n        this.iceOptions.iceServers = iceServers.filter(function(server) {\n          if (server && server.urls) {\n            var urls = server.urls;\n            if (typeof urls === 'string') {\n              urls = [urls];\n            }\n            urls = urls.filter(function(url) {\n              return url.indexOf('turn:') === 0 &&\n                  url.indexOf('transport=udp') !== -1;\n            })[0];\n            return !!urls;\n          }\n          return false;\n        });\n      }\n\n      // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n      // everything that is needed to describe a SDP m-line.\n      this.transceivers = [];\n\n      // since the iceGatherer is currently created in createOffer but we\n      // must not emit candidates until after setLocalDescription we buffer\n      // them in this array.\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {\n      var self = this;\n      var sections = SDPUtils.splitSections(self.localDescription.sdp);\n      // FIXME: need to apply ice candidates in a way which is async but\n      // in-order\n      this._localIceCandidatesBuffer.forEach(function(event) {\n        var end = !event.candidate || Object.keys(event.candidate).length === 0;\n        if (end) {\n          for (var j = 1; j < sections.length; j++) {\n            if (sections[j].indexOf('\\r\\na=end-of-candidates\\r\\n') === -1) {\n              sections[j] += 'a=end-of-candidates\\r\\n';\n            }\n          }\n        } else if (event.candidate.candidate.indexOf('typ endOfCandidates')\n            === -1) {\n          sections[event.candidate.sdpMLineIndex + 1] +=\n              'a=' + event.candidate.candidate + '\\r\\n';\n        }\n        self.localDescription.sdp = sections.join('');\n        self.dispatchEvent(event);\n        if (self.onicecandidate !== null) {\n          self.onicecandidate(event);\n        }\n        if (!event.candidate && self.iceGatheringState !== 'complete') {\n          var complete = self.transceivers.every(function(transceiver) {\n            return transceiver.iceGatherer &&\n                transceiver.iceGatherer.state === 'completed';\n          });\n          if (complete) {\n            self.iceGatheringState = 'complete';\n          }\n        }\n      });\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype.addStream = function(stream) {\n      // Clone is necessary for local demos mostly, attaching directly\n      // to two different senders does not work (build 10547).\n      this.localStreams.push(stream.clone());\n      this._maybeFireNegotiationNeeded();\n    };\n\n    window.RTCPeerConnection.prototype.removeStream = function(stream) {\n      var idx = this.localStreams.indexOf(stream);\n      if (idx > -1) {\n        this.localStreams.splice(idx, 1);\n        this._maybeFireNegotiationNeeded();\n      }\n    };\n\n    window.RTCPeerConnection.prototype.getSenders = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpSender;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpSender;\n      });\n    };\n\n    window.RTCPeerConnection.prototype.getReceivers = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpReceiver;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpReceiver;\n      });\n    };\n\n    // Determines the intersection of local and remote capabilities.\n    window.RTCPeerConnection.prototype._getCommonCapabilities =\n        function(localCapabilities, remoteCapabilities) {\n          var commonCapabilities = {\n            codecs: [],\n            headerExtensions: [],\n            fecMechanisms: []\n          };\n          localCapabilities.codecs.forEach(function(lCodec) {\n            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n              var rCodec = remoteCapabilities.codecs[i];\n              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n                  lCodec.clockRate === rCodec.clockRate &&\n                  lCodec.numChannels === rCodec.numChannels) {\n                // push rCodec so we reply with offerer payload type\n                commonCapabilities.codecs.push(rCodec);\n\n                // FIXME: also need to determine intersection between\n                // .rtcpFeedback and .parameters\n                break;\n              }\n            }\n          });\n\n          localCapabilities.headerExtensions\n              .forEach(function(lHeaderExtension) {\n                for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n                     i++) {\n                  var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n                  if (lHeaderExtension.uri === rHeaderExtension.uri) {\n                    commonCapabilities.headerExtensions.push(rHeaderExtension);\n                    break;\n                  }\n                }\n              });\n\n          // FIXME: fecMechanisms\n          return commonCapabilities;\n        };\n\n    // Create ICE gatherer, ICE transport and DTLS transport.\n    window.RTCPeerConnection.prototype._createIceAndDtlsTransports =\n        function(mid, sdpMLineIndex) {\n          var self = this;\n          var iceGatherer = new RTCIceGatherer(self.iceOptions);\n          var iceTransport = new RTCIceTransport(iceGatherer);\n          iceGatherer.onlocalcandidate = function(evt) {\n            var event = new Event('icecandidate');\n            event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n            var cand = evt.candidate;\n            var end = !cand || Object.keys(cand).length === 0;\n            // Edge emits an empty object for RTCIceCandidateComplete‥\n            if (end) {\n              // polyfill since RTCIceGatherer.state is not implemented in\n              // Edge 10547 yet.\n              if (iceGatherer.state === undefined) {\n                iceGatherer.state = 'completed';\n              }\n\n              // Emit a candidate with type endOfCandidates to make the samples\n              // work. Edge requires addIceCandidate with this empty candidate\n              // to start checking. The real solution is to signal\n              // end-of-candidates to the other side when getting the null\n              // candidate but some apps (like the samples) don't do that.\n              event.candidate.candidate =\n                  'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';\n            } else {\n              // RTCIceCandidate doesn't have a component, needs to be added\n              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;\n              event.candidate.candidate = SDPUtils.writeCandidate(cand);\n            }\n\n            // update local description.\n            var sections = SDPUtils.splitSections(self.localDescription.sdp);\n            if (event.candidate.candidate.indexOf('typ endOfCandidates')\n                === -1) {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=' + event.candidate.candidate + '\\r\\n';\n            } else {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=end-of-candidates\\r\\n';\n            }\n            self.localDescription.sdp = sections.join('');\n\n            var complete = self.transceivers.every(function(transceiver) {\n              return transceiver.iceGatherer &&\n                  transceiver.iceGatherer.state === 'completed';\n            });\n\n            // Emit candidate if localDescription is set.\n            // Also emits null candidate when all gatherers are complete.\n            switch (self.iceGatheringState) {\n              case 'new':\n                self._localIceCandidatesBuffer.push(event);\n                if (end && complete) {\n                  self._localIceCandidatesBuffer.push(\n                      new Event('icecandidate'));\n                }\n                break;\n              case 'gathering':\n                self._emitBufferedCandidates();\n                self.dispatchEvent(event);\n                if (self.onicecandidate !== null) {\n                  self.onicecandidate(event);\n                }\n                if (complete) {\n                  self.dispatchEvent(new Event('icecandidate'));\n                  if (self.onicecandidate !== null) {\n                    self.onicecandidate(new Event('icecandidate'));\n                  }\n                  self.iceGatheringState = 'complete';\n                }\n                break;\n              case 'complete':\n                // should not happen... currently!\n                break;\n              default: // no-op.\n                break;\n            }\n          };\n          iceTransport.onicestatechange = function() {\n            self._updateConnectionState();\n          };\n\n          var dtlsTransport = new RTCDtlsTransport(iceTransport);\n          dtlsTransport.ondtlsstatechange = function() {\n            self._updateConnectionState();\n          };\n          dtlsTransport.onerror = function() {\n            // onerror does not set state to failed by itself.\n            dtlsTransport.state = 'failed';\n            self._updateConnectionState();\n          };\n\n          return {\n            iceGatherer: iceGatherer,\n            iceTransport: iceTransport,\n            dtlsTransport: dtlsTransport\n          };\n        };\n\n    // Start the RTP Sender and Receiver for a transceiver.\n    window.RTCPeerConnection.prototype._transceive = function(transceiver,\n        send, recv) {\n      var params = this._getCommonCapabilities(transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n      if (send && transceiver.rtpSender) {\n        params.encodings = transceiver.sendEncodingParameters;\n        params.rtcp = {\n          cname: SDPUtils.localCName\n        };\n        if (transceiver.recvEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpSender.send(params);\n      }\n      if (recv && transceiver.rtpReceiver) {\n        params.encodings = transceiver.recvEncodingParameters;\n        params.rtcp = {\n          cname: transceiver.cname\n        };\n        if (transceiver.sendEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpReceiver.receive(params);\n      }\n    };\n\n    window.RTCPeerConnection.prototype.setLocalDescription =\n        function(description) {\n          var self = this;\n          var sections;\n          var sessionpart;\n          if (description.type === 'offer') {\n            // FIXME: What was the purpose of this empty if statement?\n            // if (!this._pendingOffer) {\n            // } else {\n            if (this._pendingOffer) {\n              // VERY limited support for SDP munging. Limited to:\n              // * changing the order of codecs\n              sections = SDPUtils.splitSections(description.sdp);\n              sessionpart = sections.shift();\n              sections.forEach(function(mediaSection, sdpMLineIndex) {\n                var caps = SDPUtils.parseRtpParameters(mediaSection);\n                self._pendingOffer[sdpMLineIndex].localCapabilities = caps;\n              });\n              this.transceivers = this._pendingOffer;\n              delete this._pendingOffer;\n            }\n          } else if (description.type === 'answer') {\n            sections = SDPUtils.splitSections(self.remoteDescription.sdp);\n            sessionpart = sections.shift();\n            var isIceLite = SDPUtils.matchPrefix(sessionpart,\n                'a=ice-lite').length > 0;\n            sections.forEach(function(mediaSection, sdpMLineIndex) {\n              var transceiver = self.transceivers[sdpMLineIndex];\n              var iceGatherer = transceiver.iceGatherer;\n              var iceTransport = transceiver.iceTransport;\n              var dtlsTransport = transceiver.dtlsTransport;\n              var localCapabilities = transceiver.localCapabilities;\n              var remoteCapabilities = transceiver.remoteCapabilities;\n              var rejected = mediaSection.split('\\n', 1)[0]\n                  .split(' ', 2)[1] === '0';\n\n              if (!rejected) {\n                var remoteIceParameters = SDPUtils.getIceParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                  .map(function(cand) {\n                    return SDPUtils.parseCandidate(cand);\n                  })\n                  .filter(function(cand) {\n                    return cand.component === '1';\n                  });\n                  // ice-lite only includes host candidates in the SDP so we can\n                  // use setRemoteCandidates (which implies an\n                  // RTCIceCandidateComplete)\n                  if (cands.length) {\n                    iceTransport.setRemoteCandidates(cands);\n                  }\n                }\n                var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  remoteDtlsParameters.role = 'server';\n                }\n\n                if (!self.usingBundle || sdpMLineIndex === 0) {\n                  iceTransport.start(iceGatherer, remoteIceParameters,\n                      isIceLite ? 'controlling' : 'controlled');\n                  dtlsTransport.start(remoteDtlsParameters);\n                }\n\n                // Calculate intersection of capabilities.\n                var params = self._getCommonCapabilities(localCapabilities,\n                    remoteCapabilities);\n\n                // Start the RTCRtpSender. The RTCRtpReceiver for this\n                // transceiver has already been started in setRemoteDescription.\n                self._transceive(transceiver,\n                    params.codecs.length > 0,\n                    false);\n              }\n            });\n          }\n\n          this.localDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-local-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n\n          // If a success callback was provided, emit ICE candidates after it\n          // has been executed. Otherwise, emit callback after the Promise is\n          // resolved.\n          var hasCallback = arguments.length > 1 &&\n            typeof arguments[1] === 'function';\n          if (hasCallback) {\n            var cb = arguments[1];\n            window.setTimeout(function() {\n              cb();\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              self._emitBufferedCandidates();\n            }, 0);\n          }\n          var p = Promise.resolve();\n          p.then(function() {\n            if (!hasCallback) {\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              // Usually candidates will be emitted earlier.\n              window.setTimeout(self._emitBufferedCandidates.bind(self), 500);\n            }\n          });\n          return p;\n        };\n\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n        function(description) {\n          var self = this;\n          var stream = new MediaStream();\n          var receiverList = [];\n          var sections = SDPUtils.splitSections(description.sdp);\n          var sessionpart = sections.shift();\n          var isIceLite = SDPUtils.matchPrefix(sessionpart,\n              'a=ice-lite').length > 0;\n          this.usingBundle = SDPUtils.matchPrefix(sessionpart,\n              'a=group:BUNDLE ').length > 0;\n          sections.forEach(function(mediaSection, sdpMLineIndex) {\n            var lines = SDPUtils.splitLines(mediaSection);\n            var mline = lines[0].substr(2).split(' ');\n            var kind = mline[0];\n            var rejected = mline[1] === '0';\n            var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n\n            var transceiver;\n            var iceGatherer;\n            var iceTransport;\n            var dtlsTransport;\n            var rtpSender;\n            var rtpReceiver;\n            var sendEncodingParameters;\n            var recvEncodingParameters;\n            var localCapabilities;\n\n            var track;\n            // FIXME: ensure the mediaSection has rtcp-mux set.\n            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n            var remoteIceParameters;\n            var remoteDtlsParameters;\n            if (!rejected) {\n              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters.role = 'client';\n            }\n            recvEncodingParameters =\n                SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');\n            if (mid.length) {\n              mid = mid[0].substr(6);\n            } else {\n              mid = SDPUtils.generateIdentifier();\n            }\n\n            var cname;\n            // Gets the first SSRC. Note that with RTX there might be multiple\n            // SSRCs.\n            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n                .map(function(line) {\n                  return SDPUtils.parseSsrcMedia(line);\n                })\n                .filter(function(obj) {\n                  return obj.attribute === 'cname';\n                })[0];\n            if (remoteSsrc) {\n              cname = remoteSsrc.value;\n            }\n\n            var isComplete = SDPUtils.matchPrefix(mediaSection,\n                'a=end-of-candidates').length > 0;\n            var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                .map(function(cand) {\n                  return SDPUtils.parseCandidate(cand);\n                })\n                .filter(function(cand) {\n                  return cand.component === '1';\n                });\n            if (description.type === 'offer' && !rejected) {\n              var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n                iceGatherer: self.transceivers[0].iceGatherer,\n                iceTransport: self.transceivers[0].iceTransport,\n                dtlsTransport: self.transceivers[0].dtlsTransport\n              } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n              if (isComplete) {\n                transports.iceTransport.setRemoteCandidates(cands);\n              }\n\n              localCapabilities = RTCRtpReceiver.getCapabilities(kind);\n              sendEncodingParameters = [{\n                ssrc: (2 * sdpMLineIndex + 2) * 1001\n              }];\n\n              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n\n              track = rtpReceiver.track;\n              receiverList.push([track, rtpReceiver]);\n              // FIXME: not correct when there are multiple streams but that is\n              // not currently supported in this shim.\n              stream.addTrack(track);\n\n              // FIXME: look at direction.\n              if (self.localStreams.length > 0 &&\n                  self.localStreams[0].getTracks().length >= sdpMLineIndex) {\n                // FIXME: actually more complicated, needs to match types etc\n                var localtrack = self.localStreams[0]\n                    .getTracks()[sdpMLineIndex];\n                rtpSender = new RTCRtpSender(localtrack,\n                    transports.dtlsTransport);\n              }\n\n              self.transceivers[sdpMLineIndex] = {\n                iceGatherer: transports.iceGatherer,\n                iceTransport: transports.iceTransport,\n                dtlsTransport: transports.dtlsTransport,\n                localCapabilities: localCapabilities,\n                remoteCapabilities: remoteCapabilities,\n                rtpSender: rtpSender,\n                rtpReceiver: rtpReceiver,\n                kind: kind,\n                mid: mid,\n                cname: cname,\n                sendEncodingParameters: sendEncodingParameters,\n                recvEncodingParameters: recvEncodingParameters\n              };\n              // Start the RTCRtpReceiver now. The RTPSender is started in\n              // setLocalDescription.\n              self._transceive(self.transceivers[sdpMLineIndex],\n                  false,\n                  direction === 'sendrecv' || direction === 'sendonly');\n            } else if (description.type === 'answer' && !rejected) {\n              transceiver = self.transceivers[sdpMLineIndex];\n              iceGatherer = transceiver.iceGatherer;\n              iceTransport = transceiver.iceTransport;\n              dtlsTransport = transceiver.dtlsTransport;\n              rtpSender = transceiver.rtpSender;\n              rtpReceiver = transceiver.rtpReceiver;\n              sendEncodingParameters = transceiver.sendEncodingParameters;\n              localCapabilities = transceiver.localCapabilities;\n\n              self.transceivers[sdpMLineIndex].recvEncodingParameters =\n                  recvEncodingParameters;\n              self.transceivers[sdpMLineIndex].remoteCapabilities =\n                  remoteCapabilities;\n              self.transceivers[sdpMLineIndex].cname = cname;\n\n              if ((isIceLite || isComplete) && cands.length) {\n                iceTransport.setRemoteCandidates(cands);\n              }\n              if (!self.usingBundle || sdpMLineIndex === 0) {\n                iceTransport.start(iceGatherer, remoteIceParameters,\n                    'controlling');\n                dtlsTransport.start(remoteDtlsParameters);\n              }\n\n              self._transceive(transceiver,\n                  direction === 'sendrecv' || direction === 'recvonly',\n                  direction === 'sendrecv' || direction === 'sendonly');\n\n              if (rtpReceiver &&\n                  (direction === 'sendrecv' || direction === 'sendonly')) {\n                track = rtpReceiver.track;\n                receiverList.push([track, rtpReceiver]);\n                stream.addTrack(track);\n              } else {\n                // FIXME: actually the receiver should be created later.\n                delete transceiver.rtpReceiver;\n              }\n            }\n          });\n\n          this.remoteDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-remote-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n          if (stream.getTracks().length) {\n            self.remoteStreams.push(stream);\n            window.setTimeout(function() {\n              var event = new Event('addstream');\n              event.stream = stream;\n              self.dispatchEvent(event);\n              if (self.onaddstream !== null) {\n                window.setTimeout(function() {\n                  self.onaddstream(event);\n                }, 0);\n              }\n\n              receiverList.forEach(function(item) {\n                var track = item[0];\n                var receiver = item[1];\n                var trackEvent = new Event('track');\n                trackEvent.track = track;\n                trackEvent.receiver = receiver;\n                trackEvent.streams = [stream];\n                self.dispatchEvent(event);\n                if (self.ontrack !== null) {\n                  window.setTimeout(function() {\n                    self.ontrack(trackEvent);\n                  }, 0);\n                }\n              });\n            }, 0);\n          }\n          if (arguments.length > 1 && typeof arguments[1] === 'function') {\n            window.setTimeout(arguments[1], 0);\n          }\n          return Promise.resolve();\n        };\n\n    window.RTCPeerConnection.prototype.close = function() {\n      this.transceivers.forEach(function(transceiver) {\n        /* not yet\n        if (transceiver.iceGatherer) {\n          transceiver.iceGatherer.close();\n        }\n        */\n        if (transceiver.iceTransport) {\n          transceiver.iceTransport.stop();\n        }\n        if (transceiver.dtlsTransport) {\n          transceiver.dtlsTransport.stop();\n        }\n        if (transceiver.rtpSender) {\n          transceiver.rtpSender.stop();\n        }\n        if (transceiver.rtpReceiver) {\n          transceiver.rtpReceiver.stop();\n        }\n      });\n      // FIXME: clean up tracks, local streams, remote streams, etc\n      this._updateSignalingState('closed');\n    };\n\n    // Update the signaling state.\n    window.RTCPeerConnection.prototype._updateSignalingState =\n        function(newState) {\n          this.signalingState = newState;\n          var event = new Event('signalingstatechange');\n          this.dispatchEvent(event);\n          if (this.onsignalingstatechange !== null) {\n            this.onsignalingstatechange(event);\n          }\n        };\n\n    // Determine whether to fire the negotiationneeded event.\n    window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =\n        function() {\n          // Fire away (for now).\n          var event = new Event('negotiationneeded');\n          this.dispatchEvent(event);\n          if (this.onnegotiationneeded !== null) {\n            this.onnegotiationneeded(event);\n          }\n        };\n\n    // Update the connection state.\n    window.RTCPeerConnection.prototype._updateConnectionState = function() {\n      var self = this;\n      var newState;\n      var states = {\n        'new': 0,\n        closed: 0,\n        connecting: 0,\n        checking: 0,\n        connected: 0,\n        completed: 0,\n        failed: 0\n      };\n      this.transceivers.forEach(function(transceiver) {\n        states[transceiver.iceTransport.state]++;\n        states[transceiver.dtlsTransport.state]++;\n      });\n      // ICETransport.completed and connected are the same for this purpose.\n      states.connected += states.completed;\n\n      newState = 'new';\n      if (states.failed > 0) {\n        newState = 'failed';\n      } else if (states.connecting > 0 || states.checking > 0) {\n        newState = 'connecting';\n      } else if (states.disconnected > 0) {\n        newState = 'disconnected';\n      } else if (states.new > 0) {\n        newState = 'new';\n      } else if (states.connected > 0 || states.completed > 0) {\n        newState = 'connected';\n      }\n\n      if (newState !== self.iceConnectionState) {\n        self.iceConnectionState = newState;\n        var event = new Event('iceconnectionstatechange');\n        this.dispatchEvent(event);\n        if (this.oniceconnectionstatechange !== null) {\n          this.oniceconnectionstatechange(event);\n        }\n      }\n    };\n\n    window.RTCPeerConnection.prototype.createOffer = function() {\n      var self = this;\n      if (this._pendingOffer) {\n        throw new Error('createOffer called while there is a pending offer.');\n      }\n      var offerOptions;\n      if (arguments.length === 1 && typeof arguments[0] !== 'function') {\n        offerOptions = arguments[0];\n      } else if (arguments.length === 3) {\n        offerOptions = arguments[2];\n      }\n\n      var tracks = [];\n      var numAudioTracks = 0;\n      var numVideoTracks = 0;\n      // Default to sendrecv.\n      if (this.localStreams.length) {\n        numAudioTracks = this.localStreams[0].getAudioTracks().length;\n        numVideoTracks = this.localStreams[0].getVideoTracks().length;\n      }\n      // Determine number of audio and video tracks we need to send/recv.\n      if (offerOptions) {\n        // Reject Chrome legacy constraints.\n        if (offerOptions.mandatory || offerOptions.optional) {\n          throw new TypeError(\n              'Legacy mandatory/optional constraints not supported.');\n        }\n        if (offerOptions.offerToReceiveAudio !== undefined) {\n          numAudioTracks = offerOptions.offerToReceiveAudio;\n        }\n        if (offerOptions.offerToReceiveVideo !== undefined) {\n          numVideoTracks = offerOptions.offerToReceiveVideo;\n        }\n      }\n      if (this.localStreams.length) {\n        // Push local streams.\n        this.localStreams[0].getTracks().forEach(function(track) {\n          tracks.push({\n            kind: track.kind,\n            track: track,\n            wantReceive: track.kind === 'audio' ?\n                numAudioTracks > 0 : numVideoTracks > 0\n          });\n          if (track.kind === 'audio') {\n            numAudioTracks--;\n          } else if (track.kind === 'video') {\n            numVideoTracks--;\n          }\n        });\n      }\n      // Create M-lines for recvonly streams.\n      while (numAudioTracks > 0 || numVideoTracks > 0) {\n        if (numAudioTracks > 0) {\n          tracks.push({\n            kind: 'audio',\n            wantReceive: true\n          });\n          numAudioTracks--;\n        }\n        if (numVideoTracks > 0) {\n          tracks.push({\n            kind: 'video',\n            wantReceive: true\n          });\n          numVideoTracks--;\n        }\n      }\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      var transceivers = [];\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        // For each track, create an ice gatherer, ice transport,\n        // dtls transport, potentially rtpsender and rtpreceiver.\n        var track = mline.track;\n        var kind = mline.kind;\n        var mid = SDPUtils.generateIdentifier();\n\n        var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n          iceGatherer: transceivers[0].iceGatherer,\n          iceTransport: transceivers[0].iceTransport,\n          dtlsTransport: transceivers[0].dtlsTransport\n        } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n        var localCapabilities = RTCRtpSender.getCapabilities(kind);\n        var rtpSender;\n        var rtpReceiver;\n\n        // generate an ssrc now, to be used later in rtpSender.send\n        var sendEncodingParameters = [{\n          ssrc: (2 * sdpMLineIndex + 1) * 1001\n        }];\n        if (track) {\n          rtpSender = new RTCRtpSender(track, transports.dtlsTransport);\n        }\n\n        if (mline.wantReceive) {\n          rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n        }\n\n        transceivers[sdpMLineIndex] = {\n          iceGatherer: transports.iceGatherer,\n          iceTransport: transports.iceTransport,\n          dtlsTransport: transports.dtlsTransport,\n          localCapabilities: localCapabilities,\n          remoteCapabilities: null,\n          rtpSender: rtpSender,\n          rtpReceiver: rtpReceiver,\n          kind: kind,\n          mid: mid,\n          sendEncodingParameters: sendEncodingParameters,\n          recvEncodingParameters: null\n        };\n      });\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        var transceiver = transceivers[sdpMLineIndex];\n        sdp += SDPUtils.writeMediaSection(transceiver,\n            transceiver.localCapabilities, 'offer', self.localStreams[0]);\n      });\n\n      this._pendingOffer = transceivers;\n      var desc = new RTCSessionDescription({\n        type: 'offer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.createAnswer = function() {\n      var self = this;\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      this.transceivers.forEach(function(transceiver) {\n        // Calculate intersection of capabilities.\n        var commonCapabilities = self._getCommonCapabilities(\n            transceiver.localCapabilities,\n            transceiver.remoteCapabilities);\n\n        sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,\n            'answer', self.localStreams[0]);\n      });\n\n      var desc = new RTCSessionDescription({\n        type: 'answer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n      if (candidate === null) {\n        this.transceivers.forEach(function(transceiver) {\n          transceiver.iceTransport.addRemoteCandidate({});\n        });\n      } else {\n        var mLineIndex = candidate.sdpMLineIndex;\n        if (candidate.sdpMid) {\n          for (var i = 0; i < this.transceivers.length; i++) {\n            if (this.transceivers[i].mid === candidate.sdpMid) {\n              mLineIndex = i;\n              break;\n            }\n          }\n        }\n        var transceiver = this.transceivers[mLineIndex];\n        if (transceiver) {\n          var cand = Object.keys(candidate.candidate).length > 0 ?\n              SDPUtils.parseCandidate(candidate.candidate) : {};\n          // Ignore Chrome's invalid candidates since Edge does not like them.\n          if (cand.protocol === 'tcp' && cand.port === 0) {\n            return;\n          }\n          // Ignore RTCP candidates, we assume RTCP-MUX.\n          if (cand.component !== '1') {\n            return;\n          }\n          // A dirty hack to make samples work.\n          if (cand.type === 'endOfCandidates') {\n            cand = {};\n          }\n          transceiver.iceTransport.addRemoteCandidate(cand);\n\n          // update the remoteDescription.\n          var sections = SDPUtils.splitSections(this.remoteDescription.sdp);\n          sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()\n              : 'a=end-of-candidates') + '\\r\\n';\n          this.remoteDescription.sdp = sections.join('');\n        }\n      }\n      if (arguments.length > 1 && typeof arguments[1] === 'function') {\n        window.setTimeout(arguments[1], 0);\n      }\n      return Promise.resolve();\n    };\n\n    window.RTCPeerConnection.prototype.getStats = function() {\n      var promises = [];\n      this.transceivers.forEach(function(transceiver) {\n        ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n            'dtlsTransport'].forEach(function(method) {\n              if (transceiver[method]) {\n                promises.push(transceiver[method].getStats());\n              }\n            });\n      });\n      var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&\n          arguments[1];\n      return new Promise(function(resolve) {\n        // shim getStats with maplike support\n        var results = new Map();\n        Promise.all(promises).then(function(res) {\n          res.forEach(function(result) {\n            Object.keys(result).forEach(function(id) {\n              results.set(id, result[id]);\n              results[id] = result[id];\n            });\n          });\n          if (cb) {\n            window.setTimeout(cb, 0, results);\n          }\n          resolve(results);\n        });\n      });\n    };\n  },\n\n  // Attach a media stream to an element.\n  attachMediaStream: function(element, stream) {\n    logging('DEPRECATED, attachMediaStream will soon be removed.');\n    element.srcObject = stream;\n  },\n\n  reattachMediaStream: function(to, from) {\n    logging('DEPRECATED, reattachMediaStream will soon be removed.');\n    to.srcObject = from.srcObject;\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimPeerConnection: edgeShim.shimPeerConnection,\n  shimGetUserMedia: __webpack_require__(483),\n  attachMediaStream: edgeShim.attachMediaStream,\n  reattachMediaStream: edgeShim.reattachMediaStream\n};\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDgyLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy90cmFjZWFibGVwZWVyY29ubmVjdGlvbi9ub2RlX21vZHVsZXMvd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2VkZ2UvZWRnZV9zaGltLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiAgQ29weXJpZ2h0IChjKSAyMDE2IFRoZSBXZWJSVEMgcHJvamVjdCBhdXRob3JzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqICBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhIEJTRC1zdHlsZSBsaWNlbnNlXG4gKiAgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBvZiB0aGUgc291cmNlXG4gKiAgdHJlZS5cbiAqL1xuIC8qIGVzbGludC1lbnYgbm9kZSAqL1xuJ3VzZSBzdHJpY3QnO1xuXG52YXIgU0RQVXRpbHMgPSByZXF1aXJlKCdzZHAnKTtcbnZhciBsb2dnaW5nID0gcmVxdWlyZSgnLi4vdXRpbHMnKS5sb2c7XG5cbnZhciBlZGdlU2hpbSA9IHtcbiAgc2hpbVBlZXJDb25uZWN0aW9uOiBmdW5jdGlvbigpIHtcbiAgICBpZiAod2luZG93LlJUQ0ljZUdhdGhlcmVyKSB7XG4gICAgICAvLyBPUlRDIGRlZmluZXMgYW4gUlRDSWNlQ2FuZGlkYXRlIG9iamVjdCBidXQgbm8gY29uc3RydWN0b3IuXG4gICAgICAvLyBOb3QgaW1wbGVtZW50ZWQgaW4gRWRnZS5cbiAgICAgIGlmICghd2luZG93LlJUQ0ljZUNhbmRpZGF0ZSkge1xuICAgICAgICB3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlID0gZnVuY3Rpb24oYXJncykge1xuICAgICAgICAgIHJldHVybiBhcmdzO1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgLy8gT1JUQyBkb2VzIG5vdCBoYXZlIGEgc2Vzc2lvbiBkZXNjcmlwdGlvbiBvYmplY3QgYnV0XG4gICAgICAvLyBvdGhlciBicm93c2VycyAoaS5lLiBDaHJvbWUpIHRoYXQgd2lsbCBzdXBwb3J0IGJvdGggUEMgYW5kIE9SVENcbiAgICAgIC8vIGluIHRoZSBmdXR1cmUgbWlnaHQgaGF2ZSB0aGlzIGRlZmluZWQgYWxyZWFkeS5cbiAgICAgIGlmICghd2luZG93LlJUQ1Nlc3Npb25EZXNjcmlwdGlvbikge1xuICAgICAgICB3aW5kb3cuUlRDU2Vzc2lvbkRlc2NyaXB0aW9uID0gZnVuY3Rpb24oYXJncykge1xuICAgICAgICAgIHJldHVybiBhcmdzO1xuICAgICAgICB9O1xuICAgICAgfVxuICAgIH1cblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiA9IGZ1bmN0aW9uKGNvbmZpZykge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgICB2YXIgX2V2ZW50VGFyZ2V0ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xuICAgICAgWydhZGRFdmVudExpc3RlbmVyJywgJ3JlbW92ZUV2ZW50TGlzdGVuZXInLCAnZGlzcGF0Y2hFdmVudCddXG4gICAgICAgICAgLmZvckVhY2goZnVuY3Rpb24obWV0aG9kKSB7XG4gICAgICAgICAgICBzZWxmW21ldGhvZF0gPSBfZXZlbnRUYXJnZXRbbWV0aG9kXS5iaW5kKF9ldmVudFRhcmdldCk7XG4gICAgICAgICAgfSk7XG5cbiAgICAgIHRoaXMub25pY2VjYW5kaWRhdGUgPSBudWxsO1xuICAgICAgdGhpcy5vbmFkZHN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9udHJhY2sgPSBudWxsO1xuICAgICAgdGhpcy5vbnJlbW92ZXN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBudWxsO1xuICAgICAgdGhpcy5vbmRhdGFjaGFubmVsID0gbnVsbDtcblxuICAgICAgdGhpcy5sb2NhbFN0cmVhbXMgPSBbXTtcbiAgICAgIHRoaXMucmVtb3RlU3RyZWFtcyA9IFtdO1xuICAgICAgdGhpcy5nZXRMb2NhbFN0cmVhbXMgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYubG9jYWxTdHJlYW1zO1xuICAgICAgfTtcbiAgICAgIHRoaXMuZ2V0UmVtb3RlU3RyZWFtcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gc2VsZi5yZW1vdGVTdHJlYW1zO1xuICAgICAgfTtcblxuICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICcnLFxuICAgICAgICBzZHA6ICcnXG4gICAgICB9KTtcbiAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24gPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJycsXG4gICAgICAgIHNkcDogJydcbiAgICAgIH0pO1xuICAgICAgdGhpcy5zaWduYWxpbmdTdGF0ZSA9ICdzdGFibGUnO1xuICAgICAgdGhpcy5pY2VDb25uZWN0aW9uU3RhdGUgPSAnbmV3JztcbiAgICAgIHRoaXMuaWNlR2F0aGVyaW5nU3RhdGUgPSAnbmV3JztcblxuICAgICAgdGhpcy5pY2VPcHRpb25zID0ge1xuICAgICAgICBnYXRoZXJQb2xpY3k6ICdhbGwnLFxuICAgICAgICBpY2VTZXJ2ZXJzOiBbXVxuICAgICAgfTtcbiAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmljZVRyYW5zcG9ydFBvbGljeSkge1xuICAgICAgICBzd2l0Y2ggKGNvbmZpZy5pY2VUcmFuc3BvcnRQb2xpY3kpIHtcbiAgICAgICAgICBjYXNlICdhbGwnOlxuICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgIHRoaXMuaWNlT3B0aW9ucy5nYXRoZXJQb2xpY3kgPSBjb25maWcuaWNlVHJhbnNwb3J0UG9saWN5O1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnbm9uZSc6XG4gICAgICAgICAgICAvLyBGSVhNRTogcmVtb3ZlIG9uY2UgaW1wbGVtZW50YXRpb24gYW5kIHNwZWMgaGF2ZSBhZGRlZCB0aGlzLlxuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignaWNlVHJhbnNwb3J0UG9saWN5IFwibm9uZVwiIG5vdCBzdXBwb3J0ZWQnKTtcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgLy8gZG9uJ3Qgc2V0IGljZVRyYW5zcG9ydFBvbGljeS5cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLnVzaW5nQnVuZGxlID0gY29uZmlnICYmIGNvbmZpZy5idW5kbGVQb2xpY3kgPT09ICdtYXgtYnVuZGxlJztcblxuICAgICAgaWYgKGNvbmZpZyAmJiBjb25maWcuaWNlU2VydmVycykge1xuICAgICAgICAvLyBFZGdlIGRvZXMgbm90IGxpa2VcbiAgICAgICAgLy8gMSkgc3R1bjpcbiAgICAgICAgLy8gMikgdHVybjogdGhhdCBkb2VzIG5vdCBoYXZlIGFsbCBvZiB0dXJuOmhvc3Q6cG9ydD90cmFuc3BvcnQ9dWRwXG4gICAgICAgIHZhciBpY2VTZXJ2ZXJzID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShjb25maWcuaWNlU2VydmVycykpO1xuICAgICAgICB0aGlzLmljZU9wdGlvbnMuaWNlU2VydmVycyA9IGljZVNlcnZlcnMuZmlsdGVyKGZ1bmN0aW9uKHNlcnZlcikge1xuICAgICAgICAgIGlmIChzZXJ2ZXIgJiYgc2VydmVyLnVybHMpIHtcbiAgICAgICAgICAgIHZhciB1cmxzID0gc2VydmVyLnVybHM7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHVybHMgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgIHVybHMgPSBbdXJsc107XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB1cmxzID0gdXJscy5maWx0ZXIoZnVuY3Rpb24odXJsKSB7XG4gICAgICAgICAgICAgIHJldHVybiB1cmwuaW5kZXhPZigndHVybjonKSA9PT0gMCAmJlxuICAgICAgICAgICAgICAgICAgdXJsLmluZGV4T2YoJ3RyYW5zcG9ydD11ZHAnKSAhPT0gLTE7XG4gICAgICAgICAgICB9KVswXTtcbiAgICAgICAgICAgIHJldHVybiAhIXVybHM7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIC8vIHBlci10cmFjayBpY2VHYXRoZXJzLCBpY2VUcmFuc3BvcnRzLCBkdGxzVHJhbnNwb3J0cywgcnRwU2VuZGVycywgLi4uXG4gICAgICAvLyBldmVyeXRoaW5nIHRoYXQgaXMgbmVlZGVkIHRvIGRlc2NyaWJlIGEgU0RQIG0tbGluZS5cbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzID0gW107XG5cbiAgICAgIC8vIHNpbmNlIHRoZSBpY2VHYXRoZXJlciBpcyBjdXJyZW50bHkgY3JlYXRlZCBpbiBjcmVhdGVPZmZlciBidXQgd2VcbiAgICAgIC8vIG11c3Qgbm90IGVtaXQgY2FuZGlkYXRlcyB1bnRpbCBhZnRlciBzZXRMb2NhbERlc2NyaXB0aW9uIHdlIGJ1ZmZlclxuICAgICAgLy8gdGhlbSBpbiB0aGlzIGFycmF5LlxuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAvLyBGSVhNRTogbmVlZCB0byBhcHBseSBpY2UgY2FuZGlkYXRlcyBpbiBhIHdheSB3aGljaCBpcyBhc3luYyBidXRcbiAgICAgIC8vIGluLW9yZGVyXG4gICAgICB0aGlzLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIuZm9yRWFjaChmdW5jdGlvbihldmVudCkge1xuICAgICAgICB2YXIgZW5kID0gIWV2ZW50LmNhbmRpZGF0ZSB8fCBPYmplY3Qua2V5cyhldmVudC5jYW5kaWRhdGUpLmxlbmd0aCA9PT0gMDtcbiAgICAgICAgaWYgKGVuZCkge1xuICAgICAgICAgIGZvciAodmFyIGogPSAxOyBqIDwgc2VjdGlvbnMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChzZWN0aW9uc1tqXS5pbmRleE9mKCdcXHJcXG5hPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJykgPT09IC0xKSB7XG4gICAgICAgICAgICAgIHNlY3Rpb25zW2pdICs9ICdhPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZS5pbmRleE9mKCd0eXAgZW5kT2ZDYW5kaWRhdGVzJylcbiAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgIHNlY3Rpb25zW2V2ZW50LmNhbmRpZGF0ZS5zZHBNTGluZUluZGV4ICsgMV0gKz1cbiAgICAgICAgICAgICAgJ2E9JyArIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgKyAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgICAgICBzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgc2VsZi5vbmljZWNhbmRpZGF0ZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFldmVudC5jYW5kaWRhdGUgJiYgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSAhPT0gJ2NvbXBsZXRlJykge1xuICAgICAgICAgIHZhciBjb21wbGV0ZSA9IHNlbGYudHJhbnNjZWl2ZXJzLmV2ZXJ5KGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5zdGF0ZSA9PT0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKGNvbXBsZXRlKSB7XG4gICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkU3RyZWFtID0gZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICAvLyBDbG9uZSBpcyBuZWNlc3NhcnkgZm9yIGxvY2FsIGRlbW9zIG1vc3RseSwgYXR0YWNoaW5nIGRpcmVjdGx5XG4gICAgICAvLyB0byB0d28gZGlmZmVyZW50IHNlbmRlcnMgZG9lcyBub3Qgd29yayAoYnVpbGQgMTA1NDcpLlxuICAgICAgdGhpcy5sb2NhbFN0cmVhbXMucHVzaChzdHJlYW0uY2xvbmUoKSk7XG4gICAgICB0aGlzLl9tYXliZUZpcmVOZWdvdGlhdGlvbk5lZWRlZCgpO1xuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnJlbW92ZVN0cmVhbSA9IGZ1bmN0aW9uKHN0cmVhbSkge1xuICAgICAgdmFyIGlkeCA9IHRoaXMubG9jYWxTdHJlYW1zLmluZGV4T2Yoc3RyZWFtKTtcbiAgICAgIGlmIChpZHggPiAtMSkge1xuICAgICAgICB0aGlzLmxvY2FsU3RyZWFtcy5zcGxpY2UoaWR4LCAxKTtcbiAgICAgICAgdGhpcy5fbWF5YmVGaXJlTmVnb3RpYXRpb25OZWVkZWQoKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTZW5kZXJzID0gZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy50cmFuc2NlaXZlcnMuZmlsdGVyKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIHJldHVybiAhIXRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgIH0pXG4gICAgICAubWFwKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIHJldHVybiB0cmFuc2NlaXZlci5ydHBTZW5kZXI7XG4gICAgICB9KTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRSZWNlaXZlcnMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiB0aGlzLnRyYW5zY2VpdmVycy5maWx0ZXIoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuICEhdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXI7XG4gICAgICB9KVxuICAgICAgLm1hcChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXI7XG4gICAgICB9KTtcbiAgICB9O1xuXG4gICAgLy8gRGV0ZXJtaW5lcyB0aGUgaW50ZXJzZWN0aW9uIG9mIGxvY2FsIGFuZCByZW1vdGUgY2FwYWJpbGl0aWVzLlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2dldENvbW1vbkNhcGFiaWxpdGllcyA9XG4gICAgICAgIGZ1bmN0aW9uKGxvY2FsQ2FwYWJpbGl0aWVzLCByZW1vdGVDYXBhYmlsaXRpZXMpIHtcbiAgICAgICAgICB2YXIgY29tbW9uQ2FwYWJpbGl0aWVzID0ge1xuICAgICAgICAgICAgY29kZWNzOiBbXSxcbiAgICAgICAgICAgIGhlYWRlckV4dGVuc2lvbnM6IFtdLFxuICAgICAgICAgICAgZmVjTWVjaGFuaXNtczogW11cbiAgICAgICAgICB9O1xuICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzLmNvZGVjcy5mb3JFYWNoKGZ1bmN0aW9uKGxDb2RlYykge1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZW1vdGVDYXBhYmlsaXRpZXMuY29kZWNzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgIHZhciByQ29kZWMgPSByZW1vdGVDYXBhYmlsaXRpZXMuY29kZWNzW2ldO1xuICAgICAgICAgICAgICBpZiAobENvZGVjLm5hbWUudG9Mb3dlckNhc2UoKSA9PT0gckNvZGVjLm5hbWUudG9Mb3dlckNhc2UoKSAmJlxuICAgICAgICAgICAgICAgICAgbENvZGVjLmNsb2NrUmF0ZSA9PT0gckNvZGVjLmNsb2NrUmF0ZSAmJlxuICAgICAgICAgICAgICAgICAgbENvZGVjLm51bUNoYW5uZWxzID09PSByQ29kZWMubnVtQ2hhbm5lbHMpIHtcbiAgICAgICAgICAgICAgICAvLyBwdXNoIHJDb2RlYyBzbyB3ZSByZXBseSB3aXRoIG9mZmVyZXIgcGF5bG9hZCB0eXBlXG4gICAgICAgICAgICAgICAgY29tbW9uQ2FwYWJpbGl0aWVzLmNvZGVjcy5wdXNoKHJDb2RlYyk7XG5cbiAgICAgICAgICAgICAgICAvLyBGSVhNRTogYWxzbyBuZWVkIHRvIGRldGVybWluZSBpbnRlcnNlY3Rpb24gYmV0d2VlblxuICAgICAgICAgICAgICAgIC8vIC5ydGNwRmVlZGJhY2sgYW5kIC5wYXJhbWV0ZXJzXG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzLmhlYWRlckV4dGVuc2lvbnNcbiAgICAgICAgICAgICAgLmZvckVhY2goZnVuY3Rpb24obEhlYWRlckV4dGVuc2lvbikge1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmVtb3RlQ2FwYWJpbGl0aWVzLmhlYWRlckV4dGVuc2lvbnMubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICAgaSsrKSB7XG4gICAgICAgICAgICAgICAgICB2YXIgckhlYWRlckV4dGVuc2lvbiA9IHJlbW90ZUNhcGFiaWxpdGllcy5oZWFkZXJFeHRlbnNpb25zW2ldO1xuICAgICAgICAgICAgICAgICAgaWYgKGxIZWFkZXJFeHRlbnNpb24udXJpID09PSBySGVhZGVyRXh0ZW5zaW9uLnVyaSkge1xuICAgICAgICAgICAgICAgICAgICBjb21tb25DYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9ucy5wdXNoKHJIZWFkZXJFeHRlbnNpb24pO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgLy8gRklYTUU6IGZlY01lY2hhbmlzbXNcbiAgICAgICAgICByZXR1cm4gY29tbW9uQ2FwYWJpbGl0aWVzO1xuICAgICAgICB9O1xuXG4gICAgLy8gQ3JlYXRlIElDRSBnYXRoZXJlciwgSUNFIHRyYW5zcG9ydCBhbmQgRFRMUyB0cmFuc3BvcnQuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fY3JlYXRlSWNlQW5kRHRsc1RyYW5zcG9ydHMgPVxuICAgICAgICBmdW5jdGlvbihtaWQsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgdmFyIGljZUdhdGhlcmVyID0gbmV3IFJUQ0ljZUdhdGhlcmVyKHNlbGYuaWNlT3B0aW9ucyk7XG4gICAgICAgICAgdmFyIGljZVRyYW5zcG9ydCA9IG5ldyBSVENJY2VUcmFuc3BvcnQoaWNlR2F0aGVyZXIpO1xuICAgICAgICAgIGljZUdhdGhlcmVyLm9ubG9jYWxjYW5kaWRhdGUgPSBmdW5jdGlvbihldnQpIHtcbiAgICAgICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnaWNlY2FuZGlkYXRlJyk7XG4gICAgICAgICAgICBldmVudC5jYW5kaWRhdGUgPSB7c2RwTWlkOiBtaWQsIHNkcE1MaW5lSW5kZXg6IHNkcE1MaW5lSW5kZXh9O1xuXG4gICAgICAgICAgICB2YXIgY2FuZCA9IGV2dC5jYW5kaWRhdGU7XG4gICAgICAgICAgICB2YXIgZW5kID0gIWNhbmQgfHwgT2JqZWN0LmtleXMoY2FuZCkubGVuZ3RoID09PSAwO1xuICAgICAgICAgICAgLy8gRWRnZSBlbWl0cyBhbiBlbXB0eSBvYmplY3QgZm9yIFJUQ0ljZUNhbmRpZGF0ZUNvbXBsZXRl4oClXG4gICAgICAgICAgICBpZiAoZW5kKSB7XG4gICAgICAgICAgICAgIC8vIHBvbHlmaWxsIHNpbmNlIFJUQ0ljZUdhdGhlcmVyLnN0YXRlIGlzIG5vdCBpbXBsZW1lbnRlZCBpblxuICAgICAgICAgICAgICAvLyBFZGdlIDEwNTQ3IHlldC5cbiAgICAgICAgICAgICAgaWYgKGljZUdhdGhlcmVyLnN0YXRlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBpY2VHYXRoZXJlci5zdGF0ZSA9ICdjb21wbGV0ZWQnO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgLy8gRW1pdCBhIGNhbmRpZGF0ZSB3aXRoIHR5cGUgZW5kT2ZDYW5kaWRhdGVzIHRvIG1ha2UgdGhlIHNhbXBsZXNcbiAgICAgICAgICAgICAgLy8gd29yay4gRWRnZSByZXF1aXJlcyBhZGRJY2VDYW5kaWRhdGUgd2l0aCB0aGlzIGVtcHR5IGNhbmRpZGF0ZVxuICAgICAgICAgICAgICAvLyB0byBzdGFydCBjaGVja2luZy4gVGhlIHJlYWwgc29sdXRpb24gaXMgdG8gc2lnbmFsXG4gICAgICAgICAgICAgIC8vIGVuZC1vZi1jYW5kaWRhdGVzIHRvIHRoZSBvdGhlciBzaWRlIHdoZW4gZ2V0dGluZyB0aGUgbnVsbFxuICAgICAgICAgICAgICAvLyBjYW5kaWRhdGUgYnV0IHNvbWUgYXBwcyAobGlrZSB0aGUgc2FtcGxlcykgZG9uJ3QgZG8gdGhhdC5cbiAgICAgICAgICAgICAgZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZSA9XG4gICAgICAgICAgICAgICAgICAnY2FuZGlkYXRlOjEgMSB1ZHAgMSAwLjAuMC4wIDkgdHlwIGVuZE9mQ2FuZGlkYXRlcyc7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAvLyBSVENJY2VDYW5kaWRhdGUgZG9lc24ndCBoYXZlIGEgY29tcG9uZW50LCBuZWVkcyB0byBiZSBhZGRlZFxuICAgICAgICAgICAgICBjYW5kLmNvbXBvbmVudCA9IGljZVRyYW5zcG9ydC5jb21wb25lbnQgPT09ICdSVENQJyA/IDIgOiAxO1xuICAgICAgICAgICAgICBldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlID0gU0RQVXRpbHMud3JpdGVDYW5kaWRhdGUoY2FuZCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSBsb2NhbCBkZXNjcmlwdGlvbi5cbiAgICAgICAgICAgIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgICAgICBpZiAoZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZS5pbmRleE9mKCd0eXAgZW5kT2ZDYW5kaWRhdGVzJylcbiAgICAgICAgICAgICAgICA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgc2VjdGlvbnNbZXZlbnQuY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXggKyAxXSArPVxuICAgICAgICAgICAgICAgICAgJ2E9JyArIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHNlY3Rpb25zW2V2ZW50LmNhbmRpZGF0ZS5zZHBNTGluZUluZGV4ICsgMV0gKz1cbiAgICAgICAgICAgICAgICAgICdhPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlbGYubG9jYWxEZXNjcmlwdGlvbi5zZHAgPSBzZWN0aW9ucy5qb2luKCcnKTtcblxuICAgICAgICAgICAgdmFyIGNvbXBsZXRlID0gc2VsZi50cmFuc2NlaXZlcnMuZXZlcnkoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyICYmXG4gICAgICAgICAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5zdGF0ZSA9PT0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgLy8gRW1pdCBjYW5kaWRhdGUgaWYgbG9jYWxEZXNjcmlwdGlvbiBpcyBzZXQuXG4gICAgICAgICAgICAvLyBBbHNvIGVtaXRzIG51bGwgY2FuZGlkYXRlIHdoZW4gYWxsIGdhdGhlcmVycyBhcmUgY29tcGxldGUuXG4gICAgICAgICAgICBzd2l0Y2ggKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUpIHtcbiAgICAgICAgICAgICAgY2FzZSAnbmV3JzpcbiAgICAgICAgICAgICAgICBzZWxmLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIucHVzaChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKGVuZCAmJiBjb21wbGV0ZSkge1xuICAgICAgICAgICAgICAgICAgc2VsZi5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyLnB1c2goXG4gICAgICAgICAgICAgICAgICAgICAgbmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlICdnYXRoZXJpbmcnOlxuICAgICAgICAgICAgICAgIHNlbGYuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMoKTtcbiAgICAgICAgICAgICAgICBzZWxmLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgICAgICAgICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLm9uaWNlY2FuZGlkYXRlKGV2ZW50KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKSk7XG4gICAgICAgICAgICAgICAgICBpZiAoc2VsZi5vbmljZWNhbmRpZGF0ZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLm9uaWNlY2FuZGlkYXRlKG5ldyBFdmVudCgnaWNlY2FuZGlkYXRlJykpO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdjb21wbGV0ZSc7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlICdjb21wbGV0ZSc6XG4gICAgICAgICAgICAgICAgLy8gc2hvdWxkIG5vdCBoYXBwZW4uLi4gY3VycmVudGx5IVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBkZWZhdWx0OiAvLyBuby1vcC5cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9O1xuICAgICAgICAgIGljZVRyYW5zcG9ydC5vbmljZXN0YXRlY2hhbmdlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBzZWxmLl91cGRhdGVDb25uZWN0aW9uU3RhdGUoKTtcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgdmFyIGR0bHNUcmFuc3BvcnQgPSBuZXcgUlRDRHRsc1RyYW5zcG9ydChpY2VUcmFuc3BvcnQpO1xuICAgICAgICAgIGR0bHNUcmFuc3BvcnQub25kdGxzc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHNlbGYuX3VwZGF0ZUNvbm5lY3Rpb25TdGF0ZSgpO1xuICAgICAgICAgIH07XG4gICAgICAgICAgZHRsc1RyYW5zcG9ydC5vbmVycm9yID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAvLyBvbmVycm9yIGRvZXMgbm90IHNldCBzdGF0ZSB0byBmYWlsZWQgYnkgaXRzZWxmLlxuICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGF0ZSA9ICdmYWlsZWQnO1xuICAgICAgICAgICAgc2VsZi5fdXBkYXRlQ29ubmVjdGlvblN0YXRlKCk7XG4gICAgICAgICAgfTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBpY2VHYXRoZXJlcjogaWNlR2F0aGVyZXIsXG4gICAgICAgICAgICBpY2VUcmFuc3BvcnQ6IGljZVRyYW5zcG9ydCxcbiAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IGR0bHNUcmFuc3BvcnRcbiAgICAgICAgICB9O1xuICAgICAgICB9O1xuXG4gICAgLy8gU3RhcnQgdGhlIFJUUCBTZW5kZXIgYW5kIFJlY2VpdmVyIGZvciBhIHRyYW5zY2VpdmVyLlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX3RyYW5zY2VpdmUgPSBmdW5jdGlvbih0cmFuc2NlaXZlcixcbiAgICAgICAgc2VuZCwgcmVjdikge1xuICAgICAgdmFyIHBhcmFtcyA9IHRoaXMuX2dldENvbW1vbkNhcGFiaWxpdGllcyh0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXMpO1xuICAgICAgaWYgKHNlbmQgJiYgdHJhbnNjZWl2ZXIucnRwU2VuZGVyKSB7XG4gICAgICAgIHBhcmFtcy5lbmNvZGluZ3MgPSB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICBwYXJhbXMucnRjcCA9IHtcbiAgICAgICAgICBjbmFtZTogU0RQVXRpbHMubG9jYWxDTmFtZVxuICAgICAgICB9O1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGgpIHtcbiAgICAgICAgICBwYXJhbXMucnRjcC5zc3JjID0gdHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVyc1swXS5zc3JjO1xuICAgICAgICB9XG4gICAgICAgIHRyYW5zY2VpdmVyLnJ0cFNlbmRlci5zZW5kKHBhcmFtcyk7XG4gICAgICB9XG4gICAgICBpZiAocmVjdiAmJiB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcikge1xuICAgICAgICBwYXJhbXMuZW5jb2RpbmdzID0gdHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVycztcbiAgICAgICAgcGFyYW1zLnJ0Y3AgPSB7XG4gICAgICAgICAgY25hbWU6IHRyYW5zY2VpdmVyLmNuYW1lXG4gICAgICAgIH07XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzLmxlbmd0aCkge1xuICAgICAgICAgIHBhcmFtcy5ydGNwLnNzcmMgPSB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmM7XG4gICAgICAgIH1cbiAgICAgICAgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIucmVjZWl2ZShwYXJhbXMpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnNldExvY2FsRGVzY3JpcHRpb24gPVxuICAgICAgICBmdW5jdGlvbihkZXNjcmlwdGlvbikge1xuICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICB2YXIgc2VjdGlvbnM7XG4gICAgICAgICAgdmFyIHNlc3Npb25wYXJ0O1xuICAgICAgICAgIGlmIChkZXNjcmlwdGlvbi50eXBlID09PSAnb2ZmZXInKSB7XG4gICAgICAgICAgICAvLyBGSVhNRTogV2hhdCB3YXMgdGhlIHB1cnBvc2Ugb2YgdGhpcyBlbXB0eSBpZiBzdGF0ZW1lbnQ/XG4gICAgICAgICAgICAvLyBpZiAoIXRoaXMuX3BlbmRpbmdPZmZlcikge1xuICAgICAgICAgICAgLy8gfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICh0aGlzLl9wZW5kaW5nT2ZmZXIpIHtcbiAgICAgICAgICAgICAgLy8gVkVSWSBsaW1pdGVkIHN1cHBvcnQgZm9yIFNEUCBtdW5naW5nLiBMaW1pdGVkIHRvOlxuICAgICAgICAgICAgICAvLyAqIGNoYW5naW5nIHRoZSBvcmRlciBvZiBjb2RlY3NcbiAgICAgICAgICAgICAgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKGRlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgICAgICAgIHNlc3Npb25wYXJ0ID0gc2VjdGlvbnMuc2hpZnQoKTtcbiAgICAgICAgICAgICAgc2VjdGlvbnMuZm9yRWFjaChmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgICAgICAgICB2YXIgY2FwcyA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgICAgIHNlbGYuX3BlbmRpbmdPZmZlcltzZHBNTGluZUluZGV4XS5sb2NhbENhcGFiaWxpdGllcyA9IGNhcHM7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB0aGlzLnRyYW5zY2VpdmVycyA9IHRoaXMuX3BlbmRpbmdPZmZlcjtcbiAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuX3BlbmRpbmdPZmZlcjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKGRlc2NyaXB0aW9uLnR5cGUgPT09ICdhbnN3ZXInKSB7XG4gICAgICAgICAgICBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgc2Vzc2lvbnBhcnQgPSBzZWN0aW9ucy5zaGlmdCgpO1xuICAgICAgICAgICAgdmFyIGlzSWNlTGl0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAgICdhPWljZS1saXRlJykubGVuZ3RoID4gMDtcbiAgICAgICAgICAgIHNlY3Rpb25zLmZvckVhY2goZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZHBNTGluZUluZGV4KSB7XG4gICAgICAgICAgICAgIHZhciB0cmFuc2NlaXZlciA9IHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICAgICAgICB2YXIgaWNlR2F0aGVyZXIgPSB0cmFuc2NlaXZlci5pY2VHYXRoZXJlcjtcbiAgICAgICAgICAgICAgdmFyIGljZVRyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgICAgdmFyIGR0bHNUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5kdGxzVHJhbnNwb3J0O1xuICAgICAgICAgICAgICB2YXIgbG9jYWxDYXBhYmlsaXRpZXMgPSB0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcztcbiAgICAgICAgICAgICAgdmFyIHJlbW90ZUNhcGFiaWxpdGllcyA9IHRyYW5zY2VpdmVyLnJlbW90ZUNhcGFiaWxpdGllcztcbiAgICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWVkaWFTZWN0aW9uLnNwbGl0KCdcXG4nLCAxKVswXVxuICAgICAgICAgICAgICAgICAgLnNwbGl0KCcgJywgMilbMV0gPT09ICcwJztcblxuICAgICAgICAgICAgICBpZiAoIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKFxuICAgICAgICAgICAgICAgICAgICBtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgICBpZiAoaXNJY2VMaXRlKSB7XG4gICAgICAgICAgICAgICAgICB2YXIgY2FuZHMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWNhbmRpZGF0ZTonKVxuICAgICAgICAgICAgICAgICAgLm1hcChmdW5jdGlvbihjYW5kKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAuZmlsdGVyKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhbmQuY29tcG9uZW50ID09PSAnMSc7XG4gICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgIC8vIGljZS1saXRlIG9ubHkgaW5jbHVkZXMgaG9zdCBjYW5kaWRhdGVzIGluIHRoZSBTRFAgc28gd2UgY2FuXG4gICAgICAgICAgICAgICAgICAvLyB1c2Ugc2V0UmVtb3RlQ2FuZGlkYXRlcyAod2hpY2ggaW1wbGllcyBhblxuICAgICAgICAgICAgICAgICAgLy8gUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGUpXG4gICAgICAgICAgICAgICAgICBpZiAoY2FuZHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUR0bHNQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMoXG4gICAgICAgICAgICAgICAgICAgIG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpO1xuICAgICAgICAgICAgICAgIGlmIChpc0ljZUxpdGUpIHtcbiAgICAgICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnc2VydmVyJztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgaWNlVHJhbnNwb3J0LnN0YXJ0KGljZUdhdGhlcmVyLCByZW1vdGVJY2VQYXJhbWV0ZXJzLFxuICAgICAgICAgICAgICAgICAgICAgIGlzSWNlTGl0ZSA/ICdjb250cm9sbGluZycgOiAnY29udHJvbGxlZCcpO1xuICAgICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIGludGVyc2VjdGlvbiBvZiBjYXBhYmlsaXRpZXMuXG4gICAgICAgICAgICAgICAgdmFyIHBhcmFtcyA9IHNlbGYuX2dldENvbW1vbkNhcGFiaWxpdGllcyhsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzKTtcblxuICAgICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBTZW5kZXIuIFRoZSBSVENSdHBSZWNlaXZlciBmb3IgdGhpc1xuICAgICAgICAgICAgICAgIC8vIHRyYW5zY2VpdmVyIGhhcyBhbHJlYWR5IGJlZW4gc3RhcnRlZCBpbiBzZXRSZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgICBwYXJhbXMuY29kZWNzLmxlbmd0aCA+IDAsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0ge1xuICAgICAgICAgICAgdHlwZTogZGVzY3JpcHRpb24udHlwZSxcbiAgICAgICAgICAgIHNkcDogZGVzY3JpcHRpb24uc2RwXG4gICAgICAgICAgfTtcbiAgICAgICAgICBzd2l0Y2ggKGRlc2NyaXB0aW9uLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ29mZmVyJzpcbiAgICAgICAgICAgICAgdGhpcy5fdXBkYXRlU2lnbmFsaW5nU3RhdGUoJ2hhdmUtbG9jYWwtb2ZmZXInKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbnN3ZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnc3RhYmxlJyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigndW5zdXBwb3J0ZWQgdHlwZSBcIicgKyBkZXNjcmlwdGlvbi50eXBlICtcbiAgICAgICAgICAgICAgICAgICdcIicpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIElmIGEgc3VjY2VzcyBjYWxsYmFjayB3YXMgcHJvdmlkZWQsIGVtaXQgSUNFIGNhbmRpZGF0ZXMgYWZ0ZXIgaXRcbiAgICAgICAgICAvLyBoYXMgYmVlbiBleGVjdXRlZC4gT3RoZXJ3aXNlLCBlbWl0IGNhbGxiYWNrIGFmdGVyIHRoZSBQcm9taXNlIGlzXG4gICAgICAgICAgLy8gcmVzb2x2ZWQuXG4gICAgICAgICAgdmFyIGhhc0NhbGxiYWNrID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiZcbiAgICAgICAgICAgIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbic7XG4gICAgICAgICAgaWYgKGhhc0NhbGxiYWNrKSB7XG4gICAgICAgICAgICB2YXIgY2IgPSBhcmd1bWVudHNbMV07XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgY2IoKTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHNlbGYuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMoKTtcbiAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB2YXIgcCA9IFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgICAgIHAudGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmICghaGFzQ2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFVzdWFsbHkgY2FuZGlkYXRlcyB3aWxsIGJlIGVtaXR0ZWQgZWFybGllci5cbiAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcy5iaW5kKHNlbGYpLCA1MDApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybiBwO1xuICAgICAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9XG4gICAgICAgIGZ1bmN0aW9uKGRlc2NyaXB0aW9uKSB7XG4gICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgIHZhciBzdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICAgICAgICB2YXIgcmVjZWl2ZXJMaXN0ID0gW107XG4gICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhkZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgIHZhciBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgdmFyIGlzSWNlTGl0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1pY2UtbGl0ZScpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgdGhpcy51c2luZ0J1bmRsZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1ncm91cDpCVU5ETEUgJykubGVuZ3RoID4gMDtcbiAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIG1saW5lID0gbGluZXNbMF0uc3Vic3RyKDIpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICB2YXIga2luZCA9IG1saW5lWzBdO1xuICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWxpbmVbMV0gPT09ICcwJztcbiAgICAgICAgICAgIHZhciBkaXJlY3Rpb24gPSBTRFBVdGlscy5nZXREaXJlY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCk7XG5cbiAgICAgICAgICAgIHZhciB0cmFuc2NlaXZlcjtcbiAgICAgICAgICAgIHZhciBpY2VHYXRoZXJlcjtcbiAgICAgICAgICAgIHZhciBpY2VUcmFuc3BvcnQ7XG4gICAgICAgICAgICB2YXIgZHRsc1RyYW5zcG9ydDtcbiAgICAgICAgICAgIHZhciBydHBTZW5kZXI7XG4gICAgICAgICAgICB2YXIgcnRwUmVjZWl2ZXI7XG4gICAgICAgICAgICB2YXIgc2VuZEVuY29kaW5nUGFyYW1ldGVycztcbiAgICAgICAgICAgIHZhciByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzO1xuXG4gICAgICAgICAgICB2YXIgdHJhY2s7XG4gICAgICAgICAgICAvLyBGSVhNRTogZW5zdXJlIHRoZSBtZWRpYVNlY3Rpb24gaGFzIHJ0Y3AtbXV4IHNldC5cbiAgICAgICAgICAgIHZhciByZW1vdGVDYXBhYmlsaXRpZXMgPSBTRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMobWVkaWFTZWN0aW9uKTtcbiAgICAgICAgICAgIHZhciByZW1vdGVJY2VQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgdmFyIHJlbW90ZUR0bHNQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgaWYgKCFyZWplY3RlZCkge1xuICAgICAgICAgICAgICByZW1vdGVJY2VQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0SWNlUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24sXG4gICAgICAgICAgICAgICAgICBzZXNzaW9ucGFydCk7XG4gICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMobWVkaWFTZWN0aW9uLFxuICAgICAgICAgICAgICAgICAgc2Vzc2lvbnBhcnQpO1xuICAgICAgICAgICAgICByZW1vdGVEdGxzUGFyYW1ldGVycy5yb2xlID0gJ2NsaWVudCc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzID1cbiAgICAgICAgICAgICAgICBTRFBVdGlscy5wYXJzZVJ0cEVuY29kaW5nUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuXG4gICAgICAgICAgICB2YXIgbWlkID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1taWQ6Jyk7XG4gICAgICAgICAgICBpZiAobWlkLmxlbmd0aCkge1xuICAgICAgICAgICAgICBtaWQgPSBtaWRbMF0uc3Vic3RyKDYpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgbWlkID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBjbmFtZTtcbiAgICAgICAgICAgIC8vIEdldHMgdGhlIGZpcnN0IFNTUkMuIE5vdGUgdGhhdCB3aXRoIFJUWCB0aGVyZSBtaWdodCBiZSBtdWx0aXBsZVxuICAgICAgICAgICAgLy8gU1NSQ3MuXG4gICAgICAgICAgICB2YXIgcmVtb3RlU3NyYyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgICAgICAgICAgICAgIC5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbHRlcihmdW5jdGlvbihvYmopIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBvYmouYXR0cmlidXRlID09PSAnY25hbWUnO1xuICAgICAgICAgICAgICAgIH0pWzBdO1xuICAgICAgICAgICAgaWYgKHJlbW90ZVNzcmMpIHtcbiAgICAgICAgICAgICAgY25hbWUgPSByZW1vdGVTc3JjLnZhbHVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgaXNDb21wbGV0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAnYT1lbmQtb2YtY2FuZGlkYXRlcycpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgICB2YXIgY2FuZHMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWNhbmRpZGF0ZTonKVxuICAgICAgICAgICAgICAgIC5tYXAoZnVuY3Rpb24oY2FuZCkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlQ2FuZGlkYXRlKGNhbmQpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbHRlcihmdW5jdGlvbihjYW5kKSB7XG4gICAgICAgICAgICAgICAgICByZXR1cm4gY2FuZC5jb21wb25lbnQgPT09ICcxJztcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGlmIChkZXNjcmlwdGlvbi50eXBlID09PSAnb2ZmZXInICYmICFyZWplY3RlZCkge1xuICAgICAgICAgICAgICB2YXIgdHJhbnNwb3J0cyA9IHNlbGYudXNpbmdCdW5kbGUgJiYgc2RwTUxpbmVJbmRleCA+IDAgPyB7XG4gICAgICAgICAgICAgICAgaWNlR2F0aGVyZXI6IHNlbGYudHJhbnNjZWl2ZXJzWzBdLmljZUdhdGhlcmVyLFxuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydDogc2VsZi50cmFuc2NlaXZlcnNbMF0uaWNlVHJhbnNwb3J0LFxuICAgICAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHNlbGYudHJhbnNjZWl2ZXJzWzBdLmR0bHNUcmFuc3BvcnRcbiAgICAgICAgICAgICAgfSA6IHNlbGYuX2NyZWF0ZUljZUFuZER0bHNUcmFuc3BvcnRzKG1pZCwgc2RwTUxpbmVJbmRleCk7XG5cbiAgICAgICAgICAgICAgaWYgKGlzQ29tcGxldGUpIHtcbiAgICAgICAgICAgICAgICB0cmFuc3BvcnRzLmljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzID0gUlRDUnRwUmVjZWl2ZXIuZ2V0Q2FwYWJpbGl0aWVzKGtpbmQpO1xuICAgICAgICAgICAgICBzZW5kRW5jb2RpbmdQYXJhbWV0ZXJzID0gW3tcbiAgICAgICAgICAgICAgICBzc3JjOiAoMiAqIHNkcE1MaW5lSW5kZXggKyAyKSAqIDEwMDFcbiAgICAgICAgICAgICAgfV07XG5cbiAgICAgICAgICAgICAgcnRwUmVjZWl2ZXIgPSBuZXcgUlRDUnRwUmVjZWl2ZXIodHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LCBraW5kKTtcblxuICAgICAgICAgICAgICB0cmFjayA9IHJ0cFJlY2VpdmVyLnRyYWNrO1xuICAgICAgICAgICAgICByZWNlaXZlckxpc3QucHVzaChbdHJhY2ssIHJ0cFJlY2VpdmVyXSk7XG4gICAgICAgICAgICAgIC8vIEZJWE1FOiBub3QgY29ycmVjdCB3aGVuIHRoZXJlIGFyZSBtdWx0aXBsZSBzdHJlYW1zIGJ1dCB0aGF0IGlzXG4gICAgICAgICAgICAgIC8vIG5vdCBjdXJyZW50bHkgc3VwcG9ydGVkIGluIHRoaXMgc2hpbS5cbiAgICAgICAgICAgICAgc3RyZWFtLmFkZFRyYWNrKHRyYWNrKTtcblxuICAgICAgICAgICAgICAvLyBGSVhNRTogbG9vayBhdCBkaXJlY3Rpb24uXG4gICAgICAgICAgICAgIGlmIChzZWxmLmxvY2FsU3RyZWFtcy5sZW5ndGggPiAwICYmXG4gICAgICAgICAgICAgICAgICBzZWxmLmxvY2FsU3RyZWFtc1swXS5nZXRUcmFja3MoKS5sZW5ndGggPj0gc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBhY3R1YWxseSBtb3JlIGNvbXBsaWNhdGVkLCBuZWVkcyB0byBtYXRjaCB0eXBlcyBldGNcbiAgICAgICAgICAgICAgICB2YXIgbG9jYWx0cmFjayA9IHNlbGYubG9jYWxTdHJlYW1zWzBdXG4gICAgICAgICAgICAgICAgICAgIC5nZXRUcmFja3MoKVtzZHBNTGluZUluZGV4XTtcbiAgICAgICAgICAgICAgICBydHBTZW5kZXIgPSBuZXcgUlRDUnRwU2VuZGVyKGxvY2FsdHJhY2ssXG4gICAgICAgICAgICAgICAgICAgIHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICAgICAgICBpY2VHYXRoZXJlcjogdHJhbnNwb3J0cy5pY2VHYXRoZXJlcixcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuaWNlVHJhbnNwb3J0LFxuICAgICAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCxcbiAgICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllczogbG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzOiByZW1vdGVDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICAgICAgcnRwU2VuZGVyOiBydHBTZW5kZXIsXG4gICAgICAgICAgICAgICAgcnRwUmVjZWl2ZXI6IHJ0cFJlY2VpdmVyLFxuICAgICAgICAgICAgICAgIGtpbmQ6IGtpbmQsXG4gICAgICAgICAgICAgICAgbWlkOiBtaWQsXG4gICAgICAgICAgICAgICAgY25hbWU6IGNuYW1lLFxuICAgICAgICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnM6IHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMsXG4gICAgICAgICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVyczogcmVjdkVuY29kaW5nUGFyYW1ldGVyc1xuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAvLyBTdGFydCB0aGUgUlRDUnRwUmVjZWl2ZXIgbm93LiBUaGUgUlRQU2VuZGVyIGlzIHN0YXJ0ZWQgaW5cbiAgICAgICAgICAgICAgLy8gc2V0TG9jYWxEZXNjcmlwdGlvbi5cbiAgICAgICAgICAgICAgc2VsZi5fdHJhbnNjZWl2ZShzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSxcbiAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID09PSAnc2VuZHJlY3YnIHx8IGRpcmVjdGlvbiA9PT0gJ3NlbmRvbmx5Jyk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGRlc2NyaXB0aW9uLnR5cGUgPT09ICdhbnN3ZXInICYmICFyZWplY3RlZCkge1xuICAgICAgICAgICAgICB0cmFuc2NlaXZlciA9IHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICAgICAgICBpY2VHYXRoZXJlciA9IHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyO1xuICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5kdGxzVHJhbnNwb3J0O1xuICAgICAgICAgICAgICBydHBTZW5kZXIgPSB0cmFuc2NlaXZlci5ydHBTZW5kZXI7XG4gICAgICAgICAgICAgIHJ0cFJlY2VpdmVyID0gdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXI7XG4gICAgICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMgPSB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllcyA9IHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzO1xuXG4gICAgICAgICAgICAgIHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdLnJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMgPVxuICAgICAgICAgICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVycztcbiAgICAgICAgICAgICAgc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF0ucmVtb3RlQ2FwYWJpbGl0aWVzID1cbiAgICAgICAgICAgICAgICAgIHJlbW90ZUNhcGFiaWxpdGllcztcbiAgICAgICAgICAgICAgc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF0uY25hbWUgPSBjbmFtZTtcblxuICAgICAgICAgICAgICBpZiAoKGlzSWNlTGl0ZSB8fCBpc0NvbXBsZXRlKSAmJiBjYW5kcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQuc2V0UmVtb3RlQ2FuZGlkYXRlcyhjYW5kcyk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKCFzZWxmLnVzaW5nQnVuZGxlIHx8IHNkcE1MaW5lSW5kZXggPT09IDApIHtcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQuc3RhcnQoaWNlR2F0aGVyZXIsIHJlbW90ZUljZVBhcmFtZXRlcnMsXG4gICAgICAgICAgICAgICAgICAgICdjb250cm9sbGluZycpO1xuICAgICAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQuc3RhcnQocmVtb3RlRHRsc1BhcmFtZXRlcnMpO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgc2VsZi5fdHJhbnNjZWl2ZSh0cmFuc2NlaXZlcixcbiAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdyZWN2b25seScsXG4gICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPT09ICdzZW5kcmVjdicgfHwgZGlyZWN0aW9uID09PSAnc2VuZG9ubHknKTtcblxuICAgICAgICAgICAgICBpZiAocnRwUmVjZWl2ZXIgJiZcbiAgICAgICAgICAgICAgICAgIChkaXJlY3Rpb24gPT09ICdzZW5kcmVjdicgfHwgZGlyZWN0aW9uID09PSAnc2VuZG9ubHknKSkge1xuICAgICAgICAgICAgICAgIHRyYWNrID0gcnRwUmVjZWl2ZXIudHJhY2s7XG4gICAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LnB1c2goW3RyYWNrLCBydHBSZWNlaXZlcl0pO1xuICAgICAgICAgICAgICAgIHN0cmVhbS5hZGRUcmFjayh0cmFjayk7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGFjdHVhbGx5IHRoZSByZWNlaXZlciBzaG91bGQgYmUgY3JlYXRlZCBsYXRlci5cbiAgICAgICAgICAgICAgICBkZWxldGUgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXI7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24gPSB7XG4gICAgICAgICAgICB0eXBlOiBkZXNjcmlwdGlvbi50eXBlLFxuICAgICAgICAgICAgc2RwOiBkZXNjcmlwdGlvbi5zZHBcbiAgICAgICAgICB9O1xuICAgICAgICAgIHN3aXRjaCAoZGVzY3JpcHRpb24udHlwZSkge1xuICAgICAgICAgICAgY2FzZSAnb2ZmZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnaGF2ZS1yZW1vdGUtb2ZmZXInKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbnN3ZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnc3RhYmxlJyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigndW5zdXBwb3J0ZWQgdHlwZSBcIicgKyBkZXNjcmlwdGlvbi50eXBlICtcbiAgICAgICAgICAgICAgICAgICdcIicpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc3RyZWFtLmdldFRyYWNrcygpLmxlbmd0aCkge1xuICAgICAgICAgICAgc2VsZi5yZW1vdGVTdHJlYW1zLnB1c2goc3RyZWFtKTtcbiAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ2FkZHN0cmVhbScpO1xuICAgICAgICAgICAgICBldmVudC5zdHJlYW0gPSBzdHJlYW07XG4gICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgIGlmIChzZWxmLm9uYWRkc3RyZWFtICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLm9uYWRkc3RyZWFtKGV2ZW50KTtcbiAgICAgICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHJlY2VpdmVyTGlzdC5mb3JFYWNoKGZ1bmN0aW9uKGl0ZW0pIHtcbiAgICAgICAgICAgICAgICB2YXIgdHJhY2sgPSBpdGVtWzBdO1xuICAgICAgICAgICAgICAgIHZhciByZWNlaXZlciA9IGl0ZW1bMV07XG4gICAgICAgICAgICAgICAgdmFyIHRyYWNrRXZlbnQgPSBuZXcgRXZlbnQoJ3RyYWNrJyk7XG4gICAgICAgICAgICAgICAgdHJhY2tFdmVudC50cmFjayA9IHRyYWNrO1xuICAgICAgICAgICAgICAgIHRyYWNrRXZlbnQucmVjZWl2ZXIgPSByZWNlaXZlcjtcbiAgICAgICAgICAgICAgICB0cmFja0V2ZW50LnN0cmVhbXMgPSBbc3RyZWFtXTtcbiAgICAgICAgICAgICAgICBzZWxmLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgICAgICAgICAgICAgIGlmIChzZWxmLm9udHJhY2sgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLm9udHJhY2sodHJhY2tFdmVudCk7XG4gICAgICAgICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSwgMCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSAmJiB0eXBlb2YgYXJndW1lbnRzWzFdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChhcmd1bWVudHNbMV0sIDApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmNsb3NlID0gZnVuY3Rpb24oKSB7XG4gICAgICB0aGlzLnRyYW5zY2VpdmVycy5mb3JFYWNoKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIC8qIG5vdCB5ZXRcbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyKSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIuY2xvc2UoKTtcbiAgICAgICAgfVxuICAgICAgICAqL1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0KSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0LnN0b3AoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydCkge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5ydHBTZW5kZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5ydHBTZW5kZXIuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5ydHBSZWNlaXZlcikge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyLnN0b3AoKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICAvLyBGSVhNRTogY2xlYW4gdXAgdHJhY2tzLCBsb2NhbCBzdHJlYW1zLCByZW1vdGUgc3RyZWFtcywgZXRjXG4gICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnY2xvc2VkJyk7XG4gICAgfTtcblxuICAgIC8vIFVwZGF0ZSB0aGUgc2lnbmFsaW5nIHN0YXRlLlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlID1cbiAgICAgICAgZnVuY3Rpb24obmV3U3RhdGUpIHtcbiAgICAgICAgICB0aGlzLnNpZ25hbGluZ1N0YXRlID0gbmV3U3RhdGU7XG4gICAgICAgICAgdmFyIGV2ZW50ID0gbmV3IEV2ZW50KCdzaWduYWxpbmdzdGF0ZWNoYW5nZScpO1xuICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgaWYgKHRoaXMub25zaWduYWxpbmdzdGF0ZWNoYW5nZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlKGV2ZW50KTtcbiAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAvLyBEZXRlcm1pbmUgd2hldGhlciB0byBmaXJlIHRoZSBuZWdvdGlhdGlvbm5lZWRlZCBldmVudC5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl9tYXliZUZpcmVOZWdvdGlhdGlvbk5lZWRlZCA9XG4gICAgICAgIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIC8vIEZpcmUgYXdheSAoZm9yIG5vdykuXG4gICAgICAgICAgdmFyIGV2ZW50ID0gbmV3IEV2ZW50KCduZWdvdGlhdGlvbm5lZWRlZCcpO1xuICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgaWYgKHRoaXMub25uZWdvdGlhdGlvbm5lZWRlZCAhPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5vbm5lZ290aWF0aW9ubmVlZGVkKGV2ZW50KTtcbiAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAvLyBVcGRhdGUgdGhlIGNvbm5lY3Rpb24gc3RhdGUuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdXBkYXRlQ29ubmVjdGlvblN0YXRlID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICB2YXIgbmV3U3RhdGU7XG4gICAgICB2YXIgc3RhdGVzID0ge1xuICAgICAgICAnbmV3JzogMCxcbiAgICAgICAgY2xvc2VkOiAwLFxuICAgICAgICBjb25uZWN0aW5nOiAwLFxuICAgICAgICBjaGVja2luZzogMCxcbiAgICAgICAgY29ubmVjdGVkOiAwLFxuICAgICAgICBjb21wbGV0ZWQ6IDAsXG4gICAgICAgIGZhaWxlZDogMFxuICAgICAgfTtcbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgc3RhdGVzW3RyYW5zY2VpdmVyLmljZVRyYW5zcG9ydC5zdGF0ZV0rKztcbiAgICAgICAgc3RhdGVzW3RyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQuc3RhdGVdKys7XG4gICAgICB9KTtcbiAgICAgIC8vIElDRVRyYW5zcG9ydC5jb21wbGV0ZWQgYW5kIGNvbm5lY3RlZCBhcmUgdGhlIHNhbWUgZm9yIHRoaXMgcHVycG9zZS5cbiAgICAgIHN0YXRlcy5jb25uZWN0ZWQgKz0gc3RhdGVzLmNvbXBsZXRlZDtcblxuICAgICAgbmV3U3RhdGUgPSAnbmV3JztcbiAgICAgIGlmIChzdGF0ZXMuZmFpbGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdmYWlsZWQnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuY29ubmVjdGluZyA+IDAgfHwgc3RhdGVzLmNoZWNraW5nID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdjb25uZWN0aW5nJztcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGVzLmRpc2Nvbm5lY3RlZCA+IDApIHtcbiAgICAgICAgbmV3U3RhdGUgPSAnZGlzY29ubmVjdGVkJztcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGVzLm5ldyA+IDApIHtcbiAgICAgICAgbmV3U3RhdGUgPSAnbmV3JztcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGVzLmNvbm5lY3RlZCA+IDAgfHwgc3RhdGVzLmNvbXBsZXRlZCA+IDApIHtcbiAgICAgICAgbmV3U3RhdGUgPSAnY29ubmVjdGVkJztcbiAgICAgIH1cblxuICAgICAgaWYgKG5ld1N0YXRlICE9PSBzZWxmLmljZUNvbm5lY3Rpb25TdGF0ZSkge1xuICAgICAgICBzZWxmLmljZUNvbm5lY3Rpb25TdGF0ZSA9IG5ld1N0YXRlO1xuICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ2ljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZScpO1xuICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgICAgICBpZiAodGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSAhPT0gbnVsbCkge1xuICAgICAgICAgIHRoaXMub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UoZXZlbnQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlT2ZmZXIgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIGlmICh0aGlzLl9wZW5kaW5nT2ZmZXIpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdjcmVhdGVPZmZlciBjYWxsZWQgd2hpbGUgdGhlcmUgaXMgYSBwZW5kaW5nIG9mZmVyLicpO1xuICAgICAgfVxuICAgICAgdmFyIG9mZmVyT3B0aW9ucztcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAxICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gIT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgb2ZmZXJPcHRpb25zID0gYXJndW1lbnRzWzBdO1xuICAgICAgfSBlbHNlIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAzKSB7XG4gICAgICAgIG9mZmVyT3B0aW9ucyA9IGFyZ3VtZW50c1syXTtcbiAgICAgIH1cblxuICAgICAgdmFyIHRyYWNrcyA9IFtdO1xuICAgICAgdmFyIG51bUF1ZGlvVHJhY2tzID0gMDtcbiAgICAgIHZhciBudW1WaWRlb1RyYWNrcyA9IDA7XG4gICAgICAvLyBEZWZhdWx0IHRvIHNlbmRyZWN2LlxuICAgICAgaWYgKHRoaXMubG9jYWxTdHJlYW1zLmxlbmd0aCkge1xuICAgICAgICBudW1BdWRpb1RyYWNrcyA9IHRoaXMubG9jYWxTdHJlYW1zWzBdLmdldEF1ZGlvVHJhY2tzKCkubGVuZ3RoO1xuICAgICAgICBudW1WaWRlb1RyYWNrcyA9IHRoaXMubG9jYWxTdHJlYW1zWzBdLmdldFZpZGVvVHJhY2tzKCkubGVuZ3RoO1xuICAgICAgfVxuICAgICAgLy8gRGV0ZXJtaW5lIG51bWJlciBvZiBhdWRpbyBhbmQgdmlkZW8gdHJhY2tzIHdlIG5lZWQgdG8gc2VuZC9yZWN2LlxuICAgICAgaWYgKG9mZmVyT3B0aW9ucykge1xuICAgICAgICAvLyBSZWplY3QgQ2hyb21lIGxlZ2FjeSBjb25zdHJhaW50cy5cbiAgICAgICAgaWYgKG9mZmVyT3B0aW9ucy5tYW5kYXRvcnkgfHwgb2ZmZXJPcHRpb25zLm9wdGlvbmFsKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcbiAgICAgICAgICAgICAgJ0xlZ2FjeSBtYW5kYXRvcnkvb3B0aW9uYWwgY29uc3RyYWludHMgbm90IHN1cHBvcnRlZC4nKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlQXVkaW8gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIG51bUF1ZGlvVHJhY2tzID0gb2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlQXVkaW87XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZVZpZGVvICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBudW1WaWRlb1RyYWNrcyA9IG9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZVZpZGVvO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5sb2NhbFN0cmVhbXMubGVuZ3RoKSB7XG4gICAgICAgIC8vIFB1c2ggbG9jYWwgc3RyZWFtcy5cbiAgICAgICAgdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0VHJhY2tzKCkuZm9yRWFjaChmdW5jdGlvbih0cmFjaykge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6IHRyYWNrLmtpbmQsXG4gICAgICAgICAgICB0cmFjazogdHJhY2ssXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJhY2sua2luZCA9PT0gJ2F1ZGlvJyA/XG4gICAgICAgICAgICAgICAgbnVtQXVkaW9UcmFja3MgPiAwIDogbnVtVmlkZW9UcmFja3MgPiAwXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKHRyYWNrLmtpbmQgPT09ICdhdWRpbycpIHtcbiAgICAgICAgICAgIG51bUF1ZGlvVHJhY2tzLS07XG4gICAgICAgICAgfSBlbHNlIGlmICh0cmFjay5raW5kID09PSAndmlkZW8nKSB7XG4gICAgICAgICAgICBudW1WaWRlb1RyYWNrcy0tO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICAvLyBDcmVhdGUgTS1saW5lcyBmb3IgcmVjdm9ubHkgc3RyZWFtcy5cbiAgICAgIHdoaWxlIChudW1BdWRpb1RyYWNrcyA+IDAgfHwgbnVtVmlkZW9UcmFja3MgPiAwKSB7XG4gICAgICAgIGlmIChudW1BdWRpb1RyYWNrcyA+IDApIHtcbiAgICAgICAgICB0cmFja3MucHVzaCh7XG4gICAgICAgICAgICBraW5kOiAnYXVkaW8nLFxuICAgICAgICAgICAgd2FudFJlY2VpdmU6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBudW1BdWRpb1RyYWNrcy0tO1xuICAgICAgICB9XG4gICAgICAgIGlmIChudW1WaWRlb1RyYWNrcyA+IDApIHtcbiAgICAgICAgICB0cmFja3MucHVzaCh7XG4gICAgICAgICAgICBraW5kOiAndmlkZW8nLFxuICAgICAgICAgICAgd2FudFJlY2VpdmU6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBudW1WaWRlb1RyYWNrcy0tO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSgpO1xuICAgICAgdmFyIHRyYW5zY2VpdmVycyA9IFtdO1xuICAgICAgdHJhY2tzLmZvckVhY2goZnVuY3Rpb24obWxpbmUsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgLy8gRm9yIGVhY2ggdHJhY2ssIGNyZWF0ZSBhbiBpY2UgZ2F0aGVyZXIsIGljZSB0cmFuc3BvcnQsXG4gICAgICAgIC8vIGR0bHMgdHJhbnNwb3J0LCBwb3RlbnRpYWxseSBydHBzZW5kZXIgYW5kIHJ0cHJlY2VpdmVyLlxuICAgICAgICB2YXIgdHJhY2sgPSBtbGluZS50cmFjaztcbiAgICAgICAgdmFyIGtpbmQgPSBtbGluZS5raW5kO1xuICAgICAgICB2YXIgbWlkID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG5cbiAgICAgICAgdmFyIHRyYW5zcG9ydHMgPSBzZWxmLnVzaW5nQnVuZGxlICYmIHNkcE1MaW5lSW5kZXggPiAwID8ge1xuICAgICAgICAgIGljZUdhdGhlcmVyOiB0cmFuc2NlaXZlcnNbMF0uaWNlR2F0aGVyZXIsXG4gICAgICAgICAgaWNlVHJhbnNwb3J0OiB0cmFuc2NlaXZlcnNbMF0uaWNlVHJhbnNwb3J0LFxuICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zY2VpdmVyc1swXS5kdGxzVHJhbnNwb3J0XG4gICAgICAgIH0gOiBzZWxmLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyhtaWQsIHNkcE1MaW5lSW5kZXgpO1xuXG4gICAgICAgIHZhciBsb2NhbENhcGFiaWxpdGllcyA9IFJUQ1J0cFNlbmRlci5nZXRDYXBhYmlsaXRpZXMoa2luZCk7XG4gICAgICAgIHZhciBydHBTZW5kZXI7XG4gICAgICAgIHZhciBydHBSZWNlaXZlcjtcblxuICAgICAgICAvLyBnZW5lcmF0ZSBhbiBzc3JjIG5vdywgdG8gYmUgdXNlZCBsYXRlciBpbiBydHBTZW5kZXIuc2VuZFxuICAgICAgICB2YXIgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IFt7XG4gICAgICAgICAgc3NyYzogKDIgKiBzZHBNTGluZUluZGV4ICsgMSkgKiAxMDAxXG4gICAgICAgIH1dO1xuICAgICAgICBpZiAodHJhY2spIHtcbiAgICAgICAgICBydHBTZW5kZXIgPSBuZXcgUlRDUnRwU2VuZGVyKHRyYWNrLCB0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG1saW5lLndhbnRSZWNlaXZlKSB7XG4gICAgICAgICAgcnRwUmVjZWl2ZXIgPSBuZXcgUlRDUnRwUmVjZWl2ZXIodHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LCBraW5kKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICBpY2VHYXRoZXJlcjogdHJhbnNwb3J0cy5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuaWNlVHJhbnNwb3J0LFxuICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCxcbiAgICAgICAgICBsb2NhbENhcGFiaWxpdGllczogbG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzOiBudWxsLFxuICAgICAgICAgIHJ0cFNlbmRlcjogcnRwU2VuZGVyLFxuICAgICAgICAgIHJ0cFJlY2VpdmVyOiBydHBSZWNlaXZlcixcbiAgICAgICAgICBraW5kOiBraW5kLFxuICAgICAgICAgIG1pZDogbWlkLFxuICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnM6IHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMsXG4gICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVyczogbnVsbFxuICAgICAgICB9O1xuICAgICAgfSk7XG4gICAgICBpZiAodGhpcy51c2luZ0J1bmRsZSkge1xuICAgICAgICBzZHAgKz0gJ2E9Z3JvdXA6QlVORExFICcgKyB0cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdHJhY2tzLmZvckVhY2goZnVuY3Rpb24obWxpbmUsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgdmFyIHRyYW5zY2VpdmVyID0gdHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24odHJhbnNjZWl2ZXIsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcywgJ29mZmVyJywgc2VsZi5sb2NhbFN0cmVhbXNbMF0pO1xuICAgICAgfSk7XG5cbiAgICAgIHRoaXMuX3BlbmRpbmdPZmZlciA9IHRyYW5zY2VpdmVycztcbiAgICAgIHZhciBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICdvZmZlcicsXG4gICAgICAgIHNkcDogc2RwXG4gICAgICB9KTtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoYXJndW1lbnRzWzBdLCAwLCBkZXNjKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGVzYyk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlQW5zd2VyID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAgIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSgpO1xuICAgICAgaWYgKHRoaXMudXNpbmdCdW5kbGUpIHtcbiAgICAgICAgc2RwICs9ICdhPWdyb3VwOkJVTkRMRSAnICsgdGhpcy50cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICAvLyBDYWxjdWxhdGUgaW50ZXJzZWN0aW9uIG9mIGNhcGFiaWxpdGllcy5cbiAgICAgICAgdmFyIGNvbW1vbkNhcGFiaWxpdGllcyA9IHNlbGYuX2dldENvbW1vbkNhcGFiaWxpdGllcyhcbiAgICAgICAgICAgIHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgICAgdHJhbnNjZWl2ZXIucmVtb3RlQ2FwYWJpbGl0aWVzKTtcblxuICAgICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24odHJhbnNjZWl2ZXIsIGNvbW1vbkNhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICdhbnN3ZXInLCBzZWxmLmxvY2FsU3RyZWFtc1swXSk7XG4gICAgICB9KTtcblxuICAgICAgdmFyIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJ2Fuc3dlcicsXG4gICAgICAgIHNkcDogc2RwXG4gICAgICB9KTtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoYXJndW1lbnRzWzBdLCAwLCBkZXNjKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGVzYyk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkSWNlQ2FuZGlkYXRlID0gZnVuY3Rpb24oY2FuZGlkYXRlKSB7XG4gICAgICBpZiAoY2FuZGlkYXRlID09PSBudWxsKSB7XG4gICAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKHt9KTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgbUxpbmVJbmRleCA9IGNhbmRpZGF0ZS5zZHBNTGluZUluZGV4O1xuICAgICAgICBpZiAoY2FuZGlkYXRlLnNkcE1pZCkge1xuICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy50cmFuc2NlaXZlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnRyYW5zY2VpdmVyc1tpXS5taWQgPT09IGNhbmRpZGF0ZS5zZHBNaWQpIHtcbiAgICAgICAgICAgICAgbUxpbmVJbmRleCA9IGk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSB0aGlzLnRyYW5zY2VpdmVyc1ttTGluZUluZGV4XTtcbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgdmFyIGNhbmQgPSBPYmplY3Qua2V5cyhjYW5kaWRhdGUuY2FuZGlkYXRlKS5sZW5ndGggPiAwID9cbiAgICAgICAgICAgICAgU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUoY2FuZGlkYXRlLmNhbmRpZGF0ZSkgOiB7fTtcbiAgICAgICAgICAvLyBJZ25vcmUgQ2hyb21lJ3MgaW52YWxpZCBjYW5kaWRhdGVzIHNpbmNlIEVkZ2UgZG9lcyBub3QgbGlrZSB0aGVtLlxuICAgICAgICAgIGlmIChjYW5kLnByb3RvY29sID09PSAndGNwJyAmJiBjYW5kLnBvcnQgPT09IDApIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gSWdub3JlIFJUQ1AgY2FuZGlkYXRlcywgd2UgYXNzdW1lIFJUQ1AtTVVYLlxuICAgICAgICAgIGlmIChjYW5kLmNvbXBvbmVudCAhPT0gJzEnKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIEEgZGlydHkgaGFjayB0byBtYWtlIHNhbXBsZXMgd29yay5cbiAgICAgICAgICBpZiAoY2FuZC50eXBlID09PSAnZW5kT2ZDYW5kaWRhdGVzJykge1xuICAgICAgICAgICAgY2FuZCA9IHt9O1xuICAgICAgICAgIH1cbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKGNhbmQpO1xuXG4gICAgICAgICAgLy8gdXBkYXRlIHRoZSByZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICB2YXIgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICBzZWN0aW9uc1ttTGluZUluZGV4ICsgMV0gKz0gKGNhbmQudHlwZSA/IGNhbmRpZGF0ZS5jYW5kaWRhdGUudHJpbSgpXG4gICAgICAgICAgICAgIDogJ2E9ZW5kLW9mLWNhbmRpZGF0ZXMnKSArICdcXHJcXG4nO1xuICAgICAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSAmJiB0eXBlb2YgYXJndW1lbnRzWzFdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0U3RhdHMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBwcm9taXNlcyA9IFtdO1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBbJ3J0cFNlbmRlcicsICdydHBSZWNlaXZlcicsICdpY2VHYXRoZXJlcicsICdpY2VUcmFuc3BvcnQnLFxuICAgICAgICAgICAgJ2R0bHNUcmFuc3BvcnQnXS5mb3JFYWNoKGZ1bmN0aW9uKG1ldGhvZCkge1xuICAgICAgICAgICAgICBpZiAodHJhbnNjZWl2ZXJbbWV0aG9kXSkge1xuICAgICAgICAgICAgICAgIHByb21pc2VzLnB1c2godHJhbnNjZWl2ZXJbbWV0aG9kXS5nZXRTdGF0cygpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICAgIHZhciBjYiA9IGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicgJiZcbiAgICAgICAgICBhcmd1bWVudHNbMV07XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSkge1xuICAgICAgICAvLyBzaGltIGdldFN0YXRzIHdpdGggbWFwbGlrZSBzdXBwb3J0XG4gICAgICAgIHZhciByZXN1bHRzID0gbmV3IE1hcCgpO1xuICAgICAgICBQcm9taXNlLmFsbChwcm9taXNlcykudGhlbihmdW5jdGlvbihyZXMpIHtcbiAgICAgICAgICByZXMuZm9yRWFjaChmdW5jdGlvbihyZXN1bHQpIHtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHJlc3VsdCkuZm9yRWFjaChmdW5jdGlvbihpZCkge1xuICAgICAgICAgICAgICByZXN1bHRzLnNldChpZCwgcmVzdWx0W2lkXSk7XG4gICAgICAgICAgICAgIHJlc3VsdHNbaWRdID0gcmVzdWx0W2lkXTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIGlmIChjYikge1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoY2IsIDAsIHJlc3VsdHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXNvbHZlKHJlc3VsdHMpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH07XG4gIH0sXG5cbiAgLy8gQXR0YWNoIGEgbWVkaWEgc3RyZWFtIHRvIGFuIGVsZW1lbnQuXG4gIGF0dGFjaE1lZGlhU3RyZWFtOiBmdW5jdGlvbihlbGVtZW50LCBzdHJlYW0pIHtcbiAgICBsb2dnaW5nKCdERVBSRUNBVEVELCBhdHRhY2hNZWRpYVN0cmVhbSB3aWxsIHNvb24gYmUgcmVtb3ZlZC4nKTtcbiAgICBlbGVtZW50LnNyY09iamVjdCA9IHN0cmVhbTtcbiAgfSxcblxuICByZWF0dGFjaE1lZGlhU3RyZWFtOiBmdW5jdGlvbih0bywgZnJvbSkge1xuICAgIGxvZ2dpbmcoJ0RFUFJFQ0FURUQsIHJlYXR0YWNoTWVkaWFTdHJlYW0gd2lsbCBzb29uIGJlIHJlbW92ZWQuJyk7XG4gICAgdG8uc3JjT2JqZWN0ID0gZnJvbS5zcmNPYmplY3Q7XG4gIH1cbn07XG5cbi8vIEV4cG9zZSBwdWJsaWMgbWV0aG9kcy5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGVkZ2VTaGltLnNoaW1QZWVyQ29ubmVjdGlvbixcbiAgc2hpbUdldFVzZXJNZWRpYTogcmVxdWlyZSgnLi9nZXR1c2VybWVkaWEnKSxcbiAgYXR0YWNoTWVkaWFTdHJlYW06IGVkZ2VTaGltLmF0dGFjaE1lZGlhU3RyZWFtLFxuICByZWF0dGFjaE1lZGlhU3RyZWFtOiBlZGdlU2hpbS5yZWF0dGFjaE1lZGlhU3RyZWFtXG59O1xuXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9+L3RyYWNlYWJsZXBlZXJjb25uZWN0aW9uL34vd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2VkZ2UvZWRnZV9zaGltLmpzXG4vLyBtb2R1bGUgaWQgPSA0ODJcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ==");

FIXME found
Open

    eval("/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar SDPUtils = __webpack_require__(452);\nvar logging = __webpack_require__(479).log;\n\nvar edgeShim = {\n  shimPeerConnection: function() {\n    if (window.RTCIceGatherer) {\n      // ORTC defines an RTCIceCandidate object but no constructor.\n      // Not implemented in Edge.\n      if (!window.RTCIceCandidate) {\n        window.RTCIceCandidate = function(args) {\n          return args;\n        };\n      }\n      // ORTC does not have a session description object but\n      // other browsers (i.e. Chrome) that will support both PC and ORTC\n      // in the future might have this defined already.\n      if (!window.RTCSessionDescription) {\n        window.RTCSessionDescription = function(args) {\n          return args;\n        };\n      }\n    }\n\n    window.RTCPeerConnection = function(config) {\n      var self = this;\n\n      var _eventTarget = document.createDocumentFragment();\n      ['addEventListener', 'removeEventListener', 'dispatchEvent']\n          .forEach(function(method) {\n            self[method] = _eventTarget[method].bind(_eventTarget);\n          });\n\n      this.onicecandidate = null;\n      this.onaddstream = null;\n      this.ontrack = null;\n      this.onremovestream = null;\n      this.onsignalingstatechange = null;\n      this.oniceconnectionstatechange = null;\n      this.onnegotiationneeded = null;\n      this.ondatachannel = null;\n\n      this.localStreams = [];\n      this.remoteStreams = [];\n      this.getLocalStreams = function() {\n        return self.localStreams;\n      };\n      this.getRemoteStreams = function() {\n        return self.remoteStreams;\n      };\n\n      this.localDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.remoteDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.signalingState = 'stable';\n      this.iceConnectionState = 'new';\n      this.iceGatheringState = 'new';\n\n      this.iceOptions = {\n        gatherPolicy: 'all',\n        iceServers: []\n      };\n      if (config && config.iceTransportPolicy) {\n        switch (config.iceTransportPolicy) {\n          case 'all':\n          case 'relay':\n            this.iceOptions.gatherPolicy = config.iceTransportPolicy;\n            break;\n          case 'none':\n            // FIXME: remove once implementation and spec have added this.\n            throw new TypeError('iceTransportPolicy \"none\" not supported');\n          default:\n            // don't set iceTransportPolicy.\n            break;\n        }\n      }\n      this.usingBundle = config && config.bundlePolicy === 'max-bundle';\n\n      if (config && config.iceServers) {\n        // Edge does not like\n        // 1) stun:\n        // 2) turn: that does not have all of turn:host:port?transport=udp\n        var iceServers = JSON.parse(JSON.stringify(config.iceServers));\n        this.iceOptions.iceServers = iceServers.filter(function(server) {\n          if (server && server.urls) {\n            var urls = server.urls;\n            if (typeof urls === 'string') {\n              urls = [urls];\n            }\n            urls = urls.filter(function(url) {\n              return url.indexOf('turn:') === 0 &&\n                  url.indexOf('transport=udp') !== -1;\n            })[0];\n            return !!urls;\n          }\n          return false;\n        });\n      }\n\n      // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n      // everything that is needed to describe a SDP m-line.\n      this.transceivers = [];\n\n      // since the iceGatherer is currently created in createOffer but we\n      // must not emit candidates until after setLocalDescription we buffer\n      // them in this array.\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {\n      var self = this;\n      var sections = SDPUtils.splitSections(self.localDescription.sdp);\n      // FIXME: need to apply ice candidates in a way which is async but\n      // in-order\n      this._localIceCandidatesBuffer.forEach(function(event) {\n        var end = !event.candidate || Object.keys(event.candidate).length === 0;\n        if (end) {\n          for (var j = 1; j < sections.length; j++) {\n            if (sections[j].indexOf('\\r\\na=end-of-candidates\\r\\n') === -1) {\n              sections[j] += 'a=end-of-candidates\\r\\n';\n            }\n          }\n        } else if (event.candidate.candidate.indexOf('typ endOfCandidates')\n            === -1) {\n          sections[event.candidate.sdpMLineIndex + 1] +=\n              'a=' + event.candidate.candidate + '\\r\\n';\n        }\n        self.localDescription.sdp = sections.join('');\n        self.dispatchEvent(event);\n        if (self.onicecandidate !== null) {\n          self.onicecandidate(event);\n        }\n        if (!event.candidate && self.iceGatheringState !== 'complete') {\n          var complete = self.transceivers.every(function(transceiver) {\n            return transceiver.iceGatherer &&\n                transceiver.iceGatherer.state === 'completed';\n          });\n          if (complete) {\n            self.iceGatheringState = 'complete';\n          }\n        }\n      });\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype.addStream = function(stream) {\n      // Clone is necessary for local demos mostly, attaching directly\n      // to two different senders does not work (build 10547).\n      this.localStreams.push(stream.clone());\n      this._maybeFireNegotiationNeeded();\n    };\n\n    window.RTCPeerConnection.prototype.removeStream = function(stream) {\n      var idx = this.localStreams.indexOf(stream);\n      if (idx > -1) {\n        this.localStreams.splice(idx, 1);\n        this._maybeFireNegotiationNeeded();\n      }\n    };\n\n    window.RTCPeerConnection.prototype.getSenders = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpSender;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpSender;\n      });\n    };\n\n    window.RTCPeerConnection.prototype.getReceivers = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpReceiver;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpReceiver;\n      });\n    };\n\n    // Determines the intersection of local and remote capabilities.\n    window.RTCPeerConnection.prototype._getCommonCapabilities =\n        function(localCapabilities, remoteCapabilities) {\n          var commonCapabilities = {\n            codecs: [],\n            headerExtensions: [],\n            fecMechanisms: []\n          };\n          localCapabilities.codecs.forEach(function(lCodec) {\n            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n              var rCodec = remoteCapabilities.codecs[i];\n              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n                  lCodec.clockRate === rCodec.clockRate &&\n                  lCodec.numChannels === rCodec.numChannels) {\n                // push rCodec so we reply with offerer payload type\n                commonCapabilities.codecs.push(rCodec);\n\n                // FIXME: also need to determine intersection between\n                // .rtcpFeedback and .parameters\n                break;\n              }\n            }\n          });\n\n          localCapabilities.headerExtensions\n              .forEach(function(lHeaderExtension) {\n                for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n                     i++) {\n                  var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n                  if (lHeaderExtension.uri === rHeaderExtension.uri) {\n                    commonCapabilities.headerExtensions.push(rHeaderExtension);\n                    break;\n                  }\n                }\n              });\n\n          // FIXME: fecMechanisms\n          return commonCapabilities;\n        };\n\n    // Create ICE gatherer, ICE transport and DTLS transport.\n    window.RTCPeerConnection.prototype._createIceAndDtlsTransports =\n        function(mid, sdpMLineIndex) {\n          var self = this;\n          var iceGatherer = new RTCIceGatherer(self.iceOptions);\n          var iceTransport = new RTCIceTransport(iceGatherer);\n          iceGatherer.onlocalcandidate = function(evt) {\n            var event = new Event('icecandidate');\n            event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n            var cand = evt.candidate;\n            var end = !cand || Object.keys(cand).length === 0;\n            // Edge emits an empty object for RTCIceCandidateComplete‥\n            if (end) {\n              // polyfill since RTCIceGatherer.state is not implemented in\n              // Edge 10547 yet.\n              if (iceGatherer.state === undefined) {\n                iceGatherer.state = 'completed';\n              }\n\n              // Emit a candidate with type endOfCandidates to make the samples\n              // work. Edge requires addIceCandidate with this empty candidate\n              // to start checking. The real solution is to signal\n              // end-of-candidates to the other side when getting the null\n              // candidate but some apps (like the samples) don't do that.\n              event.candidate.candidate =\n                  'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';\n            } else {\n              // RTCIceCandidate doesn't have a component, needs to be added\n              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;\n              event.candidate.candidate = SDPUtils.writeCandidate(cand);\n            }\n\n            // update local description.\n            var sections = SDPUtils.splitSections(self.localDescription.sdp);\n            if (event.candidate.candidate.indexOf('typ endOfCandidates')\n                === -1) {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=' + event.candidate.candidate + '\\r\\n';\n            } else {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=end-of-candidates\\r\\n';\n            }\n            self.localDescription.sdp = sections.join('');\n\n            var complete = self.transceivers.every(function(transceiver) {\n              return transceiver.iceGatherer &&\n                  transceiver.iceGatherer.state === 'completed';\n            });\n\n            // Emit candidate if localDescription is set.\n            // Also emits null candidate when all gatherers are complete.\n            switch (self.iceGatheringState) {\n              case 'new':\n                self._localIceCandidatesBuffer.push(event);\n                if (end && complete) {\n                  self._localIceCandidatesBuffer.push(\n                      new Event('icecandidate'));\n                }\n                break;\n              case 'gathering':\n                self._emitBufferedCandidates();\n                self.dispatchEvent(event);\n                if (self.onicecandidate !== null) {\n                  self.onicecandidate(event);\n                }\n                if (complete) {\n                  self.dispatchEvent(new Event('icecandidate'));\n                  if (self.onicecandidate !== null) {\n                    self.onicecandidate(new Event('icecandidate'));\n                  }\n                  self.iceGatheringState = 'complete';\n                }\n                break;\n              case 'complete':\n                // should not happen... currently!\n                break;\n              default: // no-op.\n                break;\n            }\n          };\n          iceTransport.onicestatechange = function() {\n            self._updateConnectionState();\n          };\n\n          var dtlsTransport = new RTCDtlsTransport(iceTransport);\n          dtlsTransport.ondtlsstatechange = function() {\n            self._updateConnectionState();\n          };\n          dtlsTransport.onerror = function() {\n            // onerror does not set state to failed by itself.\n            dtlsTransport.state = 'failed';\n            self._updateConnectionState();\n          };\n\n          return {\n            iceGatherer: iceGatherer,\n            iceTransport: iceTransport,\n            dtlsTransport: dtlsTransport\n          };\n        };\n\n    // Start the RTP Sender and Receiver for a transceiver.\n    window.RTCPeerConnection.prototype._transceive = function(transceiver,\n        send, recv) {\n      var params = this._getCommonCapabilities(transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n      if (send && transceiver.rtpSender) {\n        params.encodings = transceiver.sendEncodingParameters;\n        params.rtcp = {\n          cname: SDPUtils.localCName\n        };\n        if (transceiver.recvEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpSender.send(params);\n      }\n      if (recv && transceiver.rtpReceiver) {\n        params.encodings = transceiver.recvEncodingParameters;\n        params.rtcp = {\n          cname: transceiver.cname\n        };\n        if (transceiver.sendEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpReceiver.receive(params);\n      }\n    };\n\n    window.RTCPeerConnection.prototype.setLocalDescription =\n        function(description) {\n          var self = this;\n          var sections;\n          var sessionpart;\n          if (description.type === 'offer') {\n            // FIXME: What was the purpose of this empty if statement?\n            // if (!this._pendingOffer) {\n            // } else {\n            if (this._pendingOffer) {\n              // VERY limited support for SDP munging. Limited to:\n              // * changing the order of codecs\n              sections = SDPUtils.splitSections(description.sdp);\n              sessionpart = sections.shift();\n              sections.forEach(function(mediaSection, sdpMLineIndex) {\n                var caps = SDPUtils.parseRtpParameters(mediaSection);\n                self._pendingOffer[sdpMLineIndex].localCapabilities = caps;\n              });\n              this.transceivers = this._pendingOffer;\n              delete this._pendingOffer;\n            }\n          } else if (description.type === 'answer') {\n            sections = SDPUtils.splitSections(self.remoteDescription.sdp);\n            sessionpart = sections.shift();\n            var isIceLite = SDPUtils.matchPrefix(sessionpart,\n                'a=ice-lite').length > 0;\n            sections.forEach(function(mediaSection, sdpMLineIndex) {\n              var transceiver = self.transceivers[sdpMLineIndex];\n              var iceGatherer = transceiver.iceGatherer;\n              var iceTransport = transceiver.iceTransport;\n              var dtlsTransport = transceiver.dtlsTransport;\n              var localCapabilities = transceiver.localCapabilities;\n              var remoteCapabilities = transceiver.remoteCapabilities;\n              var rejected = mediaSection.split('\\n', 1)[0]\n                  .split(' ', 2)[1] === '0';\n\n              if (!rejected) {\n                var remoteIceParameters = SDPUtils.getIceParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                  .map(function(cand) {\n                    return SDPUtils.parseCandidate(cand);\n                  })\n                  .filter(function(cand) {\n                    return cand.component === '1';\n                  });\n                  // ice-lite only includes host candidates in the SDP so we can\n                  // use setRemoteCandidates (which implies an\n                  // RTCIceCandidateComplete)\n                  if (cands.length) {\n                    iceTransport.setRemoteCandidates(cands);\n                  }\n                }\n                var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  remoteDtlsParameters.role = 'server';\n                }\n\n                if (!self.usingBundle || sdpMLineIndex === 0) {\n                  iceTransport.start(iceGatherer, remoteIceParameters,\n                      isIceLite ? 'controlling' : 'controlled');\n                  dtlsTransport.start(remoteDtlsParameters);\n                }\n\n                // Calculate intersection of capabilities.\n                var params = self._getCommonCapabilities(localCapabilities,\n                    remoteCapabilities);\n\n                // Start the RTCRtpSender. The RTCRtpReceiver for this\n                // transceiver has already been started in setRemoteDescription.\n                self._transceive(transceiver,\n                    params.codecs.length > 0,\n                    false);\n              }\n            });\n          }\n\n          this.localDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-local-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n\n          // If a success callback was provided, emit ICE candidates after it\n          // has been executed. Otherwise, emit callback after the Promise is\n          // resolved.\n          var hasCallback = arguments.length > 1 &&\n            typeof arguments[1] === 'function';\n          if (hasCallback) {\n            var cb = arguments[1];\n            window.setTimeout(function() {\n              cb();\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              self._emitBufferedCandidates();\n            }, 0);\n          }\n          var p = Promise.resolve();\n          p.then(function() {\n            if (!hasCallback) {\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              // Usually candidates will be emitted earlier.\n              window.setTimeout(self._emitBufferedCandidates.bind(self), 500);\n            }\n          });\n          return p;\n        };\n\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n        function(description) {\n          var self = this;\n          var stream = new MediaStream();\n          var receiverList = [];\n          var sections = SDPUtils.splitSections(description.sdp);\n          var sessionpart = sections.shift();\n          var isIceLite = SDPUtils.matchPrefix(sessionpart,\n              'a=ice-lite').length > 0;\n          this.usingBundle = SDPUtils.matchPrefix(sessionpart,\n              'a=group:BUNDLE ').length > 0;\n          sections.forEach(function(mediaSection, sdpMLineIndex) {\n            var lines = SDPUtils.splitLines(mediaSection);\n            var mline = lines[0].substr(2).split(' ');\n            var kind = mline[0];\n            var rejected = mline[1] === '0';\n            var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n\n            var transceiver;\n            var iceGatherer;\n            var iceTransport;\n            var dtlsTransport;\n            var rtpSender;\n            var rtpReceiver;\n            var sendEncodingParameters;\n            var recvEncodingParameters;\n            var localCapabilities;\n\n            var track;\n            // FIXME: ensure the mediaSection has rtcp-mux set.\n            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n            var remoteIceParameters;\n            var remoteDtlsParameters;\n            if (!rejected) {\n              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters.role = 'client';\n            }\n            recvEncodingParameters =\n                SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');\n            if (mid.length) {\n              mid = mid[0].substr(6);\n            } else {\n              mid = SDPUtils.generateIdentifier();\n            }\n\n            var cname;\n            // Gets the first SSRC. Note that with RTX there might be multiple\n            // SSRCs.\n            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n                .map(function(line) {\n                  return SDPUtils.parseSsrcMedia(line);\n                })\n                .filter(function(obj) {\n                  return obj.attribute === 'cname';\n                })[0];\n            if (remoteSsrc) {\n              cname = remoteSsrc.value;\n            }\n\n            var isComplete = SDPUtils.matchPrefix(mediaSection,\n                'a=end-of-candidates').length > 0;\n            var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                .map(function(cand) {\n                  return SDPUtils.parseCandidate(cand);\n                })\n                .filter(function(cand) {\n                  return cand.component === '1';\n                });\n            if (description.type === 'offer' && !rejected) {\n              var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n                iceGatherer: self.transceivers[0].iceGatherer,\n                iceTransport: self.transceivers[0].iceTransport,\n                dtlsTransport: self.transceivers[0].dtlsTransport\n              } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n              if (isComplete) {\n                transports.iceTransport.setRemoteCandidates(cands);\n              }\n\n              localCapabilities = RTCRtpReceiver.getCapabilities(kind);\n              sendEncodingParameters = [{\n                ssrc: (2 * sdpMLineIndex + 2) * 1001\n              }];\n\n              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n\n              track = rtpReceiver.track;\n              receiverList.push([track, rtpReceiver]);\n              // FIXME: not correct when there are multiple streams but that is\n              // not currently supported in this shim.\n              stream.addTrack(track);\n\n              // FIXME: look at direction.\n              if (self.localStreams.length > 0 &&\n                  self.localStreams[0].getTracks().length >= sdpMLineIndex) {\n                // FIXME: actually more complicated, needs to match types etc\n                var localtrack = self.localStreams[0]\n                    .getTracks()[sdpMLineIndex];\n                rtpSender = new RTCRtpSender(localtrack,\n                    transports.dtlsTransport);\n              }\n\n              self.transceivers[sdpMLineIndex] = {\n                iceGatherer: transports.iceGatherer,\n                iceTransport: transports.iceTransport,\n                dtlsTransport: transports.dtlsTransport,\n                localCapabilities: localCapabilities,\n                remoteCapabilities: remoteCapabilities,\n                rtpSender: rtpSender,\n                rtpReceiver: rtpReceiver,\n                kind: kind,\n                mid: mid,\n                cname: cname,\n                sendEncodingParameters: sendEncodingParameters,\n                recvEncodingParameters: recvEncodingParameters\n              };\n              // Start the RTCRtpReceiver now. The RTPSender is started in\n              // setLocalDescription.\n              self._transceive(self.transceivers[sdpMLineIndex],\n                  false,\n                  direction === 'sendrecv' || direction === 'sendonly');\n            } else if (description.type === 'answer' && !rejected) {\n              transceiver = self.transceivers[sdpMLineIndex];\n              iceGatherer = transceiver.iceGatherer;\n              iceTransport = transceiver.iceTransport;\n              dtlsTransport = transceiver.dtlsTransport;\n              rtpSender = transceiver.rtpSender;\n              rtpReceiver = transceiver.rtpReceiver;\n              sendEncodingParameters = transceiver.sendEncodingParameters;\n              localCapabilities = transceiver.localCapabilities;\n\n              self.transceivers[sdpMLineIndex].recvEncodingParameters =\n                  recvEncodingParameters;\n              self.transceivers[sdpMLineIndex].remoteCapabilities =\n                  remoteCapabilities;\n              self.transceivers[sdpMLineIndex].cname = cname;\n\n              if ((isIceLite || isComplete) && cands.length) {\n                iceTransport.setRemoteCandidates(cands);\n              }\n              if (!self.usingBundle || sdpMLineIndex === 0) {\n                iceTransport.start(iceGatherer, remoteIceParameters,\n                    'controlling');\n                dtlsTransport.start(remoteDtlsParameters);\n              }\n\n              self._transceive(transceiver,\n                  direction === 'sendrecv' || direction === 'recvonly',\n                  direction === 'sendrecv' || direction === 'sendonly');\n\n              if (rtpReceiver &&\n                  (direction === 'sendrecv' || direction === 'sendonly')) {\n                track = rtpReceiver.track;\n                receiverList.push([track, rtpReceiver]);\n                stream.addTrack(track);\n              } else {\n                // FIXME: actually the receiver should be created later.\n                delete transceiver.rtpReceiver;\n              }\n            }\n          });\n\n          this.remoteDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-remote-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n          if (stream.getTracks().length) {\n            self.remoteStreams.push(stream);\n            window.setTimeout(function() {\n              var event = new Event('addstream');\n              event.stream = stream;\n              self.dispatchEvent(event);\n              if (self.onaddstream !== null) {\n                window.setTimeout(function() {\n                  self.onaddstream(event);\n                }, 0);\n              }\n\n              receiverList.forEach(function(item) {\n                var track = item[0];\n                var receiver = item[1];\n                var trackEvent = new Event('track');\n                trackEvent.track = track;\n                trackEvent.receiver = receiver;\n                trackEvent.streams = [stream];\n                self.dispatchEvent(event);\n                if (self.ontrack !== null) {\n                  window.setTimeout(function() {\n                    self.ontrack(trackEvent);\n                  }, 0);\n                }\n              });\n            }, 0);\n          }\n          if (arguments.length > 1 && typeof arguments[1] === 'function') {\n            window.setTimeout(arguments[1], 0);\n          }\n          return Promise.resolve();\n        };\n\n    window.RTCPeerConnection.prototype.close = function() {\n      this.transceivers.forEach(function(transceiver) {\n        /* not yet\n        if (transceiver.iceGatherer) {\n          transceiver.iceGatherer.close();\n        }\n        */\n        if (transceiver.iceTransport) {\n          transceiver.iceTransport.stop();\n        }\n        if (transceiver.dtlsTransport) {\n          transceiver.dtlsTransport.stop();\n        }\n        if (transceiver.rtpSender) {\n          transceiver.rtpSender.stop();\n        }\n        if (transceiver.rtpReceiver) {\n          transceiver.rtpReceiver.stop();\n        }\n      });\n      // FIXME: clean up tracks, local streams, remote streams, etc\n      this._updateSignalingState('closed');\n    };\n\n    // Update the signaling state.\n    window.RTCPeerConnection.prototype._updateSignalingState =\n        function(newState) {\n          this.signalingState = newState;\n          var event = new Event('signalingstatechange');\n          this.dispatchEvent(event);\n          if (this.onsignalingstatechange !== null) {\n            this.onsignalingstatechange(event);\n          }\n        };\n\n    // Determine whether to fire the negotiationneeded event.\n    window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =\n        function() {\n          // Fire away (for now).\n          var event = new Event('negotiationneeded');\n          this.dispatchEvent(event);\n          if (this.onnegotiationneeded !== null) {\n            this.onnegotiationneeded(event);\n          }\n        };\n\n    // Update the connection state.\n    window.RTCPeerConnection.prototype._updateConnectionState = function() {\n      var self = this;\n      var newState;\n      var states = {\n        'new': 0,\n        closed: 0,\n        connecting: 0,\n        checking: 0,\n        connected: 0,\n        completed: 0,\n        failed: 0\n      };\n      this.transceivers.forEach(function(transceiver) {\n        states[transceiver.iceTransport.state]++;\n        states[transceiver.dtlsTransport.state]++;\n      });\n      // ICETransport.completed and connected are the same for this purpose.\n      states.connected += states.completed;\n\n      newState = 'new';\n      if (states.failed > 0) {\n        newState = 'failed';\n      } else if (states.connecting > 0 || states.checking > 0) {\n        newState = 'connecting';\n      } else if (states.disconnected > 0) {\n        newState = 'disconnected';\n      } else if (states.new > 0) {\n        newState = 'new';\n      } else if (states.connected > 0 || states.completed > 0) {\n        newState = 'connected';\n      }\n\n      if (newState !== self.iceConnectionState) {\n        self.iceConnectionState = newState;\n        var event = new Event('iceconnectionstatechange');\n        this.dispatchEvent(event);\n        if (this.oniceconnectionstatechange !== null) {\n          this.oniceconnectionstatechange(event);\n        }\n      }\n    };\n\n    window.RTCPeerConnection.prototype.createOffer = function() {\n      var self = this;\n      if (this._pendingOffer) {\n        throw new Error('createOffer called while there is a pending offer.');\n      }\n      var offerOptions;\n      if (arguments.length === 1 && typeof arguments[0] !== 'function') {\n        offerOptions = arguments[0];\n      } else if (arguments.length === 3) {\n        offerOptions = arguments[2];\n      }\n\n      var tracks = [];\n      var numAudioTracks = 0;\n      var numVideoTracks = 0;\n      // Default to sendrecv.\n      if (this.localStreams.length) {\n        numAudioTracks = this.localStreams[0].getAudioTracks().length;\n        numVideoTracks = this.localStreams[0].getVideoTracks().length;\n      }\n      // Determine number of audio and video tracks we need to send/recv.\n      if (offerOptions) {\n        // Reject Chrome legacy constraints.\n        if (offerOptions.mandatory || offerOptions.optional) {\n          throw new TypeError(\n              'Legacy mandatory/optional constraints not supported.');\n        }\n        if (offerOptions.offerToReceiveAudio !== undefined) {\n          numAudioTracks = offerOptions.offerToReceiveAudio;\n        }\n        if (offerOptions.offerToReceiveVideo !== undefined) {\n          numVideoTracks = offerOptions.offerToReceiveVideo;\n        }\n      }\n      if (this.localStreams.length) {\n        // Push local streams.\n        this.localStreams[0].getTracks().forEach(function(track) {\n          tracks.push({\n            kind: track.kind,\n            track: track,\n            wantReceive: track.kind === 'audio' ?\n                numAudioTracks > 0 : numVideoTracks > 0\n          });\n          if (track.kind === 'audio') {\n            numAudioTracks--;\n          } else if (track.kind === 'video') {\n            numVideoTracks--;\n          }\n        });\n      }\n      // Create M-lines for recvonly streams.\n      while (numAudioTracks > 0 || numVideoTracks > 0) {\n        if (numAudioTracks > 0) {\n          tracks.push({\n            kind: 'audio',\n            wantReceive: true\n          });\n          numAudioTracks--;\n        }\n        if (numVideoTracks > 0) {\n          tracks.push({\n            kind: 'video',\n            wantReceive: true\n          });\n          numVideoTracks--;\n        }\n      }\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      var transceivers = [];\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        // For each track, create an ice gatherer, ice transport,\n        // dtls transport, potentially rtpsender and rtpreceiver.\n        var track = mline.track;\n        var kind = mline.kind;\n        var mid = SDPUtils.generateIdentifier();\n\n        var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n          iceGatherer: transceivers[0].iceGatherer,\n          iceTransport: transceivers[0].iceTransport,\n          dtlsTransport: transceivers[0].dtlsTransport\n        } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n        var localCapabilities = RTCRtpSender.getCapabilities(kind);\n        var rtpSender;\n        var rtpReceiver;\n\n        // generate an ssrc now, to be used later in rtpSender.send\n        var sendEncodingParameters = [{\n          ssrc: (2 * sdpMLineIndex + 1) * 1001\n        }];\n        if (track) {\n          rtpSender = new RTCRtpSender(track, transports.dtlsTransport);\n        }\n\n        if (mline.wantReceive) {\n          rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n        }\n\n        transceivers[sdpMLineIndex] = {\n          iceGatherer: transports.iceGatherer,\n          iceTransport: transports.iceTransport,\n          dtlsTransport: transports.dtlsTransport,\n          localCapabilities: localCapabilities,\n          remoteCapabilities: null,\n          rtpSender: rtpSender,\n          rtpReceiver: rtpReceiver,\n          kind: kind,\n          mid: mid,\n          sendEncodingParameters: sendEncodingParameters,\n          recvEncodingParameters: null\n        };\n      });\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        var transceiver = transceivers[sdpMLineIndex];\n        sdp += SDPUtils.writeMediaSection(transceiver,\n            transceiver.localCapabilities, 'offer', self.localStreams[0]);\n      });\n\n      this._pendingOffer = transceivers;\n      var desc = new RTCSessionDescription({\n        type: 'offer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.createAnswer = function() {\n      var self = this;\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      this.transceivers.forEach(function(transceiver) {\n        // Calculate intersection of capabilities.\n        var commonCapabilities = self._getCommonCapabilities(\n            transceiver.localCapabilities,\n            transceiver.remoteCapabilities);\n\n        sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,\n            'answer', self.localStreams[0]);\n      });\n\n      var desc = new RTCSessionDescription({\n        type: 'answer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n      if (candidate === null) {\n        this.transceivers.forEach(function(transceiver) {\n          transceiver.iceTransport.addRemoteCandidate({});\n        });\n      } else {\n        var mLineIndex = candidate.sdpMLineIndex;\n        if (candidate.sdpMid) {\n          for (var i = 0; i < this.transceivers.length; i++) {\n            if (this.transceivers[i].mid === candidate.sdpMid) {\n              mLineIndex = i;\n              break;\n            }\n          }\n        }\n        var transceiver = this.transceivers[mLineIndex];\n        if (transceiver) {\n          var cand = Object.keys(candidate.candidate).length > 0 ?\n              SDPUtils.parseCandidate(candidate.candidate) : {};\n          // Ignore Chrome's invalid candidates since Edge does not like them.\n          if (cand.protocol === 'tcp' && cand.port === 0) {\n            return;\n          }\n          // Ignore RTCP candidates, we assume RTCP-MUX.\n          if (cand.component !== '1') {\n            return;\n          }\n          // A dirty hack to make samples work.\n          if (cand.type === 'endOfCandidates') {\n            cand = {};\n          }\n          transceiver.iceTransport.addRemoteCandidate(cand);\n\n          // update the remoteDescription.\n          var sections = SDPUtils.splitSections(this.remoteDescription.sdp);\n          sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()\n              : 'a=end-of-candidates') + '\\r\\n';\n          this.remoteDescription.sdp = sections.join('');\n        }\n      }\n      if (arguments.length > 1 && typeof arguments[1] === 'function') {\n        window.setTimeout(arguments[1], 0);\n      }\n      return Promise.resolve();\n    };\n\n    window.RTCPeerConnection.prototype.getStats = function() {\n      var promises = [];\n      this.transceivers.forEach(function(transceiver) {\n        ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n            'dtlsTransport'].forEach(function(method) {\n              if (transceiver[method]) {\n                promises.push(transceiver[method].getStats());\n              }\n            });\n      });\n      var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&\n          arguments[1];\n      return new Promise(function(resolve) {\n        // shim getStats with maplike support\n        var results = new Map();\n        Promise.all(promises).then(function(res) {\n          res.forEach(function(result) {\n            Object.keys(result).forEach(function(id) {\n              results.set(id, result[id]);\n              results[id] = result[id];\n            });\n          });\n          if (cb) {\n            window.setTimeout(cb, 0, results);\n          }\n          resolve(results);\n        });\n      });\n    };\n  },\n\n  // Attach a media stream to an element.\n  attachMediaStream: function(element, stream) {\n    logging('DEPRECATED, attachMediaStream will soon be removed.');\n    element.srcObject = stream;\n  },\n\n  reattachMediaStream: function(to, from) {\n    logging('DEPRECATED, reattachMediaStream will soon be removed.');\n    to.srcObject = from.srcObject;\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimPeerConnection: edgeShim.shimPeerConnection,\n  shimGetUserMedia: __webpack_require__(483),\n  attachMediaStream: edgeShim.attachMediaStream,\n  reattachMediaStream: edgeShim.reattachMediaStream\n};\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDgyLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy90cmFjZWFibGVwZWVyY29ubmVjdGlvbi9ub2RlX21vZHVsZXMvd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2VkZ2UvZWRnZV9zaGltLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiAgQ29weXJpZ2h0IChjKSAyMDE2IFRoZSBXZWJSVEMgcHJvamVjdCBhdXRob3JzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqICBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhIEJTRC1zdHlsZSBsaWNlbnNlXG4gKiAgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBvZiB0aGUgc291cmNlXG4gKiAgdHJlZS5cbiAqL1xuIC8qIGVzbGludC1lbnYgbm9kZSAqL1xuJ3VzZSBzdHJpY3QnO1xuXG52YXIgU0RQVXRpbHMgPSByZXF1aXJlKCdzZHAnKTtcbnZhciBsb2dnaW5nID0gcmVxdWlyZSgnLi4vdXRpbHMnKS5sb2c7XG5cbnZhciBlZGdlU2hpbSA9IHtcbiAgc2hpbVBlZXJDb25uZWN0aW9uOiBmdW5jdGlvbigpIHtcbiAgICBpZiAod2luZG93LlJUQ0ljZUdhdGhlcmVyKSB7XG4gICAgICAvLyBPUlRDIGRlZmluZXMgYW4gUlRDSWNlQ2FuZGlkYXRlIG9iamVjdCBidXQgbm8gY29uc3RydWN0b3IuXG4gICAgICAvLyBOb3QgaW1wbGVtZW50ZWQgaW4gRWRnZS5cbiAgICAgIGlmICghd2luZG93LlJUQ0ljZUNhbmRpZGF0ZSkge1xuICAgICAgICB3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlID0gZnVuY3Rpb24oYXJncykge1xuICAgICAgICAgIHJldHVybiBhcmdzO1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgLy8gT1JUQyBkb2VzIG5vdCBoYXZlIGEgc2Vzc2lvbiBkZXNjcmlwdGlvbiBvYmplY3QgYnV0XG4gICAgICAvLyBvdGhlciBicm93c2VycyAoaS5lLiBDaHJvbWUpIHRoYXQgd2lsbCBzdXBwb3J0IGJvdGggUEMgYW5kIE9SVENcbiAgICAgIC8vIGluIHRoZSBmdXR1cmUgbWlnaHQgaGF2ZSB0aGlzIGRlZmluZWQgYWxyZWFkeS5cbiAgICAgIGlmICghd2luZG93LlJUQ1Nlc3Npb25EZXNjcmlwdGlvbikge1xuICAgICAgICB3aW5kb3cuUlRDU2Vzc2lvbkRlc2NyaXB0aW9uID0gZnVuY3Rpb24oYXJncykge1xuICAgICAgICAgIHJldHVybiBhcmdzO1xuICAgICAgICB9O1xuICAgICAgfVxuICAgIH1cblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiA9IGZ1bmN0aW9uKGNvbmZpZykge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgICB2YXIgX2V2ZW50VGFyZ2V0ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xuICAgICAgWydhZGRFdmVudExpc3RlbmVyJywgJ3JlbW92ZUV2ZW50TGlzdGVuZXInLCAnZGlzcGF0Y2hFdmVudCddXG4gICAgICAgICAgLmZvckVhY2goZnVuY3Rpb24obWV0aG9kKSB7XG4gICAgICAgICAgICBzZWxmW21ldGhvZF0gPSBfZXZlbnRUYXJnZXRbbWV0aG9kXS5iaW5kKF9ldmVudFRhcmdldCk7XG4gICAgICAgICAgfSk7XG5cbiAgICAgIHRoaXMub25pY2VjYW5kaWRhdGUgPSBudWxsO1xuICAgICAgdGhpcy5vbmFkZHN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9udHJhY2sgPSBudWxsO1xuICAgICAgdGhpcy5vbnJlbW92ZXN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBudWxsO1xuICAgICAgdGhpcy5vbmRhdGFjaGFubmVsID0gbnVsbDtcblxuICAgICAgdGhpcy5sb2NhbFN0cmVhbXMgPSBbXTtcbiAgICAgIHRoaXMucmVtb3RlU3RyZWFtcyA9IFtdO1xuICAgICAgdGhpcy5nZXRMb2NhbFN0cmVhbXMgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYubG9jYWxTdHJlYW1zO1xuICAgICAgfTtcbiAgICAgIHRoaXMuZ2V0UmVtb3RlU3RyZWFtcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gc2VsZi5yZW1vdGVTdHJlYW1zO1xuICAgICAgfTtcblxuICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICcnLFxuICAgICAgICBzZHA6ICcnXG4gICAgICB9KTtcbiAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24gPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJycsXG4gICAgICAgIHNkcDogJydcbiAgICAgIH0pO1xuICAgICAgdGhpcy5zaWduYWxpbmdTdGF0ZSA9ICdzdGFibGUnO1xuICAgICAgdGhpcy5pY2VDb25uZWN0aW9uU3RhdGUgPSAnbmV3JztcbiAgICAgIHRoaXMuaWNlR2F0aGVyaW5nU3RhdGUgPSAnbmV3JztcblxuICAgICAgdGhpcy5pY2VPcHRpb25zID0ge1xuICAgICAgICBnYXRoZXJQb2xpY3k6ICdhbGwnLFxuICAgICAgICBpY2VTZXJ2ZXJzOiBbXVxuICAgICAgfTtcbiAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmljZVRyYW5zcG9ydFBvbGljeSkge1xuICAgICAgICBzd2l0Y2ggKGNvbmZpZy5pY2VUcmFuc3BvcnRQb2xpY3kpIHtcbiAgICAgICAgICBjYXNlICdhbGwnOlxuICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgIHRoaXMuaWNlT3B0aW9ucy5nYXRoZXJQb2xpY3kgPSBjb25maWcuaWNlVHJhbnNwb3J0UG9saWN5O1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnbm9uZSc6XG4gICAgICAgICAgICAvLyBGSVhNRTogcmVtb3ZlIG9uY2UgaW1wbGVtZW50YXRpb24gYW5kIHNwZWMgaGF2ZSBhZGRlZCB0aGlzLlxuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignaWNlVHJhbnNwb3J0UG9saWN5IFwibm9uZVwiIG5vdCBzdXBwb3J0ZWQnKTtcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgLy8gZG9uJ3Qgc2V0IGljZVRyYW5zcG9ydFBvbGljeS5cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLnVzaW5nQnVuZGxlID0gY29uZmlnICYmIGNvbmZpZy5idW5kbGVQb2xpY3kgPT09ICdtYXgtYnVuZGxlJztcblxuICAgICAgaWYgKGNvbmZpZyAmJiBjb25maWcuaWNlU2VydmVycykge1xuICAgICAgICAvLyBFZGdlIGRvZXMgbm90IGxpa2VcbiAgICAgICAgLy8gMSkgc3R1bjpcbiAgICAgICAgLy8gMikgdHVybjogdGhhdCBkb2VzIG5vdCBoYXZlIGFsbCBvZiB0dXJuOmhvc3Q6cG9ydD90cmFuc3BvcnQ9dWRwXG4gICAgICAgIHZhciBpY2VTZXJ2ZXJzID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShjb25maWcuaWNlU2VydmVycykpO1xuICAgICAgICB0aGlzLmljZU9wdGlvbnMuaWNlU2VydmVycyA9IGljZVNlcnZlcnMuZmlsdGVyKGZ1bmN0aW9uKHNlcnZlcikge1xuICAgICAgICAgIGlmIChzZXJ2ZXIgJiYgc2VydmVyLnVybHMpIHtcbiAgICAgICAgICAgIHZhciB1cmxzID0gc2VydmVyLnVybHM7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHVybHMgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgIHVybHMgPSBbdXJsc107XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB1cmxzID0gdXJscy5maWx0ZXIoZnVuY3Rpb24odXJsKSB7XG4gICAgICAgICAgICAgIHJldHVybiB1cmwuaW5kZXhPZigndHVybjonKSA9PT0gMCAmJlxuICAgICAgICAgICAgICAgICAgdXJsLmluZGV4T2YoJ3RyYW5zcG9ydD11ZHAnKSAhPT0gLTE7XG4gICAgICAgICAgICB9KVswXTtcbiAgICAgICAgICAgIHJldHVybiAhIXVybHM7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIC8vIHBlci10cmFjayBpY2VHYXRoZXJzLCBpY2VUcmFuc3BvcnRzLCBkdGxzVHJhbnNwb3J0cywgcnRwU2VuZGVycywgLi4uXG4gICAgICAvLyBldmVyeXRoaW5nIHRoYXQgaXMgbmVlZGVkIHRvIGRlc2NyaWJlIGEgU0RQIG0tbGluZS5cbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzID0gW107XG5cbiAgICAgIC8vIHNpbmNlIHRoZSBpY2VHYXRoZXJlciBpcyBjdXJyZW50bHkgY3JlYXRlZCBpbiBjcmVhdGVPZmZlciBidXQgd2VcbiAgICAgIC8vIG11c3Qgbm90IGVtaXQgY2FuZGlkYXRlcyB1bnRpbCBhZnRlciBzZXRMb2NhbERlc2NyaXB0aW9uIHdlIGJ1ZmZlclxuICAgICAgLy8gdGhlbSBpbiB0aGlzIGFycmF5LlxuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAvLyBGSVhNRTogbmVlZCB0byBhcHBseSBpY2UgY2FuZGlkYXRlcyBpbiBhIHdheSB3aGljaCBpcyBhc3luYyBidXRcbiAgICAgIC8vIGluLW9yZGVyXG4gICAgICB0aGlzLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIuZm9yRWFjaChmdW5jdGlvbihldmVudCkge1xuICAgICAgICB2YXIgZW5kID0gIWV2ZW50LmNhbmRpZGF0ZSB8fCBPYmplY3Qua2V5cyhldmVudC5jYW5kaWRhdGUpLmxlbmd0aCA9PT0gMDtcbiAgICAgICAgaWYgKGVuZCkge1xuICAgICAgICAgIGZvciAodmFyIGogPSAxOyBqIDwgc2VjdGlvbnMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChzZWN0aW9uc1tqXS5pbmRleE9mKCdcXHJcXG5hPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJykgPT09IC0xKSB7XG4gICAgICAgICAgICAgIHNlY3Rpb25zW2pdICs9ICdhPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZS5pbmRleE9mKCd0eXAgZW5kT2ZDYW5kaWRhdGVzJylcbiAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgIHNlY3Rpb25zW2V2ZW50LmNhbmRpZGF0ZS5zZHBNTGluZUluZGV4ICsgMV0gKz1cbiAgICAgICAgICAgICAgJ2E9JyArIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgKyAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgICAgICBzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgc2VsZi5vbmljZWNhbmRpZGF0ZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFldmVudC5jYW5kaWRhdGUgJiYgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSAhPT0gJ2NvbXBsZXRlJykge1xuICAgICAgICAgIHZhciBjb21wbGV0ZSA9IHNlbGYudHJhbnNjZWl2ZXJzLmV2ZXJ5KGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5zdGF0ZSA9PT0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKGNvbXBsZXRlKSB7XG4gICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkU3RyZWFtID0gZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICAvLyBDbG9uZSBpcyBuZWNlc3NhcnkgZm9yIGxvY2FsIGRlbW9zIG1vc3RseSwgYXR0YWNoaW5nIGRpcmVjdGx5XG4gICAgICAvLyB0byB0d28gZGlmZmVyZW50IHNlbmRlcnMgZG9lcyBub3Qgd29yayAoYnVpbGQgMTA1NDcpLlxuICAgICAgdGhpcy5sb2NhbFN0cmVhbXMucHVzaChzdHJlYW0uY2xvbmUoKSk7XG4gICAgICB0aGlzLl9tYXliZUZpcmVOZWdvdGlhdGlvbk5lZWRlZCgpO1xuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnJlbW92ZVN0cmVhbSA9IGZ1bmN0aW9uKHN0cmVhbSkge1xuICAgICAgdmFyIGlkeCA9IHRoaXMubG9jYWxTdHJlYW1zLmluZGV4T2Yoc3RyZWFtKTtcbiAgICAgIGlmIChpZHggPiAtMSkge1xuICAgICAgICB0aGlzLmxvY2FsU3RyZWFtcy5zcGxpY2UoaWR4LCAxKTtcbiAgICAgICAgdGhpcy5fbWF5YmVGaXJlTmVnb3RpYXRpb25OZWVkZWQoKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTZW5kZXJzID0gZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy50cmFuc2NlaXZlcnMuZmlsdGVyKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIHJldHVybiAhIXRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgIH0pXG4gICAgICAubWFwKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIHJldHVybiB0cmFuc2NlaXZlci5ydHBTZW5kZXI7XG4gICAgICB9KTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRSZWNlaXZlcnMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiB0aGlzLnRyYW5zY2VpdmVycy5maWx0ZXIoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuICEhdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXI7XG4gICAgICB9KVxuICAgICAgLm1hcChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXI7XG4gICAgICB9KTtcbiAgICB9O1xuXG4gICAgLy8gRGV0ZXJtaW5lcyB0aGUgaW50ZXJzZWN0aW9uIG9mIGxvY2FsIGFuZCByZW1vdGUgY2FwYWJpbGl0aWVzLlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2dldENvbW1vbkNhcGFiaWxpdGllcyA9XG4gICAgICAgIGZ1bmN0aW9uKGxvY2FsQ2FwYWJpbGl0aWVzLCByZW1vdGVDYXBhYmlsaXRpZXMpIHtcbiAgICAgICAgICB2YXIgY29tbW9uQ2FwYWJpbGl0aWVzID0ge1xuICAgICAgICAgICAgY29kZWNzOiBbXSxcbiAgICAgICAgICAgIGhlYWRlckV4dGVuc2lvbnM6IFtdLFxuICAgICAgICAgICAgZmVjTWVjaGFuaXNtczogW11cbiAgICAgICAgICB9O1xuICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzLmNvZGVjcy5mb3JFYWNoKGZ1bmN0aW9uKGxDb2RlYykge1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZW1vdGVDYXBhYmlsaXRpZXMuY29kZWNzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgIHZhciByQ29kZWMgPSByZW1vdGVDYXBhYmlsaXRpZXMuY29kZWNzW2ldO1xuICAgICAgICAgICAgICBpZiAobENvZGVjLm5hbWUudG9Mb3dlckNhc2UoKSA9PT0gckNvZGVjLm5hbWUudG9Mb3dlckNhc2UoKSAmJlxuICAgICAgICAgICAgICAgICAgbENvZGVjLmNsb2NrUmF0ZSA9PT0gckNvZGVjLmNsb2NrUmF0ZSAmJlxuICAgICAgICAgICAgICAgICAgbENvZGVjLm51bUNoYW5uZWxzID09PSByQ29kZWMubnVtQ2hhbm5lbHMpIHtcbiAgICAgICAgICAgICAgICAvLyBwdXNoIHJDb2RlYyBzbyB3ZSByZXBseSB3aXRoIG9mZmVyZXIgcGF5bG9hZCB0eXBlXG4gICAgICAgICAgICAgICAgY29tbW9uQ2FwYWJpbGl0aWVzLmNvZGVjcy5wdXNoKHJDb2RlYyk7XG5cbiAgICAgICAgICAgICAgICAvLyBGSVhNRTogYWxzbyBuZWVkIHRvIGRldGVybWluZSBpbnRlcnNlY3Rpb24gYmV0d2VlblxuICAgICAgICAgICAgICAgIC8vIC5ydGNwRmVlZGJhY2sgYW5kIC5wYXJhbWV0ZXJzXG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzLmhlYWRlckV4dGVuc2lvbnNcbiAgICAgICAgICAgICAgLmZvckVhY2goZnVuY3Rpb24obEhlYWRlckV4dGVuc2lvbikge1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmVtb3RlQ2FwYWJpbGl0aWVzLmhlYWRlckV4dGVuc2lvbnMubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICAgaSsrKSB7XG4gICAgICAgICAgICAgICAgICB2YXIgckhlYWRlckV4dGVuc2lvbiA9IHJlbW90ZUNhcGFiaWxpdGllcy5oZWFkZXJFeHRlbnNpb25zW2ldO1xuICAgICAgICAgICAgICAgICAgaWYgKGxIZWFkZXJFeHRlbnNpb24udXJpID09PSBySGVhZGVyRXh0ZW5zaW9uLnVyaSkge1xuICAgICAgICAgICAgICAgICAgICBjb21tb25DYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9ucy5wdXNoKHJIZWFkZXJFeHRlbnNpb24pO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgLy8gRklYTUU6IGZlY01lY2hhbmlzbXNcbiAgICAgICAgICByZXR1cm4gY29tbW9uQ2FwYWJpbGl0aWVzO1xuICAgICAgICB9O1xuXG4gICAgLy8gQ3JlYXRlIElDRSBnYXRoZXJlciwgSUNFIHRyYW5zcG9ydCBhbmQgRFRMUyB0cmFuc3BvcnQuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fY3JlYXRlSWNlQW5kRHRsc1RyYW5zcG9ydHMgPVxuICAgICAgICBmdW5jdGlvbihtaWQsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgdmFyIGljZUdhdGhlcmVyID0gbmV3IFJUQ0ljZUdhdGhlcmVyKHNlbGYuaWNlT3B0aW9ucyk7XG4gICAgICAgICAgdmFyIGljZVRyYW5zcG9ydCA9IG5ldyBSVENJY2VUcmFuc3BvcnQoaWNlR2F0aGVyZXIpO1xuICAgICAgICAgIGljZUdhdGhlcmVyLm9ubG9jYWxjYW5kaWRhdGUgPSBmdW5jdGlvbihldnQpIHtcbiAgICAgICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnaWNlY2FuZGlkYXRlJyk7XG4gICAgICAgICAgICBldmVudC5jYW5kaWRhdGUgPSB7c2RwTWlkOiBtaWQsIHNkcE1MaW5lSW5kZXg6IHNkcE1MaW5lSW5kZXh9O1xuXG4gICAgICAgICAgICB2YXIgY2FuZCA9IGV2dC5jYW5kaWRhdGU7XG4gICAgICAgICAgICB2YXIgZW5kID0gIWNhbmQgfHwgT2JqZWN0LmtleXMoY2FuZCkubGVuZ3RoID09PSAwO1xuICAgICAgICAgICAgLy8gRWRnZSBlbWl0cyBhbiBlbXB0eSBvYmplY3QgZm9yIFJUQ0ljZUNhbmRpZGF0ZUNvbXBsZXRl4oClXG4gICAgICAgICAgICBpZiAoZW5kKSB7XG4gICAgICAgICAgICAgIC8vIHBvbHlmaWxsIHNpbmNlIFJUQ0ljZUdhdGhlcmVyLnN0YXRlIGlzIG5vdCBpbXBsZW1lbnRlZCBpblxuICAgICAgICAgICAgICAvLyBFZGdlIDEwNTQ3IHlldC5cbiAgICAgICAgICAgICAgaWYgKGljZUdhdGhlcmVyLnN0YXRlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBpY2VHYXRoZXJlci5zdGF0ZSA9ICdjb21wbGV0ZWQnO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgLy8gRW1pdCBhIGNhbmRpZGF0ZSB3aXRoIHR5cGUgZW5kT2ZDYW5kaWRhdGVzIHRvIG1ha2UgdGhlIHNhbXBsZXNcbiAgICAgICAgICAgICAgLy8gd29yay4gRWRnZSByZXF1aXJlcyBhZGRJY2VDYW5kaWRhdGUgd2l0aCB0aGlzIGVtcHR5IGNhbmRpZGF0ZVxuICAgICAgICAgICAgICAvLyB0byBzdGFydCBjaGVja2luZy4gVGhlIHJlYWwgc29sdXRpb24gaXMgdG8gc2lnbmFsXG4gICAgICAgICAgICAgIC8vIGVuZC1vZi1jYW5kaWRhdGVzIHRvIHRoZSBvdGhlciBzaWRlIHdoZW4gZ2V0dGluZyB0aGUgbnVsbFxuICAgICAgICAgICAgICAvLyBjYW5kaWRhdGUgYnV0IHNvbWUgYXBwcyAobGlrZSB0aGUgc2FtcGxlcykgZG9uJ3QgZG8gdGhhdC5cbiAgICAgICAgICAgICAgZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZSA9XG4gICAgICAgICAgICAgICAgICAnY2FuZGlkYXRlOjEgMSB1ZHAgMSAwLjAuMC4wIDkgdHlwIGVuZE9mQ2FuZGlkYXRlcyc7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAvLyBSVENJY2VDYW5kaWRhdGUgZG9lc24ndCBoYXZlIGEgY29tcG9uZW50LCBuZWVkcyB0byBiZSBhZGRlZFxuICAgICAgICAgICAgICBjYW5kLmNvbXBvbmVudCA9IGljZVRyYW5zcG9ydC5jb21wb25lbnQgPT09ICdSVENQJyA/IDIgOiAxO1xuICAgICAgICAgICAgICBldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlID0gU0RQVXRpbHMud3JpdGVDYW5kaWRhdGUoY2FuZCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSBsb2NhbCBkZXNjcmlwdGlvbi5cbiAgICAgICAgICAgIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgICAgICBpZiAoZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZS5pbmRleE9mKCd0eXAgZW5kT2ZDYW5kaWRhdGVzJylcbiAgICAgICAgICAgICAgICA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgc2VjdGlvbnNbZXZlbnQuY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXggKyAxXSArPVxuICAgICAgICAgICAgICAgICAgJ2E9JyArIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHNlY3Rpb25zW2V2ZW50LmNhbmRpZGF0ZS5zZHBNTGluZUluZGV4ICsgMV0gKz1cbiAgICAgICAgICAgICAgICAgICdhPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlbGYubG9jYWxEZXNjcmlwdGlvbi5zZHAgPSBzZWN0aW9ucy5qb2luKCcnKTtcblxuICAgICAgICAgICAgdmFyIGNvbXBsZXRlID0gc2VsZi50cmFuc2NlaXZlcnMuZXZlcnkoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyICYmXG4gICAgICAgICAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5zdGF0ZSA9PT0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgLy8gRW1pdCBjYW5kaWRhdGUgaWYgbG9jYWxEZXNjcmlwdGlvbiBpcyBzZXQuXG4gICAgICAgICAgICAvLyBBbHNvIGVtaXRzIG51bGwgY2FuZGlkYXRlIHdoZW4gYWxsIGdhdGhlcmVycyBhcmUgY29tcGxldGUuXG4gICAgICAgICAgICBzd2l0Y2ggKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUpIHtcbiAgICAgICAgICAgICAgY2FzZSAnbmV3JzpcbiAgICAgICAgICAgICAgICBzZWxmLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIucHVzaChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKGVuZCAmJiBjb21wbGV0ZSkge1xuICAgICAgICAgICAgICAgICAgc2VsZi5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyLnB1c2goXG4gICAgICAgICAgICAgICAgICAgICAgbmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlICdnYXRoZXJpbmcnOlxuICAgICAgICAgICAgICAgIHNlbGYuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMoKTtcbiAgICAgICAgICAgICAgICBzZWxmLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgICAgICAgICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLm9uaWNlY2FuZGlkYXRlKGV2ZW50KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKSk7XG4gICAgICAgICAgICAgICAgICBpZiAoc2VsZi5vbmljZWNhbmRpZGF0ZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLm9uaWNlY2FuZGlkYXRlKG5ldyBFdmVudCgnaWNlY2FuZGlkYXRlJykpO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdjb21wbGV0ZSc7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlICdjb21wbGV0ZSc6XG4gICAgICAgICAgICAgICAgLy8gc2hvdWxkIG5vdCBoYXBwZW4uLi4gY3VycmVudGx5IVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBkZWZhdWx0OiAvLyBuby1vcC5cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9O1xuICAgICAgICAgIGljZVRyYW5zcG9ydC5vbmljZXN0YXRlY2hhbmdlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBzZWxmLl91cGRhdGVDb25uZWN0aW9uU3RhdGUoKTtcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgdmFyIGR0bHNUcmFuc3BvcnQgPSBuZXcgUlRDRHRsc1RyYW5zcG9ydChpY2VUcmFuc3BvcnQpO1xuICAgICAgICAgIGR0bHNUcmFuc3BvcnQub25kdGxzc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHNlbGYuX3VwZGF0ZUNvbm5lY3Rpb25TdGF0ZSgpO1xuICAgICAgICAgIH07XG4gICAgICAgICAgZHRsc1RyYW5zcG9ydC5vbmVycm9yID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAvLyBvbmVycm9yIGRvZXMgbm90IHNldCBzdGF0ZSB0byBmYWlsZWQgYnkgaXRzZWxmLlxuICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGF0ZSA9ICdmYWlsZWQnO1xuICAgICAgICAgICAgc2VsZi5fdXBkYXRlQ29ubmVjdGlvblN0YXRlKCk7XG4gICAgICAgICAgfTtcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBpY2VHYXRoZXJlcjogaWNlR2F0aGVyZXIsXG4gICAgICAgICAgICBpY2VUcmFuc3BvcnQ6IGljZVRyYW5zcG9ydCxcbiAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IGR0bHNUcmFuc3BvcnRcbiAgICAgICAgICB9O1xuICAgICAgICB9O1xuXG4gICAgLy8gU3RhcnQgdGhlIFJUUCBTZW5kZXIgYW5kIFJlY2VpdmVyIGZvciBhIHRyYW5zY2VpdmVyLlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX3RyYW5zY2VpdmUgPSBmdW5jdGlvbih0cmFuc2NlaXZlcixcbiAgICAgICAgc2VuZCwgcmVjdikge1xuICAgICAgdmFyIHBhcmFtcyA9IHRoaXMuX2dldENvbW1vbkNhcGFiaWxpdGllcyh0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXMpO1xuICAgICAgaWYgKHNlbmQgJiYgdHJhbnNjZWl2ZXIucnRwU2VuZGVyKSB7XG4gICAgICAgIHBhcmFtcy5lbmNvZGluZ3MgPSB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICBwYXJhbXMucnRjcCA9IHtcbiAgICAgICAgICBjbmFtZTogU0RQVXRpbHMubG9jYWxDTmFtZVxuICAgICAgICB9O1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGgpIHtcbiAgICAgICAgICBwYXJhbXMucnRjcC5zc3JjID0gdHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVyc1swXS5zc3JjO1xuICAgICAgICB9XG4gICAgICAgIHRyYW5zY2VpdmVyLnJ0cFNlbmRlci5zZW5kKHBhcmFtcyk7XG4gICAgICB9XG4gICAgICBpZiAocmVjdiAmJiB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcikge1xuICAgICAgICBwYXJhbXMuZW5jb2RpbmdzID0gdHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVycztcbiAgICAgICAgcGFyYW1zLnJ0Y3AgPSB7XG4gICAgICAgICAgY25hbWU6IHRyYW5zY2VpdmVyLmNuYW1lXG4gICAgICAgIH07XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzLmxlbmd0aCkge1xuICAgICAgICAgIHBhcmFtcy5ydGNwLnNzcmMgPSB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmM7XG4gICAgICAgIH1cbiAgICAgICAgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIucmVjZWl2ZShwYXJhbXMpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnNldExvY2FsRGVzY3JpcHRpb24gPVxuICAgICAgICBmdW5jdGlvbihkZXNjcmlwdGlvbikge1xuICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICB2YXIgc2VjdGlvbnM7XG4gICAgICAgICAgdmFyIHNlc3Npb25wYXJ0O1xuICAgICAgICAgIGlmIChkZXNjcmlwdGlvbi50eXBlID09PSAnb2ZmZXInKSB7XG4gICAgICAgICAgICAvLyBGSVhNRTogV2hhdCB3YXMgdGhlIHB1cnBvc2Ugb2YgdGhpcyBlbXB0eSBpZiBzdGF0ZW1lbnQ/XG4gICAgICAgICAgICAvLyBpZiAoIXRoaXMuX3BlbmRpbmdPZmZlcikge1xuICAgICAgICAgICAgLy8gfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICh0aGlzLl9wZW5kaW5nT2ZmZXIpIHtcbiAgICAgICAgICAgICAgLy8gVkVSWSBsaW1pdGVkIHN1cHBvcnQgZm9yIFNEUCBtdW5naW5nLiBMaW1pdGVkIHRvOlxuICAgICAgICAgICAgICAvLyAqIGNoYW5naW5nIHRoZSBvcmRlciBvZiBjb2RlY3NcbiAgICAgICAgICAgICAgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKGRlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgICAgICAgIHNlc3Npb25wYXJ0ID0gc2VjdGlvbnMuc2hpZnQoKTtcbiAgICAgICAgICAgICAgc2VjdGlvbnMuZm9yRWFjaChmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgICAgICAgICB2YXIgY2FwcyA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgICAgIHNlbGYuX3BlbmRpbmdPZmZlcltzZHBNTGluZUluZGV4XS5sb2NhbENhcGFiaWxpdGllcyA9IGNhcHM7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB0aGlzLnRyYW5zY2VpdmVycyA9IHRoaXMuX3BlbmRpbmdPZmZlcjtcbiAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuX3BlbmRpbmdPZmZlcjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKGRlc2NyaXB0aW9uLnR5cGUgPT09ICdhbnN3ZXInKSB7XG4gICAgICAgICAgICBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgc2Vzc2lvbnBhcnQgPSBzZWN0aW9ucy5zaGlmdCgpO1xuICAgICAgICAgICAgdmFyIGlzSWNlTGl0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAgICdhPWljZS1saXRlJykubGVuZ3RoID4gMDtcbiAgICAgICAgICAgIHNlY3Rpb25zLmZvckVhY2goZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZHBNTGluZUluZGV4KSB7XG4gICAgICAgICAgICAgIHZhciB0cmFuc2NlaXZlciA9IHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICAgICAgICB2YXIgaWNlR2F0aGVyZXIgPSB0cmFuc2NlaXZlci5pY2VHYXRoZXJlcjtcbiAgICAgICAgICAgICAgdmFyIGljZVRyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgICAgdmFyIGR0bHNUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5kdGxzVHJhbnNwb3J0O1xuICAgICAgICAgICAgICB2YXIgbG9jYWxDYXBhYmlsaXRpZXMgPSB0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcztcbiAgICAgICAgICAgICAgdmFyIHJlbW90ZUNhcGFiaWxpdGllcyA9IHRyYW5zY2VpdmVyLnJlbW90ZUNhcGFiaWxpdGllcztcbiAgICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWVkaWFTZWN0aW9uLnNwbGl0KCdcXG4nLCAxKVswXVxuICAgICAgICAgICAgICAgICAgLnNwbGl0KCcgJywgMilbMV0gPT09ICcwJztcblxuICAgICAgICAgICAgICBpZiAoIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKFxuICAgICAgICAgICAgICAgICAgICBtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgICBpZiAoaXNJY2VMaXRlKSB7XG4gICAgICAgICAgICAgICAgICB2YXIgY2FuZHMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWNhbmRpZGF0ZTonKVxuICAgICAgICAgICAgICAgICAgLm1hcChmdW5jdGlvbihjYW5kKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAuZmlsdGVyKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhbmQuY29tcG9uZW50ID09PSAnMSc7XG4gICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgIC8vIGljZS1saXRlIG9ubHkgaW5jbHVkZXMgaG9zdCBjYW5kaWRhdGVzIGluIHRoZSBTRFAgc28gd2UgY2FuXG4gICAgICAgICAgICAgICAgICAvLyB1c2Ugc2V0UmVtb3RlQ2FuZGlkYXRlcyAod2hpY2ggaW1wbGllcyBhblxuICAgICAgICAgICAgICAgICAgLy8gUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGUpXG4gICAgICAgICAgICAgICAgICBpZiAoY2FuZHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUR0bHNQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMoXG4gICAgICAgICAgICAgICAgICAgIG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpO1xuICAgICAgICAgICAgICAgIGlmIChpc0ljZUxpdGUpIHtcbiAgICAgICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnc2VydmVyJztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgaWNlVHJhbnNwb3J0LnN0YXJ0KGljZUdhdGhlcmVyLCByZW1vdGVJY2VQYXJhbWV0ZXJzLFxuICAgICAgICAgICAgICAgICAgICAgIGlzSWNlTGl0ZSA/ICdjb250cm9sbGluZycgOiAnY29udHJvbGxlZCcpO1xuICAgICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIGludGVyc2VjdGlvbiBvZiBjYXBhYmlsaXRpZXMuXG4gICAgICAgICAgICAgICAgdmFyIHBhcmFtcyA9IHNlbGYuX2dldENvbW1vbkNhcGFiaWxpdGllcyhsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzKTtcblxuICAgICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBTZW5kZXIuIFRoZSBSVENSdHBSZWNlaXZlciBmb3IgdGhpc1xuICAgICAgICAgICAgICAgIC8vIHRyYW5zY2VpdmVyIGhhcyBhbHJlYWR5IGJlZW4gc3RhcnRlZCBpbiBzZXRSZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgICBwYXJhbXMuY29kZWNzLmxlbmd0aCA+IDAsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0ge1xuICAgICAgICAgICAgdHlwZTogZGVzY3JpcHRpb24udHlwZSxcbiAgICAgICAgICAgIHNkcDogZGVzY3JpcHRpb24uc2RwXG4gICAgICAgICAgfTtcbiAgICAgICAgICBzd2l0Y2ggKGRlc2NyaXB0aW9uLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ29mZmVyJzpcbiAgICAgICAgICAgICAgdGhpcy5fdXBkYXRlU2lnbmFsaW5nU3RhdGUoJ2hhdmUtbG9jYWwtb2ZmZXInKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbnN3ZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnc3RhYmxlJyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigndW5zdXBwb3J0ZWQgdHlwZSBcIicgKyBkZXNjcmlwdGlvbi50eXBlICtcbiAgICAgICAgICAgICAgICAgICdcIicpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIElmIGEgc3VjY2VzcyBjYWxsYmFjayB3YXMgcHJvdmlkZWQsIGVtaXQgSUNFIGNhbmRpZGF0ZXMgYWZ0ZXIgaXRcbiAgICAgICAgICAvLyBoYXMgYmVlbiBleGVjdXRlZC4gT3RoZXJ3aXNlLCBlbWl0IGNhbGxiYWNrIGFmdGVyIHRoZSBQcm9taXNlIGlzXG4gICAgICAgICAgLy8gcmVzb2x2ZWQuXG4gICAgICAgICAgdmFyIGhhc0NhbGxiYWNrID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiZcbiAgICAgICAgICAgIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbic7XG4gICAgICAgICAgaWYgKGhhc0NhbGxiYWNrKSB7XG4gICAgICAgICAgICB2YXIgY2IgPSBhcmd1bWVudHNbMV07XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgY2IoKTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHNlbGYuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMoKTtcbiAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB2YXIgcCA9IFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgICAgIHAudGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmICghaGFzQ2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFVzdWFsbHkgY2FuZGlkYXRlcyB3aWxsIGJlIGVtaXR0ZWQgZWFybGllci5cbiAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcy5iaW5kKHNlbGYpLCA1MDApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybiBwO1xuICAgICAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9XG4gICAgICAgIGZ1bmN0aW9uKGRlc2NyaXB0aW9uKSB7XG4gICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgIHZhciBzdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICAgICAgICB2YXIgcmVjZWl2ZXJMaXN0ID0gW107XG4gICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhkZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgIHZhciBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgdmFyIGlzSWNlTGl0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1pY2UtbGl0ZScpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgdGhpcy51c2luZ0J1bmRsZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1ncm91cDpCVU5ETEUgJykubGVuZ3RoID4gMDtcbiAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIG1saW5lID0gbGluZXNbMF0uc3Vic3RyKDIpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICB2YXIga2luZCA9IG1saW5lWzBdO1xuICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWxpbmVbMV0gPT09ICcwJztcbiAgICAgICAgICAgIHZhciBkaXJlY3Rpb24gPSBTRFBVdGlscy5nZXREaXJlY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCk7XG5cbiAgICAgICAgICAgIHZhciB0cmFuc2NlaXZlcjtcbiAgICAgICAgICAgIHZhciBpY2VHYXRoZXJlcjtcbiAgICAgICAgICAgIHZhciBpY2VUcmFuc3BvcnQ7XG4gICAgICAgICAgICB2YXIgZHRsc1RyYW5zcG9ydDtcbiAgICAgICAgICAgIHZhciBydHBTZW5kZXI7XG4gICAgICAgICAgICB2YXIgcnRwUmVjZWl2ZXI7XG4gICAgICAgICAgICB2YXIgc2VuZEVuY29kaW5nUGFyYW1ldGVycztcbiAgICAgICAgICAgIHZhciByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzO1xuXG4gICAgICAgICAgICB2YXIgdHJhY2s7XG4gICAgICAgICAgICAvLyBGSVhNRTogZW5zdXJlIHRoZSBtZWRpYVNlY3Rpb24gaGFzIHJ0Y3AtbXV4IHNldC5cbiAgICAgICAgICAgIHZhciByZW1vdGVDYXBhYmlsaXRpZXMgPSBTRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMobWVkaWFTZWN0aW9uKTtcbiAgICAgICAgICAgIHZhciByZW1vdGVJY2VQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgdmFyIHJlbW90ZUR0bHNQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgaWYgKCFyZWplY3RlZCkge1xuICAgICAgICAgICAgICByZW1vdGVJY2VQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0SWNlUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24sXG4gICAgICAgICAgICAgICAgICBzZXNzaW9ucGFydCk7XG4gICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMobWVkaWFTZWN0aW9uLFxuICAgICAgICAgICAgICAgICAgc2Vzc2lvbnBhcnQpO1xuICAgICAgICAgICAgICByZW1vdGVEdGxzUGFyYW1ldGVycy5yb2xlID0gJ2NsaWVudCc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzID1cbiAgICAgICAgICAgICAgICBTRFBVdGlscy5wYXJzZVJ0cEVuY29kaW5nUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuXG4gICAgICAgICAgICB2YXIgbWlkID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1taWQ6Jyk7XG4gICAgICAgICAgICBpZiAobWlkLmxlbmd0aCkge1xuICAgICAgICAgICAgICBtaWQgPSBtaWRbMF0uc3Vic3RyKDYpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgbWlkID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBjbmFtZTtcbiAgICAgICAgICAgIC8vIEdldHMgdGhlIGZpcnN0IFNTUkMuIE5vdGUgdGhhdCB3aXRoIFJUWCB0aGVyZSBtaWdodCBiZSBtdWx0aXBsZVxuICAgICAgICAgICAgLy8gU1NSQ3MuXG4gICAgICAgICAgICB2YXIgcmVtb3RlU3NyYyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgICAgICAgICAgICAgIC5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbHRlcihmdW5jdGlvbihvYmopIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBvYmouYXR0cmlidXRlID09PSAnY25hbWUnO1xuICAgICAgICAgICAgICAgIH0pWzBdO1xuICAgICAgICAgICAgaWYgKHJlbW90ZVNzcmMpIHtcbiAgICAgICAgICAgICAgY25hbWUgPSByZW1vdGVTc3JjLnZhbHVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgaXNDb21wbGV0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAnYT1lbmQtb2YtY2FuZGlkYXRlcycpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgICB2YXIgY2FuZHMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWNhbmRpZGF0ZTonKVxuICAgICAgICAgICAgICAgIC5tYXAoZnVuY3Rpb24oY2FuZCkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlQ2FuZGlkYXRlKGNhbmQpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbHRlcihmdW5jdGlvbihjYW5kKSB7XG4gICAgICAgICAgICAgICAgICByZXR1cm4gY2FuZC5jb21wb25lbnQgPT09ICcxJztcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGlmIChkZXNjcmlwdGlvbi50eXBlID09PSAnb2ZmZXInICYmICFyZWplY3RlZCkge1xuICAgICAgICAgICAgICB2YXIgdHJhbnNwb3J0cyA9IHNlbGYudXNpbmdCdW5kbGUgJiYgc2RwTUxpbmVJbmRleCA+IDAgPyB7XG4gICAgICAgICAgICAgICAgaWNlR2F0aGVyZXI6IHNlbGYudHJhbnNjZWl2ZXJzWzBdLmljZUdhdGhlcmVyLFxuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydDogc2VsZi50cmFuc2NlaXZlcnNbMF0uaWNlVHJhbnNwb3J0LFxuICAgICAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHNlbGYudHJhbnNjZWl2ZXJzWzBdLmR0bHNUcmFuc3BvcnRcbiAgICAgICAgICAgICAgfSA6IHNlbGYuX2NyZWF0ZUljZUFuZER0bHNUcmFuc3BvcnRzKG1pZCwgc2RwTUxpbmVJbmRleCk7XG5cbiAgICAgICAgICAgICAgaWYgKGlzQ29tcGxldGUpIHtcbiAgICAgICAgICAgICAgICB0cmFuc3BvcnRzLmljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzID0gUlRDUnRwUmVjZWl2ZXIuZ2V0Q2FwYWJpbGl0aWVzKGtpbmQpO1xuICAgICAgICAgICAgICBzZW5kRW5jb2RpbmdQYXJhbWV0ZXJzID0gW3tcbiAgICAgICAgICAgICAgICBzc3JjOiAoMiAqIHNkcE1MaW5lSW5kZXggKyAyKSAqIDEwMDFcbiAgICAgICAgICAgICAgfV07XG5cbiAgICAgICAgICAgICAgcnRwUmVjZWl2ZXIgPSBuZXcgUlRDUnRwUmVjZWl2ZXIodHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LCBraW5kKTtcblxuICAgICAgICAgICAgICB0cmFjayA9IHJ0cFJlY2VpdmVyLnRyYWNrO1xuICAgICAgICAgICAgICByZWNlaXZlckxpc3QucHVzaChbdHJhY2ssIHJ0cFJlY2VpdmVyXSk7XG4gICAgICAgICAgICAgIC8vIEZJWE1FOiBub3QgY29ycmVjdCB3aGVuIHRoZXJlIGFyZSBtdWx0aXBsZSBzdHJlYW1zIGJ1dCB0aGF0IGlzXG4gICAgICAgICAgICAgIC8vIG5vdCBjdXJyZW50bHkgc3VwcG9ydGVkIGluIHRoaXMgc2hpbS5cbiAgICAgICAgICAgICAgc3RyZWFtLmFkZFRyYWNrKHRyYWNrKTtcblxuICAgICAgICAgICAgICAvLyBGSVhNRTogbG9vayBhdCBkaXJlY3Rpb24uXG4gICAgICAgICAgICAgIGlmIChzZWxmLmxvY2FsU3RyZWFtcy5sZW5ndGggPiAwICYmXG4gICAgICAgICAgICAgICAgICBzZWxmLmxvY2FsU3RyZWFtc1swXS5nZXRUcmFja3MoKS5sZW5ndGggPj0gc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBhY3R1YWxseSBtb3JlIGNvbXBsaWNhdGVkLCBuZWVkcyB0byBtYXRjaCB0eXBlcyBldGNcbiAgICAgICAgICAgICAgICB2YXIgbG9jYWx0cmFjayA9IHNlbGYubG9jYWxTdHJlYW1zWzBdXG4gICAgICAgICAgICAgICAgICAgIC5nZXRUcmFja3MoKVtzZHBNTGluZUluZGV4XTtcbiAgICAgICAgICAgICAgICBydHBTZW5kZXIgPSBuZXcgUlRDUnRwU2VuZGVyKGxvY2FsdHJhY2ssXG4gICAgICAgICAgICAgICAgICAgIHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICAgICAgICBpY2VHYXRoZXJlcjogdHJhbnNwb3J0cy5pY2VHYXRoZXJlcixcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuaWNlVHJhbnNwb3J0LFxuICAgICAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCxcbiAgICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllczogbG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzOiByZW1vdGVDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICAgICAgcnRwU2VuZGVyOiBydHBTZW5kZXIsXG4gICAgICAgICAgICAgICAgcnRwUmVjZWl2ZXI6IHJ0cFJlY2VpdmVyLFxuICAgICAgICAgICAgICAgIGtpbmQ6IGtpbmQsXG4gICAgICAgICAgICAgICAgbWlkOiBtaWQsXG4gICAgICAgICAgICAgICAgY25hbWU6IGNuYW1lLFxuICAgICAgICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnM6IHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMsXG4gICAgICAgICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVyczogcmVjdkVuY29kaW5nUGFyYW1ldGVyc1xuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAvLyBTdGFydCB0aGUgUlRDUnRwUmVjZWl2ZXIgbm93LiBUaGUgUlRQU2VuZGVyIGlzIHN0YXJ0ZWQgaW5cbiAgICAgICAgICAgICAgLy8gc2V0TG9jYWxEZXNjcmlwdGlvbi5cbiAgICAgICAgICAgICAgc2VsZi5fdHJhbnNjZWl2ZShzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSxcbiAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID09PSAnc2VuZHJlY3YnIHx8IGRpcmVjdGlvbiA9PT0gJ3NlbmRvbmx5Jyk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGRlc2NyaXB0aW9uLnR5cGUgPT09ICdhbnN3ZXInICYmICFyZWplY3RlZCkge1xuICAgICAgICAgICAgICB0cmFuc2NlaXZlciA9IHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICAgICAgICBpY2VHYXRoZXJlciA9IHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyO1xuICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5kdGxzVHJhbnNwb3J0O1xuICAgICAgICAgICAgICBydHBTZW5kZXIgPSB0cmFuc2NlaXZlci5ydHBTZW5kZXI7XG4gICAgICAgICAgICAgIHJ0cFJlY2VpdmVyID0gdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXI7XG4gICAgICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMgPSB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllcyA9IHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzO1xuXG4gICAgICAgICAgICAgIHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdLnJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMgPVxuICAgICAgICAgICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVycztcbiAgICAgICAgICAgICAgc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF0ucmVtb3RlQ2FwYWJpbGl0aWVzID1cbiAgICAgICAgICAgICAgICAgIHJlbW90ZUNhcGFiaWxpdGllcztcbiAgICAgICAgICAgICAgc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF0uY25hbWUgPSBjbmFtZTtcblxuICAgICAgICAgICAgICBpZiAoKGlzSWNlTGl0ZSB8fCBpc0NvbXBsZXRlKSAmJiBjYW5kcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQuc2V0UmVtb3RlQ2FuZGlkYXRlcyhjYW5kcyk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKCFzZWxmLnVzaW5nQnVuZGxlIHx8IHNkcE1MaW5lSW5kZXggPT09IDApIHtcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQuc3RhcnQoaWNlR2F0aGVyZXIsIHJlbW90ZUljZVBhcmFtZXRlcnMsXG4gICAgICAgICAgICAgICAgICAgICdjb250cm9sbGluZycpO1xuICAgICAgICAgICAgICAgIGR0bHNUcmFuc3BvcnQuc3RhcnQocmVtb3RlRHRsc1BhcmFtZXRlcnMpO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgc2VsZi5fdHJhbnNjZWl2ZSh0cmFuc2NlaXZlcixcbiAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdyZWN2b25seScsXG4gICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPT09ICdzZW5kcmVjdicgfHwgZGlyZWN0aW9uID09PSAnc2VuZG9ubHknKTtcblxuICAgICAgICAgICAgICBpZiAocnRwUmVjZWl2ZXIgJiZcbiAgICAgICAgICAgICAgICAgIChkaXJlY3Rpb24gPT09ICdzZW5kcmVjdicgfHwgZGlyZWN0aW9uID09PSAnc2VuZG9ubHknKSkge1xuICAgICAgICAgICAgICAgIHRyYWNrID0gcnRwUmVjZWl2ZXIudHJhY2s7XG4gICAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LnB1c2goW3RyYWNrLCBydHBSZWNlaXZlcl0pO1xuICAgICAgICAgICAgICAgIHN0cmVhbS5hZGRUcmFjayh0cmFjayk7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGFjdHVhbGx5IHRoZSByZWNlaXZlciBzaG91bGQgYmUgY3JlYXRlZCBsYXRlci5cbiAgICAgICAgICAgICAgICBkZWxldGUgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXI7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24gPSB7XG4gICAgICAgICAgICB0eXBlOiBkZXNjcmlwdGlvbi50eXBlLFxuICAgICAgICAgICAgc2RwOiBkZXNjcmlwdGlvbi5zZHBcbiAgICAgICAgICB9O1xuICAgICAgICAgIHN3aXRjaCAoZGVzY3JpcHRpb24udHlwZSkge1xuICAgICAgICAgICAgY2FzZSAnb2ZmZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnaGF2ZS1yZW1vdGUtb2ZmZXInKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbnN3ZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnc3RhYmxlJyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigndW5zdXBwb3J0ZWQgdHlwZSBcIicgKyBkZXNjcmlwdGlvbi50eXBlICtcbiAgICAgICAgICAgICAgICAgICdcIicpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc3RyZWFtLmdldFRyYWNrcygpLmxlbmd0aCkge1xuICAgICAgICAgICAgc2VsZi5yZW1vdGVTdHJlYW1zLnB1c2goc3RyZWFtKTtcbiAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ2FkZHN0cmVhbScpO1xuICAgICAgICAgICAgICBldmVudC5zdHJlYW0gPSBzdHJlYW07XG4gICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgIGlmIChzZWxmLm9uYWRkc3RyZWFtICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLm9uYWRkc3RyZWFtKGV2ZW50KTtcbiAgICAgICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHJlY2VpdmVyTGlzdC5mb3JFYWNoKGZ1bmN0aW9uKGl0ZW0pIHtcbiAgICAgICAgICAgICAgICB2YXIgdHJhY2sgPSBpdGVtWzBdO1xuICAgICAgICAgICAgICAgIHZhciByZWNlaXZlciA9IGl0ZW1bMV07XG4gICAgICAgICAgICAgICAgdmFyIHRyYWNrRXZlbnQgPSBuZXcgRXZlbnQoJ3RyYWNrJyk7XG4gICAgICAgICAgICAgICAgdHJhY2tFdmVudC50cmFjayA9IHRyYWNrO1xuICAgICAgICAgICAgICAgIHRyYWNrRXZlbnQucmVjZWl2ZXIgPSByZWNlaXZlcjtcbiAgICAgICAgICAgICAgICB0cmFja0V2ZW50LnN0cmVhbXMgPSBbc3RyZWFtXTtcbiAgICAgICAgICAgICAgICBzZWxmLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgICAgICAgICAgICAgIGlmIChzZWxmLm9udHJhY2sgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLm9udHJhY2sodHJhY2tFdmVudCk7XG4gICAgICAgICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSwgMCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSAmJiB0eXBlb2YgYXJndW1lbnRzWzFdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChhcmd1bWVudHNbMV0sIDApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmNsb3NlID0gZnVuY3Rpb24oKSB7XG4gICAgICB0aGlzLnRyYW5zY2VpdmVycy5mb3JFYWNoKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIC8qIG5vdCB5ZXRcbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyKSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIuY2xvc2UoKTtcbiAgICAgICAgfVxuICAgICAgICAqL1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0KSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0LnN0b3AoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydCkge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5ydHBTZW5kZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5ydHBTZW5kZXIuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5ydHBSZWNlaXZlcikge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyLnN0b3AoKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICAvLyBGSVhNRTogY2xlYW4gdXAgdHJhY2tzLCBsb2NhbCBzdHJlYW1zLCByZW1vdGUgc3RyZWFtcywgZXRjXG4gICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnY2xvc2VkJyk7XG4gICAgfTtcblxuICAgIC8vIFVwZGF0ZSB0aGUgc2lnbmFsaW5nIHN0YXRlLlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlID1cbiAgICAgICAgZnVuY3Rpb24obmV3U3RhdGUpIHtcbiAgICAgICAgICB0aGlzLnNpZ25hbGluZ1N0YXRlID0gbmV3U3RhdGU7XG4gICAgICAgICAgdmFyIGV2ZW50ID0gbmV3IEV2ZW50KCdzaWduYWxpbmdzdGF0ZWNoYW5nZScpO1xuICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgaWYgKHRoaXMub25zaWduYWxpbmdzdGF0ZWNoYW5nZSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlKGV2ZW50KTtcbiAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAvLyBEZXRlcm1pbmUgd2hldGhlciB0byBmaXJlIHRoZSBuZWdvdGlhdGlvbm5lZWRlZCBldmVudC5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl9tYXliZUZpcmVOZWdvdGlhdGlvbk5lZWRlZCA9XG4gICAgICAgIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIC8vIEZpcmUgYXdheSAoZm9yIG5vdykuXG4gICAgICAgICAgdmFyIGV2ZW50ID0gbmV3IEV2ZW50KCduZWdvdGlhdGlvbm5lZWRlZCcpO1xuICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgaWYgKHRoaXMub25uZWdvdGlhdGlvbm5lZWRlZCAhPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5vbm5lZ290aWF0aW9ubmVlZGVkKGV2ZW50KTtcbiAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAvLyBVcGRhdGUgdGhlIGNvbm5lY3Rpb24gc3RhdGUuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdXBkYXRlQ29ubmVjdGlvblN0YXRlID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICB2YXIgbmV3U3RhdGU7XG4gICAgICB2YXIgc3RhdGVzID0ge1xuICAgICAgICAnbmV3JzogMCxcbiAgICAgICAgY2xvc2VkOiAwLFxuICAgICAgICBjb25uZWN0aW5nOiAwLFxuICAgICAgICBjaGVja2luZzogMCxcbiAgICAgICAgY29ubmVjdGVkOiAwLFxuICAgICAgICBjb21wbGV0ZWQ6IDAsXG4gICAgICAgIGZhaWxlZDogMFxuICAgICAgfTtcbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgc3RhdGVzW3RyYW5zY2VpdmVyLmljZVRyYW5zcG9ydC5zdGF0ZV0rKztcbiAgICAgICAgc3RhdGVzW3RyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQuc3RhdGVdKys7XG4gICAgICB9KTtcbiAgICAgIC8vIElDRVRyYW5zcG9ydC5jb21wbGV0ZWQgYW5kIGNvbm5lY3RlZCBhcmUgdGhlIHNhbWUgZm9yIHRoaXMgcHVycG9zZS5cbiAgICAgIHN0YXRlcy5jb25uZWN0ZWQgKz0gc3RhdGVzLmNvbXBsZXRlZDtcblxuICAgICAgbmV3U3RhdGUgPSAnbmV3JztcbiAgICAgIGlmIChzdGF0ZXMuZmFpbGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdmYWlsZWQnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuY29ubmVjdGluZyA+IDAgfHwgc3RhdGVzLmNoZWNraW5nID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdjb25uZWN0aW5nJztcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGVzLmRpc2Nvbm5lY3RlZCA+IDApIHtcbiAgICAgICAgbmV3U3RhdGUgPSAnZGlzY29ubmVjdGVkJztcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGVzLm5ldyA+IDApIHtcbiAgICAgICAgbmV3U3RhdGUgPSAnbmV3JztcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGVzLmNvbm5lY3RlZCA+IDAgfHwgc3RhdGVzLmNvbXBsZXRlZCA+IDApIHtcbiAgICAgICAgbmV3U3RhdGUgPSAnY29ubmVjdGVkJztcbiAgICAgIH1cblxuICAgICAgaWYgKG5ld1N0YXRlICE9PSBzZWxmLmljZUNvbm5lY3Rpb25TdGF0ZSkge1xuICAgICAgICBzZWxmLmljZUNvbm5lY3Rpb25TdGF0ZSA9IG5ld1N0YXRlO1xuICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ2ljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZScpO1xuICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgICAgICBpZiAodGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSAhPT0gbnVsbCkge1xuICAgICAgICAgIHRoaXMub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UoZXZlbnQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlT2ZmZXIgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIGlmICh0aGlzLl9wZW5kaW5nT2ZmZXIpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdjcmVhdGVPZmZlciBjYWxsZWQgd2hpbGUgdGhlcmUgaXMgYSBwZW5kaW5nIG9mZmVyLicpO1xuICAgICAgfVxuICAgICAgdmFyIG9mZmVyT3B0aW9ucztcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAxICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gIT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgb2ZmZXJPcHRpb25zID0gYXJndW1lbnRzWzBdO1xuICAgICAgfSBlbHNlIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAzKSB7XG4gICAgICAgIG9mZmVyT3B0aW9ucyA9IGFyZ3VtZW50c1syXTtcbiAgICAgIH1cblxuICAgICAgdmFyIHRyYWNrcyA9IFtdO1xuICAgICAgdmFyIG51bUF1ZGlvVHJhY2tzID0gMDtcbiAgICAgIHZhciBudW1WaWRlb1RyYWNrcyA9IDA7XG4gICAgICAvLyBEZWZhdWx0IHRvIHNlbmRyZWN2LlxuICAgICAgaWYgKHRoaXMubG9jYWxTdHJlYW1zLmxlbmd0aCkge1xuICAgICAgICBudW1BdWRpb1RyYWNrcyA9IHRoaXMubG9jYWxTdHJlYW1zWzBdLmdldEF1ZGlvVHJhY2tzKCkubGVuZ3RoO1xuICAgICAgICBudW1WaWRlb1RyYWNrcyA9IHRoaXMubG9jYWxTdHJlYW1zWzBdLmdldFZpZGVvVHJhY2tzKCkubGVuZ3RoO1xuICAgICAgfVxuICAgICAgLy8gRGV0ZXJtaW5lIG51bWJlciBvZiBhdWRpbyBhbmQgdmlkZW8gdHJhY2tzIHdlIG5lZWQgdG8gc2VuZC9yZWN2LlxuICAgICAgaWYgKG9mZmVyT3B0aW9ucykge1xuICAgICAgICAvLyBSZWplY3QgQ2hyb21lIGxlZ2FjeSBjb25zdHJhaW50cy5cbiAgICAgICAgaWYgKG9mZmVyT3B0aW9ucy5tYW5kYXRvcnkgfHwgb2ZmZXJPcHRpb25zLm9wdGlvbmFsKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcbiAgICAgICAgICAgICAgJ0xlZ2FjeSBtYW5kYXRvcnkvb3B0aW9uYWwgY29uc3RyYWludHMgbm90IHN1cHBvcnRlZC4nKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlQXVkaW8gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIG51bUF1ZGlvVHJhY2tzID0gb2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlQXVkaW87XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZVZpZGVvICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBudW1WaWRlb1RyYWNrcyA9IG9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZVZpZGVvO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5sb2NhbFN0cmVhbXMubGVuZ3RoKSB7XG4gICAgICAgIC8vIFB1c2ggbG9jYWwgc3RyZWFtcy5cbiAgICAgICAgdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0VHJhY2tzKCkuZm9yRWFjaChmdW5jdGlvbih0cmFjaykge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6IHRyYWNrLmtpbmQsXG4gICAgICAgICAgICB0cmFjazogdHJhY2ssXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJhY2sua2luZCA9PT0gJ2F1ZGlvJyA/XG4gICAgICAgICAgICAgICAgbnVtQXVkaW9UcmFja3MgPiAwIDogbnVtVmlkZW9UcmFja3MgPiAwXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKHRyYWNrLmtpbmQgPT09ICdhdWRpbycpIHtcbiAgICAgICAgICAgIG51bUF1ZGlvVHJhY2tzLS07XG4gICAgICAgICAgfSBlbHNlIGlmICh0cmFjay5raW5kID09PSAndmlkZW8nKSB7XG4gICAgICAgICAgICBudW1WaWRlb1RyYWNrcy0tO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICAvLyBDcmVhdGUgTS1saW5lcyBmb3IgcmVjdm9ubHkgc3RyZWFtcy5cbiAgICAgIHdoaWxlIChudW1BdWRpb1RyYWNrcyA+IDAgfHwgbnVtVmlkZW9UcmFja3MgPiAwKSB7XG4gICAgICAgIGlmIChudW1BdWRpb1RyYWNrcyA+IDApIHtcbiAgICAgICAgICB0cmFja3MucHVzaCh7XG4gICAgICAgICAgICBraW5kOiAnYXVkaW8nLFxuICAgICAgICAgICAgd2FudFJlY2VpdmU6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBudW1BdWRpb1RyYWNrcy0tO1xuICAgICAgICB9XG4gICAgICAgIGlmIChudW1WaWRlb1RyYWNrcyA+IDApIHtcbiAgICAgICAgICB0cmFja3MucHVzaCh7XG4gICAgICAgICAgICBraW5kOiAndmlkZW8nLFxuICAgICAgICAgICAgd2FudFJlY2VpdmU6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBudW1WaWRlb1RyYWNrcy0tO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSgpO1xuICAgICAgdmFyIHRyYW5zY2VpdmVycyA9IFtdO1xuICAgICAgdHJhY2tzLmZvckVhY2goZnVuY3Rpb24obWxpbmUsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgLy8gRm9yIGVhY2ggdHJhY2ssIGNyZWF0ZSBhbiBpY2UgZ2F0aGVyZXIsIGljZSB0cmFuc3BvcnQsXG4gICAgICAgIC8vIGR0bHMgdHJhbnNwb3J0LCBwb3RlbnRpYWxseSBydHBzZW5kZXIgYW5kIHJ0cHJlY2VpdmVyLlxuICAgICAgICB2YXIgdHJhY2sgPSBtbGluZS50cmFjaztcbiAgICAgICAgdmFyIGtpbmQgPSBtbGluZS5raW5kO1xuICAgICAgICB2YXIgbWlkID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG5cbiAgICAgICAgdmFyIHRyYW5zcG9ydHMgPSBzZWxmLnVzaW5nQnVuZGxlICYmIHNkcE1MaW5lSW5kZXggPiAwID8ge1xuICAgICAgICAgIGljZUdhdGhlcmVyOiB0cmFuc2NlaXZlcnNbMF0uaWNlR2F0aGVyZXIsXG4gICAgICAgICAgaWNlVHJhbnNwb3J0OiB0cmFuc2NlaXZlcnNbMF0uaWNlVHJhbnNwb3J0LFxuICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zY2VpdmVyc1swXS5kdGxzVHJhbnNwb3J0XG4gICAgICAgIH0gOiBzZWxmLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyhtaWQsIHNkcE1MaW5lSW5kZXgpO1xuXG4gICAgICAgIHZhciBsb2NhbENhcGFiaWxpdGllcyA9IFJUQ1J0cFNlbmRlci5nZXRDYXBhYmlsaXRpZXMoa2luZCk7XG4gICAgICAgIHZhciBydHBTZW5kZXI7XG4gICAgICAgIHZhciBydHBSZWNlaXZlcjtcblxuICAgICAgICAvLyBnZW5lcmF0ZSBhbiBzc3JjIG5vdywgdG8gYmUgdXNlZCBsYXRlciBpbiBydHBTZW5kZXIuc2VuZFxuICAgICAgICB2YXIgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IFt7XG4gICAgICAgICAgc3NyYzogKDIgKiBzZHBNTGluZUluZGV4ICsgMSkgKiAxMDAxXG4gICAgICAgIH1dO1xuICAgICAgICBpZiAodHJhY2spIHtcbiAgICAgICAgICBydHBTZW5kZXIgPSBuZXcgUlRDUnRwU2VuZGVyKHRyYWNrLCB0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG1saW5lLndhbnRSZWNlaXZlKSB7XG4gICAgICAgICAgcnRwUmVjZWl2ZXIgPSBuZXcgUlRDUnRwUmVjZWl2ZXIodHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LCBraW5kKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICBpY2VHYXRoZXJlcjogdHJhbnNwb3J0cy5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuaWNlVHJhbnNwb3J0LFxuICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCxcbiAgICAgICAgICBsb2NhbENhcGFiaWxpdGllczogbG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzOiBudWxsLFxuICAgICAgICAgIHJ0cFNlbmRlcjogcnRwU2VuZGVyLFxuICAgICAgICAgIHJ0cFJlY2VpdmVyOiBydHBSZWNlaXZlcixcbiAgICAgICAgICBraW5kOiBraW5kLFxuICAgICAgICAgIG1pZDogbWlkLFxuICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnM6IHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMsXG4gICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVyczogbnVsbFxuICAgICAgICB9O1xuICAgICAgfSk7XG4gICAgICBpZiAodGhpcy51c2luZ0J1bmRsZSkge1xuICAgICAgICBzZHAgKz0gJ2E9Z3JvdXA6QlVORExFICcgKyB0cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdHJhY2tzLmZvckVhY2goZnVuY3Rpb24obWxpbmUsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgdmFyIHRyYW5zY2VpdmVyID0gdHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24odHJhbnNjZWl2ZXIsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcywgJ29mZmVyJywgc2VsZi5sb2NhbFN0cmVhbXNbMF0pO1xuICAgICAgfSk7XG5cbiAgICAgIHRoaXMuX3BlbmRpbmdPZmZlciA9IHRyYW5zY2VpdmVycztcbiAgICAgIHZhciBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICdvZmZlcicsXG4gICAgICAgIHNkcDogc2RwXG4gICAgICB9KTtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoYXJndW1lbnRzWzBdLCAwLCBkZXNjKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGVzYyk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlQW5zd2VyID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAgIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSgpO1xuICAgICAgaWYgKHRoaXMudXNpbmdCdW5kbGUpIHtcbiAgICAgICAgc2RwICs9ICdhPWdyb3VwOkJVTkRMRSAnICsgdGhpcy50cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICAvLyBDYWxjdWxhdGUgaW50ZXJzZWN0aW9uIG9mIGNhcGFiaWxpdGllcy5cbiAgICAgICAgdmFyIGNvbW1vbkNhcGFiaWxpdGllcyA9IHNlbGYuX2dldENvbW1vbkNhcGFiaWxpdGllcyhcbiAgICAgICAgICAgIHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgICAgdHJhbnNjZWl2ZXIucmVtb3RlQ2FwYWJpbGl0aWVzKTtcblxuICAgICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24odHJhbnNjZWl2ZXIsIGNvbW1vbkNhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICdhbnN3ZXInLCBzZWxmLmxvY2FsU3RyZWFtc1swXSk7XG4gICAgICB9KTtcblxuICAgICAgdmFyIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJ2Fuc3dlcicsXG4gICAgICAgIHNkcDogc2RwXG4gICAgICB9KTtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoYXJndW1lbnRzWzBdLCAwLCBkZXNjKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGVzYyk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkSWNlQ2FuZGlkYXRlID0gZnVuY3Rpb24oY2FuZGlkYXRlKSB7XG4gICAgICBpZiAoY2FuZGlkYXRlID09PSBudWxsKSB7XG4gICAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKHt9KTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgbUxpbmVJbmRleCA9IGNhbmRpZGF0ZS5zZHBNTGluZUluZGV4O1xuICAgICAgICBpZiAoY2FuZGlkYXRlLnNkcE1pZCkge1xuICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy50cmFuc2NlaXZlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnRyYW5zY2VpdmVyc1tpXS5taWQgPT09IGNhbmRpZGF0ZS5zZHBNaWQpIHtcbiAgICAgICAgICAgICAgbUxpbmVJbmRleCA9IGk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSB0aGlzLnRyYW5zY2VpdmVyc1ttTGluZUluZGV4XTtcbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgdmFyIGNhbmQgPSBPYmplY3Qua2V5cyhjYW5kaWRhdGUuY2FuZGlkYXRlKS5sZW5ndGggPiAwID9cbiAgICAgICAgICAgICAgU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUoY2FuZGlkYXRlLmNhbmRpZGF0ZSkgOiB7fTtcbiAgICAgICAgICAvLyBJZ25vcmUgQ2hyb21lJ3MgaW52YWxpZCBjYW5kaWRhdGVzIHNpbmNlIEVkZ2UgZG9lcyBub3QgbGlrZSB0aGVtLlxuICAgICAgICAgIGlmIChjYW5kLnByb3RvY29sID09PSAndGNwJyAmJiBjYW5kLnBvcnQgPT09IDApIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gSWdub3JlIFJUQ1AgY2FuZGlkYXRlcywgd2UgYXNzdW1lIFJUQ1AtTVVYLlxuICAgICAgICAgIGlmIChjYW5kLmNvbXBvbmVudCAhPT0gJzEnKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIEEgZGlydHkgaGFjayB0byBtYWtlIHNhbXBsZXMgd29yay5cbiAgICAgICAgICBpZiAoY2FuZC50eXBlID09PSAnZW5kT2ZDYW5kaWRhdGVzJykge1xuICAgICAgICAgICAgY2FuZCA9IHt9O1xuICAgICAgICAgIH1cbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKGNhbmQpO1xuXG4gICAgICAgICAgLy8gdXBkYXRlIHRoZSByZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICB2YXIgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICBzZWN0aW9uc1ttTGluZUluZGV4ICsgMV0gKz0gKGNhbmQudHlwZSA/IGNhbmRpZGF0ZS5jYW5kaWRhdGUudHJpbSgpXG4gICAgICAgICAgICAgIDogJ2E9ZW5kLW9mLWNhbmRpZGF0ZXMnKSArICdcXHJcXG4nO1xuICAgICAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSAmJiB0eXBlb2YgYXJndW1lbnRzWzFdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0U3RhdHMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBwcm9taXNlcyA9IFtdO1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBbJ3J0cFNlbmRlcicsICdydHBSZWNlaXZlcicsICdpY2VHYXRoZXJlcicsICdpY2VUcmFuc3BvcnQnLFxuICAgICAgICAgICAgJ2R0bHNUcmFuc3BvcnQnXS5mb3JFYWNoKGZ1bmN0aW9uKG1ldGhvZCkge1xuICAgICAgICAgICAgICBpZiAodHJhbnNjZWl2ZXJbbWV0aG9kXSkge1xuICAgICAgICAgICAgICAgIHByb21pc2VzLnB1c2godHJhbnNjZWl2ZXJbbWV0aG9kXS5nZXRTdGF0cygpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICAgIHZhciBjYiA9IGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicgJiZcbiAgICAgICAgICBhcmd1bWVudHNbMV07XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSkge1xuICAgICAgICAvLyBzaGltIGdldFN0YXRzIHdpdGggbWFwbGlrZSBzdXBwb3J0XG4gICAgICAgIHZhciByZXN1bHRzID0gbmV3IE1hcCgpO1xuICAgICAgICBQcm9taXNlLmFsbChwcm9taXNlcykudGhlbihmdW5jdGlvbihyZXMpIHtcbiAgICAgICAgICByZXMuZm9yRWFjaChmdW5jdGlvbihyZXN1bHQpIHtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHJlc3VsdCkuZm9yRWFjaChmdW5jdGlvbihpZCkge1xuICAgICAgICAgICAgICByZXN1bHRzLnNldChpZCwgcmVzdWx0W2lkXSk7XG4gICAgICAgICAgICAgIHJlc3VsdHNbaWRdID0gcmVzdWx0W2lkXTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIGlmIChjYikge1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoY2IsIDAsIHJlc3VsdHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXNvbHZlKHJlc3VsdHMpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH07XG4gIH0sXG5cbiAgLy8gQXR0YWNoIGEgbWVkaWEgc3RyZWFtIHRvIGFuIGVsZW1lbnQuXG4gIGF0dGFjaE1lZGlhU3RyZWFtOiBmdW5jdGlvbihlbGVtZW50LCBzdHJlYW0pIHtcbiAgICBsb2dnaW5nKCdERVBSRUNBVEVELCBhdHRhY2hNZWRpYVN0cmVhbSB3aWxsIHNvb24gYmUgcmVtb3ZlZC4nKTtcbiAgICBlbGVtZW50LnNyY09iamVjdCA9IHN0cmVhbTtcbiAgfSxcblxuICByZWF0dGFjaE1lZGlhU3RyZWFtOiBmdW5jdGlvbih0bywgZnJvbSkge1xuICAgIGxvZ2dpbmcoJ0RFUFJFQ0FURUQsIHJlYXR0YWNoTWVkaWFTdHJlYW0gd2lsbCBzb29uIGJlIHJlbW92ZWQuJyk7XG4gICAgdG8uc3JjT2JqZWN0ID0gZnJvbS5zcmNPYmplY3Q7XG4gIH1cbn07XG5cbi8vIEV4cG9zZSBwdWJsaWMgbWV0aG9kcy5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGVkZ2VTaGltLnNoaW1QZWVyQ29ubmVjdGlvbixcbiAgc2hpbUdldFVzZXJNZWRpYTogcmVxdWlyZSgnLi9nZXR1c2VybWVkaWEnKSxcbiAgYXR0YWNoTWVkaWFTdHJlYW06IGVkZ2VTaGltLmF0dGFjaE1lZGlhU3RyZWFtLFxuICByZWF0dGFjaE1lZGlhU3RyZWFtOiBlZGdlU2hpbS5yZWF0dGFjaE1lZGlhU3RyZWFtXG59O1xuXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9+L3RyYWNlYWJsZXBlZXJjb25uZWN0aW9uL34vd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2VkZ2UvZWRnZV9zaGltLmpzXG4vLyBtb2R1bGUgaWQgPSA0ODJcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ==");

TODO found
Open

      // TODO fix tests so we don't need _.get
Severity: Minor
Found in frontend/src/Metamaps/JIT.js by fixme

TODO found
Open

    eval("'use strict';Object.defineProperty(exports,\"__esModule\",{value:true});var _typeof=typeof Symbol===\"function\"&&typeof Symbol.iterator===\"symbol\"?function(obj){return typeof obj;}:function(obj){return obj&&typeof Symbol===\"function\"&&obj.constructor===Symbol?\"symbol\":typeof obj;};/*\nCopyright (c) 2011 Sencha Inc. - Author: Nicolas Garcia Belmonte (http://philogb.github.com/)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n */\n\n/*\n  File: Core.js\n\n */\n\n/*\n Object: $jit\n \n Defines the namespace for all library Classes and Objects. \n This variable is the *only* global variable defined in the Toolkit. \n There are also other interesting properties attached to this variable described below.\n */\n// START METAMAPS CODE\nvar $jit=function $jit(w){\n// ORIGINAL:\n// window.$jit = function(w) {\n// END METAMAPS CODE\nw=w||window;\nfor(var k in $jit){\nif($jit[k].$extend){\nw[k]=$jit[k];\n}\n}\n};\n\n$jit.version='2.0.1';\n/*\n  Object: $jit.id\n  \n  Works just like *document.getElementById*\n  \n  Example:\n  (start code js)\n  var element = $jit.id('elementId');\n  (end code)\n\n*/\n\n/*\n Object: $jit.util\n \n Contains utility functions.\n \n Some of the utility functions and the Class system were based in the MooTools Framework \n <http://mootools.net>. Copyright (c) 2006-2010 Valerio Proietti, <http://mad4milk.net/>. \n MIT license <http://mootools.net/license.txt>.\n \n These methods are generally also implemented in DOM manipulation frameworks like JQuery, MooTools and Prototype.\n I'd suggest you to use the functions from those libraries instead of using these, since their functions \n are widely used and tested in many different platforms/browsers. Use these functions only if you have to.\n \n */\nvar $=function $(d){\nreturn document.getElementById(d);\n};\n\n$.empty=function(){\n};\n\n/*\n  Method: extend\n  \n  Augment an object by appending another object's properties.\n  \n  Parameters:\n  \n  original - (object) The object to be extended.\n  extended - (object) An object which properties are going to be appended to the original object.\n  \n  Example:\n  (start code js)\n  $jit.util.extend({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }\n  (end code)\n*/\n$.extend=function(original,extended){\nfor(var key in extended||{}){\noriginal[key]=extended[key];}\nreturn original;\n};\n\n$.lambda=function(value){\nreturn typeof value=='function'?value:function(){\nreturn value;\n};\n};\n\n$.time=Date.now||function(){\nreturn+new Date();\n};\n\n/*\n  Method: splat\n  \n  Returns an array wrapping *obj* if *obj* is not an array. Returns *obj* otherwise.\n  \n  Parameters:\n  \n  obj - (mixed) The object to be wrapped in an array.\n  \n  Example:\n  (start code js)\n  $jit.util.splat(3);   //[3]\n  $jit.util.splat([3]); //[3]\n  (end code)\n*/\n$.splat=function(obj){\nvar type=$.type(obj);\nreturn type?type!='array'?[obj]:obj:[];\n};\n\n$.type=function(elem){\nvar type=$.type.s.call(elem).match(/^\\[object\\s(.*)\\]$/)[1].toLowerCase();\nif(type!='object')return type;\nif(elem&&elem.$$family)return elem.$$family;\nreturn elem&&elem.nodeName&&elem.nodeType==1?'element':type;\n};\n$.type.s=Object.prototype.toString;\n\n/*\n  Method: each\n  \n  Iterates through an iterable applying *f*.\n  \n  Parameters:\n  \n  iterable - (array) The original array.\n  fn - (function) The function to apply to the array elements.\n  \n  Example:\n  (start code js)\n  $jit.util.each([3, 4, 5], function(n) { alert('number ' + n); });\n  (end code)\n*/\n$.each=function(iterable,fn){\nvar type=$.type(iterable);\nif(type=='object'){\nfor(var key in iterable){\nfn(iterable[key],key);}\n}else{\nfor(var i=0,l=iterable.length;i<l;i++){\nfn(iterable[i],i);}\n}\n};\n\n$.indexOf=function(array,item){\nif(Array.indexOf)return array.indexOf(item);\nfor(var i=0,l=array.length;i<l;i++){\nif(array[i]===item)return i;\n}\nreturn-1;\n};\n\n/*\n  Method: map\n  \n  Maps or collects an array by applying *f*.\n  \n  Parameters:\n  \n  array - (array) The original array.\n  f - (function) The function to apply to the array elements.\n  \n  Example:\n  (start code js)\n  $jit.util.map([3, 4, 5], function(n) { return n*n; }); //[9, 16, 25]\n  (end code)\n*/\n$.map=function(array,f){\nvar ans=[];\n$.each(array,function(elem,i){\nans.push(f(elem,i));\n});\nreturn ans;\n};\n\n/*\n  Method: reduce\n  \n  Iteratively applies the binary function *f* storing the result in an accumulator.\n  \n  Parameters:\n  \n  array - (array) The original array.\n  f - (function) The function to apply to the array elements.\n  opt - (optional|mixed) The starting value for the acumulator.\n  \n  Example:\n  (start code js)\n  $jit.util.reduce([3, 4, 5], function(x, y) { return x + y; }, 0); //12\n  (end code)\n*/\n$.reduce=function(array,f,opt){\nvar l=array.length;\nif(l==0)return opt;\nvar acum=arguments.length==3?opt:array[--l];\nwhile(l--){\nacum=f(acum,array[l]);\n}\nreturn acum;\n};\n\n/*\n  Method: merge\n  \n  Merges n-objects and their sub-objects creating a new, fresh object.\n  \n  Parameters:\n  \n  An arbitrary number of objects.\n  \n  Example:\n  (start code js)\n  $jit.util.merge({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }\n  (end code)\n*/\n$.merge=function(){\nvar mix={};\nfor(var i=0,l=arguments.length;i<l;i++){\nvar object=arguments[i];\nif($.type(object)!='object')\ncontinue;\nfor(var key in object){\nvar op=object[key],mp=mix[key];\nmix[key]=mp&&$.type(op)=='object'&&$.type(mp)=='object'?$.\nmerge(mp,op):$.unlink(op);\n}\n}\nreturn mix;\n};\n\n$.unlink=function(object){\nvar unlinked;\nswitch($.type(object)){\ncase'object':\nunlinked={};\nfor(var p in object){\nunlinked[p]=$.unlink(object[p]);}\nbreak;\ncase'array':\nunlinked=[];\nfor(var i=0,l=object.length;i<l;i++){\nunlinked[i]=$.unlink(object[i]);}\nbreak;\ndefault:\nreturn object;}\n\nreturn unlinked;\n};\n\n$.zip=function(){\nif(arguments.length===0)return[];\nfor(var j=0,ans=[],l=arguments.length,ml=arguments[0].length;j<ml;j++){\nfor(var i=0,row=[];i<l;i++){\nrow.push(arguments[i][j]);\n}\nans.push(row);\n}\nreturn ans;\n};\n\n/*\n  Method: rgbToHex\n  \n  Converts an RGB array into a Hex string.\n  \n  Parameters:\n  \n  srcArray - (array) An array with R, G and B values\n  \n  Example:\n  (start code js)\n  $jit.util.rgbToHex([255, 255, 255]); //'#ffffff'\n  (end code)\n*/\n$.rgbToHex=function(srcArray,array){\nif(srcArray.length<3)\nreturn null;\nif(srcArray.length==4&&srcArray[3]==0&&!array)\nreturn'transparent';\nvar hex=[];\nfor(var i=0;i<3;i++){\nvar bit=(srcArray[i]-0).toString(16);\nhex.push(bit.length==1?'0'+bit:bit);\n}\nreturn array?hex:'#'+hex.join('');\n};\n\n/*\n  Method: hexToRgb\n  \n  Converts an Hex color string into an RGB array.\n  \n  Parameters:\n  \n  hex - (string) A color hex string.\n  \n  Example:\n  (start code js)\n  $jit.util.hexToRgb('#fff'); //[255, 255, 255]\n  (end code)\n*/\n$.hexToRgb=function(hex){\nif(hex.length!=7){\nhex=hex.match(/^#?(\\w{1,2})(\\w{1,2})(\\w{1,2})$/);\nhex.shift();\nif(hex.length!=3)\nreturn null;\nvar rgb=[];\nfor(var i=0;i<3;i++){\nvar value=hex[i];\nif(value.length==1)\nvalue+=value;\nrgb.push(parseInt(value,16));\n}\nreturn rgb;\n}else{\nhex=parseInt(hex.slice(1),16);\nreturn[hex>>16,hex>>8&0xff,hex&0xff];\n}\n};\n\n$.destroy=function(elem){\n$.clean(elem);\nif(elem.parentNode)\nelem.parentNode.removeChild(elem);\nif(elem.clearAttributes)\nelem.clearAttributes();\n};\n\n$.clean=function(elem){\nfor(var ch=elem.childNodes,i=0,l=ch.length;i<l;i++){\n$.destroy(ch[i]);\n}\n};\n\n/*\n  Method: addEvent\n  \n  Cross-browser add event listener.\n  \n  Parameters:\n  \n  obj - (obj) The Element to attach the listener to.\n  type - (string) The listener type. For example 'click', or 'mousemove'.\n  fn - (function) The callback function to be used when the event is fired.\n  \n  Example:\n  (start code js)\n  $jit.util.addEvent(elem, 'click', function(){ alert('hello'); });\n  (end code)\n*/\n$.addEvent=function(obj,type,fn){\nif(obj.addEventListener)\nobj.addEventListener(type,fn,false);else\n\nobj.attachEvent('on'+type,fn);\n};\n\n$.addEvents=function(obj,typeObj){\nfor(var type in typeObj){\n$.addEvent(obj,type,typeObj[type]);\n}\n};\n\n$.hasClass=function(obj,klass){\nreturn(' '+obj.className+' ').indexOf(' '+klass+' ')>-1;\n};\n\n$.addClass=function(obj,klass){\nif(!$.hasClass(obj,klass))\nobj.className=obj.className+\" \"+klass;\n};\n\n$.removeClass=function(obj,klass){\nobj.className=obj.className.replace(new RegExp(\n'(^|\\\\s)'+klass+'(?:\\\\s|$)'),'$1');\n};\n\n$.getPos=function(elem){\nvar offset=getOffsets(elem);\nvar scroll=getScrolls(elem);\nreturn{\nx:offset.x-scroll.x,\ny:offset.y-scroll.y};\n\n\nfunction getOffsets(elem){\nvar position={\nx:0,\ny:0};\n\nwhile(elem&&!isBody(elem)){\nposition.x+=elem.offsetLeft;\nposition.y+=elem.offsetTop;\nelem=elem.offsetParent;\n}\nreturn position;\n}\n\nfunction getScrolls(elem){\nvar position={\nx:0,\ny:0};\n\nwhile(elem&&!isBody(elem)){\nposition.x+=elem.scrollLeft;\nposition.y+=elem.scrollTop;\nelem=elem.parentNode;\n}\nreturn position;\n}\n\nfunction isBody(element){\nreturn /^(?:body|html)$/i.test(element.tagName);\n}\n};\n\n$.event={\nget:function get(e,win){\nwin=win||window;\nreturn e||win.event;\n},\ngetWheel:function getWheel(e){\nreturn e.wheelDelta?e.wheelDelta/120:-(e.detail||0)/3;\n},\nisRightClick:function isRightClick(e){\nreturn e.which==3||e.button==2;\n},\ngetPos:function getPos(e,win){\n// get mouse position\nwin=win||window;\ne=e||win.event;\nvar doc=win.document;\ndoc=doc.documentElement||doc.body;\n//TODO(nico): make touch event handling better\nif(e.touches&&e.touches.length){\ne=e.touches[0];\n}\nvar page={\nx:e.pageX||e.clientX+doc.scrollLeft,\ny:e.pageY||e.clientY+doc.scrollTop};\n\nreturn page;\n},\nstop:function stop(e){\nif(e.stopPropagation)e.stopPropagation();\ne.cancelBubble=true;\nif(e.preventDefault)e.preventDefault();else\ne.returnValue=false;\n}};\n\n\n$jit.util=$jit.id=$;\n\nvar Class=function Class(properties){\nproperties=properties||{};\nvar klass=function klass(){\nfor(var key in this){\nif(typeof this[key]!='function')\nthis[key]=$.unlink(this[key]);\n}\nthis.constructor=klass;\nif(Class.prototyping)\nreturn this;\nvar instance=this.initialize?this.initialize.apply(this,arguments):\nthis;\n//typize\nthis.$$family='class';\nreturn instance;\n};\n\nfor(var mutator in Class.Mutators){\nif(!properties[mutator])\ncontinue;\nproperties=Class.Mutators[mutator](properties,properties[mutator]);\ndelete properties[mutator];\n}\n\n$.extend(klass,this);\nklass.constructor=Class;\nklass.prototype=properties;\nreturn klass;\n};\n\nClass.Mutators={\n\nImplements:function Implements(self,klasses){\n$.each($.splat(klasses),function(klass){\nClass.prototyping=klass;\nvar instance=typeof klass=='function'?new klass():klass;\nfor(var prop in instance){\nif(!(prop in self)){\nself[prop]=instance[prop];\n}\n}\ndelete Class.prototyping;\n});\nreturn self;\n}};\n\n\n\n$.extend(Class,{\n\ninherit:function inherit(object,properties){\nfor(var key in properties){\nvar override=properties[key];\nvar previous=object[key];\nvar type=$.type(override);\nif(previous&&type=='function'){\nif(override!=previous){\nClass.override(object,key,override);\n}\n}else if(type=='object'){\nobject[key]=$.merge(previous,override);\n}else{\nobject[key]=override;\n}\n}\nreturn object;\n},\n\noverride:function override(object,name,method){\nvar parent=Class.prototyping;\nif(parent&&object[name]!=parent[name])\nparent=null;\nvar override=function override(){\nvar previous=this.parent;\nthis.parent=parent?parent[name]:object[name];\nvar value=method.apply(this,arguments);\nthis.parent=previous;\nreturn value;\n};\nobject[name]=override;\n}});\n\n\n\nClass.prototype.implement=function(){\nvar proto=this.prototype;\n$.each(Array.prototype.slice.call(arguments||[]),function(properties){\nClass.inherit(proto,properties);\n});\nreturn this;\n};\n\n$jit.Class=Class;\n\n/*\n  Object: $jit.json\n  \n  Provides JSON utility functions.\n  \n  Most of these functions are JSON-tree traversal and manipulation functions.\n*/\n$jit.json={\n/*\n     Method: prune\n  \n     Clears all tree nodes having depth greater than maxLevel.\n  \n     Parameters:\n  \n        tree - (object) A JSON tree object. For more information please see <Loader.loadJSON>.\n        maxLevel - (number) An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted.\n\n  */\nprune:function prune(tree,maxLevel){\nthis.each(tree,function(elem,i){\nif(i==maxLevel&&elem.children){\ndelete elem.children;\nelem.children=[];\n}\n});\n},\n/*\n     Method: getParent\n  \n     Returns the parent node of the node having _id_ as id.\n  \n     Parameters:\n  \n        tree - (object) A JSON tree object. See also <Loader.loadJSON>.\n        id - (string) The _id_ of the child node whose parent will be returned.\n\n    Returns:\n\n        A tree JSON node if any, or false otherwise.\n  \n  */\ngetParent:function getParent(tree,id){\nif(tree.id==id)\nreturn false;\nvar ch=tree.children;\nif(ch&&ch.length>0){\nfor(var i=0;i<ch.length;i++){\nif(ch[i].id==id)\nreturn tree;else\n{\nvar ans=this.getParent(ch[i],id);\nif(ans)\nreturn ans;\n}\n}\n}\nreturn false;\n},\n/*\n     Method: getSubtree\n  \n     Returns the subtree that matches the given id.\n  \n     Parameters:\n  \n        tree - (object) A JSON tree object. See also <Loader.loadJSON>.\n        id - (string) A node *unique* identifier.\n  \n     Returns:\n  \n        A subtree having a root node matching the given id. Returns null if no subtree matching the id is found.\n\n  */\ngetSubtree:function getSubtree(tree,id){\nif(tree.id==id)\nreturn tree;\nfor(var i=0,ch=tree.children;ch&&i<ch.length;i++){\nvar t=this.getSubtree(ch[i],id);\nif(t!=null)\nreturn t;\n}\nreturn null;\n},\n/*\n     Method: eachLevel\n  \n      Iterates on tree nodes with relative depth less or equal than a specified level.\n  \n     Parameters:\n  \n        tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.\n        initLevel - (number) An integer specifying the initial relative level. Usually zero.\n        toLevel - (number) An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number.\n        action - (function) A function that receives a node and an integer specifying the actual level of the node.\n          \n    Example:\n   (start code js)\n     $jit.json.eachLevel(tree, 0, 3, function(node, depth) {\n        alert(node.name + ' ' + depth);\n     });\n   (end code)\n  */\neachLevel:function eachLevel(tree,initLevel,toLevel,action){\nif(initLevel<=toLevel){\naction(tree,initLevel);\nif(!tree.children)return;\nfor(var i=0,ch=tree.children;i<ch.length;i++){\nthis.eachLevel(ch[i],initLevel+1,toLevel,action);\n}\n}\n},\n/*\n     Method: each\n  \n      A JSON tree iterator.\n  \n     Parameters:\n  \n        tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.\n        action - (function) A function that receives a node.\n\n    Example:\n    (start code js)\n      $jit.json.each(tree, function(node) {\n        alert(node.name);\n      });\n    (end code)\n          \n  */\neach:function each(tree,action){\nthis.eachLevel(tree,0,Number.MAX_VALUE,action);\n}};\n\n\n\n/*\n     An object containing multiple type of transformations. \n*/\n\n$jit.Trans={\n$extend:true,\n\nlinear:function linear(p){\nreturn p;\n}};\n\n\nvar Trans=$jit.Trans;\n\n(function(){\n\nvar makeTrans=function makeTrans(transition,params){\nparams=$.splat(params);\nreturn $.extend(transition,{\neaseIn:function easeIn(pos){\nreturn transition(pos,params);\n},\neaseOut:function easeOut(pos){\nreturn 1-transition(1-pos,params);\n},\neaseInOut:function easeInOut(pos){\nreturn pos<=0.5?transition(2*pos,params)/2:(2-transition(\n2*(1-pos),params))/2;\n}});\n\n};\n\nvar transitions={\n\nPow:function Pow(p,x){\nreturn Math.pow(p,x[0]||6);\n},\n\nExpo:function Expo(p){\nreturn Math.pow(2,8*(p-1));\n},\n\nCirc:function Circ(p){\nreturn 1-Math.sin(Math.acos(p));\n},\n\nSine:function Sine(p){\nreturn 1-Math.sin((1-p)*Math.PI/2);\n},\n\nBack:function Back(p,x){\nx=x[0]||1.618;\nreturn Math.pow(p,2)*((x+1)*p-x);\n},\n\nBounce:function Bounce(p){\nvar value;\nfor(var a=0,b=1;1;a+=b,b/=2){\nif(p>=(7-4*a)/11){\nvalue=b*b-Math.pow((11-6*a-11*p)/4,2);\nbreak;\n}\n}\nreturn value;\n},\n\nElastic:function Elastic(p,x){\nreturn Math.pow(2,10*--p)*\nMath.cos(20*p*Math.PI*(x[0]||1)/3);\n}};\n\n\n\n$.each(transitions,function(val,key){\nTrans[key]=makeTrans(val);\n});\n\n$.each([\n'Quad','Cubic','Quart','Quint'],\nfunction(elem,i){\nTrans[elem]=makeTrans(function(p){\nreturn Math.pow(p,[\ni+2]);\n\n});\n});\n\n})();\n\n/*\n   A Class that can perform animations for generic objects.\n\n   If you are looking for animation transitions please take a look at the <Trans> object.\n\n   Used by:\n\n   <Graph.Plot>\n   \n   Based on:\n   \n   The Animation class is based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.\n\n*/\n\nvar Animation=new Class({\n\ninitialize:function initialize(options){\nthis.setOptions(options);\n},\n\nsetOptions:function setOptions(options){\nvar opt={\nduration:2500,\nfps:40,\ntransition:Trans.Quart.easeInOut,\ncompute:$.empty,\ncomplete:$.empty,\nlink:'ignore'};\n\nthis.opt=$.merge(opt,options||{});\nreturn this;\n},\n\nstep:function step(){\nvar time=$.time(),opt=this.opt;\nif(time<this.time+opt.duration){\nvar delta=opt.transition((time-this.time)/opt.duration);\nopt.compute(delta);\n}else{\nthis.timer=clearInterval(this.timer);\nopt.compute(1);\nopt.complete();\n}\n},\n\nstart:function start(){\nif(!this.check())\nreturn this;\nthis.time=0;\nthis.startTimer();\nreturn this;\n},\n\nstartTimer:function startTimer(){\nvar that=this,fps=this.opt.fps;\nif(this.timer)\nreturn false;\nthis.time=$.time()-this.time;\nthis.timer=setInterval(function(){\nthat.step();\n},Math.round(1000/fps));\nreturn true;\n},\n\npause:function pause(){\nthis.stopTimer();\nreturn this;\n},\n\nresume:function resume(){\nthis.startTimer();\nreturn this;\n},\n\nstopTimer:function stopTimer(){\nif(!this.timer)\nreturn false;\nthis.time=$.time()-this.time;\nthis.timer=clearInterval(this.timer);\nreturn true;\n},\n\ncheck:function check(){\nif(!this.timer)\nreturn true;\nif(this.opt.link=='cancel'){\nthis.stopTimer();\nreturn true;\n}\nreturn false;\n}});\n\n\n\nvar Options=function Options(){\nvar args=arguments;\nfor(var i=0,l=args.length,ans={};i<l;i++){\nvar opt=Options[args[i]];\nif(opt.$extend){\n$.extend(ans,opt);\n}else{\nans[args[i]]=opt;\n}\n}\nreturn ans;\n};\n\n/*\n * File: Options.Canvas.js\n *\n*/\n\n/*\n  Object: Options.Canvas\n  \n  These are Canvas general options, like where to append it in the DOM, its dimensions, background, \n  and other more advanced options.\n  \n  Syntax:\n  \n  (start code js)\n\n  Options.Canvas = {\n    injectInto: 'id',\n    type: '2D', //'3D'\n    width: false,\n    height: false,\n    useCanvas: false,\n    withLabels: true,\n    background: false\n  };  \n  (end code)\n  \n  Example:\n  \n  (start code js)\n  var viz = new $jit.Viz({\n    injectInto: 'someContainerId',\n    width: 500,\n    height: 700\n  });\n  (end code)\n  \n  Parameters:\n  \n  injectInto - *required* (string|element) The id of the DOM container for the visualization. It can also be an Element provided that it has an id.\n  type - (string) Context type. Default's 2D but can be 3D for webGL enabled browsers.\n  width - (number) Default's to the *container's offsetWidth*. The width of the canvas.\n  height - (number) Default's to the *container's offsetHeight*. The height of the canvas.\n  useCanvas - (boolean|object) Default's *false*. You can pass another <Canvas> instance to be used by the visualization.\n  withLabels - (boolean) Default's *true*. Whether to use a label container for the visualization.\n  background - (boolean|object) Default's *false*. An object containing information about the rendering of a background canvas.\n*/\n\nOptions.Canvas={\n$extend:true,\n\ninjectInto:'id',\ntype:'2D',\nwidth:false,\nheight:false,\nuseCanvas:false,\nwithLabels:true,\nbackground:false,\n\nScene:{\nLighting:{\nenable:false,\nambient:[1,1,1],\ndirectional:{\ndirection:{x:-100,y:-100,z:-100},\ncolor:[0.5,0.3,0.1]}}}};\n\n\n\n\n\n/*\n * File: Options.Node.js\n *\n*/\n\n/*\n  Object: Options.Node\n\n  Provides Node rendering options for Tree and Graph based visualizations.\n\n  Syntax:\n    \n  (start code js)\n  Options.Node = {\n    overridable: false,\n    type: 'circle',\n    color: '#ccb',\n    alpha: 1,\n    dim: 3,\n    height: 20,\n    width: 90,\n    autoHeight: false,\n    autoWidth: false,\n    lineWidth: 1,\n    transform: true,\n    align: \"center\",\n    angularWidth:1,\n    span:1,\n    CanvasStyles: {}\n  };\n  (end code)\n  \n  Example:\n  \n  (start code js)\n  var viz = new $jit.Viz({\n    Node: {\n      overridable: true,\n      width: 30,\n      autoHeight: true,\n      type: 'rectangle'\n    }\n  });\n  (end code)\n  \n  Parameters:\n\n  overridable - (boolean) Default's *false*. Determine whether or not general node properties can be overridden by a particular <Graph.Node>.\n  type - (string) Default's *circle*. Node's shape. Node built-in types include 'circle', 'rectangle', 'square', 'ellipse', 'triangle', 'star'. The default Node type might vary in each visualization. You can also implement (non built-in) custom Node types into your visualizations.\n  color - (string) Default's *#ccb*. Node color.\n  alpha - (number) Default's *1*. The Node's alpha value. *1* is for full opacity.\n  dim - (number) Default's *3*. An extra parameter used by 'circle', 'square', 'triangle' and 'star' node types. Depending on each shape, this parameter can set the radius of a circle, half the length of the side of a square, half the base and half the height of a triangle or the length of a side of a star (concave decagon).\n  height - (number) Default's *20*. Used by 'rectangle' and 'ellipse' node types. The height of the node shape.\n  width - (number) Default's *90*. Used by 'rectangle' and 'ellipse' node types. The width of the node shape.\n  autoHeight - (boolean) Default's *false*. Whether to set an auto height for the node depending on the content of the Node's label.\n  autoWidth - (boolean) Default's *false*. Whether to set an auto width for the node depending on the content of the Node's label.\n  lineWidth - (number) Default's *1*. Used only by some Node shapes. The line width of the strokes of a node.\n  transform - (boolean) Default's *true*. Only used by the <Hypertree> visualization. Whether to scale the nodes according to the moebius transformation.\n  align - (string) Default's *center*. Possible values are 'center', 'left' or 'right'. Used only by the <ST> visualization, these parameters are used for aligning nodes when some of they dimensions vary.\n  angularWidth - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The amount of relative 'space' set for a node.\n  span - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The angle span amount set for a node.\n  CanvasStyles - (object) Default's an empty object (i.e. {}). Attach any other canvas specific property that you'd set to the canvas context before plotting a Node.\n\n*/\nOptions.Node={\n$extend:false,\n\noverridable:false,\ntype:'circle',\ncolor:'#ccb',\nalpha:1,\ndim:3,\nheight:20,\nwidth:90,\nautoHeight:false,\nautoWidth:false,\nlineWidth:1,\ntransform:true,\nalign:\"center\",\nangularWidth:1,\nspan:1,\n//Raw canvas styles to be\n//applied to the context instance\n//before plotting a node\nCanvasStyles:{}};\n\n\n\n/*\n * File: Options.Edge.js\n *\n*/\n\n/*\n  Object: Options.Edge\n\n  Provides Edge rendering options for Tree and Graph based visualizations.\n\n  Syntax:\n    \n  (start code js)\n  Options.Edge = {\n    overridable: false,\n    type: 'line',\n    color: '#ccb',\n    lineWidth: 1,\n    dim:15,\n    alpha: 1,\n    CanvasStyles: {}\n  };\n  (end code)\n  \n  Example:\n  \n  (start code js)\n  var viz = new $jit.Viz({\n    Edge: {\n      overridable: true,\n      type: 'line',\n      color: '#fff',\n      CanvasStyles: {\n        shadowColor: '#ccc',\n        shadowBlur: 10\n      }\n    }\n  });\n  (end code)\n  \n  Parameters:\n    \n   overridable - (boolean) Default's *false*. Determine whether or not general edges properties can be overridden by a particular <Graph.Adjacence>.\n   type - (string) Default's 'line'. Edge styles include 'line', 'hyperline', 'arrow'. The default Edge type might vary in each visualization. You can also implement custom Edge types.\n   color - (string) Default's '#ccb'. Edge color.\n   lineWidth - (number) Default's *1*. Line/Edge width.\n   alpha - (number) Default's *1*. The Edge's alpha value. *1* is for full opacity.\n   dim - (number) Default's *15*. An extra parameter used by other complex shapes such as quadratic, bezier or arrow, to determine the shape's diameter.\n   epsilon - (number) Default's *7*. Only used when using *enableForEdges* in <Options.Events>. This dimension is used to create an area for the line where the contains method for the edge returns *true*.\n   CanvasStyles - (object) Default's an empty object (i.e. {}). Attach any other canvas specific property that you'd set to the canvas context before plotting an Edge.\n\n  See also:\n   \n   If you want to know more about how to customize Node/Edge data per element, in the JSON or programmatically, take a look at this article.\n*/\nOptions.Edge={\n$extend:false,\n\noverridable:false,\ntype:'line',\ncolor:'#ccb',\nlineWidth:1,\ndim:15,\nalpha:1,\nepsilon:7,\n\n//Raw canvas styles to be\n//applied to the context instance\n//before plotting an edge\nCanvasStyles:{}};\n\n\n\n/*\n * File: Options.Fx.js\n *\n*/\n\n/*\n  Object: Options.Fx\n\n  Provides animation options like duration of the animations, frames per second and animation transitions.  \n\n  Syntax:\n  \n  (start code js)\n    Options.Fx = {\n      fps:40,\n      duration: 2500,\n      transition: $jit.Trans.Quart.easeInOut,\n      clearCanvas: true\n    };\n  (end code)\n  \n  Example:\n  \n  (start code js)\n  var viz = new $jit.Viz({\n    duration: 1000,\n    fps: 35,\n    transition: $jit.Trans.linear\n  });\n  (end code)\n  \n  Parameters:\n  \n  clearCanvas - (boolean) Default's *true*. Whether to clear the frame/canvas when the viz is plotted or animated.\n  duration - (number) Default's *2500*. Duration of the animation in milliseconds.\n  fps - (number) Default's *40*. Frames per second.\n  transition - (object) Default's *$jit.Trans.Quart.easeInOut*. The transition used for the animations. See below for a more detailed explanation.\n  \n  Object: $jit.Trans\n  \n  This object is used for specifying different animation transitions in all visualizations.\n\n  There are many different type of animation transitions.\n\n  linear:\n\n  Displays a linear transition\n\n  >Trans.linear\n  \n  (see Linear.png)\n\n  Quad:\n\n  Displays a Quadratic transition.\n\n  >Trans.Quad.easeIn\n  >Trans.Quad.easeOut\n  >Trans.Quad.easeInOut\n  \n (see Quad.png)\n\n Cubic:\n\n Displays a Cubic transition.\n\n >Trans.Cubic.easeIn\n >Trans.Cubic.easeOut\n >Trans.Cubic.easeInOut\n\n (see Cubic.png)\n\n Quart:\n\n Displays a Quartetic transition.\n\n >Trans.Quart.easeIn\n >Trans.Quart.easeOut\n >Trans.Quart.easeInOut\n\n (see Quart.png)\n\n Quint:\n\n Displays a Quintic transition.\n\n >Trans.Quint.easeIn\n >Trans.Quint.easeOut\n >Trans.Quint.easeInOut\n\n (see Quint.png)\n\n Expo:\n\n Displays an Exponential transition.\n\n >Trans.Expo.easeIn\n >Trans.Expo.easeOut\n >Trans.Expo.easeInOut\n\n (see Expo.png)\n\n Circ:\n\n Displays a Circular transition.\n\n >Trans.Circ.easeIn\n >Trans.Circ.easeOut\n >Trans.Circ.easeInOut\n\n (see Circ.png)\n\n Sine:\n\n Displays a Sineousidal transition.\n\n >Trans.Sine.easeIn\n >Trans.Sine.easeOut\n >Trans.Sine.easeInOut\n\n (see Sine.png)\n\n Back:\n\n >Trans.Back.easeIn\n >Trans.Back.easeOut\n >Trans.Back.easeInOut\n\n (see Back.png)\n\n Bounce:\n\n Bouncy transition.\n\n >Trans.Bounce.easeIn\n >Trans.Bounce.easeOut\n >Trans.Bounce.easeInOut\n\n (see Bounce.png)\n\n Elastic:\n\n Elastic curve.\n\n >Trans.Elastic.easeIn\n >Trans.Elastic.easeOut\n >Trans.Elastic.easeInOut\n\n (see Elastic.png)\n \n Based on:\n     \n Easing and Transition animation methods are based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2010 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.\n\n\n*/\nOptions.Fx={\n$extend:true,\n\nfps:40,\nduration:2500,\ntransition:$jit.Trans.Quart.easeInOut,\nclearCanvas:true};\n\n\n/*\n * File: Options.Label.js\n *\n*/\n/*\n  Object: Options.Label\n\n  Provides styling for Labels such as font size, family, etc. Also sets Node labels as HTML, SVG or Native canvas elements.  \n\n  Syntax:\n  \n  (start code js)\n    Options.Label = {\n      overridable: false,\n      type: 'HTML', //'SVG', 'Native'\n      style: ' ',\n      size: 10,\n      family: 'sans-serif',\n      textAlign: 'center',\n      textBaseline: 'alphabetic',\n      color: '#fff'\n    };\n  (end code)\n  \n  Example:\n  \n  (start code js)\n  var viz = new $jit.Viz({\n    Label: {\n      type: 'Native',\n      size: 11,\n      color: '#ccc'\n    }\n  });\n  (end code)\n  \n  Parameters:\n    \n  overridable - (boolean) Default's *false*. Determine whether or not general label properties can be overridden by a particular <Graph.Node>.\n  type - (string) Default's *HTML*. The type for the labels. Can be 'HTML', 'SVG' or 'Native' canvas labels.\n  style - (string) Default's *empty string*. Can be 'italic' or 'bold'. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.\n  size - (number) Default's *10*. The font's size. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.\n  family - (string) Default's *sans-serif*. The font's family. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.\n  color - (string) Default's *#fff*. The font's color. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.\n*/\nOptions.Label={\n$extend:false,\n\noverridable:false,\ntype:'HTML',//'SVG', 'Native'\nstyle:' ',\nsize:10,\nfamily:'sans-serif',\ntextAlign:'center',\ntextBaseline:'alphabetic',\ncolor:'#fff'};\n\n\n\n/*\n * File: Options.Tips.js\n *\n */\n\n/*\n  Object: Options.Tips\n  \n  Tips options\n  \n  Syntax:\n    \n  (start code js)\n  Options.Tips = {\n    enable: false,\n    type: 'auto',\n    offsetX: 20,\n    offsetY: 20,\n    onShow: $.empty,\n    onHide: $.empty\n  };\n  (end code)\n  \n  Example:\n  \n  (start code js)\n  var viz = new $jit.Viz({\n    Tips: {\n      enable: true,\n      type: 'Native',\n      offsetX: 10,\n      offsetY: 10,\n      onShow: function(tip, node) {\n        tip.innerHTML = node.name;\n      }\n    }\n  });\n  (end code)\n\n  Parameters:\n\n  enable - (boolean) Default's *false*. If *true*, a tooltip will be shown when a node is hovered. The tooltip is a div DOM element having \"tip\" as CSS class. \n  type - (string) Default's *auto*. Defines where to attach the MouseEnter/Leave tooltip events. Possible values are 'Native' to attach them to the canvas or 'HTML' to attach them to DOM label elements (if defined). 'auto' sets this property to the value of <Options.Label>'s *type* property.\n  offsetX - (number) Default's *20*. An offset added to the current tooltip x-position (which is the same as the current mouse position). Default's 20.\n  offsetY - (number) Default's *20*. An offset added to the current tooltip y-position (which is the same as the current mouse position). Default's 20.\n  onShow(tip, node) - This callack is used right before displaying a tooltip. The first formal parameter is the tip itself (which is a DivElement). The second parameter may be a <Graph.Node> for graph based visualizations or an object with label, value properties for charts.\n  onHide() - This callack is used when hiding a tooltip.\n\n*/\nOptions.Tips={\n$extend:false,\n\nenable:false,\ntype:'auto',\noffsetX:20,\noffsetY:20,\nforce:false,\nonShow:$.empty,\nonHide:$.empty};\n\n\n\n/*\n * File: Options.NodeStyles.js\n *\n */\n\n/*\n  Object: Options.NodeStyles\n  \n  Apply different styles when a node is hovered or selected.\n  \n  Syntax:\n    \n  (start code js)\n  Options.NodeStyles = {\n    enable: false,\n    type: 'auto',\n    stylesHover: false,\n    stylesClick: false\n  };\n  (end code)\n  \n  Example:\n  \n  (start code js)\n  var viz = new $jit.Viz({\n    NodeStyles: {\n      enable: true,\n      type: 'Native',\n      stylesHover: {\n        dim: 30,\n        color: '#fcc'\n      },\n      duration: 600\n    }\n  });\n  (end code)\n\n  Parameters:\n  \n  enable - (boolean) Default's *false*. Whether to enable this option.\n  type - (string) Default's *auto*. Use this to attach the hover/click events in the nodes or the nodes labels (if they have been defined as DOM elements: 'HTML' or 'SVG', see <Options.Label> for more details). The default 'auto' value will set NodeStyles to the same type defined for <Options.Label>.\n  stylesHover - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.\n  stylesClick - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.\n*/\n\nOptions.NodeStyles={\n$extend:false,\n\nenable:false,\ntype:'auto',\nstylesHover:false,\nstylesClick:false};\n\n\n\n/*\n * File: Options.Events.js\n *\n*/\n\n/*\n  Object: Options.Events\n  \n  Configuration for adding mouse/touch event handlers to Nodes.\n  \n  Syntax:\n  \n  (start code js)\n  Options.Events = {\n    enable: false,\n    enableForEdges: false,\n    type: 'auto',\n    onClick: $.empty,\n    onRightClick: $.empty,\n    onMouseMove: $.empty,\n    onMouseEnter: $.empty,\n    onMouseLeave: $.empty,\n    onDragStart: $.empty,\n    onDragMove: $.empty,\n    onDragCancel: $.empty,\n    onDragEnd: $.empty,\n    onTouchStart: $.empty,\n    onTouchMove: $.empty,\n    onTouchEnd: $.empty,\n    onTouchCancel: $.empty,\n    onMouseWheel: $.empty\n  };\n  (end code)\n  \n  Example:\n  \n  (start code js)\n  var viz = new $jit.Viz({\n    Events: {\n      enable: true,\n      onClick: function(node, eventInfo, e) {\n        viz.doSomething();\n      },\n      onMouseEnter: function(node, eventInfo, e) {\n        viz.canvas.getElement().style.cursor = 'pointer';\n      },\n      onMouseLeave: function(node, eventInfo, e) {\n        viz.canvas.getElement().style.cursor = '';\n      }\n    }\n  });\n  (end code)\n  \n  Parameters:\n  \n  enable - (boolean) Default's *false*. Whether to enable the Event system.\n  enableForEdges - (boolean) Default's *false*. Whether to track events also in arcs. If *true* the same callbacks -described below- are used for nodes *and* edges. A simple duck type check for edges is to check for *node.nodeFrom*.\n  type - (string) Default's 'auto'. Whether to attach the events onto the HTML labels (via event delegation) or to use the custom 'Native' canvas Event System of the library. 'auto' is set when you let the <Options.Label> *type* parameter decide this.\n  onClick(node, eventInfo, e) - Triggered when a user performs a click in the canvas. *node* is the <Graph.Node> clicked or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. \n  onRightClick(node, eventInfo, e) - Triggered when a user performs a right click in the canvas. *node* is the <Graph.Node> right clicked or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. \n  onMouseMove(node, eventInfo, e) - Triggered when the user moves the mouse. *node* is the <Graph.Node> under the cursor as it's moving over the canvas or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner).  *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.\n  onMouseEnter(node, eventInfo, e) - Triggered when a user moves the mouse over a node. *node* is the <Graph.Node> that the mouse just entered. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. \n  onMouseLeave(node, eventInfo, e) - Triggered when the user mouse-outs a node. *node* is the <Graph.Node> 'mouse-outed'. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. \n  onDragStart(node, eventInfo, e) - Triggered when the user mouse-downs over a node. *node* is the <Graph.Node> being pressed. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. \n  onDragMove(node, eventInfo, e) - Triggered when a user, after pressing the mouse button over a node, moves the mouse around. *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. \n  onDragEnd(node, eventInfo, e) - Triggered when a user finished dragging a node. *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. \n  onDragCancel(node, eventInfo, e) - Triggered when the user releases the mouse button over a <Graph.Node> that wasn't dragged (i.e. the user didn't perform any mouse movement after pressing the mouse button). *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. \n  onTouchStart(node, eventInfo, e) - Behaves just like onDragStart. \n  onTouchMove(node, eventInfo, e) - Behaves just like onDragMove. \n  onTouchEnd(node, eventInfo, e) - Behaves just like onDragEnd. \n  onTouchCancel(node, eventInfo, e) - Behaves just like onDragCancel.\n  onMouseWheel(delta, e) - Triggered when the user uses the mouse scroll over the canvas. *delta* is 1 or -1 depending on the sense of the mouse scroll.\n*/\n\nOptions.Events={\n$extend:false,\n\nenable:false,\nenableForEdges:false,\ntype:'auto',\nonClick:$.empty,\nonRightClick:$.empty,\nonMouseMove:$.empty,\nonMouseEnter:$.empty,\nonMouseLeave:$.empty,\nonDragStart:$.empty,\nonDragMove:$.empty,\nonDragCancel:$.empty,\nonDragEnd:$.empty,\nonTouchStart:$.empty,\nonTouchMove:$.empty,\nonTouchEnd:$.empty,\nonMouseWheel:$.empty};\n\n\n/*\n * File: Options.Navigation.js\n *\n*/\n\n/*\n  Object: Options.Navigation\n  \n  Panning and zooming options for Graph/Tree based visualizations. These options are implemented \n  by all visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).\n  \n  Syntax:\n  \n  (start code js)\n\n  Options.Navigation = {\n    enable: false,\n    type: 'auto',\n    panning: false, //true, 'avoid nodes'\n    zooming: false\n  };\n  \n  (end code)\n  \n  Example:\n    \n  (start code js)\n  var viz = new $jit.Viz({\n    Navigation: {\n      enable: true,\n      panning: 'avoid nodes',\n      zooming: 20\n    }\n  });\n  (end code)\n  \n  Parameters:\n  \n  enable - (boolean) Default's *false*. Whether to enable Navigation capabilities.\n  type - (string) Default's 'auto'. Whether to attach the navigation events onto the HTML labels (via event delegation) or to use the custom 'Native' canvas Event System of the library. When 'auto' set when you let the <Options.Label> *type* parameter decide this.\n  panning - (boolean|string) Default's *false*. Set this property to *true* if you want to add Drag and Drop panning support to the visualization. You can also set this parameter to 'avoid nodes' to enable DnD panning but disable it if the DnD is taking place over a node. This is useful when some other events like Drag & Drop for nodes are added to <Graph.Nodes>.\n  zooming - (boolean|number) Default's *false*. Set this property to a numeric value to turn mouse-scroll zooming on. The number will be proportional to the mouse-scroll sensitivity.\n  \n*/\n\nOptions.Navigation={\n$extend:false,\n\nenable:false,\ntype:'auto',\npanning:false,//true | 'avoid nodes'\nzooming:false};\n\n\n/*\n * File: Options.Controller.js\n *\n*/\n\n/*\n  Object: Options.Controller\n  \n  Provides controller methods. Controller methods are callback functions that get called at different stages \n  of the animation, computing or plotting of the visualization.\n  \n  Implemented by:\n    \n  All visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).\n  \n  Syntax:\n  \n  (start code js)\n\n  Options.Controller = {\n    onBeforeCompute: $.empty,\n    onAfterCompute:  $.empty,\n    onCreateLabel:   $.empty,\n    onPlaceLabel:    $.empty,\n    onComplete:      $.empty,\n    onBeforePlotLine:$.empty,\n    onAfterPlotLine: $.empty,\n    onBeforePlotNode:$.empty,\n    onAfterPlotNode: $.empty,\n    request:         false\n  };\n  \n  (end code)\n  \n  Example:\n    \n  (start code js)\n  var viz = new $jit.Viz({\n    onBeforePlotNode: function(node) {\n      if(node.selected) {\n        node.setData('color', '#ffc');\n      } else {\n        node.removeData('color');\n      }\n    },\n    onBeforePlotLine: function(adj) {\n      if(adj.nodeFrom.selected && adj.nodeTo.selected) {\n        adj.setData('color', '#ffc');\n      } else {\n        adj.removeData('color');\n      }\n    },\n    onAfterCompute: function() {\n      alert(\"computed!\");\n    }\n  });\n  (end code)\n  \n  Parameters:\n\n   onBeforeCompute(node) - This method is called right before performing all computations and animations. The selected <Graph.Node> is passed as parameter.\n   onAfterCompute() - This method is triggered after all animations or computations ended.\n   onCreateLabel(domElement, node) - This method receives a new label DIV element as first parameter, and the corresponding <Graph.Node> as second parameter. This method will only be called once for each label. This method is useful when adding events or styles to the labels used by the JIT.\n   onPlaceLabel(domElement, node) - This method receives a label DIV element as first parameter and the corresponding <Graph.Node> as second parameter. This method is called each time a label has been placed in the visualization, for example at each step of an animation, and thus it allows you to update the labels properties, such as size or position. Note that onPlaceLabel will be triggered after updating the labels positions. That means that, for example, the left and top css properties are already updated to match the nodes positions. Width and height properties are not set however.\n   onBeforePlotNode(node) - This method is triggered right before plotting each <Graph.Node>. This method is useful for changing a node style right before plotting it.\n   onAfterPlotNode(node) - This method is triggered right after plotting each <Graph.Node>.\n   onBeforePlotLine(adj) - This method is triggered right before plotting a <Graph.Adjacence>. This method is useful for adding some styles to a particular edge before being plotted.\n   onAfterPlotLine(adj) - This method is triggered right after plotting a <Graph.Adjacence>.\n\n    *Used in <ST>, <TM.Base> and <Icicle> visualizations*\n    \n    request(nodeId, level, onComplete) - This method is used for buffering information into the visualization. When clicking on an empty node, the visualization will make a request for this node's subtrees, specifying a given level for this subtree (defined by _levelsToShow_). Once the request is completed, the onComplete callback should be called with the given result. This is useful to provide on-demand information into the visualizations withought having to load the entire information from start. The parameters used by this method are _nodeId_, which is the id of the root of the subtree to request, _level_ which is the depth of the subtree to be requested (0 would mean just the root node). _onComplete_ is an object having the callback method _onComplete.onComplete(json)_ that should be called once the json has been retrieved.  \n \n */\nOptions.Controller={\n$extend:true,\n\nonBeforeCompute:$.empty,\nonAfterCompute:$.empty,\nonCreateLabel:$.empty,\nonPlaceLabel:$.empty,\nonComplete:$.empty,\nonBeforePlotLine:$.empty,\nonAfterPlotLine:$.empty,\nonBeforePlotNode:$.empty,\nonAfterPlotNode:$.empty,\nrequest:false};\n\n\n\n/*\n * File: Extras.js\n * \n * Provides Extras such as Tips and Style Effects.\n * \n * Description:\n * \n * Provides the <Tips> and <NodeStyles> classes and functions.\n *\n */\n\n/*\n * Manager for mouse events (clicking and mouse moving).\n * \n * This class is used for registering objects implementing onClick\n * and onMousemove methods. These methods are called when clicking or\n * moving the mouse around  the Canvas.\n * For now, <Tips> and <NodeStyles> are classes implementing these methods.\n * \n */\nvar ExtrasInitializer={\ninitialize:function initialize(className,viz){\nthis.viz=viz;\nthis.canvas=viz.canvas;\nthis.config=viz.config[className];\nthis.nodeTypes=viz.fx.nodeTypes;\nvar type=this.config.type;\nthis.dom=type=='auto'?viz.config.Label.type!='Native':type!='Native';\nthis.labelContainer=this.dom&&viz.labels.getLabelContainer();\nthis.isEnabled()&&this.initializePost();\n},\ninitializePost:$.empty,\nsetAsProperty:$.lambda(false),\nisEnabled:function isEnabled(){\nreturn this.config.enable;\n},\nisLabel:function isLabel(e,win,group){\ne=$.event.get(e,win);\nvar labelContainer=this.labelContainer,\ntarget=e.target||e.srcElement,\nrelated=e.relatedTarget;\nif(group){\nreturn related&&related==this.viz.canvas.getCtx().canvas&&\n!!target&&this.isDescendantOf(target,labelContainer);\n}else{\nreturn this.isDescendantOf(target,labelContainer);\n}\n},\nisDescendantOf:function isDescendantOf(elem,par){\nwhile(elem&&elem.parentNode){\nif(elem.parentNode==par)\nreturn elem;\nelem=elem.parentNode;\n}\nreturn false;\n}};\n\n\nvar EventsInterface={\nonMouseUp:$.empty,\nonMouseDown:$.empty,\nonMouseMove:$.empty,\nonMouseOver:$.empty,\nonMouseOut:$.empty,\nonMouseWheel:$.empty,\nonTouchStart:$.empty,\nonTouchMove:$.empty,\nonTouchEnd:$.empty,\nonTouchCancel:$.empty};\n\n\nvar MouseEventsManager=new Class({\ninitialize:function initialize(viz){\nthis.viz=viz;\nthis.canvas=viz.canvas;\nthis.node=false;\nthis.edge=false;\nthis.registeredObjects=[];\nthis.attachEvents();\n},\n\nattachEvents:function attachEvents(){\nvar htmlCanvas=this.canvas.getElement(),\nthat=this;\nhtmlCanvas.oncontextmenu=$.lambda(false);\n$.addEvents(htmlCanvas,{\n'mouseup':function mouseup(e,win){\nvar event=$.event.get(e,win);\nthat.handleEvent('MouseUp',e,win,\nthat.makeEventObject(e,win),\n$.event.isRightClick(event));\n},\n'mousedown':function mousedown(e,win){\nvar event=$.event.get(e,win);\nthat.handleEvent('MouseDown',e,win,that.makeEventObject(e,win),\n$.event.isRightClick(event));\n},\n'mousemove':function mousemove(e,win){\nthat.handleEvent('MouseMove',e,win,that.makeEventObject(e,win));\n},\n'mouseover':function mouseover(e,win){\nthat.handleEvent('MouseOver',e,win,that.makeEventObject(e,win));\n},\n'mouseout':function mouseout(e,win){\nthat.handleEvent('MouseOut',e,win,that.makeEventObject(e,win));\n},\n'touchstart':function touchstart(e,win){\nthat.handleEvent('TouchStart',e,win,that.makeEventObject(e,win));\n},\n'touchmove':function touchmove(e,win){\nthat.handleEvent('TouchMove',e,win,that.makeEventObject(e,win));\n},\n'touchend':function touchend(e,win){\nthat.handleEvent('TouchEnd',e,win,that.makeEventObject(e,win));\n}});\n\n//attach mousewheel event\nvar handleMouseWheel=function handleMouseWheel(e,win){\nvar event=$.event.get(e,win);\nvar wheel=$.event.getWheel(event);\nthat.handleEvent('MouseWheel',e,win,wheel);\n};\n//TODO(nico): this is a horrible check for non-gecko browsers!\nif(!document.getBoxObjectFor&&window.mozInnerScreenX==null){\n$.addEvent(htmlCanvas,'mousewheel',handleMouseWheel);\n}else{\nhtmlCanvas.addEventListener('DOMMouseScroll',handleMouseWheel,false);\n}\n},\n\nregister:function register(obj){\nthis.registeredObjects.push(obj);\n},\n\nhandleEvent:function handleEvent(){\nvar args=Array.prototype.slice.call(arguments),\ntype=args.shift();\nfor(var i=0,regs=this.registeredObjects,l=regs.length;i<l;i++){\nregs[i]['on'+type].apply(regs[i],args);\n}\n},\n\nmakeEventObject:function makeEventObject(e,win){\nvar that=this,\ngraph=this.viz.graph,\nfx=this.viz.fx,\nntypes=fx.nodeTypes,\netypes=fx.edgeTypes;\nreturn{\npos:false,\nnode:false,\nedge:false,\ncontains:false,\ngetNodeCalled:false,\ngetEdgeCalled:false,\ngetPos:function getPos(){\n//TODO(nico): check why this can't be cache anymore when using edge detection\n//if(this.pos) return this.pos;\nvar canvas=that.viz.canvas,\ns=canvas.getSize(),\np=canvas.getPos(),\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY,\npos=$.event.getPos(e,win);\nthis.pos={\nx:(pos.x-p.x-s.width/2-ox)*1/sx,\ny:(pos.y-p.y-s.height/2-oy)*1/sy};\n\nreturn this.pos;\n},\ngetNode:function getNode(){\nif(this.getNodeCalled)return this.node;\nthis.getNodeCalled=true;\nfor(var id in graph.nodes){\nvar n=graph.nodes[id],\ngeom=n&&ntypes[n.getData('type')],\n\n// START METAMAPS CODE\ncontains=n.getData('alpha')!==0&&geom&&geom.contains&&geom.contains.call(fx,n,this.getPos());\n// END METAMAPS CODE\n// ORIGINAL CODE contains = geom && geom.contains && geom.contains.call(fx, n, this.getPos());\n\nif(contains){\nthis.contains=contains;\nreturn that.node=this.node=n;\n}\n}\nreturn that.node=this.node=false;\n},\ngetEdge:function getEdge(){\nif(this.getEdgeCalled)return this.edge;\nthis.getEdgeCalled=true;\nvar hashset={};\nfor(var id in graph.edges){\nvar edgeFrom=graph.edges[id];\nhashset[id]=true;\nfor(var edgeId in edgeFrom){\nif(edgeId in hashset)continue;\nvar e=edgeFrom[edgeId],\ngeom=e&&etypes[e.getData('type')],\n\n// START METAMAPS CODE\ncontains=e.getData('alpha')!==0&&geom&&geom.contains&&geom.contains.call(fx,e,this.getPos());\n// END METAMAPS CODE\n// ORIGINAL CODE contains = geom && geom.contains && geom.contains.call(fx, n, this.getPos());\nif(contains){\nthis.contains=contains;\nreturn that.edge=this.edge=e;\n}\n}\n}\nreturn that.edge=this.edge=false;\n},\ngetContains:function getContains(){\nif(this.getNodeCalled)return this.contains;\nthis.getNode();\nreturn this.contains;\n}};\n\n}});\n\n\n/* \n * Provides the initialization function for <NodeStyles> and <Tips> implemented \n * by all main visualizations.\n *\n */\nvar Extras={\ninitializeExtras:function initializeExtras(){\nvar mem=new MouseEventsManager(this),that=this;\n$.each(['NodeStyles','Tips','Navigation','Events'],function(k){\nvar obj=new Extras.Classes[k](k,that);\nif(obj.isEnabled()){\nmem.register(obj);\n}\nif(obj.setAsProperty()){\nthat[k.toLowerCase()]=obj;\n}\n});\n}};\n\n\nExtras.Classes={};\n/*\n  Class: Events\n   \n  This class defines an Event API to be accessed by the user.\n  The methods implemented are the ones defined in the <Options.Events> object.\n*/\n\nExtras.Classes.Events=new Class({\nImplements:[ExtrasInitializer,EventsInterface],\n\ninitializePost:function initializePost(){\nthis.fx=this.viz.fx;\nthis.ntypes=this.viz.fx.nodeTypes;\nthis.etypes=this.viz.fx.edgeTypes;\n\nthis.hovered=false;\nthis.pressed=false;\nthis.touched=false;\n\nthis.touchMoved=false;\nthis.moved=false;\n\n},\n\nsetAsProperty:$.lambda(true),\n\nonMouseUp:function onMouseUp(e,win,event,isRightClick){\nvar evt=$.event.get(e,win);\nif(!this.moved){\nif(isRightClick){\nthis.config.onRightClick(this.hovered,event,evt);\n}else{\nthis.config.onClick(this.pressed,event,evt);\n}\n}\nif(this.pressed){\nif(this.moved){\nthis.config.onDragEnd(this.pressed,event,evt);\n}else{\nthis.config.onDragCancel(this.pressed,event,evt);\n}\nthis.pressed=this.moved=false;\n}\n},\n\nonMouseOut:function onMouseOut(e,win,event){\n//mouseout a label\nvar evt=$.event.get(e,win),label;\nif(this.dom&&(label=this.isLabel(e,win,true))){\nthis.config.onMouseLeave(this.viz.graph.getNode(label.id),\nevent,evt);\nthis.hovered=false;\nreturn;\n}\n//mouseout canvas\nvar rt=evt.relatedTarget,\ncanvasWidget=this.canvas.getElement();\nwhile(rt&&rt.parentNode){\nif(canvasWidget==rt.parentNode)return;\nrt=rt.parentNode;\n}\nif(this.hovered){\nthis.config.onMouseLeave(this.hovered,\nevent,evt);\nthis.hovered=false;\n}\n},\n\nonMouseOver:function onMouseOver(e,win,event){\n//mouseover a label\nvar evt=$.event.get(e,win),label;\nif(this.dom&&(label=this.isLabel(e,win,true))){\nthis.hovered=this.viz.graph.getNode(label.id);\nthis.config.onMouseEnter(this.hovered,\nevent,evt);\n}\n},\n\nonMouseMove:function onMouseMove(e,win,event){\nvar label,evt=$.event.get(e,win);\nif(this.pressed){\nthis.moved=true;\nthis.config.onDragMove(this.pressed,event,evt);\nreturn;\n}\nif(this.dom){\nthis.config.onMouseMove(this.hovered,\nevent,evt);\n}else{\nif(this.hovered){\nvar hn=this.hovered;\nvar geom=hn.nodeFrom?this.etypes[hn.getData('type')]:this.ntypes[hn.getData('type')];\nvar contains=geom&&geom.contains&&\ngeom.contains.call(this.fx,hn,event.getPos());\nif(contains){\nthis.config.onMouseMove(hn,event,evt);\nreturn;\n}else{\nthis.config.onMouseLeave(hn,event,evt);\nthis.hovered=false;\n}\n}\nif(this.hovered=event.getNode()||this.config.enableForEdges&&event.getEdge()){\nthis.config.onMouseEnter(this.hovered,event,evt);\n}else{\nthis.config.onMouseMove(false,event,evt);\n}\n}\n},\n\nonMouseWheel:function onMouseWheel(e,win,delta){\nthis.config.onMouseWheel(delta,$.event.get(e,win));\n},\n\nonMouseDown:function onMouseDown(e,win,event){\n\n// START METAMAPS CODE\nvar evt=$.event.get(e,win);\nthis.pressed=event.getNode()||this.config.enableForEdges&&event.getEdge();\n// END METAMAPS CODE    \n// ORIGINAL CODE\n/*var evt = $.event.get(e, win), label;\n    if(this.dom) {\n      if(label = this.isLabel(e, win)) {\n        this.pressed = this.viz.graph.getNode(label.id);\n      }\n    } else {\n      this.pressed = event.getNode() || (this.config.enableForEdges && event.getEdge());\n    } */\nthis.pressed&&this.config.onDragStart(this.pressed,event,evt);\n},\n\nonTouchStart:function onTouchStart(e,win,event){\nvar evt=$.event.get(e,win),label;\nif(this.dom&&(label=this.isLabel(e,win))){\nthis.touched=this.viz.graph.getNode(label.id);\n}else{\nthis.touched=event.getNode()||this.config.enableForEdges&&event.getEdge();\n}\nthis.touched&&this.config.onTouchStart(this.touched,event,evt);\n},\n\nonTouchMove:function onTouchMove(e,win,event){\nvar evt=$.event.get(e,win);\nif(this.touched){\nthis.touchMoved=true;\nthis.config.onTouchMove(this.touched,event,evt);\n}\n},\n\nonTouchEnd:function onTouchEnd(e,win,event){\nvar evt=$.event.get(e,win);\nif(this.touched){\nif(this.touchMoved){\nthis.config.onTouchEnd(this.touched,event,evt);\n}else{\nthis.config.onTouchCancel(this.touched,event,evt);\n}\nthis.touched=this.touchMoved=false;\n}\n}});\n\n\n/*\n   Class: Tips\n    \n   A class containing tip related functions. This class is used internally.\n   \n   Used by:\n   \n   <ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>\n   \n   See also:\n   \n   <Options.Tips>\n*/\n\nExtras.Classes.Tips=new Class({\nImplements:[ExtrasInitializer,EventsInterface],\n\ninitializePost:function initializePost(){\n//add DOM tooltip\nif(document.body){\nvar tip=$('_tooltip')||document.createElement('div');\ntip.id='_tooltip';\ntip.className='tip';\n$.extend(tip.style,{\nposition:'absolute',\ndisplay:'none',\nzIndex:13000});\n\ndocument.body.appendChild(tip);\nthis.tip=tip;\nthis.node=false;\n}\n},\n\nsetAsProperty:$.lambda(true),\n\nonMouseOut:function onMouseOut(e,win){\n//mouseout a label\nvar evt=$.event.get(e,win);\nif(this.dom&&this.isLabel(e,win,true)){\nthis.hide(true);\nreturn;\n}\n//mouseout canvas\nvar rt=e.relatedTarget,\ncanvasWidget=this.canvas.getElement();\nwhile(rt&&rt.parentNode){\nif(canvasWidget==rt.parentNode)return;\nrt=rt.parentNode;\n}\nthis.hide(false);\n},\n\nonMouseOver:function onMouseOver(e,win){\n//mouseover a label\nvar label;\nif(this.dom&&(label=this.isLabel(e,win,false))){\nthis.node=this.viz.graph.getNode(label.id);\nthis.config.onShow(this.tip,this.node,label);\n}\n},\n\nonMouseMove:function onMouseMove(e,win,opt){\nif(this.dom&&this.isLabel(e,win)){\nthis.setTooltipPosition($.event.getPos(e,win));\n}\nif(!this.dom){\nvar node=opt.getNode();\nif(!node){\nthis.hide(true);\nreturn;\n}\nif(this.config.force||!this.node||this.node.id!=node.id){\nthis.node=node;\nthis.config.onShow(this.tip,node,opt.getContains());\n}\nthis.setTooltipPosition($.event.getPos(e,win));\n}\n},\n\nsetTooltipPosition:function setTooltipPosition(pos){\nvar tip=this.tip,\nstyle=tip.style,\ncont=this.config;\nstyle.display='';\n//get window dimensions\nvar win={\n'height':document.body.clientHeight,\n'width':document.body.clientWidth};\n\n//get tooltip dimensions\nvar obj={\n'width':tip.offsetWidth,\n'height':tip.offsetHeight};\n\n//set tooltip position\nvar x=cont.offsetX,y=cont.offsetY;\nstyle.top=(pos.y+y+obj.height>win.height?\npos.y-obj.height-y:pos.y+y)+'px';\nstyle.left=(pos.x+obj.width+x>win.width?\npos.x-obj.width-x:pos.x+x)+'px';\n},\n\nhide:function hide(triggerCallback){\nthis.tip.style.display='none';\ntriggerCallback&&this.config.onHide();\n}});\n\n\n/*\n  Class: NodeStyles\n   \n  Change node styles when clicking or hovering a node. This class is used internally.\n  \n  Used by:\n  \n  <ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>\n  \n  See also:\n  \n  <Options.NodeStyles>\n*/\nExtras.Classes.NodeStyles=new Class({\nImplements:[ExtrasInitializer,EventsInterface],\n\ninitializePost:function initializePost(){\nthis.fx=this.viz.fx;\nthis.types=this.viz.fx.nodeTypes;\nthis.nStyles=this.config;\nthis.nodeStylesOnHover=this.nStyles.stylesHover;\nthis.nodeStylesOnClick=this.nStyles.stylesClick;\nthis.hoveredNode=false;\nthis.fx.nodeFxAnimation=new Animation();\n\nthis.down=false;\nthis.move=false;\n},\n\nonMouseOut:function onMouseOut(e,win){\nthis.down=this.move=false;\nif(!this.hoveredNode)return;\n//mouseout a label\nif(this.dom&&this.isLabel(e,win,true)){\nthis.toggleStylesOnHover(this.hoveredNode,false);\n}\n//mouseout canvas\nvar rt=e.relatedTarget,\ncanvasWidget=this.canvas.getElement();\nwhile(rt&&rt.parentNode){\nif(canvasWidget==rt.parentNode)return;\nrt=rt.parentNode;\n}\nthis.toggleStylesOnHover(this.hoveredNode,false);\nthis.hoveredNode=false;\n},\n\nonMouseOver:function onMouseOver(e,win){\n//mouseover a label\nvar label;\nif(this.dom&&(label=this.isLabel(e,win,true))){\nvar node=this.viz.graph.getNode(label.id);\nif(node.selected)return;\nthis.hoveredNode=node;\nthis.toggleStylesOnHover(this.hoveredNode,true);\n}\n},\n\nonMouseDown:function onMouseDown(e,win,event,isRightClick){\nif(isRightClick)return;\nvar label;\nif(this.dom&&(label=this.isLabel(e,win))){\nthis.down=this.viz.graph.getNode(label.id);\n}else if(!this.dom){\nthis.down=event.getNode();\n}\nthis.move=false;\n},\n\nonMouseUp:function onMouseUp(e,win,event,isRightClick){\nif(isRightClick)return;\nif(!this.move){\nthis.onClick(event.getNode());\n}\nthis.down=this.move=false;\n},\n\ngetRestoredStyles:function getRestoredStyles(node,type){\nvar restoredStyles={},\nnStyles=this['nodeStylesOn'+type];\nfor(var prop in nStyles){\nrestoredStyles[prop]=node.styles['$'+prop];\n}\nreturn restoredStyles;\n},\n\ntoggleStylesOnHover:function toggleStylesOnHover(node,set){\nif(this.nodeStylesOnHover){\nthis.toggleStylesOn('Hover',node,set);\n}\n},\n\ntoggleStylesOnClick:function toggleStylesOnClick(node,set){\nif(this.nodeStylesOnClick){\nthis.toggleStylesOn('Click',node,set);\n}\n},\n\ntoggleStylesOn:function toggleStylesOn(type,node,set){\nvar viz=this.viz;\nvar nStyles=this.nStyles;\nif(set){\nvar that=this;\nif(!node.styles){\nnode.styles=$.merge(node.data,{});\n}\nfor(var s in this['nodeStylesOn'+type]){\nvar $s='$'+s;\nif(!($s in node.styles)){\nnode.styles[$s]=node.getData(s);\n}\n}\nviz.fx.nodeFx($.extend({\n'elements':{\n'id':node.id,\n'properties':that['nodeStylesOn'+type]},\n\ntransition:Trans.Quart.easeOut,\nduration:300,\nfps:40},\nthis.config));\n}else{\nvar restoredStyles=this.getRestoredStyles(node,type);\nviz.fx.nodeFx($.extend({\n'elements':{\n'id':node.id,\n'properties':restoredStyles},\n\ntransition:Trans.Quart.easeOut,\nduration:300,\nfps:40},\nthis.config));\n}\n},\n\nonClick:function onClick(node){\nif(!node)return;\nvar nStyles=this.nodeStylesOnClick;\nif(!nStyles)return;\n//if the node is selected then unselect it\nif(node.selected){\nthis.toggleStylesOnClick(node,false);\ndelete node.selected;\n}else{\n//unselect all selected nodes...\nthis.viz.graph.eachNode(function(n){\nif(n.selected){\nfor(var s in nStyles){\nn.setData(s,n.styles['$'+s],'end');\n}\ndelete n.selected;\n}\n});\n//select clicked node\nthis.toggleStylesOnClick(node,true);\nnode.selected=true;\ndelete node.hovered;\nthis.hoveredNode=false;\n}\n},\n\nonMouseMove:function onMouseMove(e,win,event){\n//if mouse button is down and moving set move=true\nif(this.down)this.move=true;\n//already handled by mouseover/out\nif(this.dom&&this.isLabel(e,win))return;\nvar nStyles=this.nodeStylesOnHover;\nif(!nStyles)return;\n\nif(!this.dom){\nif(this.hoveredNode){\nvar geom=this.types[this.hoveredNode.getData('type')];\nvar contains=geom&&geom.contains&&geom.contains.call(this.fx,\nthis.hoveredNode,event.getPos());\nif(contains)return;\n}\nvar node=event.getNode();\n//if no node is being hovered then just exit\nif(!this.hoveredNode&&!node)return;\n//if the node is hovered then exit\nif(node.hovered)return;\n//select hovered node\nif(node&&!node.selected){\n//check if an animation is running and exit it\nthis.fx.nodeFxAnimation.stopTimer();\n//unselect all hovered nodes...\nthis.viz.graph.eachNode(function(n){\nif(n.hovered&&!n.selected){\nfor(var s in nStyles){\nn.setData(s,n.styles['$'+s],'end');\n}\ndelete n.hovered;\n}\n});\n//select hovered node\nnode.hovered=true;\nthis.hoveredNode=node;\nthis.toggleStylesOnHover(node,true);\n}else if(this.hoveredNode&&!this.hoveredNode.selected){\n//check if an animation is running and exit it\nthis.fx.nodeFxAnimation.stopTimer();\n//unselect hovered node\nthis.toggleStylesOnHover(this.hoveredNode,false);\ndelete this.hoveredNode.hovered;\nthis.hoveredNode=false;\n}\n}\n}});\n\n\nExtras.Classes.Navigation=new Class({\nImplements:[ExtrasInitializer,EventsInterface],\n\ninitializePost:function initializePost(){\nthis.pos=false;\nthis.pressed=false;\n// START METAMAPS CODE\nthis.initDist=false;\n// END METAMAPS CODE\n},\n\nonMouseWheel:function onMouseWheel(e,win,scroll){\nif(!this.config.zooming)return;\n\n// START METAMAPS CODE\ne.preventDefault();\nif(e.target.id!='infovis-canvas')return;\nif(Metamaps.Create.newTopic.beingCreated&&!Metamaps.Create.newTopic.pinned)return;\n// END METAMAPS CODE\n\n//$.event.stop($.event.get(e, win));\n// END METAMAPS CODE\n// ORIGINAL CODE $.event.stop($.event.get(e, win));\n\nvar val=this.config.zooming/1000,\nans=1+scroll*val;\n\n// START METAMAPS CODE\nif(ans>1&&5>=this.canvas.scaleOffsetX||ans<1&&this.canvas.scaleOffsetX>=0.2){\nvar s=this.canvas.getSize(),\np=this.canvas.getPos(),\nox=this.canvas.translateOffsetX,\noy=this.canvas.translateOffsetY,\nsx=this.canvas.scaleOffsetX,\nsy=this.canvas.scaleOffsetY;\n\n//Basically this is just a duplication of the Util function pixelsToCoords, it finds the canvas coordinate of the mouse pointer\nvar pointerCoordX=(e.pageX-p.x-s.width/2-ox)*(1/sx),\npointerCoordY=(e.pageY-p.y-s.height/2-oy)*(1/sy);\n\n//This translates the canvas to be centred over the mouse pointer, then the canvas is zoomed as intended.\nthis.canvas.translate(-pointerCoordX,-pointerCoordY);\nthis.canvas.scale(ans,ans);\n\n//Get the canvas attributes again now that is has changed\ns=this.canvas.getSize(),\np=this.canvas.getPos(),\nox=this.canvas.translateOffsetX,\noy=this.canvas.translateOffsetY,\nsx=this.canvas.scaleOffsetX,\nsy=this.canvas.scaleOffsetY;\nvar newX=(e.pageX-p.x-s.width/2-ox)*(1/sx),\nnewY=(e.pageY-p.y-s.height/2-oy)*(1/sy);\n\n//Translate the canvas to put the pointer back over top the same coordinate it was over before\nthis.canvas.translate(newX-pointerCoordX,newY-pointerCoordY);\n}\n\n// END METAMAPS CODE\n// ORIGINAL CODE this.canvas.scale(ans, ans);\n\n// START METAMAPS CODE\njQuery(document).trigger(Metamaps.JIT.events.zoom,[e]);\n// END METAMAPS CODE\n},\n\nonMouseDown:function onMouseDown(e,win,eventInfo){\n///console.log('mouse down!!!!');\nif(!this.config.panning)return;\n\n//START METAMAPS CODE\nMetamaps.Mouse.changeInX=0;\nMetamaps.Mouse.changeInY=0;\nif(this.config.panning=='avoid nodes'&&eventInfo.getNode()||eventInfo.getEdge())return;\n// END METAMAPS CODE\n// ORIGINAl CODE if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;\n\nthis.pressed=true;\n\n//START METAMAPS CODE\nvar rightClick=e.button==2||navigator.platform.indexOf(\"Mac\")!=-1&&e.ctrlKey;\n// TODO make sure this works across browsers  \nif(!Metamaps.Mouse.boxStartCoordinates&&(e.button==0&&e.shiftKey||e.button==0&&e.ctrlKey||rightClick)){\nMetamaps.Mouse.boxStartCoordinates=eventInfo.getPos();\n//console.log('mouse down');\n}\n\nMetamaps.Mouse.didPan=false;\n\n\n\n// END METAMAPS CODE\n\nthis.pos=eventInfo.getPos();\nvar canvas=this.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY;\nthis.pos.x*=sx;\nthis.pos.x+=ox;\nthis.pos.y*=sy;\nthis.pos.y+=oy;\n},\n\nonMouseMove:function onMouseMove(e,win,eventInfo){\nif(!this.config.panning)return;\nif(!this.pressed)return;\nif(this.config.panning=='avoid nodes'&&(this.dom?this.isLabel(e,win):eventInfo.getNode()))return;\n\n// START METAMAPS CODE\nvar rightClick=e.button==2||navigator.platform.indexOf(\"Mac\")!=-1&&e.ctrlKey;\nif(!Metamaps.Mouse.boxStartCoordinates&&(e.button==0&&e.shiftKey||e.button==0&&e.ctrlKey||rightClick)){\nMetamaps.Visualize.mGraph.busy=true;\nMetamaps.boxStartCoordinates=eventInfo.getPos();\n//console.log('mouse move');\nreturn;\n}\nif(Metamaps.Mouse.boxStartCoordinates&&(e.button==0&&e.shiftKey||e.button==0&&e.ctrlKey||rightClick)){\nMetamaps.Visualize.mGraph.busy=true;\nMetamaps.Mouse.boxEndCoordinates={\nx:eventInfo.getPos().x,\ny:eventInfo.getPos().y};\n\nMetamaps.Visualize.mGraph.plot();\n//console.log('mouse move');\nreturn;\n}\nif(rightClick){\nreturn;\n}\nif(e.target.id!='infovis-canvas'){\nthis.pressed=false;\nreturn;\n}\nMetamaps.Mouse.didPan=true;\n// END METAMAPS CODE\n\nvar thispos=this.pos,\ncurrentPos=eventInfo.getPos(),\ncanvas=this.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY;\ncurrentPos.x*=sx;\ncurrentPos.y*=sy;\ncurrentPos.x+=ox;\ncurrentPos.y+=oy;\nvar x=currentPos.x-thispos.x,\ny=currentPos.y-thispos.y;\n\n// START METAMAPS CODE\nMetamaps.Mouse.changeInX=x;\nMetamaps.Mouse.changeInY=y;\n// END METAMAPS CODE\n\nthis.pos=currentPos;\nthis.canvas.translate(x*1/sx,y*1/sy);\n\n// START METAMAPS CODE\njQuery(document).trigger(Metamaps.JIT.events.pan);\n// END METAMAPS CODE\n},\n\nonMouseUp:function onMouseUp(e,win,eventInfo,isRightClick){\nif(!this.config.panning)return;\nthis.pressed=false;\n\n// START METAMAPS CODE\nif(Metamaps.Mouse.didPan)Metamaps.JIT.SmoothPanning();\n// END METAMAPS CODE\n\n},\n\n// START METAMAPS CODE\nonTouchStart:function onTouchStart(e,win,eventInfo){\nif(!this.config.panning)return;\nMetamaps.Mouse.changeInX=0;\nMetamaps.Mouse.changeInY=0;\nif(this.config.panning=='avoid nodes'&&eventInfo.getNode()||eventInfo.getEdge())return;\nthis.pressed=true;\nvar rightClick=e.button==2||navigator.platform.indexOf(\"Mac\")!=-1&&e.ctrlKey;\nif(!Metamaps.Mouse.boxStartCoordinates&&(e.button==0&&e.shiftKey||e.button==0&&e.ctrlKey||rightClick)){\nMetamaps.Mouse.boxStartCoordinates=eventInfo.getPos();\n}\nMetamaps.Mouse.didPan=false;\nthis.pos=eventInfo.getPos();\nvar canvas=this.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY;\nthis.pos.x*=sx;\nthis.pos.x+=ox;\nthis.pos.y*=sy;\nthis.pos.y+=oy;\n},\n\nonTouchMove:function onTouchMove(e,win,eventInfo){\nif(!this.config.panning)return;\nif(!this.pressed)return;\nif(this.config.panning=='avoid nodes'&&(this.dom?this.isLabel(e,win):eventInfo.getNode()))return;\n\nif(e.touches.length==1){\nvar rightClick=e.button==2||navigator.platform.indexOf(\"Mac\")!=-1&&e.ctrlKey;\nif(!Metamaps.Mouse.boxStartCoordinates&&(e.button==0&&e.shiftKey||e.button==0&&e.ctrlKey||rightClick)){\nMetamaps.Visualize.mGraph.busy=true;\nMetamaps.boxStartCoordinates=eventInfo.getPos();\nreturn;\n}\nif(Metamaps.Mouse.boxStartCoordinates&&(e.button==0&&e.shiftKey||e.button==0&&e.ctrlKey||rightClick)){\nMetamaps.Visualize.mGraph.busy=true;\nMetamaps.Mouse.boxEndCoordinates={\nx:eventInfo.getPos().x,\ny:eventInfo.getPos().y};\n\nreturn;\n}\nif(rightClick){\nreturn;\n}\nif(e.target.id!='infovis-canvas'){\nthis.pressed=false;\nreturn;\n}\nMetamaps.Mouse.didPan=true;\nvar thispos=this.pos,\ncurrentPos=eventInfo.getPos(),\ncanvas=this.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY;\ncurrentPos.x*=sx;\ncurrentPos.y*=sy;\ncurrentPos.x+=ox;\ncurrentPos.y+=oy;\nvar x=currentPos.x-thispos.x,\ny=currentPos.y-thispos.y;\nMetamaps.Mouse.changeInX=x;\nMetamaps.Mouse.changeInY=y;\nthis.pos=currentPos;\nthis.canvas.translate(x*1/sx,y*1/sy);\njQuery(document).trigger(Metamaps.JIT.events.pan);\n}\n/*\n    else if (e.touches.length == 2) {\n      var touch1 = e.touches[0]\n      var touch2 = e.touches[1]\n      var canvas = this.canvas\n      \n      callCount++;\n\n      var dist = Metamaps.Util.getDistance({\n        x: touch1.clientX,\n        y: touch1.clientY\n      }, {\n        x: touch2.clientX,\n        y: touch2.clientY\n      })\n\n      if (!this.initDist) {\n        this.initDist = dist\n        this.initScale = canvas.scaleOffsetX\n      }\n      var scale = (dist / this.initDist)\n      \n      document.getElementById(\"header_content\").innerHTML = scale + ' ' + canvas.scaleOffsetX\n      if (30 >= this.initScale * scale && this.initScale * scale >= 0.2) {\n        canvas.scale(this.initScale * scale, this.initScale * scale)\n      }\n      if (canvas.scaleOffsetX < 0.5) {\n        canvas.viz.labels.hideLabels(true)\n      } else if (canvas.scaleOffsetX > 0.5) {\n        canvas.viz.labels.hideLabels(false)\n      }\n      \n      jQuery(document).trigger(Metamaps.JIT.events.zoom);\n    }\n    */\n},\n\nonTouchEnd:function onTouchEnd(e,win,eventInfo,isRightClick){\nif(!this.config.panning)return;\nthis.pressed=false;\nif(Metamaps.Mouse.didPan)Metamaps.JIT.SmoothPanning();\nthis.initDist=false;\n}\n// END METAMAPS CODE\n});\n\n\n/*\n * File: Canvas.js\n *\n */\n\n/*\n Class: Canvas\n \n \tA canvas widget used by all visualizations. The canvas object can be accessed by doing *viz.canvas*. If you want to \n \tknow more about <Canvas> options take a look at <Options.Canvas>.\n \n A canvas widget is a set of DOM elements that wrap the native canvas DOM Element providing a consistent API and behavior \n across all browsers. It can also include Elements to add DOM (SVG or HTML) label support to all visualizations.\n \n Example:\n \n Suppose we have this HTML\n \n (start code xml)\n \t<div id=\"infovis\"></div>\n (end code)\n \n Now we create a new Visualization\n \n (start code js)\n \tvar viz = new $jit.Viz({\n \t\t//Where to inject the canvas. Any div container will do.\n \t\t'injectInto':'infovis',\n\t\t //width and height for canvas. \n\t\t //Default's to the container offsetWidth and Height.\n\t\t 'width': 900,\n\t\t 'height':500\n\t });\n (end code)\n\n The generated HTML will look like this\n \n (start code xml)\n <div id=\"infovis\">\n \t<div id=\"infovis-canvaswidget\" style=\"position:relative;\">\n \t<canvas id=\"infovis-canvas\" width=900 height=500\n \tstyle=\"position:absolute; top:0; left:0; width:900px; height:500px;\" />\n \t<div id=\"infovis-label\"\n \tstyle=\"overflow:visible; position:absolute; top:0; left:0; width:900px; height:0px\">\n \t</div>\n \t</div>\n </div>\n (end code)\n \n As you can see, the generated HTML consists of a canvas DOM Element of id *infovis-canvas* and a div label container\n of id *infovis-label*, wrapped in a main div container of id *infovis-canvaswidget*.\n */\n\nvar Canvas;\n(function(){\n//check for native canvas support\nvar canvasType=typeof HTMLCanvasElement==='undefined'?'undefined':_typeof(HTMLCanvasElement),\nsupportsCanvas=canvasType=='object'||canvasType=='function';\n//create element function\nfunction $E(tag,props){\nvar elem=document.createElement(tag);\nfor(var p in props){\nif(_typeof(props[p])==\"object\"){\n$.extend(elem[p],props[p]);\n}else{\nelem[p]=props[p];\n}\n}\nif(tag==\"canvas\"&&!supportsCanvas&&G_vmlCanvasManager){\nelem=G_vmlCanvasManager.initElement(document.body.appendChild(elem));\n}\nreturn elem;\n}\n//canvas widget which we will call just Canvas\n$jit.Canvas=Canvas=new Class({\ncanvases:[],\npos:false,\nelement:false,\nlabelContainer:false,\ntranslateOffsetX:0,\ntranslateOffsetY:0,\nscaleOffsetX:1,\nscaleOffsetY:1,\n\ninitialize:function initialize(viz,opt){\nthis.viz=viz;\nthis.opt=this.config=opt;\nvar id=$.type(opt.injectInto)=='string'?\nopt.injectInto:opt.injectInto.id,\ntype=opt.type,\nidLabel=id+\"-label\",\nwrapper=$(id),\nwidth=opt.width,// || wrapper.offsetWidth,\nheight=opt.height;// || wrapper.offsetHeight;\nthis.id=id;\n//canvas options\nvar canvasOptions={\ninjectInto:id,\nwidth:width,\nheight:height};\n\n//create main wrapper\nthis.element=$E('div',{\n'id':id+'-canvaswidget',\n'style':{\n'position':'relative',\n'width':width+'px',\n'height':height+'px'}});\n\n\n//create label container\nthis.labelContainer=this.createLabelContainer(opt.Label.type,\nidLabel,canvasOptions);\n//create primary canvas\nthis.canvases.push(new Canvas.Base[type]({\nconfig:$.extend({idSuffix:'-canvas'},canvasOptions),\nplot:function plot(base){\nviz.fx.plot();\n},\nresize:function resize(){\nviz.refresh();\n}}));\n\n//create secondary canvas\nvar back=opt.background;\nif(back){\nvar backCanvas=new Canvas.Background[back.type](viz,$.extend(back,canvasOptions));\nthis.canvases.push(new Canvas.Base[type](backCanvas));\n}\n//insert canvases\nvar len=this.canvases.length;\nwhile(len--){\nthis.element.appendChild(this.canvases[len].canvas);\nif(len>0){\nthis.canvases[len].plot();\n}\n}\nthis.element.appendChild(this.labelContainer);\nwrapper.appendChild(this.element);\n\n//Update canvas position when the page is scrolled.\nvar timer=null,that=this;\n$.addEvent(window,'scroll',function(){\nclearTimeout(timer);\ntimer=setTimeout(function(){\nthat.getPos(true);//update canvas position\n},500);\n});\n},\n/*\n      Method: getCtx\n      \n      Returns the main canvas context object\n      \n      Example:\n      \n      (start code js)\n       var ctx = canvas.getCtx();\n       //Now I can use the native canvas context\n       //and for example change some canvas styles\n       ctx.globalAlpha = 1;\n      (end code)\n    */\ngetCtx:function getCtx(i){\nreturn this.canvases[i||0].getCtx();\n},\n/*\n      Method: getConfig\n      \n      Returns the current Configuration for this Canvas Widget.\n      \n      Example:\n      \n      (start code js)\n       var config = canvas.getConfig();\n      (end code)\n    */\ngetConfig:function getConfig(){\nreturn this.opt;\n},\n/*\n      Method: getElement\n\n      Returns the main Canvas DOM wrapper\n      \n      Example:\n      \n      (start code js)\n       var wrapper = canvas.getElement();\n       //Returns <div id=\"infovis-canvaswidget\" ... >...</div> as element\n      (end code)\n    */\ngetElement:function getElement(){\nreturn this.element;\n},\n/*\n      Method: getSize\n      \n      Returns canvas dimensions.\n      \n      Returns:\n      \n      An object with *width* and *height* properties.\n      \n      Example:\n      (start code js)\n      canvas.getSize(); //returns { width: 900, height: 500 }\n      (end code)\n    */\ngetSize:function getSize(i){\nreturn this.canvases[i||0].getSize();\n},\n/*\n      Method: resize\n      \n      Resizes the canvas.\n      \n      Parameters:\n      \n      width - New canvas width.\n      height - New canvas height.\n      \n      Example:\n      \n      (start code js)\n       canvas.resize(width, height);\n      (end code)\n    \n    */\nresize:function resize(width,height){\nthis.getPos(true);\nthis.translateOffsetX=this.translateOffsetY=0;\nthis.scaleOffsetX=this.scaleOffsetY=1;\n\nfor(var i=0,l=this.canvases.length;i<l;i++){\nthis.canvases[i].resize(width,height);\n}\nvar style=this.element.style;\nstyle.width=width+'px';\nstyle.height=height+'px';\nif(this.labelContainer)\nthis.labelContainer.style.width=width+'px';\n},\n/*\n      Method: translate\n      \n      Applies a translation to the canvas.\n      \n      Parameters:\n      \n      x - (number) x offset.\n      y - (number) y offset.\n      disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.\n      \n      Example:\n      \n      (start code js)\n       canvas.translate(30, 30);\n      (end code)\n    \n    */\ntranslate:function translate(x,y,disablePlot){\nthis.translateOffsetX+=x*this.scaleOffsetX;\nthis.translateOffsetY+=y*this.scaleOffsetY;\nfor(var i=0,l=this.canvases.length;i<l;i++){\nthis.canvases[i].translate(x,y,disablePlot);\n}\n},\n/*\n      Method: scale\n      \n      Scales the canvas.\n      \n      Parameters:\n      \n      x - (number) scale value.\n      y - (number) scale value.\n      disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.\n      \n      Example:\n      \n      (start code js)\n       canvas.scale(0.5, 0.5);\n      (end code)\n    \n    */\nscale:function scale(x,y,disablePlot){\nvar px=this.scaleOffsetX*x,\npy=this.scaleOffsetY*y;\nvar dx=this.translateOffsetX*(x-1)/px,\ndy=this.translateOffsetY*(y-1)/py;\nthis.scaleOffsetX=px;\nthis.scaleOffsetY=py;\nfor(var i=0,l=this.canvases.length;i<l;i++){\nthis.canvases[i].scale(x,y,true);\n}\nthis.translate(dx,dy,false);\n},\n/*\n      Method: getPos\n      \n      Returns the canvas position as an *x, y* object.\n      \n      Parameters:\n      \n      force - (boolean) Default's *false*. Set this to *true* if you want to recalculate the position without using any cache information.\n      \n      Returns:\n      \n      An object with *x* and *y* properties.\n      \n      Example:\n      (start code js)\n      canvas.getPos(true); //returns { x: 900, y: 500 }\n      (end code)\n    */\ngetPos:function getPos(force){\nif(force||!this.pos){\nreturn this.pos=$.getPos(this.getElement());\n}\nreturn this.pos;\n},\n/*\n       Method: clear\n       \n       Clears the canvas.\n    */\nclear:function clear(i){\nthis.canvases[i||0].clear();\n},\n\npath:function path(type,action){\nvar ctx=this.canvases[0].getCtx();\nctx.beginPath();\naction(ctx);\nctx[type]();\nctx.closePath();\n},\n\ncreateLabelContainer:function createLabelContainer(type,idLabel,dim){\nvar NS='http://www.w3.org/2000/svg';\nif(type=='HTML'||type=='Native'){\nreturn $E('div',{\n'id':idLabel,\n'style':{\n'overflow':'visible',\n'position':'absolute',\n'top':0,\n'left':0,\n'width':dim.width+'px',\n'height':0}});\n\n\n}else if(type=='SVG'){\nvar svgContainer=document.createElementNS(NS,'svg:svg');\nsvgContainer.setAttribute(\"width\",dim.width);\nsvgContainer.setAttribute('height',dim.height);\nvar style=svgContainer.style;\nstyle.position='absolute';\nstyle.left=style.top='0px';\nvar labelContainer=document.createElementNS(NS,'svg:g');\nlabelContainer.setAttribute('width',dim.width);\nlabelContainer.setAttribute('height',dim.height);\nlabelContainer.setAttribute('x',0);\nlabelContainer.setAttribute('y',0);\nlabelContainer.setAttribute('id',idLabel);\nsvgContainer.appendChild(labelContainer);\nreturn svgContainer;\n}\n}});\n\n//base canvas wrapper\nCanvas.Base={};\nCanvas.Base['2D']=new Class({\ntranslateOffsetX:0,\ntranslateOffsetY:0,\nscaleOffsetX:1,\nscaleOffsetY:1,\n\ninitialize:function initialize(viz){\nthis.viz=viz;\nthis.opt=viz.config;\nthis.size=false;\nthis.createCanvas();\nthis.translateToCenter();\n},\ncreateCanvas:function createCanvas(){\nvar opt=this.opt,\nwidth=opt.width,\nheight=opt.height;\nthis.canvas=$E('canvas',{\n'id':opt.injectInto+opt.idSuffix,\n'width':width,\n'height':height,\n'style':{\n'position':'absolute',\n'top':0,\n'left':0,\n'width':width+'px',\n'height':height+'px'}});\n\n\n},\ngetCtx:function getCtx(){\nif(!this.ctx)\nreturn this.ctx=this.canvas.getContext('2d');\nreturn this.ctx;\n},\ngetSize:function getSize(){\nif(this.size)return this.size;\nvar canvas=this.canvas;\nreturn this.size={\nwidth:canvas.width,\nheight:canvas.height};\n\n},\ntranslateToCenter:function translateToCenter(ps){\n// START METAMAPS CODE\nvar size=this.getSize();\nvar width=ps?size.width-ps.width-this.translateOffsetX*2:size.width;\nvar height=ps?size.height-ps.height-this.translateOffsetY*2:size.height;\n// ORIGINAL CODE\n// var size = this.getSize(),\n//     width = ps? (size.width - ps.width - this.translateOffsetX*2) : size.width;\n//     height = ps? (size.height - ps.height - this.translateOffsetY*2) : size.height;\n// END METAMAPS CODE\nvar ctx=this.getCtx();\nps&&ctx.scale(1/this.scaleOffsetX,1/this.scaleOffsetY);\nctx.translate(width/2,height/2);\n},\nresize:function resize(width,height){\nvar size=this.getSize(),\ncanvas=this.canvas,\nstyles=canvas.style;\nthis.size=false;\ncanvas.width=width;\ncanvas.height=height;\nstyles.width=width+\"px\";\nstyles.height=height+\"px\";\n\n//small ExCanvas fix\nif(!supportsCanvas){\nthis.translateToCenter(size);\n}else{\nthis.translateToCenter();\n}\nthis.translateOffsetX=\nthis.translateOffsetY=0;\nthis.scaleOffsetX=\nthis.scaleOffsetY=1;\n\nthis.clear();\nthis.viz.resize(width,height,this);\n},\ntranslate:function translate(x,y,disablePlot){\nvar sx=this.scaleOffsetX,\nsy=this.scaleOffsetY;\nthis.translateOffsetX+=x*sx;\nthis.translateOffsetY+=y*sy;\nthis.getCtx().translate(x,y);\n!disablePlot&&this.plot();\n},\nscale:function scale(x,y,disablePlot){\nthis.scaleOffsetX*=x;\nthis.scaleOffsetY*=y;\nthis.getCtx().scale(x,y);\n!disablePlot&&this.plot();\n},\nclear:function clear(){\nvar size=this.getSize(),\nox=this.translateOffsetX,\noy=this.translateOffsetY,\nsx=this.scaleOffsetX,\nsy=this.scaleOffsetY;\nthis.getCtx().clearRect((-size.width/2-ox)*1/sx,\n(-size.height/2-oy)*1/sy,\nsize.width*1/sx,size.height*1/sy);\n},\nplot:function plot(){\nthis.clear();\nthis.viz.plot(this);\n}});\n\n//background canvases\n//TODO(nico): document this!\nCanvas.Background={};\nCanvas.Background.Circles=new Class({\ninitialize:function initialize(viz,options){\nthis.viz=viz;\nthis.config=$.merge({\nidSuffix:'-bkcanvas',\nlevelDistance:100,\nnumberOfCircles:6,\nCanvasStyles:{},\noffset:0},\noptions);\n},\nresize:function resize(width,height,base){\nthis.plot(base);\n},\nplot:function plot(base){\nvar canvas=base.canvas,\nctx=base.getCtx(),\nconf=this.config,\nstyles=conf.CanvasStyles;\n//set canvas styles\nfor(var s in styles){ctx[s]=styles[s];}\nvar n=conf.numberOfCircles,\nrho=conf.levelDistance;\nfor(var i=1;i<=n;i++){\nctx.beginPath();\nctx.arc(0,0,rho*i,0,2*Math.PI,false);\nctx.stroke();\nctx.closePath();\n}\n//TODO(nico): print labels too!\n}});\n\n\n// START METAMAPS CODE\nCanvas.Background.Metamaps=new Class({\ninitialize:function initialize(viz,options){\nthis.viz=viz;\nthis.config=options;\n},\nresize:function resize(width,height,base){\nthis.plot(base);\n},\nplot:function plot(base){\nvar canvas=base.canvas,\nctx=base.getCtx(),\nscale=base.scaleOffsetX;\n//var pattern = new Image();\n//pattern.src = Metamaps.ServerData['cubes.png']\n//var ptrn = ctx.createPattern(pattern, 'repeat');\n//ctx.fillStyle = ptrn;\nctx.fillStyle=Metamaps.Settings.colors.background;\nvar xPoint=-(canvas.width/scale)/2-base.translateOffsetX/scale,\nyPoint=-(canvas.height/scale)/2-base.translateOffsetY/scale;\n//ctx.fillRect(xPoint,yPoint,canvas.width/scale,canvas.height/scale);\n}});\n\n// END METAMAPS CODE\n})();\n\n\n/*\n * File: Polar.js\n * \n * Defines the <Polar> class.\n *\n * Description:\n *\n * The <Polar> class, just like the <Complex> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.\n *\n * See also:\n *\n * <http://en.wikipedia.org/wiki/Polar_coordinates>\n *\n*/\n\n/*\n   Class: Polar\n\n   A multi purpose polar representation.\n\n   Description:\n \n   The <Polar> class, just like the <Complex> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.\n \n   See also:\n \n   <http://en.wikipedia.org/wiki/Polar_coordinates>\n \n   Parameters:\n\n      theta - An angle.\n      rho - The norm.\n*/\n\nvar Polar=function Polar(theta,rho){\nthis.theta=theta||0;\nthis.rho=rho||0;\n};\n\n$jit.Polar=Polar;\n\nPolar.prototype={\n/*\n       Method: getc\n    \n       Returns a complex number.\n    \n       Parameters:\n\n       simple - _optional_ If *true*, this method will return only an object holding x and y properties and not a <Complex> instance. Default's *false*.\n\n      Returns:\n    \n          A complex number.\n    */\ngetc:function getc(simple){\nreturn this.toComplex(simple);\n},\n\n/*\n       Method: getp\n    \n       Returns a <Polar> representation.\n    \n       Returns:\n    \n          A variable in polar coordinates.\n    */\ngetp:function getp(){\nreturn this;\n},\n\n\n/*\n       Method: set\n    \n       Sets a number.\n\n       Parameters:\n\n       v - A <Complex> or <Polar> instance.\n    \n    */\nset:function set(v){\nv=v.getp();\nthis.theta=v.theta;this.rho=v.rho;\n},\n\n/*\n       Method: setc\n    \n       Sets a <Complex> number.\n\n       Parameters:\n\n       x - A <Complex> number real part.\n       y - A <Complex> number imaginary part.\n    \n    */\nsetc:function setc(x,y){\nthis.rho=Math.sqrt(x*x+y*y);\nthis.theta=Math.atan2(y,x);\nif(this.theta<0)this.theta+=Math.PI*2;\n},\n\n/*\n       Method: setp\n    \n       Sets a polar number.\n\n       Parameters:\n\n       theta - A <Polar> number angle property.\n       rho - A <Polar> number rho property.\n    \n    */\nsetp:function setp(theta,rho){\nthis.theta=theta;\nthis.rho=rho;\n},\n\n/*\n       Method: clone\n    \n       Returns a copy of the current object.\n    \n       Returns:\n    \n          A copy of the real object.\n    */\nclone:function clone(){\nreturn new Polar(this.theta,this.rho);\n},\n\n/*\n       Method: toComplex\n    \n        Translates from polar to cartesian coordinates and returns a new <Complex> instance.\n    \n        Parameters:\n\n        simple - _optional_ If *true* this method will only return an object with x and y properties (and not the whole <Complex> instance). Default's *false*.\n \n        Returns:\n    \n          A new <Complex> instance.\n    */\ntoComplex:function toComplex(simple){\nvar x=Math.cos(this.theta)*this.rho;\nvar y=Math.sin(this.theta)*this.rho;\nif(simple)return{'x':x,'y':y};\nreturn new Complex(x,y);\n},\n\n/*\n       Method: add\n    \n        Adds two <Polar> instances.\n    \n       Parameters:\n\n       polar - A <Polar> number.\n\n       Returns:\n    \n          A new Polar instance.\n    */\nadd:function add(polar){\nreturn new Polar(this.theta+polar.theta,this.rho+polar.rho);\n},\n\n/*\n       Method: scale\n    \n        Scales a polar norm.\n    \n        Parameters:\n\n        number - A scale factor.\n        \n        Returns:\n    \n          A new Polar instance.\n    */\nscale:function scale(number){\nreturn new Polar(this.theta,this.rho*number);\n},\n\n/*\n       Method: equals\n    \n       Comparison method.\n\n       Returns *true* if the theta and rho properties are equal.\n\n       Parameters:\n\n       c - A <Polar> number.\n\n       Returns:\n\n       *true* if the theta and rho parameters for these objects are equal. *false* otherwise.\n    */\nequals:function equals(c){\nreturn this.theta==c.theta&&this.rho==c.rho;\n},\n\n/*\n       Method: $add\n    \n        Adds two <Polar> instances affecting the current object.\n    \n       Paramters:\n\n       polar - A <Polar> instance.\n\n       Returns:\n    \n          The changed object.\n    */\n$add:function $add(polar){\nthis.theta=this.theta+polar.theta;this.rho+=polar.rho;\nreturn this;\n},\n\n/*\n       Method: $madd\n    \n        Adds two <Polar> instances affecting the current object. The resulting theta angle is modulo 2pi.\n    \n       Parameters:\n\n       polar - A <Polar> instance.\n\n       Returns:\n    \n          The changed object.\n    */\n$madd:function $madd(polar){\nthis.theta=(this.theta+polar.theta)%(Math.PI*2);this.rho+=polar.rho;\nreturn this;\n},\n\n\n/*\n       Method: $scale\n    \n        Scales a polar instance affecting the object.\n    \n      Parameters:\n\n      number - A scaling factor.\n\n      Returns:\n    \n          The changed object.\n    */\n$scale:function $scale(number){\nthis.rho*=number;\nreturn this;\n},\n\n/*\n      Method: isZero\n   \n      Returns *true* if the number is zero.\n   \n   */\nisZero:function isZero(){\nvar almostZero=0.0001,abs=Math.abs;\nreturn abs(this.theta)<almostZero&&abs(this.rho)<almostZero;\n},\n\n/*\n       Method: interpolate\n    \n        Calculates a polar interpolation between two points at a given delta moment.\n\n        Parameters:\n      \n        elem - A <Polar> instance.\n        delta - A delta factor ranging [0, 1].\n    \n       Returns:\n    \n          A new <Polar> instance representing an interpolation between _this_ and _elem_\n    */\ninterpolate:function interpolate(elem,delta){\nvar pi=Math.PI,pi2=pi*2;\nvar ch=function ch(t){\nvar a=t<0?t%pi2+pi2:t%pi2;\nreturn a;\n};\nvar tt=this.theta,et=elem.theta;\nvar sum,diff=Math.abs(tt-et);\nif(diff==pi){\nif(tt>et){\nsum=ch(et+(tt-pi2-et)*delta);\n}else{\nsum=ch(et-pi2+(tt-et)*delta);\n}\n}else if(diff>=pi){\nif(tt>et){\nsum=ch(et+(tt-pi2-et)*delta);\n}else{\nsum=ch(et-pi2+(tt-(et-pi2))*delta);\n}\n}else{\nsum=ch(et+(tt-et)*delta);\n}\nvar r=(this.rho-elem.rho)*delta+elem.rho;\nreturn{\n'theta':sum,\n'rho':r};\n\n}};\n\n\n\nvar $P=function $P(a,b){return new Polar(a,b);};\n\nPolar.KER=$P(0,0);\n\n\n\n/*\n * File: Complex.js\n * \n * Defines the <Complex> class.\n *\n * Description:\n *\n * The <Complex> class, just like the <Polar> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.\n *\n * See also:\n *\n * <http://en.wikipedia.org/wiki/Complex_number>\n *\n*/\n\n/*\n   Class: Complex\n    \n   A multi-purpose Complex Class with common methods.\n \n   Description:\n \n   The <Complex> class, just like the <Polar> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.\n \n   See also:\n \n   <http://en.wikipedia.org/wiki/Complex_number>\n\n   Parameters:\n\n   x - _optional_ A Complex number real part.\n   y - _optional_ A Complex number imaginary part.\n \n*/\n\nvar Complex=function Complex(x,y){\nthis.x=x||0;\nthis.y=y||0;\n};\n\n$jit.Complex=Complex;\n\nComplex.prototype={\n/*\n       Method: getc\n    \n       Returns a complex number.\n    \n       Returns:\n    \n          A complex number.\n    */\ngetc:function getc(){\nreturn this;\n},\n\n/*\n       Method: getp\n    \n       Returns a <Polar> representation of this number.\n    \n       Parameters:\n\n       simple - _optional_ If *true*, this method will return only an object holding theta and rho properties and not a <Polar> instance. Default's *false*.\n\n       Returns:\n    \n          A variable in <Polar> coordinates.\n    */\ngetp:function getp(simple){\nreturn this.toPolar(simple);\n},\n\n\n/*\n       Method: set\n    \n       Sets a number.\n\n       Parameters:\n\n       c - A <Complex> or <Polar> instance.\n    \n    */\nset:function set(c){\nc=c.getc(true);\nthis.x=c.x;\nthis.y=c.y;\n},\n\n/*\n       Method: setc\n    \n       Sets a complex number.\n\n       Parameters:\n\n       x - A <Complex> number Real part.\n       y - A <Complex> number Imaginary part.\n    \n    */\nsetc:function setc(x,y){\nthis.x=x;\nthis.y=y;\n},\n\n/*\n       Method: setp\n    \n       Sets a polar number.\n\n       Parameters:\n\n       theta - A <Polar> number theta property.\n       rho - A <Polar> number rho property.\n    \n    */\nsetp:function setp(theta,rho){\nthis.x=Math.cos(theta)*rho;\nthis.y=Math.sin(theta)*rho;\n},\n\n/*\n       Method: clone\n    \n       Returns a copy of the current object.\n    \n       Returns:\n    \n          A copy of the real object.\n    */\nclone:function clone(){\nreturn new Complex(this.x,this.y);\n},\n\n/*\n       Method: toPolar\n    \n       Transforms cartesian to polar coordinates.\n    \n       Parameters:\n\n       simple - _optional_ If *true* this method will only return an object with theta and rho properties (and not the whole <Polar> instance). Default's *false*.\n       \n       Returns:\n    \n          A new <Polar> instance.\n    */\n\ntoPolar:function toPolar(simple){\nvar rho=this.norm();\nvar atan=Math.atan2(this.y,this.x);\nif(atan<0)atan+=Math.PI*2;\nif(simple)return{'theta':atan,'rho':rho};\nreturn new Polar(atan,rho);\n},\n/*\n       Method: norm\n    \n       Calculates a <Complex> number norm.\n    \n       Returns:\n    \n          A real number representing the complex norm.\n    */\nnorm:function norm(){\nreturn Math.sqrt(this.squaredNorm());\n},\n\n/*\n       Method: squaredNorm\n    \n       Calculates a <Complex> number squared norm.\n    \n       Returns:\n    \n          A real number representing the complex squared norm.\n    */\nsquaredNorm:function squaredNorm(){\nreturn this.x*this.x+this.y*this.y;\n},\n\n/*\n       Method: add\n    \n       Returns the result of adding two complex numbers.\n       \n       Does not alter the original object.\n\n       Parameters:\n    \n          pos - A <Complex> instance.\n    \n       Returns:\n    \n         The result of adding two complex numbers.\n    */\nadd:function add(pos){\nreturn new Complex(this.x+pos.x,this.y+pos.y);\n},\n\n/*\n       Method: prod\n    \n       Returns the result of multiplying two <Complex> numbers.\n       \n       Does not alter the original object.\n\n       Parameters:\n    \n          pos - A <Complex> instance.\n    \n       Returns:\n    \n         The result of multiplying two complex numbers.\n    */\nprod:function prod(pos){\nreturn new Complex(this.x*pos.x-this.y*pos.y,this.y*pos.x+this.x*pos.y);\n},\n\n/*\n       Method: conjugate\n    \n       Returns the conjugate of this <Complex> number.\n\n       Does not alter the original object.\n\n       Returns:\n    \n         The conjugate of this <Complex> number.\n    */\nconjugate:function conjugate(){\nreturn new Complex(this.x,-this.y);\n},\n\n\n/*\n       Method: scale\n    \n       Returns the result of scaling a <Complex> instance.\n       \n       Does not alter the original object.\n\n       Parameters:\n    \n          factor - A scale factor.\n    \n       Returns:\n    \n         The result of scaling this complex to a factor.\n    */\nscale:function scale(factor){\nreturn new Complex(this.x*factor,this.y*factor);\n},\n\n/*\n       Method: equals\n    \n       Comparison method.\n\n       Returns *true* if both real and imaginary parts are equal.\n\n       Parameters:\n\n       c - A <Complex> instance.\n\n       Returns:\n\n       A boolean instance indicating if both <Complex> numbers are equal.\n    */\nequals:function equals(c){\nreturn this.x==c.x&&this.y==c.y;\n},\n\n/*\n       Method: $add\n    \n       Returns the result of adding two <Complex> numbers.\n       \n       Alters the original object.\n\n       Parameters:\n    \n          pos - A <Complex> instance.\n    \n       Returns:\n    \n         The result of adding two complex numbers.\n    */\n$add:function $add(pos){\nthis.x+=pos.x;this.y+=pos.y;\nreturn this;\n},\n\n/*\n       Method: $prod\n    \n       Returns the result of multiplying two <Complex> numbers.\n       \n       Alters the original object.\n\n       Parameters:\n    \n          pos - A <Complex> instance.\n    \n       Returns:\n    \n         The result of multiplying two complex numbers.\n    */\n$prod:function $prod(pos){\nvar x=this.x,y=this.y;\nthis.x=x*pos.x-y*pos.y;\nthis.y=y*pos.x+x*pos.y;\nreturn this;\n},\n\n/*\n       Method: $conjugate\n    \n       Returns the conjugate for this <Complex>.\n       \n       Alters the original object.\n\n       Returns:\n    \n         The conjugate for this complex.\n    */\n$conjugate:function $conjugate(){\nthis.y=-this.y;\nreturn this;\n},\n\n/*\n       Method: $scale\n    \n       Returns the result of scaling a <Complex> instance.\n       \n       Alters the original object.\n\n       Parameters:\n    \n          factor - A scale factor.\n    \n       Returns:\n    \n         The result of scaling this complex to a factor.\n    */\n$scale:function $scale(factor){\nthis.x*=factor;this.y*=factor;\nreturn this;\n},\n\n/*\n       Method: $div\n    \n       Returns the division of two <Complex> numbers.\n       \n       Alters the original object.\n\n       Parameters:\n    \n          pos - A <Complex> number.\n    \n       Returns:\n    \n         The result of scaling this complex to a factor.\n    */\n$div:function $div(pos){\nvar x=this.x,y=this.y;\nvar sq=pos.squaredNorm();\nthis.x=x*pos.x+y*pos.y;this.y=y*pos.x-x*pos.y;\nreturn this.$scale(1/sq);\n},\n\n/*\n      Method: isZero\n   \n      Returns *true* if the number is zero.\n   \n   */\nisZero:function isZero(){\nvar almostZero=0.0001,abs=Math.abs;\nreturn abs(this.x)<almostZero&&abs(this.y)<almostZero;\n}};\n\n\nvar $C=function $C(a,b){return new Complex(a,b);};\n\nComplex.KER=$C(0,0);\n\n\n\n/*\n * File: Graph.js\n *\n*/\n\n/*\n Class: Graph\n\n A Graph Class that provides useful manipulation functions. You can find more manipulation methods in the <Graph.Util> object.\n\n An instance of this class can be accessed by using the *graph* parameter of any tree or graph visualization.\n \n Example:\n\n (start code js)\n   //create new visualization\n   var viz = new $jit.Viz(options);\n   //load JSON data\n   viz.loadJSON(json);\n   //access model\n   viz.graph; //<Graph> instance\n (end code)\n \n Implements:\n \n The following <Graph.Util> methods are implemented in <Graph>\n \n  - <Graph.Util.getNode>\n  - <Graph.Util.eachNode>\n  - <Graph.Util.computeLevels>\n  - <Graph.Util.eachBFS>\n  - <Graph.Util.clean>\n  - <Graph.Util.getClosestNodeToPos>\n  - <Graph.Util.getClosestNodeToOrigin>\n \n*/\n\n$jit.Graph=new Class({\n\ninitialize:function initialize(opt,Node,Edge,Label){\nvar innerOptions={\n'klass':Complex,\n'Node':{}};\n\nthis.Node=Node;\nthis.Edge=Edge;\nthis.Label=Label;\nthis.opt=$.merge(innerOptions,opt||{});\nthis.nodes={};\nthis.edges={};\n\n//add nodeList methods\nvar that=this;\nthis.nodeList={};\nfor(var p in Accessors){\nthat.nodeList[p]=function(p){\nreturn function(){\nvar args=Array.prototype.slice.call(arguments);\nthat.eachNode(function(n){\nn[p].apply(n,args);\n});\n};\n}(p);\n}\n\n},\n\n/*\n     Method: getNode\n    \n     Returns a <Graph.Node> by *id*.\n\n     Parameters:\n\n     id - (string) A <Graph.Node> id.\n\n     Example:\n\n     (start code js)\n       var node = graph.getNode('nodeId');\n     (end code)\n*/\ngetNode:function getNode(id){\nif(this.hasNode(id))return this.nodes[id];\nreturn false;\n},\n\n/*\n     Method: get\n    \n     An alias for <Graph.Util.getNode>. Returns a node by *id*.\n    \n     Parameters:\n    \n     id - (string) A <Graph.Node> id.\n    \n     Example:\n    \n     (start code js)\n       var node = graph.get('nodeId');\n     (end code)\n*/\nget:function get(id){\nreturn this.getNode(id);\n},\n\n/*\n   Method: getByName\n  \n   Returns a <Graph.Node> by *name*.\n  \n   Parameters:\n  \n   name - (string) A <Graph.Node> name.\n  \n   Example:\n  \n   (start code js)\n     var node = graph.getByName('someName');\n   (end code)\n  */\ngetByName:function getByName(name){\nfor(var id in this.nodes){\nvar n=this.nodes[id];\nif(n.name==name)return n;\n}\nreturn false;\n},\n\n/*\n   Method: getAdjacence\n  \n   Returns a <Graph.Adjacence> object connecting nodes with ids *id* and *id2*.\n\n   Parameters:\n\n   id - (string) A <Graph.Node> id.\n   id2 - (string) A <Graph.Node> id.\n*/\ngetAdjacence:function getAdjacence(id,id2){\nif(id in this.edges){\nreturn this.edges[id][id2];\n}\nreturn false;\n},\n\n/*\n     Method: addNode\n    \n     Adds a node.\n     \n     Parameters:\n    \n      obj - An object with the properties described below\n\n      id - (string) A node id\n      name - (string) A node's name\n      data - (object) A node's data hash\n\n    See also:\n    <Graph.Node>\n\n  */\naddNode:function addNode(obj){\nif(!this.nodes[obj.id]){\nvar edges=this.edges[obj.id]={};\nthis.nodes[obj.id]=new Graph.Node($.extend({\n'id':obj.id,\n'name':obj.name,\n'data':$.merge(obj.data||{},{}),\n'adjacencies':edges},\nthis.opt.Node),\nthis.opt.klass,\nthis.Node,\nthis.Edge,\nthis.Label);\n}\nreturn this.nodes[obj.id];\n},\n\n/*\n     Method: addAdjacence\n    \n     Connects nodes specified by *obj* and *obj2*. If not found, nodes are created.\n     \n     Parameters:\n    \n      obj - (object) A <Graph.Node> object.\n      obj2 - (object) Another <Graph.Node> object.\n      data - (object) A data object. Used to store some extra information in the <Graph.Adjacence> object created.\n\n    See also:\n\n    <Graph.Node>, <Graph.Adjacence>\n    */\naddAdjacence:function addAdjacence(obj,obj2,data){\nif(!this.hasNode(obj.id)){this.addNode(obj);}\nif(!this.hasNode(obj2.id)){this.addNode(obj2);}\nobj=this.nodes[obj.id];obj2=this.nodes[obj2.id];\nif(!obj.adjacentTo(obj2)){\nvar adjsObj=this.edges[obj.id]=this.edges[obj.id]||{};\nvar adjsObj2=this.edges[obj2.id]=this.edges[obj2.id]||{};\nadjsObj[obj2.id]=adjsObj2[obj.id]=new Graph.Adjacence(obj,obj2,data,this.Edge,this.Label);\nreturn adjsObj[obj2.id];\n}\nreturn this.edges[obj.id][obj2.id];\n},\n\n/*\n     Method: removeNode\n    \n     Removes a <Graph.Node> matching the specified *id*.\n\n     Parameters:\n\n     id - (string) A node's id.\n\n    */\nremoveNode:function removeNode(id){\nif(this.hasNode(id)){\ndelete this.nodes[id];\nvar adjs=this.edges[id];\nfor(var to in adjs){\ndelete this.edges[to][id];\n}\ndelete this.edges[id];\n}\n},\n\n/*\n     Method: removeAdjacence\n    \n     Removes a <Graph.Adjacence> matching *id1* and *id2*.\n\n     Parameters:\n\n     id1 - (string) A <Graph.Node> id.\n     id2 - (string) A <Graph.Node> id.\n*/\nremoveAdjacence:function removeAdjacence(id1,id2){\ndelete this.edges[id1][id2];\ndelete this.edges[id2][id1];\n},\n\n/*\n     Method: hasNode\n    \n     Returns a boolean indicating if the node belongs to the <Graph> or not.\n     \n     Parameters:\n    \n        id - (string) Node id.\n   */\nhasNode:function hasNode(id){\nreturn id in this.nodes;\n},\n\n/*\n    Method: empty\n\n    Empties the Graph\n\n  */\nempty:function empty(){this.nodes={};this.edges={};}});\n\n\n\nvar Graph=$jit.Graph;\n\n/*\n Object: Accessors\n \n Defines a set of methods for data, canvas and label styles manipulation implemented by <Graph.Node> and <Graph.Adjacence> instances.\n \n */\nvar Accessors;\n\n(function(){\nvar getDataInternal=function getDataInternal(prefix,prop,type,force,prefixConfig){\nvar data;\ntype=type||'current';\nprefix=\"$\"+(prefix?prefix+\"-\":\"\");\n\nif(type=='current'){\ndata=this.data;\n}else if(type=='start'){\ndata=this.startData;\n}else if(type=='end'){\ndata=this.endData;\n}\n\nvar dollar=prefix+prop;\n\nif(force){\nreturn data[dollar];\n}\n\nif(!this.Config.overridable)\nreturn prefixConfig[prop]||0;\n\nreturn dollar in data?\ndata[dollar]:dollar in this.data?this.data[dollar]:prefixConfig[prop]||0;\n};\n\nvar setDataInternal=function setDataInternal(prefix,prop,value,type){\ntype=type||'current';\nprefix='$'+(prefix?prefix+'-':'');\n\nvar data;\n\nif(type=='current'){\ndata=this.data;\n}else if(type=='start'){\ndata=this.startData;\n}else if(type=='end'){\ndata=this.endData;\n}\n\ndata[prefix+prop]=value;\n};\n\nvar removeDataInternal=function removeDataInternal(prefix,properties){\nprefix='$'+(prefix?prefix+'-':'');\nvar that=this;\n$.each(properties,function(prop){\nvar pref=prefix+prop;\ndelete that.data[pref];\ndelete that.endData[pref];\ndelete that.startData[pref];\n});\n};\n\nAccessors={\n/*\n    Method: getData\n\n    Returns the specified data value property.\n    This is useful for querying special/reserved <Graph.Node> data properties\n    (i.e dollar prefixed properties).\n\n    Parameters:\n\n      prop  - (string) The name of the property. The dollar sign is not needed. For\n              example *getData(width)* will return *data.$width*.\n      type  - (string) The type of the data property queried. Default's \"current\". You can access *start* and *end* \n              data properties also. These properties are used when making animations.\n      force - (boolean) Whether to obtain the true value of the property (equivalent to\n              *data.$prop*) or to check for *node.overridable = true* first.\n\n    Returns:\n\n      The value of the dollar prefixed property or the global Node/Edge property\n      value if *overridable=false*\n\n    Example:\n    (start code js)\n     node.getData('width'); //will return node.data.$width if Node.overridable=true;\n    (end code)\n    */\ngetData:function getData(prop,type,force){\nreturn getDataInternal.call(this,\"\",prop,type,force,this.Config);\n},\n\n\n/*\n    Method: setData\n\n    Sets the current data property with some specific value.\n    This method is only useful for reserved (dollar prefixed) properties.\n\n    Parameters:\n\n      prop  - (string) The name of the property. The dollar sign is not necessary. For\n              example *setData(width)* will set *data.$width*.\n      value - (mixed) The value to store.\n      type  - (string) The type of the data property to store. Default's \"current\" but\n              can also be \"start\" or \"end\".\n\n    Example:\n    \n    (start code js)\n     node.setData('width', 30);\n    (end code)\n    \n    If we were to make an animation of a node/edge width then we could do\n    \n    (start code js)\n      var node = viz.getNode('nodeId');\n      //set start and end values\n      node.setData('width', 10, 'start');\n      node.setData('width', 30, 'end');\n      //will animate nodes width property\n      viz.fx.animate({\n        modes: ['node-property:width'],\n        duration: 1000\n      });\n    (end code)\n    */\nsetData:function setData(prop,value,type){\nsetDataInternal.call(this,\"\",prop,value,type);\n},\n\n/*\n    Method: setDataset\n\n    Convenience method to set multiple data values at once.\n    \n    Parameters:\n    \n    types - (array|string) A set of 'current', 'end' or 'start' values.\n    obj - (object) A hash containing the names and values of the properties to be altered.\n\n    Example:\n    (start code js)\n      node.setDataset(['current', 'end'], {\n        'width': [100, 5],\n        'color': ['#fff', '#ccc']\n      });\n      //...or also\n      node.setDataset('end', {\n        'width': 5,\n        'color': '#ccc'\n      });\n    (end code)\n    \n    See also: \n    \n    <Accessors.setData>\n    \n    */\nsetDataset:function setDataset(types,obj){\ntypes=$.splat(types);\nfor(var attr in obj){\nfor(var i=0,val=$.splat(obj[attr]),l=types.length;i<l;i++){\nthis.setData(attr,val[i],types[i]);\n}\n}\n},\n\n/*\n    Method: removeData\n\n    Remove data properties.\n\n    Parameters:\n\n    One or more property names as arguments. The dollar sign is not needed.\n\n    Example:\n    (start code js)\n    node.removeData('width'); //now the default width value is returned\n    (end code)\n    */\nremoveData:function removeData(){\nremoveDataInternal.call(this,\"\",Array.prototype.slice.call(arguments));\n},\n\n/*\n    Method: getCanvasStyle\n\n    Returns the specified canvas style data value property. This is useful for\n    querying special/reserved <Graph.Node> canvas style data properties (i.e.\n    dollar prefixed properties that match with $canvas-<name of canvas style>).\n\n    Parameters:\n\n      prop  - (string) The name of the property. The dollar sign is not needed. For\n              example *getCanvasStyle(shadowBlur)* will return *data[$canvas-shadowBlur]*.\n      type  - (string) The type of the data property queried. Default's *current*. You can access *start* and *end* \n              data properties also.\n              \n    Example:\n    (start code js)\n      node.getCanvasStyle('shadowBlur');\n    (end code)\n    \n    See also:\n    \n    <Accessors.getData>\n    */\ngetCanvasStyle:function getCanvasStyle(prop,type,force){\nreturn getDataInternal.call(\nthis,'canvas',prop,type,force,this.Config.CanvasStyles);\n},\n\n/*\n    Method: setCanvasStyle\n\n    Sets the canvas style data property with some specific value.\n    This method is only useful for reserved (dollar prefixed) properties.\n    \n    Parameters:\n    \n    prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.\n    value - (mixed) The value to set to the property.\n    type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.\n    \n    Example:\n    \n    (start code js)\n     node.setCanvasStyle('shadowBlur', 30);\n    (end code)\n    \n    If we were to make an animation of a node/edge shadowBlur canvas style then we could do\n    \n    (start code js)\n      var node = viz.getNode('nodeId');\n      //set start and end values\n      node.setCanvasStyle('shadowBlur', 10, 'start');\n      node.setCanvasStyle('shadowBlur', 30, 'end');\n      //will animate nodes canvas style property for nodes\n      viz.fx.animate({\n        modes: ['node-style:shadowBlur'],\n        duration: 1000\n      });\n    (end code)\n    \n    See also:\n    \n    <Accessors.setData>.\n    */\nsetCanvasStyle:function setCanvasStyle(prop,value,type){\nsetDataInternal.call(this,'canvas',prop,value,type);\n},\n\n/*\n    Method: setCanvasStyles\n\n    Convenience method to set multiple styles at once.\n\n    Parameters:\n    \n    types - (array|string) A set of 'current', 'end' or 'start' values.\n    obj - (object) A hash containing the names and values of the properties to be altered.\n\n    See also:\n    \n    <Accessors.setDataset>.\n    */\nsetCanvasStyles:function setCanvasStyles(types,obj){\ntypes=$.splat(types);\nfor(var attr in obj){\nfor(var i=0,val=$.splat(obj[attr]),l=types.length;i<l;i++){\nthis.setCanvasStyle(attr,val[i],types[i]);\n}\n}\n},\n\n/*\n    Method: removeCanvasStyle\n\n    Remove canvas style properties from data.\n\n    Parameters:\n    \n    A variable number of canvas style strings.\n\n    See also:\n    \n    <Accessors.removeData>.\n    */\nremoveCanvasStyle:function removeCanvasStyle(){\nremoveDataInternal.call(this,'canvas',Array.prototype.slice.call(arguments));\n},\n\n/*\n    Method: getLabelData\n\n    Returns the specified label data value property. This is useful for\n    querying special/reserved <Graph.Node> label options (i.e.\n    dollar prefixed properties that match with $label-<name of label style>).\n\n    Parameters:\n\n      prop  - (string) The name of the property. The dollar sign prefix is not needed. For\n              example *getLabelData(size)* will return *data[$label-size]*.\n      type  - (string) The type of the data property queried. Default's *current*. You can access *start* and *end* \n              data properties also.\n              \n    See also:\n    \n    <Accessors.getData>.\n    */\ngetLabelData:function getLabelData(prop,type,force){\nreturn getDataInternal.call(\nthis,'label',prop,type,force,this.Label);\n},\n\n/*\n    Method: setLabelData\n\n    Sets the current label data with some specific value.\n    This method is only useful for reserved (dollar prefixed) properties.\n\n    Parameters:\n    \n    prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.\n    value - (mixed) The value to set to the property.\n    type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.\n    \n    Example:\n    \n    (start code js)\n     node.setLabelData('size', 30);\n    (end code)\n    \n    If we were to make an animation of a node label size then we could do\n    \n    (start code js)\n      var node = viz.getNode('nodeId');\n      //set start and end values\n      node.setLabelData('size', 10, 'start');\n      node.setLabelData('size', 30, 'end');\n      //will animate nodes label size\n      viz.fx.animate({\n        modes: ['label-property:size'],\n        duration: 1000\n      });\n    (end code)\n    \n    See also:\n    \n    <Accessors.setData>.\n    */\nsetLabelData:function setLabelData(prop,value,type){\nsetDataInternal.call(this,'label',prop,value,type);\n},\n\n/*\n    Method: setLabelDataset\n\n    Convenience function to set multiple label data at once.\n\n    Parameters:\n    \n    types - (array|string) A set of 'current', 'end' or 'start' values.\n    obj - (object) A hash containing the names and values of the properties to be altered.\n\n    See also:\n    \n    <Accessors.setDataset>.\n    */\nsetLabelDataset:function setLabelDataset(types,obj){\ntypes=$.splat(types);\nfor(var attr in obj){\nfor(var i=0,val=$.splat(obj[attr]),l=types.length;i<l;i++){\nthis.setLabelData(attr,val[i],types[i]);\n}\n}\n},\n\n/*\n    Method: removeLabelData\n\n    Remove label properties from data.\n    \n    Parameters:\n    \n    A variable number of label property strings.\n\n    See also:\n    \n    <Accessors.removeData>.\n    */\nremoveLabelData:function removeLabelData(){\nremoveDataInternal.call(this,'label',Array.prototype.slice.call(arguments));\n}};\n\n})();\n\n/*\n     Class: Graph.Node\n\n     A <Graph> node.\n     \n     Implements:\n     \n     <Accessors> methods.\n     \n     The following <Graph.Util> methods are implemented by <Graph.Node>\n     \n    - <Graph.Util.eachAdjacency>\n    - <Graph.Util.eachLevel>\n    - <Graph.Util.eachSubgraph>\n    - <Graph.Util.eachSubnode>\n    - <Graph.Util.anySubnode>\n    - <Graph.Util.getSubnodes>\n    - <Graph.Util.getParents>\n    - <Graph.Util.isDescendantOf>     \n*/\nGraph.Node=new Class({\n\ninitialize:function initialize(opt,klass,Node,Edge,Label){\nvar innerOptions={\n'id':'',\n'name':'',\n'data':{},\n'startData':{},\n'endData':{},\n'adjacencies':{},\n\n'selected':false,\n'drawn':false,\n'exist':false,\n\n'angleSpan':{\n'begin':0,\n'end':0},\n\n\n'pos':new klass(),\n'startPos':new klass(),\n'endPos':new klass()};\n\n\n$.extend(this,$.extend(innerOptions,opt));\nthis.Config=this.Node=Node;\nthis.Edge=Edge;\nthis.Label=Label;\n},\n\n/*\n       Method: adjacentTo\n    \n       Indicates if the node is adjacent to the node specified by id\n\n       Parameters:\n    \n          id - (string) A node id.\n    \n       Example:\n       (start code js)\n        node.adjacentTo('nodeId') == true;\n       (end code)\n    */\nadjacentTo:function adjacentTo(node){\nreturn node.id in this.adjacencies;\n},\n\n/*\n       Method: getAdjacency\n    \n       Returns a <Graph.Adjacence> object connecting the current <Graph.Node> and the node having *id* as id.\n\n       Parameters:\n    \n          id - (string) A node id.\n    */\ngetAdjacency:function getAdjacency(id){\nreturn this.adjacencies[id];\n},\n\n/*\n      Method: getPos\n   \n      Returns the position of the node.\n  \n      Parameters:\n   \n         type - (string) Default's *current*. Possible values are \"start\", \"end\" or \"current\".\n   \n      Returns:\n   \n        A <Complex> or <Polar> instance.\n  \n      Example:\n      (start code js)\n       var pos = node.getPos('end');\n      (end code)\n   */\ngetPos:function getPos(type){\ntype=type||\"current\";\nif(type==\"current\"){\nreturn this.pos;\n}else if(type==\"end\"){\nreturn this.endPos;\n}else if(type==\"start\"){\nreturn this.startPos;\n}\n},\n/*\n     Method: setPos\n  \n     Sets the node's position.\n  \n     Parameters:\n  \n        value - (object) A <Complex> or <Polar> instance.\n        type - (string) Default's *current*. Possible values are \"start\", \"end\" or \"current\".\n  \n     Example:\n     (start code js)\n      node.setPos(new $jit.Complex(0, 0), 'end');\n     (end code)\n  */\nsetPos:function setPos(value,type){\ntype=type||\"current\";\nvar pos;\nif(type==\"current\"){\npos=this.pos;\n}else if(type==\"end\"){\npos=this.endPos;\n}else if(type==\"start\"){\npos=this.startPos;\n}\npos.set(value);\n}});\n\n\nGraph.Node.implement(Accessors);\n\n/*\n     Class: Graph.Adjacence\n\n     A <Graph> adjacence (or edge) connecting two <Graph.Nodes>.\n     \n     Implements:\n     \n     <Accessors> methods.\n\n     See also:\n\n     <Graph>, <Graph.Node>\n\n     Properties:\n     \n      nodeFrom - A <Graph.Node> connected by this edge.\n      nodeTo - Another  <Graph.Node> connected by this edge.\n      data - Node data property containing a hash (i.e {}) with custom options.\n*/\nGraph.Adjacence=new Class({\n\ninitialize:function initialize(nodeFrom,nodeTo,data,Edge,Label){\nthis.nodeFrom=nodeFrom;\nthis.nodeTo=nodeTo;\nthis.data=data||{};\nthis.startData={};\nthis.endData={};\nthis.Config=this.Edge=Edge;\nthis.Label=Label;\n}});\n\n\nGraph.Adjacence.implement(Accessors);\n\n/*\n   Object: Graph.Util\n\n   <Graph> traversal and processing utility object.\n   \n   Note:\n   \n   For your convenience some of these methods have also been appended to <Graph> and <Graph.Node> classes.\n*/\nGraph.Util={\n/*\n       filter\n    \n       For internal use only. Provides a filtering function based on flags.\n    */\nfilter:function filter(param){\nif(!param||!($.type(param)=='string'))return function(){return true;};\nvar props=param.split(\" \");\nreturn function(elem){\nfor(var i=0;i<props.length;i++){\nif(elem[props[i]]){\nreturn false;\n}\n}\nreturn true;\n};\n},\n/*\n       Method: getNode\n    \n       Returns a <Graph.Node> by *id*.\n       \n       Also implemented by:\n       \n       <Graph>\n\n       Parameters:\n\n       graph - (object) A <Graph> instance.\n       id - (string) A <Graph.Node> id.\n\n       Example:\n\n       (start code js)\n         $jit.Graph.Util.getNode(graph, 'nodeid');\n         //or...\n         graph.getNode('nodeid');\n       (end code)\n    */\ngetNode:function getNode(graph,id){\nreturn graph.nodes[id];\n},\n\n/*\n       Method: eachNode\n    \n       Iterates over <Graph> nodes performing an *action*.\n       \n       Also implemented by:\n       \n       <Graph>.\n\n       Parameters:\n\n       graph - (object) A <Graph> instance.\n       action - (function) A callback function having a <Graph.Node> as first formal parameter.\n\n       Example:\n       (start code js)\n         $jit.Graph.Util.eachNode(graph, function(node) {\n          alert(node.name);\n         });\n         //or...\n         graph.eachNode(function(node) {\n           alert(node.name);\n         });\n       (end code)\n    */\neachNode:function eachNode(graph,action,flags){\nvar filter=this.filter(flags);\nfor(var i in graph.nodes){\nif(filter(graph.nodes[i]))action(graph.nodes[i]);\n}\n},\n\n/*\n      Method: each\n   \n      Iterates over <Graph> nodes performing an *action*. It's an alias for <Graph.Util.eachNode>.\n      \n      Also implemented by:\n      \n      <Graph>.\n  \n      Parameters:\n  \n      graph - (object) A <Graph> instance.\n      action - (function) A callback function having a <Graph.Node> as first formal parameter.\n  \n      Example:\n      (start code js)\n        $jit.Graph.Util.each(graph, function(node) {\n         alert(node.name);\n        });\n        //or...\n        graph.each(function(node) {\n          alert(node.name);\n        });\n      (end code)\n   */\neach:function each(graph,action,flags){\nthis.eachNode(graph,action,flags);\n},\n\n/*\n       Method: eachAdjacency\n    \n       Iterates over <Graph.Node> adjacencies applying the *action* function.\n       \n       Also implemented by:\n       \n       <Graph.Node>.\n\n       Parameters:\n\n       node - (object) A <Graph.Node>.\n       action - (function) A callback function having <Graph.Adjacence> as first formal parameter.\n\n       Example:\n       (start code js)\n         $jit.Graph.Util.eachAdjacency(node, function(adj) {\n          alert(adj.nodeTo.name);\n         });\n         //or...\n         node.eachAdjacency(function(adj) {\n           alert(adj.nodeTo.name);\n         });\n       (end code)\n    */\neachAdjacency:function eachAdjacency(node,action,flags){\nvar adj=node.adjacencies,filter=this.filter(flags);\nfor(var id in adj){\nvar a=adj[id];\nif(filter(a)){\nif(a.nodeFrom!=node){\nvar tmp=a.nodeFrom;\na.nodeFrom=a.nodeTo;\na.nodeTo=tmp;\n}\naction(a,id);\n}\n}\n},\n\n/*\n       Method: computeLevels\n    \n       Performs a BFS traversal setting the correct depth for each node.\n        \n       Also implemented by:\n       \n       <Graph>.\n       \n       Note:\n       \n       The depth of each node can then be accessed by \n       >node._depth\n\n       Parameters:\n\n       graph - (object) A <Graph>.\n       id - (string) A starting node id for the BFS traversal.\n       startDepth - (optional|number) A minimum depth value. Default's 0.\n\n    */\ncomputeLevels:function computeLevels(graph,id,startDepth,flags){\nstartDepth=startDepth||0;\nvar filter=this.filter(flags);\nthis.eachNode(graph,function(elem){\nelem._flag=false;\nelem._depth=-1;\n},flags);\nvar root=graph.getNode(id);\nroot._depth=startDepth;\nvar queue=[root];\nwhile(queue.length!=0){\nvar node=queue.pop();\nnode._flag=true;\nthis.eachAdjacency(node,function(adj){\nvar n=adj.nodeTo;\nif(n._flag==false&&filter(n)){\nif(n._depth<0)n._depth=node._depth+1+startDepth;\nqueue.unshift(n);\n}\n},flags);\n}\n},\n\n/*\n       Method: eachBFS\n    \n       Performs a BFS traversal applying *action* to each <Graph.Node>.\n       \n       Also implemented by:\n       \n       <Graph>.\n\n       Parameters:\n\n       graph - (object) A <Graph>.\n       id - (string) A starting node id for the BFS traversal.\n       action - (function) A callback function having a <Graph.Node> as first formal parameter.\n\n       Example:\n       (start code js)\n         $jit.Graph.Util.eachBFS(graph, 'mynodeid', function(node) {\n          alert(node.name);\n         });\n         //or...\n         graph.eachBFS('mynodeid', function(node) {\n           alert(node.name);\n         });\n       (end code)\n    */\neachBFS:function eachBFS(graph,id,action,flags){\nvar filter=this.filter(flags);\nthis.clean(graph);\nvar queue=[graph.getNode(id)];\nwhile(queue.length!=0){\nvar node=queue.pop();\nnode._flag=true;\naction(node,node._depth);\nthis.eachAdjacency(node,function(adj){\nvar n=adj.nodeTo;\nif(n._flag==false&&filter(n)){\nn._flag=true;\nqueue.unshift(n);\n}\n},flags);\n}\n},\n\n/*\n       Method: eachLevel\n    \n       Iterates over a node's subgraph applying *action* to the nodes of relative depth between *levelBegin* and *levelEnd*.\n       \n       Also implemented by:\n       \n       <Graph.Node>.\n\n       Parameters:\n       \n       node - (object) A <Graph.Node>.\n       levelBegin - (number) A relative level value.\n       levelEnd - (number) A relative level value.\n       action - (function) A callback function having a <Graph.Node> as first formal parameter.\n\n    */\neachLevel:function eachLevel(node,levelBegin,levelEnd,action,flags){\nvar d=node._depth,filter=this.filter(flags),that=this;\nlevelEnd=levelEnd===false?Number.MAX_VALUE-d:levelEnd;\n(function loopLevel(node,levelBegin,levelEnd){\nvar d=node._depth;\nif(d>=levelBegin&&d<=levelEnd&&filter(node))action(node,d);\nif(d<levelEnd){\nthat.eachAdjacency(node,function(adj){\nvar n=adj.nodeTo;\nif(n._depth>d)loopLevel(n,levelBegin,levelEnd);\n});\n}\n})(node,levelBegin+d,levelEnd+d);\n},\n\n/*\n       Method: eachSubgraph\n    \n       Iterates over a node's children recursively.\n       \n       Also implemented by:\n       \n       <Graph.Node>.\n\n       Parameters:\n       node - (object) A <Graph.Node>.\n       action - (function) A callback function having a <Graph.Node> as first formal parameter.\n\n       Example:\n       (start code js)\n         $jit.Graph.Util.eachSubgraph(node, function(node) {\n           alert(node.name);\n         });\n         //or...\n         node.eachSubgraph(function(node) {\n           alert(node.name);\n         });\n       (end code)\n    */\neachSubgraph:function eachSubgraph(node,action,flags){\nthis.eachLevel(node,0,false,action,flags);\n},\n\n/*\n       Method: eachSubnode\n    \n       Iterates over a node's children (without deeper recursion).\n       \n       Also implemented by:\n       \n       <Graph.Node>.\n       \n       Parameters:\n       node - (object) A <Graph.Node>.\n       action - (function) A callback function having a <Graph.Node> as first formal parameter.\n\n       Example:\n       (start code js)\n         $jit.Graph.Util.eachSubnode(node, function(node) {\n          alert(node.name);\n         });\n         //or...\n         node.eachSubnode(function(node) {\n           alert(node.name);\n         });\n       (end code)\n    */\neachSubnode:function eachSubnode(node,action,flags){\nthis.eachLevel(node,1,1,action,flags);\n},\n\n/*\n       Method: anySubnode\n    \n       Returns *true* if any subnode matches the given condition.\n       \n       Also implemented by:\n       \n       <Graph.Node>.\n\n       Parameters:\n       node - (object) A <Graph.Node>.\n       cond - (function) A callback function returning a Boolean instance. This function has as first formal parameter a <Graph.Node>.\n\n       Example:\n       (start code js)\n         $jit.Graph.Util.anySubnode(node, function(node) { return node.name == \"mynodename\"; });\n         //or...\n         node.anySubnode(function(node) { return node.name == 'mynodename'; });\n       (end code)\n    */\nanySubnode:function anySubnode(node,cond,flags){\nvar flag=false;\ncond=cond||$.lambda(true);\nvar c=$.type(cond)=='string'?function(n){return n[cond];}:cond;\nthis.eachSubnode(node,function(elem){\nif(c(elem))flag=true;\n},flags);\nreturn flag;\n},\n\n/*\n       Method: getSubnodes\n    \n       Collects all subnodes for a specified node. \n       The *level* parameter filters nodes having relative depth of *level* from the root node. \n       \n       Also implemented by:\n       \n       <Graph.Node>.\n\n       Parameters:\n       node - (object) A <Graph.Node>.\n       level - (optional|number) Default's *0*. A starting relative depth for collecting nodes.\n\n       Returns:\n       An array of nodes.\n\n    */\ngetSubnodes:function getSubnodes(node,level,flags){\nvar ans=[],that=this;\nlevel=level||0;\nvar levelStart,levelEnd;\nif($.type(level)=='array'){\nlevelStart=level[0];\nlevelEnd=level[1];\n}else{\nlevelStart=level;\nlevelEnd=Number.MAX_VALUE-node._depth;\n}\nthis.eachLevel(node,levelStart,levelEnd,function(n){\nans.push(n);\n},flags);\nreturn ans;\n},\n\n\n/*\n       Method: getParents\n    \n       Returns an Array of <Graph.Nodes> which are parents of the given node.\n       \n       Also implemented by:\n       \n       <Graph.Node>.\n\n       Parameters:\n       node - (object) A <Graph.Node>.\n\n       Returns:\n       An Array of <Graph.Nodes>.\n\n       Example:\n       (start code js)\n         var pars = $jit.Graph.Util.getParents(node);\n         //or...\n         var pars = node.getParents();\n         \n         if(pars.length > 0) {\n           //do stuff with parents\n         }\n       (end code)\n    */\ngetParents:function getParents(node){\nvar ans=[];\nthis.eachAdjacency(node,function(adj){\nvar n=adj.nodeTo;\nif(n._depth<node._depth)ans.push(n);\n});\nreturn ans;\n},\n\n/*\n    Method: isDescendantOf\n \n    Returns a boolean indicating if some node is descendant of the node with the given id. \n\n    Also implemented by:\n    \n    <Graph.Node>.\n    \n    \n    Parameters:\n    node - (object) A <Graph.Node>.\n    id - (string) A <Graph.Node> id.\n\n    Example:\n    (start code js)\n      $jit.Graph.Util.isDescendantOf(node, \"nodeid\"); //true|false\n      //or...\n      node.isDescendantOf('nodeid');//true|false\n    (end code)\n */\nisDescendantOf:function isDescendantOf(node,id){\nif(node.id==id)return true;\nvar pars=this.getParents(node),ans=false;\nfor(var i=0;!ans&&i<pars.length;i++){\nans=ans||this.isDescendantOf(pars[i],id);\n}\nreturn ans;\n},\n\n/*\n     Method: clean\n  \n     Cleans flags from nodes.\n\n     Also implemented by:\n     \n     <Graph>.\n     \n     Parameters:\n     graph - A <Graph> instance.\n  */\nclean:function clean(graph){this.eachNode(graph,function(elem){elem._flag=false;});},\n\n/* \n    Method: getClosestNodeToOrigin \n  \n    Returns the closest node to the center of canvas.\n  \n    Also implemented by:\n    \n    <Graph>.\n    \n    Parameters:\n   \n     graph - (object) A <Graph> instance.\n     prop - (optional|string) Default's 'current'. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.\n  \n  */\ngetClosestNodeToOrigin:function getClosestNodeToOrigin(graph,prop,flags){\nreturn this.getClosestNodeToPos(graph,Polar.KER,prop,flags);\n},\n\n/* \n    Method: getClosestNodeToPos\n  \n    Returns the closest node to the given position.\n  \n    Also implemented by:\n    \n    <Graph>.\n    \n    Parameters:\n   \n     graph - (object) A <Graph> instance.\n     pos - (object) A <Complex> or <Polar> instance.\n     prop - (optional|string) Default's *current*. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.\n  \n  */\ngetClosestNodeToPos:function getClosestNodeToPos(graph,pos,prop,flags){\nvar node=null;\nprop=prop||'current';\npos=pos&&pos.getc(true)||Complex.KER;\nvar distance=function distance(a,b){\nvar d1=a.x-b.x,d2=a.y-b.y;\nreturn d1*d1+d2*d2;\n};\nthis.eachNode(graph,function(elem){\nnode=node==null||distance(elem.getPos(prop).getc(true),pos)<distance(\nnode.getPos(prop).getc(true),pos)?elem:node;\n},flags);\nreturn node;\n}};\n\n\n//Append graph methods to <Graph>\n$.each(['get','getNode','each','eachNode','computeLevels','eachBFS','clean','getClosestNodeToPos','getClosestNodeToOrigin'],function(m){\nGraph.prototype[m]=function(){\nreturn Graph.Util[m].apply(Graph.Util,[this].concat(Array.prototype.slice.call(arguments)));\n};\n});\n\n//Append node methods to <Graph.Node>\n$.each(['eachAdjacency','eachLevel','eachSubgraph','eachSubnode','anySubnode','getSubnodes','getParents','isDescendantOf'],function(m){\nGraph.Node.prototype[m]=function(){\nreturn Graph.Util[m].apply(Graph.Util,[this].concat(Array.prototype.slice.call(arguments)));\n};\n});\n\n/*\n * File: Graph.Op.js\n *\n*/\n\n/*\n   Object: Graph.Op\n\n   Perform <Graph> operations like adding/removing <Graph.Nodes> or <Graph.Adjacences>, \n   morphing a <Graph> into another <Graph>, contracting or expanding subtrees, etc.\n\n*/\nGraph.Op={\n\noptions:{\ntype:'nothing',\nduration:2000,\nhideLabels:true,\nfps:30},\n\n\ninitialize:function initialize(viz){\nthis.viz=viz;\n},\n\n/*\n       Method: removeNode\n    \n       Removes one or more <Graph.Nodes> from the visualization. \n       It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.\n\n       Parameters:\n    \n        node - (string|array) The node's id. Can also be an array having many ids.\n        opt - (object) Animation options. It's an object with optional properties described below\n        type - (string) Default's *nothing*. Type of the animation. Can be \"nothing\", \"replot\", \"fade:seq\",  \"fade:con\" or \"iter\".\n        duration - Described in <Options.Fx>.\n        fps - Described in <Options.Fx>.\n        transition - Described in <Options.Fx>.\n        hideLabels - (boolean) Default's *true*. Hide labels during the animation.\n   \n      Example:\n      (start code js)\n        var viz = new $jit.Viz(options);\n        viz.op.removeNode('nodeId', {\n          type: 'fade:seq',\n          duration: 1000,\n          hideLabels: false,\n          transition: $jit.Trans.Quart.easeOut\n        });\n        //or also\n        viz.op.removeNode(['someId', 'otherId'], {\n          type: 'fade:con',\n          duration: 1500\n        });\n      (end code)\n    */\n\nremoveNode:function removeNode(node,opt){\nvar viz=this.viz;\nvar options=$.merge(this.options,viz.controller,opt);\nvar n=$.splat(node);\nvar i,that,nodeObj;\nswitch(options.type){\ncase'nothing':\nfor(i=0;i<n.length;i++){viz.graph.removeNode(n[i]);}\nbreak;\n\ncase'replot':\nthis.removeNode(n,{type:'nothing'});\nviz.labels.clearLabels();\nviz.refresh(true);\nbreak;\n\ncase'fade:seq':case'fade':\nthat=this;\n//set alpha to 0 for nodes to remove.\nfor(i=0;i<n.length;i++){\nnodeObj=viz.graph.getNode(n[i]);\nnodeObj.setData('alpha',0,'end');\n}\nviz.fx.animate($.merge(options,{\nmodes:['node-property:alpha'],\nonComplete:function onComplete(){\nthat.removeNode(n,{type:'nothing'});\nviz.labels.clearLabels();\nviz.reposition();\nviz.fx.animate($.merge(options,{\nmodes:['linear']}));\n\n}}));\n\nbreak;\n\ncase'fade:con':\nthat=this;\n//set alpha to 0 for nodes to remove. Tag them for being ignored on computing positions.\nfor(i=0;i<n.length;i++){\nnodeObj=viz.graph.getNode(n[i]);\nnodeObj.setData('alpha',0,'end');\nnodeObj.ignore=true;\n}\nviz.reposition();\nviz.fx.animate($.merge(options,{\nmodes:['node-property:alpha','linear'],\nonComplete:function onComplete(){\nthat.removeNode(n,{type:'nothing'});\noptions.onComplete&&options.onComplete();\n}}));\n\nbreak;\n\ncase'iter':\nthat=this;\nviz.fx.sequence({\ncondition:function condition(){return n.length!=0;},\nstep:function step(){that.removeNode(n.shift(),{type:'nothing'});viz.labels.clearLabels();},\nonComplete:function onComplete(){options.onComplete&&options.onComplete();},\nduration:Math.ceil(options.duration/n.length)});\n\nbreak;\n\ndefault:this.doError();}\n\n},\n\n/*\n       Method: removeEdge\n    \n       Removes one or more <Graph.Adjacences> from the visualization. \n       It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.\n\n       Parameters:\n    \n       vertex - (array) An array having two strings which are the ids of the nodes connected by this edge (i.e ['id1', 'id2']). Can also be a two dimensional array holding many edges (i.e [['id1', 'id2'], ['id3', 'id4'], ...]).\n       opt - (object) Animation options. It's an object with optional properties described below\n       type - (string) Default's *nothing*. Type of the animation. Can be \"nothing\", \"replot\", \"fade:seq\",  \"fade:con\" or \"iter\".\n       duration - Described in <Options.Fx>.\n       fps - Described in <Options.Fx>.\n       transition - Described in <Options.Fx>.\n       hideLabels - (boolean) Default's *true*. Hide labels during the animation.\n   \n      Example:\n      (start code js)\n        var viz = new $jit.Viz(options);\n        viz.op.removeEdge(['nodeId', 'otherId'], {\n          type: 'fade:seq',\n          duration: 1000,\n          hideLabels: false,\n          transition: $jit.Trans.Quart.easeOut\n        });\n        //or also\n        viz.op.removeEdge([['someId', 'otherId'], ['id3', 'id4']], {\n          type: 'fade:con',\n          duration: 1500\n        });\n      (end code)\n    \n    */\nremoveEdge:function removeEdge(vertex,opt){\nvar viz=this.viz;\nvar options=$.merge(this.options,viz.controller,opt);\nvar v=$.type(vertex[0])=='string'?[vertex]:vertex;\nvar i,that,adj;\nswitch(options.type){\ncase'nothing':\nfor(i=0;i<v.length;i++){viz.graph.removeAdjacence(v[i][0],v[i][1]);}\nbreak;\n\ncase'replot':\nthis.removeEdge(v,{type:'nothing'});\nviz.refresh(true);\nbreak;\n\ncase'fade:seq':case'fade':\nthat=this;\n//set alpha to 0 for edges to remove.\nfor(i=0;i<v.length;i++){\nadj=viz.graph.getAdjacence(v[i][0],v[i][1]);\nif(adj){\nadj.setData('alpha',0,'end');\n}\n}\nviz.fx.animate($.merge(options,{\nmodes:['edge-property:alpha'],\nonComplete:function onComplete(){\nthat.removeEdge(v,{type:'nothing'});\nviz.reposition();\nviz.fx.animate($.merge(options,{\nmodes:['linear']}));\n\n}}));\n\nbreak;\n\ncase'fade:con':\nthat=this;\n//set alpha to 0 for nodes to remove. Tag them for being ignored when computing positions.\nfor(i=0;i<v.length;i++){\nadj=viz.graph.getAdjacence(v[i][0],v[i][1]);\nif(adj){\nadj.setData('alpha',0,'end');\nadj.ignore=true;\n}\n}\nviz.reposition();\nviz.fx.animate($.merge(options,{\nmodes:['edge-property:alpha','linear'],\nonComplete:function onComplete(){\nthat.removeEdge(v,{type:'nothing'});\noptions.onComplete&&options.onComplete();\n}}));\n\nbreak;\n\ncase'iter':\nthat=this;\nviz.fx.sequence({\ncondition:function condition(){return v.length!=0;},\nstep:function step(){that.removeEdge(v.shift(),{type:'nothing'});viz.labels.clearLabels();},\nonComplete:function onComplete(){options.onComplete();},\nduration:Math.ceil(options.duration/v.length)});\n\nbreak;\n\ndefault:this.doError();}\n\n},\n\n/*\n       Method: sum\n    \n       Adds a new graph to the visualization. \n       The JSON graph (or tree) must at least have a common node with the current graph plotted by the visualization. \n       The resulting graph can be defined as follows <http://mathworld.wolfram.com/GraphSum.html>\n\n       Parameters:\n    \n       json - (object) A json tree or graph structure. See also <Loader.loadJSON>.\n       opt - (object) Animation options. It's an object with optional properties described below\n       type - (string) Default's *nothing*. Type of the animation. Can be \"nothing\", \"replot\", \"fade:seq\",  \"fade:con\".\n       duration - Described in <Options.Fx>.\n       fps - Described in <Options.Fx>.\n       transition - Described in <Options.Fx>.\n       hideLabels - (boolean) Default's *true*. Hide labels during the animation.\n   \n      Example:\n      (start code js)\n        //...json contains a tree or graph structure...\n\n        var viz = new $jit.Viz(options);\n        viz.op.sum(json, {\n          type: 'fade:seq',\n          duration: 1000,\n          hideLabels: false,\n          transition: $jit.Trans.Quart.easeOut\n        });\n        //or also\n        viz.op.sum(json, {\n          type: 'fade:con',\n          duration: 1500\n        });\n      (end code)\n    \n    */\nsum:function sum(json,opt){\nvar viz=this.viz;\nvar options=$.merge(this.options,viz.controller,opt),root=viz.root;\nvar graph;\nviz.root=opt.id||viz.root;\nswitch(options.type){\ncase'nothing':\ngraph=viz.construct(json);\ngraph.eachNode(function(elem){\nelem.eachAdjacency(function(adj){\nviz.graph.addAdjacence(adj.nodeFrom,adj.nodeTo,adj.data);\n});\n});\nbreak;\n\ncase'replot':\nviz.refresh(true);\nthis.sum(json,{type:'nothing'});\nviz.refresh(true);\nbreak;\n\ncase'fade:seq':case'fade':case'fade:con':\n// START METAMAPS CODE\nvar that=this;\n// ORIGINAL CODE:\n// that = this;\n// END METAMAPS CODE\ngraph=viz.construct(json);\n\n//set alpha to 0 for nodes to add.\nvar fadeEdges=this.preprocessSum(graph);\nvar modes=!fadeEdges?['node-property:alpha']:['node-property:alpha','edge-property:alpha'];\nviz.reposition();\nif(options.type!='fade:con'){\nviz.fx.animate($.merge(options,{\nmodes:['linear'],\nonComplete:function onComplete(){\nviz.fx.animate($.merge(options,{\nmodes:modes,\nonComplete:function onComplete(){\noptions.onComplete();\n}}));\n\n}}));\n\n}else{\nviz.graph.eachNode(function(elem){\nif(elem.id!=root&&elem.pos.isZero()){\nelem.pos.set(elem.endPos);\nelem.startPos.set(elem.endPos);\n}\n});\nviz.fx.animate($.merge(options,{\nmodes:['linear'].concat(modes)}));\n\n}\nbreak;\n\ndefault:this.doError();}\n\n},\n\n/*\n       Method: morph\n    \n       This method will transform the current visualized graph into the new JSON representation passed in the method. \n       The JSON object must at least have the root node in common with the current visualized graph.\n\n       Parameters:\n    \n       json - (object) A json tree or graph structure. See also <Loader.loadJSON>.\n       opt - (object) Animation options. It's an object with optional properties described below\n       type - (string) Default's *nothing*. Type of the animation. Can be \"nothing\", \"replot\", \"fade:con\".\n       duration - Described in <Options.Fx>.\n       fps - Described in <Options.Fx>.\n       transition - Described in <Options.Fx>.\n       hideLabels - (boolean) Default's *true*. Hide labels during the animation.\n       id - (string) The shared <Graph.Node> id between both graphs.\n       \n       extraModes - (optional|object) When morphing with an animation, dollar prefixed data parameters are added to \n                    *endData* and not *data* itself. This way you can animate dollar prefixed parameters during your morphing operation. \n                    For animating these extra-parameters you have to specify an object that has animation groups as keys and animation \n                    properties as values, just like specified in <Graph.Plot.animate>.\n   \n      Example:\n      (start code js)\n        //...json contains a tree or graph structure...\n\n        var viz = new $jit.Viz(options);\n        viz.op.morph(json, {\n          type: 'fade',\n          duration: 1000,\n          hideLabels: false,\n          transition: $jit.Trans.Quart.easeOut\n        });\n        //or also\n        viz.op.morph(json, {\n          type: 'fade',\n          duration: 1500\n        });\n        //if the json data contains dollar prefixed params\n        //like $width or $height these too can be animated\n        viz.op.morph(json, {\n          type: 'fade',\n          duration: 1500\n        }, {\n          'node-property': ['width', 'height']\n        });\n      (end code)\n    \n    */\nmorph:function morph(json,opt,extraModes){\nextraModes=extraModes||{};\nvar viz=this.viz;\nvar options=$.merge(this.options,viz.controller,opt),root=viz.root;\nvar graph;\n//TODO(nico) this hack makes morphing work with the Hypertree. \n//Need to check if it has been solved and this can be removed.\nviz.root=opt.id||viz.root;\nswitch(options.type){\ncase'nothing':\ngraph=viz.construct(json);\ngraph.eachNode(function(elem){\nvar nodeExists=viz.graph.hasNode(elem.id);\nelem.eachAdjacency(function(adj){\nvar adjExists=!!viz.graph.getAdjacence(adj.nodeFrom.id,adj.nodeTo.id);\nviz.graph.addAdjacence(adj.nodeFrom,adj.nodeTo,adj.data);\n//Update data properties if the node existed\nif(adjExists){\nvar addedAdj=viz.graph.getAdjacence(adj.nodeFrom.id,adj.nodeTo.id);\nfor(var prop in adj.data||{}){\naddedAdj.data[prop]=adj.data[prop];\n}\n}\n});\n//Update data properties if the node existed\nif(nodeExists){\nvar addedNode=viz.graph.getNode(elem.id);\nfor(var prop in elem.data||{}){\naddedNode.data[prop]=elem.data[prop];\n}\n}\n});\nviz.graph.eachNode(function(elem){\nelem.eachAdjacency(function(adj){\nif(!graph.getAdjacence(adj.nodeFrom.id,adj.nodeTo.id)){\nviz.graph.removeAdjacence(adj.nodeFrom.id,adj.nodeTo.id);\n}\n});\nif(!graph.hasNode(elem.id))viz.graph.removeNode(elem.id);\n});\n\nbreak;\n\ncase'replot':\nviz.labels.clearLabels(true);\nthis.morph(json,{type:'nothing'});\nviz.refresh(true);\nviz.refresh(true);\nbreak;\n\ncase'fade:seq':case'fade':case'fade:con':\n// START METAMAPS CODE\nvar that=this;\n// ORIGINAL CODE:\n// that = this;\n// END METAMAPS CODE\ngraph=viz.construct(json);\n//preprocessing for nodes to delete.\n//get node property modes to interpolate\nvar nodeModes='node-property'in extraModes&&\n$.map($.splat(extraModes['node-property']),\nfunction(n){return'$'+n;});\nviz.graph.eachNode(function(elem){\nvar graphNode=graph.getNode(elem.id);\nif(!graphNode){\nelem.setData('alpha',1);\nelem.setData('alpha',1,'start');\nelem.setData('alpha',0,'end');\nelem.ignore=true;\n}else{\n//Update node data information\nvar graphNodeData=graphNode.data;\nfor(var prop in graphNodeData){\nif(nodeModes&&$.indexOf(nodeModes,prop)>-1){\nelem.endData[prop]=graphNodeData[prop];\n}else{\nelem.data[prop]=graphNodeData[prop];\n}\n}\n}\n});\nviz.graph.eachNode(function(elem){\nif(elem.ignore)return;\nelem.eachAdjacency(function(adj){\nif(adj.nodeFrom.ignore||adj.nodeTo.ignore)return;\nvar nodeFrom=graph.getNode(adj.nodeFrom.id);\nvar nodeTo=graph.getNode(adj.nodeTo.id);\nif(!nodeFrom.adjacentTo(nodeTo)){\nvar adj=viz.graph.getAdjacence(nodeFrom.id,nodeTo.id);\nfadeEdges=true;\nadj.setData('alpha',1);\nadj.setData('alpha',1,'start');\nadj.setData('alpha',0,'end');\n}\n});\n});\n//preprocessing for adding nodes.\nvar fadeEdges=this.preprocessSum(graph);\n\nvar modes=!fadeEdges?['node-property:alpha']:\n['node-property:alpha',\n'edge-property:alpha'];\n//Append extra node-property animations (if any)\nmodes[0]=modes[0]+('node-property'in extraModes?\n':'+$.splat(extraModes['node-property']).join(':'):'');\n//Append extra edge-property animations (if any)\nmodes[1]=(modes[1]||'edge-property:alpha')+('edge-property'in extraModes?\n':'+$.splat(extraModes['edge-property']).join(':'):'');\n//Add label-property animations (if any)\nif('label-property'in extraModes){\nmodes.push('label-property:'+$.splat(extraModes['label-property']).join(':'));\n}\n//only use reposition if its implemented.\nif(viz.reposition){\nviz.reposition();\n}else{\nviz.compute('end');\n}\nviz.graph.eachNode(function(elem){\nif(elem.id!=root&&elem.pos.getp().equals(Polar.KER)){\nelem.pos.set(elem.endPos);elem.startPos.set(elem.endPos);\n}\n});\nviz.fx.animate($.merge(options,{\nmodes:[extraModes.position||'polar'].concat(modes),\nonComplete:function onComplete(){\nviz.graph.eachNode(function(elem){\nif(elem.ignore)viz.graph.removeNode(elem.id);\n});\nviz.graph.eachNode(function(elem){\nelem.eachAdjacency(function(adj){\nif(adj.ignore)viz.graph.removeAdjacence(adj.nodeFrom.id,adj.nodeTo.id);\n});\n});\noptions.onComplete();\n}}));\n\nbreak;\n\ndefault:;}\n\n},\n\n\n/*\n    Method: contract\n \n    Collapses the subtree of the given node. The node will have a _collapsed=true_ property.\n    \n    Parameters:\n \n    node - (object) A <Graph.Node>.\n    opt - (object) An object containing options described below\n    type - (string) Whether to 'replot' or 'animate' the contraction.\n   \n    There are also a number of Animation options. For more information see <Options.Fx>.\n\n    Example:\n    (start code js)\n     var viz = new $jit.Viz(options);\n     viz.op.contract(node, {\n       type: 'animate',\n       duration: 1000,\n       hideLabels: true,\n       transition: $jit.Trans.Quart.easeOut\n     });\n   (end code)\n \n   */\ncontract:function contract(node,opt){\nvar viz=this.viz;\nif(node.collapsed||!node.anySubnode($.lambda(true)))return;\nopt=$.merge(this.options,viz.config,opt||{},{\n'modes':['node-property:alpha:span','linear']});\n\nnode.collapsed=true;\n(function subn(n){\nn.eachSubnode(function(ch){\nch.ignore=true;\nch.setData('alpha',0,opt.type=='animate'?'end':'current');\nsubn(ch);\n});\n})(node);\nif(opt.type=='animate'){\nviz.compute('end');\nif(viz.rotated){\nviz.rotate(viz.rotated,'none',{\n'property':'end'});\n\n}\n(function subn(n){\nn.eachSubnode(function(ch){\nch.setPos(node.getPos('end'),'end');\nsubn(ch);\n});\n})(node);\nviz.fx.animate(opt);\n}else if(opt.type=='replot'){\nviz.refresh();\n}\n},\n\n/*\n    Method: expand\n \n    Expands the previously contracted subtree. The given node must have the _collapsed=true_ property.\n    \n    Parameters:\n \n    node - (object) A <Graph.Node>.\n    opt - (object) An object containing options described below\n    type - (string) Whether to 'replot' or 'animate'.\n     \n    There are also a number of Animation options. For more information see <Options.Fx>.\n\n    Example:\n    (start code js)\n      var viz = new $jit.Viz(options);\n      viz.op.expand(node, {\n        type: 'animate',\n        duration: 1000,\n        hideLabels: true,\n        transition: $jit.Trans.Quart.easeOut\n      });\n    (end code)\n \n   */\nexpand:function expand(node,opt){\nif(!('collapsed'in node))return;\nvar viz=this.viz;\nopt=$.merge(this.options,viz.config,opt||{},{\n'modes':['node-property:alpha:span','linear']});\n\ndelete node.collapsed;\n(function subn(n){\nn.eachSubnode(function(ch){\ndelete ch.ignore;\nch.setData('alpha',1,opt.type=='animate'?'end':'current');\nsubn(ch);\n});\n})(node);\nif(opt.type=='animate'){\nviz.compute('end');\nif(viz.rotated){\nviz.rotate(viz.rotated,'none',{\n'property':'end'});\n\n}\nviz.fx.animate(opt);\n}else if(opt.type=='replot'){\nviz.refresh();\n}\n},\n\npreprocessSum:function preprocessSum(graph){\nvar viz=this.viz;\ngraph.eachNode(function(elem){\nif(!viz.graph.hasNode(elem.id)){\nviz.graph.addNode(elem);\nvar n=viz.graph.getNode(elem.id);\nn.setData('alpha',0);\nn.setData('alpha',0,'start');\nn.setData('alpha',1,'end');\n}\n});\nvar fadeEdges=false;\ngraph.eachNode(function(elem){\nelem.eachAdjacency(function(adj){\nvar nodeFrom=viz.graph.getNode(adj.nodeFrom.id);\nvar nodeTo=viz.graph.getNode(adj.nodeTo.id);\nif(!nodeFrom.adjacentTo(nodeTo)){\nvar adj=viz.graph.addAdjacence(nodeFrom,nodeTo,adj.data);\nif(nodeFrom.startAlpha==nodeFrom.endAlpha&&\nnodeTo.startAlpha==nodeTo.endAlpha){\nfadeEdges=true;\nadj.setData('alpha',0);\nadj.setData('alpha',0,'start');\nadj.setData('alpha',1,'end');\n}\n}\n});\n});\nreturn fadeEdges;\n}};\n\n\n\n\n/*\n   File: Helpers.js\n \n   Helpers are objects that contain rendering primitives (like rectangles, ellipses, etc), for plotting nodes and edges.\n   Helpers also contain implementations of the *contains* method, a method returning a boolean indicating whether the mouse\n   position is over the rendered shape.\n   \n   Helpers are very useful when implementing new NodeTypes, since you can access them through *this.nodeHelper* and \n   *this.edgeHelper* <Graph.Plot> properties, providing you with simple primitives and mouse-position check functions.\n   \n   Example:\n   (start code js)\n   //implement a new node type\n   $jit.Viz.Plot.NodeTypes.implement({\n     'customNodeType': {\n       'render': function(node, canvas) {\n         this.nodeHelper.circle.render ...\n       },\n       'contains': function(node, pos) {\n         this.nodeHelper.circle.contains ...\n       }\n     }\n   });\n   //implement an edge type\n   $jit.Viz.Plot.EdgeTypes.implement({\n     'customNodeType': {\n       'render': function(node, canvas) {\n         this.edgeHelper.circle.render ...\n       },\n       //optional\n       'contains': function(node, pos) {\n         this.edgeHelper.circle.contains ...\n       }\n     }\n   });\n   (end code)\n\n*/\n\n/*\n   Object: NodeHelper\n   \n   Contains rendering and other type of primitives for simple shapes.\n */\nvar NodeHelper={\n'none':{\n'render':$.empty,\n'contains':$.lambda(false)},\n\n/*\n   Object: NodeHelper.circle\n   */\n'circle':{\n/*\n     Method: render\n     \n     Renders a circle into the canvas.\n     \n     Parameters:\n     \n     type - (string) Possible options are 'fill' or 'stroke'.\n     pos - (object) An *x*, *y* object with the position of the center of the circle.\n     radius - (number) The radius of the circle to be rendered.\n     canvas - (object) A <Canvas> instance.\n     \n     Example:\n     (start code js)\n     NodeHelper.circle.render('fill', { x: 10, y: 30 }, 30, viz.canvas);\n     (end code)\n     */\n'render':function render(type,pos,radius,canvas){\nvar ctx=canvas.getCtx();\nctx.beginPath();\nctx.arc(pos.x,pos.y,radius,0,Math.PI*2,true);\nctx.closePath();\nctx[type]();\n},\n/*\n    Method: contains\n    \n    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n    \n    Parameters:\n    \n    npos - (object) An *x*, *y* object with the <Graph.Node> position.\n    pos - (object) An *x*, *y* object with the position to check.\n    radius - (number) The radius of the rendered circle.\n    \n    Example:\n    (start code js)\n    NodeHelper.circle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30); //true\n    (end code)\n    */\n'contains':function contains(npos,pos,radius){\nvar diffx=npos.x-pos.x,\ndiffy=npos.y-pos.y,\ndiff=diffx*diffx+diffy*diffy;\nreturn diff<=radius*radius;\n}},\n\n/*\n  Object: NodeHelper.ellipse\n  */\n'ellipse':{\n/*\n    Method: render\n    \n    Renders an ellipse into the canvas.\n    \n    Parameters:\n    \n    type - (string) Possible options are 'fill' or 'stroke'.\n    pos - (object) An *x*, *y* object with the position of the center of the ellipse.\n    width - (number) The width of the ellipse.\n    height - (number) The height of the ellipse.\n    canvas - (object) A <Canvas> instance.\n    \n    Example:\n    (start code js)\n    NodeHelper.ellipse.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);\n    (end code)\n    */\n'render':function render(type,pos,width,height,canvas){\nvar ctx=canvas.getCtx(),\nscalex=1,\nscaley=1,\nscaleposx=1,\nscaleposy=1,\nradius=0;\n\nif(width>height){\nradius=width/2;\nscaley=height/width;\nscaleposy=width/height;\n}else{\nradius=height/2;\nscalex=width/height;\nscaleposx=height/width;\n}\n\nctx.save();\nctx.scale(scalex,scaley);\nctx.beginPath();\nctx.arc(pos.x*scaleposx,pos.y*scaleposy,radius,0,Math.PI*2,true);\nctx.closePath();\nctx[type]();\nctx.restore();\n},\n/*\n    Method: contains\n    \n    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n    \n    Parameters:\n    \n    npos - (object) An *x*, *y* object with the <Graph.Node> position.\n    pos - (object) An *x*, *y* object with the position to check.\n    width - (number) The width of the rendered ellipse.\n    height - (number) The height of the rendered ellipse.\n    \n    Example:\n    (start code js)\n    NodeHelper.ellipse.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);\n    (end code)\n    */\n'contains':function contains(npos,pos,width,height){\nvar radius=0,\nscalex=1,\nscaley=1,\ndiffx=0,\ndiffy=0,\ndiff=0;\n\nif(width>height){\nradius=width/2;\nscaley=height/width;\n}else{\nradius=height/2;\nscalex=width/height;\n}\n\ndiffx=(npos.x-pos.x)*(1/scalex);\ndiffy=(npos.y-pos.y)*(1/scaley);\ndiff=diffx*diffx+diffy*diffy;\nreturn diff<=radius*radius;\n}},\n\n/*\n  Object: NodeHelper.square\n  */\n'square':{\n/*\n    Method: render\n    \n    Renders a square into the canvas.\n    \n    Parameters:\n    \n    type - (string) Possible options are 'fill' or 'stroke'.\n    pos - (object) An *x*, *y* object with the position of the center of the square.\n    dim - (number) The radius (or half-diameter) of the square.\n    canvas - (object) A <Canvas> instance.\n    \n    Example:\n    (start code js)\n    NodeHelper.square.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);\n    (end code)\n    */\n'render':function render(type,pos,dim,canvas){\ncanvas.getCtx()[type+\"Rect\"](pos.x-dim,pos.y-dim,2*dim,2*dim);\n},\n/*\n    Method: contains\n    \n    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n    \n    Parameters:\n    \n    npos - (object) An *x*, *y* object with the <Graph.Node> position.\n    pos - (object) An *x*, *y* object with the position to check.\n    dim - (number) The radius (or half-diameter) of the square.\n    \n    Example:\n    (start code js)\n    NodeHelper.square.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);\n    (end code)\n    */\n'contains':function contains(npos,pos,dim){\nreturn Math.abs(pos.x-npos.x)<=dim&&Math.abs(pos.y-npos.y)<=dim;\n}},\n\n/*\n  Object: NodeHelper.rectangle\n  */\n'rectangle':{\n/*\n    Method: render\n    \n    Renders a rectangle into the canvas.\n    \n    Parameters:\n    \n    type - (string) Possible options are 'fill' or 'stroke'.\n    pos - (object) An *x*, *y* object with the position of the center of the rectangle.\n    width - (number) The width of the rectangle.\n    height - (number) The height of the rectangle.\n    canvas - (object) A <Canvas> instance.\n    \n    Example:\n    (start code js)\n    NodeHelper.rectangle.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);\n    (end code)\n    */\n'render':function render(type,pos,width,height,canvas){\ncanvas.getCtx()[type+\"Rect\"](pos.x-width/2,pos.y-height/2,\nwidth,height);\n},\n/*\n    Method: contains\n    \n    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n    \n    Parameters:\n    \n    npos - (object) An *x*, *y* object with the <Graph.Node> position.\n    pos - (object) An *x*, *y* object with the position to check.\n    width - (number) The width of the rendered rectangle.\n    height - (number) The height of the rendered rectangle.\n    \n    Example:\n    (start code js)\n    NodeHelper.rectangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);\n    (end code)\n    */\n'contains':function contains(npos,pos,width,height){\nreturn Math.abs(pos.x-npos.x)<=width/2&&\nMath.abs(pos.y-npos.y)<=height/2;\n}},\n\n/*\n  Object: NodeHelper.triangle\n  */\n'triangle':{\n/*\n    Method: render\n    \n    Renders a triangle into the canvas.\n    \n    Parameters:\n    \n    type - (string) Possible options are 'fill' or 'stroke'.\n    pos - (object) An *x*, *y* object with the position of the center of the triangle.\n    dim - (number) Half the base and half the height of the triangle.\n    canvas - (object) A <Canvas> instance.\n    \n    Example:\n    (start code js)\n    NodeHelper.triangle.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);\n    (end code)\n    */\n'render':function render(type,pos,dim,canvas){\nvar ctx=canvas.getCtx(),\nc1x=pos.x,\nc1y=pos.y-dim,\nc2x=c1x-dim,\nc2y=pos.y+dim,\nc3x=c1x+dim,\nc3y=c2y;\nctx.beginPath();\nctx.moveTo(c1x,c1y);\nctx.lineTo(c2x,c2y);\nctx.lineTo(c3x,c3y);\nctx.closePath();\nctx[type]();\n},\n/*\n    Method: contains\n    \n    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n    \n    Parameters:\n    \n    npos - (object) An *x*, *y* object with the <Graph.Node> position.\n    pos - (object) An *x*, *y* object with the position to check.\n    dim - (number) Half the base and half the height of the triangle.\n    \n    Example:\n    (start code js)\n    NodeHelper.triangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);\n    (end code)\n    */\n'contains':function contains(npos,pos,dim){\nreturn NodeHelper.circle.contains(npos,pos,dim);\n}},\n\n/*\n  Object: NodeHelper.star\n  */\n'star':{\n/*\n    Method: render\n    \n    Renders a star (concave decagon) into the canvas.\n    \n    Parameters:\n    \n    type - (string) Possible options are 'fill' or 'stroke'.\n    pos - (object) An *x*, *y* object with the position of the center of the star.\n    dim - (number) The length of a side of a concave decagon.\n    canvas - (object) A <Canvas> instance.\n    \n    Example:\n    (start code js)\n    NodeHelper.star.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);\n    (end code)\n    */\n'render':function render(type,pos,dim,canvas){\nvar ctx=canvas.getCtx(),\npi5=Math.PI/5;\nctx.save();\nctx.translate(pos.x,pos.y);\nctx.beginPath();\nctx.moveTo(dim,0);\nfor(var i=0;i<9;i++){\nctx.rotate(pi5);\nif(i%2==0){\nctx.lineTo(dim/0.525731*0.200811,0);\n}else{\nctx.lineTo(dim,0);\n}\n}\nctx.closePath();\nctx[type]();\nctx.restore();\n},\n/*\n    Method: contains\n    \n    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n    \n    Parameters:\n    \n    npos - (object) An *x*, *y* object with the <Graph.Node> position.\n    pos - (object) An *x*, *y* object with the position to check.\n    dim - (number) The length of a side of a concave decagon.\n    \n    Example:\n    (start code js)\n    NodeHelper.star.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);\n    (end code)\n    */\n'contains':function contains(npos,pos,dim){\nreturn NodeHelper.circle.contains(npos,pos,dim);\n}}};\n\n\n\n/*\n  Object: EdgeHelper\n  \n  Contains rendering primitives for simple edge shapes.\n*/\nvar EdgeHelper={\n/*\n    Object: EdgeHelper.line\n  */\n'line':{\n/*\n      Method: render\n      \n      Renders a line into the canvas.\n      \n      Parameters:\n      \n      from - (object) An *x*, *y* object with the starting position of the line.\n      to - (object) An *x*, *y* object with the ending position of the line.\n      canvas - (object) A <Canvas> instance.\n      \n      Example:\n      (start code js)\n      EdgeHelper.line.render({ x: 10, y: 30 }, { x: 10, y: 50 }, viz.canvas);\n      (end code)\n      */\n'render':function render(from,to,canvas){\nvar ctx=canvas.getCtx();\nctx.beginPath();\nctx.moveTo(from.x,from.y);\nctx.lineTo(to.x,to.y);\nctx.stroke();\n},\n/*\n      Method: contains\n      \n      Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n      \n      Parameters:\n      \n      posFrom - (object) An *x*, *y* object with a <Graph.Node> position.\n      posTo - (object) An *x*, *y* object with a <Graph.Node> position.\n      pos - (object) An *x*, *y* object with the position to check.\n      epsilon - (number) The dimension of the shape.\n      \n      Example:\n      (start code js)\n      EdgeHelper.line.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);\n      (end code)\n      */\n'contains':function contains(posFrom,posTo,pos,epsilon){\nvar min=Math.min,\nmax=Math.max,\nminPosX=min(posFrom.x,posTo.x),\nmaxPosX=max(posFrom.x,posTo.x),\nminPosY=min(posFrom.y,posTo.y),\nmaxPosY=max(posFrom.y,posTo.y);\n\nif(pos.x>=minPosX&&pos.x<=maxPosX&&\npos.y>=minPosY&&pos.y<=maxPosY){\nif(Math.abs(posTo.x-posFrom.x)<=epsilon){\n\nreturn true;\n}\nvar dist=(posTo.y-posFrom.y)/(posTo.x-posFrom.x)*(pos.x-posFrom.x)+posFrom.y;\n\nreturn Math.abs(dist-pos.y)<=epsilon;\n}\nreturn false;\n}},\n\n/*\n    Object: EdgeHelper.arrow\n  */\n'arrow':{\n/*\n      Method: render\n      \n      Renders an arrow into the canvas.\n      \n      Parameters:\n      \n      from - (object) An *x*, *y* object with the starting position of the arrow.\n      to - (object) An *x*, *y* object with the ending position of the arrow.\n      dim - (number) The dimension of the arrow.\n      swap - (boolean) Whether to set the arrow pointing to the starting position or the ending position.\n      canvas - (object) A <Canvas> instance.\n      \n      Example:\n      (start code js)\n      EdgeHelper.arrow.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 13, false, viz.canvas);\n      (end code)\n      */\n'render':function render(from,to,dim,swap,canvas){\nvar ctx=canvas.getCtx();\n// invert edge direction\nif(swap){\nvar tmp=from;\nfrom=to;\nto=tmp;\n}\nvar vect=new Complex(to.x-from.x,to.y-from.y);\nvect.$scale(dim/vect.norm());\nvar intermediatePoint=new Complex(to.x-vect.x,to.y-vect.y),\nnormal=new Complex(-vect.y/2,vect.x/2),\nv1=intermediatePoint.add(normal),\nv2=intermediatePoint.$add(normal.$scale(-1));\n\nctx.beginPath();\nctx.moveTo(from.x,from.y);\nctx.lineTo(to.x,to.y);\nctx.stroke();\nctx.beginPath();\nctx.moveTo(v1.x,v1.y);\nctx.lineTo(v2.x,v2.y);\nctx.lineTo(to.x,to.y);\nctx.closePath();\nctx.fill();\n},\n/*\n    Method: contains\n    \n    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n    \n    Parameters:\n    \n    posFrom - (object) An *x*, *y* object with a <Graph.Node> position.\n    posTo - (object) An *x*, *y* object with a <Graph.Node> position.\n    pos - (object) An *x*, *y* object with the position to check.\n    epsilon - (number) The dimension of the shape.\n    \n    Example:\n    (start code js)\n    EdgeHelper.arrow.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);\n    (end code)\n    */\n'contains':function contains(posFrom,posTo,pos,epsilon){\nreturn EdgeHelper.line.contains(posFrom,posTo,pos,epsilon);\n}},\n\n/*\n    Object: EdgeHelper.hyperline\n  */\n'hyperline':{\n/*\n    Method: render\n    \n    Renders a hyperline into the canvas. A hyperline are the lines drawn for the <Hypertree> visualization.\n    \n    Parameters:\n    \n    from - (object) An *x*, *y* object with the starting position of the hyperline. *x* and *y* must belong to [0, 1).\n    to - (object) An *x*, *y* object with the ending position of the hyperline. *x* and *y* must belong to [0, 1).\n    r - (number) The scaling factor.\n    canvas - (object) A <Canvas> instance.\n    \n    Example:\n    (start code js)\n    EdgeHelper.hyperline.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 100, viz.canvas);\n    (end code)\n    */\n'render':function render(from,to,r,canvas){\nvar ctx=canvas.getCtx();\nvar centerOfCircle=computeArcThroughTwoPoints(from,to);\nif(centerOfCircle.a>1000||centerOfCircle.b>1000||\ncenterOfCircle.ratio<0){\nctx.beginPath();\nctx.moveTo(from.x*r,from.y*r);\nctx.lineTo(to.x*r,to.y*r);\nctx.stroke();\n}else{\nvar angleBegin=Math.atan2(to.y-centerOfCircle.y,to.x-\ncenterOfCircle.x);\nvar angleEnd=Math.atan2(from.y-centerOfCircle.y,from.x-\ncenterOfCircle.x);\nvar sense=sense(angleBegin,angleEnd);\nctx.beginPath();\nctx.arc(centerOfCircle.x*r,centerOfCircle.y*r,centerOfCircle.ratio*\nr,angleBegin,angleEnd,sense);\nctx.stroke();\n}\n/*      \n        Calculates the arc parameters through two points.\n        \n        More information in <http://en.wikipedia.org/wiki/Poincar%C3%A9_disc_model#Analytic_geometry_constructions_in_the_hyperbolic_plane> \n      \n        Parameters:\n      \n        p1 - A <Complex> instance.\n        p2 - A <Complex> instance.\n        scale - The Disk's diameter.\n      \n        Returns:\n      \n        An object containing some arc properties.\n      */\nfunction computeArcThroughTwoPoints(p1,p2){\nvar aDen=p1.x*p2.y-p1.y*p2.x,bDen=aDen;\nvar sq1=p1.squaredNorm(),sq2=p2.squaredNorm();\n// Fall back to a straight line\nif(aDen==0)\nreturn{\nx:0,\ny:0,\nratio:-1};\n\n\nvar a=(p1.y*sq2-p2.y*sq1+p1.y-p2.y)/aDen;\nvar b=(p2.x*sq1-p1.x*sq2+p2.x-p1.x)/bDen;\nvar x=-a/2;\nvar y=-b/2;\nvar squaredRatio=(a*a+b*b)/4-1;\n// Fall back to a straight line\nif(squaredRatio<0)\nreturn{\nx:0,\ny:0,\nratio:-1};\n\nvar ratio=Math.sqrt(squaredRatio);\nvar out={\nx:x,\ny:y,\nratio:ratio>1000?-1:ratio,\na:a,\nb:b};\n\n\nreturn out;\n}\n/*      \n        Sets angle direction to clockwise (true) or counterclockwise (false). \n         \n        Parameters: \n      \n           angleBegin - Starting angle for drawing the arc. \n           angleEnd - The HyperLine will be drawn from angleBegin to angleEnd. \n      \n        Returns: \n      \n           A Boolean instance describing the sense for drawing the HyperLine. \n      */\nfunction sense(angleBegin,angleEnd){\nreturn angleBegin<angleEnd?angleBegin+Math.PI>angleEnd?false:\ntrue:angleEnd+Math.PI>angleBegin?true:false;\n}\n},\n/*\n    Method: contains\n    \n    Not Implemented\n    \n    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.\n    \n    Parameters:\n    \n    posFrom - (object) An *x*, *y* object with a <Graph.Node> position.\n    posTo - (object) An *x*, *y* object with a <Graph.Node> position.\n    pos - (object) An *x*, *y* object with the position to check.\n    epsilon - (number) The dimension of the shape.\n    \n    Example:\n    (start code js)\n    EdgeHelper.hyperline.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);\n    (end code)\n    */\n'contains':$.lambda(false)}};\n\n\n\n\n/*\n * File: Graph.Plot.js\n */\n\n/*\n   Object: Graph.Plot\n\n   <Graph> rendering and animation methods.\n   \n   Properties:\n   \n   nodeHelper - <NodeHelper> object.\n   edgeHelper - <EdgeHelper> object.\n*/\nGraph.Plot={\n//Default initializer\ninitialize:function initialize(viz,klass){\nthis.viz=viz;\nthis.config=viz.config;\nthis.node=viz.config.Node;\nthis.edge=viz.config.Edge;\nthis.animation=new Animation();\nthis.nodeTypes=new klass.Plot.NodeTypes();\nthis.edgeTypes=new klass.Plot.EdgeTypes();\nthis.labels=viz.labels;\n},\n\n//Add helpers\nnodeHelper:NodeHelper,\nedgeHelper:EdgeHelper,\n\nInterpolator:{\n//node/edge property parsers\n'map':{\n'border':'color',\n'color':'color',\n'width':'number',\n'height':'number',\n'dim':'number',\n'alpha':'number',\n'lineWidth':'number',\n'angularWidth':'number',\n'span':'number',\n'valueArray':'array-number',\n'dimArray':'array-number'\n//'colorArray':'array-color'\n},\n\n//canvas specific parsers\n'canvas':{\n'globalAlpha':'number',\n'fillStyle':'color',\n'strokeStyle':'color',\n'lineWidth':'number',\n'shadowBlur':'number',\n'shadowColor':'color',\n'shadowOffsetX':'number',\n'shadowOffsetY':'number',\n'miterLimit':'number'},\n\n\n//label parsers\n'label':{\n'size':'number',\n'color':'color'},\n\n\n//Number interpolator\n'compute':function compute(from,to,delta){\nreturn from+(to-from)*delta;\n},\n\n//Position interpolators\n'moebius':function moebius(elem,props,delta,vector){\nvar v=vector.scale(-delta);\nif(v.norm()<1){\nvar x=v.x,y=v.y;\nvar ans=elem.startPos.\ngetc().moebiusTransformation(v);\nelem.pos.setc(ans.x,ans.y);\nv.x=x;v.y=y;\n}\n},\n\n'linear':function linear(elem,props,delta){\nvar from=elem.startPos.getc(true);\nvar to=elem.endPos.getc(true);\nelem.pos.setc(this.compute(from.x,to.x,delta),\nthis.compute(from.y,to.y,delta));\n},\n\n'polar':function polar(elem,props,delta){\nvar from=elem.startPos.getp(true);\nvar to=elem.endPos.getp();\nvar ans=to.interpolate(from,delta);\nelem.pos.setp(ans.theta,ans.rho);\n},\n\n//Graph's Node/Edge interpolators\n'number':function number(elem,prop,delta,getter,setter){\nvar from=elem[getter](prop,'start');\nvar to=elem[getter](prop,'end');\nelem[setter](prop,this.compute(from,to,delta));\n},\n\n'color':function color(elem,prop,delta,getter,setter){\nvar from=$.hexToRgb(elem[getter](prop,'start'));\nvar to=$.hexToRgb(elem[getter](prop,'end'));\nvar comp=this.compute;\nvar val=$.rgbToHex([parseInt(comp(from[0],to[0],delta)),\nparseInt(comp(from[1],to[1],delta)),\nparseInt(comp(from[2],to[2],delta))]);\n\nelem[setter](prop,val);\n},\n\n'array-number':function arrayNumber(elem,prop,delta,getter,setter){\nvar from=elem[getter](prop,'start'),\nto=elem[getter](prop,'end'),\ncur=[];\nfor(var i=0,l=from.length;i<l;i++){\nvar fromi=from[i],toi=to[i];\nif(fromi.length){\nfor(var j=0,len=fromi.length,curi=[];j<len;j++){\ncuri.push(this.compute(fromi[j],toi[j],delta));\n}\ncur.push(curi);\n}else{\ncur.push(this.compute(fromi,toi,delta));\n}\n}\nelem[setter](prop,cur);\n},\n\n'node':function node(elem,props,delta,map,getter,setter){\nmap=this[map];\nif(props){\nvar len=props.length;\nfor(var i=0;i<len;i++){\nvar pi=props[i];\nthis[map[pi]](elem,pi,delta,getter,setter);\n}\n}else{\nfor(var pi in map){\nthis[map[pi]](elem,pi,delta,getter,setter);\n}\n}\n},\n\n'edge':function edge(elem,props,delta,mapKey,getter,setter){\nvar adjs=elem.adjacencies;\nfor(var id in adjs){this['node'](adjs[id],props,delta,mapKey,getter,setter);}\n},\n\n'node-property':function nodeProperty(elem,props,delta){\nthis['node'](elem,props,delta,'map','getData','setData');\n},\n\n'edge-property':function edgeProperty(elem,props,delta){\nthis['edge'](elem,props,delta,'map','getData','setData');\n},\n\n'label-property':function labelProperty(elem,props,delta){\nthis['node'](elem,props,delta,'label','getLabelData','setLabelData');\n},\n\n'node-style':function nodeStyle(elem,props,delta){\nthis['node'](elem,props,delta,'canvas','getCanvasStyle','setCanvasStyle');\n},\n\n'edge-style':function edgeStyle(elem,props,delta){\nthis['edge'](elem,props,delta,'canvas','getCanvasStyle','setCanvasStyle');\n}},\n\n\n\n/*\n       sequence\n    \n       Iteratively performs an action while refreshing the state of the visualization.\n\n       Parameters:\n\n       options - (object) An object containing some sequence options described below\n       condition - (function) A function returning a boolean instance in order to stop iterations.\n       step - (function) A function to execute on each step of the iteration.\n       onComplete - (function) A function to execute when the sequence finishes.\n       duration - (number) Duration (in milliseconds) of each step.\n\n      Example:\n       (start code js)\n        var rg = new $jit.RGraph(options);\n        var i = 0;\n        rg.fx.sequence({\n          condition: function() {\n           return i == 10;\n          },\n          step: function() {\n            alert(i++);\n          },\n          onComplete: function() {\n           alert('done!');\n          }\n        });\n       (end code)\n\n    */\nsequence:function sequence(options){\nvar that=this;\noptions=$.merge({\ncondition:$.lambda(false),\nstep:$.empty,\nonComplete:$.empty,\nduration:200},\noptions||{});\n\nvar interval=setInterval(function(){\nif(options.condition()){\noptions.step();\n}else{\nclearInterval(interval);\noptions.onComplete();\n}\nthat.viz.refresh(true);\n},options.duration);\n},\n\n/*\n      prepare\n \n      Prepare graph position and other attribute values before performing an Animation. \n      This method is used internally by the Toolkit.\n      \n      See also:\n       \n       <Animation>, <Graph.Plot.animate>\n\n    */\nprepare:function prepare(modes){\nvar graph=this.viz.graph,\naccessors={\n'node-property':{\n'getter':'getData',\n'setter':'setData'},\n\n'edge-property':{\n'getter':'getData',\n'setter':'setData'},\n\n'node-style':{\n'getter':'getCanvasStyle',\n'setter':'setCanvasStyle'},\n\n'edge-style':{\n'getter':'getCanvasStyle',\n'setter':'setCanvasStyle'}};\n\n\n\n//parse modes\nvar m={};\nif($.type(modes)=='array'){\nfor(var i=0,len=modes.length;i<len;i++){\nvar elems=modes[i].split(':');\nm[elems.shift()]=elems;\n}\n}else{\nfor(var p in modes){\nif(p=='position'){\nm[modes.position]=[];\n}else{\nm[p]=$.splat(modes[p]);\n}\n}\n}\n\ngraph.eachNode(function(node){\nnode.startPos.set(node.pos);\n$.each(['node-property','node-style'],function(p){\nif(p in m){\nvar prop=m[p];\nfor(var i=0,l=prop.length;i<l;i++){\nnode[accessors[p].setter](prop[i],node[accessors[p].getter](prop[i]),'start');\n}\n}\n});\n$.each(['edge-property','edge-style'],function(p){\nif(p in m){\nvar prop=m[p];\nnode.eachAdjacency(function(adj){\nfor(var i=0,l=prop.length;i<l;i++){\nadj[accessors[p].setter](prop[i],adj[accessors[p].getter](prop[i]),'start');\n}\n});\n}\n});\n});\nreturn m;\n},\n\n/*\n       Method: animate\n    \n       Animates a <Graph> by interpolating some <Graph.Node>, <Graph.Adjacence> or <Graph.Label> properties.\n\n       Parameters:\n\n       opt - (object) Animation options. The object properties are described below\n       duration - (optional) Described in <Options.Fx>.\n       fps - (optional) Described in <Options.Fx>.\n       hideLabels - (optional|boolean) Whether to hide labels during the animation.\n       modes - (required|object) An object with animation modes (described below).\n\n       Animation modes:\n       \n       Animation modes are strings representing different node/edge and graph properties that you'd like to animate. \n       They are represented by an object that has as keys main categories of properties to animate and as values a list \n       of these specific properties. The properties are described below\n       \n       position - Describes the way nodes' positions must be interpolated. Possible values are 'linear', 'polar' or 'moebius'.\n       node-property - Describes which Node properties will be interpolated. These properties can be any of the ones defined in <Options.Node>.\n       edge-property - Describes which Edge properties will be interpolated. These properties can be any the ones defined in <Options.Edge>.\n       label-property - Describes which Label properties will be interpolated. These properties can be any of the ones defined in <Options.Label> like color or size.\n       node-style - Describes which Node Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.\n       edge-style - Describes which Edge Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.\n\n       Example:\n       (start code js)\n       var viz = new $jit.Viz(options);\n       //...tweak some Data, CanvasStyles or LabelData properties...\n       viz.fx.animate({\n         modes: {\n           'position': 'linear',\n           'node-property': ['width', 'height'],\n           'node-style': 'shadowColor',\n           'label-property': 'size'\n         },\n         hideLabels: false\n       });\n       //...can also be written like this...\n       viz.fx.animate({\n         modes: ['linear',\n                 'node-property:width:height',\n                 'node-style:shadowColor',\n                 'label-property:size'],\n         hideLabels: false\n       });\n       (end code)\n    */\nanimate:function animate(opt,versor){\nopt=$.merge(this.viz.config,opt||{});\nvar that=this,\nviz=this.viz,\ngraph=viz.graph,\ninterp=this.Interpolator,\nanimation=opt.type==='nodefx'?this.nodeFxAnimation:this.animation;\n//prepare graph values\nvar m=this.prepare(opt.modes);\n\n//animate\nif(opt.hideLabels)this.labels.hideLabels(true);\nanimation.setOptions($.extend(opt,{\n$animating:false,\ncompute:function compute(delta){\ngraph.eachNode(function(node){\nfor(var p in m){\ninterp[p](node,m[p],delta,versor);\n}\n});\nthat.plot(opt,this.$animating,delta);\nthis.$animating=true;\n},\ncomplete:function complete(){\nif(opt.hideLabels)that.labels.hideLabels(false);\nthat.plot(opt);\nopt.onComplete();\n//TODO(nico): This shouldn't be here!\n//opt.onAfterCompute();\n}})).\nstart();\n},\n\n/*\n      nodeFx\n   \n      Apply animation to node properties like color, width, height, dim, etc.\n  \n      Parameters:\n  \n      options - Animation options. This object properties is described below\n      elements - The Elements to be transformed. This is an object that has a properties\n      \n      (start code js)\n      'elements': {\n        //can also be an array of ids\n        'id': 'id-of-node-to-transform',\n        //properties to be modified. All properties are optional.\n        'properties': {\n          'color': '#ccc', //some color\n          'width': 10, //some width\n          'height': 10, //some height\n          'dim': 20, //some dim\n          'lineWidth': 10 //some line width\n        } \n      }\n      (end code)\n      \n      - _reposition_ Whether to recalculate positions and add a motion animation. \n      This might be used when changing _width_ or _height_ properties in a <Layouts.Tree> like layout. Default's *false*.\n      \n      - _onComplete_ A method that is called when the animation completes.\n      \n      ...and all other <Graph.Plot.animate> options like _duration_, _fps_, _transition_, etc.\n  \n      Example:\n      (start code js)\n       var rg = new RGraph(canvas, config); //can be also Hypertree or ST\n       rg.fx.nodeFx({\n         'elements': {\n           'id':'mynodeid',\n           'properties': {\n             'color':'#ccf'\n           },\n           'transition': Trans.Quart.easeOut\n         }\n       });\n      (end code)    \n   */\nnodeFx:function nodeFx(opt){\nvar viz=this.viz,\ngraph=viz.graph,\nanimation=this.nodeFxAnimation,\noptions=$.merge(this.viz.config,{\n'elements':{\n'id':false,\n'properties':{}},\n\n'reposition':false});\n\nopt=$.merge(options,opt||{},{\nonBeforeCompute:$.empty,\nonAfterCompute:$.empty});\n\n//check if an animation is running\nanimation.stopTimer();\nvar props=opt.elements.properties;\n//set end values for nodes\nif(!opt.elements.id){\ngraph.eachNode(function(n){\nfor(var prop in props){\nn.setData(prop,props[prop],'end');\n}\n});\n}else{\nvar ids=$.splat(opt.elements.id);\n$.each(ids,function(id){\nvar n=graph.getNode(id);\nif(n){\nfor(var prop in props){\nn.setData(prop,props[prop],'end');\n}\n}\n});\n}\n//get keys\nvar propnames=[];\nfor(var prop in props){propnames.push(prop);}\n//add node properties modes\nvar modes=['node-property:'+propnames.join(':')];\n//set new node positions\nif(opt.reposition){\nmodes.push('linear');\nviz.compute('end');\n}\n//animate\nthis.animate($.merge(opt,{\nmodes:modes,\ntype:'nodefx'}));\n\n},\n\n\n/*\n       Method: plot\n    \n       Plots a <Graph>.\n\n       Parameters:\n\n       opt - (optional) Plotting options. Most of them are described in <Options.Fx>.\n\n       Example:\n\n       (start code js)\n       var viz = new $jit.Viz(options);\n       viz.fx.plot(); \n       (end code)\n\n    */\nplot:function plot(opt,animating){\nvar viz=this.viz,\naGraph=viz.graph,\ncanvas=viz.canvas,\nid=viz.root,\nthat=this,\nctx=canvas.getCtx(),\nmin=Math.min,\nopt=opt||this.viz.controller;\n\nopt.clearCanvas&&canvas.clear();\n\nvar root=aGraph.getNode(id);\nif(!root)return;\n\nvar T=!!root.visited;\n\n//START METAMAPS CODE\nif(Metamaps.Mouse.synapseStartCoordinates.length>0&&Metamaps.Mouse.synapseEndCoordinates){\nctx.save();\nvar start;\nvar end=Metamaps.Mouse.synapseEndCoordinates;\n\nvar l=Metamaps.Mouse.synapseStartCoordinates.length;\nfor(var i=l-1;i>=0;i-=1){\nstart=Metamaps.Mouse.synapseStartCoordinates[i];\nMetamaps.JIT.renderMidArrow(start,end,13,false,canvas,0.3,true);\nMetamaps.JIT.renderMidArrow(start,end,13,false,canvas,0.7,true);\n}\nctx.restore();\n}\n\nif(Metamaps.Mouse.focusNodeCoords){\nctx.save();\nMetamaps.JIT.renderMidArrow(Metamaps.Mouse.focusNodeCoords,Metamaps.Mouse.newNodeCoords,13,false,canvas,0.3,true);\nMetamaps.JIT.renderMidArrow(Metamaps.Mouse.focusNodeCoords,Metamaps.Mouse.newNodeCoords,13,false,canvas,0.7,true);\nctx.restore();\n}\n\nif(Metamaps.Mouse.boxStartCoordinates&&Metamaps.Mouse.boxEndCoordinates){\nctx.save();\nctx.beginPath();\nctx.moveTo(Metamaps.Mouse.boxStartCoordinates.x,Metamaps.Mouse.boxStartCoordinates.y);\nctx.lineTo(Metamaps.Mouse.boxStartCoordinates.x,Metamaps.Mouse.boxEndCoordinates.y);\nctx.lineTo(Metamaps.Mouse.boxEndCoordinates.x,Metamaps.Mouse.boxEndCoordinates.y);\nctx.lineTo(Metamaps.Mouse.boxEndCoordinates.x,Metamaps.Mouse.boxStartCoordinates.y);\nctx.lineTo(Metamaps.Mouse.boxStartCoordinates.x,Metamaps.Mouse.boxStartCoordinates.y);\nctx.strokeStyle='black';\nctx.stroke();\nctx.restore();\n}\n//END METAMAPS CODE  \n\naGraph.eachNode(function(node){\nvar nodeAlpha=node.getData('alpha');\nnode.eachAdjacency(function(adj){\nvar nodeTo=adj.nodeTo;\nif(!!nodeTo.visited===T&&node.drawn&&nodeTo.drawn){\n!animating&&opt.onBeforePlotLine(adj);\nthat.plotLine(adj,canvas,animating);\n!animating&&opt.onAfterPlotLine(adj);\n}\n});\nif(node.drawn){\n!animating&&opt.onBeforePlotNode(node);\nthat.plotNode(node,canvas,animating);\n!animating&&opt.onAfterPlotNode(node);\n}\nif(!that.labelsHidden&&opt.withLabels){\nif(node.drawn&&nodeAlpha>=0.95){\nthat.labels.plotLabel(canvas,node,opt);\n}else{\nthat.labels.hideLabel(node,false);\n}\n}\nnode.visited=!T;\n});\n},\n\n/*\n      Plots a Subtree.\n   */\nplotTree:function plotTree(node,opt,animating){\nvar that=this,\nviz=this.viz,\ncanvas=viz.canvas,\nconfig=this.config,\nctx=canvas.getCtx();\nvar nodeAlpha=node.getData('alpha');\nnode.eachSubnode(function(elem){\nif(opt.plotSubtree(node,elem)&&elem.exist&&elem.drawn){\nvar adj=node.getAdjacency(elem.id);\n!animating&&opt.onBeforePlotLine(adj);\nthat.plotLine(adj,canvas,animating);\n!animating&&opt.onAfterPlotLine(adj);\nthat.plotTree(elem,opt,animating);\n}\n});\nif(node.drawn){\n!animating&&opt.onBeforePlotNode(node);\nthis.plotNode(node,canvas,animating);\n!animating&&opt.onAfterPlotNode(node);\nif(!opt.hideLabels&&opt.withLabels&&nodeAlpha>=0.95)\nthis.labels.plotLabel(canvas,node,opt);else\n\nthis.labels.hideLabel(node,false);\n}else{\nthis.labels.hideLabel(node,true);\n}\n},\n\n/*\n       Method: plotNode\n    \n       Plots a <Graph.Node>.\n\n       Parameters:\n       \n       node - (object) A <Graph.Node>.\n       canvas - (object) A <Canvas> element.\n\n    */\nplotNode:function plotNode(node,canvas,animating){\nvar f=node.getData('type'),\nctxObj=this.node.CanvasStyles;\nif(f!='none'){\nvar width=node.getData('lineWidth'),\ncolor=node.getData('color'),\nalpha=node.getData('alpha'),\nctx=canvas.getCtx();\nctx.save();\nctx.lineWidth=width;\nctx.fillStyle=ctx.strokeStyle=color;\nctx.globalAlpha=alpha;\n\nfor(var s in ctxObj){\nctx[s]=node.getCanvasStyle(s);\n}\n\nthis.nodeTypes[f].render.call(this,node,canvas,animating);\nctx.restore();\n}\n},\n\n/*\n       Method: plotLine\n    \n       Plots a <Graph.Adjacence>.\n\n       Parameters:\n\n       adj - (object) A <Graph.Adjacence>.\n       canvas - (object) A <Canvas> instance.\n\n    */\nplotLine:function plotLine(adj,canvas,animating){\nvar f=adj.getData('type'),\nctxObj=this.edge.CanvasStyles;\nif(f!='none'){\nvar width=adj.getData('lineWidth'),\ncolor=adj.getData('color'),\nctx=canvas.getCtx(),\nnodeFrom=adj.nodeFrom,\nnodeTo=adj.nodeTo;\n\nctx.save();\nctx.lineWidth=width;\nctx.fillStyle=ctx.strokeStyle=color;\nctx.globalAlpha=Math.min(nodeFrom.getData('alpha'),\nnodeTo.getData('alpha'),\nadj.getData('alpha'));\n\nfor(var s in ctxObj){\nctx[s]=adj.getCanvasStyle(s);\n}\n\nthis.edgeTypes[f].render.call(this,adj,canvas,animating);\nctx.restore();\n}\n}};\n\n\n\n/*\n  Object: Graph.Plot3D\n  \n  <Graph> 3D rendering and animation methods.\n  \n  Properties:\n  \n  nodeHelper - <NodeHelper> object.\n  edgeHelper - <EdgeHelper> object.\n\n*/\nGraph.Plot3D=$.merge(Graph.Plot,{\nInterpolator:{\n'linear':function linear(elem,props,delta){\nvar from=elem.startPos.getc(true);\nvar to=elem.endPos.getc(true);\nelem.pos.setc(this.compute(from.x,to.x,delta),\nthis.compute(from.y,to.y,delta),\nthis.compute(from.z,to.z,delta));\n}},\n\n\nplotNode:function plotNode(node,canvas){\nif(node.getData('type')=='none')return;\nthis.plotElement(node,canvas,{\ngetAlpha:function getAlpha(){\nreturn node.getData('alpha');\n}});\n\n},\n\nplotLine:function plotLine(adj,canvas){\nif(adj.getData('type')=='none')return;\nthis.plotElement(adj,canvas,{\ngetAlpha:function getAlpha(){\nreturn Math.min(adj.nodeFrom.getData('alpha'),\nadj.nodeTo.getData('alpha'),\nadj.getData('alpha'));\n}});\n\n},\n\nplotElement:function plotElement(elem,canvas,opt){\nvar gl=canvas.getCtx(),\nviewMatrix=new Matrix4(),\nlighting=canvas.config.Scene.Lighting,\nwcanvas=canvas.canvases[0],\nprogram=wcanvas.program,\ncamera=wcanvas.camera;\n\nif(!elem.geometry){\nelem.geometry=new O3D[elem.getData('type')]();\n}\nelem.geometry.update(elem);\nif(!elem.webGLVertexBuffer){\nvar vertices=[],\nfaces=[],\nnormals=[],\nvertexIndex=0,\ngeom=elem.geometry;\n\nfor(var i=0,vs=geom.vertices,fs=geom.faces,fsl=fs.length;i<fsl;i++){\nvar face=fs[i],\nv1=vs[face.a],\nv2=vs[face.b],\nv3=vs[face.c],\nv4=face.d?vs[face.d]:false,\nn=face.normal;\n\nvertices.push(v1.x,v1.y,v1.z);\nvertices.push(v2.x,v2.y,v2.z);\nvertices.push(v3.x,v3.y,v3.z);\nif(v4)vertices.push(v4.x,v4.y,v4.z);\n\nnormals.push(n.x,n.y,n.z);\nnormals.push(n.x,n.y,n.z);\nnormals.push(n.x,n.y,n.z);\nif(v4)normals.push(n.x,n.y,n.z);\n\nfaces.push(vertexIndex,vertexIndex+1,vertexIndex+2);\nif(v4){\nfaces.push(vertexIndex,vertexIndex+2,vertexIndex+3);\nvertexIndex+=4;\n}else{\nvertexIndex+=3;\n}\n}\n//create and store vertex data\nelem.webGLVertexBuffer=gl.createBuffer();\ngl.bindBuffer(gl.ARRAY_BUFFER,elem.webGLVertexBuffer);\ngl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);\n//create and store faces index data\nelem.webGLFaceBuffer=gl.createBuffer();\ngl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elem.webGLFaceBuffer);\ngl.bufferData(gl.ELEMENT_ARRAY_BUFFER,new Uint16Array(faces),gl.STATIC_DRAW);\nelem.webGLFaceCount=faces.length;\n//calculate vertex normals and store them\nelem.webGLNormalBuffer=gl.createBuffer();\ngl.bindBuffer(gl.ARRAY_BUFFER,elem.webGLNormalBuffer);\ngl.bufferData(gl.ARRAY_BUFFER,new Float32Array(normals),gl.STATIC_DRAW);\n}\nviewMatrix.multiply(camera.matrix,elem.geometry.matrix);\n//send matrix data\ngl.uniformMatrix4fv(program.viewMatrix,false,viewMatrix.flatten());\ngl.uniformMatrix4fv(program.projectionMatrix,false,camera.projectionMatrix.flatten());\n//send normal matrix for lighting\nvar normalMatrix=Matrix4.makeInvert(viewMatrix);\nnormalMatrix.$transpose();\ngl.uniformMatrix4fv(program.normalMatrix,false,normalMatrix.flatten());\n//send color data\nvar color=$.hexToRgb(elem.getData('color'));\ncolor.push(opt.getAlpha());\ngl.uniform4f(program.color,color[0]/255,color[1]/255,color[2]/255,color[3]);\n//send lighting data\ngl.uniform1i(program.enableLighting,lighting.enable);\nif(lighting.enable){\n//set ambient light color\nif(lighting.ambient){\nvar acolor=lighting.ambient;\ngl.uniform3f(program.ambientColor,acolor[0],acolor[1],acolor[2]);\n}\n//set directional light\nif(lighting.directional){\nvar dir=lighting.directional,\ncolor=dir.color,\npos=dir.direction,\nvd=new Vector3(pos.x,pos.y,pos.z).normalize().$scale(-1);\ngl.uniform3f(program.lightingDirection,vd.x,vd.y,vd.z);\ngl.uniform3f(program.directionalColor,color[0],color[1],color[2]);\n}\n}\n//send vertices data\ngl.bindBuffer(gl.ARRAY_BUFFER,elem.webGLVertexBuffer);\ngl.vertexAttribPointer(program.position,3,gl.FLOAT,false,0,0);\n//send normals data\ngl.bindBuffer(gl.ARRAY_BUFFER,elem.webGLNormalBuffer);\ngl.vertexAttribPointer(program.normal,3,gl.FLOAT,false,0,0);\n//draw!\ngl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,elem.webGLFaceBuffer);\ngl.drawElements(gl.TRIANGLES,elem.webGLFaceCount,gl.UNSIGNED_SHORT,0);\n}});\n\n\n\n/*\n * File: Graph.Label.js\n *\n*/\n\n/*\n   Object: Graph.Label\n\n   An interface for plotting/hiding/showing labels.\n\n   Description:\n\n   This is a generic interface for plotting/hiding/showing labels.\n   The <Graph.Label> interface is implemented in multiple ways to provide\n   different label types.\n\n   For example, the Graph.Label interface is implemented as <Graph.Label.HTML> to provide\n   HTML label elements. Also we provide the <Graph.Label.SVG> interface for SVG type labels. \n   The <Graph.Label.Native> interface implements these methods with the native Canvas text rendering functions.\n   \n   All subclasses (<Graph.Label.HTML>, <Graph.Label.SVG> and <Graph.Label.Native>) implement the method plotLabel.\n*/\n\nGraph.Label={};\n\n/*\n   Class: Graph.Label.Native\n\n   Implements labels natively, using the Canvas text API.\n*/\nGraph.Label.Native=new Class({\ninitialize:function initialize(viz){\nthis.viz=viz;\n},\n\n/*\n       Method: plotLabel\n\n       Plots a label for a given node.\n\n       Parameters:\n\n       canvas - (object) A <Canvas> instance.\n       node - (object) A <Graph.Node>.\n       controller - (object) A configuration object.\n       \n       Example:\n       \n       (start code js)\n       var viz = new $jit.Viz(options);\n       var node = viz.graph.getNode('nodeId');\n       viz.labels.plotLabel(viz.canvas, node, viz.config);\n       (end code)\n    */\nplotLabel:function plotLabel(canvas,node,controller){\n\nvar ctx=canvas.getCtx();\nvar pos=node.pos.getc(true);\n\nctx.font=node.getLabelData('style')+' '+node.getLabelData('size')+'px '+node.getLabelData('family');\nctx.textAlign=node.getLabelData('textAlign');\n// ORIGINAL CODE ctx.fillStyle = ctx.strokeStyle = node.getLabelData('color');\nctx.textBaseline=node.getLabelData('textBaseline');\n\n//START METAMAPS CODE\n\nvar arrayOfLabelLines=Metamaps.Util.splitLine(node.name,25).split('\\n');\n//render background\nctx.fillStyle=ctx.strokeStyle=Metamaps.Settings.colors.labels.background;\nctx.lineWidth=2;\nvar height=25*arrayOfLabelLines.length;//font size + margin\n\nvar index,lineWidths=[];\nfor(index=0;index<arrayOfLabelLines.length;++index){\nlineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width);\n}\nvar width=Math.max.apply(null,lineWidths)+8;\nvar x=pos.x-width/2;\nvar y=pos.y+node.getData(\"height\")+5;\nvar radius=5;\n\nctx.beginPath();\nctx.moveTo(x+radius,y);\nctx.lineTo(x+width-radius,y);\nctx.quadraticCurveTo(x+width,y,x+width,y+radius);\nctx.lineTo(x+width,y+height-radius);\nctx.quadraticCurveTo(x+width,y+height,x+width-radius,y+height);\nctx.lineTo(x+radius,y+height);\nctx.quadraticCurveTo(x,y+height,x,y+height-radius);\nctx.lineTo(x,y+radius);\nctx.quadraticCurveTo(x,y,x+radius,y);\nctx.closePath();\nctx.fill();\n//ctx.stroke();\n\nctx.fillStyle=ctx.strokeStyle=node.getLabelData('color');\n\nthis.renderLabel(arrayOfLabelLines,canvas,node,controller);\n// END METAMAPS CODE\n// ORIGINAL CODE  this.renderLabel(canvas, node, controller);\n},\n\n/*\n       renderLabel\n\n       Does the actual rendering of the label in the canvas. The default\n       implementation renders the label close to the position of the node, this\n       method should be overriden to position the labels differently.\n\n       Parameters:\n\n       canvas - A <Canvas> instance.\n       node - A <Graph.Node>.\n       controller - A configuration object. See also <Hypertree>, <RGraph>, <ST>.\n    */\nrenderLabel:function renderLabel(customLabel,canvas,node,controller){\nvar ctx=canvas.getCtx();\nvar pos=node.pos.getc(true);\n//ctx.fillText(node.name, pos.x, pos.y + node.getData(\"height\") / 2);\n// START METAMAPS CODE\nvar index;\nfor(index=0;index<customLabel.length;++index){\nctx.fillText(customLabel[index],pos.x,pos.y+node.getData(\"height\")+23+25*index);\n}\n// END METAMAPS CODE\n},\n\nhideLabel:$.empty,\nhideLabels:$.empty});\n\n\n/*\n   Class: Graph.Label.DOM\n\n   Abstract Class implementing some DOM label methods.\n\n   Implemented by:\n\n   <Graph.Label.HTML> and <Graph.Label.SVG>.\n\n*/\nGraph.Label.DOM=new Class({\n//A flag value indicating if node labels are being displayed or not.\nlabelsHidden:false,\n//Label container\nlabelContainer:false,\n//Label elements hash.\nlabels:{},\n\n/*\n       Method: getLabelContainer\n\n       Lazy fetcher for the label container.\n\n       Returns:\n\n       The label container DOM element.\n\n       Example:\n\n      (start code js)\n        var viz = new $jit.Viz(options);\n        var labelContainer = viz.labels.getLabelContainer();\n        alert(labelContainer.innerHTML);\n      (end code)\n    */\ngetLabelContainer:function getLabelContainer(){\nreturn this.labelContainer?\nthis.labelContainer:\nthis.labelContainer=document.getElementById(this.viz.config.labelContainer);\n},\n\n/*\n       Method: getLabel\n\n       Lazy fetcher for the label element.\n\n       Parameters:\n\n       id - (string) The label id (which is also a <Graph.Node> id).\n\n       Returns:\n\n       The label element.\n\n       Example:\n\n      (start code js)\n        var viz = new $jit.Viz(options);\n        var label = viz.labels.getLabel('someid');\n        alert(label.innerHTML);\n      (end code)\n\n    */\ngetLabel:function getLabel(id){\nreturn id in this.labels&&this.labels[id]!=null?\nthis.labels[id]:\nthis.labels[id]=document.getElementById(id);\n},\n\n/*\n       Method: hideLabels\n\n       Hides all labels (by hiding the label container).\n\n       Parameters:\n\n       hide - (boolean) A boolean value indicating if the label container must be hidden or not.\n\n       Example:\n       (start code js)\n        var viz = new $jit.Viz(options);\n        rg.labels.hideLabels(true);\n       (end code)\n\n    */\nhideLabels:function hideLabels(hide){\nvar container=this.getLabelContainer();\nif(hide)\ncontainer.style.display='none';else\n\ncontainer.style.display='';\nthis.labelsHidden=hide;\n},\n\n/*\n       Method: clearLabels\n\n       Clears the label container.\n\n       Useful when using a new visualization with the same canvas element/widget.\n\n       Parameters:\n\n       force - (boolean) Forces deletion of all labels.\n\n       Example:\n       (start code js)\n        var viz = new $jit.Viz(options);\n        viz.labels.clearLabels();\n        (end code)\n    */\nclearLabels:function clearLabels(force){\nfor(var id in this.labels){\nif(force||!this.viz.graph.hasNode(id)){\nthis.disposeLabel(id);\ndelete this.labels[id];\n}\n}\n},\n\n/*\n       Method: disposeLabel\n\n       Removes a label.\n\n       Parameters:\n\n       id - (string) A label id (which generally is also a <Graph.Node> id).\n\n       Example:\n       (start code js)\n        var viz = new $jit.Viz(options);\n        viz.labels.disposeLabel('labelid');\n       (end code)\n    */\ndisposeLabel:function disposeLabel(id){\nvar elem=this.getLabel(id);\nif(elem&&elem.parentNode){\nelem.parentNode.removeChild(elem);\n}\n},\n\n/*\n       Method: hideLabel\n\n       Hides the corresponding <Graph.Node> label.\n\n       Parameters:\n\n       node - (object) A <Graph.Node>. Can also be an array of <Graph.Nodes>.\n       show - (boolean) If *true*, nodes will be shown. Otherwise nodes will be hidden.\n\n       Example:\n       (start code js)\n        var rg = new $jit.Viz(options);\n        viz.labels.hideLabel(viz.graph.getNode('someid'), false);\n       (end code)\n    */\nhideLabel:function hideLabel(node,show){\nnode=$.splat(node);\nvar st=show?\"\":\"none\",lab,that=this;\n$.each(node,function(n){\nvar lab=that.getLabel(n.id);\nif(lab){\nlab.style.display=st;\n}\n});\n},\n\n/*\n       fitsInCanvas\n\n       Returns _true_ or _false_ if the label for the node is contained in the canvas dom element or not.\n\n       Parameters:\n\n       pos - A <Complex> instance (I'm doing duck typing here so any object with _x_ and _y_ parameters will do).\n       canvas - A <Canvas> instance.\n\n       Returns:\n\n       A boolean value specifying if the label is contained in the <Canvas> DOM element or not.\n\n    */\nfitsInCanvas:function fitsInCanvas(pos,canvas){\nvar size=canvas.getSize();\nif(pos.x>=size.width||pos.x<0||\npos.y>=size.height||pos.y<0)return false;\nreturn true;\n}});\n\n\n/*\n   Class: Graph.Label.HTML\n\n   Implements HTML labels.\n\n   Extends:\n\n   All <Graph.Label.DOM> methods.\n\n*/\nGraph.Label.HTML=new Class({\nImplements:Graph.Label.DOM,\n\n/*\n       Method: plotLabel\n\n       Plots a label for a given node.\n\n       Parameters:\n\n       canvas - (object) A <Canvas> instance.\n       node - (object) A <Graph.Node>.\n       controller - (object) A configuration object.\n       \n      Example:\n       \n       (start code js)\n       var viz = new $jit.Viz(options);\n       var node = viz.graph.getNode('nodeId');\n       viz.labels.plotLabel(viz.canvas, node, viz.config);\n       (end code)\n\n\n    */\nplotLabel:function plotLabel(canvas,node,controller){\nvar id=node.id,tag=this.getLabel(id);\n\nif(!tag&&!(tag=document.getElementById(id))){\ntag=document.createElement('div');\nvar container=this.getLabelContainer();\ntag.id=id;\ntag.className='node';\ntag.style.position='absolute';\ncontroller.onCreateLabel(tag,node);\ncontainer.appendChild(tag);\nthis.labels[node.id]=tag;\n}\n\nthis.placeLabel(tag,node,controller);\n}});\n\n\n/*\n   Class: Graph.Label.SVG\n\n   Implements SVG labels.\n\n   Extends:\n\n   All <Graph.Label.DOM> methods.\n*/\nGraph.Label.SVG=new Class({\nImplements:Graph.Label.DOM,\n\n/*\n       Method: plotLabel\n\n       Plots a label for a given node.\n\n       Parameters:\n\n       canvas - (object) A <Canvas> instance.\n       node - (object) A <Graph.Node>.\n       controller - (object) A configuration object.\n       \n       Example:\n       \n       (start code js)\n       var viz = new $jit.Viz(options);\n       var node = viz.graph.getNode('nodeId');\n       viz.labels.plotLabel(viz.canvas, node, viz.config);\n       (end code)\n\n\n    */\nplotLabel:function plotLabel(canvas,node,controller){\nvar id=node.id,tag=this.getLabel(id);\nif(!tag&&!(tag=document.getElementById(id))){\nvar ns='http://www.w3.org/2000/svg';\ntag=document.createElementNS(ns,'svg:text');\nvar tspan=document.createElementNS(ns,'svg:tspan');\ntag.appendChild(tspan);\nvar container=this.getLabelContainer();\ntag.setAttribute('id',id);\ntag.setAttribute('class','node');\ncontainer.appendChild(tag);\ncontroller.onCreateLabel(tag,node);\nthis.labels[node.id]=tag;\n}\nthis.placeLabel(tag,node,controller);\n}});\n\n\n\n\n/*\n * File: Loader.js\n * \n */\n\n/*\n   Object: Loader\n\n   Provides methods for loading and serving JSON data.\n*/\nvar Loader={\nconstruct:function construct(json){\nvar isGraph=$.type(json)=='array';\nvar ans=new Graph(this.graphOptions,this.config.Node,this.config.Edge,this.config.Label);\nif(!isGraph)\n//make tree\n(function(ans,json){\nans.addNode(json);\nif(json.children){\nfor(var i=0,ch=json.children;i<ch.length;i++){\nans.addAdjacence(json,ch[i]);\narguments.callee(ans,ch[i]);\n}\n}\n})(ans,json);else\n\n//make graph\n(function(ans,json){\nvar getNode=function getNode(id){\nfor(var i=0,l=json.length;i<l;i++){\nif(json[i].id==id){\nreturn json[i];\n}\n}\n// The node was not defined in the JSON\n// Let's create it\nvar newNode={\n\"id\":id,\n\"name\":id};\n\nreturn ans.addNode(newNode);\n};\n\nfor(var i=0,l=json.length;i<l;i++){\nans.addNode(json[i]);\nvar adj=json[i].adjacencies;\nif(adj){\nfor(var j=0,lj=adj.length;j<lj;j++){\nvar node=adj[j],data={};\nif(typeof adj[j]!='string'){\ndata=$.merge(node.data,{});\nnode=node.nodeTo;\n}\nans.addAdjacence(json[i],getNode(node),data);\n}\n}\n}\n})(ans,json);\n\nreturn ans;\n},\n\n/*\n     Method: loadJSON\n    \n     Loads a JSON structure to the visualization. The JSON structure can be a JSON *tree* or *graph* structure.\n     \n      A JSON tree or graph structure consists of nodes, each having as properties\n       \n       id - (string) A unique identifier for the node\n       name - (string) A node's name\n       data - (object) The data optional property contains a hash (i.e {}) \n       where you can store all the information you want about this node.\n        \n      For JSON *Tree* structures, there's an extra optional property *children* of type Array which contains the node's children.\n      \n      Example:\n\n      (start code js)\n        var json = {  \n          \"id\": \"aUniqueIdentifier\",  \n          \"name\": \"usually a nodes name\",  \n          \"data\": {\n            \"some key\": \"some value\",\n            \"some other key\": \"some other value\"\n           },  \n          \"children\": [ *other nodes or empty* ]  \n        };  \n      (end code)\n        \n        JSON *Graph* structures consist of an array of nodes, each specifying the nodes to which the current node is connected. \n        For JSON *Graph* structures, the *children* property is replaced by the *adjacencies* property.\n        \n        There are two types of *Graph* structures, *simple* and *extended* graph structures.\n        \n        For *simple* Graph structures, the adjacencies property contains an array of strings, each specifying the \n        id of the node connected to the main node.\n        \n        Example:\n        \n        (start code js)\n        var json = [  \n          {  \n            \"id\": \"aUniqueIdentifier\",  \n            \"name\": \"usually a nodes name\",  \n            \"data\": {\n              \"some key\": \"some value\",\n              \"some other key\": \"some other value\"\n             },  \n            \"adjacencies\": [\"anotherUniqueIdentifier\", \"yetAnotherUniqueIdentifier\", 'etc']  \n          },\n\n          'other nodes go here...' \n        ];          \n        (end code)\n        \n        For *extended Graph structures*, the adjacencies property contains an array of Adjacency objects that have as properties\n        \n        nodeTo - (string) The other node connected by this adjacency.\n        data - (object) A data property, where we can store custom key/value information.\n        \n        Example:\n        \n        (start code js)\n        var json = [  \n          {  \n            \"id\": \"aUniqueIdentifier\",  \n            \"name\": \"usually a nodes name\",  \n            \"data\": {\n              \"some key\": \"some value\",\n              \"some other key\": \"some other value\"\n             },  \n            \"adjacencies\": [  \n            {  \n              nodeTo:\"aNodeId\",  \n              data: {} //put whatever you want here  \n            },\n            'other adjacencies go here...'  \n          },\n\n          'other nodes go here...' \n        ];          \n        (end code)\n       \n       About the data property:\n       \n       As described before, you can store custom data in the *data* property of JSON *nodes* and *adjacencies*. \n       You can use almost any string as key for the data object. Some keys though are reserved by the toolkit, and \n       have special meanings. This is the case for keys starting with a dollar sign, for example, *$width*.\n       \n       For JSON *node* objects, adding dollar prefixed properties that match the names of the options defined in \n       <Options.Node> will override the general value for that option with that particular value. For this to work \n       however, you do have to set *overridable = true* in <Options.Node>.\n       \n       The same thing is true for JSON adjacencies. Dollar prefixed data properties will alter values set in <Options.Edge> \n       if <Options.Edge> has *overridable = true*.\n       \n       When loading JSON data into TreeMaps, the *data* property must contain a value for the *$area* key, \n       since this is the value which will be taken into account when creating the layout. \n       The same thing goes for the *$color* parameter.\n       \n       In JSON Nodes you can use also *$label-* prefixed properties to refer to <Options.Label> properties. For example, \n       *$label-size* will refer to <Options.Label> size property. Also, in JSON nodes and adjacencies you can set \n       canvas specific properties individually by using the *$canvas-* prefix. For example, *$canvas-shadowBlur* will refer \n       to the *shadowBlur* property.\n       \n       These properties can also be accessed after loading the JSON data from <Graph.Nodes> and <Graph.Adjacences> \n       by using <Accessors>. For more information take a look at the <Graph> and <Accessors> documentation.\n       \n       Finally, these properties can also be used to create advanced animations like with <Options.NodeStyles>. For more \n       information about creating animations please take a look at the <Graph.Plot> and <Graph.Plot.animate> documentation.\n       \n       loadJSON Parameters:\n    \n        json - A JSON Tree or Graph structure.\n        i - For Graph structures only. Sets the indexed node as root for the visualization.\n\n    */\nloadJSON:function loadJSON(json,i){\nthis.json=json;\n//if they're canvas labels erase them.\nif(this.labels&&this.labels.clearLabels){\nthis.labels.clearLabels(true);\n}\nthis.graph=this.construct(json);\nif($.type(json)!='array'){\nthis.root=json.id;\n}else{\nthis.root=json[i?i:0].id;\n}\n},\n\n/*\n      Method: toJSON\n   \n      Returns a JSON tree/graph structure from the visualization's <Graph>. \n      See <Loader.loadJSON> for the graph formats available.\n      \n      See also:\n      \n      <Loader.loadJSON>\n      \n      Parameters:\n      \n      type - (string) Default's \"tree\". The type of the JSON structure to be returned. \n      Possible options are \"tree\" or \"graph\".\n    */\ntoJSON:function toJSON(type){\ntype=type||\"tree\";\nif(type=='tree'){\nvar ans={};\nvar rootNode=this.graph.getNode(this.root);\nvar ans=function recTree(node){\nvar ans={};\nans.id=node.id;\nans.name=node.name;\nans.data=node.data;\nvar ch=[];\nnode.eachSubnode(function(n){\nch.push(recTree(n));\n});\nans.children=ch;\nreturn ans;\n}(rootNode);\nreturn ans;\n}else{\nvar ans=[];\nvar T=!!this.graph.getNode(this.root).visited;\nthis.graph.eachNode(function(node){\nvar ansNode={};\nansNode.id=node.id;\nansNode.name=node.name;\nansNode.data=node.data;\nvar adjs=[];\nnode.eachAdjacency(function(adj){\nvar nodeTo=adj.nodeTo;\nif(!!nodeTo.visited===T){\nvar ansAdj={};\nansAdj.nodeTo=nodeTo.id;\nansAdj.data=adj.data;\nadjs.push(ansAdj);\n}\n});\nansNode.adjacencies=adjs;\nans.push(ansNode);\nnode.visited=!T;\n});\nreturn ans;\n}\n}};\n\n\n\n\n/*\n * File: Layouts.js\n * \n * Implements base Tree and Graph layouts.\n *\n * Description:\n *\n * Implements base Tree and Graph layouts like Radial, Tree, etc.\n * \n */\n\n/*\n * Object: Layouts\n * \n * Parent object for common layouts.\n *\n */\nvar Layouts=$jit.Layouts={};\n\n\n//Some util shared layout functions are defined here.\nvar NodeDim={\nlabel:null,\n\ncompute:function compute(graph,prop,opt){\nthis.initializeLabel(opt);\nvar label=this.label,style=label.style;\ngraph.eachNode(function(n){\nvar autoWidth=n.getData('autoWidth'),\nautoHeight=n.getData('autoHeight');\nif(autoWidth||autoHeight){\n//delete dimensions since these are\n//going to be overridden now.\ndelete n.data.$width;\ndelete n.data.$height;\ndelete n.data.$dim;\n\nvar width=n.getData('width'),\nheight=n.getData('height');\n//reset label dimensions\nstyle.width=autoWidth?'auto':width+'px';\nstyle.height=autoHeight?'auto':height+'px';\n\n//TODO(nico) should let the user choose what to insert here.\nlabel.innerHTML=n.name;\n\nvar offsetWidth=label.offsetWidth,\noffsetHeight=label.offsetHeight;\nvar type=n.getData('type');\nif($.indexOf(['circle','square','triangle','star'],type)===-1){\nn.setData('width',offsetWidth);\nn.setData('height',offsetHeight);\n}else{\nvar dim=offsetWidth>offsetHeight?offsetWidth:offsetHeight;\nn.setData('width',dim);\nn.setData('height',dim);\nn.setData('dim',dim);\n}\n}\n});\n},\n\ninitializeLabel:function initializeLabel(opt){\nif(!this.label){\nthis.label=document.createElement('div');\ndocument.body.appendChild(this.label);\n}\nthis.setLabelStyles(opt);\n},\n\nsetLabelStyles:function setLabelStyles(opt){\n$.extend(this.label.style,{\n'visibility':'hidden',\n'position':'absolute',\n'width':'auto',\n'height':'auto'});\n\nthis.label.className='jit-autoadjust-label';\n}};\n\n\n\n/*\n * Class: Layouts.Radial\n * \n * Implements a Radial Layout.\n * \n * Implemented By:\n * \n * <RGraph>, <Hypertree>\n * \n */\nLayouts.Radial=new Class({\n\n/*\n   * Method: compute\n   * \n   * Computes nodes' positions.\n   * \n   * Parameters:\n   * \n   * property - _optional_ A <Graph.Node> position property to store the new\n   * positions. Possible values are 'pos', 'end' or 'start'.\n   * \n   */\ncompute:function compute(property){\nvar prop=$.splat(property||['current','start','end']);\nNodeDim.compute(this.graph,prop,this.config);\nthis.graph.computeLevels(this.root,0,\"ignore\");\nvar lengthFunc=this.createLevelDistanceFunc();\nthis.computeAngularWidths(prop);\nthis.computePositions(prop,lengthFunc);\n},\n\n/*\n   * computePositions\n   * \n   * Performs the main algorithm for computing node positions.\n   */\ncomputePositions:function computePositions(property,getLength){\nvar propArray=property;\nvar graph=this.graph;\nvar root=graph.getNode(this.root);\nvar parent=this.parent;\nvar config=this.config;\n\nfor(var i=0,l=propArray.length;i<l;i++){\nvar pi=propArray[i];\nroot.setPos($P(0,0),pi);\nroot.setData('span',Math.PI*2,pi);\n}\n\nroot.angleSpan={\nbegin:0,\nend:2*Math.PI};\n\n\ngraph.eachBFS(this.root,function(elem){\nvar angleSpan=elem.angleSpan.end-elem.angleSpan.begin;\nvar angleInit=elem.angleSpan.begin;\nvar len=getLength(elem);\n//Calculate the sum of all angular widths\nvar totalAngularWidths=0,subnodes=[],maxDim={};\nelem.eachSubnode(function(sib){\ntotalAngularWidths+=sib._treeAngularWidth;\n//get max dim\nfor(var i=0,l=propArray.length;i<l;i++){\nvar pi=propArray[i],dim=sib.getData('dim',pi);\nmaxDim[pi]=pi in maxDim?dim>maxDim[pi]?dim:maxDim[pi]:dim;\n}\nsubnodes.push(sib);\n},\"ignore\");\n//Maintain children order\n//Second constraint for <http://bailando.sims.berkeley.edu/papers/infovis01.htm>\nif(parent&&parent.id==elem.id&&subnodes.length>0&&\nsubnodes[0].dist){\nsubnodes.sort(function(a,b){\nreturn(a.dist>=b.dist)-(a.dist<=b.dist);\n});\n}\n//Calculate nodes positions.\nfor(var k=0,ls=subnodes.length;k<ls;k++){\nvar child=subnodes[k];\nif(!child._flag){\nvar angleProportion=child._treeAngularWidth/totalAngularWidths*angleSpan;\nvar theta=angleInit+angleProportion/2;\n\nfor(var i=0,l=propArray.length;i<l;i++){\nvar pi=propArray[i];\nchild.setPos($P(theta,len),pi);\nchild.setData('span',angleProportion,pi);\nchild.setData('dim-quotient',child.getData('dim',pi)/maxDim[pi],pi);\n}\n\nchild.angleSpan={\nbegin:angleInit,\nend:angleInit+angleProportion};\n\nangleInit+=angleProportion;\n}\n}\n},\"ignore\");\n},\n\n/*\n   * Method: setAngularWidthForNodes\n   * \n   * Sets nodes angular widths.\n   */\nsetAngularWidthForNodes:function setAngularWidthForNodes(prop){\nthis.graph.eachBFS(this.root,function(elem,i){\nvar diamValue=elem.getData('angularWidth',prop[0])||5;\nelem._angularWidth=diamValue/i;\n},\"ignore\");\n},\n\n/*\n   * Method: setSubtreesAngularWidth\n   * \n   * Sets subtrees angular widths.\n   */\nsetSubtreesAngularWidth:function setSubtreesAngularWidth(){\nvar that=this;\nthis.graph.eachNode(function(elem){\nthat.setSubtreeAngularWidth(elem);\n},\"ignore\");\n},\n\n/*\n   * Method: setSubtreeAngularWidth\n   * \n   * Sets the angular width for a subtree.\n   */\nsetSubtreeAngularWidth:function setSubtreeAngularWidth(elem){\nvar that=this,nodeAW=elem._angularWidth,sumAW=0;\nelem.eachSubnode(function(child){\nthat.setSubtreeAngularWidth(child);\nsumAW+=child._treeAngularWidth;\n},\"ignore\");\nelem._treeAngularWidth=Math.max(nodeAW,sumAW);\n},\n\n/*\n   * Method: computeAngularWidths\n   * \n   * Computes nodes and subtrees angular widths.\n   */\ncomputeAngularWidths:function computeAngularWidths(prop){\nthis.setAngularWidthForNodes(prop);\nthis.setSubtreesAngularWidth();\n}});\n\n\n\n\n/*\n * File: RGraph.js\n *\n */\n\n/*\n   Class: RGraph\n   \n   A radial graph visualization with advanced animations.\n   \n   Inspired by:\n \n   Animated Exploration of Dynamic Graphs with Radial Layout (Ka-Ping Yee, Danyel Fisher, Rachna Dhamija, Marti Hearst) <http://bailando.sims.berkeley.edu/papers/infovis01.htm>\n   \n   Note:\n   \n   This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.\n   \n  Implements:\n  \n  All <Loader> methods\n  \n   Constructor Options:\n   \n   Inherits options from\n   \n   - <Options.Canvas>\n   - <Options.Controller>\n   - <Options.Node>\n   - <Options.Edge>\n   - <Options.Label>\n   - <Options.Events>\n   - <Options.Tips>\n   - <Options.NodeStyles>\n   - <Options.Navigation>\n   \n   Additionally, there are other parameters and some default values changed\n   \n   interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.\n   levelDistance - (number) Default's *100*. The distance between levels of the tree. \n     \n   Instance Properties:\n\n   canvas - Access a <Canvas> instance.\n   graph - Access a <Graph> instance.\n   op - Access a <RGraph.Op> instance.\n   fx - Access a <RGraph.Plot> instance.\n   labels - Access a <RGraph.Label> interface implementation.   \n*/\n\n$jit.RGraph=new Class({\n\nImplements:[\nLoader,Extras,Layouts.Radial],\n\n\ninitialize:function initialize(controller){\nvar $RGraph=$jit.RGraph;\n\nvar config={\ninterpolation:'linear',\nlevelDistance:100};\n\n\nthis.controller=this.config=$.merge(Options(\"Canvas\",\"Node\",\"Edge\",\n\"Fx\",\"Controller\",\"Tips\",\"NodeStyles\",\"Events\",\"Navigation\",\"Label\"),config,controller);\n\nvar canvasConfig=this.config;\nif(canvasConfig.useCanvas){\nthis.canvas=canvasConfig.useCanvas;\nthis.config.labelContainer=this.canvas.id+'-label';\n}else{\nif(canvasConfig.background){\ncanvasConfig.background=$.merge({\ntype:'Circles'},\ncanvasConfig.background);\n}\nthis.canvas=new Canvas(this,canvasConfig);\nthis.config.labelContainer=(typeof canvasConfig.injectInto=='string'?canvasConfig.injectInto:canvasConfig.injectInto.id)+'-label';\n}\n\nthis.graphOptions={\n'klass':Polar,\n'Node':{\n'selected':false,\n'exist':true,\n'drawn':true}};\n\n\nthis.graph=new Graph(this.graphOptions,this.config.Node,\nthis.config.Edge);\nthis.labels=new $RGraph.Label[canvasConfig.Label.type](this);\nthis.fx=new $RGraph.Plot(this,$RGraph);\nthis.op=new $RGraph.Op(this);\nthis.json=null;\nthis.root=null;\nthis.busy=false;\nthis.parent=false;\n// initialize extras\nthis.initializeExtras();\n},\n\n/* \n  \n    createLevelDistanceFunc \n  \n    Returns the levelDistance function used for calculating a node distance \n    to its origin. This function returns a function that is computed \n    per level and not per node, such that all nodes with the same depth will have the \n    same distance to the origin. The resulting function gets the \n    parent node as parameter and returns a float.\n\n   */\ncreateLevelDistanceFunc:function createLevelDistanceFunc(){\nvar ld=this.config.levelDistance;\nreturn function(elem){\nreturn(elem._depth+1)*ld;\n};\n},\n\n/* \n     Method: refresh \n     \n     Computes positions and plots the tree.\n\n   */\nrefresh:function refresh(){\n\n// START METAMAPS CODE\n// this.compute();\n// END METAMAPS CODE\n// ORIGINAL CODE: this.compute();\nthis.plot();\n},\n\nreposition:function reposition(){\nthis.compute('end');\n},\n\n/*\n   Method: plot\n  \n   Plots the RGraph. This is a shortcut to *fx.plot*.\n  */\nplot:function plot(){\nthis.fx.plot();\n},\n/*\n   getNodeAndParentAngle\n  \n   Returns the _parent_ of the given node, also calculating its angle span.\n  */\ngetNodeAndParentAngle:function getNodeAndParentAngle(id){\nvar theta=false;\nvar n=this.graph.getNode(id);\nvar ps=n.getParents();\nvar p=ps.length>0?ps[0]:false;\nif(p){\nvar posParent=p.pos.getc(),posChild=n.pos.getc();\nvar newPos=posParent.add(posChild.scale(-1));\ntheta=Math.atan2(newPos.y,newPos.x);\nif(theta<0)\ntheta+=2*Math.PI;\n}\nreturn{\nparent:p,\ntheta:theta};\n\n},\n/*\n   tagChildren\n  \n   Enumerates the children in order to maintain child ordering (second constraint of the paper).\n  */\ntagChildren:function tagChildren(par,id){\nif(par.angleSpan){\nvar adjs=[];\npar.eachAdjacency(function(elem){\nadjs.push(elem.nodeTo);\n},\"ignore\");\nvar len=adjs.length;\nfor(var i=0;i<len&&id!=adjs[i].id;i++){}\n\nfor(var j=(i+1)%len,k=0;id!=adjs[j].id;j=(j+1)%len){\nadjs[j].dist=k++;\n}\n}\n},\n/* \n  Method: onClick \n  \n  Animates the <RGraph> to center the node specified by *id*.\n\n   Parameters:\n\n   id - A <Graph.Node> id.\n   opt - (optional|object) An object containing some extra properties described below\n   hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.\n\n   Example:\n\n   (start code js)\n     rgraph.onClick('someid');\n     //or also...\n     rgraph.onClick('someid', {\n      hideLabels: false\n     });\n    (end code)\n    \n  */\nonClick:function onClick(id,opt){\nif(this.root!=id&&!this.busy){\nthis.busy=true;\nthis.root=id;\nvar that=this;\nthis.controller.onBeforeCompute(this.graph.getNode(id));\nvar obj=this.getNodeAndParentAngle(id);\n\n// second constraint\nthis.tagChildren(obj.parent,id);\nthis.parent=obj.parent;\nthis.compute('end');\n\n// first constraint\nvar thetaDiff=obj.theta-obj.parent.endPos.theta;\nthis.graph.eachNode(function(elem){\nelem.endPos.set(elem.endPos.getp().add($P(thetaDiff,0)));\n});\n\nvar mode=this.config.interpolation;\nopt=$.merge({\nonComplete:$.empty},\nopt||{});\n\nthis.fx.animate($.merge({\nhideLabels:true,\nmodes:[\nmode]},\n\nopt,{\nonComplete:function onComplete(){\nthat.busy=false;\nopt.onComplete();\n}}));\n\n}\n}});\n\n\n$jit.RGraph.$extend=true;\n\n(function(RGraph){\n\n/*\n     Class: RGraph.Op\n     \n     Custom extension of <Graph.Op>.\n\n     Extends:\n\n     All <Graph.Op> methods\n     \n     See also:\n     \n     <Graph.Op>\n\n  */\nRGraph.Op=new Class({\n\nImplements:Graph.Op});\n\n\n\n/*\n     Class: RGraph.Plot\n    \n    Custom extension of <Graph.Plot>.\n  \n    Extends:\n  \n    All <Graph.Plot> methods\n    \n    See also:\n    \n    <Graph.Plot>\n  \n  */\nRGraph.Plot=new Class({\n\nImplements:Graph.Plot});\n\n\n\n/*\n    Object: RGraph.Label\n\n    Custom extension of <Graph.Label>. \n    Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.\n  \n    Extends:\n  \n    All <Graph.Label> methods and subclasses.\n  \n    See also:\n  \n    <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.\n  \n   */\nRGraph.Label={};\n\n/*\n     RGraph.Label.Native\n\n     Custom extension of <Graph.Label.Native>.\n\n     Extends:\n\n     All <Graph.Label.Native> methods\n\n     See also:\n\n     <Graph.Label.Native>\n\n  */\nRGraph.Label.Native=new Class({\nImplements:Graph.Label.Native});\n\n\n/*\n     RGraph.Label.SVG\n    \n    Custom extension of <Graph.Label.SVG>.\n  \n    Extends:\n  \n    All <Graph.Label.SVG> methods\n  \n    See also:\n  \n    <Graph.Label.SVG>\n  \n  */\nRGraph.Label.SVG=new Class({\nImplements:Graph.Label.SVG,\n\ninitialize:function initialize(viz){\nthis.viz=viz;\n},\n\n/* \n       placeLabel\n\n       Overrides abstract method placeLabel in <Graph.Plot>.\n\n       Parameters:\n\n       tag - A DOM label element.\n       node - A <Graph.Node>.\n       controller - A configuration/controller object passed to the visualization.\n      \n     */\nplaceLabel:function placeLabel(tag,node,controller){\nvar pos=node.pos.getc(true),\ncanvas=this.viz.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY,\nradius=canvas.getSize();\nvar labelPos={\nx:Math.round(pos.x*sx+ox+radius.width/2),\ny:Math.round(pos.y*sy+oy+radius.height/2)};\n\ntag.setAttribute('x',labelPos.x);\ntag.setAttribute('y',labelPos.y);\n\ncontroller.onPlaceLabel(tag,node);\n}});\n\n\n/*\n     RGraph.Label.HTML\n\n     Custom extension of <Graph.Label.HTML>.\n\n     Extends:\n\n     All <Graph.Label.HTML> methods.\n\n     See also:\n\n     <Graph.Label.HTML>\n\n  */\nRGraph.Label.HTML=new Class({\nImplements:Graph.Label.HTML,\n\ninitialize:function initialize(viz){\nthis.viz=viz;\n},\n/* \n       placeLabel\n\n       Overrides abstract method placeLabel in <Graph.Plot>.\n\n       Parameters:\n\n       tag - A DOM label element.\n       node - A <Graph.Node>.\n       controller - A configuration/controller object passed to the visualization.\n      \n     */\nplaceLabel:function placeLabel(tag,node,controller){\nvar pos=node.pos.getc(true),\ncanvas=this.viz.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY,\nradius=canvas.getSize();\nvar labelPos={\nx:Math.round(pos.x*sx+ox+radius.width/2),\ny:Math.round(pos.y*sy+oy+radius.height/2)};\n\n\nvar style=tag.style;\nstyle.left=labelPos.x+'px';\nstyle.top=labelPos.y+'px';\nstyle.display=this.fitsInCanvas(labelPos,canvas)?'':'none';\n\ncontroller.onPlaceLabel(tag,node);\n}});\n\n\n/*\n    Class: RGraph.Plot.NodeTypes\n\n    This class contains a list of <Graph.Node> built-in types. \n    Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.\n\n    You can add your custom node types, customizing your visualization to the extreme.\n\n    Example:\n\n    (start code js)\n      RGraph.Plot.NodeTypes.implement({\n        'mySpecialType': {\n          'render': function(node, canvas) {\n            //print your custom node to canvas\n          },\n          //optional\n          'contains': function(node, pos) {\n            //return true if pos is inside the node or false otherwise\n          }\n        }\n      });\n    (end code)\n\n  */\nRGraph.Plot.NodeTypes=new Class({\n'none':{\n'render':$.empty,\n'contains':$.lambda(false)},\n\n'circle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.circle.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.circle.contains(npos,pos,dim);\n}},\n\n'ellipse':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nthis.nodeHelper.ellipse.render('fill',pos,width,height,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nreturn this.nodeHelper.ellipse.contains(npos,pos,width,height);\n}},\n\n'square':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.square.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.square.contains(npos,pos,dim);\n}},\n\n'rectangle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nthis.nodeHelper.rectangle.render('fill',pos,width,height,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nreturn this.nodeHelper.rectangle.contains(npos,pos,width,height);\n}},\n\n'triangle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.triangle.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.triangle.contains(npos,pos,dim);\n}},\n\n'star':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.star.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.star.contains(npos,pos,dim);\n}}});\n\n\n\n/*\n    Class: RGraph.Plot.EdgeTypes\n\n    This class contains a list of <Graph.Adjacence> built-in types. \n    Edge types implemented are 'none', 'line' and 'arrow'.\n  \n    You can add your custom edge types, customizing your visualization to the extreme.\n  \n    Example:\n  \n    (start code js)\n      RGraph.Plot.EdgeTypes.implement({\n        'mySpecialType': {\n          'render': function(adj, canvas) {\n            //print your custom edge to canvas\n          },\n          //optional\n          'contains': function(adj, pos) {\n            //return true if pos is inside the arc or false otherwise\n          }\n        }\n      });\n    (end code)\n  \n  */\nRGraph.Plot.EdgeTypes=new Class({\n'none':$.empty,\n'line':{\n'render':function render(adj,canvas){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nthis.edgeHelper.line.render(from,to,canvas);\n},\n'contains':function contains(adj,pos){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nreturn this.edgeHelper.line.contains(from,to,pos,this.edge.epsilon);\n}},\n\n'arrow':{\n'render':function render(adj,canvas){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true),\ndim=adj.getData('dim'),\ndirection=adj.data.$direction,\ninv=direction&&direction.length>1&&direction[0]!=adj.nodeFrom.id;\nthis.edgeHelper.arrow.render(from,to,dim,inv,canvas);\n},\n'contains':function contains(adj,pos){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nreturn this.edgeHelper.arrow.contains(from,to,pos,this.edge.epsilon);\n}}});\n\n\n\n})($jit.RGraph);\n\n\n/*\n * File: Layouts.ForceDirected.js\n *\n*/\n\n/*\n * Class: Layouts.ForceDirected\n * \n * Implements a Force Directed Layout.\n * \n * Implemented By:\n * \n * <ForceDirected>\n * \n * Credits:\n * \n * Marcus Cobden <http://marcuscobden.co.uk>\n * \n */\nLayouts.ForceDirected=new Class({\n\ngetOptions:function getOptions(random){\nvar s=this.canvas.getSize();\nvar w=s.width,h=s.height;\n//count nodes\nvar count=0;\nthis.graph.eachNode(function(n){\ncount++;\n});\nvar k2=w*h/count,k=Math.sqrt(k2);\nvar l=this.config.levelDistance;\n\nreturn{\nwidth:w,\nheight:h,\ntstart:w*0.1,\nnodef:function nodef(x){return k2/(x||1);},\nedgef:function edgef(x){return(/* x * x / k; */k*(x-l));}};\n\n},\n\ncompute:function compute(property,incremental){\nvar prop=$.splat(property||['current','start','end']);\nvar opt=this.getOptions();\nNodeDim.compute(this.graph,prop,this.config);\nthis.graph.computeLevels(this.root,0,\"ignore\");\nthis.graph.eachNode(function(n){\n$.each(prop,function(p){\nvar pos=n.getPos(p);\nif(pos.equals(Complex.KER)){\npos.x=opt.width/5*(Math.random()-0.5);\npos.y=opt.height/5*(Math.random()-0.5);\n}\n//initialize disp vector\nn.disp={};\n$.each(prop,function(p){\nn.disp[p]=$C(0,0);\n});\n});\n});\nthis.computePositions(prop,opt,incremental);\n},\n\ncomputePositions:function computePositions(property,opt,incremental){\nvar times=this.config.iterations,i=0,that=this;\nif(incremental){\n(function iter(){\nfor(var total=incremental.iter,j=0;j<total;j++){\nopt.t=opt.tstart;\nif(times)opt.t*=1-i++/(times-1);\nthat.computePositionStep(property,opt);\nif(times&&i>=times){\nincremental.onComplete();\nreturn;\n}\n}\nincremental.onStep(Math.round(i/(times-1)*100));\nsetTimeout(iter,1);\n})();\n}else{\nfor(;i<times;i++){\nopt.t=opt.tstart*(1-i/(times-1));\nthis.computePositionStep(property,opt);\n}\n}\n},\n\ncomputePositionStep:function computePositionStep(property,opt){\nvar graph=this.graph;\nvar min=Math.min,max=Math.max;\nvar dpos=$C(0,0);\n//calculate repulsive forces\ngraph.eachNode(function(v){\n//initialize disp\n$.each(property,function(p){\nv.disp[p].x=0;v.disp[p].y=0;\n});\ngraph.eachNode(function(u){\nif(u.id!=v.id){\n$.each(property,function(p){\nvar vp=v.getPos(p),up=u.getPos(p);\ndpos.x=vp.x-up.x;\ndpos.y=vp.y-up.y;\nvar norm=dpos.norm()||1;\nv.disp[p].$add(dpos.\n$scale(opt.nodef(norm)/norm));\n});\n}\n});\n});\n//calculate attractive forces\nvar T=!!graph.getNode(this.root).visited;\ngraph.eachNode(function(node){\nnode.eachAdjacency(function(adj){\nvar nodeTo=adj.nodeTo;\nif(!!nodeTo.visited===T){\n$.each(property,function(p){\nvar vp=node.getPos(p),up=nodeTo.getPos(p);\ndpos.x=vp.x-up.x;\ndpos.y=vp.y-up.y;\nvar norm=dpos.norm()||1;\nnode.disp[p].$add(dpos.$scale(-opt.edgef(norm)/norm));\nnodeTo.disp[p].$add(dpos.$scale(-1));\n});\n}\n});\nnode.visited=!T;\n});\n//arrange positions to fit the canvas\nvar t=opt.t,w2=opt.width/2,h2=opt.height/2;\ngraph.eachNode(function(u){\n$.each(property,function(p){\nvar disp=u.disp[p];\nvar norm=disp.norm()||1;\nvar p=u.getPos(p);\np.$add($C(disp.x*min(Math.abs(disp.x),t)/norm,\ndisp.y*min(Math.abs(disp.y),t)/norm));\np.x=min(w2,max(-w2,p.x));\np.y=min(h2,max(-h2,p.y));\n});\n});\n}});\n\n\n/*\n * File: ForceDirected.js\n */\n\n/*\n   Class: ForceDirected\n      \n   A visualization that lays graphs using a Force-Directed layout algorithm.\n   \n   Inspired by:\n  \n   Force-Directed Drawing Algorithms (Stephen G. Kobourov) <http://www.cs.brown.edu/~rt/gdhandbook/chapters/force-directed.pdf>\n   \n  Implements:\n  \n  All <Loader> methods\n  \n   Constructor Options:\n   \n   Inherits options from\n   \n   - <Options.Canvas>\n   - <Options.Controller>\n   - <Options.Node>\n   - <Options.Edge>\n   - <Options.Label>\n   - <Options.Events>\n   - <Options.Tips>\n   - <Options.NodeStyles>\n   - <Options.Navigation>\n   \n   Additionally, there are two parameters\n   \n   levelDistance - (number) Default's *50*. The natural length desired for the edges.\n   iterations - (number) Default's *50*. The number of iterations for the spring layout simulation. Depending on the browser's speed you could set this to a more 'interesting' number, like *200*. \n     \n   Instance Properties:\n\n   canvas - Access a <Canvas> instance.\n   graph - Access a <Graph> instance.\n   op - Access a <ForceDirected.Op> instance.\n   fx - Access a <ForceDirected.Plot> instance.\n   labels - Access a <ForceDirected.Label> interface implementation.\n\n*/\n\n$jit.ForceDirected=new Class({\n\nImplements:[Loader,Extras,Layouts.ForceDirected],\n\ninitialize:function initialize(controller){\nvar $ForceDirected=$jit.ForceDirected;\n\nvar config={\niterations:50,\nlevelDistance:50};\n\n\nthis.controller=this.config=$.merge(Options(\"Canvas\",\"Node\",\"Edge\",\n\"Fx\",\"Tips\",\"NodeStyles\",\"Events\",\"Navigation\",\"Controller\",\"Label\"),config,controller);\n\nvar canvasConfig=this.config;\nif(canvasConfig.useCanvas){\nthis.canvas=canvasConfig.useCanvas;\nthis.config.labelContainer=this.canvas.id+'-label';\n}else{\nif(canvasConfig.background){\ncanvasConfig.background=$.merge({\ntype:'Circles'},\ncanvasConfig.background);\n}\nthis.canvas=new Canvas(this,canvasConfig);\nthis.config.labelContainer=(typeof canvasConfig.injectInto=='string'?canvasConfig.injectInto:canvasConfig.injectInto.id)+'-label';\n}\n\nthis.graphOptions={\n'klass':Complex,\n'Node':{\n'selected':false,\n'exist':true,\n'drawn':true}};\n\n\nthis.graph=new Graph(this.graphOptions,this.config.Node,\nthis.config.Edge);\nthis.labels=new $ForceDirected.Label[canvasConfig.Label.type](this);\nthis.fx=new $ForceDirected.Plot(this,$ForceDirected);\nthis.op=new $ForceDirected.Op(this);\nthis.json=null;\nthis.busy=false;\n// initialize extras\nthis.initializeExtras();\n},\n\n/* \n    Method: refresh \n    \n    Computes positions and plots the tree.\n  */\nrefresh:function refresh(){\n// START METAMAPS CODE\n// this.compute();\n// END METAMAPS CODE\n// ORIGINAL CODE: this.compute();\nthis.plot();\n},\n\nreposition:function reposition(){\nthis.compute('end');\n},\n\n/*\n  Method: computeIncremental\n  \n  Performs the Force Directed algorithm incrementally.\n  \n  Description:\n  \n  ForceDirected algorithms can perform many computations and lead to JavaScript taking too much time to complete. \n  This method splits the algorithm into smaller parts allowing the user to track the evolution of the algorithm and \n  avoiding browser messages such as \"This script is taking too long to complete\".\n  \n  Parameters:\n  \n  opt - (object) The object properties are described below\n  \n  iter - (number) Default's *20*. Split the algorithm into pieces of _iter_ iterations. For example, if the _iterations_ configuration property \n  of your <ForceDirected> class is 100, then you could set _iter_ to 20 to split the main algorithm into 5 smaller pieces.\n  \n  property - (string) Default's *end*. Whether to update starting, current or ending node positions. Possible values are 'end', 'start', 'current'. \n  You can also set an array of these properties. If you'd like to keep the current node positions but to perform these \n  computations for final animation positions then you can just choose 'end'.\n  \n  onStep - (function) A callback function called when each \"small part\" of the algorithm completed. This function gets as first formal \n  parameter a percentage value.\n  \n  onComplete - A callback function called when the algorithm completed.\n  \n  Example:\n  \n  In this example I calculate the end positions and then animate the graph to those positions\n  \n  (start code js)\n  var fd = new $jit.ForceDirected(...);\n  fd.computeIncremental({\n    iter: 20,\n    property: 'end',\n    onStep: function(perc) {\n      Log.write(\"loading \" + perc + \"%\");\n    },\n    onComplete: function() {\n      Log.write(\"done\");\n      fd.animate();\n    }\n  });\n  (end code)\n  \n  In this example I calculate all positions and (re)plot the graph\n  \n  (start code js)\n  var fd = new ForceDirected(...);\n  fd.computeIncremental({\n    iter: 20,\n    property: ['end', 'start', 'current'],\n    onStep: function(perc) {\n      Log.write(\"loading \" + perc + \"%\");\n    },\n    onComplete: function() {\n      Log.write(\"done\");\n      fd.plot();\n    }\n  });\n  (end code)\n  \n  */\ncomputeIncremental:function computeIncremental(opt){\nopt=$.merge({\niter:20,\nproperty:'end',\nonStep:$.empty,\nonComplete:$.empty},\nopt||{});\n\nthis.config.onBeforeCompute(this.graph.getNode(this.root));\nthis.compute(opt.property,opt);\n},\n\n/*\n    Method: plot\n   \n    Plots the ForceDirected graph. This is a shortcut to *fx.plot*.\n   */\nplot:function plot(){\nthis.fx.plot();\n},\n\n/*\n     Method: animate\n    \n     Animates the graph from the current positions to the 'end' node positions.\n  */\nanimate:function animate(opt){\nthis.fx.animate($.merge({\nmodes:['linear']},\nopt||{}));\n}});\n\n\n$jit.ForceDirected.$extend=true;\n\n(function(ForceDirected){\n\n/*\n     Class: ForceDirected.Op\n     \n     Custom extension of <Graph.Op>.\n\n     Extends:\n\n     All <Graph.Op> methods\n     \n     See also:\n     \n     <Graph.Op>\n\n  */\nForceDirected.Op=new Class({\n\nImplements:Graph.Op});\n\n\n\n/*\n    Class: ForceDirected.Plot\n    \n    Custom extension of <Graph.Plot>.\n  \n    Extends:\n  \n    All <Graph.Plot> methods\n    \n    See also:\n    \n    <Graph.Plot>\n  \n  */\nForceDirected.Plot=new Class({\n\nImplements:Graph.Plot});\n\n\n\n/*\n    Class: ForceDirected.Label\n    \n    Custom extension of <Graph.Label>. \n    Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.\n  \n    Extends:\n  \n    All <Graph.Label> methods and subclasses.\n  \n    See also:\n  \n    <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.\n  \n  */\nForceDirected.Label={};\n\n/*\n     ForceDirected.Label.Native\n     \n     Custom extension of <Graph.Label.Native>.\n\n     Extends:\n\n     All <Graph.Label.Native> methods\n\n     See also:\n\n     <Graph.Label.Native>\n\n  */\nForceDirected.Label.Native=new Class({\nImplements:Graph.Label.Native});\n\n\n/*\n    ForceDirected.Label.SVG\n    \n    Custom extension of <Graph.Label.SVG>.\n  \n    Extends:\n  \n    All <Graph.Label.SVG> methods\n  \n    See also:\n  \n    <Graph.Label.SVG>\n  \n  */\nForceDirected.Label.SVG=new Class({\nImplements:Graph.Label.SVG,\n\ninitialize:function initialize(viz){\nthis.viz=viz;\n},\n\n/* \n       placeLabel\n\n       Overrides abstract method placeLabel in <Graph.Label>.\n\n       Parameters:\n\n       tag - A DOM label element.\n       node - A <Graph.Node>.\n       controller - A configuration/controller object passed to the visualization.\n      \n     */\nplaceLabel:function placeLabel(tag,node,controller){\nvar pos=node.pos.getc(true),\ncanvas=this.viz.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY,\nradius=canvas.getSize();\nvar labelPos={\nx:Math.round(pos.x*sx+ox+radius.width/2),\ny:Math.round(pos.y*sy+oy+radius.height/2)};\n\ntag.setAttribute('x',labelPos.x);\ntag.setAttribute('y',labelPos.y);\n\ncontroller.onPlaceLabel(tag,node);\n}});\n\n\n/*\n     ForceDirected.Label.HTML\n     \n     Custom extension of <Graph.Label.HTML>.\n\n     Extends:\n\n     All <Graph.Label.HTML> methods.\n\n     See also:\n\n     <Graph.Label.HTML>\n\n  */\nForceDirected.Label.HTML=new Class({\nImplements:Graph.Label.HTML,\n\ninitialize:function initialize(viz){\nthis.viz=viz;\n},\n/* \n       placeLabel\n\n       Overrides abstract method placeLabel in <Graph.Plot>.\n\n       Parameters:\n\n       tag - A DOM label element.\n       node - A <Graph.Node>.\n       controller - A configuration/controller object passed to the visualization.\n      \n     */\nplaceLabel:function placeLabel(tag,node,controller){\nvar pos=node.pos.getc(true),\ncanvas=this.viz.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY,\nradius=canvas.getSize();\nvar labelPos={\nx:Math.round(pos.x*sx+ox+radius.width/2),\ny:Math.round(pos.y*sy+oy+radius.height/2)};\n\nvar style=tag.style;\nstyle.left=labelPos.x+'px';\nstyle.top=labelPos.y+'px';\nstyle.display=this.fitsInCanvas(labelPos,canvas)?'':'none';\n\ncontroller.onPlaceLabel(tag,node);\n}});\n\n\n/*\n    Class: ForceDirected.Plot.NodeTypes\n\n    This class contains a list of <Graph.Node> built-in types. \n    Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.\n\n    You can add your custom node types, customizing your visualization to the extreme.\n\n    Example:\n\n    (start code js)\n      ForceDirected.Plot.NodeTypes.implement({\n        'mySpecialType': {\n          'render': function(node, canvas) {\n            //print your custom node to canvas\n          },\n          //optional\n          'contains': function(node, pos) {\n            //return true if pos is inside the node or false otherwise\n          }\n        }\n      });\n    (end code)\n\n  */\nForceDirected.Plot.NodeTypes=new Class({\n'none':{\n'render':$.empty,\n'contains':$.lambda(false)},\n\n'circle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.circle.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.circle.contains(npos,pos,dim);\n}},\n\n'ellipse':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nthis.nodeHelper.ellipse.render('fill',pos,width,height,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nreturn this.nodeHelper.ellipse.contains(npos,pos,width,height);\n}},\n\n'square':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.square.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.square.contains(npos,pos,dim);\n}},\n\n'rectangle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nthis.nodeHelper.rectangle.render('fill',pos,width,height,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nreturn this.nodeHelper.rectangle.contains(npos,pos,width,height);\n}},\n\n'triangle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.triangle.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.triangle.contains(npos,pos,dim);\n}},\n\n'star':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.star.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.star.contains(npos,pos,dim);\n}}});\n\n\n\n/*\n    Class: ForceDirected.Plot.EdgeTypes\n  \n    This class contains a list of <Graph.Adjacence> built-in types. \n    Edge types implemented are 'none', 'line' and 'arrow'.\n  \n    You can add your custom edge types, customizing your visualization to the extreme.\n  \n    Example:\n  \n    (start code js)\n      ForceDirected.Plot.EdgeTypes.implement({\n        'mySpecialType': {\n          'render': function(adj, canvas) {\n            //print your custom edge to canvas\n          },\n          //optional\n          'contains': function(adj, pos) {\n            //return true if pos is inside the arc or false otherwise\n          }\n        }\n      });\n    (end code)\n  \n  */\nForceDirected.Plot.EdgeTypes=new Class({\n'none':$.empty,\n'line':{\n'render':function render(adj,canvas){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nthis.edgeHelper.line.render(from,to,canvas);\n},\n'contains':function contains(adj,pos){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nreturn this.edgeHelper.line.contains(from,to,pos,this.edge.epsilon);\n}},\n\n'arrow':{\n'render':function render(adj,canvas){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true),\ndim=adj.getData('dim'),\ndirection=adj.data.$direction,\ninv=direction&&direction.length>1&&direction[0]!=adj.nodeFrom.id;\nthis.edgeHelper.arrow.render(from,to,dim,inv,canvas);\n},\n'contains':function contains(adj,pos){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nreturn this.edgeHelper.arrow.contains(from,to,pos,this.edge.epsilon);\n}}});\n\n\n\n})($jit.ForceDirected);\n\n\n/*\n * Vector3 class based on three.js http://github.com/mrdoob/three.js, Copyright (c) Mr.doob http://mrdoob.com/, MIT License http://github.com/mrdoob/three.js/blob/master/LICENSE \n */\n\nvar Vector3=function Vector3(x,y,z){\nthis.x=x||0;\nthis.y=y||0;\nthis.z=z||0;\n};\n\n$jit.Vector3=Vector3;\n\nVector3.prototype={\nset:function set(v){\nthis.x=v.x;\nthis.y=v.y;\nthis.z=v.z;\n},\n\nsetc:function setc(x,y,z){\nthis.x=x;\nthis.y=y;\nthis.z=z;\n},\n\ngetc:function getc(){\nreturn this;\n},\n\n//TODO(nico): getp\n\nadd:function add(v1,v2){\nthis.x=v1.x+v2.x;\nthis.y=v1.y+v2.y;\nthis.z=v1.z+v2.z;\nreturn this;\n},\n\n$add:function $add(v){\nthis.x+=v.x;\nthis.y+=v.y;\nthis.z+=v.z;\nreturn this;\n},\n\naddScalar:function addScalar(s){\nthis.x+=s;\nthis.y+=s;\nthis.z+=s;\nreturn this;\n},\n\nsub:function sub(v1,v2){\nthis.x=v1.x-v2.x;\nthis.y=v1.y-v2.y;\nthis.z=v1.z-v2.z;\nreturn this;\n},\n\n$sub:function $sub(v){\nthis.x-=v.x;\nthis.y-=v.y;\nthis.z-=v.z;\nreturn this;\n},\n\ncross:function cross(v1,v2){\nthis.x=v1.y*v2.z-v1.z*v2.y;\nthis.y=v1.z*v2.x-v1.x*v2.z;\nthis.z=v1.x*v2.y-v1.y*v2.x;\nreturn this;\n},\n\n$cross:function $cross(v){\nvar tx=this.x,ty=this.y,tz=this.z;\n\nthis.x=ty*v.z-tz*v.y;\nthis.y=tz*v.x-tx*v.z;\nthis.z=tx*v.y-ty*v.x;\nreturn this;\n},\n\n$multiply:function $multiply(v){\nthis.x*=v.x;\nthis.y*=v.y;\nthis.z*=v.z;\nreturn this;\n},\n\n$scale:function $scale(s){\nthis.x*=s;\nthis.y*=s;\nthis.z*=s;\nreturn this;\n},\n\ndot:function dot(v){\nreturn this.x*v.x+this.y*v.y+this.z*v.z;\n},\n\ndistanceTo:function distanceTo(v){\nreturn Math.sqrt(this.distanceToSquared(v));\n},\n\ndistanceToSquared:function distanceToSquared(v){\nvar dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;\nreturn dx*dx+dy*dy+dz*dz;\n},\n\nnorm:function norm(){\nreturn Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z);\n},\n\nnormSquared:function normSquared(){\nreturn this.x*this.x+this.y*this.y+this.z*this.z;\n},\n\nnegate:function negate(){\nthis.x=-this.x;\nthis.y=-this.y;\nthis.z=-this.z;\nreturn this;\n},\n\nnormalize:function normalize(){\nvar len=this.norm();\nif(len>0){\nthis.$scale(1/len);\n}\nreturn this;\n},\n\nisZero:function isZero(){\nvar almostZero=0.0001,\nabs=Math.abs;\n\nreturn abs(this.x)<almostZero&&abs(this.y)<almostZero&&abs(this.z)<almostZero;\n},\n\nclone:function clone(){\nreturn new Vector3(this.x,this.y,this.z);\n}};\n\n\nvar $V3=function $V3(a,b,c){return new Vector3(a,b,c);};\n\n/*\n * Matrix4 class based on three.js http://github.com/mrdoob/three.js, Copyright (c) Mr.doob http://mrdoob.com/, MIT License http://github.com/mrdoob/three.js/blob/master/LICENSE \n */\n\nvar Matrix4=function Matrix4(){\nthis._x=new Vector3();\nthis._y=new Vector3();\nthis._z=new Vector3();\n};\n\n$jit.Matrix4=Matrix4;\n\nMatrix4.prototype={\n\nn11:1,n12:0,n13:0,n14:0,\nn21:0,n22:1,n23:0,n24:0,\nn31:0,n32:0,n33:1,n34:0,\nn41:0,n42:0,n43:0,n44:1,\n\nidentity:function identity(){\nthis.n11=1;this.n12=0;this.n13=0;this.n14=0;\nthis.n21=0;this.n22=1;this.n23=0;this.n24=0;\nthis.n31=0;this.n32=0;this.n33=1;this.n34=0;\nthis.n41=0;this.n42=0;this.n43=0;this.n44=1;\n},\n\nlookAt:function lookAt(eye,center,up){\nvar x=this._x,y=this._y,z=this._z;\n\nz.sub(eye,center);\nz.normalize();\n\nx.cross(up,z);\nx.normalize();\n\ny.cross(z,x);\ny.normalize();\n\nthis.n11=x.x;this.n12=x.y;this.n13=x.z;this.n14=-x.dot(eye);\nthis.n21=y.x;this.n22=y.y;this.n23=y.z;this.n24=-y.dot(eye);\nthis.n31=z.x;this.n32=z.y;this.n33=z.z;this.n34=-z.dot(eye);\n},\n\ntransform:function transform(v){\nvar vx=v.x,vy=v.y,vz=v.z,vw=v.w?v.w:1.0;\n\nv.x=this.n11*vx+this.n12*vy+this.n13*vz+this.n14*vw;\nv.y=this.n21*vx+this.n22*vy+this.n23*vz+this.n24*vw;\nv.z=this.n31*vx+this.n32*vy+this.n33*vz+this.n34*vw;\n\nvw=this.n41*vx+this.n42*vy+this.n43*vz+this.n44*vw;\n\nif(v.w){\nv.w=vw;\n}else{\nv.x=v.x/vw;\nv.y=v.y/vw;\nv.z=v.z/vw;\n}\n},\n\nmultiply:function multiply(a,b){\nthis.n11=a.n11*b.n11+a.n12*b.n21+a.n13*b.n31+a.n14*b.n41;\nthis.n12=a.n11*b.n12+a.n12*b.n22+a.n13*b.n32+a.n14*b.n42;\nthis.n13=a.n11*b.n13+a.n12*b.n23+a.n13*b.n33+a.n14*b.n43;\nthis.n14=a.n11*b.n14+a.n12*b.n24+a.n13*b.n34+a.n14*b.n44;\n\nthis.n21=a.n21*b.n11+a.n22*b.n21+a.n23*b.n31+a.n24*b.n41;\nthis.n22=a.n21*b.n12+a.n22*b.n22+a.n23*b.n32+a.n24*b.n42;\nthis.n23=a.n21*b.n13+a.n22*b.n23+a.n23*b.n33+a.n24*b.n43;\nthis.n24=a.n21*b.n14+a.n22*b.n24+a.n23*b.n34+a.n24*b.n44;\n\nthis.n31=a.n31*b.n11+a.n32*b.n21+a.n33*b.n31+a.n34*b.n41;\nthis.n32=a.n31*b.n12+a.n32*b.n22+a.n33*b.n32+a.n34*b.n42;\nthis.n33=a.n31*b.n13+a.n32*b.n23+a.n33*b.n33+a.n34*b.n43;\nthis.n34=a.n31*b.n14+a.n32*b.n24+a.n33*b.n34+a.n34*b.n44;\n\nthis.n41=a.n41*b.n11+a.n42*b.n21+a.n43*b.n31+a.n44*b.n41;\nthis.n42=a.n41*b.n12+a.n42*b.n22+a.n43*b.n32+a.n44*b.n42;\nthis.n43=a.n41*b.n13+a.n42*b.n23+a.n43*b.n33+a.n44*b.n43;\nthis.n44=a.n41*b.n14+a.n42*b.n24+a.n43*b.n34+a.n44*b.n44;\n},\n\n$multiply:function $multiply(m){\nvar n11=this.n11,n12=this.n12,n13=this.n13,n14=this.n14,\nn21=this.n21,n22=this.n22,n23=this.n23,n24=this.n24,\nn31=this.n31,n32=this.n32,n33=this.n33,n34=this.n34,\nn41=this.n41,n42=this.n42,n43=this.n43,n44=this.n44;\n\nthis.n11=n11*m.n11+n12*m.n21+n13*m.n31+n14*m.n41;\nthis.n12=n11*m.n12+n12*m.n22+n13*m.n32+n14*m.n42;\nthis.n13=n11*m.n13+n12*m.n23+n13*m.n33+n14*m.n43;\nthis.n14=n11*m.n14+n12*m.n24+n13*m.n34+n14*m.n44;\n\nthis.n21=n21*m.n11+n22*m.n21+n23*m.n31+n24*m.n41;\nthis.n22=n21*m.n12+n22*m.n22+n23*m.n32+n24*m.n42;\nthis.n23=n21*m.n13+n22*m.n23+n23*m.n33+n24*m.n43;\nthis.n24=n21*m.n14+n22*m.n24+n23*m.n34+n24*m.n44;\n\nthis.n31=n31*m.n11+n32*m.n21+n33*m.n31+n34*m.n41;\nthis.n32=n31*m.n12+n32*m.n22+n33*m.n32+n34*m.n42;\nthis.n33=n31*m.n13+n32*m.n23+n33*m.n33+n34*m.n43;\nthis.n34=n31*m.n14+n32*m.n24+n33*m.n34+n34*m.n44;\n\nthis.n41=n41*m.n11+n42*m.n21+n43*m.n31+n44*m.n41;\nthis.n42=n41*m.n12+n42*m.n22+n43*m.n32+n44*m.n42;\nthis.n43=n41*m.n13+n42*m.n23+n43*m.n33+n44*m.n43;\nthis.n44=n41*m.n14+n42*m.n24+n43*m.n34+n44*m.n44;\n},\n\n$scale:function $scale(s){\nthis.n11*=s;this.n12*=s;this.n13*=s;this.n14*=s;\nthis.n21*=s;this.n22*=s;this.n23*=s;this.n24*=s;\nthis.n31*=s;this.n32*=s;this.n33*=s;this.n34*=s;\nthis.n41*=s;this.n42*=s;this.n43*=s;this.n44*=s;\nreturn this;\n},\n\n$add:function $add(m){\nthis.n11+=m.n11;\nthis.n12+=m.n12;\nthis.n13+=m.n13;\nthis.n14+=m.n14;\nthis.n21+=m.n21;\nthis.n22+=m.n22;\nthis.n23+=m.n23;\nthis.n24+=m.n24;\nthis.n31+=m.n31;\nthis.n32+=m.n32;\nthis.n33+=m.n33;\nthis.n34+=m.n34;\nthis.n41+=m.n41;\nthis.n42+=m.n42;\nthis.n43+=m.n43;\nthis.n44+=m.n44;\nreturn this;\n},\n\ndeterminant:function determinant(){\nreturn(\nthis.n14*this.n23*this.n32*this.n41-\nthis.n13*this.n24*this.n32*this.n41-\nthis.n14*this.n22*this.n33*this.n41+\nthis.n12*this.n24*this.n33*this.n41+\n\nthis.n13*this.n22*this.n34*this.n41-\nthis.n12*this.n23*this.n34*this.n41-\nthis.n14*this.n23*this.n31*this.n42+\nthis.n13*this.n24*this.n31*this.n42+\n\nthis.n14*this.n21*this.n33*this.n42-\nthis.n11*this.n24*this.n33*this.n42-\nthis.n13*this.n21*this.n34*this.n42+\nthis.n11*this.n23*this.n34*this.n42+\n\nthis.n14*this.n22*this.n31*this.n43-\nthis.n12*this.n24*this.n31*this.n43-\nthis.n14*this.n21*this.n32*this.n43+\nthis.n11*this.n24*this.n32*this.n43+\n\nthis.n12*this.n21*this.n34*this.n43-\nthis.n11*this.n22*this.n34*this.n43-\nthis.n13*this.n22*this.n31*this.n44+\nthis.n12*this.n23*this.n31*this.n44+\n\nthis.n13*this.n21*this.n32*this.n44-\nthis.n11*this.n23*this.n32*this.n44-\nthis.n12*this.n21*this.n33*this.n44+\nthis.n11*this.n22*this.n33*this.n44);\n},\n\n$transpose:function $transpose(){\nfunction swap(obj,p1,p2){\nvar aux=obj[p1];\nobj[p1]=obj[p2];\nobj[p2]=aux;\n}\n\nswap(this,'n21','n12');\nswap(this,'n31','n13');\nswap(this,'n32','n23');\nswap(this,'n41','n14');\nswap(this,'n42','n24');\nswap(this,'n43','n34');\nreturn this;\n},\n\nclone:function clone(){\nvar m=new Matrix4();\nm.n11=this.n11;m.n12=this.n12;m.n13=this.n13;m.n14=this.n14;\nm.n21=this.n21;m.n22=this.n22;m.n23=this.n23;m.n24=this.n24;\nm.n31=this.n31;m.n32=this.n32;m.n33=this.n33;m.n34=this.n34;\nm.n41=this.n41;m.n42=this.n42;m.n43=this.n43;m.n44=this.n44;\nreturn m;\n},\n\nflatten:function flatten(){\nreturn[this.n11,this.n21,this.n31,this.n41,\nthis.n12,this.n22,this.n32,this.n42,\nthis.n13,this.n23,this.n33,this.n43,\nthis.n14,this.n24,this.n34,this.n44];\n}};\n\n\nMatrix4.translationMatrix=function(x,y,z){\nvar m=new Matrix4();\n\nm.n14=x;\nm.n24=y;\nm.n34=z;\n\nreturn m;\n};\n\nMatrix4.scaleMatrix=function(x,y,z){\nvar m=new Matrix4();\n\nm.n11=x;\nm.n22=y;\nm.n33=z;\n\nreturn m;\n};\n\nMatrix4.rotationXMatrix=function(theta){\nvar rot=new Matrix4();\n\nrot.n22=rot.n33=Math.cos(theta);\nrot.n32=Math.sin(theta);\nrot.n23=-rot.n32;\n\nreturn rot;\n};\n\nMatrix4.rotationYMatrix=function(theta){\nvar rot=new Matrix4();\n\nrot.n11=rot.n33=Math.cos(theta);\nrot.n13=Math.sin(theta);\nrot.n31=-rot.n13;\n\nreturn rot;\n};\n\nMatrix4.rotationZMatrix=function(theta){\nvar rot=new Matrix4();\n\nrot.n11=rot.n22=Math.cos(theta);\nrot.n21=Math.sin(theta);\nrot.n12=-rot.n21;\n\nreturn rot;\n};\n\nMatrix4.makeInvert=function(m1){\nvar m2=new Matrix4();\n\nm2.n11=m1.n23*m1.n34*m1.n42-m1.n24*m1.n33*m1.n42+m1.n24*m1.n32*m1.n43-m1.n22*m1.n34*m1.n43-m1.n23*m1.n32*m1.n44+m1.n22*m1.n33*m1.n44;\nm2.n12=m1.n14*m1.n33*m1.n42-m1.n13*m1.n34*m1.n42-m1.n14*m1.n32*m1.n43+m1.n12*m1.n34*m1.n43+m1.n13*m1.n32*m1.n44-m1.n12*m1.n33*m1.n44;\nm2.n13=m1.n13*m1.n24*m1.n42-m1.n14*m1.n23*m1.n42+m1.n14*m1.n22*m1.n43-m1.n12*m1.n24*m1.n43-m1.n13*m1.n22*m1.n44+m1.n12*m1.n23*m1.n44;\nm2.n14=m1.n14*m1.n23*m1.n32-m1.n13*m1.n24*m1.n32-m1.n14*m1.n22*m1.n33+m1.n12*m1.n24*m1.n33+m1.n13*m1.n22*m1.n34-m1.n12*m1.n23*m1.n34;\nm2.n21=m1.n24*m1.n33*m1.n41-m1.n23*m1.n34*m1.n41-m1.n24*m1.n31*m1.n43+m1.n21*m1.n34*m1.n43+m1.n23*m1.n31*m1.n44-m1.n21*m1.n33*m1.n44;\nm2.n22=m1.n13*m1.n34*m1.n41-m1.n14*m1.n33*m1.n41+m1.n14*m1.n31*m1.n43-m1.n11*m1.n34*m1.n43-m1.n13*m1.n31*m1.n44+m1.n11*m1.n33*m1.n44;\nm2.n23=m1.n14*m1.n23*m1.n41-m1.n13*m1.n24*m1.n41-m1.n14*m1.n21*m1.n43+m1.n11*m1.n24*m1.n43+m1.n13*m1.n21*m1.n44-m1.n11*m1.n23*m1.n44;\nm2.n24=m1.n13*m1.n24*m1.n31-m1.n14*m1.n23*m1.n31+m1.n14*m1.n21*m1.n33-m1.n11*m1.n24*m1.n33-m1.n13*m1.n21*m1.n34+m1.n11*m1.n23*m1.n34;\nm2.n31=m1.n22*m1.n34*m1.n41-m1.n24*m1.n32*m1.n41+m1.n24*m1.n31*m1.n42-m1.n21*m1.n34*m1.n42-m1.n22*m1.n31*m1.n44+m1.n21*m1.n32*m1.n44;\nm2.n32=m1.n14*m1.n32*m1.n41-m1.n12*m1.n34*m1.n41-m1.n14*m1.n31*m1.n42+m1.n11*m1.n34*m1.n42+m1.n12*m1.n31*m1.n44-m1.n11*m1.n32*m1.n44;\nm2.n33=m1.n13*m1.n24*m1.n41-m1.n14*m1.n22*m1.n41+m1.n14*m1.n21*m1.n42-m1.n11*m1.n24*m1.n42-m1.n12*m1.n21*m1.n44+m1.n11*m1.n22*m1.n44;\nm2.n34=m1.n14*m1.n22*m1.n31-m1.n12*m1.n24*m1.n31-m1.n14*m1.n21*m1.n32+m1.n11*m1.n24*m1.n32+m1.n12*m1.n21*m1.n34-m1.n11*m1.n22*m1.n34;\nm2.n41=m1.n23*m1.n32*m1.n41-m1.n22*m1.n33*m1.n41-m1.n23*m1.n31*m1.n42+m1.n21*m1.n33*m1.n42+m1.n22*m1.n31*m1.n43-m1.n21*m1.n32*m1.n43;\nm2.n42=m1.n12*m1.n33*m1.n41-m1.n13*m1.n32*m1.n41+m1.n13*m1.n31*m1.n42-m1.n11*m1.n33*m1.n42-m1.n12*m1.n31*m1.n43+m1.n11*m1.n32*m1.n43;\nm2.n43=m1.n13*m1.n22*m1.n41-m1.n12*m1.n23*m1.n41-m1.n13*m1.n21*m1.n42+m1.n11*m1.n23*m1.n42+m1.n12*m1.n21*m1.n43-m1.n11*m1.n22*m1.n43;\nm2.n44=m1.n12*m1.n23*m1.n31-m1.n13*m1.n22*m1.n31+m1.n13*m1.n21*m1.n32-m1.n11*m1.n23*m1.n32-m1.n12*m1.n21*m1.n33+m1.n11*m1.n22*m1.n33;\nm2.$scale(1/m1.determinant());\n\nreturn m2;\n};\n\nMatrix4.makeFrustum=function(left,right,bottom,top,near,far){\nvar m,x,y,a,b,c,d;\n\nm=new Matrix4();\nx=2*near/(right-left);\ny=2*near/(top-bottom);\na=(right+left)/(right-left);\nb=(top+bottom)/(top-bottom);\nc=-(far+near)/(far-near);\nd=-2*far*near/(far-near);\n\nm.n11=x;m.n12=0;m.n13=a;m.n14=0;\nm.n21=0;m.n22=y;m.n23=b;m.n24=0;\nm.n31=0;m.n32=0;m.n33=c;m.n34=d;\nm.n41=0;m.n42=0;m.n43=-1;m.n44=0;\n\nreturn m;\n};\n\nMatrix4.makePerspective=function(fov,aspect,near,far){\nvar ymax,ymin,xmin,xmax;\n\nymax=near*Math.tan(fov*Math.PI/360);\nymin=-ymax;\nxmin=ymin*aspect;\nxmax=ymax*aspect;\n\nreturn Matrix4.makeFrustum(xmin,xmax,ymin,ymax,near,far);\n};\n\nMatrix4.makeOrtho=function(left,right,top,bottom,near,far){\nvar m,x,y,z,w,h,p;\n\nm=new Matrix4();\nw=right-left;\nh=bottom-top;\np=far-near;\nx=(right+left)/w;\ny=(bottom+top)/h;\nz=(far+near)/p;\n\nm.n11=2/w;m.n12=0;m.n13=0;m.n14=-x;\nm.n21=0;m.n22=2/h;m.n23=0;m.n24=-y;\nm.n31=0;m.n32=0;m.n33=-2/p;m.n34=-z;\nm.n41=0;m.n42=0;m.n43=0;m.n44=1;\n\nreturn m;\n};\n\n\n/*\n * Camera class based on three.js http://github.com/mrdoob/three.js, Copyright (c) Mr.doob http://mrdoob.com/, MIT License http://github.com/mrdoob/three.js/blob/master/LICENSE \n */\n\nvar Camera=function Camera(fov,aspect,near,far){\nthis.projectionMatrix=Matrix4.makePerspective(fov,aspect,near,far);\n};\n\nCamera.prototype={\nposition:new Vector3(),\ntarget:{\nposition:new Vector3()},\n\nup:new Vector3(0,1,0),\nmatrix:new Matrix4(),\n\nupdateMatrix:function updateMatrix(){\nthis.matrix.lookAt(this.position,this.target.position,this.up);\n}};\n\n\n\nCanvas.Base['3D']=new Class({\nImplements:Canvas.Base['2D'],\n\nprogram:null,\ncamera:null,\n\ninitialize:function initialize(viz){\nthis.viz=viz;\nthis.opt=viz.config;\nthis.size=false;\nthis.createCanvas();\nthis.initWebGL();\nthis.initCamera();\n},\n\ninitWebGL:function initWebGL(){\n//initialize context\nvar gl=this.getCtx();\n//get viewport size\nvar size=this.getSize();\n//compile and get shaders\nvar fragmentShader=this.getShader(Canvas.Base['3D'].FragmentShader,gl.FRAGMENT_SHADER);\nvar vertexShader=this.getShader(Canvas.Base['3D'].VertexShader,gl.VERTEX_SHADER);\n//create program and link shaders\nvar program=gl.createProgram();\ngl.attachShader(program,vertexShader);\ngl.attachShader(program,fragmentShader);\ngl.linkProgram(program);\nif(!gl.getProgramParameter(program,gl.LINK_STATUS)){\nthrow\"Could not link shaders\";\n}\ngl.useProgram(program);\n//bind name to variable location in shaders\n$.extend(program,{\n'viewMatrix':gl.getUniformLocation(program,'viewMatrix'),\n'projectionMatrix':gl.getUniformLocation(program,'projectionMatrix'),\n'normalMatrix':gl.getUniformLocation(program,'normalMatrix'),\n'color':gl.getUniformLocation(program,'color'),\n\n'enableLighting':gl.getUniformLocation(program,'enableLighting'),\n'ambientColor':gl.getUniformLocation(program,'ambientColor'),\n'directionalColor':gl.getUniformLocation(program,'directionalColor'),\n'lightingDirection':gl.getUniformLocation(program,'lightingDirection'),\n\n'position':gl.getAttribLocation(program,'position'),\n'normal':gl.getAttribLocation(program,'normal')});\n\ngl.enableVertexAttribArray(program.position);\ngl.enableVertexAttribArray(program.normal);\nthis.program=program;\n//set general rendering options\ngl.clearColor(0,0,0,0);\ngl.clearDepth(1);\n\ngl.enable(gl.DEPTH_TEST);\ngl.depthFunc(gl.LEQUAL);\n\ngl.enable(gl.BLEND);\ngl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);\n\ngl.viewport(0,0,size.width,size.height);\n},\n\ninitCamera:function initCamera(){\nvar size=this.getSize();\nvar camera=new Camera(75,size.width/size.height,1,1000);\ncamera.position.z=500;\nthis.camera=camera;\n},\n\ngetShader:function getShader(src,type){\nvar gl=this.ctx;\nvar shader=gl.createShader(type);\ngl.shaderSource(shader,src);\ngl.compileShader(shader);\nif(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){\nvar info=gl.getShaderInfoLog(shader);\nthrow\"Could not compile shader src: \"+info;\n}\nreturn shader;\n},\n\ngetCtx:function getCtx(){\nif(!this.ctx)\nreturn this.ctx=this.canvas.getContext('experimental-webgl');\nreturn this.ctx;\n},\n\nresize:function resize(width,height){\nvar size=this.getSize(),\ncanvas=this.canvas,\nstyles=canvas.style,\ngl=this.getCtx();\nthis.size=false;\ncanvas.width=width;\ncanvas.height=height;\nstyles.width=width+\"px\";\nstyles.height=height+\"px\";\ngl.viewport(0,0,width,height);\n\nthis.translateOffsetX=\nthis.translateOffsetY=0;\nthis.scaleOffsetX=\nthis.scaleOffsetY=1;\nthis.clear();\nthis.viz.resize(width,height,this);\n},\n\ntranslateToCenter:$.empty,\nscale:$.empty,\n\ntranslate:function translate(x,y,z,disablePlot){\nvar sx=this.scaleOffsetX,\nsy=this.scaleOffsetY;\nthis.translateOffsetX+=x*sx;\nthis.translateOffsetY+=y*sy;\nvar pos=this.camera.position;\npos.x+=x;\npos.y+=y;\npos.z+=z;\n!disablePlot&&this.plot();\n},\n\nclear:function clear(){\nvar gl=this.getCtx();\ngl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);\n//TODO(nico) is this OK? I mean, to put this line here.\nthis.camera.updateMatrix();\n},\n\nplot:function plot(){\nthis.clear();\nthis.viz.plot(this);\n}});\n\n\n//Shaders code\nCanvas.Base['3D'].FragmentShader=[\n\"#ifdef GL_ES\",\n\"precision highp float;\",\n\"#endif\",\n\n\"varying vec4 vcolor;\",\n\"varying vec3 lightWeighting;\",\n\n\"void main(){\",\n\n\"gl_FragColor = vec4(vcolor.rgb * lightWeighting, vcolor.a);\",\n\n\"}\"].\njoin(\"\\n\");\n\nCanvas.Base['3D'].VertexShader=[\n\"attribute vec3 position;\",\n\"attribute vec3 normal;\",\n\"uniform vec4 color;\",\n\n\"uniform mat4 viewMatrix;\",\n\"uniform mat4 projectionMatrix;\",\n\"uniform mat4 normalMatrix;\",\n\n\"uniform bool enableLighting;\",\n\"uniform vec3 ambientColor;\",\n\"uniform vec3 directionalColor;\",\n\"uniform vec3 lightingDirection;\",\n\n\"varying vec4 vcolor;\",\n\"varying vec3 lightWeighting;\",\n\n\"void main(void) {\",\n\n\"if(!enableLighting) {\",\n\"lightWeighting = vec3(1.0, 1.0, 1.0);\",\n\"} else {\",\n\"vec4 transformedNormal = normalMatrix * vec4(normal, 1.0);\",\n\"float directionalLightWeighting = max(dot(transformedNormal.xyz, lightingDirection), 0.0);\",\n\"lightWeighting = ambientColor + directionalColor * directionalLightWeighting;\",\n\"}\",\n\n\"vcolor = color;\",\n\"gl_Position = projectionMatrix * viewMatrix * vec4( position, 1.0 );\",\n\n\"}\"].\njoin(\"\\n\");\n\n/*\n * Some of the geometries where inspired by three.js http://github.com/mrdoob/three.js, Copyright (c) Mr.doob http://mrdoob.com/, MIT License http://github.com/mrdoob/three.js/blob/master/LICENSE \n */\n\nvar O3D={};\n\n$jit.O3D=O3D;\n\nO3D.base=new Class({\n//array of { x, y, z } of float\nvertices:[],\n//array of { a, b, c, d? } of int\nfaces:[],\n//updated on plotNode/plotEdge\nposition:new Vector3(),\nrotation:new Vector3(),\nscale:new Vector3(1,1,1),\n//intrinsic coordinates\nmatrix:new Matrix4(),\n\nupdate:function update(elem){\nif(elem.nodeFrom&&elem.nodeTo){\nthis.updateEdge(elem);\n}else{\nthis.updateNode(elem);\n}\n},\n\nupdateNode:$.empty,\n\nupdateEdge:function updateEdge(elem){\nthis.updateNode(elem);\n},\n\nupdateMatrix:function updateMatrix(){\nvar pos=this.position,\nrot=this.rotation,\nscale=this.scale,\nmatrix=this.matrix;\n\nmatrix.identity();\n\nmatrix.$multiply(Matrix4.translationMatrix(pos.x,pos.y,pos.z));\nmatrix.$multiply(Matrix4.rotationXMatrix(rot.x));\nmatrix.$multiply(Matrix4.rotationYMatrix(rot.y));\nmatrix.$multiply(Matrix4.rotationZMatrix(rot.z));\nmatrix.$multiply(Matrix4.scaleMatrix(scale.x,scale.y,scale.z));\n},\n//compute faces normals\ncomputeNormals:function computeNormals(){\nfor(var f=0,vs=this.vertices,fs=this.faces,len=fs.length;f<len;f++){\nvar va=vs[fs[f].a],\nvb=vs[fs[f].b],\nvc=vs[fs[f].c],\ncb=new Vector3(),\nab=new Vector3();\n\ncb.sub(vc,vb);\nab.sub(va,vb);\ncb.$cross(ab);\n\nif(!cb.isZero())cb.normalize();\n\nfs[f].normal=cb;\n}\n}});\n\n\n//IsoCube\nfunction IsoCube(){\nvar vs=this.vertices,\nf4=this.faces,\nvsp=function vsp(x,y,z){vs.push({x:x,y:y,z:z});},\nf4p=function f4p(a,b,c,d){f4.push({a:a,b:b,c:c,d:d});};\n\nvsp(1,1,-1);\nvsp(1,-1,-1);\nvsp(-1,-1,-1);\nvsp(-1,1,-1);\nvsp(1,1,1);\nvsp(1,-1,1);\nvsp(-1,-1,1);\nvsp(-1,1,1);\n\nf4p(0,1,2,3);\nf4p(4,7,6,5);\nf4p(0,4,5,1);\nf4p(1,5,6,2);\nf4p(2,6,7,3);\nf4p(4,0,3,7);\n}\n\n//Cube\nO3D.cube=new Class({\nImplements:O3D.base,\n\ninitialize:function initialize(){\nIsoCube.call(this);\nthis.computeNormals();\n},\n\nupdateNode:function updateNode(obj){\nvar dim=obj.getData('dim'),\npos=obj.pos;\n\nthis.position.setc(pos.x,pos.y,pos.z);\nthis.scale.setc(dim,dim,dim);\nthis.updateMatrix();\n}});\n\n\nO3D.sphere=new Class({\nImplements:O3D.base,\n\nradius:1,\nsegments_width:10,\nsegments_height:10,\n\ninitialize:function initialize(){\nvar radius=this.radius,\nsegments_width=this.segments_width,\nsegments_height=this.segments_height,\ngridX=segments_width||8,\ngridY=segments_height||6,\ncos=Math.cos,\nsin=Math.sin,\nmax=Math.max,\npi=Math.PI;\n\nvar iHor=max(3,gridX),\niVer=max(2,gridY),\naVtc=[];\n\nfor(var j=0;j<iVer+1;j++){\nvar fRad1=j/iVer,\nfZ=radius*cos(fRad1*pi),\nfRds=radius*sin(fRad1*pi),\naRow=[],\noVtx=0;\n\nfor(var i=0;i<iHor;i++){\nvar fRad2=2*i/iHor,\nfX=fRds*Math.sin(fRad2*pi),\nfY=fRds*Math.cos(fRad2*pi);\nif(!((j==0||j==iVer)&&i>0)){\noVtx=this.vertices.push({x:fY,y:fZ,z:fX})-1;\n}\naRow.push(oVtx);\n}\naVtc.push(aRow);\n}\n\nvar iVerNum=aVtc.length;\nfor(var j=0;j<iVerNum;j++){\nvar iHorNum=aVtc[j].length;\nif(j>0){\nfor(var i=0;i<iHorNum;i++){\nvar bEnd=i==iHorNum-1;\nvar aP1=aVtc[j][bEnd?0:i+1];\nvar aP2=aVtc[j][bEnd?iHorNum-1:i];\nvar aP3=aVtc[j-1][bEnd?iHorNum-1:i];\nvar aP4=aVtc[j-1][bEnd?0:i+1];\n\nif(j<aVtc.length-1){\nthis.faces.push({a:aP1,b:aP2,c:aP3});\n}\nif(j>1){\nthis.faces.push({a:aP1,b:aP3,c:aP4});\n}\n}\n}\n}\nthis.computeNormals();\n},\n\nupdateNode:function updateNode(obj){\nvar dim=obj.getData('dim'),\npos=obj.pos;\n\nthis.position.setc(pos.x,pos.y,pos.z);\nthis.scale.setc(dim,dim,dim);\nthis.updateMatrix();\n}});\n\n\n\n\nO3D.tube=new Class({\nImplements:O3D.base,\n\nnumSegs:10,\ndim:1,\ninitialize:function initialize(){\nvar vs=this.vertices,\nf4=this.faces,\nvsp=function vsp(x,y,z){vs.push({x:x,y:y,z:z});},\nf4p=function f4p(a,b,c,d){f4.push({a:a,b:b,c:c,d:d});};\n\nvar scope=this,\nsin=Math.sin,\ncos=Math.cos,\npi=Math.PI,\npi2=pi*2,\nnumSegs=this.numSegs,\ntopRad=this.dim,\nbotRad=this.dim;\n\n// Top circle vertices\nfor(var i=0;i<numSegs;i++){\nvsp(sin(pi2*i/numSegs)*topRad,cos(pi2*i/numSegs)*topRad,-0.5);\n}\n// Bottom circle vertices\nfor(var i=0;i<numSegs;i++){\nvsp(sin(pi2*i/numSegs)*botRad,cos(pi2*i/numSegs)*botRad,0.5);\n}\n// Body \nfor(var i=0;i<numSegs;i++){\nf4p(i,(i+1)%numSegs,numSegs+(i+1)%numSegs,i+numSegs);\n}\nthis.computeNormals();\n},\n\nupdateEdge:function updateEdge(obj){\nvar lineWidth=obj.getData('lineWidth'),\nnodeFrom=obj.nodeFrom,\nnodeTo=obj.nodeTo,\nnodeFromPos=nodeFrom.pos,\nnodeToPos=nodeTo.pos,\ndist=nodeFromPos.distanceTo(nodeToPos),\nmiddle=new Vector3(),\ncurrentDir=new Vector3(0,0,1),\ndvec=new Vector3();\n\nmiddle.add(nodeFromPos,nodeToPos).$scale(0.5);\ndvec.sub(nodeToPos,nodeFromPos).normalize();\n\nvar c=dvec.dot(currentDir),\nxc=dvec.dot(new Vector3(1,0,0)),\nyc=dvec.dot(new Vector3(0,1,0)),\nt=1-c,\nrotAngle=Math.acos(c),\ns=Math.sin(rotAngle),\nrotAxis=currentDir.$cross(dvec).normalize(),\nx=rotAxis.x,\ny=rotAxis.y,\nz=rotAxis.z;\n\nvar rot=new Matrix4();\nrot.n11=t*x*x+c;\nrot.n12=t*x*y-s*z;\nrot.n13=t*x*z+s*y;\nrot.n21=t*x*y+s*z;\nrot.n22=t*y*y+c;\nrot.n23=t*y*z-s*x;\nrot.n31=t*x*z-s*y;\nrot.n32=t*y*z+s*x;\nrot.n33=t*z*z+c;\nthis.rotationMatrix=rot;\nthis.scale.setc(lineWidth,lineWidth,dist);\nthis.position.setc(middle.x,middle.y,middle.z);\nthis.updateMatrix();\n},\n\nupdateMatrix:function updateMatrix(){\nvar pos=this.position,\nscale=this.scale,\nmatrix=this.matrix;\n\nmatrix.identity();\n\nmatrix.$multiply(Matrix4.translationMatrix(pos.x,pos.y,pos.z));\nmatrix.$multiply(this.rotationMatrix);\nmatrix.$multiply(Matrix4.scaleMatrix(scale.x,scale.y,scale.z));\n}});\n\n\n\n\n/*\n * File: Layouts.ForceDirected3D.js\n *\n*/\n\n/*\n * Class: Layouts.ForceDirected3D\n * \n * Implements a Force Directed Layout.\n * \n * Implemented By:\n * \n * <ForceDirected3D>\n * \n */\nLayouts.ForceDirected3D=new Class({\n\ngetOptions:function getOptions(){\nvar s=this.canvas.getSize();\nvar w=s.width,h=s.height;\n//count nodes\nvar count=0;\nthis.graph.eachNode(function(n){\ncount++;\n});\nvar k2=w*h/count,k=Math.sqrt(k2);\nvar l=this.config.levelDistance;\n\nreturn{\nwidth:w,\nheight:h,\ntstart:w*0.1,\nnodef:function nodef(x){return k2/(x||1);},\nedgef:function edgef(x){return(/* x * x / k; */k*(x-l));}};\n\n},\n\ncompute:function compute(property,incremental){\nvar prop=$.splat(property||['current','start','end']);\nvar opt=this.getOptions();\nNodeDim.compute(this.graph,prop,this.config);\nthis.graph.computeLevels(this.root,0,\"ignore\");\nthis.graph.eachNode(function(n){\n$.each(prop,function(p){\nvar pos=n.getPos(p);\nif(pos.isZero()){\npos.x=opt.width/5*(Math.random()-0.5);\npos.y=opt.height/5*(Math.random()-0.5);\npos.z=200*(Math.random()-0.5);\n}\n//initialize disp vector\nn.disp={};\n$.each(prop,function(p){\nn.disp[p]=$V3(0,0,0);\n});\n});\n});\nthis.computePositions(prop,opt,incremental);\n},\n\ncomputePositions:function computePositions(property,opt,incremental){\nvar times=this.config.iterations,i=0,that=this;\nif(incremental){\n(function iter(){\nfor(var total=incremental.iter,j=0;j<total;j++){\nopt.t=opt.tstart*(1-i++/(times-1));\nthat.computePositionStep(property,opt);\nif(i>=times){\nincremental.onComplete();\nreturn;\n}\n}\nincremental.onStep(Math.round(i/(times-1)*100));\nsetTimeout(iter,1);\n})();\n}else{\nfor(;i<times;i++){\nopt.t=opt.tstart*(1-i/(times-1));\nthis.computePositionStep(property,opt);\n}\n}\n},\n\ncomputePositionStep:function computePositionStep(property,opt){\nvar graph=this.graph;\nvar min=Math.min,max=Math.max;\nvar dpos=$V3(0,0,0);\n//calculate repulsive forces\ngraph.eachNode(function(v){\n//initialize disp\n$.each(property,function(p){\nv.disp[p].x=0;\nv.disp[p].y=0;\nv.disp[p].z=0;\n});\ngraph.eachNode(function(u){\nif(u.id!=v.id){\n$.each(property,function(p){\nvar vp=v.getPos(p),up=u.getPos(p);\ndpos.x=vp.x-up.x;\ndpos.y=vp.y-up.y;\ndpos.z=vp.z-up.z;\nvar norm=dpos.norm()||1;\nv.disp[p].$add(dpos.\n$scale(opt.nodef(norm)/norm));\n});\n}\n});\n});\n//calculate attractive forces\nvar T=!!graph.getNode(this.root).visited;\ngraph.eachNode(function(node){\nnode.eachAdjacency(function(adj){\nvar nodeTo=adj.nodeTo;\nif(!!nodeTo.visited===T){\n$.each(property,function(p){\nvar vp=node.getPos(p),up=nodeTo.getPos(p);\ndpos.x=vp.x-up.x;\ndpos.y=vp.y-up.y;\ndpos.z=vp.z-up.z;\nvar norm=dpos.norm()||1;\nnode.disp[p].$add(dpos.$scale(-opt.edgef(norm)/norm));\nnodeTo.disp[p].$add(dpos.$scale(-1));\n});\n}\n});\nnode.visited=!T;\n});\n//arrange positions to fit the canvas\nvar t=opt.t,w2=opt.width/2,h2=opt.height/2;\ngraph.eachNode(function(u){\n$.each(property,function(p){\nvar disp=u.disp[p];\nvar norm=disp.norm()||1;\nvar p=u.getPos(p);\np.$add($V3(disp.x*min(Math.abs(disp.x),t)/norm,\ndisp.y*min(Math.abs(disp.y),t)/norm,\ndisp.z*min(Math.abs(disp.z),t)/norm));\np.x=min(w2,max(-w2,p.x));\np.y=min(h2,max(-h2,p.y));\np.z=min(h2,max(-h2,p.z));\n});\n});\n}});\n\n\n$jit.ForceDirected3D=new Class({\n\nImplements:[Loader,Extras,Layouts.ForceDirected3D],\n\ninitialize:function initialize(controller){\nvar $ForceDirected3D=$jit.ForceDirected3D;\n\nvar config={\niterations:50,\nlevelDistance:50};\n\n\nthis.controller=this.config=$.merge(Options(\"Canvas\",\"Node\",\"Edge\",\n\"Fx\",\"Tips\",\"NodeStyles\",\"Events\",\"Navigation\",\"Controller\",\"Label\"),config,controller);\n\nvar canvasConfig=this.config;\nif(canvasConfig.useCanvas){\nthis.canvas=canvasConfig.useCanvas;\nthis.config.labelContainer=this.canvas.id+'-label';\n}else{\nif(canvasConfig.background){\ncanvasConfig.background=$.merge({\ntype:'Circles'},\ncanvasConfig.background);\n}\nthis.canvas=new Canvas(this,canvasConfig);\nthis.config.labelContainer=(typeof canvasConfig.injectInto=='string'?canvasConfig.injectInto:canvasConfig.injectInto.id)+'-label';\n}\n\nthis.graphOptions={\n'klass':Vector3,\n'Node':{\n'selected':false,\n'exist':true,\n'drawn':true}};\n\n\nthis.graph=new Graph(this.graphOptions,this.config.Node,\nthis.config.Edge);\nthis.labels=new $ForceDirected3D.Label[canvasConfig.Label.type](this);\nthis.fx=new $ForceDirected3D.Plot(this,$ForceDirected3D);\nthis.op=new $ForceDirected3D.Op(this);\nthis.json=null;\nthis.busy=false;\n// initialize extras\nthis.initializeExtras();\n},\n\n/* \n    refresh \n    \n    Computes positions and plots the tree.\n  */\nrefresh:function refresh(){\nthis.compute();\nthis.plot();\n},\n\nreposition:function reposition(){\nthis.compute('end');\n},\n\n/*\n  computeIncremental\n  \n  Performs the Force Directed algorithm incrementally.\n  \n  Description:\n  \n  ForceDirected3D algorithms can perform many computations and lead to JavaScript taking too much time to complete. \n  This method splits the algorithm into smaller parts allowing the user to track the evolution of the algorithm and \n  avoiding browser messages such as \"This script is taking too long to complete\".\n  \n  Parameters:\n  \n  opt - (object) The object properties are described below\n  \n  iter - (number) Default's *20*. Split the algorithm into pieces of _iter_ iterations. For example, if the _iterations_ configuration property \n  of your <ForceDirected3D> class is 100, then you could set _iter_ to 20 to split the main algorithm into 5 smaller pieces.\n  \n  property - (string) Default's *end*. Whether to update starting, current or ending node positions. Possible values are 'end', 'start', 'current'. \n  You can also set an array of these properties. If you'd like to keep the current node positions but to perform these \n  computations for final animation positions then you can just choose 'end'.\n  \n  onStep - (function) A callback function called when each \"small part\" of the algorithm completed. This function gets as first formal \n  parameter a percentage value.\n  \n  onComplete - A callback function called when the algorithm completed.\n  \n  Example:\n  \n  In this example I calculate the end positions and then animate the graph to those positions\n  \n  (start code js)\n  var fd = new $jit.ForceDirected3D(...);\n  fd.computeIncremental({\n    iter: 20,\n    property: 'end',\n    onStep: function(perc) {\n      Log.write(\"loading \" + perc + \"%\");\n    },\n    onComplete: function() {\n      Log.write(\"done\");\n      fd.animate();\n    }\n  });\n  (end code)\n  \n  In this example I calculate all positions and (re)plot the graph\n  \n  (start code js)\n  var fd = new ForceDirected3D(...);\n  fd.computeIncremental({\n    iter: 20,\n    property: ['end', 'start', 'current'],\n    onStep: function(perc) {\n      Log.write(\"loading \" + perc + \"%\");\n    },\n    onComplete: function() {\n      Log.write(\"done\");\n      fd.plot();\n    }\n  });\n  (end code)\n  \n  */\ncomputeIncremental:function computeIncremental(opt){\nopt=$.merge({\niter:20,\nproperty:'end',\nonStep:$.empty,\nonComplete:$.empty},\nopt||{});\n\nthis.config.onBeforeCompute(this.graph.getNode(this.root));\nthis.compute(opt.property,opt);\n},\n\n/*\n    plot\n   \n    Plots the ForceDirected3D graph. This is a shortcut to *fx.plot*.\n   */\nplot:function plot(){\nthis.fx.plot();\n},\n\n/*\n     animate\n    \n     Animates the graph from the current positions to the 'end' node positions.\n  */\nanimate:function animate(opt){\nthis.fx.animate($.merge({\nmodes:['linear']},\nopt||{}));\n}});\n\n\n$jit.ForceDirected3D.$extend=true;\n\n(function(ForceDirected3D){\n\n/*\n     ForceDirected3D.Op\n     \n     Custom extension of <Graph.Op>.\n\n     Extends:\n\n     All <Graph.Op> methods\n     \n     See also:\n     \n     <Graph.Op>\n\n  */\nForceDirected3D.Op=new Class({\n\nImplements:Graph.Op});\n\n\n\n/*\n    ForceDirected3D.Plot\n    \n    Custom extension of <Graph.Plot>.\n  \n    Extends:\n  \n    All <Graph.Plot> methods\n    \n    See also:\n    \n    <Graph.Plot>\n  \n  */\nForceDirected3D.Plot=new Class({\n\nImplements:Graph.Plot3D});\n\n\n\n/*\n    ForceDirected3D.Label\n    \n    Custom extension of <Graph.Label>. \n    Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.\n  \n    Extends:\n  \n    All <Graph.Label> methods and subclasses.\n  \n    See also:\n  \n    <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.\n  \n  */\nForceDirected3D.Label={};\n\n/*\n     ForceDirected3D.Label.Native\n     \n     Custom extension of <Graph.Label.Native>.\n\n     Extends:\n\n     All <Graph.Label.Native> methods\n\n     See also:\n\n     <Graph.Label.Native>\n\n  */\nForceDirected3D.Label.Native=new Class({\nImplements:Graph.Label.Native});\n\n\n/*\n    ForceDirected3D.Label.SVG\n    \n    Custom extension of <Graph.Label.SVG>.\n  \n    Extends:\n  \n    All <Graph.Label.SVG> methods\n  \n    See also:\n  \n    <Graph.Label.SVG>\n  \n  */\nForceDirected3D.Label.SVG=new Class({\nImplements:Graph.Label.SVG,\n\ninitialize:function initialize(viz){\nthis.viz=viz;\n},\n\n/* \n       placeLabel\n\n       Overrides abstract method placeLabel in <Graph.Label>.\n\n       Parameters:\n\n       tag - A DOM label element.\n       node - A <Graph.Node>.\n       controller - A configuration/controller object passed to the visualization.\n      \n     */\nplaceLabel:function placeLabel(tag,node,controller){\nvar pos=node.pos.getc(true),\ncanvas=this.viz.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY,\nradius=canvas.getSize();\nvar labelPos={\nx:Math.round(pos.x*sx+ox+radius.width/2),\ny:Math.round(pos.y*sy+oy+radius.height/2)};\n\ntag.setAttribute('x',labelPos.x);\ntag.setAttribute('y',labelPos.y);\n\ncontroller.onPlaceLabel(tag,node);\n}});\n\n\n/*\n     ForceDirected3D.Label.HTML\n     \n     Custom extension of <Graph.Label.HTML>.\n\n     Extends:\n\n     All <Graph.Label.HTML> methods.\n\n     See also:\n\n     <Graph.Label.HTML>\n\n  */\nForceDirected3D.Label.HTML=new Class({\nImplements:Graph.Label.HTML,\n\ninitialize:function initialize(viz){\nthis.viz=viz;\n},\n/* \n       placeLabel\n\n       Overrides abstract method placeLabel in <Graph.Plot>.\n\n       Parameters:\n\n       tag - A DOM label element.\n       node - A <Graph.Node>.\n       controller - A configuration/controller object passed to the visualization.\n      \n     */\nplaceLabel:function placeLabel(tag,node,controller){\nvar pos=node.pos.getc(true),\ncanvas=this.viz.canvas,\nox=canvas.translateOffsetX,\noy=canvas.translateOffsetY,\nsx=canvas.scaleOffsetX,\nsy=canvas.scaleOffsetY,\nradius=canvas.getSize();\nvar labelPos={\nx:Math.round(pos.x*sx+ox+radius.width/2),\ny:Math.round(pos.y*sy+oy+radius.height/2)};\n\nvar style=tag.style;\nstyle.left=labelPos.x+'px';\nstyle.top=labelPos.y+'px';\nstyle.display=this.fitsInCanvas(labelPos,canvas)?'':'none';\n\ncontroller.onPlaceLabel(tag,node);\n}});\n\n\n/*\n    ForceDirected3D.Plot.NodeTypes\n\n    This class contains a list of <Graph.Node> built-in types. \n    Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.\n\n    You can add your custom node types, customizing your visualization to the extreme.\n\n    Example:\n\n    (start code js)\n      ForceDirected3D.Plot.NodeTypes.implement({\n        'mySpecialType': {\n          'render': function(node, canvas) {\n            //print your custom node to canvas\n          },\n          //optional\n          'contains': function(node, pos) {\n            //return true if pos is inside the node or false otherwise\n          }\n        }\n      });\n    (end code)\n\n  */\nForceDirected3D.Plot.NodeTypes=new Class({\n'none':{\n'render':$.empty,\n'contains':$.lambda(false)},\n\n'circle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.circle.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.circle.contains(npos,pos,dim);\n}},\n\n'ellipse':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nthis.nodeHelper.ellipse.render('fill',pos,width,height,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nreturn this.nodeHelper.ellipse.contains(npos,pos,width,height);\n}},\n\n'square':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.square.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.square.contains(npos,pos,dim);\n}},\n\n'rectangle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nthis.nodeHelper.rectangle.render('fill',pos,width,height,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\nwidth=node.getData('width'),\nheight=node.getData('height');\nreturn this.nodeHelper.rectangle.contains(npos,pos,width,height);\n}},\n\n'triangle':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.triangle.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.triangle.contains(npos,pos,dim);\n}},\n\n'star':{\n'render':function render(node,canvas){\nvar pos=node.pos.getc(true),\ndim=node.getData('dim');\nthis.nodeHelper.star.render('fill',pos,dim,canvas);\n},\n'contains':function contains(node,pos){\nvar npos=node.pos.getc(true),\ndim=node.getData('dim');\nreturn this.nodeHelper.star.contains(npos,pos,dim);\n}}});\n\n\n\n/*\n    ForceDirected3D.Plot.EdgeTypes\n  \n    This class contains a list of <Graph.Adjacence> built-in types. \n    Edge types implemented are 'none', 'line' and 'arrow'.\n  \n    You can add your custom edge types, customizing your visualization to the extreme.\n  \n    Example:\n  \n    (start code js)\n      ForceDirected3D.Plot.EdgeTypes.implement({\n        'mySpecialType': {\n          'render': function(adj, canvas) {\n            //print your custom edge to canvas\n          },\n          //optional\n          'contains': function(adj, pos) {\n            //return true if pos is inside the arc or false otherwise\n          }\n        }\n      });\n    (end code)\n  \n  */\nForceDirected3D.Plot.EdgeTypes=new Class({\n'none':$.empty,\n'line':{\n'render':function render(adj,canvas){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nthis.edgeHelper.line.render(from,to,canvas);\n},\n'contains':function contains(adj,pos){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nreturn this.edgeHelper.line.contains(from,to,pos,this.edge.epsilon);\n}},\n\n'arrow':{\n'render':function render(adj,canvas){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true),\ndim=adj.getData('dim'),\ndirection=adj.data.$direction,\ninv=direction&&direction.length>1&&direction[0]!=adj.nodeFrom.id;\nthis.edgeHelper.arrow.render(from,to,dim,inv,canvas);\n},\n'contains':function contains(adj,pos){\nvar from=adj.nodeFrom.pos.getc(true),\nto=adj.nodeTo.pos.getc(true);\nreturn this.edgeHelper.arrow.contains(from,to,pos,this.edge.epsilon);\n}}});\n\n\n\n})($jit.ForceDirected3D);\n\n// START METAMAPS CODE\nexports.default=$jit;\n// END METAMAPS CODE//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMTcwLmpzIiwic291cmNlcyI6WyJmcm9udGVuZC9zcmMvcGF0Y2hlZC9KSVQuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAoYykgMjAxMSBTZW5jaGEgSW5jLiAtIEF1dGhvcjogTmljb2xhcyBHYXJjaWEgQmVsbW9udGUgKGh0dHA6Ly9waGlsb2diLmdpdGh1Yi5jb20vKVxuXG5QZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG5vZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSBcIlNvZnR3YXJlXCIpLCB0byBkZWFsXG5pbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzXG50byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsXG5jb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXNcbmZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6XG5cblRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluXG5hbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS5cblxuVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUlxuSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksXG5GSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbkFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVJcbkxJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sXG5PVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG5USEUgU09GVFdBUkUuXG5cbiAqL1xuXG4vKlxuICBGaWxlOiBDb3JlLmpzXG5cbiAqL1xuXG4vKlxuIE9iamVjdDogJGppdFxuIFxuIERlZmluZXMgdGhlIG5hbWVzcGFjZSBmb3IgYWxsIGxpYnJhcnkgQ2xhc3NlcyBhbmQgT2JqZWN0cy4gXG4gVGhpcyB2YXJpYWJsZSBpcyB0aGUgKm9ubHkqIGdsb2JhbCB2YXJpYWJsZSBkZWZpbmVkIGluIHRoZSBUb29sa2l0LiBcbiBUaGVyZSBhcmUgYWxzbyBvdGhlciBpbnRlcmVzdGluZyBwcm9wZXJ0aWVzIGF0dGFjaGVkIHRvIHRoaXMgdmFyaWFibGUgZGVzY3JpYmVkIGJlbG93LlxuICovXG4vLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG5jb25zdCAkaml0ID0gZnVuY3Rpb24odykge1xuLy8gT1JJR0lOQUw6XG4vLyB3aW5kb3cuJGppdCA9IGZ1bmN0aW9uKHcpIHtcbi8vIEVORCBNRVRBTUFQUyBDT0RFXG4gIHcgPSB3IHx8IHdpbmRvdztcbiAgZm9yKHZhciBrIGluICRqaXQpIHtcbiAgICBpZigkaml0W2tdLiRleHRlbmQpIHtcbiAgICAgIHdba10gPSAkaml0W2tdO1xuICAgIH1cbiAgfVxufTtcblxuJGppdC52ZXJzaW9uID0gJzIuMC4xJztcbi8qXG4gIE9iamVjdDogJGppdC5pZFxuICBcbiAgV29ya3MganVzdCBsaWtlICpkb2N1bWVudC5nZXRFbGVtZW50QnlJZCpcbiAgXG4gIEV4YW1wbGU6XG4gIChzdGFydCBjb2RlIGpzKVxuICB2YXIgZWxlbWVudCA9ICRqaXQuaWQoJ2VsZW1lbnRJZCcpO1xuICAoZW5kIGNvZGUpXG5cbiovXG5cbi8qXG4gT2JqZWN0OiAkaml0LnV0aWxcbiBcbiBDb250YWlucyB1dGlsaXR5IGZ1bmN0aW9ucy5cbiBcbiBTb21lIG9mIHRoZSB1dGlsaXR5IGZ1bmN0aW9ucyBhbmQgdGhlIENsYXNzIHN5c3RlbSB3ZXJlIGJhc2VkIGluIHRoZSBNb29Ub29scyBGcmFtZXdvcmsgXG4gPGh0dHA6Ly9tb290b29scy5uZXQ+LiBDb3B5cmlnaHQgKGMpIDIwMDYtMjAxMCBWYWxlcmlvIFByb2lldHRpLCA8aHR0cDovL21hZDRtaWxrLm5ldC8+LiBcbiBNSVQgbGljZW5zZSA8aHR0cDovL21vb3Rvb2xzLm5ldC9saWNlbnNlLnR4dD4uXG4gXG4gVGhlc2UgbWV0aG9kcyBhcmUgZ2VuZXJhbGx5IGFsc28gaW1wbGVtZW50ZWQgaW4gRE9NIG1hbmlwdWxhdGlvbiBmcmFtZXdvcmtzIGxpa2UgSlF1ZXJ5LCBNb29Ub29scyBhbmQgUHJvdG90eXBlLlxuIEknZCBzdWdnZXN0IHlvdSB0byB1c2UgdGhlIGZ1bmN0aW9ucyBmcm9tIHRob3NlIGxpYnJhcmllcyBpbnN0ZWFkIG9mIHVzaW5nIHRoZXNlLCBzaW5jZSB0aGVpciBmdW5jdGlvbnMgXG4gYXJlIHdpZGVseSB1c2VkIGFuZCB0ZXN0ZWQgaW4gbWFueSBkaWZmZXJlbnQgcGxhdGZvcm1zL2Jyb3dzZXJzLiBVc2UgdGhlc2UgZnVuY3Rpb25zIG9ubHkgaWYgeW91IGhhdmUgdG8uXG4gXG4gKi9cbnZhciAkID0gZnVuY3Rpb24oZCkge1xuICByZXR1cm4gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoZCk7XG59O1xuXG4kLmVtcHR5ID0gZnVuY3Rpb24oKSB7XG59O1xuXG4vKlxuICBNZXRob2Q6IGV4dGVuZFxuICBcbiAgQXVnbWVudCBhbiBvYmplY3QgYnkgYXBwZW5kaW5nIGFub3RoZXIgb2JqZWN0J3MgcHJvcGVydGllcy5cbiAgXG4gIFBhcmFtZXRlcnM6XG4gIFxuICBvcmlnaW5hbCAtIChvYmplY3QpIFRoZSBvYmplY3QgdG8gYmUgZXh0ZW5kZWQuXG4gIGV4dGVuZGVkIC0gKG9iamVjdCkgQW4gb2JqZWN0IHdoaWNoIHByb3BlcnRpZXMgYXJlIGdvaW5nIHRvIGJlIGFwcGVuZGVkIHRvIHRoZSBvcmlnaW5hbCBvYmplY3QuXG4gIFxuICBFeGFtcGxlOlxuICAoc3RhcnQgY29kZSBqcylcbiAgJGppdC51dGlsLmV4dGVuZCh7ICdhJzogMSwgJ2InOiAyIH0sIHsgJ2InOiAzLCAnYyc6IDQgfSk7IC8veyAnYSc6MSwgJ2InOiAzLCAnYyc6IDQgfVxuICAoZW5kIGNvZGUpXG4qL1xuJC5leHRlbmQgPSBmdW5jdGlvbihvcmlnaW5hbCwgZXh0ZW5kZWQpIHtcbiAgZm9yICggdmFyIGtleSBpbiAoZXh0ZW5kZWQgfHwge30pKVxuICAgIG9yaWdpbmFsW2tleV0gPSBleHRlbmRlZFtrZXldO1xuICByZXR1cm4gb3JpZ2luYWw7XG59O1xuXG4kLmxhbWJkYSA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gIHJldHVybiAodHlwZW9mIHZhbHVlID09ICdmdW5jdGlvbicpID8gdmFsdWUgOiBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH07XG59O1xuXG4kLnRpbWUgPSBEYXRlLm5vdyB8fCBmdW5jdGlvbigpIHtcbiAgcmV0dXJuICtuZXcgRGF0ZTtcbn07XG5cbi8qXG4gIE1ldGhvZDogc3BsYXRcbiAgXG4gIFJldHVybnMgYW4gYXJyYXkgd3JhcHBpbmcgKm9iaiogaWYgKm9iaiogaXMgbm90IGFuIGFycmF5LiBSZXR1cm5zICpvYmoqIG90aGVyd2lzZS5cbiAgXG4gIFBhcmFtZXRlcnM6XG4gIFxuICBvYmogLSAobWl4ZWQpIFRoZSBvYmplY3QgdG8gYmUgd3JhcHBlZCBpbiBhbiBhcnJheS5cbiAgXG4gIEV4YW1wbGU6XG4gIChzdGFydCBjb2RlIGpzKVxuICAkaml0LnV0aWwuc3BsYXQoMyk7ICAgLy9bM11cbiAgJGppdC51dGlsLnNwbGF0KFszXSk7IC8vWzNdXG4gIChlbmQgY29kZSlcbiovXG4kLnNwbGF0ID0gZnVuY3Rpb24ob2JqKSB7XG4gIHZhciB0eXBlID0gJC50eXBlKG9iaik7XG4gIHJldHVybiB0eXBlID8gKCh0eXBlICE9ICdhcnJheScpID8gWyBvYmogXSA6IG9iaikgOiBbXTtcbn07XG5cbiQudHlwZSA9IGZ1bmN0aW9uKGVsZW0pIHtcbiAgdmFyIHR5cGUgPSAkLnR5cGUucy5jYWxsKGVsZW0pLm1hdGNoKC9eXFxbb2JqZWN0XFxzKC4qKVxcXSQvKVsxXS50b0xvd2VyQ2FzZSgpO1xuICBpZih0eXBlICE9ICdvYmplY3QnKSByZXR1cm4gdHlwZTtcbiAgaWYoZWxlbSAmJiBlbGVtLiQkZmFtaWx5KSByZXR1cm4gZWxlbS4kJGZhbWlseTtcbiAgcmV0dXJuIChlbGVtICYmIGVsZW0ubm9kZU5hbWUgJiYgZWxlbS5ub2RlVHlwZSA9PSAxKT8gJ2VsZW1lbnQnIDogdHlwZTtcbn07XG4kLnR5cGUucyA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7XG5cbi8qXG4gIE1ldGhvZDogZWFjaFxuICBcbiAgSXRlcmF0ZXMgdGhyb3VnaCBhbiBpdGVyYWJsZSBhcHBseWluZyAqZiouXG4gIFxuICBQYXJhbWV0ZXJzOlxuICBcbiAgaXRlcmFibGUgLSAoYXJyYXkpIFRoZSBvcmlnaW5hbCBhcnJheS5cbiAgZm4gLSAoZnVuY3Rpb24pIFRoZSBmdW5jdGlvbiB0byBhcHBseSB0byB0aGUgYXJyYXkgZWxlbWVudHMuXG4gIFxuICBFeGFtcGxlOlxuICAoc3RhcnQgY29kZSBqcylcbiAgJGppdC51dGlsLmVhY2goWzMsIDQsIDVdLCBmdW5jdGlvbihuKSB7IGFsZXJ0KCdudW1iZXIgJyArIG4pOyB9KTtcbiAgKGVuZCBjb2RlKVxuKi9cbiQuZWFjaCA9IGZ1bmN0aW9uKGl0ZXJhYmxlLCBmbikge1xuICB2YXIgdHlwZSA9ICQudHlwZShpdGVyYWJsZSk7XG4gIGlmICh0eXBlID09ICdvYmplY3QnKSB7XG4gICAgZm9yICggdmFyIGtleSBpbiBpdGVyYWJsZSlcbiAgICAgIGZuKGl0ZXJhYmxlW2tleV0sIGtleSk7XG4gIH0gZWxzZSB7XG4gICAgZm9yICggdmFyIGkgPSAwLCBsID0gaXRlcmFibGUubGVuZ3RoOyBpIDwgbDsgaSsrKVxuICAgICAgZm4oaXRlcmFibGVbaV0sIGkpO1xuICB9XG59O1xuXG4kLmluZGV4T2YgPSBmdW5jdGlvbihhcnJheSwgaXRlbSkge1xuICBpZihBcnJheS5pbmRleE9mKSByZXR1cm4gYXJyYXkuaW5kZXhPZihpdGVtKTtcbiAgZm9yKHZhciBpPTAsbD1hcnJheS5sZW5ndGg7IGk8bDsgaSsrKSB7XG4gICAgaWYoYXJyYXlbaV0gPT09IGl0ZW0pIHJldHVybiBpO1xuICB9XG4gIHJldHVybiAtMTtcbn07XG5cbi8qXG4gIE1ldGhvZDogbWFwXG4gIFxuICBNYXBzIG9yIGNvbGxlY3RzIGFuIGFycmF5IGJ5IGFwcGx5aW5nICpmKi5cbiAgXG4gIFBhcmFtZXRlcnM6XG4gIFxuICBhcnJheSAtIChhcnJheSkgVGhlIG9yaWdpbmFsIGFycmF5LlxuICBmIC0gKGZ1bmN0aW9uKSBUaGUgZnVuY3Rpb24gdG8gYXBwbHkgdG8gdGhlIGFycmF5IGVsZW1lbnRzLlxuICBcbiAgRXhhbXBsZTpcbiAgKHN0YXJ0IGNvZGUganMpXG4gICRqaXQudXRpbC5tYXAoWzMsIDQsIDVdLCBmdW5jdGlvbihuKSB7IHJldHVybiBuKm47IH0pOyAvL1s5LCAxNiwgMjVdXG4gIChlbmQgY29kZSlcbiovXG4kLm1hcCA9IGZ1bmN0aW9uKGFycmF5LCBmKSB7XG4gIHZhciBhbnMgPSBbXTtcbiAgJC5lYWNoKGFycmF5LCBmdW5jdGlvbihlbGVtLCBpKSB7XG4gICAgYW5zLnB1c2goZihlbGVtLCBpKSk7XG4gIH0pO1xuICByZXR1cm4gYW5zO1xufTtcblxuLypcbiAgTWV0aG9kOiByZWR1Y2VcbiAgXG4gIEl0ZXJhdGl2ZWx5IGFwcGxpZXMgdGhlIGJpbmFyeSBmdW5jdGlvbiAqZiogc3RvcmluZyB0aGUgcmVzdWx0IGluIGFuIGFjY3VtdWxhdG9yLlxuICBcbiAgUGFyYW1ldGVyczpcbiAgXG4gIGFycmF5IC0gKGFycmF5KSBUaGUgb3JpZ2luYWwgYXJyYXkuXG4gIGYgLSAoZnVuY3Rpb24pIFRoZSBmdW5jdGlvbiB0byBhcHBseSB0byB0aGUgYXJyYXkgZWxlbWVudHMuXG4gIG9wdCAtIChvcHRpb25hbHxtaXhlZCkgVGhlIHN0YXJ0aW5nIHZhbHVlIGZvciB0aGUgYWN1bXVsYXRvci5cbiAgXG4gIEV4YW1wbGU6XG4gIChzdGFydCBjb2RlIGpzKVxuICAkaml0LnV0aWwucmVkdWNlKFszLCA0LCA1XSwgZnVuY3Rpb24oeCwgeSkgeyByZXR1cm4geCArIHk7IH0sIDApOyAvLzEyXG4gIChlbmQgY29kZSlcbiovXG4kLnJlZHVjZSA9IGZ1bmN0aW9uKGFycmF5LCBmLCBvcHQpIHtcbiAgdmFyIGwgPSBhcnJheS5sZW5ndGg7XG4gIGlmKGw9PTApIHJldHVybiBvcHQ7XG4gIHZhciBhY3VtID0gYXJndW1lbnRzLmxlbmd0aCA9PSAzPyBvcHQgOiBhcnJheVstLWxdO1xuICB3aGlsZShsLS0pIHtcbiAgICBhY3VtID0gZihhY3VtLCBhcnJheVtsXSk7XG4gIH1cbiAgcmV0dXJuIGFjdW07XG59O1xuXG4vKlxuICBNZXRob2Q6IG1lcmdlXG4gIFxuICBNZXJnZXMgbi1vYmplY3RzIGFuZCB0aGVpciBzdWItb2JqZWN0cyBjcmVhdGluZyBhIG5ldywgZnJlc2ggb2JqZWN0LlxuICBcbiAgUGFyYW1ldGVyczpcbiAgXG4gIEFuIGFyYml0cmFyeSBudW1iZXIgb2Ygb2JqZWN0cy5cbiAgXG4gIEV4YW1wbGU6XG4gIChzdGFydCBjb2RlIGpzKVxuICAkaml0LnV0aWwubWVyZ2UoeyAnYSc6IDEsICdiJzogMiB9LCB7ICdiJzogMywgJ2MnOiA0IH0pOyAvL3sgJ2EnOjEsICdiJzogMywgJ2MnOiA0IH1cbiAgKGVuZCBjb2RlKVxuKi9cbiQubWVyZ2UgPSBmdW5jdGlvbigpIHtcbiAgdmFyIG1peCA9IHt9O1xuICBmb3IgKCB2YXIgaSA9IDAsIGwgPSBhcmd1bWVudHMubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgdmFyIG9iamVjdCA9IGFyZ3VtZW50c1tpXTtcbiAgICBpZiAoJC50eXBlKG9iamVjdCkgIT0gJ29iamVjdCcpXG4gICAgICBjb250aW51ZTtcbiAgICBmb3IgKCB2YXIga2V5IGluIG9iamVjdCkge1xuICAgICAgdmFyIG9wID0gb2JqZWN0W2tleV0sIG1wID0gbWl4W2tleV07XG4gICAgICBtaXhba2V5XSA9IChtcCAmJiAkLnR5cGUob3ApID09ICdvYmplY3QnICYmICQudHlwZShtcCkgPT0gJ29iamVjdCcpID8gJFxuICAgICAgICAgIC5tZXJnZShtcCwgb3ApIDogJC51bmxpbmsob3ApO1xuICAgIH1cbiAgfVxuICByZXR1cm4gbWl4O1xufTtcblxuJC51bmxpbmsgPSBmdW5jdGlvbihvYmplY3QpIHtcbiAgdmFyIHVubGlua2VkO1xuICBzd2l0Y2ggKCQudHlwZShvYmplY3QpKSB7XG4gIGNhc2UgJ29iamVjdCc6XG4gICAgdW5saW5rZWQgPSB7fTtcbiAgICBmb3IgKCB2YXIgcCBpbiBvYmplY3QpXG4gICAgICB1bmxpbmtlZFtwXSA9ICQudW5saW5rKG9iamVjdFtwXSk7XG4gICAgYnJlYWs7XG4gIGNhc2UgJ2FycmF5JzpcbiAgICB1bmxpbmtlZCA9IFtdO1xuICAgIGZvciAoIHZhciBpID0gMCwgbCA9IG9iamVjdC5sZW5ndGg7IGkgPCBsOyBpKyspXG4gICAgICB1bmxpbmtlZFtpXSA9ICQudW5saW5rKG9iamVjdFtpXSk7XG4gICAgYnJlYWs7XG4gIGRlZmF1bHQ6XG4gICAgcmV0dXJuIG9iamVjdDtcbiAgfVxuICByZXR1cm4gdW5saW5rZWQ7XG59O1xuXG4kLnppcCA9IGZ1bmN0aW9uKCkge1xuICBpZihhcmd1bWVudHMubGVuZ3RoID09PSAwKSByZXR1cm4gW107XG4gIGZvcih2YXIgaj0wLCBhbnM9W10sIGw9YXJndW1lbnRzLmxlbmd0aCwgbWw9YXJndW1lbnRzWzBdLmxlbmd0aDsgajxtbDsgaisrKSB7XG4gICAgZm9yKHZhciBpPTAsIHJvdz1bXTsgaTxsOyBpKyspIHtcbiAgICAgIHJvdy5wdXNoKGFyZ3VtZW50c1tpXVtqXSk7XG4gICAgfVxuICAgIGFucy5wdXNoKHJvdyk7XG4gIH1cbiAgcmV0dXJuIGFucztcbn07XG5cbi8qXG4gIE1ldGhvZDogcmdiVG9IZXhcbiAgXG4gIENvbnZlcnRzIGFuIFJHQiBhcnJheSBpbnRvIGEgSGV4IHN0cmluZy5cbiAgXG4gIFBhcmFtZXRlcnM6XG4gIFxuICBzcmNBcnJheSAtIChhcnJheSkgQW4gYXJyYXkgd2l0aCBSLCBHIGFuZCBCIHZhbHVlc1xuICBcbiAgRXhhbXBsZTpcbiAgKHN0YXJ0IGNvZGUganMpXG4gICRqaXQudXRpbC5yZ2JUb0hleChbMjU1LCAyNTUsIDI1NV0pOyAvLycjZmZmZmZmJ1xuICAoZW5kIGNvZGUpXG4qL1xuJC5yZ2JUb0hleCA9IGZ1bmN0aW9uKHNyY0FycmF5LCBhcnJheSkge1xuICBpZiAoc3JjQXJyYXkubGVuZ3RoIDwgMylcbiAgICByZXR1cm4gbnVsbDtcbiAgaWYgKHNyY0FycmF5Lmxlbmd0aCA9PSA0ICYmIHNyY0FycmF5WzNdID09IDAgJiYgIWFycmF5KVxuICAgIHJldHVybiAndHJhbnNwYXJlbnQnO1xuICB2YXIgaGV4ID0gW107XG4gIGZvciAoIHZhciBpID0gMDsgaSA8IDM7IGkrKykge1xuICAgIHZhciBiaXQgPSAoc3JjQXJyYXlbaV0gLSAwKS50b1N0cmluZygxNik7XG4gICAgaGV4LnB1c2goYml0Lmxlbmd0aCA9PSAxID8gJzAnICsgYml0IDogYml0KTtcbiAgfVxuICByZXR1cm4gYXJyYXkgPyBoZXggOiAnIycgKyBoZXguam9pbignJyk7XG59O1xuXG4vKlxuICBNZXRob2Q6IGhleFRvUmdiXG4gIFxuICBDb252ZXJ0cyBhbiBIZXggY29sb3Igc3RyaW5nIGludG8gYW4gUkdCIGFycmF5LlxuICBcbiAgUGFyYW1ldGVyczpcbiAgXG4gIGhleCAtIChzdHJpbmcpIEEgY29sb3IgaGV4IHN0cmluZy5cbiAgXG4gIEV4YW1wbGU6XG4gIChzdGFydCBjb2RlIGpzKVxuICAkaml0LnV0aWwuaGV4VG9SZ2IoJyNmZmYnKTsgLy9bMjU1LCAyNTUsIDI1NV1cbiAgKGVuZCBjb2RlKVxuKi9cbiQuaGV4VG9SZ2IgPSBmdW5jdGlvbihoZXgpIHtcbiAgaWYgKGhleC5sZW5ndGggIT0gNykge1xuICAgIGhleCA9IGhleC5tYXRjaCgvXiM/KFxcd3sxLDJ9KShcXHd7MSwyfSkoXFx3ezEsMn0pJC8pO1xuICAgIGhleC5zaGlmdCgpO1xuICAgIGlmIChoZXgubGVuZ3RoICE9IDMpXG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB2YXIgcmdiID0gW107XG4gICAgZm9yICggdmFyIGkgPSAwOyBpIDwgMzsgaSsrKSB7XG4gICAgICB2YXIgdmFsdWUgPSBoZXhbaV07XG4gICAgICBpZiAodmFsdWUubGVuZ3RoID09IDEpXG4gICAgICAgIHZhbHVlICs9IHZhbHVlO1xuICAgICAgcmdiLnB1c2gocGFyc2VJbnQodmFsdWUsIDE2KSk7XG4gICAgfVxuICAgIHJldHVybiByZ2I7XG4gIH0gZWxzZSB7XG4gICAgaGV4ID0gcGFyc2VJbnQoaGV4LnNsaWNlKDEpLCAxNik7XG4gICAgcmV0dXJuIFsgaGV4ID4+IDE2LCBoZXggPj4gOCAmIDB4ZmYsIGhleCAmIDB4ZmYgXTtcbiAgfVxufTtcblxuJC5kZXN0cm95ID0gZnVuY3Rpb24oZWxlbSkge1xuICAkLmNsZWFuKGVsZW0pO1xuICBpZiAoZWxlbS5wYXJlbnROb2RlKVxuICAgIGVsZW0ucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChlbGVtKTtcbiAgaWYgKGVsZW0uY2xlYXJBdHRyaWJ1dGVzKVxuICAgIGVsZW0uY2xlYXJBdHRyaWJ1dGVzKCk7XG59O1xuXG4kLmNsZWFuID0gZnVuY3Rpb24oZWxlbSkge1xuICBmb3IgKHZhciBjaCA9IGVsZW0uY2hpbGROb2RlcywgaSA9IDAsIGwgPSBjaC5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAkLmRlc3Ryb3koY2hbaV0pO1xuICB9XG59O1xuXG4vKlxuICBNZXRob2Q6IGFkZEV2ZW50XG4gIFxuICBDcm9zcy1icm93c2VyIGFkZCBldmVudCBsaXN0ZW5lci5cbiAgXG4gIFBhcmFtZXRlcnM6XG4gIFxuICBvYmogLSAob2JqKSBUaGUgRWxlbWVudCB0byBhdHRhY2ggdGhlIGxpc3RlbmVyIHRvLlxuICB0eXBlIC0gKHN0cmluZykgVGhlIGxpc3RlbmVyIHR5cGUuIEZvciBleGFtcGxlICdjbGljaycsIG9yICdtb3VzZW1vdmUnLlxuICBmbiAtIChmdW5jdGlvbikgVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIHVzZWQgd2hlbiB0aGUgZXZlbnQgaXMgZmlyZWQuXG4gIFxuICBFeGFtcGxlOlxuICAoc3RhcnQgY29kZSBqcylcbiAgJGppdC51dGlsLmFkZEV2ZW50KGVsZW0sICdjbGljaycsIGZ1bmN0aW9uKCl7IGFsZXJ0KCdoZWxsbycpOyB9KTtcbiAgKGVuZCBjb2RlKVxuKi9cbiQuYWRkRXZlbnQgPSBmdW5jdGlvbihvYmosIHR5cGUsIGZuKSB7XG4gIGlmIChvYmouYWRkRXZlbnRMaXN0ZW5lcilcbiAgICBvYmouYWRkRXZlbnRMaXN0ZW5lcih0eXBlLCBmbiwgZmFsc2UpO1xuICBlbHNlXG4gICAgb2JqLmF0dGFjaEV2ZW50KCdvbicgKyB0eXBlLCBmbik7XG59O1xuXG4kLmFkZEV2ZW50cyA9IGZ1bmN0aW9uKG9iaiwgdHlwZU9iaikge1xuICBmb3IodmFyIHR5cGUgaW4gdHlwZU9iaikge1xuICAgICQuYWRkRXZlbnQob2JqLCB0eXBlLCB0eXBlT2JqW3R5cGVdKTtcbiAgfVxufTtcblxuJC5oYXNDbGFzcyA9IGZ1bmN0aW9uKG9iaiwga2xhc3MpIHtcbiAgcmV0dXJuICgnICcgKyBvYmouY2xhc3NOYW1lICsgJyAnKS5pbmRleE9mKCcgJyArIGtsYXNzICsgJyAnKSA+IC0xO1xufTtcblxuJC5hZGRDbGFzcyA9IGZ1bmN0aW9uKG9iaiwga2xhc3MpIHtcbiAgaWYgKCEkLmhhc0NsYXNzKG9iaiwga2xhc3MpKVxuICAgIG9iai5jbGFzc05hbWUgPSAob2JqLmNsYXNzTmFtZSArIFwiIFwiICsga2xhc3MpO1xufTtcblxuJC5yZW1vdmVDbGFzcyA9IGZ1bmN0aW9uKG9iaiwga2xhc3MpIHtcbiAgb2JqLmNsYXNzTmFtZSA9IG9iai5jbGFzc05hbWUucmVwbGFjZShuZXcgUmVnRXhwKFxuICAgICAgJyhefFxcXFxzKScgKyBrbGFzcyArICcoPzpcXFxcc3wkKScpLCAnJDEnKTtcbn07XG5cbiQuZ2V0UG9zID0gZnVuY3Rpb24oZWxlbSkge1xuICB2YXIgb2Zmc2V0ID0gZ2V0T2Zmc2V0cyhlbGVtKTtcbiAgdmFyIHNjcm9sbCA9IGdldFNjcm9sbHMoZWxlbSk7XG4gIHJldHVybiB7XG4gICAgeDogb2Zmc2V0LnggLSBzY3JvbGwueCxcbiAgICB5OiBvZmZzZXQueSAtIHNjcm9sbC55XG4gIH07XG5cbiAgZnVuY3Rpb24gZ2V0T2Zmc2V0cyhlbGVtKSB7XG4gICAgdmFyIHBvc2l0aW9uID0ge1xuICAgICAgeDogMCxcbiAgICAgIHk6IDBcbiAgICB9O1xuICAgIHdoaWxlIChlbGVtICYmICFpc0JvZHkoZWxlbSkpIHtcbiAgICAgIHBvc2l0aW9uLnggKz0gZWxlbS5vZmZzZXRMZWZ0O1xuICAgICAgcG9zaXRpb24ueSArPSBlbGVtLm9mZnNldFRvcDtcbiAgICAgIGVsZW0gPSBlbGVtLm9mZnNldFBhcmVudDtcbiAgICB9XG4gICAgcmV0dXJuIHBvc2l0aW9uO1xuICB9XG5cbiAgZnVuY3Rpb24gZ2V0U2Nyb2xscyhlbGVtKSB7XG4gICAgdmFyIHBvc2l0aW9uID0ge1xuICAgICAgeDogMCxcbiAgICAgIHk6IDBcbiAgICB9O1xuICAgIHdoaWxlIChlbGVtICYmICFpc0JvZHkoZWxlbSkpIHtcbiAgICAgIHBvc2l0aW9uLnggKz0gZWxlbS5zY3JvbGxMZWZ0O1xuICAgICAgcG9zaXRpb24ueSArPSBlbGVtLnNjcm9sbFRvcDtcbiAgICAgIGVsZW0gPSBlbGVtLnBhcmVudE5vZGU7XG4gICAgfVxuICAgIHJldHVybiBwb3NpdGlvbjtcbiAgfVxuXG4gIGZ1bmN0aW9uIGlzQm9keShlbGVtZW50KSB7XG4gICAgcmV0dXJuICgvXig/OmJvZHl8aHRtbCkkL2kpLnRlc3QoZWxlbWVudC50YWdOYW1lKTtcbiAgfVxufTtcblxuJC5ldmVudCA9IHtcbiAgZ2V0OiBmdW5jdGlvbihlLCB3aW4pIHtcbiAgICB3aW4gPSB3aW4gfHwgd2luZG93O1xuICAgIHJldHVybiBlIHx8IHdpbi5ldmVudDtcbiAgfSxcbiAgZ2V0V2hlZWw6IGZ1bmN0aW9uKGUpIHtcbiAgICByZXR1cm4gZS53aGVlbERlbHRhPyBlLndoZWVsRGVsdGEgLyAxMjAgOiAtKGUuZGV0YWlsIHx8IDApIC8gMztcbiAgfSxcbiAgaXNSaWdodENsaWNrOiBmdW5jdGlvbihlKSB7XG4gICAgcmV0dXJuIChlLndoaWNoID09IDMgfHwgZS5idXR0b24gPT0gMik7XG4gIH0sXG4gIGdldFBvczogZnVuY3Rpb24oZSwgd2luKSB7XG4gICAgLy8gZ2V0IG1vdXNlIHBvc2l0aW9uXG4gICAgd2luID0gd2luIHx8IHdpbmRvdztcbiAgICBlID0gZSB8fCB3aW4uZXZlbnQ7XG4gICAgdmFyIGRvYyA9IHdpbi5kb2N1bWVudDtcbiAgICBkb2MgPSBkb2MuZG9jdW1lbnRFbGVtZW50IHx8IGRvYy5ib2R5O1xuICAgIC8vVE9ETyhuaWNvKTogbWFrZSB0b3VjaCBldmVudCBoYW5kbGluZyBiZXR0ZXJcbiAgICBpZihlLnRvdWNoZXMgJiYgZS50b3VjaGVzLmxlbmd0aCkge1xuICAgICAgZSA9IGUudG91Y2hlc1swXTtcbiAgICB9XG4gICAgdmFyIHBhZ2UgPSB7XG4gICAgICB4OiBlLnBhZ2VYIHx8IChlLmNsaWVudFggKyBkb2Muc2Nyb2xsTGVmdCksXG4gICAgICB5OiBlLnBhZ2VZIHx8IChlLmNsaWVudFkgKyBkb2Muc2Nyb2xsVG9wKVxuICAgIH07XG4gICAgcmV0dXJuIHBhZ2U7XG4gIH0sXG4gIHN0b3A6IGZ1bmN0aW9uKGUpIHtcbiAgICBpZiAoZS5zdG9wUHJvcGFnYXRpb24pIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgZS5jYW5jZWxCdWJibGUgPSB0cnVlO1xuICAgIGlmIChlLnByZXZlbnREZWZhdWx0KSBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgZWxzZSBlLnJldHVyblZhbHVlID0gZmFsc2U7XG4gIH1cbn07XG5cbiRqaXQudXRpbCA9ICRqaXQuaWQgPSAkO1xuXG52YXIgQ2xhc3MgPSBmdW5jdGlvbihwcm9wZXJ0aWVzKSB7XG4gIHByb3BlcnRpZXMgPSBwcm9wZXJ0aWVzIHx8IHt9O1xuICB2YXIga2xhc3MgPSBmdW5jdGlvbigpIHtcbiAgICBmb3IgKCB2YXIga2V5IGluIHRoaXMpIHtcbiAgICAgIGlmICh0eXBlb2YgdGhpc1trZXldICE9ICdmdW5jdGlvbicpXG4gICAgICAgIHRoaXNba2V5XSA9ICQudW5saW5rKHRoaXNba2V5XSk7XG4gICAgfVxuICAgIHRoaXMuY29uc3RydWN0b3IgPSBrbGFzcztcbiAgICBpZiAoQ2xhc3MucHJvdG90eXBpbmcpXG4gICAgICByZXR1cm4gdGhpcztcbiAgICB2YXIgaW5zdGFuY2UgPSB0aGlzLmluaXRpYWxpemUgPyB0aGlzLmluaXRpYWxpemUuYXBwbHkodGhpcywgYXJndW1lbnRzKVxuICAgICAgICA6IHRoaXM7XG4gICAgLy90eXBpemVcbiAgICB0aGlzLiQkZmFtaWx5ID0gJ2NsYXNzJztcbiAgICByZXR1cm4gaW5zdGFuY2U7XG4gIH07XG5cbiAgZm9yICggdmFyIG11dGF0b3IgaW4gQ2xhc3MuTXV0YXRvcnMpIHtcbiAgICBpZiAoIXByb3BlcnRpZXNbbXV0YXRvcl0pXG4gICAgICBjb250aW51ZTtcbiAgICBwcm9wZXJ0aWVzID0gQ2xhc3MuTXV0YXRvcnNbbXV0YXRvcl0ocHJvcGVydGllcywgcHJvcGVydGllc1ttdXRhdG9yXSk7XG4gICAgZGVsZXRlIHByb3BlcnRpZXNbbXV0YXRvcl07XG4gIH1cblxuICAkLmV4dGVuZChrbGFzcywgdGhpcyk7XG4gIGtsYXNzLmNvbnN0cnVjdG9yID0gQ2xhc3M7XG4gIGtsYXNzLnByb3RvdHlwZSA9IHByb3BlcnRpZXM7XG4gIHJldHVybiBrbGFzcztcbn07XG5cbkNsYXNzLk11dGF0b3JzID0ge1xuXG4gIEltcGxlbWVudHM6IGZ1bmN0aW9uKHNlbGYsIGtsYXNzZXMpIHtcbiAgICAkLmVhY2goJC5zcGxhdChrbGFzc2VzKSwgZnVuY3Rpb24oa2xhc3MpIHtcbiAgICAgIENsYXNzLnByb3RvdHlwaW5nID0ga2xhc3M7XG4gICAgICB2YXIgaW5zdGFuY2UgPSAodHlwZW9mIGtsYXNzID09ICdmdW5jdGlvbicpID8gbmV3IGtsYXNzIDoga2xhc3M7XG4gICAgICBmb3IgKCB2YXIgcHJvcCBpbiBpbnN0YW5jZSkge1xuICAgICAgICBpZiAoIShwcm9wIGluIHNlbGYpKSB7XG4gICAgICAgICAgc2VsZltwcm9wXSA9IGluc3RhbmNlW3Byb3BdO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBkZWxldGUgQ2xhc3MucHJvdG90eXBpbmc7XG4gICAgfSk7XG4gICAgcmV0dXJuIHNlbGY7XG4gIH1cblxufTtcblxuJC5leHRlbmQoQ2xhc3MsIHtcblxuICBpbmhlcml0OiBmdW5jdGlvbihvYmplY3QsIHByb3BlcnRpZXMpIHtcbiAgICBmb3IgKCB2YXIga2V5IGluIHByb3BlcnRpZXMpIHtcbiAgICAgIHZhciBvdmVycmlkZSA9IHByb3BlcnRpZXNba2V5XTtcbiAgICAgIHZhciBwcmV2aW91cyA9IG9iamVjdFtrZXldO1xuICAgICAgdmFyIHR5cGUgPSAkLnR5cGUob3ZlcnJpZGUpO1xuICAgICAgaWYgKHByZXZpb3VzICYmIHR5cGUgPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBpZiAob3ZlcnJpZGUgIT0gcHJldmlvdXMpIHtcbiAgICAgICAgICBDbGFzcy5vdmVycmlkZShvYmplY3QsIGtleSwgb3ZlcnJpZGUpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHR5cGUgPT0gJ29iamVjdCcpIHtcbiAgICAgICAgb2JqZWN0W2tleV0gPSAkLm1lcmdlKHByZXZpb3VzLCBvdmVycmlkZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBvYmplY3Rba2V5XSA9IG92ZXJyaWRlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gb2JqZWN0O1xuICB9LFxuXG4gIG92ZXJyaWRlOiBmdW5jdGlvbihvYmplY3QsIG5hbWUsIG1ldGhvZCkge1xuICAgIHZhciBwYXJlbnQgPSBDbGFzcy5wcm90b3R5cGluZztcbiAgICBpZiAocGFyZW50ICYmIG9iamVjdFtuYW1lXSAhPSBwYXJlbnRbbmFtZV0pXG4gICAgICBwYXJlbnQgPSBudWxsO1xuICAgIHZhciBvdmVycmlkZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHByZXZpb3VzID0gdGhpcy5wYXJlbnQ7XG4gICAgICB0aGlzLnBhcmVudCA9IHBhcmVudCA/IHBhcmVudFtuYW1lXSA6IG9iamVjdFtuYW1lXTtcbiAgICAgIHZhciB2YWx1ZSA9IG1ldGhvZC5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgdGhpcy5wYXJlbnQgPSBwcmV2aW91cztcbiAgICAgIHJldHVybiB2YWx1ZTtcbiAgICB9O1xuICAgIG9iamVjdFtuYW1lXSA9IG92ZXJyaWRlO1xuICB9XG5cbn0pO1xuXG5DbGFzcy5wcm90b3R5cGUuaW1wbGVtZW50ID0gZnVuY3Rpb24oKSB7XG4gIHZhciBwcm90byA9IHRoaXMucHJvdG90eXBlO1xuICAkLmVhY2goQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzIHx8IFtdKSwgZnVuY3Rpb24ocHJvcGVydGllcykge1xuICAgIENsYXNzLmluaGVyaXQocHJvdG8sIHByb3BlcnRpZXMpO1xuICB9KTtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4kaml0LkNsYXNzID0gQ2xhc3M7XG5cbi8qXG4gIE9iamVjdDogJGppdC5qc29uXG4gIFxuICBQcm92aWRlcyBKU09OIHV0aWxpdHkgZnVuY3Rpb25zLlxuICBcbiAgTW9zdCBvZiB0aGVzZSBmdW5jdGlvbnMgYXJlIEpTT04tdHJlZSB0cmF2ZXJzYWwgYW5kIG1hbmlwdWxhdGlvbiBmdW5jdGlvbnMuXG4qL1xuJGppdC5qc29uID0ge1xuICAvKlxuICAgICBNZXRob2Q6IHBydW5lXG4gIFxuICAgICBDbGVhcnMgYWxsIHRyZWUgbm9kZXMgaGF2aW5nIGRlcHRoIGdyZWF0ZXIgdGhhbiBtYXhMZXZlbC5cbiAgXG4gICAgIFBhcmFtZXRlcnM6XG4gIFxuICAgICAgICB0cmVlIC0gKG9iamVjdCkgQSBKU09OIHRyZWUgb2JqZWN0LiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBwbGVhc2Ugc2VlIDxMb2FkZXIubG9hZEpTT04+LlxuICAgICAgICBtYXhMZXZlbCAtIChudW1iZXIpIEFuIGludGVnZXIgc3BlY2lmeWluZyB0aGUgbWF4aW11bSBsZXZlbCBhbGxvd2VkIGZvciB0aGlzIHRyZWUuIEFsbCBub2RlcyBoYXZpbmcgZGVwdGggZ3JlYXRlciB0aGFuIG1heCBsZXZlbCB3aWxsIGJlIGRlbGV0ZWQuXG5cbiAgKi9cbiAgcHJ1bmU6IGZ1bmN0aW9uKHRyZWUsIG1heExldmVsKSB7XG4gICAgdGhpcy5lYWNoKHRyZWUsIGZ1bmN0aW9uKGVsZW0sIGkpIHtcbiAgICAgIGlmIChpID09IG1heExldmVsICYmIGVsZW0uY2hpbGRyZW4pIHtcbiAgICAgICAgZGVsZXRlIGVsZW0uY2hpbGRyZW47XG4gICAgICAgIGVsZW0uY2hpbGRyZW4gPSBbXTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSxcbiAgLypcbiAgICAgTWV0aG9kOiBnZXRQYXJlbnRcbiAgXG4gICAgIFJldHVybnMgdGhlIHBhcmVudCBub2RlIG9mIHRoZSBub2RlIGhhdmluZyBfaWRfIGFzIGlkLlxuICBcbiAgICAgUGFyYW1ldGVyczpcbiAgXG4gICAgICAgIHRyZWUgLSAob2JqZWN0KSBBIEpTT04gdHJlZSBvYmplY3QuIFNlZSBhbHNvIDxMb2FkZXIubG9hZEpTT04+LlxuICAgICAgICBpZCAtIChzdHJpbmcpIFRoZSBfaWRfIG9mIHRoZSBjaGlsZCBub2RlIHdob3NlIHBhcmVudCB3aWxsIGJlIHJldHVybmVkLlxuXG4gICAgUmV0dXJuczpcblxuICAgICAgICBBIHRyZWUgSlNPTiBub2RlIGlmIGFueSwgb3IgZmFsc2Ugb3RoZXJ3aXNlLlxuICBcbiAgKi9cbiAgZ2V0UGFyZW50OiBmdW5jdGlvbih0cmVlLCBpZCkge1xuICAgIGlmICh0cmVlLmlkID09IGlkKVxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIHZhciBjaCA9IHRyZWUuY2hpbGRyZW47XG4gICAgaWYgKGNoICYmIGNoLmxlbmd0aCA+IDApIHtcbiAgICAgIGZvciAoIHZhciBpID0gMDsgaSA8IGNoLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChjaFtpXS5pZCA9PSBpZClcbiAgICAgICAgICByZXR1cm4gdHJlZTtcbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgdmFyIGFucyA9IHRoaXMuZ2V0UGFyZW50KGNoW2ldLCBpZCk7XG4gICAgICAgICAgaWYgKGFucylcbiAgICAgICAgICAgIHJldHVybiBhbnM7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9LFxuICAvKlxuICAgICBNZXRob2Q6IGdldFN1YnRyZWVcbiAgXG4gICAgIFJldHVybnMgdGhlIHN1YnRyZWUgdGhhdCBtYXRjaGVzIHRoZSBnaXZlbiBpZC5cbiAgXG4gICAgIFBhcmFtZXRlcnM6XG4gIFxuICAgICAgICB0cmVlIC0gKG9iamVjdCkgQSBKU09OIHRyZWUgb2JqZWN0LiBTZWUgYWxzbyA8TG9hZGVyLmxvYWRKU09OPi5cbiAgICAgICAgaWQgLSAoc3RyaW5nKSBBIG5vZGUgKnVuaXF1ZSogaWRlbnRpZmllci5cbiAgXG4gICAgIFJldHVybnM6XG4gIFxuICAgICAgICBBIHN1YnRyZWUgaGF2aW5nIGEgcm9vdCBub2RlIG1hdGNoaW5nIHRoZSBnaXZlbiBpZC4gUmV0dXJucyBudWxsIGlmIG5vIHN1YnRyZWUgbWF0Y2hpbmcgdGhlIGlkIGlzIGZvdW5kLlxuXG4gICovXG4gIGdldFN1YnRyZWU6IGZ1bmN0aW9uKHRyZWUsIGlkKSB7XG4gICAgaWYgKHRyZWUuaWQgPT0gaWQpXG4gICAgICByZXR1cm4gdHJlZTtcbiAgICBmb3IgKCB2YXIgaSA9IDAsIGNoID0gdHJlZS5jaGlsZHJlbjsgY2ggJiYgaSA8IGNoLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgdCA9IHRoaXMuZ2V0U3VidHJlZShjaFtpXSwgaWQpO1xuICAgICAgaWYgKHQgIT0gbnVsbClcbiAgICAgICAgcmV0dXJuIHQ7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9LFxuICAvKlxuICAgICBNZXRob2Q6IGVhY2hMZXZlbFxuICBcbiAgICAgIEl0ZXJhdGVzIG9uIHRyZWUgbm9kZXMgd2l0aCByZWxhdGl2ZSBkZXB0aCBsZXNzIG9yIGVxdWFsIHRoYW4gYSBzcGVjaWZpZWQgbGV2ZWwuXG4gIFxuICAgICBQYXJhbWV0ZXJzOlxuICBcbiAgICAgICAgdHJlZSAtIChvYmplY3QpIEEgSlNPTiB0cmVlIG9yIHN1YnRyZWUuIFNlZSBhbHNvIDxMb2FkZXIubG9hZEpTT04+LlxuICAgICAgICBpbml0TGV2ZWwgLSAobnVtYmVyKSBBbiBpbnRlZ2VyIHNwZWNpZnlpbmcgdGhlIGluaXRpYWwgcmVsYXRpdmUgbGV2ZWwuIFVzdWFsbHkgemVyby5cbiAgICAgICAgdG9MZXZlbCAtIChudW1iZXIpIEFuIGludGVnZXIgc3BlY2lmeWluZyBhIHRvcCBsZXZlbC4gVGhpcyBtZXRob2Qgd2lsbCBpdGVyYXRlIG9ubHkgdGhyb3VnaCBub2RlcyB3aXRoIGRlcHRoIGxlc3MgdGhhbiBvciBlcXVhbCB0aGlzIG51bWJlci5cbiAgICAgICAgYWN0aW9uIC0gKGZ1bmN0aW9uKSBBIGZ1bmN0aW9uIHRoYXQgcmVjZWl2ZXMgYSBub2RlIGFuZCBhbiBpbnRlZ2VyIHNwZWNpZnlpbmcgdGhlIGFjdHVhbCBsZXZlbCBvZiB0aGUgbm9kZS5cbiAgICAgICAgICBcbiAgICBFeGFtcGxlOlxuICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICRqaXQuanNvbi5lYWNoTGV2ZWwodHJlZSwgMCwgMywgZnVuY3Rpb24obm9kZSwgZGVwdGgpIHtcbiAgICAgICAgYWxlcnQobm9kZS5uYW1lICsgJyAnICsgZGVwdGgpO1xuICAgICB9KTtcbiAgIChlbmQgY29kZSlcbiAgKi9cbiAgZWFjaExldmVsOiBmdW5jdGlvbih0cmVlLCBpbml0TGV2ZWwsIHRvTGV2ZWwsIGFjdGlvbikge1xuICAgIGlmIChpbml0TGV2ZWwgPD0gdG9MZXZlbCkge1xuICAgICAgYWN0aW9uKHRyZWUsIGluaXRMZXZlbCk7XG4gICAgICBpZighdHJlZS5jaGlsZHJlbikgcmV0dXJuO1xuICAgICAgZm9yICggdmFyIGkgPSAwLCBjaCA9IHRyZWUuY2hpbGRyZW47IGkgPCBjaC5sZW5ndGg7IGkrKykge1xuICAgICAgICB0aGlzLmVhY2hMZXZlbChjaFtpXSwgaW5pdExldmVsICsgMSwgdG9MZXZlbCwgYWN0aW9uKTtcbiAgICAgIH1cbiAgICB9XG4gIH0sXG4gIC8qXG4gICAgIE1ldGhvZDogZWFjaFxuICBcbiAgICAgIEEgSlNPTiB0cmVlIGl0ZXJhdG9yLlxuICBcbiAgICAgUGFyYW1ldGVyczpcbiAgXG4gICAgICAgIHRyZWUgLSAob2JqZWN0KSBBIEpTT04gdHJlZSBvciBzdWJ0cmVlLiBTZWUgYWxzbyA8TG9hZGVyLmxvYWRKU09OPi5cbiAgICAgICAgYWN0aW9uIC0gKGZ1bmN0aW9uKSBBIGZ1bmN0aW9uIHRoYXQgcmVjZWl2ZXMgYSBub2RlLlxuXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICRqaXQuanNvbi5lYWNoKHRyZWUsIGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgICAgYWxlcnQobm9kZS5uYW1lKTtcbiAgICAgIH0pO1xuICAgIChlbmQgY29kZSlcbiAgICAgICAgICBcbiAgKi9cbiAgZWFjaDogZnVuY3Rpb24odHJlZSwgYWN0aW9uKSB7XG4gICAgdGhpcy5lYWNoTGV2ZWwodHJlZSwgMCwgTnVtYmVyLk1BWF9WQUxVRSwgYWN0aW9uKTtcbiAgfVxufTtcblxuXG4vKlxuICAgICBBbiBvYmplY3QgY29udGFpbmluZyBtdWx0aXBsZSB0eXBlIG9mIHRyYW5zZm9ybWF0aW9ucy4gXG4qL1xuXG4kaml0LlRyYW5zID0ge1xuICAkZXh0ZW5kOiB0cnVlLFxuICBcbiAgbGluZWFyOiBmdW5jdGlvbihwKXtcbiAgICByZXR1cm4gcDtcbiAgfVxufTtcblxudmFyIFRyYW5zID0gJGppdC5UcmFucztcblxuKGZ1bmN0aW9uKCl7XG5cbiAgdmFyIG1ha2VUcmFucyA9IGZ1bmN0aW9uKHRyYW5zaXRpb24sIHBhcmFtcyl7XG4gICAgcGFyYW1zID0gJC5zcGxhdChwYXJhbXMpO1xuICAgIHJldHVybiAkLmV4dGVuZCh0cmFuc2l0aW9uLCB7XG4gICAgICBlYXNlSW46IGZ1bmN0aW9uKHBvcyl7XG4gICAgICAgIHJldHVybiB0cmFuc2l0aW9uKHBvcywgcGFyYW1zKTtcbiAgICAgIH0sXG4gICAgICBlYXNlT3V0OiBmdW5jdGlvbihwb3Mpe1xuICAgICAgICByZXR1cm4gMSAtIHRyYW5zaXRpb24oMSAtIHBvcywgcGFyYW1zKTtcbiAgICAgIH0sXG4gICAgICBlYXNlSW5PdXQ6IGZ1bmN0aW9uKHBvcyl7XG4gICAgICAgIHJldHVybiAocG9zIDw9IDAuNSk/IHRyYW5zaXRpb24oMiAqIHBvcywgcGFyYW1zKSAvIDIgOiAoMiAtIHRyYW5zaXRpb24oXG4gICAgICAgICAgICAyICogKDEgLSBwb3MpLCBwYXJhbXMpKSAvIDI7XG4gICAgICB9XG4gICAgfSk7XG4gIH07XG5cbiAgdmFyIHRyYW5zaXRpb25zID0ge1xuXG4gICAgUG93OiBmdW5jdGlvbihwLCB4KXtcbiAgICAgIHJldHVybiBNYXRoLnBvdyhwLCB4WzBdIHx8IDYpO1xuICAgIH0sXG5cbiAgICBFeHBvOiBmdW5jdGlvbihwKXtcbiAgICAgIHJldHVybiBNYXRoLnBvdygyLCA4ICogKHAgLSAxKSk7XG4gICAgfSxcblxuICAgIENpcmM6IGZ1bmN0aW9uKHApe1xuICAgICAgcmV0dXJuIDEgLSBNYXRoLnNpbihNYXRoLmFjb3MocCkpO1xuICAgIH0sXG5cbiAgICBTaW5lOiBmdW5jdGlvbihwKXtcbiAgICAgIHJldHVybiAxIC0gTWF0aC5zaW4oKDEgLSBwKSAqIE1hdGguUEkgLyAyKTtcbiAgICB9LFxuXG4gICAgQmFjazogZnVuY3Rpb24ocCwgeCl7XG4gICAgICB4ID0geFswXSB8fCAxLjYxODtcbiAgICAgIHJldHVybiBNYXRoLnBvdyhwLCAyKSAqICgoeCArIDEpICogcCAtIHgpO1xuICAgIH0sXG5cbiAgICBCb3VuY2U6IGZ1bmN0aW9uKHApe1xuICAgICAgdmFyIHZhbHVlO1xuICAgICAgZm9yICggdmFyIGEgPSAwLCBiID0gMTsgMTsgYSArPSBiLCBiIC89IDIpIHtcbiAgICAgICAgaWYgKHAgPj0gKDcgLSA0ICogYSkgLyAxMSkge1xuICAgICAgICAgIHZhbHVlID0gYiAqIGIgLSBNYXRoLnBvdygoMTEgLSA2ICogYSAtIDExICogcCkgLyA0LCAyKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH0sXG5cbiAgICBFbGFzdGljOiBmdW5jdGlvbihwLCB4KXtcbiAgICAgIHJldHVybiBNYXRoLnBvdygyLCAxMCAqIC0tcClcbiAgICAgICAgICAqIE1hdGguY29zKDIwICogcCAqIE1hdGguUEkgKiAoeFswXSB8fCAxKSAvIDMpO1xuICAgIH1cblxuICB9O1xuXG4gICQuZWFjaCh0cmFuc2l0aW9ucywgZnVuY3Rpb24odmFsLCBrZXkpe1xuICAgIFRyYW5zW2tleV0gPSBtYWtlVHJhbnModmFsKTtcbiAgfSk7XG5cbiAgJC5lYWNoKCBbXG4gICAgICAnUXVhZCcsICdDdWJpYycsICdRdWFydCcsICdRdWludCdcbiAgXSwgZnVuY3Rpb24oZWxlbSwgaSl7XG4gICAgVHJhbnNbZWxlbV0gPSBtYWtlVHJhbnMoZnVuY3Rpb24ocCl7XG4gICAgICByZXR1cm4gTWF0aC5wb3cocCwgW1xuICAgICAgICBpICsgMlxuICAgICAgXSk7XG4gICAgfSk7XG4gIH0pO1xuXG59KSgpO1xuXG4vKlxuICAgQSBDbGFzcyB0aGF0IGNhbiBwZXJmb3JtIGFuaW1hdGlvbnMgZm9yIGdlbmVyaWMgb2JqZWN0cy5cblxuICAgSWYgeW91IGFyZSBsb29raW5nIGZvciBhbmltYXRpb24gdHJhbnNpdGlvbnMgcGxlYXNlIHRha2UgYSBsb29rIGF0IHRoZSA8VHJhbnM+IG9iamVjdC5cblxuICAgVXNlZCBieTpcblxuICAgPEdyYXBoLlBsb3Q+XG4gICBcbiAgIEJhc2VkIG9uOlxuICAgXG4gICBUaGUgQW5pbWF0aW9uIGNsYXNzIGlzIGJhc2VkIGluIHRoZSBNb29Ub29scyBGcmFtZXdvcmsgPGh0dHA6Ly9tb290b29scy5uZXQ+LiBDb3B5cmlnaHQgKGMpIDIwMDYtMjAwOSBWYWxlcmlvIFByb2lldHRpLCA8aHR0cDovL21hZDRtaWxrLm5ldC8+LiBNSVQgbGljZW5zZSA8aHR0cDovL21vb3Rvb2xzLm5ldC9saWNlbnNlLnR4dD4uXG5cbiovXG5cbnZhciBBbmltYXRpb24gPSBuZXcgQ2xhc3MoIHtcblxuICBpbml0aWFsaXplOiBmdW5jdGlvbihvcHRpb25zKXtcbiAgICB0aGlzLnNldE9wdGlvbnMob3B0aW9ucyk7XG4gIH0sXG5cbiAgc2V0T3B0aW9uczogZnVuY3Rpb24ob3B0aW9ucyl7XG4gICAgdmFyIG9wdCA9IHtcbiAgICAgIGR1cmF0aW9uOiAyNTAwLFxuICAgICAgZnBzOiA0MCxcbiAgICAgIHRyYW5zaXRpb246IFRyYW5zLlF1YXJ0LmVhc2VJbk91dCxcbiAgICAgIGNvbXB1dGU6ICQuZW1wdHksXG4gICAgICBjb21wbGV0ZTogJC5lbXB0eSxcbiAgICAgIGxpbms6ICdpZ25vcmUnXG4gICAgfTtcbiAgICB0aGlzLm9wdCA9ICQubWVyZ2Uob3B0LCBvcHRpb25zIHx8IHt9KTtcbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICBzdGVwOiBmdW5jdGlvbigpe1xuICAgIHZhciB0aW1lID0gJC50aW1lKCksIG9wdCA9IHRoaXMub3B0O1xuICAgIGlmICh0aW1lIDwgdGhpcy50aW1lICsgb3B0LmR1cmF0aW9uKSB7XG4gICAgICB2YXIgZGVsdGEgPSBvcHQudHJhbnNpdGlvbigodGltZSAtIHRoaXMudGltZSkgLyBvcHQuZHVyYXRpb24pO1xuICAgICAgb3B0LmNvbXB1dGUoZGVsdGEpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnRpbWVyID0gY2xlYXJJbnRlcnZhbCh0aGlzLnRpbWVyKTtcbiAgICAgIG9wdC5jb21wdXRlKDEpO1xuICAgICAgb3B0LmNvbXBsZXRlKCk7XG4gICAgfVxuICB9LFxuXG4gIHN0YXJ0OiBmdW5jdGlvbigpe1xuICAgIGlmICghdGhpcy5jaGVjaygpKVxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgdGhpcy50aW1lID0gMDtcbiAgICB0aGlzLnN0YXJ0VGltZXIoKTtcbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICBzdGFydFRpbWVyOiBmdW5jdGlvbigpe1xuICAgIHZhciB0aGF0ID0gdGhpcywgZnBzID0gdGhpcy5vcHQuZnBzO1xuICAgIGlmICh0aGlzLnRpbWVyKVxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIHRoaXMudGltZSA9ICQudGltZSgpIC0gdGhpcy50aW1lO1xuICAgIHRoaXMudGltZXIgPSBzZXRJbnRlcnZhbCgoZnVuY3Rpb24oKXtcbiAgICAgIHRoYXQuc3RlcCgpO1xuICAgIH0pLCBNYXRoLnJvdW5kKDEwMDAgLyBmcHMpKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSxcblxuICBwYXVzZTogZnVuY3Rpb24oKXtcbiAgICB0aGlzLnN0b3BUaW1lcigpO1xuICAgIHJldHVybiB0aGlzO1xuICB9LFxuXG4gIHJlc3VtZTogZnVuY3Rpb24oKXtcbiAgICB0aGlzLnN0YXJ0VGltZXIoKTtcbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICBzdG9wVGltZXI6IGZ1bmN0aW9uKCl7XG4gICAgaWYgKCF0aGlzLnRpbWVyKVxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIHRoaXMudGltZSA9ICQudGltZSgpIC0gdGhpcy50aW1lO1xuICAgIHRoaXMudGltZXIgPSBjbGVhckludGVydmFsKHRoaXMudGltZXIpO1xuICAgIHJldHVybiB0cnVlO1xuICB9LFxuXG4gIGNoZWNrOiBmdW5jdGlvbigpe1xuICAgIGlmICghdGhpcy50aW1lcilcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIGlmICh0aGlzLm9wdC5saW5rID09ICdjYW5jZWwnKSB7XG4gICAgICB0aGlzLnN0b3BUaW1lcigpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufSk7XG5cblxudmFyIE9wdGlvbnMgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGFyZ3MgPSBhcmd1bWVudHM7XG4gIGZvcih2YXIgaT0wLCBsPWFyZ3MubGVuZ3RoLCBhbnM9e307IGk8bDsgaSsrKSB7XG4gICAgdmFyIG9wdCA9IE9wdGlvbnNbYXJnc1tpXV07XG4gICAgaWYob3B0LiRleHRlbmQpIHtcbiAgICAgICQuZXh0ZW5kKGFucywgb3B0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgYW5zW2FyZ3NbaV1dID0gb3B0OyAgXG4gICAgfVxuICB9XG4gIHJldHVybiBhbnM7XG59O1xuXG4vKlxuICogRmlsZTogT3B0aW9ucy5DYW52YXMuanNcbiAqXG4qL1xuXG4vKlxuICBPYmplY3Q6IE9wdGlvbnMuQ2FudmFzXG4gIFxuICBUaGVzZSBhcmUgQ2FudmFzIGdlbmVyYWwgb3B0aW9ucywgbGlrZSB3aGVyZSB0byBhcHBlbmQgaXQgaW4gdGhlIERPTSwgaXRzIGRpbWVuc2lvbnMsIGJhY2tncm91bmQsIFxuICBhbmQgb3RoZXIgbW9yZSBhZHZhbmNlZCBvcHRpb25zLlxuICBcbiAgU3ludGF4OlxuICBcbiAgKHN0YXJ0IGNvZGUganMpXG5cbiAgT3B0aW9ucy5DYW52YXMgPSB7XG4gICAgaW5qZWN0SW50bzogJ2lkJyxcbiAgICB0eXBlOiAnMkQnLCAvLyczRCdcbiAgICB3aWR0aDogZmFsc2UsXG4gICAgaGVpZ2h0OiBmYWxzZSxcbiAgICB1c2VDYW52YXM6IGZhbHNlLFxuICAgIHdpdGhMYWJlbHM6IHRydWUsXG4gICAgYmFja2dyb3VuZDogZmFsc2VcbiAgfTsgIFxuICAoZW5kIGNvZGUpXG4gIFxuICBFeGFtcGxlOlxuICBcbiAgKHN0YXJ0IGNvZGUganMpXG4gIHZhciB2aXogPSBuZXcgJGppdC5WaXooe1xuICAgIGluamVjdEludG86ICdzb21lQ29udGFpbmVySWQnLFxuICAgIHdpZHRoOiA1MDAsXG4gICAgaGVpZ2h0OiA3MDBcbiAgfSk7XG4gIChlbmQgY29kZSlcbiAgXG4gIFBhcmFtZXRlcnM6XG4gIFxuICBpbmplY3RJbnRvIC0gKnJlcXVpcmVkKiAoc3RyaW5nfGVsZW1lbnQpIFRoZSBpZCBvZiB0aGUgRE9NIGNvbnRhaW5lciBmb3IgdGhlIHZpc3VhbGl6YXRpb24uIEl0IGNhbiBhbHNvIGJlIGFuIEVsZW1lbnQgcHJvdmlkZWQgdGhhdCBpdCBoYXMgYW4gaWQuXG4gIHR5cGUgLSAoc3RyaW5nKSBDb250ZXh0IHR5cGUuIERlZmF1bHQncyAyRCBidXQgY2FuIGJlIDNEIGZvciB3ZWJHTCBlbmFibGVkIGJyb3dzZXJzLlxuICB3aWR0aCAtIChudW1iZXIpIERlZmF1bHQncyB0byB0aGUgKmNvbnRhaW5lcidzIG9mZnNldFdpZHRoKi4gVGhlIHdpZHRoIG9mIHRoZSBjYW52YXMuXG4gIGhlaWdodCAtIChudW1iZXIpIERlZmF1bHQncyB0byB0aGUgKmNvbnRhaW5lcidzIG9mZnNldEhlaWdodCouIFRoZSBoZWlnaHQgb2YgdGhlIGNhbnZhcy5cbiAgdXNlQ2FudmFzIC0gKGJvb2xlYW58b2JqZWN0KSBEZWZhdWx0J3MgKmZhbHNlKi4gWW91IGNhbiBwYXNzIGFub3RoZXIgPENhbnZhcz4gaW5zdGFuY2UgdG8gYmUgdXNlZCBieSB0aGUgdmlzdWFsaXphdGlvbi5cbiAgd2l0aExhYmVscyAtIChib29sZWFuKSBEZWZhdWx0J3MgKnRydWUqLiBXaGV0aGVyIHRvIHVzZSBhIGxhYmVsIGNvbnRhaW5lciBmb3IgdGhlIHZpc3VhbGl6YXRpb24uXG4gIGJhY2tncm91bmQgLSAoYm9vbGVhbnxvYmplY3QpIERlZmF1bHQncyAqZmFsc2UqLiBBbiBvYmplY3QgY29udGFpbmluZyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgcmVuZGVyaW5nIG9mIGEgYmFja2dyb3VuZCBjYW52YXMuXG4qL1xuXG5PcHRpb25zLkNhbnZhcyA9IHtcbiAgICAkZXh0ZW5kOiB0cnVlLFxuICAgIFxuICAgIGluamVjdEludG86ICdpZCcsXG4gICAgdHlwZTogJzJEJyxcbiAgICB3aWR0aDogZmFsc2UsXG4gICAgaGVpZ2h0OiBmYWxzZSxcbiAgICB1c2VDYW52YXM6IGZhbHNlLFxuICAgIHdpdGhMYWJlbHM6IHRydWUsXG4gICAgYmFja2dyb3VuZDogZmFsc2UsXG4gICAgXG4gICAgU2NlbmU6IHtcbiAgICAgIExpZ2h0aW5nOiB7XG4gICAgICAgIGVuYWJsZTogZmFsc2UsXG4gICAgICAgIGFtYmllbnQ6IFsxLCAxLCAxXSxcbiAgICAgICAgZGlyZWN0aW9uYWw6IHtcbiAgICAgICAgICBkaXJlY3Rpb246IHsgeDogLTEwMCwgeTogLTEwMCwgejogLTEwMCB9LFxuICAgICAgICAgIGNvbG9yOiBbMC41LCAwLjMsIDAuMV1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbn07XG5cbi8qXG4gKiBGaWxlOiBPcHRpb25zLk5vZGUuanNcbiAqXG4qL1xuXG4vKlxuICBPYmplY3Q6IE9wdGlvbnMuTm9kZVxuXG4gIFByb3ZpZGVzIE5vZGUgcmVuZGVyaW5nIG9wdGlvbnMgZm9yIFRyZWUgYW5kIEdyYXBoIGJhc2VkIHZpc3VhbGl6YXRpb25zLlxuXG4gIFN5bnRheDpcbiAgICBcbiAgKHN0YXJ0IGNvZGUganMpXG4gIE9wdGlvbnMuTm9kZSA9IHtcbiAgICBvdmVycmlkYWJsZTogZmFsc2UsXG4gICAgdHlwZTogJ2NpcmNsZScsXG4gICAgY29sb3I6ICcjY2NiJyxcbiAgICBhbHBoYTogMSxcbiAgICBkaW06IDMsXG4gICAgaGVpZ2h0OiAyMCxcbiAgICB3aWR0aDogOTAsXG4gICAgYXV0b0hlaWdodDogZmFsc2UsXG4gICAgYXV0b1dpZHRoOiBmYWxzZSxcbiAgICBsaW5lV2lkdGg6IDEsXG4gICAgdHJhbnNmb3JtOiB0cnVlLFxuICAgIGFsaWduOiBcImNlbnRlclwiLFxuICAgIGFuZ3VsYXJXaWR0aDoxLFxuICAgIHNwYW46MSxcbiAgICBDYW52YXNTdHlsZXM6IHt9XG4gIH07XG4gIChlbmQgY29kZSlcbiAgXG4gIEV4YW1wbGU6XG4gIFxuICAoc3RhcnQgY29kZSBqcylcbiAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeih7XG4gICAgTm9kZToge1xuICAgICAgb3ZlcnJpZGFibGU6IHRydWUsXG4gICAgICB3aWR0aDogMzAsXG4gICAgICBhdXRvSGVpZ2h0OiB0cnVlLFxuICAgICAgdHlwZTogJ3JlY3RhbmdsZSdcbiAgICB9XG4gIH0pO1xuICAoZW5kIGNvZGUpXG4gIFxuICBQYXJhbWV0ZXJzOlxuXG4gIG92ZXJyaWRhYmxlIC0gKGJvb2xlYW4pIERlZmF1bHQncyAqZmFsc2UqLiBEZXRlcm1pbmUgd2hldGhlciBvciBub3QgZ2VuZXJhbCBub2RlIHByb3BlcnRpZXMgY2FuIGJlIG92ZXJyaWRkZW4gYnkgYSBwYXJ0aWN1bGFyIDxHcmFwaC5Ob2RlPi5cbiAgdHlwZSAtIChzdHJpbmcpIERlZmF1bHQncyAqY2lyY2xlKi4gTm9kZSdzIHNoYXBlLiBOb2RlIGJ1aWx0LWluIHR5cGVzIGluY2x1ZGUgJ2NpcmNsZScsICdyZWN0YW5nbGUnLCAnc3F1YXJlJywgJ2VsbGlwc2UnLCAndHJpYW5nbGUnLCAnc3RhcicuIFRoZSBkZWZhdWx0IE5vZGUgdHlwZSBtaWdodCB2YXJ5IGluIGVhY2ggdmlzdWFsaXphdGlvbi4gWW91IGNhbiBhbHNvIGltcGxlbWVudCAobm9uIGJ1aWx0LWluKSBjdXN0b20gTm9kZSB0eXBlcyBpbnRvIHlvdXIgdmlzdWFsaXphdGlvbnMuXG4gIGNvbG9yIC0gKHN0cmluZykgRGVmYXVsdCdzICojY2NiKi4gTm9kZSBjb2xvci5cbiAgYWxwaGEgLSAobnVtYmVyKSBEZWZhdWx0J3MgKjEqLiBUaGUgTm9kZSdzIGFscGhhIHZhbHVlLiAqMSogaXMgZm9yIGZ1bGwgb3BhY2l0eS5cbiAgZGltIC0gKG51bWJlcikgRGVmYXVsdCdzICozKi4gQW4gZXh0cmEgcGFyYW1ldGVyIHVzZWQgYnkgJ2NpcmNsZScsICdzcXVhcmUnLCAndHJpYW5nbGUnIGFuZCAnc3Rhcicgbm9kZSB0eXBlcy4gRGVwZW5kaW5nIG9uIGVhY2ggc2hhcGUsIHRoaXMgcGFyYW1ldGVyIGNhbiBzZXQgdGhlIHJhZGl1cyBvZiBhIGNpcmNsZSwgaGFsZiB0aGUgbGVuZ3RoIG9mIHRoZSBzaWRlIG9mIGEgc3F1YXJlLCBoYWxmIHRoZSBiYXNlIGFuZCBoYWxmIHRoZSBoZWlnaHQgb2YgYSB0cmlhbmdsZSBvciB0aGUgbGVuZ3RoIG9mIGEgc2lkZSBvZiBhIHN0YXIgKGNvbmNhdmUgZGVjYWdvbikuXG4gIGhlaWdodCAtIChudW1iZXIpIERlZmF1bHQncyAqMjAqLiBVc2VkIGJ5ICdyZWN0YW5nbGUnIGFuZCAnZWxsaXBzZScgbm9kZSB0eXBlcy4gVGhlIGhlaWdodCBvZiB0aGUgbm9kZSBzaGFwZS5cbiAgd2lkdGggLSAobnVtYmVyKSBEZWZhdWx0J3MgKjkwKi4gVXNlZCBieSAncmVjdGFuZ2xlJyBhbmQgJ2VsbGlwc2UnIG5vZGUgdHlwZXMuIFRoZSB3aWR0aCBvZiB0aGUgbm9kZSBzaGFwZS5cbiAgYXV0b0hlaWdodCAtIChib29sZWFuKSBEZWZhdWx0J3MgKmZhbHNlKi4gV2hldGhlciB0byBzZXQgYW4gYXV0byBoZWlnaHQgZm9yIHRoZSBub2RlIGRlcGVuZGluZyBvbiB0aGUgY29udGVudCBvZiB0aGUgTm9kZSdzIGxhYmVsLlxuICBhdXRvV2lkdGggLSAoYm9vbGVhbikgRGVmYXVsdCdzICpmYWxzZSouIFdoZXRoZXIgdG8gc2V0IGFuIGF1dG8gd2lkdGggZm9yIHRoZSBub2RlIGRlcGVuZGluZyBvbiB0aGUgY29udGVudCBvZiB0aGUgTm9kZSdzIGxhYmVsLlxuICBsaW5lV2lkdGggLSAobnVtYmVyKSBEZWZhdWx0J3MgKjEqLiBVc2VkIG9ubHkgYnkgc29tZSBOb2RlIHNoYXBlcy4gVGhlIGxpbmUgd2lkdGggb2YgdGhlIHN0cm9rZXMgb2YgYSBub2RlLlxuICB0cmFuc2Zvcm0gLSAoYm9vbGVhbikgRGVmYXVsdCdzICp0cnVlKi4gT25seSB1c2VkIGJ5IHRoZSA8SHlwZXJ0cmVlPiB2aXN1YWxpemF0aW9uLiBXaGV0aGVyIHRvIHNjYWxlIHRoZSBub2RlcyBhY2NvcmRpbmcgdG8gdGhlIG1vZWJpdXMgdHJhbnNmb3JtYXRpb24uXG4gIGFsaWduIC0gKHN0cmluZykgRGVmYXVsdCdzICpjZW50ZXIqLiBQb3NzaWJsZSB2YWx1ZXMgYXJlICdjZW50ZXInLCAnbGVmdCcgb3IgJ3JpZ2h0Jy4gVXNlZCBvbmx5IGJ5IHRoZSA8U1Q+IHZpc3VhbGl6YXRpb24sIHRoZXNlIHBhcmFtZXRlcnMgYXJlIHVzZWQgZm9yIGFsaWduaW5nIG5vZGVzIHdoZW4gc29tZSBvZiB0aGV5IGRpbWVuc2lvbnMgdmFyeS5cbiAgYW5ndWxhcldpZHRoIC0gKG51bWJlcikgRGVmYXVsdCdzICoxKi4gVXNlZCBpbiByYWRpYWwgbGF5b3V0cyAobGlrZSA8UkdyYXBoPiBvciA8U3VuYnVyc3Q+IHZpc3VhbGl6YXRpb25zKS4gVGhlIGFtb3VudCBvZiByZWxhdGl2ZSAnc3BhY2UnIHNldCBmb3IgYSBub2RlLlxuICBzcGFuIC0gKG51bWJlcikgRGVmYXVsdCdzICoxKi4gVXNlZCBpbiByYWRpYWwgbGF5b3V0cyAobGlrZSA8UkdyYXBoPiBvciA8U3VuYnVyc3Q+IHZpc3VhbGl6YXRpb25zKS4gVGhlIGFuZ2xlIHNwYW4gYW1vdW50IHNldCBmb3IgYSBub2RlLlxuICBDYW52YXNTdHlsZXMgLSAob2JqZWN0KSBEZWZhdWx0J3MgYW4gZW1wdHkgb2JqZWN0IChpLmUuIHt9KS4gQXR0YWNoIGFueSBvdGhlciBjYW52YXMgc3BlY2lmaWMgcHJvcGVydHkgdGhhdCB5b3UnZCBzZXQgdG8gdGhlIGNhbnZhcyBjb250ZXh0IGJlZm9yZSBwbG90dGluZyBhIE5vZGUuXG5cbiovXG5PcHRpb25zLk5vZGUgPSB7XG4gICRleHRlbmQ6IGZhbHNlLFxuICBcbiAgb3ZlcnJpZGFibGU6IGZhbHNlLFxuICB0eXBlOiAnY2lyY2xlJyxcbiAgY29sb3I6ICcjY2NiJyxcbiAgYWxwaGE6IDEsXG4gIGRpbTogMyxcbiAgaGVpZ2h0OiAyMCxcbiAgd2lkdGg6IDkwLFxuICBhdXRvSGVpZ2h0OiBmYWxzZSxcbiAgYXV0b1dpZHRoOiBmYWxzZSxcbiAgbGluZVdpZHRoOiAxLFxuICB0cmFuc2Zvcm06IHRydWUsXG4gIGFsaWduOiBcImNlbnRlclwiLFxuICBhbmd1bGFyV2lkdGg6MSxcbiAgc3BhbjoxLFxuICAvL1JhdyBjYW52YXMgc3R5bGVzIHRvIGJlXG4gIC8vYXBwbGllZCB0byB0aGUgY29udGV4dCBpbnN0YW5jZVxuICAvL2JlZm9yZSBwbG90dGluZyBhIG5vZGVcbiAgQ2FudmFzU3R5bGVzOiB7fVxufTtcblxuXG4vKlxuICogRmlsZTogT3B0aW9ucy5FZGdlLmpzXG4gKlxuKi9cblxuLypcbiAgT2JqZWN0OiBPcHRpb25zLkVkZ2VcblxuICBQcm92aWRlcyBFZGdlIHJlbmRlcmluZyBvcHRpb25zIGZvciBUcmVlIGFuZCBHcmFwaCBiYXNlZCB2aXN1YWxpemF0aW9ucy5cblxuICBTeW50YXg6XG4gICAgXG4gIChzdGFydCBjb2RlIGpzKVxuICBPcHRpb25zLkVkZ2UgPSB7XG4gICAgb3ZlcnJpZGFibGU6IGZhbHNlLFxuICAgIHR5cGU6ICdsaW5lJyxcbiAgICBjb2xvcjogJyNjY2InLFxuICAgIGxpbmVXaWR0aDogMSxcbiAgICBkaW06MTUsXG4gICAgYWxwaGE6IDEsXG4gICAgQ2FudmFzU3R5bGVzOiB7fVxuICB9O1xuICAoZW5kIGNvZGUpXG4gIFxuICBFeGFtcGxlOlxuICBcbiAgKHN0YXJ0IGNvZGUganMpXG4gIHZhciB2aXogPSBuZXcgJGppdC5WaXooe1xuICAgIEVkZ2U6IHtcbiAgICAgIG92ZXJyaWRhYmxlOiB0cnVlLFxuICAgICAgdHlwZTogJ2xpbmUnLFxuICAgICAgY29sb3I6ICcjZmZmJyxcbiAgICAgIENhbnZhc1N0eWxlczoge1xuICAgICAgICBzaGFkb3dDb2xvcjogJyNjY2MnLFxuICAgICAgICBzaGFkb3dCbHVyOiAxMFxuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIChlbmQgY29kZSlcbiAgXG4gIFBhcmFtZXRlcnM6XG4gICAgXG4gICBvdmVycmlkYWJsZSAtIChib29sZWFuKSBEZWZhdWx0J3MgKmZhbHNlKi4gRGV0ZXJtaW5lIHdoZXRoZXIgb3Igbm90IGdlbmVyYWwgZWRnZXMgcHJvcGVydGllcyBjYW4gYmUgb3ZlcnJpZGRlbiBieSBhIHBhcnRpY3VsYXIgPEdyYXBoLkFkamFjZW5jZT4uXG4gICB0eXBlIC0gKHN0cmluZykgRGVmYXVsdCdzICdsaW5lJy4gRWRnZSBzdHlsZXMgaW5jbHVkZSAnbGluZScsICdoeXBlcmxpbmUnLCAnYXJyb3cnLiBUaGUgZGVmYXVsdCBFZGdlIHR5cGUgbWlnaHQgdmFyeSBpbiBlYWNoIHZpc3VhbGl6YXRpb24uIFlvdSBjYW4gYWxzbyBpbXBsZW1lbnQgY3VzdG9tIEVkZ2UgdHlwZXMuXG4gICBjb2xvciAtIChzdHJpbmcpIERlZmF1bHQncyAnI2NjYicuIEVkZ2UgY29sb3IuXG4gICBsaW5lV2lkdGggLSAobnVtYmVyKSBEZWZhdWx0J3MgKjEqLiBMaW5lL0VkZ2Ugd2lkdGguXG4gICBhbHBoYSAtIChudW1iZXIpIERlZmF1bHQncyAqMSouIFRoZSBFZGdlJ3MgYWxwaGEgdmFsdWUuICoxKiBpcyBmb3IgZnVsbCBvcGFjaXR5LlxuICAgZGltIC0gKG51bWJlcikgRGVmYXVsdCdzICoxNSouIEFuIGV4dHJhIHBhcmFtZXRlciB1c2VkIGJ5IG90aGVyIGNvbXBsZXggc2hhcGVzIHN1Y2ggYXMgcXVhZHJhdGljLCBiZXppZXIgb3IgYXJyb3csIHRvIGRldGVybWluZSB0aGUgc2hhcGUncyBkaWFtZXRlci5cbiAgIGVwc2lsb24gLSAobnVtYmVyKSBEZWZhdWx0J3MgKjcqLiBPbmx5IHVzZWQgd2hlbiB1c2luZyAqZW5hYmxlRm9yRWRnZXMqIGluIDxPcHRpb25zLkV2ZW50cz4uIFRoaXMgZGltZW5zaW9uIGlzIHVzZWQgdG8gY3JlYXRlIGFuIGFyZWEgZm9yIHRoZSBsaW5lIHdoZXJlIHRoZSBjb250YWlucyBtZXRob2QgZm9yIHRoZSBlZGdlIHJldHVybnMgKnRydWUqLlxuICAgQ2FudmFzU3R5bGVzIC0gKG9iamVjdCkgRGVmYXVsdCdzIGFuIGVtcHR5IG9iamVjdCAoaS5lLiB7fSkuIEF0dGFjaCBhbnkgb3RoZXIgY2FudmFzIHNwZWNpZmljIHByb3BlcnR5IHRoYXQgeW91J2Qgc2V0IHRvIHRoZSBjYW52YXMgY29udGV4dCBiZWZvcmUgcGxvdHRpbmcgYW4gRWRnZS5cblxuICBTZWUgYWxzbzpcbiAgIFxuICAgSWYgeW91IHdhbnQgdG8ga25vdyBtb3JlIGFib3V0IGhvdyB0byBjdXN0b21pemUgTm9kZS9FZGdlIGRhdGEgcGVyIGVsZW1lbnQsIGluIHRoZSBKU09OIG9yIHByb2dyYW1tYXRpY2FsbHksIHRha2UgYSBsb29rIGF0IHRoaXMgYXJ0aWNsZS5cbiovXG5PcHRpb25zLkVkZ2UgPSB7XG4gICRleHRlbmQ6IGZhbHNlLFxuICBcbiAgb3ZlcnJpZGFibGU6IGZhbHNlLFxuICB0eXBlOiAnbGluZScsXG4gIGNvbG9yOiAnI2NjYicsXG4gIGxpbmVXaWR0aDogMSxcbiAgZGltOjE1LFxuICBhbHBoYTogMSxcbiAgZXBzaWxvbjogNyxcblxuICAvL1JhdyBjYW52YXMgc3R5bGVzIHRvIGJlXG4gIC8vYXBwbGllZCB0byB0aGUgY29udGV4dCBpbnN0YW5jZVxuICAvL2JlZm9yZSBwbG90dGluZyBhbiBlZGdlXG4gIENhbnZhc1N0eWxlczoge31cbn07XG5cblxuLypcbiAqIEZpbGU6IE9wdGlvbnMuRnguanNcbiAqXG4qL1xuXG4vKlxuICBPYmplY3Q6IE9wdGlvbnMuRnhcblxuICBQcm92aWRlcyBhbmltYXRpb24gb3B0aW9ucyBsaWtlIGR1cmF0aW9uIG9mIHRoZSBhbmltYXRpb25zLCBmcmFtZXMgcGVyIHNlY29uZCBhbmQgYW5pbWF0aW9uIHRyYW5zaXRpb25zLiAgXG5cbiAgU3ludGF4OlxuICBcbiAgKHN0YXJ0IGNvZGUganMpXG4gICAgT3B0aW9ucy5GeCA9IHtcbiAgICAgIGZwczo0MCxcbiAgICAgIGR1cmF0aW9uOiAyNTAwLFxuICAgICAgdHJhbnNpdGlvbjogJGppdC5UcmFucy5RdWFydC5lYXNlSW5PdXQsXG4gICAgICBjbGVhckNhbnZhczogdHJ1ZVxuICAgIH07XG4gIChlbmQgY29kZSlcbiAgXG4gIEV4YW1wbGU6XG4gIFxuICAoc3RhcnQgY29kZSBqcylcbiAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeih7XG4gICAgZHVyYXRpb246IDEwMDAsXG4gICAgZnBzOiAzNSxcbiAgICB0cmFuc2l0aW9uOiAkaml0LlRyYW5zLmxpbmVhclxuICB9KTtcbiAgKGVuZCBjb2RlKVxuICBcbiAgUGFyYW1ldGVyczpcbiAgXG4gIGNsZWFyQ2FudmFzIC0gKGJvb2xlYW4pIERlZmF1bHQncyAqdHJ1ZSouIFdoZXRoZXIgdG8gY2xlYXIgdGhlIGZyYW1lL2NhbnZhcyB3aGVuIHRoZSB2aXogaXMgcGxvdHRlZCBvciBhbmltYXRlZC5cbiAgZHVyYXRpb24gLSAobnVtYmVyKSBEZWZhdWx0J3MgKjI1MDAqLiBEdXJhdGlvbiBvZiB0aGUgYW5pbWF0aW9uIGluIG1pbGxpc2Vjb25kcy5cbiAgZnBzIC0gKG51bWJlcikgRGVmYXVsdCdzICo0MCouIEZyYW1lcyBwZXIgc2Vjb25kLlxuICB0cmFuc2l0aW9uIC0gKG9iamVjdCkgRGVmYXVsdCdzICokaml0LlRyYW5zLlF1YXJ0LmVhc2VJbk91dCouIFRoZSB0cmFuc2l0aW9uIHVzZWQgZm9yIHRoZSBhbmltYXRpb25zLiBTZWUgYmVsb3cgZm9yIGEgbW9yZSBkZXRhaWxlZCBleHBsYW5hdGlvbi5cbiAgXG4gIE9iamVjdDogJGppdC5UcmFuc1xuICBcbiAgVGhpcyBvYmplY3QgaXMgdXNlZCBmb3Igc3BlY2lmeWluZyBkaWZmZXJlbnQgYW5pbWF0aW9uIHRyYW5zaXRpb25zIGluIGFsbCB2aXN1YWxpemF0aW9ucy5cblxuICBUaGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgdHlwZSBvZiBhbmltYXRpb24gdHJhbnNpdGlvbnMuXG5cbiAgbGluZWFyOlxuXG4gIERpc3BsYXlzIGEgbGluZWFyIHRyYW5zaXRpb25cblxuICA+VHJhbnMubGluZWFyXG4gIFxuICAoc2VlIExpbmVhci5wbmcpXG5cbiAgUXVhZDpcblxuICBEaXNwbGF5cyBhIFF1YWRyYXRpYyB0cmFuc2l0aW9uLlxuXG4gID5UcmFucy5RdWFkLmVhc2VJblxuICA+VHJhbnMuUXVhZC5lYXNlT3V0XG4gID5UcmFucy5RdWFkLmVhc2VJbk91dFxuICBcbiAoc2VlIFF1YWQucG5nKVxuXG4gQ3ViaWM6XG5cbiBEaXNwbGF5cyBhIEN1YmljIHRyYW5zaXRpb24uXG5cbiA+VHJhbnMuQ3ViaWMuZWFzZUluXG4gPlRyYW5zLkN1YmljLmVhc2VPdXRcbiA+VHJhbnMuQ3ViaWMuZWFzZUluT3V0XG5cbiAoc2VlIEN1YmljLnBuZylcblxuIFF1YXJ0OlxuXG4gRGlzcGxheXMgYSBRdWFydGV0aWMgdHJhbnNpdGlvbi5cblxuID5UcmFucy5RdWFydC5lYXNlSW5cbiA+VHJhbnMuUXVhcnQuZWFzZU91dFxuID5UcmFucy5RdWFydC5lYXNlSW5PdXRcblxuIChzZWUgUXVhcnQucG5nKVxuXG4gUXVpbnQ6XG5cbiBEaXNwbGF5cyBhIFF1aW50aWMgdHJhbnNpdGlvbi5cblxuID5UcmFucy5RdWludC5lYXNlSW5cbiA+VHJhbnMuUXVpbnQuZWFzZU91dFxuID5UcmFucy5RdWludC5lYXNlSW5PdXRcblxuIChzZWUgUXVpbnQucG5nKVxuXG4gRXhwbzpcblxuIERpc3BsYXlzIGFuIEV4cG9uZW50aWFsIHRyYW5zaXRpb24uXG5cbiA+VHJhbnMuRXhwby5lYXNlSW5cbiA+VHJhbnMuRXhwby5lYXNlT3V0XG4gPlRyYW5zLkV4cG8uZWFzZUluT3V0XG5cbiAoc2VlIEV4cG8ucG5nKVxuXG4gQ2lyYzpcblxuIERpc3BsYXlzIGEgQ2lyY3VsYXIgdHJhbnNpdGlvbi5cblxuID5UcmFucy5DaXJjLmVhc2VJblxuID5UcmFucy5DaXJjLmVhc2VPdXRcbiA+VHJhbnMuQ2lyYy5lYXNlSW5PdXRcblxuIChzZWUgQ2lyYy5wbmcpXG5cbiBTaW5lOlxuXG4gRGlzcGxheXMgYSBTaW5lb3VzaWRhbCB0cmFuc2l0aW9uLlxuXG4gPlRyYW5zLlNpbmUuZWFzZUluXG4gPlRyYW5zLlNpbmUuZWFzZU91dFxuID5UcmFucy5TaW5lLmVhc2VJbk91dFxuXG4gKHNlZSBTaW5lLnBuZylcblxuIEJhY2s6XG5cbiA+VHJhbnMuQmFjay5lYXNlSW5cbiA+VHJhbnMuQmFjay5lYXNlT3V0XG4gPlRyYW5zLkJhY2suZWFzZUluT3V0XG5cbiAoc2VlIEJhY2sucG5nKVxuXG4gQm91bmNlOlxuXG4gQm91bmN5IHRyYW5zaXRpb24uXG5cbiA+VHJhbnMuQm91bmNlLmVhc2VJblxuID5UcmFucy5Cb3VuY2UuZWFzZU91dFxuID5UcmFucy5Cb3VuY2UuZWFzZUluT3V0XG5cbiAoc2VlIEJvdW5jZS5wbmcpXG5cbiBFbGFzdGljOlxuXG4gRWxhc3RpYyBjdXJ2ZS5cblxuID5UcmFucy5FbGFzdGljLmVhc2VJblxuID5UcmFucy5FbGFzdGljLmVhc2VPdXRcbiA+VHJhbnMuRWxhc3RpYy5lYXNlSW5PdXRcblxuIChzZWUgRWxhc3RpYy5wbmcpXG4gXG4gQmFzZWQgb246XG4gICAgIFxuIEVhc2luZyBhbmQgVHJhbnNpdGlvbiBhbmltYXRpb24gbWV0aG9kcyBhcmUgYmFzZWQgaW4gdGhlIE1vb1Rvb2xzIEZyYW1ld29yayA8aHR0cDovL21vb3Rvb2xzLm5ldD4uIENvcHlyaWdodCAoYykgMjAwNi0yMDEwIFZhbGVyaW8gUHJvaWV0dGksIDxodHRwOi8vbWFkNG1pbGsubmV0Lz4uIE1JVCBsaWNlbnNlIDxodHRwOi8vbW9vdG9vbHMubmV0L2xpY2Vuc2UudHh0Pi5cblxuXG4qL1xuT3B0aW9ucy5GeCA9IHtcbiAgJGV4dGVuZDogdHJ1ZSxcbiAgXG4gIGZwczo0MCxcbiAgZHVyYXRpb246IDI1MDAsXG4gIHRyYW5zaXRpb246ICRqaXQuVHJhbnMuUXVhcnQuZWFzZUluT3V0LFxuICBjbGVhckNhbnZhczogdHJ1ZVxufTtcblxuLypcbiAqIEZpbGU6IE9wdGlvbnMuTGFiZWwuanNcbiAqXG4qL1xuLypcbiAgT2JqZWN0OiBPcHRpb25zLkxhYmVsXG5cbiAgUHJvdmlkZXMgc3R5bGluZyBmb3IgTGFiZWxzIHN1Y2ggYXMgZm9udCBzaXplLCBmYW1pbHksIGV0Yy4gQWxzbyBzZXRzIE5vZGUgbGFiZWxzIGFzIEhUTUwsIFNWRyBvciBOYXRpdmUgY2FudmFzIGVsZW1lbnRzLiAgXG5cbiAgU3ludGF4OlxuICBcbiAgKHN0YXJ0IGNvZGUganMpXG4gICAgT3B0aW9ucy5MYWJlbCA9IHtcbiAgICAgIG92ZXJyaWRhYmxlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdIVE1MJywgLy8nU1ZHJywgJ05hdGl2ZSdcbiAgICAgIHN0eWxlOiAnICcsXG4gICAgICBzaXplOiAxMCxcbiAgICAgIGZhbWlseTogJ3NhbnMtc2VyaWYnLFxuICAgICAgdGV4dEFsaWduOiAnY2VudGVyJyxcbiAgICAgIHRleHRCYXNlbGluZTogJ2FscGhhYmV0aWMnLFxuICAgICAgY29sb3I6ICcjZmZmJ1xuICAgIH07XG4gIChlbmQgY29kZSlcbiAgXG4gIEV4YW1wbGU6XG4gIFxuICAoc3RhcnQgY29kZSBqcylcbiAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeih7XG4gICAgTGFiZWw6IHtcbiAgICAgIHR5cGU6ICdOYXRpdmUnLFxuICAgICAgc2l6ZTogMTEsXG4gICAgICBjb2xvcjogJyNjY2MnXG4gICAgfVxuICB9KTtcbiAgKGVuZCBjb2RlKVxuICBcbiAgUGFyYW1ldGVyczpcbiAgICBcbiAgb3ZlcnJpZGFibGUgLSAoYm9vbGVhbikgRGVmYXVsdCdzICpmYWxzZSouIERldGVybWluZSB3aGV0aGVyIG9yIG5vdCBnZW5lcmFsIGxhYmVsIHByb3BlcnRpZXMgY2FuIGJlIG92ZXJyaWRkZW4gYnkgYSBwYXJ0aWN1bGFyIDxHcmFwaC5Ob2RlPi5cbiAgdHlwZSAtIChzdHJpbmcpIERlZmF1bHQncyAqSFRNTCouIFRoZSB0eXBlIGZvciB0aGUgbGFiZWxzLiBDYW4gYmUgJ0hUTUwnLCAnU1ZHJyBvciAnTmF0aXZlJyBjYW52YXMgbGFiZWxzLlxuICBzdHlsZSAtIChzdHJpbmcpIERlZmF1bHQncyAqZW1wdHkgc3RyaW5nKi4gQ2FuIGJlICdpdGFsaWMnIG9yICdib2xkJy4gVGhpcyBwYXJhbWV0ZXIgaXMgb25seSB0YWtlbiBpbnRvIGFjY291bnQgd2hlbiB1c2luZyAnTmF0aXZlJyBjYW52YXMgbGFiZWxzLiBGb3IgRE9NIGJhc2VkIGxhYmVscyB0aGUgY2xhc3NOYW1lICpub2RlKiBpcyBhZGRlZCB0byB0aGUgRE9NIGVsZW1lbnQgZm9yIHN0eWxpbmcgdmlhIENTUy4gWW91IGNhbiBhbHNvIHVzZSA8T3B0aW9ucy5Db250cm9sbGVyPiBtZXRob2RzIHRvIHN0eWxlIGluZGl2aWR1YWwgbGFiZWxzLlxuICBzaXplIC0gKG51bWJlcikgRGVmYXVsdCdzICoxMCouIFRoZSBmb250J3Mgc2l6ZS4gVGhpcyBwYXJhbWV0ZXIgaXMgb25seSB0YWtlbiBpbnRvIGFjY291bnQgd2hlbiB1c2luZyAnTmF0aXZlJyBjYW52YXMgbGFiZWxzLiBGb3IgRE9NIGJhc2VkIGxhYmVscyB0aGUgY2xhc3NOYW1lICpub2RlKiBpcyBhZGRlZCB0byB0aGUgRE9NIGVsZW1lbnQgZm9yIHN0eWxpbmcgdmlhIENTUy4gWW91IGNhbiBhbHNvIHVzZSA8T3B0aW9ucy5Db250cm9sbGVyPiBtZXRob2RzIHRvIHN0eWxlIGluZGl2aWR1YWwgbGFiZWxzLlxuICBmYW1pbHkgLSAoc3RyaW5nKSBEZWZhdWx0J3MgKnNhbnMtc2VyaWYqLiBUaGUgZm9udCdzIGZhbWlseS4gVGhpcyBwYXJhbWV0ZXIgaXMgb25seSB0YWtlbiBpbnRvIGFjY291bnQgd2hlbiB1c2luZyAnTmF0aXZlJyBjYW52YXMgbGFiZWxzLiBGb3IgRE9NIGJhc2VkIGxhYmVscyB0aGUgY2xhc3NOYW1lICpub2RlKiBpcyBhZGRlZCB0byB0aGUgRE9NIGVsZW1lbnQgZm9yIHN0eWxpbmcgdmlhIENTUy4gWW91IGNhbiBhbHNvIHVzZSA8T3B0aW9ucy5Db250cm9sbGVyPiBtZXRob2RzIHRvIHN0eWxlIGluZGl2aWR1YWwgbGFiZWxzLlxuICBjb2xvciAtIChzdHJpbmcpIERlZmF1bHQncyAqI2ZmZiouIFRoZSBmb250J3MgY29sb3IuIFRoaXMgcGFyYW1ldGVyIGlzIG9ubHkgdGFrZW4gaW50byBhY2NvdW50IHdoZW4gdXNpbmcgJ05hdGl2ZScgY2FudmFzIGxhYmVscy4gRm9yIERPTSBiYXNlZCBsYWJlbHMgdGhlIGNsYXNzTmFtZSAqbm9kZSogaXMgYWRkZWQgdG8gdGhlIERPTSBlbGVtZW50IGZvciBzdHlsaW5nIHZpYSBDU1MuIFlvdSBjYW4gYWxzbyB1c2UgPE9wdGlvbnMuQ29udHJvbGxlcj4gbWV0aG9kcyB0byBzdHlsZSBpbmRpdmlkdWFsIGxhYmVscy5cbiovXG5PcHRpb25zLkxhYmVsID0ge1xuICAkZXh0ZW5kOiBmYWxzZSxcbiAgXG4gIG92ZXJyaWRhYmxlOiBmYWxzZSxcbiAgdHlwZTogJ0hUTUwnLCAvLydTVkcnLCAnTmF0aXZlJ1xuICBzdHlsZTogJyAnLFxuICBzaXplOiAxMCxcbiAgZmFtaWx5OiAnc2Fucy1zZXJpZicsXG4gIHRleHRBbGlnbjogJ2NlbnRlcicsXG4gIHRleHRCYXNlbGluZTogJ2FscGhhYmV0aWMnLFxuICBjb2xvcjogJyNmZmYnXG59O1xuXG5cbi8qXG4gKiBGaWxlOiBPcHRpb25zLlRpcHMuanNcbiAqXG4gKi9cblxuLypcbiAgT2JqZWN0OiBPcHRpb25zLlRpcHNcbiAgXG4gIFRpcHMgb3B0aW9uc1xuICBcbiAgU3ludGF4OlxuICAgIFxuICAoc3RhcnQgY29kZSBqcylcbiAgT3B0aW9ucy5UaXBzID0ge1xuICAgIGVuYWJsZTogZmFsc2UsXG4gICAgdHlwZTogJ2F1dG8nLFxuICAgIG9mZnNldFg6IDIwLFxuICAgIG9mZnNldFk6IDIwLFxuICAgIG9uU2hvdzogJC5lbXB0eSxcbiAgICBvbkhpZGU6ICQuZW1wdHlcbiAgfTtcbiAgKGVuZCBjb2RlKVxuICBcbiAgRXhhbXBsZTpcbiAgXG4gIChzdGFydCBjb2RlIGpzKVxuICB2YXIgdml6ID0gbmV3ICRqaXQuVml6KHtcbiAgICBUaXBzOiB7XG4gICAgICBlbmFibGU6IHRydWUsXG4gICAgICB0eXBlOiAnTmF0aXZlJyxcbiAgICAgIG9mZnNldFg6IDEwLFxuICAgICAgb2Zmc2V0WTogMTAsXG4gICAgICBvblNob3c6IGZ1bmN0aW9uKHRpcCwgbm9kZSkge1xuICAgICAgICB0aXAuaW5uZXJIVE1MID0gbm9kZS5uYW1lO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIChlbmQgY29kZSlcblxuICBQYXJhbWV0ZXJzOlxuXG4gIGVuYWJsZSAtIChib29sZWFuKSBEZWZhdWx0J3MgKmZhbHNlKi4gSWYgKnRydWUqLCBhIHRvb2x0aXAgd2lsbCBiZSBzaG93biB3aGVuIGEgbm9kZSBpcyBob3ZlcmVkLiBUaGUgdG9vbHRpcCBpcyBhIGRpdiBET00gZWxlbWVudCBoYXZpbmcgXCJ0aXBcIiBhcyBDU1MgY2xhc3MuIFxuICB0eXBlIC0gKHN0cmluZykgRGVmYXVsdCdzICphdXRvKi4gRGVmaW5lcyB3aGVyZSB0byBhdHRhY2ggdGhlIE1vdXNlRW50ZXIvTGVhdmUgdG9vbHRpcCBldmVudHMuIFBvc3NpYmxlIHZhbHVlcyBhcmUgJ05hdGl2ZScgdG8gYXR0YWNoIHRoZW0gdG8gdGhlIGNhbnZhcyBvciAnSFRNTCcgdG8gYXR0YWNoIHRoZW0gdG8gRE9NIGxhYmVsIGVsZW1lbnRzIChpZiBkZWZpbmVkKS4gJ2F1dG8nIHNldHMgdGhpcyBwcm9wZXJ0eSB0byB0aGUgdmFsdWUgb2YgPE9wdGlvbnMuTGFiZWw+J3MgKnR5cGUqIHByb3BlcnR5LlxuICBvZmZzZXRYIC0gKG51bWJlcikgRGVmYXVsdCdzICoyMCouIEFuIG9mZnNldCBhZGRlZCB0byB0aGUgY3VycmVudCB0b29sdGlwIHgtcG9zaXRpb24gKHdoaWNoIGlzIHRoZSBzYW1lIGFzIHRoZSBjdXJyZW50IG1vdXNlIHBvc2l0aW9uKS4gRGVmYXVsdCdzIDIwLlxuICBvZmZzZXRZIC0gKG51bWJlcikgRGVmYXVsdCdzICoyMCouIEFuIG9mZnNldCBhZGRlZCB0byB0aGUgY3VycmVudCB0b29sdGlwIHktcG9zaXRpb24gKHdoaWNoIGlzIHRoZSBzYW1lIGFzIHRoZSBjdXJyZW50IG1vdXNlIHBvc2l0aW9uKS4gRGVmYXVsdCdzIDIwLlxuICBvblNob3codGlwLCBub2RlKSAtIFRoaXMgY2FsbGFjayBpcyB1c2VkIHJpZ2h0IGJlZm9yZSBkaXNwbGF5aW5nIGEgdG9vbHRpcC4gVGhlIGZpcnN0IGZvcm1hbCBwYXJhbWV0ZXIgaXMgdGhlIHRpcCBpdHNlbGYgKHdoaWNoIGlzIGEgRGl2RWxlbWVudCkuIFRoZSBzZWNvbmQgcGFyYW1ldGVyIG1heSBiZSBhIDxHcmFwaC5Ob2RlPiBmb3IgZ3JhcGggYmFzZWQgdmlzdWFsaXphdGlvbnMgb3IgYW4gb2JqZWN0IHdpdGggbGFiZWwsIHZhbHVlIHByb3BlcnRpZXMgZm9yIGNoYXJ0cy5cbiAgb25IaWRlKCkgLSBUaGlzIGNhbGxhY2sgaXMgdXNlZCB3aGVuIGhpZGluZyBhIHRvb2x0aXAuXG5cbiovXG5PcHRpb25zLlRpcHMgPSB7XG4gICRleHRlbmQ6IGZhbHNlLFxuICBcbiAgZW5hYmxlOiBmYWxzZSxcbiAgdHlwZTogJ2F1dG8nLFxuICBvZmZzZXRYOiAyMCxcbiAgb2Zmc2V0WTogMjAsXG4gIGZvcmNlOiBmYWxzZSxcbiAgb25TaG93OiAkLmVtcHR5LFxuICBvbkhpZGU6ICQuZW1wdHlcbn07XG5cblxuLypcbiAqIEZpbGU6IE9wdGlvbnMuTm9kZVN0eWxlcy5qc1xuICpcbiAqL1xuXG4vKlxuICBPYmplY3Q6IE9wdGlvbnMuTm9kZVN0eWxlc1xuICBcbiAgQXBwbHkgZGlmZmVyZW50IHN0eWxlcyB3aGVuIGEgbm9kZSBpcyBob3ZlcmVkIG9yIHNlbGVjdGVkLlxuICBcbiAgU3ludGF4OlxuICAgIFxuICAoc3RhcnQgY29kZSBqcylcbiAgT3B0aW9ucy5Ob2RlU3R5bGVzID0ge1xuICAgIGVuYWJsZTogZmFsc2UsXG4gICAgdHlwZTogJ2F1dG8nLFxuICAgIHN0eWxlc0hvdmVyOiBmYWxzZSxcbiAgICBzdHlsZXNDbGljazogZmFsc2VcbiAgfTtcbiAgKGVuZCBjb2RlKVxuICBcbiAgRXhhbXBsZTpcbiAgXG4gIChzdGFydCBjb2RlIGpzKVxuICB2YXIgdml6ID0gbmV3ICRqaXQuVml6KHtcbiAgICBOb2RlU3R5bGVzOiB7XG4gICAgICBlbmFibGU6IHRydWUsXG4gICAgICB0eXBlOiAnTmF0aXZlJyxcbiAgICAgIHN0eWxlc0hvdmVyOiB7XG4gICAgICAgIGRpbTogMzAsXG4gICAgICAgIGNvbG9yOiAnI2ZjYydcbiAgICAgIH0sXG4gICAgICBkdXJhdGlvbjogNjAwXG4gICAgfVxuICB9KTtcbiAgKGVuZCBjb2RlKVxuXG4gIFBhcmFtZXRlcnM6XG4gIFxuICBlbmFibGUgLSAoYm9vbGVhbikgRGVmYXVsdCdzICpmYWxzZSouIFdoZXRoZXIgdG8gZW5hYmxlIHRoaXMgb3B0aW9uLlxuICB0eXBlIC0gKHN0cmluZykgRGVmYXVsdCdzICphdXRvKi4gVXNlIHRoaXMgdG8gYXR0YWNoIHRoZSBob3Zlci9jbGljayBldmVudHMgaW4gdGhlIG5vZGVzIG9yIHRoZSBub2RlcyBsYWJlbHMgKGlmIHRoZXkgaGF2ZSBiZWVuIGRlZmluZWQgYXMgRE9NIGVsZW1lbnRzOiAnSFRNTCcgb3IgJ1NWRycsIHNlZSA8T3B0aW9ucy5MYWJlbD4gZm9yIG1vcmUgZGV0YWlscykuIFRoZSBkZWZhdWx0ICdhdXRvJyB2YWx1ZSB3aWxsIHNldCBOb2RlU3R5bGVzIHRvIHRoZSBzYW1lIHR5cGUgZGVmaW5lZCBmb3IgPE9wdGlvbnMuTGFiZWw+LlxuICBzdHlsZXNIb3ZlciAtIChib29sZWFufG9iamVjdCkgRGVmYXVsdCdzICpmYWxzZSouIEFuIG9iamVjdCB3aXRoIG5vZGUgc3R5bGVzIGp1c3QgbGlrZSB0aGUgb25lcyBkZWZpbmVkIGZvciA8T3B0aW9ucy5Ob2RlPiBvciAqZmFsc2UqIG90aGVyd2lzZS5cbiAgc3R5bGVzQ2xpY2sgLSAoYm9vbGVhbnxvYmplY3QpIERlZmF1bHQncyAqZmFsc2UqLiBBbiBvYmplY3Qgd2l0aCBub2RlIHN0eWxlcyBqdXN0IGxpa2UgdGhlIG9uZXMgZGVmaW5lZCBmb3IgPE9wdGlvbnMuTm9kZT4gb3IgKmZhbHNlKiBvdGhlcndpc2UuXG4qL1xuXG5PcHRpb25zLk5vZGVTdHlsZXMgPSB7XG4gICRleHRlbmQ6IGZhbHNlLFxuICBcbiAgZW5hYmxlOiBmYWxzZSxcbiAgdHlwZTogJ2F1dG8nLFxuICBzdHlsZXNIb3ZlcjogZmFsc2UsXG4gIHN0eWxlc0NsaWNrOiBmYWxzZVxufTtcblxuXG4vKlxuICogRmlsZTogT3B0aW9ucy5FdmVudHMuanNcbiAqXG4qL1xuXG4vKlxuICBPYmplY3Q6IE9wdGlvbnMuRXZlbnRzXG4gIFxuICBDb25maWd1cmF0aW9uIGZvciBhZGRpbmcgbW91c2UvdG91Y2ggZXZlbnQgaGFuZGxlcnMgdG8gTm9kZXMuXG4gIFxuICBTeW50YXg6XG4gIFxuICAoc3RhcnQgY29kZSBqcylcbiAgT3B0aW9ucy5FdmVudHMgPSB7XG4gICAgZW5hYmxlOiBmYWxzZSxcbiAgICBlbmFibGVGb3JFZGdlczogZmFsc2UsXG4gICAgdHlwZTogJ2F1dG8nLFxuICAgIG9uQ2xpY2s6ICQuZW1wdHksXG4gICAgb25SaWdodENsaWNrOiAkLmVtcHR5LFxuICAgIG9uTW91c2VNb3ZlOiAkLmVtcHR5LFxuICAgIG9uTW91c2VFbnRlcjogJC5lbXB0eSxcbiAgICBvbk1vdXNlTGVhdmU6ICQuZW1wdHksXG4gICAgb25EcmFnU3RhcnQ6ICQuZW1wdHksXG4gICAgb25EcmFnTW92ZTogJC5lbXB0eSxcbiAgICBvbkRyYWdDYW5jZWw6ICQuZW1wdHksXG4gICAgb25EcmFnRW5kOiAkLmVtcHR5LFxuICAgIG9uVG91Y2hTdGFydDogJC5lbXB0eSxcbiAgICBvblRvdWNoTW92ZTogJC5lbXB0eSxcbiAgICBvblRvdWNoRW5kOiAkLmVtcHR5LFxuICAgIG9uVG91Y2hDYW5jZWw6ICQuZW1wdHksXG4gICAgb25Nb3VzZVdoZWVsOiAkLmVtcHR5XG4gIH07XG4gIChlbmQgY29kZSlcbiAgXG4gIEV4YW1wbGU6XG4gIFxuICAoc3RhcnQgY29kZSBqcylcbiAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeih7XG4gICAgRXZlbnRzOiB7XG4gICAgICBlbmFibGU6IHRydWUsXG4gICAgICBvbkNsaWNrOiBmdW5jdGlvbihub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICAgICAgdml6LmRvU29tZXRoaW5nKCk7XG4gICAgICB9LFxuICAgICAgb25Nb3VzZUVudGVyOiBmdW5jdGlvbihub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICAgICAgdml6LmNhbnZhcy5nZXRFbGVtZW50KCkuc3R5bGUuY3Vyc29yID0gJ3BvaW50ZXInO1xuICAgICAgfSxcbiAgICAgIG9uTW91c2VMZWF2ZTogZnVuY3Rpb24obm9kZSwgZXZlbnRJbmZvLCBlKSB7XG4gICAgICAgIHZpei5jYW52YXMuZ2V0RWxlbWVudCgpLnN0eWxlLmN1cnNvciA9ICcnO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIChlbmQgY29kZSlcbiAgXG4gIFBhcmFtZXRlcnM6XG4gIFxuICBlbmFibGUgLSAoYm9vbGVhbikgRGVmYXVsdCdzICpmYWxzZSouIFdoZXRoZXIgdG8gZW5hYmxlIHRoZSBFdmVudCBzeXN0ZW0uXG4gIGVuYWJsZUZvckVkZ2VzIC0gKGJvb2xlYW4pIERlZmF1bHQncyAqZmFsc2UqLiBXaGV0aGVyIHRvIHRyYWNrIGV2ZW50cyBhbHNvIGluIGFyY3MuIElmICp0cnVlKiB0aGUgc2FtZSBjYWxsYmFja3MgLWRlc2NyaWJlZCBiZWxvdy0gYXJlIHVzZWQgZm9yIG5vZGVzICphbmQqIGVkZ2VzLiBBIHNpbXBsZSBkdWNrIHR5cGUgY2hlY2sgZm9yIGVkZ2VzIGlzIHRvIGNoZWNrIGZvciAqbm9kZS5ub2RlRnJvbSouXG4gIHR5cGUgLSAoc3RyaW5nKSBEZWZhdWx0J3MgJ2F1dG8nLiBXaGV0aGVyIHRvIGF0dGFjaCB0aGUgZXZlbnRzIG9udG8gdGhlIEhUTUwgbGFiZWxzICh2aWEgZXZlbnQgZGVsZWdhdGlvbikgb3IgdG8gdXNlIHRoZSBjdXN0b20gJ05hdGl2ZScgY2FudmFzIEV2ZW50IFN5c3RlbSBvZiB0aGUgbGlicmFyeS4gJ2F1dG8nIGlzIHNldCB3aGVuIHlvdSBsZXQgdGhlIDxPcHRpb25zLkxhYmVsPiAqdHlwZSogcGFyYW1ldGVyIGRlY2lkZSB0aGlzLlxuICBvbkNsaWNrKG5vZGUsIGV2ZW50SW5mbywgZSkgLSBUcmlnZ2VyZWQgd2hlbiBhIHVzZXIgcGVyZm9ybXMgYSBjbGljayBpbiB0aGUgY2FudmFzLiAqbm9kZSogaXMgdGhlIDxHcmFwaC5Ob2RlPiBjbGlja2VkIG9yIGZhbHNlIGlmIG5vIG5vZGUgaGFzIGJlZW4gY2xpY2tlZC4gKmUqIGlzIHRoZSBncmFiYmVkIGV2ZW50IChzaG91bGQgcmV0dXJuIHRoZSBuYXRpdmUgZXZlbnQgaW4gYSBjcm9zcy1icm93c2VyIG1hbm5lcikuICpldmVudEluZm8qIGlzIGFuIG9iamVjdCBjb250YWluaW5nIHVzZWZ1bCBtZXRob2RzIGxpa2UgKmdldFBvcyogdG8gZ2V0IHRoZSBtb3VzZSBwb3NpdGlvbiByZWxhdGl2ZSB0byB0aGUgY2FudmFzLiBcbiAgb25SaWdodENsaWNrKG5vZGUsIGV2ZW50SW5mbywgZSkgLSBUcmlnZ2VyZWQgd2hlbiBhIHVzZXIgcGVyZm9ybXMgYSByaWdodCBjbGljayBpbiB0aGUgY2FudmFzLiAqbm9kZSogaXMgdGhlIDxHcmFwaC5Ob2RlPiByaWdodCBjbGlja2VkIG9yIGZhbHNlIGlmIG5vIG5vZGUgaGFzIGJlZW4gY2xpY2tlZC4gKmUqIGlzIHRoZSBncmFiYmVkIGV2ZW50IChzaG91bGQgcmV0dXJuIHRoZSBuYXRpdmUgZXZlbnQgaW4gYSBjcm9zcy1icm93c2VyIG1hbm5lcikuICpldmVudEluZm8qIGlzIGFuIG9iamVjdCBjb250YWluaW5nIHVzZWZ1bCBtZXRob2RzIGxpa2UgKmdldFBvcyogdG8gZ2V0IHRoZSBtb3VzZSBwb3NpdGlvbiByZWxhdGl2ZSB0byB0aGUgY2FudmFzLiBcbiAgb25Nb3VzZU1vdmUobm9kZSwgZXZlbnRJbmZvLCBlKSAtIFRyaWdnZXJlZCB3aGVuIHRoZSB1c2VyIG1vdmVzIHRoZSBtb3VzZS4gKm5vZGUqIGlzIHRoZSA8R3JhcGguTm9kZT4gdW5kZXIgdGhlIGN1cnNvciBhcyBpdCdzIG1vdmluZyBvdmVyIHRoZSBjYW52YXMgb3IgZmFsc2UgaWYgbm8gbm9kZSBoYXMgYmVlbiBjbGlja2VkLiAqZSogaXMgdGhlIGdyYWJiZWQgZXZlbnQgKHNob3VsZCByZXR1cm4gdGhlIG5hdGl2ZSBldmVudCBpbiBhIGNyb3NzLWJyb3dzZXIgbWFubmVyKS4gICpldmVudEluZm8qIGlzIGFuIG9iamVjdCBjb250YWluaW5nIHVzZWZ1bCBtZXRob2RzIGxpa2UgKmdldFBvcyogdG8gZ2V0IHRoZSBtb3VzZSBwb3NpdGlvbiByZWxhdGl2ZSB0byB0aGUgY2FudmFzLlxuICBvbk1vdXNlRW50ZXIobm9kZSwgZXZlbnRJbmZvLCBlKSAtIFRyaWdnZXJlZCB3aGVuIGEgdXNlciBtb3ZlcyB0aGUgbW91c2Ugb3ZlciBhIG5vZGUuICpub2RlKiBpcyB0aGUgPEdyYXBoLk5vZGU+IHRoYXQgdGhlIG1vdXNlIGp1c3QgZW50ZXJlZC4gKmUqIGlzIHRoZSBncmFiYmVkIGV2ZW50IChzaG91bGQgcmV0dXJuIHRoZSBuYXRpdmUgZXZlbnQgaW4gYSBjcm9zcy1icm93c2VyIG1hbm5lcikuICpldmVudEluZm8qIGlzIGFuIG9iamVjdCBjb250YWluaW5nIHVzZWZ1bCBtZXRob2RzIGxpa2UgKmdldFBvcyogdG8gZ2V0IHRoZSBtb3VzZSBwb3NpdGlvbiByZWxhdGl2ZSB0byB0aGUgY2FudmFzLiBcbiAgb25Nb3VzZUxlYXZlKG5vZGUsIGV2ZW50SW5mbywgZSkgLSBUcmlnZ2VyZWQgd2hlbiB0aGUgdXNlciBtb3VzZS1vdXRzIGEgbm9kZS4gKm5vZGUqIGlzIHRoZSA8R3JhcGguTm9kZT4gJ21vdXNlLW91dGVkJy4gKmUqIGlzIHRoZSBncmFiYmVkIGV2ZW50IChzaG91bGQgcmV0dXJuIHRoZSBuYXRpdmUgZXZlbnQgaW4gYSBjcm9zcy1icm93c2VyIG1hbm5lcikuICpldmVudEluZm8qIGlzIGFuIG9iamVjdCBjb250YWluaW5nIHVzZWZ1bCBtZXRob2RzIGxpa2UgKmdldFBvcyogdG8gZ2V0IHRoZSBtb3VzZSBwb3NpdGlvbiByZWxhdGl2ZSB0byB0aGUgY2FudmFzLiBcbiAgb25EcmFnU3RhcnQobm9kZSwgZXZlbnRJbmZvLCBlKSAtIFRyaWdnZXJlZCB3aGVuIHRoZSB1c2VyIG1vdXNlLWRvd25zIG92ZXIgYSBub2RlLiAqbm9kZSogaXMgdGhlIDxHcmFwaC5Ob2RlPiBiZWluZyBwcmVzc2VkLiAqZSogaXMgdGhlIGdyYWJiZWQgZXZlbnQgKHNob3VsZCByZXR1cm4gdGhlIG5hdGl2ZSBldmVudCBpbiBhIGNyb3NzLWJyb3dzZXIgbWFubmVyKS4gKmV2ZW50SW5mbyogaXMgYW4gb2JqZWN0IGNvbnRhaW5pbmcgdXNlZnVsIG1ldGhvZHMgbGlrZSAqZ2V0UG9zKiB0byBnZXQgdGhlIG1vdXNlIHBvc2l0aW9uIHJlbGF0aXZlIHRvIHRoZSBjYW52YXMuIFxuICBvbkRyYWdNb3ZlKG5vZGUsIGV2ZW50SW5mbywgZSkgLSBUcmlnZ2VyZWQgd2hlbiBhIHVzZXIsIGFmdGVyIHByZXNzaW5nIHRoZSBtb3VzZSBidXR0b24gb3ZlciBhIG5vZGUsIG1vdmVzIHRoZSBtb3VzZSBhcm91bmQuICpub2RlKiBpcyB0aGUgPEdyYXBoLk5vZGU+IGJlaW5nIGRyYWdnZWQuICplKiBpcyB0aGUgZ3JhYmJlZCBldmVudCAoc2hvdWxkIHJldHVybiB0aGUgbmF0aXZlIGV2ZW50IGluIGEgY3Jvc3MtYnJvd3NlciBtYW5uZXIpLiAqZXZlbnRJbmZvKiBpcyBhbiBvYmplY3QgY29udGFpbmluZyB1c2VmdWwgbWV0aG9kcyBsaWtlICpnZXRQb3MqIHRvIGdldCB0aGUgbW91c2UgcG9zaXRpb24gcmVsYXRpdmUgdG8gdGhlIGNhbnZhcy4gXG4gIG9uRHJhZ0VuZChub2RlLCBldmVudEluZm8sIGUpIC0gVHJpZ2dlcmVkIHdoZW4gYSB1c2VyIGZpbmlzaGVkIGRyYWdnaW5nIGEgbm9kZS4gKm5vZGUqIGlzIHRoZSA8R3JhcGguTm9kZT4gYmVpbmcgZHJhZ2dlZC4gKmUqIGlzIHRoZSBncmFiYmVkIGV2ZW50IChzaG91bGQgcmV0dXJuIHRoZSBuYXRpdmUgZXZlbnQgaW4gYSBjcm9zcy1icm93c2VyIG1hbm5lcikuICpldmVudEluZm8qIGlzIGFuIG9iamVjdCBjb250YWluaW5nIHVzZWZ1bCBtZXRob2RzIGxpa2UgKmdldFBvcyogdG8gZ2V0IHRoZSBtb3VzZSBwb3NpdGlvbiByZWxhdGl2ZSB0byB0aGUgY2FudmFzLiBcbiAgb25EcmFnQ2FuY2VsKG5vZGUsIGV2ZW50SW5mbywgZSkgLSBUcmlnZ2VyZWQgd2hlbiB0aGUgdXNlciByZWxlYXNlcyB0aGUgbW91c2UgYnV0dG9uIG92ZXIgYSA8R3JhcGguTm9kZT4gdGhhdCB3YXNuJ3QgZHJhZ2dlZCAoaS5lLiB0aGUgdXNlciBkaWRuJ3QgcGVyZm9ybSBhbnkgbW91c2UgbW92ZW1lbnQgYWZ0ZXIgcHJlc3NpbmcgdGhlIG1vdXNlIGJ1dHRvbikuICpub2RlKiBpcyB0aGUgPEdyYXBoLk5vZGU+IGJlaW5nIGRyYWdnZWQuICplKiBpcyB0aGUgZ3JhYmJlZCBldmVudCAoc2hvdWxkIHJldHVybiB0aGUgbmF0aXZlIGV2ZW50IGluIGEgY3Jvc3MtYnJvd3NlciBtYW5uZXIpLiAqZXZlbnRJbmZvKiBpcyBhbiBvYmplY3QgY29udGFpbmluZyB1c2VmdWwgbWV0aG9kcyBsaWtlICpnZXRQb3MqIHRvIGdldCB0aGUgbW91c2UgcG9zaXRpb24gcmVsYXRpdmUgdG8gdGhlIGNhbnZhcy4gXG4gIG9uVG91Y2hTdGFydChub2RlLCBldmVudEluZm8sIGUpIC0gQmVoYXZlcyBqdXN0IGxpa2Ugb25EcmFnU3RhcnQuIFxuICBvblRvdWNoTW92ZShub2RlLCBldmVudEluZm8sIGUpIC0gQmVoYXZlcyBqdXN0IGxpa2Ugb25EcmFnTW92ZS4gXG4gIG9uVG91Y2hFbmQobm9kZSwgZXZlbnRJbmZvLCBlKSAtIEJlaGF2ZXMganVzdCBsaWtlIG9uRHJhZ0VuZC4gXG4gIG9uVG91Y2hDYW5jZWwobm9kZSwgZXZlbnRJbmZvLCBlKSAtIEJlaGF2ZXMganVzdCBsaWtlIG9uRHJhZ0NhbmNlbC5cbiAgb25Nb3VzZVdoZWVsKGRlbHRhLCBlKSAtIFRyaWdnZXJlZCB3aGVuIHRoZSB1c2VyIHVzZXMgdGhlIG1vdXNlIHNjcm9sbCBvdmVyIHRoZSBjYW52YXMuICpkZWx0YSogaXMgMSBvciAtMSBkZXBlbmRpbmcgb24gdGhlIHNlbnNlIG9mIHRoZSBtb3VzZSBzY3JvbGwuXG4qL1xuXG5PcHRpb25zLkV2ZW50cyA9IHtcbiAgJGV4dGVuZDogZmFsc2UsXG4gIFxuICBlbmFibGU6IGZhbHNlLFxuICBlbmFibGVGb3JFZGdlczogZmFsc2UsXG4gIHR5cGU6ICdhdXRvJyxcbiAgb25DbGljazogJC5lbXB0eSxcbiAgb25SaWdodENsaWNrOiAkLmVtcHR5LFxuICBvbk1vdXNlTW92ZTogJC5lbXB0eSxcbiAgb25Nb3VzZUVudGVyOiAkLmVtcHR5LFxuICBvbk1vdXNlTGVhdmU6ICQuZW1wdHksXG4gIG9uRHJhZ1N0YXJ0OiAkLmVtcHR5LFxuICBvbkRyYWdNb3ZlOiAkLmVtcHR5LFxuICBvbkRyYWdDYW5jZWw6ICQuZW1wdHksXG4gIG9uRHJhZ0VuZDogJC5lbXB0eSxcbiAgb25Ub3VjaFN0YXJ0OiAkLmVtcHR5LFxuICBvblRvdWNoTW92ZTogJC5lbXB0eSxcbiAgb25Ub3VjaEVuZDogJC5lbXB0eSxcbiAgb25Nb3VzZVdoZWVsOiAkLmVtcHR5XG59O1xuXG4vKlxuICogRmlsZTogT3B0aW9ucy5OYXZpZ2F0aW9uLmpzXG4gKlxuKi9cblxuLypcbiAgT2JqZWN0OiBPcHRpb25zLk5hdmlnYXRpb25cbiAgXG4gIFBhbm5pbmcgYW5kIHpvb21pbmcgb3B0aW9ucyBmb3IgR3JhcGgvVHJlZSBiYXNlZCB2aXN1YWxpemF0aW9ucy4gVGhlc2Ugb3B0aW9ucyBhcmUgaW1wbGVtZW50ZWQgXG4gIGJ5IGFsbCB2aXN1YWxpemF0aW9ucyBleGNlcHQgY2hhcnRzICg8QXJlYUNoYXJ0PiwgPEJhckNoYXJ0PiBhbmQgPFBpZUNoYXJ0PikuXG4gIFxuICBTeW50YXg6XG4gIFxuICAoc3RhcnQgY29kZSBqcylcblxuICBPcHRpb25zLk5hdmlnYXRpb24gPSB7XG4gICAgZW5hYmxlOiBmYWxzZSxcbiAgICB0eXBlOiAnYXV0bycsXG4gICAgcGFubmluZzogZmFsc2UsIC8vdHJ1ZSwgJ2F2b2lkIG5vZGVzJ1xuICAgIHpvb21pbmc6IGZhbHNlXG4gIH07XG4gIFxuICAoZW5kIGNvZGUpXG4gIFxuICBFeGFtcGxlOlxuICAgIFxuICAoc3RhcnQgY29kZSBqcylcbiAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeih7XG4gICAgTmF2aWdhdGlvbjoge1xuICAgICAgZW5hYmxlOiB0cnVlLFxuICAgICAgcGFubmluZzogJ2F2b2lkIG5vZGVzJyxcbiAgICAgIHpvb21pbmc6IDIwXG4gICAgfVxuICB9KTtcbiAgKGVuZCBjb2RlKVxuICBcbiAgUGFyYW1ldGVyczpcbiAgXG4gIGVuYWJsZSAtIChib29sZWFuKSBEZWZhdWx0J3MgKmZhbHNlKi4gV2hldGhlciB0byBlbmFibGUgTmF2aWdhdGlvbiBjYXBhYmlsaXRpZXMuXG4gIHR5cGUgLSAoc3RyaW5nKSBEZWZhdWx0J3MgJ2F1dG8nLiBXaGV0aGVyIHRvIGF0dGFjaCB0aGUgbmF2aWdhdGlvbiBldmVudHMgb250byB0aGUgSFRNTCBsYWJlbHMgKHZpYSBldmVudCBkZWxlZ2F0aW9uKSBvciB0byB1c2UgdGhlIGN1c3RvbSAnTmF0aXZlJyBjYW52YXMgRXZlbnQgU3lzdGVtIG9mIHRoZSBsaWJyYXJ5LiBXaGVuICdhdXRvJyBzZXQgd2hlbiB5b3UgbGV0IHRoZSA8T3B0aW9ucy5MYWJlbD4gKnR5cGUqIHBhcmFtZXRlciBkZWNpZGUgdGhpcy5cbiAgcGFubmluZyAtIChib29sZWFufHN0cmluZykgRGVmYXVsdCdzICpmYWxzZSouIFNldCB0aGlzIHByb3BlcnR5IHRvICp0cnVlKiBpZiB5b3Ugd2FudCB0byBhZGQgRHJhZyBhbmQgRHJvcCBwYW5uaW5nIHN1cHBvcnQgdG8gdGhlIHZpc3VhbGl6YXRpb24uIFlvdSBjYW4gYWxzbyBzZXQgdGhpcyBwYXJhbWV0ZXIgdG8gJ2F2b2lkIG5vZGVzJyB0byBlbmFibGUgRG5EIHBhbm5pbmcgYnV0IGRpc2FibGUgaXQgaWYgdGhlIERuRCBpcyB0YWtpbmcgcGxhY2Ugb3ZlciBhIG5vZGUuIFRoaXMgaXMgdXNlZnVsIHdoZW4gc29tZSBvdGhlciBldmVudHMgbGlrZSBEcmFnICYgRHJvcCBmb3Igbm9kZXMgYXJlIGFkZGVkIHRvIDxHcmFwaC5Ob2Rlcz4uXG4gIHpvb21pbmcgLSAoYm9vbGVhbnxudW1iZXIpIERlZmF1bHQncyAqZmFsc2UqLiBTZXQgdGhpcyBwcm9wZXJ0eSB0byBhIG51bWVyaWMgdmFsdWUgdG8gdHVybiBtb3VzZS1zY3JvbGwgem9vbWluZyBvbi4gVGhlIG51bWJlciB3aWxsIGJlIHByb3BvcnRpb25hbCB0byB0aGUgbW91c2Utc2Nyb2xsIHNlbnNpdGl2aXR5LlxuICBcbiovXG5cbk9wdGlvbnMuTmF2aWdhdGlvbiA9IHtcbiAgJGV4dGVuZDogZmFsc2UsXG4gIFxuICBlbmFibGU6IGZhbHNlLFxuICB0eXBlOiAnYXV0bycsXG4gIHBhbm5pbmc6IGZhbHNlLCAvL3RydWUgfCAnYXZvaWQgbm9kZXMnXG4gIHpvb21pbmc6IGZhbHNlXG59O1xuXG4vKlxuICogRmlsZTogT3B0aW9ucy5Db250cm9sbGVyLmpzXG4gKlxuKi9cblxuLypcbiAgT2JqZWN0OiBPcHRpb25zLkNvbnRyb2xsZXJcbiAgXG4gIFByb3ZpZGVzIGNvbnRyb2xsZXIgbWV0aG9kcy4gQ29udHJvbGxlciBtZXRob2RzIGFyZSBjYWxsYmFjayBmdW5jdGlvbnMgdGhhdCBnZXQgY2FsbGVkIGF0IGRpZmZlcmVudCBzdGFnZXMgXG4gIG9mIHRoZSBhbmltYXRpb24sIGNvbXB1dGluZyBvciBwbG90dGluZyBvZiB0aGUgdmlzdWFsaXphdGlvbi5cbiAgXG4gIEltcGxlbWVudGVkIGJ5OlxuICAgIFxuICBBbGwgdmlzdWFsaXphdGlvbnMgZXhjZXB0IGNoYXJ0cyAoPEFyZWFDaGFydD4sIDxCYXJDaGFydD4gYW5kIDxQaWVDaGFydD4pLlxuICBcbiAgU3ludGF4OlxuICBcbiAgKHN0YXJ0IGNvZGUganMpXG5cbiAgT3B0aW9ucy5Db250cm9sbGVyID0ge1xuICAgIG9uQmVmb3JlQ29tcHV0ZTogJC5lbXB0eSxcbiAgICBvbkFmdGVyQ29tcHV0ZTogICQuZW1wdHksXG4gICAgb25DcmVhdGVMYWJlbDogICAkLmVtcHR5LFxuICAgIG9uUGxhY2VMYWJlbDogICAgJC5lbXB0eSxcbiAgICBvbkNvbXBsZXRlOiAgICAgICQuZW1wdHksXG4gICAgb25CZWZvcmVQbG90TGluZTokLmVtcHR5LFxuICAgIG9uQWZ0ZXJQbG90TGluZTogJC5lbXB0eSxcbiAgICBvbkJlZm9yZVBsb3ROb2RlOiQuZW1wdHksXG4gICAgb25BZnRlclBsb3ROb2RlOiAkLmVtcHR5LFxuICAgIHJlcXVlc3Q6ICAgICAgICAgZmFsc2VcbiAgfTtcbiAgXG4gIChlbmQgY29kZSlcbiAgXG4gIEV4YW1wbGU6XG4gICAgXG4gIChzdGFydCBjb2RlIGpzKVxuICB2YXIgdml6ID0gbmV3ICRqaXQuVml6KHtcbiAgICBvbkJlZm9yZVBsb3ROb2RlOiBmdW5jdGlvbihub2RlKSB7XG4gICAgICBpZihub2RlLnNlbGVjdGVkKSB7XG4gICAgICAgIG5vZGUuc2V0RGF0YSgnY29sb3InLCAnI2ZmYycpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbm9kZS5yZW1vdmVEYXRhKCdjb2xvcicpO1xuICAgICAgfVxuICAgIH0sXG4gICAgb25CZWZvcmVQbG90TGluZTogZnVuY3Rpb24oYWRqKSB7XG4gICAgICBpZihhZGoubm9kZUZyb20uc2VsZWN0ZWQgJiYgYWRqLm5vZGVUby5zZWxlY3RlZCkge1xuICAgICAgICBhZGouc2V0RGF0YSgnY29sb3InLCAnI2ZmYycpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYWRqLnJlbW92ZURhdGEoJ2NvbG9yJyk7XG4gICAgICB9XG4gICAgfSxcbiAgICBvbkFmdGVyQ29tcHV0ZTogZnVuY3Rpb24oKSB7XG4gICAgICBhbGVydChcImNvbXB1dGVkIVwiKTtcbiAgICB9XG4gIH0pO1xuICAoZW5kIGNvZGUpXG4gIFxuICBQYXJhbWV0ZXJzOlxuXG4gICBvbkJlZm9yZUNvbXB1dGUobm9kZSkgLSBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgcmlnaHQgYmVmb3JlIHBlcmZvcm1pbmcgYWxsIGNvbXB1dGF0aW9ucyBhbmQgYW5pbWF0aW9ucy4gVGhlIHNlbGVjdGVkIDxHcmFwaC5Ob2RlPiBpcyBwYXNzZWQgYXMgcGFyYW1ldGVyLlxuICAgb25BZnRlckNvbXB1dGUoKSAtIFRoaXMgbWV0aG9kIGlzIHRyaWdnZXJlZCBhZnRlciBhbGwgYW5pbWF0aW9ucyBvciBjb21wdXRhdGlvbnMgZW5kZWQuXG4gICBvbkNyZWF0ZUxhYmVsKGRvbUVsZW1lbnQsIG5vZGUpIC0gVGhpcyBtZXRob2QgcmVjZWl2ZXMgYSBuZXcgbGFiZWwgRElWIGVsZW1lbnQgYXMgZmlyc3QgcGFyYW1ldGVyLCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgPEdyYXBoLk5vZGU+IGFzIHNlY29uZCBwYXJhbWV0ZXIuIFRoaXMgbWV0aG9kIHdpbGwgb25seSBiZSBjYWxsZWQgb25jZSBmb3IgZWFjaCBsYWJlbC4gVGhpcyBtZXRob2QgaXMgdXNlZnVsIHdoZW4gYWRkaW5nIGV2ZW50cyBvciBzdHlsZXMgdG8gdGhlIGxhYmVscyB1c2VkIGJ5IHRoZSBKSVQuXG4gICBvblBsYWNlTGFiZWwoZG9tRWxlbWVudCwgbm9kZSkgLSBUaGlzIG1ldGhvZCByZWNlaXZlcyBhIGxhYmVsIERJViBlbGVtZW50IGFzIGZpcnN0IHBhcmFtZXRlciBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgPEdyYXBoLk5vZGU+IGFzIHNlY29uZCBwYXJhbWV0ZXIuIFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBlYWNoIHRpbWUgYSBsYWJlbCBoYXMgYmVlbiBwbGFjZWQgaW4gdGhlIHZpc3VhbGl6YXRpb24sIGZvciBleGFtcGxlIGF0IGVhY2ggc3RlcCBvZiBhbiBhbmltYXRpb24sIGFuZCB0aHVzIGl0IGFsbG93cyB5b3UgdG8gdXBkYXRlIHRoZSBsYWJlbHMgcHJvcGVydGllcywgc3VjaCBhcyBzaXplIG9yIHBvc2l0aW9uLiBOb3RlIHRoYXQgb25QbGFjZUxhYmVsIHdpbGwgYmUgdHJpZ2dlcmVkIGFmdGVyIHVwZGF0aW5nIHRoZSBsYWJlbHMgcG9zaXRpb25zLiBUaGF0IG1lYW5zIHRoYXQsIGZvciBleGFtcGxlLCB0aGUgbGVmdCBhbmQgdG9wIGNzcyBwcm9wZXJ0aWVzIGFyZSBhbHJlYWR5IHVwZGF0ZWQgdG8gbWF0Y2ggdGhlIG5vZGVzIHBvc2l0aW9ucy4gV2lkdGggYW5kIGhlaWdodCBwcm9wZXJ0aWVzIGFyZSBub3Qgc2V0IGhvd2V2ZXIuXG4gICBvbkJlZm9yZVBsb3ROb2RlKG5vZGUpIC0gVGhpcyBtZXRob2QgaXMgdHJpZ2dlcmVkIHJpZ2h0IGJlZm9yZSBwbG90dGluZyBlYWNoIDxHcmFwaC5Ob2RlPi4gVGhpcyBtZXRob2QgaXMgdXNlZnVsIGZvciBjaGFuZ2luZyBhIG5vZGUgc3R5bGUgcmlnaHQgYmVmb3JlIHBsb3R0aW5nIGl0LlxuICAgb25BZnRlclBsb3ROb2RlKG5vZGUpIC0gVGhpcyBtZXRob2QgaXMgdHJpZ2dlcmVkIHJpZ2h0IGFmdGVyIHBsb3R0aW5nIGVhY2ggPEdyYXBoLk5vZGU+LlxuICAgb25CZWZvcmVQbG90TGluZShhZGopIC0gVGhpcyBtZXRob2QgaXMgdHJpZ2dlcmVkIHJpZ2h0IGJlZm9yZSBwbG90dGluZyBhIDxHcmFwaC5BZGphY2VuY2U+LiBUaGlzIG1ldGhvZCBpcyB1c2VmdWwgZm9yIGFkZGluZyBzb21lIHN0eWxlcyB0byBhIHBhcnRpY3VsYXIgZWRnZSBiZWZvcmUgYmVpbmcgcGxvdHRlZC5cbiAgIG9uQWZ0ZXJQbG90TGluZShhZGopIC0gVGhpcyBtZXRob2QgaXMgdHJpZ2dlcmVkIHJpZ2h0IGFmdGVyIHBsb3R0aW5nIGEgPEdyYXBoLkFkamFjZW5jZT4uXG5cbiAgICAqVXNlZCBpbiA8U1Q+LCA8VE0uQmFzZT4gYW5kIDxJY2ljbGU+IHZpc3VhbGl6YXRpb25zKlxuICAgIFxuICAgIHJlcXVlc3Qobm9kZUlkLCBsZXZlbCwgb25Db21wbGV0ZSkgLSBUaGlzIG1ldGhvZCBpcyB1c2VkIGZvciBidWZmZXJpbmcgaW5mb3JtYXRpb24gaW50byB0aGUgdmlzdWFsaXphdGlvbi4gV2hlbiBjbGlja2luZyBvbiBhbiBlbXB0eSBub2RlLCB0aGUgdmlzdWFsaXphdGlvbiB3aWxsIG1ha2UgYSByZXF1ZXN0IGZvciB0aGlzIG5vZGUncyBzdWJ0cmVlcywgc3BlY2lmeWluZyBhIGdpdmVuIGxldmVsIGZvciB0aGlzIHN1YnRyZWUgKGRlZmluZWQgYnkgX2xldmVsc1RvU2hvd18pLiBPbmNlIHRoZSByZXF1ZXN0IGlzIGNvbXBsZXRlZCwgdGhlIG9uQ29tcGxldGUgY2FsbGJhY2sgc2hvdWxkIGJlIGNhbGxlZCB3aXRoIHRoZSBnaXZlbiByZXN1bHQuIFRoaXMgaXMgdXNlZnVsIHRvIHByb3ZpZGUgb24tZGVtYW5kIGluZm9ybWF0aW9uIGludG8gdGhlIHZpc3VhbGl6YXRpb25zIHdpdGhvdWdodCBoYXZpbmcgdG8gbG9hZCB0aGUgZW50aXJlIGluZm9ybWF0aW9uIGZyb20gc3RhcnQuIFRoZSBwYXJhbWV0ZXJzIHVzZWQgYnkgdGhpcyBtZXRob2QgYXJlIF9ub2RlSWRfLCB3aGljaCBpcyB0aGUgaWQgb2YgdGhlIHJvb3Qgb2YgdGhlIHN1YnRyZWUgdG8gcmVxdWVzdCwgX2xldmVsXyB3aGljaCBpcyB0aGUgZGVwdGggb2YgdGhlIHN1YnRyZWUgdG8gYmUgcmVxdWVzdGVkICgwIHdvdWxkIG1lYW4ganVzdCB0aGUgcm9vdCBub2RlKS4gX29uQ29tcGxldGVfIGlzIGFuIG9iamVjdCBoYXZpbmcgdGhlIGNhbGxiYWNrIG1ldGhvZCBfb25Db21wbGV0ZS5vbkNvbXBsZXRlKGpzb24pXyB0aGF0IHNob3VsZCBiZSBjYWxsZWQgb25jZSB0aGUganNvbiBoYXMgYmVlbiByZXRyaWV2ZWQuICBcbiBcbiAqL1xuT3B0aW9ucy5Db250cm9sbGVyID0ge1xuICAkZXh0ZW5kOiB0cnVlLFxuICBcbiAgb25CZWZvcmVDb21wdXRlOiAkLmVtcHR5LFxuICBvbkFmdGVyQ29tcHV0ZTogICQuZW1wdHksXG4gIG9uQ3JlYXRlTGFiZWw6ICAgJC5lbXB0eSxcbiAgb25QbGFjZUxhYmVsOiAgICAkLmVtcHR5LFxuICBvbkNvbXBsZXRlOiAgICAgICQuZW1wdHksXG4gIG9uQmVmb3JlUGxvdExpbmU6JC5lbXB0eSxcbiAgb25BZnRlclBsb3RMaW5lOiAkLmVtcHR5LFxuICBvbkJlZm9yZVBsb3ROb2RlOiQuZW1wdHksXG4gIG9uQWZ0ZXJQbG90Tm9kZTogJC5lbXB0eSxcbiAgcmVxdWVzdDogICAgICAgICBmYWxzZVxufTtcblxuXG4vKlxuICogRmlsZTogRXh0cmFzLmpzXG4gKiBcbiAqIFByb3ZpZGVzIEV4dHJhcyBzdWNoIGFzIFRpcHMgYW5kIFN0eWxlIEVmZmVjdHMuXG4gKiBcbiAqIERlc2NyaXB0aW9uOlxuICogXG4gKiBQcm92aWRlcyB0aGUgPFRpcHM+IGFuZCA8Tm9kZVN0eWxlcz4gY2xhc3NlcyBhbmQgZnVuY3Rpb25zLlxuICpcbiAqL1xuXG4vKlxuICogTWFuYWdlciBmb3IgbW91c2UgZXZlbnRzIChjbGlja2luZyBhbmQgbW91c2UgbW92aW5nKS5cbiAqIFxuICogVGhpcyBjbGFzcyBpcyB1c2VkIGZvciByZWdpc3RlcmluZyBvYmplY3RzIGltcGxlbWVudGluZyBvbkNsaWNrXG4gKiBhbmQgb25Nb3VzZW1vdmUgbWV0aG9kcy4gVGhlc2UgbWV0aG9kcyBhcmUgY2FsbGVkIHdoZW4gY2xpY2tpbmcgb3JcbiAqIG1vdmluZyB0aGUgbW91c2UgYXJvdW5kICB0aGUgQ2FudmFzLlxuICogRm9yIG5vdywgPFRpcHM+IGFuZCA8Tm9kZVN0eWxlcz4gYXJlIGNsYXNzZXMgaW1wbGVtZW50aW5nIHRoZXNlIG1ldGhvZHMuXG4gKiBcbiAqL1xudmFyIEV4dHJhc0luaXRpYWxpemVyID0ge1xuICBpbml0aWFsaXplOiBmdW5jdGlvbihjbGFzc05hbWUsIHZpeikge1xuICAgIHRoaXMudml6ID0gdml6O1xuICAgIHRoaXMuY2FudmFzID0gdml6LmNhbnZhcztcbiAgICB0aGlzLmNvbmZpZyA9IHZpei5jb25maWdbY2xhc3NOYW1lXTtcbiAgICB0aGlzLm5vZGVUeXBlcyA9IHZpei5meC5ub2RlVHlwZXM7XG4gICAgdmFyIHR5cGUgPSB0aGlzLmNvbmZpZy50eXBlO1xuICAgIHRoaXMuZG9tID0gdHlwZSA9PSAnYXV0byc/ICh2aXouY29uZmlnLkxhYmVsLnR5cGUgIT0gJ05hdGl2ZScpIDogKHR5cGUgIT0gJ05hdGl2ZScpO1xuICAgIHRoaXMubGFiZWxDb250YWluZXIgPSB0aGlzLmRvbSAmJiB2aXoubGFiZWxzLmdldExhYmVsQ29udGFpbmVyKCk7XG4gICAgdGhpcy5pc0VuYWJsZWQoKSAmJiB0aGlzLmluaXRpYWxpemVQb3N0KCk7XG4gIH0sXG4gIGluaXRpYWxpemVQb3N0OiAkLmVtcHR5LFxuICBzZXRBc1Byb3BlcnR5OiAkLmxhbWJkYShmYWxzZSksXG4gIGlzRW5hYmxlZDogZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLmVuYWJsZTtcbiAgfSxcbiAgaXNMYWJlbDogZnVuY3Rpb24oZSwgd2luLCBncm91cCkge1xuICAgIGUgPSAkLmV2ZW50LmdldChlLCB3aW4pO1xuICAgIHZhciBsYWJlbENvbnRhaW5lciA9IHRoaXMubGFiZWxDb250YWluZXIsXG4gICAgICAgIHRhcmdldCA9IGUudGFyZ2V0IHx8IGUuc3JjRWxlbWVudCxcbiAgICAgICAgcmVsYXRlZCA9IGUucmVsYXRlZFRhcmdldDtcbiAgICBpZihncm91cCkge1xuICAgICAgcmV0dXJuIHJlbGF0ZWQgJiYgcmVsYXRlZCA9PSB0aGlzLnZpei5jYW52YXMuZ2V0Q3R4KCkuY2FudmFzIFxuICAgICAgICAgICYmICEhdGFyZ2V0ICYmIHRoaXMuaXNEZXNjZW5kYW50T2YodGFyZ2V0LCBsYWJlbENvbnRhaW5lcik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB0aGlzLmlzRGVzY2VuZGFudE9mKHRhcmdldCwgbGFiZWxDb250YWluZXIpO1xuICAgIH1cbiAgfSxcbiAgaXNEZXNjZW5kYW50T2Y6IGZ1bmN0aW9uKGVsZW0sIHBhcikge1xuICAgIHdoaWxlKGVsZW0gJiYgZWxlbS5wYXJlbnROb2RlKSB7XG4gICAgICBpZihlbGVtLnBhcmVudE5vZGUgPT0gcGFyKVxuICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgIGVsZW0gPSBlbGVtLnBhcmVudE5vZGU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufTtcblxudmFyIEV2ZW50c0ludGVyZmFjZSA9IHtcbiAgb25Nb3VzZVVwOiAkLmVtcHR5LFxuICBvbk1vdXNlRG93bjogJC5lbXB0eSxcbiAgb25Nb3VzZU1vdmU6ICQuZW1wdHksXG4gIG9uTW91c2VPdmVyOiAkLmVtcHR5LFxuICBvbk1vdXNlT3V0OiAkLmVtcHR5LFxuICBvbk1vdXNlV2hlZWw6ICQuZW1wdHksXG4gIG9uVG91Y2hTdGFydDogJC5lbXB0eSxcbiAgb25Ub3VjaE1vdmU6ICQuZW1wdHksXG4gIG9uVG91Y2hFbmQ6ICQuZW1wdHksXG4gIG9uVG91Y2hDYW5jZWw6ICQuZW1wdHlcbn07XG5cbnZhciBNb3VzZUV2ZW50c01hbmFnZXIgPSBuZXcgQ2xhc3Moe1xuICBpbml0aWFsaXplOiBmdW5jdGlvbih2aXopIHtcbiAgICB0aGlzLnZpeiA9IHZpejtcbiAgICB0aGlzLmNhbnZhcyA9IHZpei5jYW52YXM7XG4gICAgdGhpcy5ub2RlID0gZmFsc2U7XG4gICAgdGhpcy5lZGdlID0gZmFsc2U7XG4gICAgdGhpcy5yZWdpc3RlcmVkT2JqZWN0cyA9IFtdO1xuICAgIHRoaXMuYXR0YWNoRXZlbnRzKCk7XG4gIH0sXG4gIFxuICBhdHRhY2hFdmVudHM6IGZ1bmN0aW9uKCkge1xuICAgIHZhciBodG1sQ2FudmFzID0gdGhpcy5jYW52YXMuZ2V0RWxlbWVudCgpLCBcbiAgICAgICAgdGhhdCA9IHRoaXM7XG4gICAgaHRtbENhbnZhcy5vbmNvbnRleHRtZW51ID0gJC5sYW1iZGEoZmFsc2UpO1xuICAgICQuYWRkRXZlbnRzKGh0bWxDYW52YXMsIHtcbiAgICAgICdtb3VzZXVwJzogZnVuY3Rpb24oZSwgd2luKSB7XG4gICAgICAgIHZhciBldmVudCA9ICQuZXZlbnQuZ2V0KGUsIHdpbik7XG4gICAgICAgIHRoYXQuaGFuZGxlRXZlbnQoJ01vdXNlVXAnLCBlLCB3aW4sIFxuICAgICAgICAgICAgdGhhdC5tYWtlRXZlbnRPYmplY3QoZSwgd2luKSwgXG4gICAgICAgICAgICAkLmV2ZW50LmlzUmlnaHRDbGljayhldmVudCkpO1xuICAgICAgfSxcbiAgICAgICdtb3VzZWRvd24nOiBmdW5jdGlvbihlLCB3aW4pIHtcbiAgICAgICAgdmFyIGV2ZW50ID0gJC5ldmVudC5nZXQoZSwgd2luKTtcbiAgICAgICAgdGhhdC5oYW5kbGVFdmVudCgnTW91c2VEb3duJywgZSwgd2luLCB0aGF0Lm1ha2VFdmVudE9iamVjdChlLCB3aW4pLCBcbiAgICAgICAgICAgICQuZXZlbnQuaXNSaWdodENsaWNrKGV2ZW50KSk7XG4gICAgICB9LFxuICAgICAgJ21vdXNlbW92ZSc6IGZ1bmN0aW9uKGUsIHdpbikge1xuICAgICAgICB0aGF0LmhhbmRsZUV2ZW50KCdNb3VzZU1vdmUnLCBlLCB3aW4sIHRoYXQubWFrZUV2ZW50T2JqZWN0KGUsIHdpbikpO1xuICAgICAgfSxcbiAgICAgICdtb3VzZW92ZXInOiBmdW5jdGlvbihlLCB3aW4pIHtcbiAgICAgICAgdGhhdC5oYW5kbGVFdmVudCgnTW91c2VPdmVyJywgZSwgd2luLCB0aGF0Lm1ha2VFdmVudE9iamVjdChlLCB3aW4pKTtcbiAgICAgIH0sXG4gICAgICAnbW91c2VvdXQnOiBmdW5jdGlvbihlLCB3aW4pIHtcbiAgICAgICAgdGhhdC5oYW5kbGVFdmVudCgnTW91c2VPdXQnLCBlLCB3aW4sIHRoYXQubWFrZUV2ZW50T2JqZWN0KGUsIHdpbikpO1xuICAgICAgfSxcbiAgICAgICd0b3VjaHN0YXJ0JzogZnVuY3Rpb24oZSwgd2luKSB7XG4gICAgICAgIHRoYXQuaGFuZGxlRXZlbnQoJ1RvdWNoU3RhcnQnLCBlLCB3aW4sIHRoYXQubWFrZUV2ZW50T2JqZWN0KGUsIHdpbikpO1xuICAgICAgfSxcbiAgICAgICd0b3VjaG1vdmUnOiBmdW5jdGlvbihlLCB3aW4pIHtcbiAgICAgICAgdGhhdC5oYW5kbGVFdmVudCgnVG91Y2hNb3ZlJywgZSwgd2luLCB0aGF0Lm1ha2VFdmVudE9iamVjdChlLCB3aW4pKTtcbiAgICAgIH0sXG4gICAgICAndG91Y2hlbmQnOiBmdW5jdGlvbihlLCB3aW4pIHtcbiAgICAgICAgdGhhdC5oYW5kbGVFdmVudCgnVG91Y2hFbmQnLCBlLCB3aW4sIHRoYXQubWFrZUV2ZW50T2JqZWN0KGUsIHdpbikpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIC8vYXR0YWNoIG1vdXNld2hlZWwgZXZlbnRcbiAgICB2YXIgaGFuZGxlTW91c2VXaGVlbCA9IGZ1bmN0aW9uKGUsIHdpbikge1xuICAgICAgdmFyIGV2ZW50ID0gJC5ldmVudC5nZXQoZSwgd2luKTtcbiAgICAgIHZhciB3aGVlbCA9ICQuZXZlbnQuZ2V0V2hlZWwoZXZlbnQpO1xuICAgICAgdGhhdC5oYW5kbGVFdmVudCgnTW91c2VXaGVlbCcsIGUsIHdpbiwgd2hlZWwpO1xuICAgIH07XG4gICAgLy9UT0RPKG5pY28pOiB0aGlzIGlzIGEgaG9ycmlibGUgY2hlY2sgZm9yIG5vbi1nZWNrbyBicm93c2VycyFcbiAgICBpZighZG9jdW1lbnQuZ2V0Qm94T2JqZWN0Rm9yICYmIHdpbmRvdy5tb3pJbm5lclNjcmVlblggPT0gbnVsbCkge1xuICAgICAgJC5hZGRFdmVudChodG1sQ2FudmFzLCAnbW91c2V3aGVlbCcsIGhhbmRsZU1vdXNlV2hlZWwpO1xuICAgIH0gZWxzZSB7XG4gICAgICBodG1sQ2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoJ0RPTU1vdXNlU2Nyb2xsJywgaGFuZGxlTW91c2VXaGVlbCwgZmFsc2UpO1xuICAgIH1cbiAgfSxcbiAgXG4gIHJlZ2lzdGVyOiBmdW5jdGlvbihvYmopIHtcbiAgICB0aGlzLnJlZ2lzdGVyZWRPYmplY3RzLnB1c2gob2JqKTtcbiAgfSxcbiAgXG4gIGhhbmRsZUV2ZW50OiBmdW5jdGlvbigpIHtcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyksXG4gICAgICAgIHR5cGUgPSBhcmdzLnNoaWZ0KCk7XG4gICAgZm9yKHZhciBpPTAsIHJlZ3M9dGhpcy5yZWdpc3RlcmVkT2JqZWN0cywgbD1yZWdzLmxlbmd0aDsgaTxsOyBpKyspIHtcbiAgICAgIHJlZ3NbaV1bJ29uJyArIHR5cGVdLmFwcGx5KHJlZ3NbaV0sIGFyZ3MpO1xuICAgIH1cbiAgfSxcbiAgXG4gIG1ha2VFdmVudE9iamVjdDogZnVuY3Rpb24oZSwgd2luKSB7XG4gICAgdmFyIHRoYXQgPSB0aGlzLFxuICAgICAgICBncmFwaCA9IHRoaXMudml6LmdyYXBoLFxuICAgICAgICBmeCA9IHRoaXMudml6LmZ4LFxuICAgICAgICBudHlwZXMgPSBmeC5ub2RlVHlwZXMsXG4gICAgICAgIGV0eXBlcyA9IGZ4LmVkZ2VUeXBlcztcbiAgICByZXR1cm4ge1xuICAgICAgcG9zOiBmYWxzZSxcbiAgICAgIG5vZGU6IGZhbHNlLFxuICAgICAgZWRnZTogZmFsc2UsXG4gICAgICBjb250YWluczogZmFsc2UsXG4gICAgICBnZXROb2RlQ2FsbGVkOiBmYWxzZSxcbiAgICAgIGdldEVkZ2VDYWxsZWQ6IGZhbHNlLFxuICAgICAgZ2V0UG9zOiBmdW5jdGlvbigpIHtcbiAgICAgICAgLy9UT0RPKG5pY28pOiBjaGVjayB3aHkgdGhpcyBjYW4ndCBiZSBjYWNoZSBhbnltb3JlIHdoZW4gdXNpbmcgZWRnZSBkZXRlY3Rpb25cbiAgICAgICAgLy9pZih0aGlzLnBvcykgcmV0dXJuIHRoaXMucG9zO1xuICAgICAgICB2YXIgY2FudmFzID0gdGhhdC52aXouY2FudmFzLFxuICAgICAgICAgICAgcyA9IGNhbnZhcy5nZXRTaXplKCksXG4gICAgICAgICAgICBwID0gY2FudmFzLmdldFBvcygpLFxuICAgICAgICAgICAgb3ggPSBjYW52YXMudHJhbnNsYXRlT2Zmc2V0WCxcbiAgICAgICAgICAgIG95ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFksXG4gICAgICAgICAgICBzeCA9IGNhbnZhcy5zY2FsZU9mZnNldFgsXG4gICAgICAgICAgICBzeSA9IGNhbnZhcy5zY2FsZU9mZnNldFksXG4gICAgICAgICAgICBwb3MgPSAkLmV2ZW50LmdldFBvcyhlLCB3aW4pO1xuICAgICAgICB0aGlzLnBvcyA9IHtcbiAgICAgICAgICB4OiAocG9zLnggLSBwLnggLSBzLndpZHRoLzIgLSBveCkgKiAxL3N4LFxuICAgICAgICAgIHk6IChwb3MueSAtIHAueSAtIHMuaGVpZ2h0LzIgLSBveSkgKiAxL3N5XG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiB0aGlzLnBvcztcbiAgICAgIH0sXG4gICAgICBnZXROb2RlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYodGhpcy5nZXROb2RlQ2FsbGVkKSByZXR1cm4gdGhpcy5ub2RlO1xuICAgICAgICB0aGlzLmdldE5vZGVDYWxsZWQgPSB0cnVlO1xuICAgICAgICBmb3IodmFyIGlkIGluIGdyYXBoLm5vZGVzKSB7XG4gICAgICAgICAgdmFyIG4gPSBncmFwaC5ub2Rlc1tpZF0sXG4gICAgICAgICAgICAgIGdlb20gPSBuICYmIG50eXBlc1tuLmdldERhdGEoJ3R5cGUnKV0sXG4gICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG4gICAgICAgICAgICAgIGNvbnRhaW5zID0gbi5nZXREYXRhKCdhbHBoYScpICE9PSAwICYmIGdlb20gJiYgZ2VvbS5jb250YWlucyAmJiBnZW9tLmNvbnRhaW5zLmNhbGwoZngsIG4sIHRoaXMuZ2V0UG9zKCkpO1xuICAgICAgICAgICAgICAvLyBFTkQgTUVUQU1BUFMgQ09ERVxuICAgICAgICAgICAgICAvLyBPUklHSU5BTCBDT0RFIGNvbnRhaW5zID0gZ2VvbSAmJiBnZW9tLmNvbnRhaW5zICYmIGdlb20uY29udGFpbnMuY2FsbChmeCwgbiwgdGhpcy5nZXRQb3MoKSk7XG5cbiAgICAgICAgICBpZihjb250YWlucykge1xuICAgICAgICAgICAgdGhpcy5jb250YWlucyA9IGNvbnRhaW5zO1xuICAgICAgICAgICAgcmV0dXJuIHRoYXQubm9kZSA9IHRoaXMubm9kZSA9IG47XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGF0Lm5vZGUgPSB0aGlzLm5vZGUgPSBmYWxzZTtcbiAgICAgIH0sXG4gICAgICBnZXRFZGdlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYodGhpcy5nZXRFZGdlQ2FsbGVkKSByZXR1cm4gdGhpcy5lZGdlO1xuICAgICAgICB0aGlzLmdldEVkZ2VDYWxsZWQgPSB0cnVlO1xuICAgICAgICB2YXIgaGFzaHNldCA9IHt9O1xuICAgICAgICBmb3IodmFyIGlkIGluIGdyYXBoLmVkZ2VzKSB7XG4gICAgICAgICAgdmFyIGVkZ2VGcm9tID0gZ3JhcGguZWRnZXNbaWRdO1xuICAgICAgICAgIGhhc2hzZXRbaWRdID0gdHJ1ZTtcbiAgICAgICAgICBmb3IodmFyIGVkZ2VJZCBpbiBlZGdlRnJvbSkge1xuICAgICAgICAgICAgaWYoZWRnZUlkIGluIGhhc2hzZXQpIGNvbnRpbnVlO1xuICAgICAgICAgICAgdmFyIGUgPSBlZGdlRnJvbVtlZGdlSWRdLFxuICAgICAgICAgICAgICAgIGdlb20gPSBlICYmIGV0eXBlc1tlLmdldERhdGEoJ3R5cGUnKV0sXG5cbiAgICAgICAgICAgICAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG4gICAgICAgICAgICAgICAgY29udGFpbnMgPSBlLmdldERhdGEoJ2FscGhhJykgIT09IDAgJiYgZ2VvbSAmJiBnZW9tLmNvbnRhaW5zICYmIGdlb20uY29udGFpbnMuY2FsbChmeCwgZSwgdGhpcy5nZXRQb3MoKSk7XG4gICAgICAgICAgICAgICAgLy8gRU5EIE1FVEFNQVBTIENPREVcbiAgICAgICAgICAgICAgICAvLyBPUklHSU5BTCBDT0RFIGNvbnRhaW5zID0gZ2VvbSAmJiBnZW9tLmNvbnRhaW5zICYmIGdlb20uY29udGFpbnMuY2FsbChmeCwgbiwgdGhpcy5nZXRQb3MoKSk7XG4gICAgICAgICAgICBpZihjb250YWlucykge1xuICAgICAgICAgICAgICB0aGlzLmNvbnRhaW5zID0gY29udGFpbnM7XG4gICAgICAgICAgICAgIHJldHVybiB0aGF0LmVkZ2UgPSB0aGlzLmVkZ2UgPSBlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhhdC5lZGdlID0gdGhpcy5lZGdlID0gZmFsc2U7XG4gICAgICB9LFxuICAgICAgZ2V0Q29udGFpbnM6IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZih0aGlzLmdldE5vZGVDYWxsZWQpIHJldHVybiB0aGlzLmNvbnRhaW5zO1xuICAgICAgICB0aGlzLmdldE5vZGUoKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udGFpbnM7XG4gICAgICB9XG4gICAgfTtcbiAgfVxufSk7XG5cbi8qIFxuICogUHJvdmlkZXMgdGhlIGluaXRpYWxpemF0aW9uIGZ1bmN0aW9uIGZvciA8Tm9kZVN0eWxlcz4gYW5kIDxUaXBzPiBpbXBsZW1lbnRlZCBcbiAqIGJ5IGFsbCBtYWluIHZpc3VhbGl6YXRpb25zLlxuICpcbiAqL1xudmFyIEV4dHJhcyA9IHtcbiAgaW5pdGlhbGl6ZUV4dHJhczogZnVuY3Rpb24oKSB7XG4gICAgdmFyIG1lbSA9IG5ldyBNb3VzZUV2ZW50c01hbmFnZXIodGhpcyksIHRoYXQgPSB0aGlzO1xuICAgICQuZWFjaChbJ05vZGVTdHlsZXMnLCAnVGlwcycsICdOYXZpZ2F0aW9uJywgJ0V2ZW50cyddLCBmdW5jdGlvbihrKSB7XG4gICAgICB2YXIgb2JqID0gbmV3IEV4dHJhcy5DbGFzc2VzW2tdKGssIHRoYXQpO1xuICAgICAgaWYob2JqLmlzRW5hYmxlZCgpKSB7XG4gICAgICAgIG1lbS5yZWdpc3RlcihvYmopO1xuICAgICAgfVxuICAgICAgaWYob2JqLnNldEFzUHJvcGVydHkoKSkge1xuICAgICAgICB0aGF0W2sudG9Mb3dlckNhc2UoKV0gPSBvYmo7XG4gICAgICB9XG4gICAgfSk7XG4gIH0gICBcbn07XG5cbkV4dHJhcy5DbGFzc2VzID0ge307XG4vKlxuICBDbGFzczogRXZlbnRzXG4gICBcbiAgVGhpcyBjbGFzcyBkZWZpbmVzIGFuIEV2ZW50IEFQSSB0byBiZSBhY2Nlc3NlZCBieSB0aGUgdXNlci5cbiAgVGhlIG1ldGhvZHMgaW1wbGVtZW50ZWQgYXJlIHRoZSBvbmVzIGRlZmluZWQgaW4gdGhlIDxPcHRpb25zLkV2ZW50cz4gb2JqZWN0LlxuKi9cblxuRXh0cmFzLkNsYXNzZXMuRXZlbnRzID0gbmV3IENsYXNzKHtcbiAgSW1wbGVtZW50czogW0V4dHJhc0luaXRpYWxpemVyLCBFdmVudHNJbnRlcmZhY2VdLFxuICBcbiAgaW5pdGlhbGl6ZVBvc3Q6IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMuZnggPSB0aGlzLnZpei5meDtcbiAgICB0aGlzLm50eXBlcyA9IHRoaXMudml6LmZ4Lm5vZGVUeXBlcztcbiAgICB0aGlzLmV0eXBlcyA9IHRoaXMudml6LmZ4LmVkZ2VUeXBlcztcbiAgICBcbiAgICB0aGlzLmhvdmVyZWQgPSBmYWxzZTtcbiAgICB0aGlzLnByZXNzZWQgPSBmYWxzZTtcbiAgICB0aGlzLnRvdWNoZWQgPSBmYWxzZTtcblxuICAgIHRoaXMudG91Y2hNb3ZlZCA9IGZhbHNlO1xuICAgIHRoaXMubW92ZWQgPSBmYWxzZTtcbiAgICBcbiAgfSxcbiAgXG4gIHNldEFzUHJvcGVydHk6ICQubGFtYmRhKHRydWUpLFxuICBcbiAgb25Nb3VzZVVwOiBmdW5jdGlvbihlLCB3aW4sIGV2ZW50LCBpc1JpZ2h0Q2xpY2spIHtcbiAgICB2YXIgZXZ0ID0gJC5ldmVudC5nZXQoZSwgd2luKTtcbiAgICBpZighdGhpcy5tb3ZlZCkge1xuICAgICAgaWYoaXNSaWdodENsaWNrKSB7XG4gICAgICAgIHRoaXMuY29uZmlnLm9uUmlnaHRDbGljayh0aGlzLmhvdmVyZWQsIGV2ZW50LCBldnQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5jb25maWcub25DbGljayh0aGlzLnByZXNzZWQsIGV2ZW50LCBldnQpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZih0aGlzLnByZXNzZWQpIHtcbiAgICAgIGlmKHRoaXMubW92ZWQpIHtcbiAgICAgICAgdGhpcy5jb25maWcub25EcmFnRW5kKHRoaXMucHJlc3NlZCwgZXZlbnQsIGV2dCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmNvbmZpZy5vbkRyYWdDYW5jZWwodGhpcy5wcmVzc2VkLCBldmVudCwgZXZ0KTtcbiAgICAgIH1cbiAgICAgIHRoaXMucHJlc3NlZCA9IHRoaXMubW92ZWQgPSBmYWxzZTtcbiAgICB9XG4gIH0sXG5cbiAgb25Nb3VzZU91dDogZnVuY3Rpb24oZSwgd2luLCBldmVudCkge1xuICAgLy9tb3VzZW91dCBhIGxhYmVsXG4gICB2YXIgZXZ0ID0gJC5ldmVudC5nZXQoZSwgd2luKSwgbGFiZWw7XG4gICBpZih0aGlzLmRvbSAmJiAobGFiZWwgPSB0aGlzLmlzTGFiZWwoZSwgd2luLCB0cnVlKSkpIHtcbiAgICAgdGhpcy5jb25maWcub25Nb3VzZUxlYXZlKHRoaXMudml6LmdyYXBoLmdldE5vZGUobGFiZWwuaWQpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQsIGV2dCk7XG4gICAgIHRoaXMuaG92ZXJlZCA9IGZhbHNlO1xuICAgICByZXR1cm47XG4gICB9XG4gICAvL21vdXNlb3V0IGNhbnZhc1xuICAgdmFyIHJ0ID0gZXZ0LnJlbGF0ZWRUYXJnZXQsXG4gICAgICAgY2FudmFzV2lkZ2V0ID0gdGhpcy5jYW52YXMuZ2V0RWxlbWVudCgpO1xuICAgd2hpbGUocnQgJiYgcnQucGFyZW50Tm9kZSkge1xuICAgICBpZihjYW52YXNXaWRnZXQgPT0gcnQucGFyZW50Tm9kZSkgcmV0dXJuO1xuICAgICBydCA9IHJ0LnBhcmVudE5vZGU7XG4gICB9XG4gICBpZih0aGlzLmhvdmVyZWQpIHtcbiAgICAgdGhpcy5jb25maWcub25Nb3VzZUxlYXZlKHRoaXMuaG92ZXJlZCxcbiAgICAgICAgIGV2ZW50LCBldnQpO1xuICAgICB0aGlzLmhvdmVyZWQgPSBmYWxzZTtcbiAgIH1cbiAgfSxcbiAgXG4gIG9uTW91c2VPdmVyOiBmdW5jdGlvbihlLCB3aW4sIGV2ZW50KSB7XG4gICAgLy9tb3VzZW92ZXIgYSBsYWJlbFxuICAgIHZhciBldnQgPSAkLmV2ZW50LmdldChlLCB3aW4pLCBsYWJlbDtcbiAgICBpZih0aGlzLmRvbSAmJiAobGFiZWwgPSB0aGlzLmlzTGFiZWwoZSwgd2luLCB0cnVlKSkpIHtcbiAgICAgIHRoaXMuaG92ZXJlZCA9IHRoaXMudml6LmdyYXBoLmdldE5vZGUobGFiZWwuaWQpO1xuICAgICAgdGhpcy5jb25maWcub25Nb3VzZUVudGVyKHRoaXMuaG92ZXJlZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmVudCwgZXZ0KTtcbiAgICB9XG4gIH0sXG4gIFxuICBvbk1vdXNlTW92ZTogZnVuY3Rpb24oZSwgd2luLCBldmVudCkge1xuICAgdmFyIGxhYmVsLCBldnQgPSAkLmV2ZW50LmdldChlLCB3aW4pO1xuICAgaWYodGhpcy5wcmVzc2VkKSB7XG4gICAgIHRoaXMubW92ZWQgPSB0cnVlO1xuICAgICB0aGlzLmNvbmZpZy5vbkRyYWdNb3ZlKHRoaXMucHJlc3NlZCwgZXZlbnQsIGV2dCk7XG4gICAgIHJldHVybjtcbiAgIH1cbiAgIGlmKHRoaXMuZG9tKSB7XG4gICAgIHRoaXMuY29uZmlnLm9uTW91c2VNb3ZlKHRoaXMuaG92ZXJlZCxcbiAgICAgICAgIGV2ZW50LCBldnQpO1xuICAgfSBlbHNlIHtcbiAgICAgaWYodGhpcy5ob3ZlcmVkKSB7XG4gICAgICAgdmFyIGhuID0gdGhpcy5ob3ZlcmVkO1xuICAgICAgIHZhciBnZW9tID0gaG4ubm9kZUZyb20/IHRoaXMuZXR5cGVzW2huLmdldERhdGEoJ3R5cGUnKV0gOiB0aGlzLm50eXBlc1tobi5nZXREYXRhKCd0eXBlJyldO1xuICAgICAgIHZhciBjb250YWlucyA9IGdlb20gJiYgZ2VvbS5jb250YWlucyBcbiAgICAgICAgICYmIGdlb20uY29udGFpbnMuY2FsbCh0aGlzLmZ4LCBobiwgZXZlbnQuZ2V0UG9zKCkpO1xuICAgICAgIGlmKGNvbnRhaW5zKSB7XG4gICAgICAgICB0aGlzLmNvbmZpZy5vbk1vdXNlTW92ZShobiwgZXZlbnQsIGV2dCk7XG4gICAgICAgICByZXR1cm47XG4gICAgICAgfSBlbHNlIHtcbiAgICAgICAgIHRoaXMuY29uZmlnLm9uTW91c2VMZWF2ZShobiwgZXZlbnQsIGV2dCk7XG4gICAgICAgICB0aGlzLmhvdmVyZWQgPSBmYWxzZTtcbiAgICAgICB9XG4gICAgIH1cbiAgICAgaWYodGhpcy5ob3ZlcmVkID0gKGV2ZW50LmdldE5vZGUoKSB8fCAodGhpcy5jb25maWcuZW5hYmxlRm9yRWRnZXMgJiYgZXZlbnQuZ2V0RWRnZSgpKSkpIHtcbiAgICAgICB0aGlzLmNvbmZpZy5vbk1vdXNlRW50ZXIodGhpcy5ob3ZlcmVkLCBldmVudCwgZXZ0KTtcbiAgICAgfSBlbHNlIHtcbiAgICAgICB0aGlzLmNvbmZpZy5vbk1vdXNlTW92ZShmYWxzZSwgZXZlbnQsIGV2dCk7XG4gICAgIH1cbiAgIH1cbiAgfSxcbiAgXG4gIG9uTW91c2VXaGVlbDogZnVuY3Rpb24oZSwgd2luLCBkZWx0YSkge1xuICAgIHRoaXMuY29uZmlnLm9uTW91c2VXaGVlbChkZWx0YSwgJC5ldmVudC5nZXQoZSwgd2luKSk7XG4gIH0sXG4gIFxuICBvbk1vdXNlRG93bjogZnVuY3Rpb24oZSwgd2luLCBldmVudCkge1xuICAgIFxuICAgIC8vIFNUQVJUIE1FVEFNQVBTIENPREVcbiAgICB2YXIgZXZ0ID0gJC5ldmVudC5nZXQoZSwgd2luKTtcbiAgICB0aGlzLnByZXNzZWQgPSBldmVudC5nZXROb2RlKCkgfHwgKHRoaXMuY29uZmlnLmVuYWJsZUZvckVkZ2VzICYmIGV2ZW50LmdldEVkZ2UoKSk7XG4gICAgLy8gRU5EIE1FVEFNQVBTIENPREUgICAgXG4gICAgLy8gT1JJR0lOQUwgQ09ERVxuICAgIC8qdmFyIGV2dCA9ICQuZXZlbnQuZ2V0KGUsIHdpbiksIGxhYmVsO1xuICAgIGlmKHRoaXMuZG9tKSB7XG4gICAgICBpZihsYWJlbCA9IHRoaXMuaXNMYWJlbChlLCB3aW4pKSB7XG4gICAgICAgIHRoaXMucHJlc3NlZCA9IHRoaXMudml6LmdyYXBoLmdldE5vZGUobGFiZWwuaWQpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnByZXNzZWQgPSBldmVudC5nZXROb2RlKCkgfHwgKHRoaXMuY29uZmlnLmVuYWJsZUZvckVkZ2VzICYmIGV2ZW50LmdldEVkZ2UoKSk7XG4gICAgfSAqL1xuICAgIHRoaXMucHJlc3NlZCAmJiB0aGlzLmNvbmZpZy5vbkRyYWdTdGFydCh0aGlzLnByZXNzZWQsIGV2ZW50LCBldnQpO1xuICB9LFxuICBcbiAgb25Ub3VjaFN0YXJ0OiBmdW5jdGlvbihlLCB3aW4sIGV2ZW50KSB7XG4gICAgdmFyIGV2dCA9ICQuZXZlbnQuZ2V0KGUsIHdpbiksIGxhYmVsO1xuICAgIGlmKHRoaXMuZG9tICYmIChsYWJlbCA9IHRoaXMuaXNMYWJlbChlLCB3aW4pKSkge1xuICAgICAgdGhpcy50b3VjaGVkID0gdGhpcy52aXouZ3JhcGguZ2V0Tm9kZShsYWJlbC5pZCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMudG91Y2hlZCA9IGV2ZW50LmdldE5vZGUoKSB8fCAodGhpcy5jb25maWcuZW5hYmxlRm9yRWRnZXMgJiYgZXZlbnQuZ2V0RWRnZSgpKTtcbiAgICB9XG4gICAgdGhpcy50b3VjaGVkICYmIHRoaXMuY29uZmlnLm9uVG91Y2hTdGFydCh0aGlzLnRvdWNoZWQsIGV2ZW50LCBldnQpO1xuICB9LFxuICBcbiAgb25Ub3VjaE1vdmU6IGZ1bmN0aW9uKGUsIHdpbiwgZXZlbnQpIHtcbiAgICB2YXIgZXZ0ID0gJC5ldmVudC5nZXQoZSwgd2luKTtcbiAgICBpZih0aGlzLnRvdWNoZWQpIHtcbiAgICAgIHRoaXMudG91Y2hNb3ZlZCA9IHRydWU7XG4gICAgICB0aGlzLmNvbmZpZy5vblRvdWNoTW92ZSh0aGlzLnRvdWNoZWQsIGV2ZW50LCBldnQpO1xuICAgIH1cbiAgfSxcbiAgXG4gIG9uVG91Y2hFbmQ6IGZ1bmN0aW9uKGUsIHdpbiwgZXZlbnQpIHtcbiAgICB2YXIgZXZ0ID0gJC5ldmVudC5nZXQoZSwgd2luKTtcbiAgICBpZih0aGlzLnRvdWNoZWQpIHtcbiAgICAgIGlmKHRoaXMudG91Y2hNb3ZlZCkge1xuICAgICAgICB0aGlzLmNvbmZpZy5vblRvdWNoRW5kKHRoaXMudG91Y2hlZCwgZXZlbnQsIGV2dCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmNvbmZpZy5vblRvdWNoQ2FuY2VsKHRoaXMudG91Y2hlZCwgZXZlbnQsIGV2dCk7XG4gICAgICB9XG4gICAgICB0aGlzLnRvdWNoZWQgPSB0aGlzLnRvdWNoTW92ZWQgPSBmYWxzZTtcbiAgICB9XG4gIH1cbn0pO1xuXG4vKlxuICAgQ2xhc3M6IFRpcHNcbiAgICBcbiAgIEEgY2xhc3MgY29udGFpbmluZyB0aXAgcmVsYXRlZCBmdW5jdGlvbnMuIFRoaXMgY2xhc3MgaXMgdXNlZCBpbnRlcm5hbGx5LlxuICAgXG4gICBVc2VkIGJ5OlxuICAgXG4gICA8U1Q+LCA8U3VuYnVyc3Q+LCA8SHlwZXJ0cmVlPiwgPFJHcmFwaD4sIDxUTT4sIDxGb3JjZURpcmVjdGVkPiwgPEljaWNsZT5cbiAgIFxuICAgU2VlIGFsc286XG4gICBcbiAgIDxPcHRpb25zLlRpcHM+XG4qL1xuXG5FeHRyYXMuQ2xhc3Nlcy5UaXBzID0gbmV3IENsYXNzKHtcbiAgSW1wbGVtZW50czogW0V4dHJhc0luaXRpYWxpemVyLCBFdmVudHNJbnRlcmZhY2VdLFxuICBcbiAgaW5pdGlhbGl6ZVBvc3Q6IGZ1bmN0aW9uKCkge1xuICAgIC8vYWRkIERPTSB0b29sdGlwXG4gICAgaWYoZG9jdW1lbnQuYm9keSkge1xuICAgICAgdmFyIHRpcCA9ICQoJ190b29sdGlwJykgfHwgZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICB0aXAuaWQgPSAnX3Rvb2x0aXAnO1xuICAgICAgdGlwLmNsYXNzTmFtZSA9ICd0aXAnO1xuICAgICAgJC5leHRlbmQodGlwLnN0eWxlLCB7XG4gICAgICAgIHBvc2l0aW9uOiAnYWJzb2x1dGUnLFxuICAgICAgICBkaXNwbGF5OiAnbm9uZScsXG4gICAgICAgIHpJbmRleDogMTMwMDBcbiAgICAgIH0pO1xuICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZCh0aXApO1xuICAgICAgdGhpcy50aXAgPSB0aXA7XG4gICAgICB0aGlzLm5vZGUgPSBmYWxzZTtcbiAgICB9XG4gIH0sXG4gIFxuICBzZXRBc1Byb3BlcnR5OiAkLmxhbWJkYSh0cnVlKSxcbiAgXG4gIG9uTW91c2VPdXQ6IGZ1bmN0aW9uKGUsIHdpbikge1xuICAgIC8vbW91c2VvdXQgYSBsYWJlbFxuICAgIHZhciBldnQgPSAkLmV2ZW50LmdldChlLCB3aW4pO1xuICAgIGlmKHRoaXMuZG9tICYmIHRoaXMuaXNMYWJlbChlLCB3aW4sIHRydWUpKSB7XG4gICAgICB0aGlzLmhpZGUodHJ1ZSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vbW91c2VvdXQgY2FudmFzXG4gICAgdmFyIHJ0ID0gZS5yZWxhdGVkVGFyZ2V0LFxuICAgICAgICBjYW52YXNXaWRnZXQgPSB0aGlzLmNhbnZhcy5nZXRFbGVtZW50KCk7XG4gICAgd2hpbGUocnQgJiYgcnQucGFyZW50Tm9kZSkge1xuICAgICAgaWYoY2FudmFzV2lkZ2V0ID09IHJ0LnBhcmVudE5vZGUpIHJldHVybjtcbiAgICAgIHJ0ID0gcnQucGFyZW50Tm9kZTtcbiAgICB9XG4gICAgdGhpcy5oaWRlKGZhbHNlKTtcbiAgfSxcbiAgXG4gIG9uTW91c2VPdmVyOiBmdW5jdGlvbihlLCB3aW4pIHtcbiAgICAvL21vdXNlb3ZlciBhIGxhYmVsXG4gICAgdmFyIGxhYmVsO1xuICAgIGlmKHRoaXMuZG9tICYmIChsYWJlbCA9IHRoaXMuaXNMYWJlbChlLCB3aW4sIGZhbHNlKSkpIHtcbiAgICAgIHRoaXMubm9kZSA9IHRoaXMudml6LmdyYXBoLmdldE5vZGUobGFiZWwuaWQpO1xuICAgICAgdGhpcy5jb25maWcub25TaG93KHRoaXMudGlwLCB0aGlzLm5vZGUsIGxhYmVsKTtcbiAgICB9XG4gIH0sXG4gIFxuICBvbk1vdXNlTW92ZTogZnVuY3Rpb24oZSwgd2luLCBvcHQpIHtcbiAgICBpZih0aGlzLmRvbSAmJiB0aGlzLmlzTGFiZWwoZSwgd2luKSkge1xuICAgICAgdGhpcy5zZXRUb29sdGlwUG9zaXRpb24oJC5ldmVudC5nZXRQb3MoZSwgd2luKSk7XG4gICAgfVxuICAgIGlmKCF0aGlzLmRvbSkge1xuICAgICAgdmFyIG5vZGUgPSBvcHQuZ2V0Tm9kZSgpO1xuICAgICAgaWYoIW5vZGUpIHtcbiAgICAgICAgdGhpcy5oaWRlKHRydWUpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZih0aGlzLmNvbmZpZy5mb3JjZSB8fCAhdGhpcy5ub2RlIHx8IHRoaXMubm9kZS5pZCAhPSBub2RlLmlkKSB7XG4gICAgICAgIHRoaXMubm9kZSA9IG5vZGU7XG4gICAgICAgIHRoaXMuY29uZmlnLm9uU2hvdyh0aGlzLnRpcCwgbm9kZSwgb3B0LmdldENvbnRhaW5zKCkpO1xuICAgICAgfVxuICAgICAgdGhpcy5zZXRUb29sdGlwUG9zaXRpb24oJC5ldmVudC5nZXRQb3MoZSwgd2luKSk7XG4gICAgfVxuICB9LFxuICBcbiAgc2V0VG9vbHRpcFBvc2l0aW9uOiBmdW5jdGlvbihwb3MpIHtcbiAgICB2YXIgdGlwID0gdGhpcy50aXAsIFxuICAgICAgICBzdHlsZSA9IHRpcC5zdHlsZSwgXG4gICAgICAgIGNvbnQgPSB0aGlzLmNvbmZpZztcbiAgICBzdHlsZS5kaXNwbGF5ID0gJyc7XG4gICAgLy9nZXQgd2luZG93IGRpbWVuc2lvbnNcbiAgICB2YXIgd2luID0ge1xuICAgICAgJ2hlaWdodCc6IGRvY3VtZW50LmJvZHkuY2xpZW50SGVpZ2h0LFxuICAgICAgJ3dpZHRoJzogZG9jdW1lbnQuYm9keS5jbGllbnRXaWR0aFxuICAgIH07XG4gICAgLy9nZXQgdG9vbHRpcCBkaW1lbnNpb25zXG4gICAgdmFyIG9iaiA9IHtcbiAgICAgICd3aWR0aCc6IHRpcC5vZmZzZXRXaWR0aCxcbiAgICAgICdoZWlnaHQnOiB0aXAub2Zmc2V0SGVpZ2h0ICBcbiAgICB9O1xuICAgIC8vc2V0IHRvb2x0aXAgcG9zaXRpb25cbiAgICB2YXIgeCA9IGNvbnQub2Zmc2V0WCwgeSA9IGNvbnQub2Zmc2V0WTtcbiAgICBzdHlsZS50b3AgPSAoKHBvcy55ICsgeSArIG9iai5oZWlnaHQgPiB3aW4uaGVpZ2h0KT8gIFxuICAgICAgICAocG9zLnkgLSBvYmouaGVpZ2h0IC0geSkgOiBwb3MueSArIHkpICsgJ3B4JztcbiAgICBzdHlsZS5sZWZ0ID0gKChwb3MueCArIG9iai53aWR0aCArIHggPiB3aW4ud2lkdGgpPyBcbiAgICAgICAgKHBvcy54IC0gb2JqLndpZHRoIC0geCkgOiBwb3MueCArIHgpICsgJ3B4JztcbiAgfSxcbiAgXG4gIGhpZGU6IGZ1bmN0aW9uKHRyaWdnZXJDYWxsYmFjaykge1xuICAgIHRoaXMudGlwLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgdHJpZ2dlckNhbGxiYWNrICYmIHRoaXMuY29uZmlnLm9uSGlkZSgpO1xuICB9XG59KTtcblxuLypcbiAgQ2xhc3M6IE5vZGVTdHlsZXNcbiAgIFxuICBDaGFuZ2Ugbm9kZSBzdHlsZXMgd2hlbiBjbGlja2luZyBvciBob3ZlcmluZyBhIG5vZGUuIFRoaXMgY2xhc3MgaXMgdXNlZCBpbnRlcm5hbGx5LlxuICBcbiAgVXNlZCBieTpcbiAgXG4gIDxTVD4sIDxTdW5idXJzdD4sIDxIeXBlcnRyZWU+LCA8UkdyYXBoPiwgPFRNPiwgPEZvcmNlRGlyZWN0ZWQ+LCA8SWNpY2xlPlxuICBcbiAgU2VlIGFsc286XG4gIFxuICA8T3B0aW9ucy5Ob2RlU3R5bGVzPlxuKi9cbkV4dHJhcy5DbGFzc2VzLk5vZGVTdHlsZXMgPSBuZXcgQ2xhc3Moe1xuICBJbXBsZW1lbnRzOiBbRXh0cmFzSW5pdGlhbGl6ZXIsIEV2ZW50c0ludGVyZmFjZV0sXG4gIFxuICBpbml0aWFsaXplUG9zdDogZnVuY3Rpb24oKSB7XG4gICAgdGhpcy5meCA9IHRoaXMudml6LmZ4O1xuICAgIHRoaXMudHlwZXMgPSB0aGlzLnZpei5meC5ub2RlVHlwZXM7XG4gICAgdGhpcy5uU3R5bGVzID0gdGhpcy5jb25maWc7XG4gICAgdGhpcy5ub2RlU3R5bGVzT25Ib3ZlciA9IHRoaXMublN0eWxlcy5zdHlsZXNIb3ZlcjtcbiAgICB0aGlzLm5vZGVTdHlsZXNPbkNsaWNrID0gdGhpcy5uU3R5bGVzLnN0eWxlc0NsaWNrO1xuICAgIHRoaXMuaG92ZXJlZE5vZGUgPSBmYWxzZTtcbiAgICB0aGlzLmZ4Lm5vZGVGeEFuaW1hdGlvbiA9IG5ldyBBbmltYXRpb24oKTtcbiAgICBcbiAgICB0aGlzLmRvd24gPSBmYWxzZTtcbiAgICB0aGlzLm1vdmUgPSBmYWxzZTtcbiAgfSxcbiAgXG4gIG9uTW91c2VPdXQ6IGZ1bmN0aW9uKGUsIHdpbikge1xuICAgIHRoaXMuZG93biA9IHRoaXMubW92ZSA9IGZhbHNlO1xuICAgIGlmKCF0aGlzLmhvdmVyZWROb2RlKSByZXR1cm47XG4gICAgLy9tb3VzZW91dCBhIGxhYmVsXG4gICAgaWYodGhpcy5kb20gJiYgdGhpcy5pc0xhYmVsKGUsIHdpbiwgdHJ1ZSkpIHtcbiAgICAgIHRoaXMudG9nZ2xlU3R5bGVzT25Ib3Zlcih0aGlzLmhvdmVyZWROb2RlLCBmYWxzZSk7XG4gICAgfVxuICAgIC8vbW91c2VvdXQgY2FudmFzXG4gICAgdmFyIHJ0ID0gZS5yZWxhdGVkVGFyZ2V0LFxuICAgICAgICBjYW52YXNXaWRnZXQgPSB0aGlzLmNhbnZhcy5nZXRFbGVtZW50KCk7XG4gICAgd2hpbGUocnQgJiYgcnQucGFyZW50Tm9kZSkge1xuICAgICAgaWYoY2FudmFzV2lkZ2V0ID09IHJ0LnBhcmVudE5vZGUpIHJldHVybjtcbiAgICAgIHJ0ID0gcnQucGFyZW50Tm9kZTtcbiAgICB9XG4gICAgdGhpcy50b2dnbGVTdHlsZXNPbkhvdmVyKHRoaXMuaG92ZXJlZE5vZGUsIGZhbHNlKTtcbiAgICB0aGlzLmhvdmVyZWROb2RlID0gZmFsc2U7XG4gIH0sXG4gIFxuICBvbk1vdXNlT3ZlcjogZnVuY3Rpb24oZSwgd2luKSB7XG4gICAgLy9tb3VzZW92ZXIgYSBsYWJlbFxuICAgIHZhciBsYWJlbDtcbiAgICBpZih0aGlzLmRvbSAmJiAobGFiZWwgPSB0aGlzLmlzTGFiZWwoZSwgd2luLCB0cnVlKSkpIHtcbiAgICAgIHZhciBub2RlID0gdGhpcy52aXouZ3JhcGguZ2V0Tm9kZShsYWJlbC5pZCk7XG4gICAgICBpZihub2RlLnNlbGVjdGVkKSByZXR1cm47XG4gICAgICB0aGlzLmhvdmVyZWROb2RlID0gbm9kZTtcbiAgICAgIHRoaXMudG9nZ2xlU3R5bGVzT25Ib3Zlcih0aGlzLmhvdmVyZWROb2RlLCB0cnVlKTtcbiAgICB9XG4gIH0sXG4gIFxuICBvbk1vdXNlRG93bjogZnVuY3Rpb24oZSwgd2luLCBldmVudCwgaXNSaWdodENsaWNrKSB7XG4gICAgaWYoaXNSaWdodENsaWNrKSByZXR1cm47XG4gICAgdmFyIGxhYmVsO1xuICAgIGlmKHRoaXMuZG9tICYmIChsYWJlbCA9IHRoaXMuaXNMYWJlbChlLCB3aW4pKSkge1xuICAgICAgdGhpcy5kb3duID0gdGhpcy52aXouZ3JhcGguZ2V0Tm9kZShsYWJlbC5pZCk7XG4gICAgfSBlbHNlIGlmKCF0aGlzLmRvbSkge1xuICAgICAgdGhpcy5kb3duID0gZXZlbnQuZ2V0Tm9kZSgpO1xuICAgIH1cbiAgICB0aGlzLm1vdmUgPSBmYWxzZTtcbiAgfSxcbiAgXG4gIG9uTW91c2VVcDogZnVuY3Rpb24oZSwgd2luLCBldmVudCwgaXNSaWdodENsaWNrKSB7XG4gICAgaWYoaXNSaWdodENsaWNrKSByZXR1cm47XG4gICAgaWYoIXRoaXMubW92ZSkge1xuICAgICAgdGhpcy5vbkNsaWNrKGV2ZW50LmdldE5vZGUoKSk7XG4gICAgfVxuICAgIHRoaXMuZG93biA9IHRoaXMubW92ZSA9IGZhbHNlO1xuICB9LFxuICBcbiAgZ2V0UmVzdG9yZWRTdHlsZXM6IGZ1bmN0aW9uKG5vZGUsIHR5cGUpIHtcbiAgICB2YXIgcmVzdG9yZWRTdHlsZXMgPSB7fSwgXG4gICAgICAgIG5TdHlsZXMgPSB0aGlzWydub2RlU3R5bGVzT24nICsgdHlwZV07XG4gICAgZm9yKHZhciBwcm9wIGluIG5TdHlsZXMpIHtcbiAgICAgIHJlc3RvcmVkU3R5bGVzW3Byb3BdID0gbm9kZS5zdHlsZXNbJyQnICsgcHJvcF07XG4gICAgfVxuICAgIHJldHVybiByZXN0b3JlZFN0eWxlcztcbiAgfSxcbiAgXG4gIHRvZ2dsZVN0eWxlc09uSG92ZXI6IGZ1bmN0aW9uKG5vZGUsIHNldCkge1xuICAgIGlmKHRoaXMubm9kZVN0eWxlc09uSG92ZXIpIHtcbiAgICAgIHRoaXMudG9nZ2xlU3R5bGVzT24oJ0hvdmVyJywgbm9kZSwgc2V0KTtcbiAgICB9XG4gIH0sXG5cbiAgdG9nZ2xlU3R5bGVzT25DbGljazogZnVuY3Rpb24obm9kZSwgc2V0KSB7XG4gICAgaWYodGhpcy5ub2RlU3R5bGVzT25DbGljaykge1xuICAgICAgdGhpcy50b2dnbGVTdHlsZXNPbignQ2xpY2snLCBub2RlLCBzZXQpO1xuICAgIH1cbiAgfSxcbiAgXG4gIHRvZ2dsZVN0eWxlc09uOiBmdW5jdGlvbih0eXBlLCBub2RlLCBzZXQpIHtcbiAgICB2YXIgdml6ID0gdGhpcy52aXo7XG4gICAgdmFyIG5TdHlsZXMgPSB0aGlzLm5TdHlsZXM7XG4gICAgaWYoc2V0KSB7XG4gICAgICB2YXIgdGhhdCA9IHRoaXM7XG4gICAgICBpZighbm9kZS5zdHlsZXMpIHtcbiAgICAgICAgbm9kZS5zdHlsZXMgPSAkLm1lcmdlKG5vZGUuZGF0YSwge30pO1xuICAgICAgfVxuICAgICAgZm9yKHZhciBzIGluIHRoaXNbJ25vZGVTdHlsZXNPbicgKyB0eXBlXSkge1xuICAgICAgICB2YXIgJHMgPSAnJCcgKyBzO1xuICAgICAgICBpZighKCRzIGluIG5vZGUuc3R5bGVzKSkge1xuICAgICAgICAgICAgbm9kZS5zdHlsZXNbJHNdID0gbm9kZS5nZXREYXRhKHMpOyBcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdml6LmZ4Lm5vZGVGeCgkLmV4dGVuZCh7XG4gICAgICAgICdlbGVtZW50cyc6IHtcbiAgICAgICAgICAnaWQnOiBub2RlLmlkLFxuICAgICAgICAgICdwcm9wZXJ0aWVzJzogdGhhdFsnbm9kZVN0eWxlc09uJyArIHR5cGVdXG4gICAgICAgICB9LFxuICAgICAgICAgdHJhbnNpdGlvbjogVHJhbnMuUXVhcnQuZWFzZU91dCxcbiAgICAgICAgIGR1cmF0aW9uOjMwMCxcbiAgICAgICAgIGZwczo0MFxuICAgICAgfSwgdGhpcy5jb25maWcpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIHJlc3RvcmVkU3R5bGVzID0gdGhpcy5nZXRSZXN0b3JlZFN0eWxlcyhub2RlLCB0eXBlKTtcbiAgICAgIHZpei5meC5ub2RlRngoJC5leHRlbmQoe1xuICAgICAgICAnZWxlbWVudHMnOiB7XG4gICAgICAgICAgJ2lkJzogbm9kZS5pZCxcbiAgICAgICAgICAncHJvcGVydGllcyc6IHJlc3RvcmVkU3R5bGVzXG4gICAgICAgICB9LFxuICAgICAgICAgdHJhbnNpdGlvbjogVHJhbnMuUXVhcnQuZWFzZU91dCxcbiAgICAgICAgIGR1cmF0aW9uOjMwMCxcbiAgICAgICAgIGZwczo0MFxuICAgICAgfSwgdGhpcy5jb25maWcpKTtcbiAgICB9XG4gIH0sXG5cbiAgb25DbGljazogZnVuY3Rpb24obm9kZSkge1xuICAgIGlmKCFub2RlKSByZXR1cm47XG4gICAgdmFyIG5TdHlsZXMgPSB0aGlzLm5vZGVTdHlsZXNPbkNsaWNrO1xuICAgIGlmKCFuU3R5bGVzKSByZXR1cm47XG4gICAgLy9pZiB0aGUgbm9kZSBpcyBzZWxlY3RlZCB0aGVuIHVuc2VsZWN0IGl0XG4gICAgaWYobm9kZS5zZWxlY3RlZCkge1xuICAgICAgdGhpcy50b2dnbGVTdHlsZXNPbkNsaWNrKG5vZGUsIGZhbHNlKTtcbiAgICAgIGRlbGV0ZSBub2RlLnNlbGVjdGVkO1xuICAgIH0gZWxzZSB7XG4gICAgICAvL3Vuc2VsZWN0IGFsbCBzZWxlY3RlZCBub2Rlcy4uLlxuICAgICAgdGhpcy52aXouZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obikge1xuICAgICAgICBpZihuLnNlbGVjdGVkKSB7XG4gICAgICAgICAgZm9yKHZhciBzIGluIG5TdHlsZXMpIHtcbiAgICAgICAgICAgIG4uc2V0RGF0YShzLCBuLnN0eWxlc1snJCcgKyBzXSwgJ2VuZCcpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBkZWxldGUgbi5zZWxlY3RlZDtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICAvL3NlbGVjdCBjbGlja2VkIG5vZGVcbiAgICAgIHRoaXMudG9nZ2xlU3R5bGVzT25DbGljayhub2RlLCB0cnVlKTtcbiAgICAgIG5vZGUuc2VsZWN0ZWQgPSB0cnVlO1xuICAgICAgZGVsZXRlIG5vZGUuaG92ZXJlZDtcbiAgICAgIHRoaXMuaG92ZXJlZE5vZGUgPSBmYWxzZTtcbiAgICB9XG4gIH0sXG4gIFxuICBvbk1vdXNlTW92ZTogZnVuY3Rpb24oZSwgd2luLCBldmVudCkge1xuICAgIC8vaWYgbW91c2UgYnV0dG9uIGlzIGRvd24gYW5kIG1vdmluZyBzZXQgbW92ZT10cnVlXG4gICAgaWYodGhpcy5kb3duKSB0aGlzLm1vdmUgPSB0cnVlO1xuICAgIC8vYWxyZWFkeSBoYW5kbGVkIGJ5IG1vdXNlb3Zlci9vdXRcbiAgICBpZih0aGlzLmRvbSAmJiB0aGlzLmlzTGFiZWwoZSwgd2luKSkgcmV0dXJuO1xuICAgIHZhciBuU3R5bGVzID0gdGhpcy5ub2RlU3R5bGVzT25Ib3ZlcjtcbiAgICBpZighblN0eWxlcykgcmV0dXJuO1xuICAgIFxuICAgIGlmKCF0aGlzLmRvbSkge1xuICAgICAgaWYodGhpcy5ob3ZlcmVkTm9kZSkge1xuICAgICAgICB2YXIgZ2VvbSA9IHRoaXMudHlwZXNbdGhpcy5ob3ZlcmVkTm9kZS5nZXREYXRhKCd0eXBlJyldO1xuICAgICAgICB2YXIgY29udGFpbnMgPSBnZW9tICYmIGdlb20uY29udGFpbnMgJiYgZ2VvbS5jb250YWlucy5jYWxsKHRoaXMuZngsIFxuICAgICAgICAgICAgdGhpcy5ob3ZlcmVkTm9kZSwgZXZlbnQuZ2V0UG9zKCkpO1xuICAgICAgICBpZihjb250YWlucykgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdmFyIG5vZGUgPSBldmVudC5nZXROb2RlKCk7XG4gICAgICAvL2lmIG5vIG5vZGUgaXMgYmVpbmcgaG92ZXJlZCB0aGVuIGp1c3QgZXhpdFxuICAgICAgaWYoIXRoaXMuaG92ZXJlZE5vZGUgJiYgIW5vZGUpIHJldHVybjtcbiAgICAgIC8vaWYgdGhlIG5vZGUgaXMgaG92ZXJlZCB0aGVuIGV4aXRcbiAgICAgIGlmKG5vZGUuaG92ZXJlZCkgcmV0dXJuO1xuICAgICAgLy9zZWxlY3QgaG92ZXJlZCBub2RlXG4gICAgICBpZihub2RlICYmICFub2RlLnNlbGVjdGVkKSB7XG4gICAgICAgIC8vY2hlY2sgaWYgYW4gYW5pbWF0aW9uIGlzIHJ1bm5pbmcgYW5kIGV4aXQgaXRcbiAgICAgICAgdGhpcy5meC5ub2RlRnhBbmltYXRpb24uc3RvcFRpbWVyKCk7XG4gICAgICAgIC8vdW5zZWxlY3QgYWxsIGhvdmVyZWQgbm9kZXMuLi5cbiAgICAgICAgdGhpcy52aXouZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obikge1xuICAgICAgICAgIGlmKG4uaG92ZXJlZCAmJiAhbi5zZWxlY3RlZCkge1xuICAgICAgICAgICAgZm9yKHZhciBzIGluIG5TdHlsZXMpIHtcbiAgICAgICAgICAgICAgbi5zZXREYXRhKHMsIG4uc3R5bGVzWyckJyArIHNdLCAnZW5kJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBkZWxldGUgbi5ob3ZlcmVkO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIC8vc2VsZWN0IGhvdmVyZWQgbm9kZVxuICAgICAgICBub2RlLmhvdmVyZWQgPSB0cnVlO1xuICAgICAgICB0aGlzLmhvdmVyZWROb2RlID0gbm9kZTtcbiAgICAgICAgdGhpcy50b2dnbGVTdHlsZXNPbkhvdmVyKG5vZGUsIHRydWUpO1xuICAgICAgfSBlbHNlIGlmKHRoaXMuaG92ZXJlZE5vZGUgJiYgIXRoaXMuaG92ZXJlZE5vZGUuc2VsZWN0ZWQpIHtcbiAgICAgICAgLy9jaGVjayBpZiBhbiBhbmltYXRpb24gaXMgcnVubmluZyBhbmQgZXhpdCBpdFxuICAgICAgICB0aGlzLmZ4Lm5vZGVGeEFuaW1hdGlvbi5zdG9wVGltZXIoKTtcbiAgICAgICAgLy91bnNlbGVjdCBob3ZlcmVkIG5vZGVcbiAgICAgICAgdGhpcy50b2dnbGVTdHlsZXNPbkhvdmVyKHRoaXMuaG92ZXJlZE5vZGUsIGZhbHNlKTtcbiAgICAgICAgZGVsZXRlIHRoaXMuaG92ZXJlZE5vZGUuaG92ZXJlZDtcbiAgICAgICAgdGhpcy5ob3ZlcmVkTm9kZSA9IGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgfVxufSk7XG5cbkV4dHJhcy5DbGFzc2VzLk5hdmlnYXRpb24gPSBuZXcgQ2xhc3Moe1xuICBJbXBsZW1lbnRzOiBbRXh0cmFzSW5pdGlhbGl6ZXIsIEV2ZW50c0ludGVyZmFjZV0sXG4gIFxuICBpbml0aWFsaXplUG9zdDogZnVuY3Rpb24oKSB7XG4gICAgdGhpcy5wb3MgPSBmYWxzZTtcbiAgICB0aGlzLnByZXNzZWQgPSBmYWxzZTtcbiAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG4gICAgdGhpcy5pbml0RGlzdCA9IGZhbHNlO1xuICAgIC8vIEVORCBNRVRBTUFQUyBDT0RFXG4gIH0sXG4gIFxuICBvbk1vdXNlV2hlZWw6IGZ1bmN0aW9uKGUsIHdpbiwgc2Nyb2xsKSB7ICAgICAgXG4gICAgaWYoIXRoaXMuY29uZmlnLnpvb21pbmcpIHJldHVybjtcbiAgICBcbiAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuXHQgIGlmIChlLnRhcmdldC5pZCAhPSAnaW5mb3Zpcy1jYW52YXMnKSByZXR1cm47XG4gICAgaWYgKE1ldGFtYXBzLkNyZWF0ZS5uZXdUb3BpYy5iZWluZ0NyZWF0ZWQgJiYgIU1ldGFtYXBzLkNyZWF0ZS5uZXdUb3BpYy5waW5uZWQpIHJldHVybjtcblx0ICAvLyBFTkQgTUVUQU1BUFMgQ09ERVxuICBcbiAgICAvLyQuZXZlbnQuc3RvcCgkLmV2ZW50LmdldChlLCB3aW4pKTtcbiAgICAvLyBFTkQgTUVUQU1BUFMgQ09ERVxuICAgIC8vIE9SSUdJTkFMIENPREUgJC5ldmVudC5zdG9wKCQuZXZlbnQuZ2V0KGUsIHdpbikpO1xuXG4gICAgdmFyIHZhbCA9IHRoaXMuY29uZmlnLnpvb21pbmcgLyAxMDAwLFxuICAgICAgICBhbnMgPSAxICsgc2Nyb2xsICogdmFsO1xuICAgICAgICBcbiAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG5cdCAgaWYgKCgoYW5zID4gMSkgJiYgKDUgPj0gdGhpcy5jYW52YXMuc2NhbGVPZmZzZXRYKSkgfHwgKChhbnMgPCAxKSAmJiAodGhpcy5jYW52YXMuc2NhbGVPZmZzZXRYID49IDAuMikpKSB7XG5cdCAgICB2YXIgcyA9IHRoaXMuY2FudmFzLmdldFNpemUoKSxcbiAgICAgICAgICBwID0gdGhpcy5jYW52YXMuZ2V0UG9zKCksXG4gICAgICAgICAgb3ggPSB0aGlzLmNhbnZhcy50cmFuc2xhdGVPZmZzZXRYLFxuICAgICAgICAgIG95ID0gdGhpcy5jYW52YXMudHJhbnNsYXRlT2Zmc2V0WSxcbiAgICAgICAgICBzeCA9IHRoaXMuY2FudmFzLnNjYWxlT2Zmc2V0WCxcbiAgICAgICAgICBzeSA9IHRoaXMuY2FudmFzLnNjYWxlT2Zmc2V0WTtcblx0ICAgIFxuXHQgICAgLy9CYXNpY2FsbHkgdGhpcyBpcyBqdXN0IGEgZHVwbGljYXRpb24gb2YgdGhlIFV0aWwgZnVuY3Rpb24gcGl4ZWxzVG9Db29yZHMsIGl0IGZpbmRzIHRoZSBjYW52YXMgY29vcmRpbmF0ZSBvZiB0aGUgbW91c2UgcG9pbnRlclxuICAgICAgdmFyIHBvaW50ZXJDb29yZFggPSAoZS5wYWdlWCAtIHAueCAtIHMud2lkdGggLyAyIC0gb3gpICogKDEgLyBzeCksXG4gICAgICAgICAgcG9pbnRlckNvb3JkWSA9IChlLnBhZ2VZIC0gcC55IC0gcy5oZWlnaHQgLyAyIC0gb3kpICogKDEgLyBzeSk7XG5cbiAgICAgIC8vVGhpcyB0cmFuc2xhdGVzIHRoZSBjYW52YXMgdG8gYmUgY2VudHJlZCBvdmVyIHRoZSBtb3VzZSBwb2ludGVyLCB0aGVuIHRoZSBjYW52YXMgaXMgem9vbWVkIGFzIGludGVuZGVkLlxuICAgICAgdGhpcy5jYW52YXMudHJhbnNsYXRlKC1wb2ludGVyQ29vcmRYLC1wb2ludGVyQ29vcmRZKTtcbiAgICAgIHRoaXMuY2FudmFzLnNjYWxlKGFucywgYW5zKTtcbiAgICAgIFxuICAgICAgLy9HZXQgdGhlIGNhbnZhcyBhdHRyaWJ1dGVzIGFnYWluIG5vdyB0aGF0IGlzIGhhcyBjaGFuZ2VkXG4gICAgICBzID0gdGhpcy5jYW52YXMuZ2V0U2l6ZSgpLFxuICAgICAgcCA9IHRoaXMuY2FudmFzLmdldFBvcygpLFxuICAgICAgb3ggPSB0aGlzLmNhbnZhcy50cmFuc2xhdGVPZmZzZXRYLFxuICAgICAgb3kgPSB0aGlzLmNhbnZhcy50cmFuc2xhdGVPZmZzZXRZLFxuICAgICAgc3ggPSB0aGlzLmNhbnZhcy5zY2FsZU9mZnNldFgsXG4gICAgICBzeSA9IHRoaXMuY2FudmFzLnNjYWxlT2Zmc2V0WTtcbiAgICAgIHZhciBuZXdYID0gKGUucGFnZVggLSBwLnggLSBzLndpZHRoIC8gMiAtIG94KSAqICgxIC8gc3gpLFxuICAgICAgICAgIG5ld1kgPSAoZS5wYWdlWSAtIHAueSAtIHMuaGVpZ2h0IC8gMiAtIG95KSAqICgxIC8gc3kpO1xuICAgICAgICAgIFxuICAgICAgLy9UcmFuc2xhdGUgdGhlIGNhbnZhcyB0byBwdXQgdGhlIHBvaW50ZXIgYmFjayBvdmVyIHRvcCB0aGUgc2FtZSBjb29yZGluYXRlIGl0IHdhcyBvdmVyIGJlZm9yZVxuICAgICAgdGhpcy5jYW52YXMudHJhbnNsYXRlKG5ld1gtcG9pbnRlckNvb3JkWCxuZXdZLXBvaW50ZXJDb29yZFkpO1xuXHQgIH1cblx0ICBcbiAgICAvLyBFTkQgTUVUQU1BUFMgQ09ERVxuICAgIC8vIE9SSUdJTkFMIENPREUgdGhpcy5jYW52YXMuc2NhbGUoYW5zLCBhbnMpO1xuXG4gICAgLy8gU1RBUlQgTUVUQU1BUFMgQ09ERVxuICAgICAgalF1ZXJ5KGRvY3VtZW50KS50cmlnZ2VyKE1ldGFtYXBzLkpJVC5ldmVudHMuem9vbSwgW2VdKTtcbiAgICAvLyBFTkQgTUVUQU1BUFMgQ09ERVxuICB9LFxuICBcbiAgb25Nb3VzZURvd246IGZ1bmN0aW9uKGUsIHdpbiwgZXZlbnRJbmZvKSB7XG4gICAgLy8vY29uc29sZS5sb2coJ21vdXNlIGRvd24hISEhJyk7XG4gICAgaWYoIXRoaXMuY29uZmlnLnBhbm5pbmcpIHJldHVybjtcbiAgICBcbiAgICAvL1NUQVJUIE1FVEFNQVBTIENPREVcbiAgICBNZXRhbWFwcy5Nb3VzZS5jaGFuZ2VJblggPSAwO1xuICAgIE1ldGFtYXBzLk1vdXNlLmNoYW5nZUluWSA9IDA7XG4gICAgaWYoKHRoaXMuY29uZmlnLnBhbm5pbmcgPT0gJ2F2b2lkIG5vZGVzJyAmJiBldmVudEluZm8uZ2V0Tm9kZSgpKSB8fCBldmVudEluZm8uZ2V0RWRnZSgpKSByZXR1cm47XG4gICAgLy8gRU5EIE1FVEFNQVBTIENPREVcbiAgICAvLyBPUklHSU5BbCBDT0RFIGlmKHRoaXMuY29uZmlnLnBhbm5pbmcgPT0gJ2F2b2lkIG5vZGVzJyAmJiAodGhpcy5kb20/IHRoaXMuaXNMYWJlbChlLCB3aW4pIDogZXZlbnRJbmZvLmdldE5vZGUoKSkpIHJldHVybjtcbiAgICBcbiAgICB0aGlzLnByZXNzZWQgPSB0cnVlO1xuICAgIFxuICAgIC8vU1RBUlQgTUVUQU1BUFMgQ09ERVxuICAgIHZhciByaWdodENsaWNrID0gZS5idXR0b24gPT0gMiB8fCAobmF2aWdhdG9yLnBsYXRmb3JtLmluZGV4T2YoXCJNYWNcIikgIT0gLTEgJiYgZS5jdHJsS2V5KTsgXG4gICAgLy8gVE9ETyBtYWtlIHN1cmUgdGhpcyB3b3JrcyBhY3Jvc3MgYnJvd3NlcnMgIFxuICAgIGlmICghTWV0YW1hcHMuTW91c2UuYm94U3RhcnRDb29yZGluYXRlcyAmJiAoKGUuYnV0dG9uID09IDAgJiYgZS5zaGlmdEtleSkgfHwgKGUuYnV0dG9uID09IDAgJiYgZS5jdHJsS2V5KSAgfHwgcmlnaHRDbGljaykpIHtcbiAgICAgIE1ldGFtYXBzLk1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMgPSBldmVudEluZm8uZ2V0UG9zKCk7XG4gICAgICAvL2NvbnNvbGUubG9nKCdtb3VzZSBkb3duJyk7XG4gICAgfVxuXG4gICAgTWV0YW1hcHMuTW91c2UuZGlkUGFuID0gZmFsc2U7XG4gICAgXG5cbiAgICBcbiAgICAvLyBFTkQgTUVUQU1BUFMgQ09ERVxuICAgIFxuICAgIHRoaXMucG9zID0gZXZlbnRJbmZvLmdldFBvcygpO1xuICAgIHZhciBjYW52YXMgPSB0aGlzLmNhbnZhcyxcbiAgICAgICAgb3ggPSBjYW52YXMudHJhbnNsYXRlT2Zmc2V0WCxcbiAgICAgICAgb3kgPSBjYW52YXMudHJhbnNsYXRlT2Zmc2V0WSxcbiAgICAgICAgc3ggPSBjYW52YXMuc2NhbGVPZmZzZXRYLFxuICAgICAgICBzeSA9IGNhbnZhcy5zY2FsZU9mZnNldFk7XG4gICAgdGhpcy5wb3MueCAqPSBzeDtcbiAgICB0aGlzLnBvcy54ICs9IG94O1xuICAgIHRoaXMucG9zLnkgKj0gc3k7XG4gICAgdGhpcy5wb3MueSArPSBveTtcbiAgfSxcbiAgXG4gIG9uTW91c2VNb3ZlOiBmdW5jdGlvbihlLCB3aW4sIGV2ZW50SW5mbykge1xuICAgIGlmKCF0aGlzLmNvbmZpZy5wYW5uaW5nKSByZXR1cm47XG4gICAgaWYoIXRoaXMucHJlc3NlZCkgcmV0dXJuO1xuICAgIGlmKHRoaXMuY29uZmlnLnBhbm5pbmcgPT0gJ2F2b2lkIG5vZGVzJyAmJiAodGhpcy5kb20/IHRoaXMuaXNMYWJlbChlLCB3aW4pIDogZXZlbnRJbmZvLmdldE5vZGUoKSkpIHJldHVybjtcbiAgICBcbiAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG4gICAgdmFyIHJpZ2h0Q2xpY2sgPSBlLmJ1dHRvbiA9PSAyIHx8IChuYXZpZ2F0b3IucGxhdGZvcm0uaW5kZXhPZihcIk1hY1wiKSAhPSAtMSAmJiBlLmN0cmxLZXkpO1xuICAgIGlmICghTWV0YW1hcHMuTW91c2UuYm94U3RhcnRDb29yZGluYXRlcyAmJiAoKGUuYnV0dG9uID09IDAgJiYgZS5zaGlmdEtleSkgfHwgKGUuYnV0dG9uID09IDAgJiYgZS5jdHJsS2V5KSAgfHwgcmlnaHRDbGljaykpIHtcbiAgICAgIE1ldGFtYXBzLlZpc3VhbGl6ZS5tR3JhcGguYnVzeSA9IHRydWU7XG4gICAgICBNZXRhbWFwcy5ib3hTdGFydENvb3JkaW5hdGVzID0gZXZlbnRJbmZvLmdldFBvcygpO1xuICAgICAgLy9jb25zb2xlLmxvZygnbW91c2UgbW92ZScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoTWV0YW1hcHMuTW91c2UuYm94U3RhcnRDb29yZGluYXRlcyAmJiAoKGUuYnV0dG9uID09IDAgJiYgZS5zaGlmdEtleSkgfHwgKGUuYnV0dG9uID09IDAgJiYgZS5jdHJsS2V5KSAgfHwgcmlnaHRDbGljaykpIHtcbiAgICAgIE1ldGFtYXBzLlZpc3VhbGl6ZS5tR3JhcGguYnVzeSA9IHRydWU7XG4gICAgICBNZXRhbWFwcy5Nb3VzZS5ib3hFbmRDb29yZGluYXRlcyA9IHtcbiAgICAgICAgeDogZXZlbnRJbmZvLmdldFBvcygpLngsXG4gICAgICAgIHk6IGV2ZW50SW5mby5nZXRQb3MoKS55XG4gICAgICB9XG4gICAgICBNZXRhbWFwcy5WaXN1YWxpemUubUdyYXBoLnBsb3QoKVxuICAgICAgLy9jb25zb2xlLmxvZygnbW91c2UgbW92ZScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAocmlnaHRDbGljayl7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChlLnRhcmdldC5pZCAhPSAnaW5mb3Zpcy1jYW52YXMnKSB7IFxuICAgICAgdGhpcy5wcmVzc2VkID0gZmFsc2U7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIE1ldGFtYXBzLk1vdXNlLmRpZFBhbiA9IHRydWU7XG4gICAgLy8gRU5EIE1FVEFNQVBTIENPREVcbiAgICBcbiAgICB2YXIgdGhpc3BvcyA9IHRoaXMucG9zLCBcbiAgICAgICAgY3VycmVudFBvcyA9IGV2ZW50SW5mby5nZXRQb3MoKSxcbiAgICAgICAgY2FudmFzID0gdGhpcy5jYW52YXMsXG4gICAgICAgIG94ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFgsXG4gICAgICAgIG95ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFksXG4gICAgICAgIHN4ID0gY2FudmFzLnNjYWxlT2Zmc2V0WCxcbiAgICAgICAgc3kgPSBjYW52YXMuc2NhbGVPZmZzZXRZO1xuICAgIGN1cnJlbnRQb3MueCAqPSBzeDtcbiAgICBjdXJyZW50UG9zLnkgKj0gc3k7XG4gICAgY3VycmVudFBvcy54ICs9IG94O1xuICAgIGN1cnJlbnRQb3MueSArPSBveTtcbiAgICB2YXIgeCA9IGN1cnJlbnRQb3MueCAtIHRoaXNwb3MueCxcbiAgICAgICAgeSA9IGN1cnJlbnRQb3MueSAtIHRoaXNwb3MueTtcbiAgICBcbiAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG4gICAgTWV0YW1hcHMuTW91c2UuY2hhbmdlSW5YID0geDtcbiAgICBNZXRhbWFwcy5Nb3VzZS5jaGFuZ2VJblkgPSB5O1xuICAgIC8vIEVORCBNRVRBTUFQUyBDT0RFXG4gICAgXG4gICAgdGhpcy5wb3MgPSBjdXJyZW50UG9zO1xuICAgIHRoaXMuY2FudmFzLnRyYW5zbGF0ZSh4ICogMS9zeCwgeSAqIDEvc3kpO1xuXG4gICAgLy8gU1RBUlQgTUVUQU1BUFMgQ09ERVxuICAgICAgalF1ZXJ5KGRvY3VtZW50KS50cmlnZ2VyKE1ldGFtYXBzLkpJVC5ldmVudHMucGFuKTtcbiAgICAvLyBFTkQgTUVUQU1BUFMgQ09ERVxuICB9LFxuICBcbiAgb25Nb3VzZVVwOiBmdW5jdGlvbihlLCB3aW4sIGV2ZW50SW5mbywgaXNSaWdodENsaWNrKSB7XG4gICAgaWYoIXRoaXMuY29uZmlnLnBhbm5pbmcpIHJldHVybjtcbiAgICB0aGlzLnByZXNzZWQgPSBmYWxzZTtcbiAgICBcbiAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG4gICAgaWYgKE1ldGFtYXBzLk1vdXNlLmRpZFBhbikgTWV0YW1hcHMuSklULlNtb290aFBhbm5pbmcoKTsgXG4gICAgLy8gRU5EIE1FVEFNQVBTIENPREVcbiAgICBcbiAgfSxcbiAgXG4gIC8vIFNUQVJUIE1FVEFNQVBTIENPREVcbiAgb25Ub3VjaFN0YXJ0OiBmdW5jdGlvbihlLCB3aW4sIGV2ZW50SW5mbykge1xuICAgIGlmKCF0aGlzLmNvbmZpZy5wYW5uaW5nKSByZXR1cm47XG4gICAgTWV0YW1hcHMuTW91c2UuY2hhbmdlSW5YID0gMDtcbiAgICBNZXRhbWFwcy5Nb3VzZS5jaGFuZ2VJblkgPSAwO1xuICAgIGlmKCh0aGlzLmNvbmZpZy5wYW5uaW5nID09ICdhdm9pZCBub2RlcycgJiYgZXZlbnRJbmZvLmdldE5vZGUoKSkgfHwgZXZlbnRJbmZvLmdldEVkZ2UoKSkgcmV0dXJuO1xuICAgIHRoaXMucHJlc3NlZCA9IHRydWU7XG4gICAgdmFyIHJpZ2h0Q2xpY2sgPSBlLmJ1dHRvbiA9PSAyIHx8IChuYXZpZ2F0b3IucGxhdGZvcm0uaW5kZXhPZihcIk1hY1wiKSAhPSAtMSAmJiBlLmN0cmxLZXkpOyBcbiAgICBpZiAoIU1ldGFtYXBzLk1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMgJiYgKChlLmJ1dHRvbiA9PSAwICYmIGUuc2hpZnRLZXkpIHx8IChlLmJ1dHRvbiA9PSAwICYmIGUuY3RybEtleSkgIHx8IHJpZ2h0Q2xpY2spKSB7XG4gICAgICBNZXRhbWFwcy5Nb3VzZS5ib3hTdGFydENvb3JkaW5hdGVzID0gZXZlbnRJbmZvLmdldFBvcygpO1xuICAgIH1cbiAgICBNZXRhbWFwcy5Nb3VzZS5kaWRQYW4gPSBmYWxzZTtcbiAgICB0aGlzLnBvcyA9IGV2ZW50SW5mby5nZXRQb3MoKTtcbiAgICB2YXIgY2FudmFzID0gdGhpcy5jYW52YXMsXG4gICAgICAgIG94ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFgsXG4gICAgICAgIG95ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFksXG4gICAgICAgIHN4ID0gY2FudmFzLnNjYWxlT2Zmc2V0WCxcbiAgICAgICAgc3kgPSBjYW52YXMuc2NhbGVPZmZzZXRZO1xuICAgIHRoaXMucG9zLnggKj0gc3g7XG4gICAgdGhpcy5wb3MueCArPSBveDtcbiAgICB0aGlzLnBvcy55ICo9IHN5O1xuICAgIHRoaXMucG9zLnkgKz0gb3k7XG4gIH0sXG4gIFxuICBvblRvdWNoTW92ZTogZnVuY3Rpb24oZSwgd2luLCBldmVudEluZm8pIHtcbiAgICBpZighdGhpcy5jb25maWcucGFubmluZykgcmV0dXJuO1xuICAgIGlmKCF0aGlzLnByZXNzZWQpIHJldHVybjtcbiAgICBpZih0aGlzLmNvbmZpZy5wYW5uaW5nID09ICdhdm9pZCBub2RlcycgJiYgKHRoaXMuZG9tPyB0aGlzLmlzTGFiZWwoZSwgd2luKSA6IGV2ZW50SW5mby5nZXROb2RlKCkpKSByZXR1cm47XG4gICAgXG4gICAgaWYgKGUudG91Y2hlcy5sZW5ndGggPT0gMSkge1xuICAgICAgdmFyIHJpZ2h0Q2xpY2sgPSBlLmJ1dHRvbiA9PSAyIHx8IChuYXZpZ2F0b3IucGxhdGZvcm0uaW5kZXhPZihcIk1hY1wiKSAhPSAtMSAmJiBlLmN0cmxLZXkpO1xuICAgICAgaWYgKCFNZXRhbWFwcy5Nb3VzZS5ib3hTdGFydENvb3JkaW5hdGVzICYmICgoZS5idXR0b24gPT0gMCAmJiBlLnNoaWZ0S2V5KSB8fCAoZS5idXR0b24gPT0gMCAmJiBlLmN0cmxLZXkpICB8fCByaWdodENsaWNrKSkge1xuICAgICAgICBNZXRhbWFwcy5WaXN1YWxpemUubUdyYXBoLmJ1c3kgPSB0cnVlO1xuICAgICAgICBNZXRhbWFwcy5ib3hTdGFydENvb3JkaW5hdGVzID0gZXZlbnRJbmZvLmdldFBvcygpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAoTWV0YW1hcHMuTW91c2UuYm94U3RhcnRDb29yZGluYXRlcyAmJiAoKGUuYnV0dG9uID09IDAgJiYgZS5zaGlmdEtleSkgfHwgKGUuYnV0dG9uID09IDAgJiYgZS5jdHJsS2V5KSAgfHwgcmlnaHRDbGljaykpIHtcbiAgICAgICAgTWV0YW1hcHMuVmlzdWFsaXplLm1HcmFwaC5idXN5ID0gdHJ1ZTtcbiAgICAgICAgTWV0YW1hcHMuTW91c2UuYm94RW5kQ29vcmRpbmF0ZXMgPSB7XG4gICAgICAgICAgeDogZXZlbnRJbmZvLmdldFBvcygpLngsXG4gICAgICAgICAgeTogZXZlbnRJbmZvLmdldFBvcygpLnlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAocmlnaHRDbGljayl7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmIChlLnRhcmdldC5pZCAhPSAnaW5mb3Zpcy1jYW52YXMnKSB7IFxuICAgICAgICB0aGlzLnByZXNzZWQgPSBmYWxzZTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgTWV0YW1hcHMuTW91c2UuZGlkUGFuID0gdHJ1ZTtcbiAgICAgIHZhciB0aGlzcG9zID0gdGhpcy5wb3MsIFxuICAgICAgICAgIGN1cnJlbnRQb3MgPSBldmVudEluZm8uZ2V0UG9zKCksXG4gICAgICAgICAgY2FudmFzID0gdGhpcy5jYW52YXMsXG4gICAgICAgICAgb3ggPSBjYW52YXMudHJhbnNsYXRlT2Zmc2V0WCxcbiAgICAgICAgICBveSA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRZLFxuICAgICAgICAgIHN4ID0gY2FudmFzLnNjYWxlT2Zmc2V0WCxcbiAgICAgICAgICBzeSA9IGNhbnZhcy5zY2FsZU9mZnNldFk7XG4gICAgICBjdXJyZW50UG9zLnggKj0gc3g7XG4gICAgICBjdXJyZW50UG9zLnkgKj0gc3k7XG4gICAgICBjdXJyZW50UG9zLnggKz0gb3g7XG4gICAgICBjdXJyZW50UG9zLnkgKz0gb3k7XG4gICAgICB2YXIgeCA9IGN1cnJlbnRQb3MueCAtIHRoaXNwb3MueCxcbiAgICAgICAgICB5ID0gY3VycmVudFBvcy55IC0gdGhpc3Bvcy55O1xuICAgICAgTWV0YW1hcHMuTW91c2UuY2hhbmdlSW5YID0geDtcbiAgICAgIE1ldGFtYXBzLk1vdXNlLmNoYW5nZUluWSA9IHk7XG4gICAgICB0aGlzLnBvcyA9IGN1cnJlbnRQb3M7XG4gICAgICB0aGlzLmNhbnZhcy50cmFuc2xhdGUoeCAqIDEvc3gsIHkgKiAxL3N5KTtcbiAgICAgIGpRdWVyeShkb2N1bWVudCkudHJpZ2dlcihNZXRhbWFwcy5KSVQuZXZlbnRzLnBhbik7XG4gICAgfSBcbiAgICAvKlxuICAgIGVsc2UgaWYgKGUudG91Y2hlcy5sZW5ndGggPT0gMikge1xuICAgICAgdmFyIHRvdWNoMSA9IGUudG91Y2hlc1swXVxuICAgICAgdmFyIHRvdWNoMiA9IGUudG91Y2hlc1sxXVxuICAgICAgdmFyIGNhbnZhcyA9IHRoaXMuY2FudmFzXG4gICAgICBcbiAgICAgIGNhbGxDb3VudCsrO1xuXG4gICAgICB2YXIgZGlzdCA9IE1ldGFtYXBzLlV0aWwuZ2V0RGlzdGFuY2Uoe1xuICAgICAgICB4OiB0b3VjaDEuY2xpZW50WCxcbiAgICAgICAgeTogdG91Y2gxLmNsaWVudFlcbiAgICAgIH0sIHtcbiAgICAgICAgeDogdG91Y2gyLmNsaWVudFgsXG4gICAgICAgIHk6IHRvdWNoMi5jbGllbnRZXG4gICAgICB9KVxuXG4gICAgICBpZiAoIXRoaXMuaW5pdERpc3QpIHtcbiAgICAgICAgdGhpcy5pbml0RGlzdCA9IGRpc3RcbiAgICAgICAgdGhpcy5pbml0U2NhbGUgPSBjYW52YXMuc2NhbGVPZmZzZXRYXG4gICAgICB9XG4gICAgICB2YXIgc2NhbGUgPSAoZGlzdCAvIHRoaXMuaW5pdERpc3QpXG4gICAgICBcbiAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwiaGVhZGVyX2NvbnRlbnRcIikuaW5uZXJIVE1MID0gc2NhbGUgKyAnICcgKyBjYW52YXMuc2NhbGVPZmZzZXRYXG4gICAgICBpZiAoMzAgPj0gdGhpcy5pbml0U2NhbGUgKiBzY2FsZSAmJiB0aGlzLmluaXRTY2FsZSAqIHNjYWxlID49IDAuMikge1xuICAgICAgICBjYW52YXMuc2NhbGUodGhpcy5pbml0U2NhbGUgKiBzY2FsZSwgdGhpcy5pbml0U2NhbGUgKiBzY2FsZSlcbiAgICAgIH1cbiAgICAgIGlmIChjYW52YXMuc2NhbGVPZmZzZXRYIDwgMC41KSB7XG4gICAgICAgIGNhbnZhcy52aXoubGFiZWxzLmhpZGVMYWJlbHModHJ1ZSlcbiAgICAgIH0gZWxzZSBpZiAoY2FudmFzLnNjYWxlT2Zmc2V0WCA+IDAuNSkge1xuICAgICAgICBjYW52YXMudml6LmxhYmVscy5oaWRlTGFiZWxzKGZhbHNlKVxuICAgICAgfVxuICAgICAgXG4gICAgICBqUXVlcnkoZG9jdW1lbnQpLnRyaWdnZXIoTWV0YW1hcHMuSklULmV2ZW50cy56b29tKTtcbiAgICB9XG4gICAgKi9cbiAgfSxcbiAgXG4gIG9uVG91Y2hFbmQ6IGZ1bmN0aW9uKGUsIHdpbiwgZXZlbnRJbmZvLCBpc1JpZ2h0Q2xpY2spIHtcbiAgICBpZighdGhpcy5jb25maWcucGFubmluZykgcmV0dXJuO1xuICAgIHRoaXMucHJlc3NlZCA9IGZhbHNlO1xuICAgIGlmIChNZXRhbWFwcy5Nb3VzZS5kaWRQYW4pIE1ldGFtYXBzLkpJVC5TbW9vdGhQYW5uaW5nKCk7XG4gICAgdGhpcy5pbml0RGlzdCA9IGZhbHNlXG4gIH1cbiAgLy8gRU5EIE1FVEFNQVBTIENPREVcbn0pO1xuXG5cbi8qXG4gKiBGaWxlOiBDYW52YXMuanNcbiAqXG4gKi9cblxuLypcbiBDbGFzczogQ2FudmFzXG4gXG4gXHRBIGNhbnZhcyB3aWRnZXQgdXNlZCBieSBhbGwgdmlzdWFsaXphdGlvbnMuIFRoZSBjYW52YXMgb2JqZWN0IGNhbiBiZSBhY2Nlc3NlZCBieSBkb2luZyAqdml6LmNhbnZhcyouIElmIHlvdSB3YW50IHRvIFxuIFx0a25vdyBtb3JlIGFib3V0IDxDYW52YXM+IG9wdGlvbnMgdGFrZSBhIGxvb2sgYXQgPE9wdGlvbnMuQ2FudmFzPi5cbiBcbiBBIGNhbnZhcyB3aWRnZXQgaXMgYSBzZXQgb2YgRE9NIGVsZW1lbnRzIHRoYXQgd3JhcCB0aGUgbmF0aXZlIGNhbnZhcyBET00gRWxlbWVudCBwcm92aWRpbmcgYSBjb25zaXN0ZW50IEFQSSBhbmQgYmVoYXZpb3IgXG4gYWNyb3NzIGFsbCBicm93c2Vycy4gSXQgY2FuIGFsc28gaW5jbHVkZSBFbGVtZW50cyB0byBhZGQgRE9NIChTVkcgb3IgSFRNTCkgbGFiZWwgc3VwcG9ydCB0byBhbGwgdmlzdWFsaXphdGlvbnMuXG4gXG4gRXhhbXBsZTpcbiBcbiBTdXBwb3NlIHdlIGhhdmUgdGhpcyBIVE1MXG4gXG4gKHN0YXJ0IGNvZGUgeG1sKVxuIFx0PGRpdiBpZD1cImluZm92aXNcIj48L2Rpdj5cbiAoZW5kIGNvZGUpXG4gXG4gTm93IHdlIGNyZWF0ZSBhIG5ldyBWaXN1YWxpemF0aW9uXG4gXG4gKHN0YXJ0IGNvZGUganMpXG4gXHR2YXIgdml6ID0gbmV3ICRqaXQuVml6KHtcbiBcdFx0Ly9XaGVyZSB0byBpbmplY3QgdGhlIGNhbnZhcy4gQW55IGRpdiBjb250YWluZXIgd2lsbCBkby5cbiBcdFx0J2luamVjdEludG8nOidpbmZvdmlzJyxcblx0XHQgLy93aWR0aCBhbmQgaGVpZ2h0IGZvciBjYW52YXMuIFxuXHRcdCAvL0RlZmF1bHQncyB0byB0aGUgY29udGFpbmVyIG9mZnNldFdpZHRoIGFuZCBIZWlnaHQuXG5cdFx0ICd3aWR0aCc6IDkwMCxcblx0XHQgJ2hlaWdodCc6NTAwXG5cdCB9KTtcbiAoZW5kIGNvZGUpXG5cbiBUaGUgZ2VuZXJhdGVkIEhUTUwgd2lsbCBsb29rIGxpa2UgdGhpc1xuIFxuIChzdGFydCBjb2RlIHhtbClcbiA8ZGl2IGlkPVwiaW5mb3Zpc1wiPlxuIFx0PGRpdiBpZD1cImluZm92aXMtY2FudmFzd2lkZ2V0XCIgc3R5bGU9XCJwb3NpdGlvbjpyZWxhdGl2ZTtcIj5cbiBcdDxjYW52YXMgaWQ9XCJpbmZvdmlzLWNhbnZhc1wiIHdpZHRoPTkwMCBoZWlnaHQ9NTAwXG4gXHRzdHlsZT1cInBvc2l0aW9uOmFic29sdXRlOyB0b3A6MDsgbGVmdDowOyB3aWR0aDo5MDBweDsgaGVpZ2h0OjUwMHB4O1wiIC8+XG4gXHQ8ZGl2IGlkPVwiaW5mb3Zpcy1sYWJlbFwiXG4gXHRzdHlsZT1cIm92ZXJmbG93OnZpc2libGU7IHBvc2l0aW9uOmFic29sdXRlOyB0b3A6MDsgbGVmdDowOyB3aWR0aDo5MDBweDsgaGVpZ2h0OjBweFwiPlxuIFx0PC9kaXY+XG4gXHQ8L2Rpdj5cbiA8L2Rpdj5cbiAoZW5kIGNvZGUpXG4gXG4gQXMgeW91IGNhbiBzZWUsIHRoZSBnZW5lcmF0ZWQgSFRNTCBjb25zaXN0cyBvZiBhIGNhbnZhcyBET00gRWxlbWVudCBvZiBpZCAqaW5mb3Zpcy1jYW52YXMqIGFuZCBhIGRpdiBsYWJlbCBjb250YWluZXJcbiBvZiBpZCAqaW5mb3Zpcy1sYWJlbCosIHdyYXBwZWQgaW4gYSBtYWluIGRpdiBjb250YWluZXIgb2YgaWQgKmluZm92aXMtY2FudmFzd2lkZ2V0Ki5cbiAqL1xuXG52YXIgQ2FudmFzO1xuKGZ1bmN0aW9uKCkge1xuICAvL2NoZWNrIGZvciBuYXRpdmUgY2FudmFzIHN1cHBvcnRcbiAgdmFyIGNhbnZhc1R5cGUgPSB0eXBlb2YgSFRNTENhbnZhc0VsZW1lbnQsXG4gICAgICBzdXBwb3J0c0NhbnZhcyA9IChjYW52YXNUeXBlID09ICdvYmplY3QnIHx8IGNhbnZhc1R5cGUgPT0gJ2Z1bmN0aW9uJyk7XG4gIC8vY3JlYXRlIGVsZW1lbnQgZnVuY3Rpb25cbiAgZnVuY3Rpb24gJEUodGFnLCBwcm9wcykge1xuICAgIHZhciBlbGVtID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCh0YWcpO1xuICAgIGZvcih2YXIgcCBpbiBwcm9wcykge1xuICAgICAgaWYodHlwZW9mIHByb3BzW3BdID09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgJC5leHRlbmQoZWxlbVtwXSwgcHJvcHNbcF0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZWxlbVtwXSA9IHByb3BzW3BdO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGFnID09IFwiY2FudmFzXCIgJiYgIXN1cHBvcnRzQ2FudmFzICYmIEdfdm1sQ2FudmFzTWFuYWdlcikge1xuICAgICAgZWxlbSA9IEdfdm1sQ2FudmFzTWFuYWdlci5pbml0RWxlbWVudChkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGVsZW0pKTtcbiAgICB9XG4gICAgcmV0dXJuIGVsZW07XG4gIH1cbiAgLy9jYW52YXMgd2lkZ2V0IHdoaWNoIHdlIHdpbGwgY2FsbCBqdXN0IENhbnZhc1xuICAkaml0LkNhbnZhcyA9IENhbnZhcyA9IG5ldyBDbGFzcyh7XG4gICAgY2FudmFzZXM6IFtdLFxuICAgIHBvczogZmFsc2UsXG4gICAgZWxlbWVudDogZmFsc2UsXG4gICAgbGFiZWxDb250YWluZXI6IGZhbHNlLFxuICAgIHRyYW5zbGF0ZU9mZnNldFg6IDAsXG4gICAgdHJhbnNsYXRlT2Zmc2V0WTogMCxcbiAgICBzY2FsZU9mZnNldFg6IDEsXG4gICAgc2NhbGVPZmZzZXRZOiAxLFxuICAgIFxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKHZpeiwgb3B0KSB7XG4gICAgICB0aGlzLnZpeiA9IHZpejtcbiAgICAgIHRoaXMub3B0ID0gdGhpcy5jb25maWcgPSBvcHQ7XG4gICAgICB2YXIgaWQgPSAkLnR5cGUob3B0LmluamVjdEludG8pID09ICdzdHJpbmcnPyBcbiAgICAgICAgICBvcHQuaW5qZWN0SW50bzpvcHQuaW5qZWN0SW50by5pZCxcbiAgICAgICAgICB0eXBlID0gb3B0LnR5cGUsXG4gICAgICAgICAgaWRMYWJlbCA9IGlkICsgXCItbGFiZWxcIiwgXG4gICAgICAgICAgd3JhcHBlciA9ICQoaWQpLFxuICAgICAgICAgIHdpZHRoID0gb3B0LndpZHRoLCAvLyB8fCB3cmFwcGVyLm9mZnNldFdpZHRoLFxuICAgICAgICAgIGhlaWdodCA9IG9wdC5oZWlnaHQ7IC8vIHx8IHdyYXBwZXIub2Zmc2V0SGVpZ2h0O1xuICAgICAgdGhpcy5pZCA9IGlkO1xuICAgICAgLy9jYW52YXMgb3B0aW9uc1xuICAgICAgdmFyIGNhbnZhc09wdGlvbnMgPSB7XG4gICAgICAgIGluamVjdEludG86IGlkLFxuICAgICAgICB3aWR0aDogd2lkdGgsXG4gICAgICAgIGhlaWdodDogaGVpZ2h0XG4gICAgICB9O1xuICAgICAgLy9jcmVhdGUgbWFpbiB3cmFwcGVyXG4gICAgICB0aGlzLmVsZW1lbnQgPSAkRSgnZGl2Jywge1xuICAgICAgICAnaWQnOiBpZCArICctY2FudmFzd2lkZ2V0JyxcbiAgICAgICAgJ3N0eWxlJzoge1xuICAgICAgICAgICdwb3NpdGlvbic6ICdyZWxhdGl2ZScsXG4gICAgICAgICAgJ3dpZHRoJzogd2lkdGggKyAncHgnLFxuICAgICAgICAgICdoZWlnaHQnOiBoZWlnaHQgKyAncHgnXG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgLy9jcmVhdGUgbGFiZWwgY29udGFpbmVyXG4gICAgICB0aGlzLmxhYmVsQ29udGFpbmVyID0gdGhpcy5jcmVhdGVMYWJlbENvbnRhaW5lcihvcHQuTGFiZWwudHlwZSwgXG4gICAgICAgICAgaWRMYWJlbCwgY2FudmFzT3B0aW9ucyk7XG4gICAgICAvL2NyZWF0ZSBwcmltYXJ5IGNhbnZhc1xuICAgICAgdGhpcy5jYW52YXNlcy5wdXNoKG5ldyBDYW52YXMuQmFzZVt0eXBlXSh7XG4gICAgICAgIGNvbmZpZzogJC5leHRlbmQoe2lkU3VmZml4OiAnLWNhbnZhcyd9LCBjYW52YXNPcHRpb25zKSxcbiAgICAgICAgcGxvdDogZnVuY3Rpb24oYmFzZSkge1xuICAgICAgICAgIHZpei5meC5wbG90KCk7XG4gICAgICAgIH0sXG4gICAgICAgIHJlc2l6ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgICAgdml6LnJlZnJlc2goKTtcbiAgICAgICAgfVxuICAgICAgfSkpO1xuICAgICAgLy9jcmVhdGUgc2Vjb25kYXJ5IGNhbnZhc1xuICAgICAgdmFyIGJhY2sgPSBvcHQuYmFja2dyb3VuZDtcbiAgICAgIGlmKGJhY2spIHtcbiAgICAgICAgdmFyIGJhY2tDYW52YXMgPSBuZXcgQ2FudmFzLkJhY2tncm91bmRbYmFjay50eXBlXSh2aXosICQuZXh0ZW5kKGJhY2ssIGNhbnZhc09wdGlvbnMpKTtcbiAgICAgICAgdGhpcy5jYW52YXNlcy5wdXNoKG5ldyBDYW52YXMuQmFzZVt0eXBlXShiYWNrQ2FudmFzKSk7XG4gICAgICB9XG4gICAgICAvL2luc2VydCBjYW52YXNlc1xuICAgICAgdmFyIGxlbiA9IHRoaXMuY2FudmFzZXMubGVuZ3RoO1xuICAgICAgd2hpbGUobGVuLS0pIHtcbiAgICAgICAgdGhpcy5lbGVtZW50LmFwcGVuZENoaWxkKHRoaXMuY2FudmFzZXNbbGVuXS5jYW52YXMpO1xuICAgICAgICBpZihsZW4gPiAwKSB7XG4gICAgICAgICAgdGhpcy5jYW52YXNlc1tsZW5dLnBsb3QoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdGhpcy5lbGVtZW50LmFwcGVuZENoaWxkKHRoaXMubGFiZWxDb250YWluZXIpO1xuICAgICAgd3JhcHBlci5hcHBlbmRDaGlsZCh0aGlzLmVsZW1lbnQpO1xuXG4gICAgICAvL1VwZGF0ZSBjYW52YXMgcG9zaXRpb24gd2hlbiB0aGUgcGFnZSBpcyBzY3JvbGxlZC5cbiAgICAgIHZhciB0aW1lciA9IG51bGwsIHRoYXQgPSB0aGlzO1xuICAgICAgJC5hZGRFdmVudCh3aW5kb3csICdzY3JvbGwnLCBmdW5jdGlvbigpIHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgICAgdGltZXIgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgIHRoYXQuZ2V0UG9zKHRydWUpOyAvL3VwZGF0ZSBjYW52YXMgcG9zaXRpb25cbiAgICAgICAgfSwgNTAwKTtcbiAgICAgIH0pO1xuICAgIH0sXG4gICAgLypcbiAgICAgIE1ldGhvZDogZ2V0Q3R4XG4gICAgICBcbiAgICAgIFJldHVybnMgdGhlIG1haW4gY2FudmFzIGNvbnRleHQgb2JqZWN0XG4gICAgICBcbiAgICAgIEV4YW1wbGU6XG4gICAgICBcbiAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgIHZhciBjdHggPSBjYW52YXMuZ2V0Q3R4KCk7XG4gICAgICAgLy9Ob3cgSSBjYW4gdXNlIHRoZSBuYXRpdmUgY2FudmFzIGNvbnRleHRcbiAgICAgICAvL2FuZCBmb3IgZXhhbXBsZSBjaGFuZ2Ugc29tZSBjYW52YXMgc3R5bGVzXG4gICAgICAgY3R4Lmdsb2JhbEFscGhhID0gMTtcbiAgICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgIGdldEN0eDogZnVuY3Rpb24oaSkge1xuICAgICAgcmV0dXJuIHRoaXMuY2FudmFzZXNbaSB8fCAwXS5nZXRDdHgoKTtcbiAgICB9LFxuICAgIC8qXG4gICAgICBNZXRob2Q6IGdldENvbmZpZ1xuICAgICAgXG4gICAgICBSZXR1cm5zIHRoZSBjdXJyZW50IENvbmZpZ3VyYXRpb24gZm9yIHRoaXMgQ2FudmFzIFdpZGdldC5cbiAgICAgIFxuICAgICAgRXhhbXBsZTpcbiAgICAgIFxuICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgdmFyIGNvbmZpZyA9IGNhbnZhcy5nZXRDb25maWcoKTtcbiAgICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgIGdldENvbmZpZzogZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy5vcHQ7XG4gICAgfSxcbiAgICAvKlxuICAgICAgTWV0aG9kOiBnZXRFbGVtZW50XG5cbiAgICAgIFJldHVybnMgdGhlIG1haW4gQ2FudmFzIERPTSB3cmFwcGVyXG4gICAgICBcbiAgICAgIEV4YW1wbGU6XG4gICAgICBcbiAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgIHZhciB3cmFwcGVyID0gY2FudmFzLmdldEVsZW1lbnQoKTtcbiAgICAgICAvL1JldHVybnMgPGRpdiBpZD1cImluZm92aXMtY2FudmFzd2lkZ2V0XCIgLi4uID4uLi48L2Rpdj4gYXMgZWxlbWVudFxuICAgICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgZ2V0RWxlbWVudDogZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy5lbGVtZW50O1xuICAgIH0sXG4gICAgLypcbiAgICAgIE1ldGhvZDogZ2V0U2l6ZVxuICAgICAgXG4gICAgICBSZXR1cm5zIGNhbnZhcyBkaW1lbnNpb25zLlxuICAgICAgXG4gICAgICBSZXR1cm5zOlxuICAgICAgXG4gICAgICBBbiBvYmplY3Qgd2l0aCAqd2lkdGgqIGFuZCAqaGVpZ2h0KiBwcm9wZXJ0aWVzLlxuICAgICAgXG4gICAgICBFeGFtcGxlOlxuICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICBjYW52YXMuZ2V0U2l6ZSgpOyAvL3JldHVybnMgeyB3aWR0aDogOTAwLCBoZWlnaHQ6IDUwMCB9XG4gICAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBnZXRTaXplOiBmdW5jdGlvbihpKSB7XG4gICAgICByZXR1cm4gdGhpcy5jYW52YXNlc1tpIHx8IDBdLmdldFNpemUoKTtcbiAgICB9LFxuICAgIC8qXG4gICAgICBNZXRob2Q6IHJlc2l6ZVxuICAgICAgXG4gICAgICBSZXNpemVzIHRoZSBjYW52YXMuXG4gICAgICBcbiAgICAgIFBhcmFtZXRlcnM6XG4gICAgICBcbiAgICAgIHdpZHRoIC0gTmV3IGNhbnZhcyB3aWR0aC5cbiAgICAgIGhlaWdodCAtIE5ldyBjYW52YXMgaGVpZ2h0LlxuICAgICAgXG4gICAgICBFeGFtcGxlOlxuICAgICAgXG4gICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICBjYW52YXMucmVzaXplKHdpZHRoLCBoZWlnaHQpO1xuICAgICAgKGVuZCBjb2RlKVxuICAgIFxuICAgICovXG4gICAgcmVzaXplOiBmdW5jdGlvbih3aWR0aCwgaGVpZ2h0KSB7XG4gICAgICB0aGlzLmdldFBvcyh0cnVlKTtcbiAgICAgIHRoaXMudHJhbnNsYXRlT2Zmc2V0WCA9IHRoaXMudHJhbnNsYXRlT2Zmc2V0WSA9IDA7XG4gICAgICB0aGlzLnNjYWxlT2Zmc2V0WCA9IHRoaXMuc2NhbGVPZmZzZXRZID0gMTtcblxuICAgICAgZm9yKHZhciBpPTAsIGw9dGhpcy5jYW52YXNlcy5sZW5ndGg7IGk8bDsgaSsrKSB7XG4gICAgICAgIHRoaXMuY2FudmFzZXNbaV0ucmVzaXplKHdpZHRoLCBoZWlnaHQpO1xuICAgICAgfVxuICAgICAgdmFyIHN0eWxlID0gdGhpcy5lbGVtZW50LnN0eWxlO1xuICAgICAgc3R5bGUud2lkdGggPSB3aWR0aCArICdweCc7XG4gICAgICBzdHlsZS5oZWlnaHQgPSBoZWlnaHQgKyAncHgnO1xuICAgICAgaWYodGhpcy5sYWJlbENvbnRhaW5lcilcbiAgICAgICAgdGhpcy5sYWJlbENvbnRhaW5lci5zdHlsZS53aWR0aCA9IHdpZHRoICsgJ3B4JztcbiAgICB9LFxuICAgIC8qXG4gICAgICBNZXRob2Q6IHRyYW5zbGF0ZVxuICAgICAgXG4gICAgICBBcHBsaWVzIGEgdHJhbnNsYXRpb24gdG8gdGhlIGNhbnZhcy5cbiAgICAgIFxuICAgICAgUGFyYW1ldGVyczpcbiAgICAgIFxuICAgICAgeCAtIChudW1iZXIpIHggb2Zmc2V0LlxuICAgICAgeSAtIChudW1iZXIpIHkgb2Zmc2V0LlxuICAgICAgZGlzYWJsZVBsb3QgLSAoYm9vbGVhbikgRGVmYXVsdCdzICpmYWxzZSouIFNldCB0aGlzIHRvICp0cnVlKiBpZiB5b3UgZG9uJ3Qgd2FudCB0byByZWZyZXNoIHRoZSB2aXN1YWxpemF0aW9uLlxuICAgICAgXG4gICAgICBFeGFtcGxlOlxuICAgICAgXG4gICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICBjYW52YXMudHJhbnNsYXRlKDMwLCAzMCk7XG4gICAgICAoZW5kIGNvZGUpXG4gICAgXG4gICAgKi9cbiAgICB0cmFuc2xhdGU6IGZ1bmN0aW9uKHgsIHksIGRpc2FibGVQbG90KSB7XG4gICAgICB0aGlzLnRyYW5zbGF0ZU9mZnNldFggKz0geCp0aGlzLnNjYWxlT2Zmc2V0WDtcbiAgICAgIHRoaXMudHJhbnNsYXRlT2Zmc2V0WSArPSB5KnRoaXMuc2NhbGVPZmZzZXRZO1xuICAgICAgZm9yKHZhciBpPTAsIGw9dGhpcy5jYW52YXNlcy5sZW5ndGg7IGk8bDsgaSsrKSB7XG4gICAgICAgIHRoaXMuY2FudmFzZXNbaV0udHJhbnNsYXRlKHgsIHksIGRpc2FibGVQbG90KTtcbiAgICAgIH1cbiAgICB9LFxuICAgIC8qXG4gICAgICBNZXRob2Q6IHNjYWxlXG4gICAgICBcbiAgICAgIFNjYWxlcyB0aGUgY2FudmFzLlxuICAgICAgXG4gICAgICBQYXJhbWV0ZXJzOlxuICAgICAgXG4gICAgICB4IC0gKG51bWJlcikgc2NhbGUgdmFsdWUuXG4gICAgICB5IC0gKG51bWJlcikgc2NhbGUgdmFsdWUuXG4gICAgICBkaXNhYmxlUGxvdCAtIChib29sZWFuKSBEZWZhdWx0J3MgKmZhbHNlKi4gU2V0IHRoaXMgdG8gKnRydWUqIGlmIHlvdSBkb24ndCB3YW50IHRvIHJlZnJlc2ggdGhlIHZpc3VhbGl6YXRpb24uXG4gICAgICBcbiAgICAgIEV4YW1wbGU6XG4gICAgICBcbiAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgIGNhbnZhcy5zY2FsZSgwLjUsIDAuNSk7XG4gICAgICAoZW5kIGNvZGUpXG4gICAgXG4gICAgKi9cbiAgICBzY2FsZTogZnVuY3Rpb24oeCwgeSwgZGlzYWJsZVBsb3QpIHtcbiAgICAgIHZhciBweCA9IHRoaXMuc2NhbGVPZmZzZXRYICogeCxcbiAgICAgICAgICBweSA9IHRoaXMuc2NhbGVPZmZzZXRZICogeTtcbiAgICAgIHZhciBkeCA9IHRoaXMudHJhbnNsYXRlT2Zmc2V0WCAqICh4IC0xKSAvIHB4LFxuICAgICAgICAgIGR5ID0gdGhpcy50cmFuc2xhdGVPZmZzZXRZICogKHkgLTEpIC8gcHk7XG4gICAgICB0aGlzLnNjYWxlT2Zmc2V0WCA9IHB4O1xuICAgICAgdGhpcy5zY2FsZU9mZnNldFkgPSBweTtcbiAgICAgIGZvcih2YXIgaT0wLCBsPXRoaXMuY2FudmFzZXMubGVuZ3RoOyBpPGw7IGkrKykge1xuICAgICAgICB0aGlzLmNhbnZhc2VzW2ldLnNjYWxlKHgsIHksIHRydWUpO1xuICAgICAgfVxuICAgICAgdGhpcy50cmFuc2xhdGUoZHgsIGR5LCBmYWxzZSk7XG4gICAgfSxcbiAgICAvKlxuICAgICAgTWV0aG9kOiBnZXRQb3NcbiAgICAgIFxuICAgICAgUmV0dXJucyB0aGUgY2FudmFzIHBvc2l0aW9uIGFzIGFuICp4LCB5KiBvYmplY3QuXG4gICAgICBcbiAgICAgIFBhcmFtZXRlcnM6XG4gICAgICBcbiAgICAgIGZvcmNlIC0gKGJvb2xlYW4pIERlZmF1bHQncyAqZmFsc2UqLiBTZXQgdGhpcyB0byAqdHJ1ZSogaWYgeW91IHdhbnQgdG8gcmVjYWxjdWxhdGUgdGhlIHBvc2l0aW9uIHdpdGhvdXQgdXNpbmcgYW55IGNhY2hlIGluZm9ybWF0aW9uLlxuICAgICAgXG4gICAgICBSZXR1cm5zOlxuICAgICAgXG4gICAgICBBbiBvYmplY3Qgd2l0aCAqeCogYW5kICp5KiBwcm9wZXJ0aWVzLlxuICAgICAgXG4gICAgICBFeGFtcGxlOlxuICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICBjYW52YXMuZ2V0UG9zKHRydWUpOyAvL3JldHVybnMgeyB4OiA5MDAsIHk6IDUwMCB9XG4gICAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBnZXRQb3M6IGZ1bmN0aW9uKGZvcmNlKXtcbiAgICAgIGlmKGZvcmNlIHx8ICF0aGlzLnBvcykge1xuICAgICAgICByZXR1cm4gdGhpcy5wb3MgPSAkLmdldFBvcyh0aGlzLmdldEVsZW1lbnQoKSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wb3M7XG4gICAgfSxcbiAgICAvKlxuICAgICAgIE1ldGhvZDogY2xlYXJcbiAgICAgICBcbiAgICAgICBDbGVhcnMgdGhlIGNhbnZhcy5cbiAgICAqL1xuICAgIGNsZWFyOiBmdW5jdGlvbihpKXtcbiAgICAgIHRoaXMuY2FudmFzZXNbaXx8MF0uY2xlYXIoKTtcbiAgICB9LFxuICAgIFxuICAgIHBhdGg6IGZ1bmN0aW9uKHR5cGUsIGFjdGlvbil7XG4gICAgICB2YXIgY3R4ID0gdGhpcy5jYW52YXNlc1swXS5nZXRDdHgoKTtcbiAgICAgIGN0eC5iZWdpblBhdGgoKTtcbiAgICAgIGFjdGlvbihjdHgpO1xuICAgICAgY3R4W3R5cGVdKCk7XG4gICAgICBjdHguY2xvc2VQYXRoKCk7XG4gICAgfSxcbiAgICBcbiAgICBjcmVhdGVMYWJlbENvbnRhaW5lcjogZnVuY3Rpb24odHlwZSwgaWRMYWJlbCwgZGltKSB7XG4gICAgICB2YXIgTlMgPSAnaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnO1xuICAgICAgaWYodHlwZSA9PSAnSFRNTCcgfHwgdHlwZSA9PSAnTmF0aXZlJykge1xuICAgICAgICByZXR1cm4gJEUoJ2RpdicsIHtcbiAgICAgICAgICAnaWQnOiBpZExhYmVsLFxuICAgICAgICAgICdzdHlsZSc6IHtcbiAgICAgICAgICAgICdvdmVyZmxvdyc6ICd2aXNpYmxlJyxcbiAgICAgICAgICAgICdwb3NpdGlvbic6ICdhYnNvbHV0ZScsXG4gICAgICAgICAgICAndG9wJzogMCxcbiAgICAgICAgICAgICdsZWZ0JzogMCxcbiAgICAgICAgICAgICd3aWR0aCc6IGRpbS53aWR0aCArICdweCcsXG4gICAgICAgICAgICAnaGVpZ2h0JzogMFxuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYodHlwZSA9PSAnU1ZHJykge1xuICAgICAgICB2YXIgc3ZnQ29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKE5TLCAnc3ZnOnN2ZycpO1xuICAgICAgICBzdmdDb250YWluZXIuc2V0QXR0cmlidXRlKFwid2lkdGhcIiwgZGltLndpZHRoKTtcbiAgICAgICAgc3ZnQ29udGFpbmVyLnNldEF0dHJpYnV0ZSgnaGVpZ2h0JywgZGltLmhlaWdodCk7XG4gICAgICAgIHZhciBzdHlsZSA9IHN2Z0NvbnRhaW5lci5zdHlsZTtcbiAgICAgICAgc3R5bGUucG9zaXRpb24gPSAnYWJzb2x1dGUnO1xuICAgICAgICBzdHlsZS5sZWZ0ID0gc3R5bGUudG9wID0gJzBweCc7XG4gICAgICAgIHZhciBsYWJlbENvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyhOUywgJ3N2ZzpnJyk7XG4gICAgICAgIGxhYmVsQ29udGFpbmVyLnNldEF0dHJpYnV0ZSgnd2lkdGgnLCBkaW0ud2lkdGgpO1xuICAgICAgICBsYWJlbENvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ2hlaWdodCcsIGRpbS5oZWlnaHQpO1xuICAgICAgICBsYWJlbENvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ3gnLCAwKTtcbiAgICAgICAgbGFiZWxDb250YWluZXIuc2V0QXR0cmlidXRlKCd5JywgMCk7XG4gICAgICAgIGxhYmVsQ29udGFpbmVyLnNldEF0dHJpYnV0ZSgnaWQnLCBpZExhYmVsKTtcbiAgICAgICAgc3ZnQ29udGFpbmVyLmFwcGVuZENoaWxkKGxhYmVsQ29udGFpbmVyKTtcbiAgICAgICAgcmV0dXJuIHN2Z0NvbnRhaW5lcjtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuICAvL2Jhc2UgY2FudmFzIHdyYXBwZXJcbiAgQ2FudmFzLkJhc2UgPSB7fTtcbiAgQ2FudmFzLkJhc2VbJzJEJ10gPSBuZXcgQ2xhc3Moe1xuICAgIHRyYW5zbGF0ZU9mZnNldFg6IDAsXG4gICAgdHJhbnNsYXRlT2Zmc2V0WTogMCxcbiAgICBzY2FsZU9mZnNldFg6IDEsXG4gICAgc2NhbGVPZmZzZXRZOiAxLFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24odml6KSB7XG4gICAgICB0aGlzLnZpeiA9IHZpejtcbiAgICAgIHRoaXMub3B0ID0gdml6LmNvbmZpZztcbiAgICAgIHRoaXMuc2l6ZSA9IGZhbHNlO1xuICAgICAgdGhpcy5jcmVhdGVDYW52YXMoKTtcbiAgICAgIHRoaXMudHJhbnNsYXRlVG9DZW50ZXIoKTtcbiAgICB9LFxuICAgIGNyZWF0ZUNhbnZhczogZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgb3B0ID0gdGhpcy5vcHQsXG4gICAgICAgICAgd2lkdGggPSBvcHQud2lkdGgsXG4gICAgICAgICAgaGVpZ2h0ID0gb3B0LmhlaWdodDtcbiAgICAgIHRoaXMuY2FudmFzID0gJEUoJ2NhbnZhcycsIHtcbiAgICAgICAgJ2lkJzogb3B0LmluamVjdEludG8gKyBvcHQuaWRTdWZmaXgsXG4gICAgICAgICd3aWR0aCc6IHdpZHRoLFxuICAgICAgICAnaGVpZ2h0JzogaGVpZ2h0LFxuICAgICAgICAnc3R5bGUnOiB7XG4gICAgICAgICAgJ3Bvc2l0aW9uJzogJ2Fic29sdXRlJyxcbiAgICAgICAgICAndG9wJzogMCxcbiAgICAgICAgICAnbGVmdCc6IDAsXG4gICAgICAgICAgJ3dpZHRoJzogd2lkdGggKyAncHgnLFxuICAgICAgICAgICdoZWlnaHQnOiBoZWlnaHQgKyAncHgnXG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0sXG4gICAgZ2V0Q3R4OiBmdW5jdGlvbigpIHtcbiAgICAgIGlmKCF0aGlzLmN0eCkgXG4gICAgICAgIHJldHVybiB0aGlzLmN0eCA9IHRoaXMuY2FudmFzLmdldENvbnRleHQoJzJkJyk7XG4gICAgICByZXR1cm4gdGhpcy5jdHg7XG4gICAgfSxcbiAgICBnZXRTaXplOiBmdW5jdGlvbigpIHtcbiAgICAgIGlmKHRoaXMuc2l6ZSkgcmV0dXJuIHRoaXMuc2l6ZTtcbiAgICAgIHZhciBjYW52YXMgPSB0aGlzLmNhbnZhcztcbiAgICAgIHJldHVybiB0aGlzLnNpemUgPSB7XG4gICAgICAgIHdpZHRoOiBjYW52YXMud2lkdGgsXG4gICAgICAgIGhlaWdodDogY2FudmFzLmhlaWdodFxuICAgICAgfTtcbiAgICB9LFxuICAgIHRyYW5zbGF0ZVRvQ2VudGVyOiBmdW5jdGlvbihwcykge1xuICAgICAgLy8gU1RBUlQgTUVUQU1BUFMgQ09ERVxuICAgICAgdmFyIHNpemUgPSB0aGlzLmdldFNpemUoKTtcbiAgICAgIHZhciB3aWR0aCA9IHBzID8gKHNpemUud2lkdGggLSBwcy53aWR0aCAtIHRoaXMudHJhbnNsYXRlT2Zmc2V0WCoyKSA6IHNpemUud2lkdGg7XG4gICAgICB2YXIgaGVpZ2h0ID0gcHMgPyAoc2l6ZS5oZWlnaHQgLSBwcy5oZWlnaHQgLSB0aGlzLnRyYW5zbGF0ZU9mZnNldFkqMikgOiBzaXplLmhlaWdodDtcbiAgICAgIC8vIE9SSUdJTkFMIENPREVcbiAgICAgIC8vIHZhciBzaXplID0gdGhpcy5nZXRTaXplKCksXG4gICAgICAvLyAgICAgd2lkdGggPSBwcz8gKHNpemUud2lkdGggLSBwcy53aWR0aCAtIHRoaXMudHJhbnNsYXRlT2Zmc2V0WCoyKSA6IHNpemUud2lkdGg7XG4gICAgICAvLyAgICAgaGVpZ2h0ID0gcHM/IChzaXplLmhlaWdodCAtIHBzLmhlaWdodCAtIHRoaXMudHJhbnNsYXRlT2Zmc2V0WSoyKSA6IHNpemUuaGVpZ2h0O1xuICAgICAgLy8gRU5EIE1FVEFNQVBTIENPREVcbiAgICAgIHZhciBjdHggPSB0aGlzLmdldEN0eCgpO1xuICAgICAgcHMgJiYgY3R4LnNjYWxlKDEvdGhpcy5zY2FsZU9mZnNldFgsIDEvdGhpcy5zY2FsZU9mZnNldFkpO1xuICAgICAgY3R4LnRyYW5zbGF0ZSh3aWR0aC8yLCBoZWlnaHQvMik7XG4gICAgfSxcbiAgICByZXNpemU6IGZ1bmN0aW9uKHdpZHRoLCBoZWlnaHQpIHtcbiAgICAgIHZhciBzaXplID0gdGhpcy5nZXRTaXplKCksXG4gICAgICAgICAgY2FudmFzID0gdGhpcy5jYW52YXMsXG4gICAgICAgICAgc3R5bGVzID0gY2FudmFzLnN0eWxlO1xuICAgICAgdGhpcy5zaXplID0gZmFsc2U7XG4gICAgICBjYW52YXMud2lkdGggPSB3aWR0aDtcbiAgICAgIGNhbnZhcy5oZWlnaHQgPSBoZWlnaHQ7XG4gICAgICBzdHlsZXMud2lkdGggPSB3aWR0aCArIFwicHhcIjtcbiAgICAgIHN0eWxlcy5oZWlnaHQgPSBoZWlnaHQgKyBcInB4XCI7XG5cbiAgICAgIC8vc21hbGwgRXhDYW52YXMgZml4XG4gICAgICBpZighc3VwcG9ydHNDYW52YXMpIHtcbiAgICAgICAgdGhpcy50cmFuc2xhdGVUb0NlbnRlcihzaXplKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMudHJhbnNsYXRlVG9DZW50ZXIoKTtcbiAgICAgIH1cbiAgICAgIHRoaXMudHJhbnNsYXRlT2Zmc2V0WCA9XG4gICAgICAgIHRoaXMudHJhbnNsYXRlT2Zmc2V0WSA9IDA7XG4gICAgICB0aGlzLnNjYWxlT2Zmc2V0WCA9IFxuICAgICAgICB0aGlzLnNjYWxlT2Zmc2V0WSA9IDE7XG5cbiAgICAgIHRoaXMuY2xlYXIoKTtcbiAgICAgIHRoaXMudml6LnJlc2l6ZSh3aWR0aCwgaGVpZ2h0LCB0aGlzKTtcbiAgICB9LFxuICAgIHRyYW5zbGF0ZTogZnVuY3Rpb24oeCwgeSwgZGlzYWJsZVBsb3QpIHtcbiAgICAgIHZhciBzeCA9IHRoaXMuc2NhbGVPZmZzZXRYLFxuICAgICAgICAgIHN5ID0gdGhpcy5zY2FsZU9mZnNldFk7XG4gICAgICB0aGlzLnRyYW5zbGF0ZU9mZnNldFggKz0geCpzeDtcbiAgICAgIHRoaXMudHJhbnNsYXRlT2Zmc2V0WSArPSB5KnN5O1xuICAgICAgdGhpcy5nZXRDdHgoKS50cmFuc2xhdGUoeCwgeSk7XG4gICAgICAhZGlzYWJsZVBsb3QgJiYgdGhpcy5wbG90KCk7XG4gICAgfSxcbiAgICBzY2FsZTogZnVuY3Rpb24oeCwgeSwgZGlzYWJsZVBsb3QpIHtcbiAgICAgIHRoaXMuc2NhbGVPZmZzZXRYICo9IHg7XG4gICAgICB0aGlzLnNjYWxlT2Zmc2V0WSAqPSB5O1xuICAgICAgdGhpcy5nZXRDdHgoKS5zY2FsZSh4LCB5KTtcbiAgICAgICFkaXNhYmxlUGxvdCAmJiB0aGlzLnBsb3QoKTtcbiAgICB9LFxuICAgIGNsZWFyOiBmdW5jdGlvbigpe1xuICAgICAgdmFyIHNpemUgPSB0aGlzLmdldFNpemUoKSxcbiAgICAgICAgICBveCA9IHRoaXMudHJhbnNsYXRlT2Zmc2V0WCxcbiAgICAgICAgICBveSA9IHRoaXMudHJhbnNsYXRlT2Zmc2V0WSxcbiAgICAgICAgICBzeCA9IHRoaXMuc2NhbGVPZmZzZXRYLFxuICAgICAgICAgIHN5ID0gdGhpcy5zY2FsZU9mZnNldFk7XG4gICAgICB0aGlzLmdldEN0eCgpLmNsZWFyUmVjdCgoLXNpemUud2lkdGggLyAyIC0gb3gpICogMS9zeCwgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoLXNpemUuaGVpZ2h0IC8gMiAtIG95KSAqIDEvc3ksIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZS53aWR0aCAqIDEvc3gsIHNpemUuaGVpZ2h0ICogMS9zeSk7XG4gICAgfSxcbiAgICBwbG90OiBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMuY2xlYXIoKTtcbiAgICAgIHRoaXMudml6LnBsb3QodGhpcyk7XG4gICAgfVxuICB9KTtcbiAgLy9iYWNrZ3JvdW5kIGNhbnZhc2VzXG4gIC8vVE9ETyhuaWNvKTogZG9jdW1lbnQgdGhpcyFcbiAgQ2FudmFzLkJhY2tncm91bmQgPSB7fTtcbiAgQ2FudmFzLkJhY2tncm91bmQuQ2lyY2xlcyA9IG5ldyBDbGFzcyh7XG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24odml6LCBvcHRpb25zKSB7XG4gICAgICB0aGlzLnZpeiA9IHZpejtcbiAgICAgIHRoaXMuY29uZmlnID0gJC5tZXJnZSh7XG4gICAgICAgIGlkU3VmZml4OiAnLWJrY2FudmFzJyxcbiAgICAgICAgbGV2ZWxEaXN0YW5jZTogMTAwLFxuICAgICAgICBudW1iZXJPZkNpcmNsZXM6IDYsXG4gICAgICAgIENhbnZhc1N0eWxlczoge30sXG4gICAgICAgIG9mZnNldDogMFxuICAgICAgfSwgb3B0aW9ucyk7XG4gICAgfSxcbiAgICByZXNpemU6IGZ1bmN0aW9uKHdpZHRoLCBoZWlnaHQsIGJhc2UpIHtcbiAgICAgIHRoaXMucGxvdChiYXNlKTtcbiAgICB9LFxuICAgIHBsb3Q6IGZ1bmN0aW9uKGJhc2UpIHtcbiAgICAgIHZhciBjYW52YXMgPSBiYXNlLmNhbnZhcyxcbiAgICAgICAgICBjdHggPSBiYXNlLmdldEN0eCgpLFxuICAgICAgICAgIGNvbmYgPSB0aGlzLmNvbmZpZyxcbiAgICAgICAgICBzdHlsZXMgPSBjb25mLkNhbnZhc1N0eWxlcztcbiAgICAgIC8vc2V0IGNhbnZhcyBzdHlsZXNcbiAgICAgIGZvcih2YXIgcyBpbiBzdHlsZXMpIGN0eFtzXSA9IHN0eWxlc1tzXTtcbiAgICAgIHZhciBuID0gY29uZi5udW1iZXJPZkNpcmNsZXMsXG4gICAgICAgICAgcmhvID0gY29uZi5sZXZlbERpc3RhbmNlO1xuICAgICAgZm9yKHZhciBpPTE7IGk8PW47IGkrKykge1xuICAgICAgICBjdHguYmVnaW5QYXRoKCk7XG4gICAgICAgIGN0eC5hcmMoMCwgMCwgcmhvICogaSwgMCwgMiAqIE1hdGguUEksIGZhbHNlKTtcbiAgICAgICAgY3R4LnN0cm9rZSgpO1xuICAgICAgICBjdHguY2xvc2VQYXRoKCk7XG4gICAgICB9XG4gICAgICAvL1RPRE8obmljbyk6IHByaW50IGxhYmVscyB0b28hXG4gICAgfVxuICB9KTtcbiAgXG4gIC8vIFNUQVJUIE1FVEFNQVBTIENPREVcbiAgQ2FudmFzLkJhY2tncm91bmQuTWV0YW1hcHMgPSBuZXcgQ2xhc3Moe1xuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKHZpeiwgb3B0aW9ucykge1xuICAgICAgdGhpcy52aXogPSB2aXo7XG4gICAgICB0aGlzLmNvbmZpZyA9IG9wdGlvbnM7XG4gICAgfSxcbiAgICByZXNpemU6IGZ1bmN0aW9uKHdpZHRoLCBoZWlnaHQsIGJhc2UpIHtcbiAgICAgIHRoaXMucGxvdChiYXNlKTtcbiAgICB9LFxuICAgIHBsb3Q6IGZ1bmN0aW9uKGJhc2UpIHtcbiAgICAgIHZhciBjYW52YXMgPSBiYXNlLmNhbnZhcyxcbiAgICAgICAgICBjdHggPSBiYXNlLmdldEN0eCgpLFxuICAgICAgICAgIHNjYWxlID0gYmFzZS5zY2FsZU9mZnNldFg7XG4gICAgICAvL3ZhciBwYXR0ZXJuID0gbmV3IEltYWdlKCk7XG4gICAgICAvL3BhdHRlcm4uc3JjID0gTWV0YW1hcHMuU2VydmVyRGF0YVsnY3ViZXMucG5nJ11cbiAgICAgIC8vdmFyIHB0cm4gPSBjdHguY3JlYXRlUGF0dGVybihwYXR0ZXJuLCAncmVwZWF0Jyk7XG4gICAgICAvL2N0eC5maWxsU3R5bGUgPSBwdHJuO1xuICAgICAgY3R4LmZpbGxTdHlsZSA9IE1ldGFtYXBzLlNldHRpbmdzLmNvbG9ycy5iYWNrZ3JvdW5kO1xuICAgICAgdmFyIHhQb2ludCA9ICgtKGNhbnZhcy53aWR0aC9zY2FsZSkvMikgLSAoYmFzZS50cmFuc2xhdGVPZmZzZXRYL3NjYWxlKSxcbiAgICAgICAgeVBvaW50ID0gKC0oY2FudmFzLmhlaWdodC9zY2FsZSkvMikgLSAoYmFzZS50cmFuc2xhdGVPZmZzZXRZL3NjYWxlKTtcbiAgICAgIC8vY3R4LmZpbGxSZWN0KHhQb2ludCx5UG9pbnQsY2FudmFzLndpZHRoL3NjYWxlLGNhbnZhcy5oZWlnaHQvc2NhbGUpO1xuICAgIH1cbiAgfSk7XG4gIC8vIEVORCBNRVRBTUFQUyBDT0RFXG59KSgpO1xuXG5cbi8qXG4gKiBGaWxlOiBQb2xhci5qc1xuICogXG4gKiBEZWZpbmVzIHRoZSA8UG9sYXI+IGNsYXNzLlxuICpcbiAqIERlc2NyaXB0aW9uOlxuICpcbiAqIFRoZSA8UG9sYXI+IGNsYXNzLCBqdXN0IGxpa2UgdGhlIDxDb21wbGV4PiBjbGFzcywgaXMgdXNlZCBieSB0aGUgPEh5cGVydHJlZT4sIDxTVD4gYW5kIDxSR3JhcGg+IGFzIGEgMkQgcG9pbnQgcmVwcmVzZW50YXRpb24uXG4gKlxuICogU2VlIGFsc286XG4gKlxuICogPGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUG9sYXJfY29vcmRpbmF0ZXM+XG4gKlxuKi9cblxuLypcbiAgIENsYXNzOiBQb2xhclxuXG4gICBBIG11bHRpIHB1cnBvc2UgcG9sYXIgcmVwcmVzZW50YXRpb24uXG5cbiAgIERlc2NyaXB0aW9uOlxuIFxuICAgVGhlIDxQb2xhcj4gY2xhc3MsIGp1c3QgbGlrZSB0aGUgPENvbXBsZXg+IGNsYXNzLCBpcyB1c2VkIGJ5IHRoZSA8SHlwZXJ0cmVlPiwgPFNUPiBhbmQgPFJHcmFwaD4gYXMgYSAyRCBwb2ludCByZXByZXNlbnRhdGlvbi5cbiBcbiAgIFNlZSBhbHNvOlxuIFxuICAgPGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUG9sYXJfY29vcmRpbmF0ZXM+XG4gXG4gICBQYXJhbWV0ZXJzOlxuXG4gICAgICB0aGV0YSAtIEFuIGFuZ2xlLlxuICAgICAgcmhvIC0gVGhlIG5vcm0uXG4qL1xuXG52YXIgUG9sYXIgPSBmdW5jdGlvbih0aGV0YSwgcmhvKSB7XG4gIHRoaXMudGhldGEgPSB0aGV0YSB8fCAwO1xuICB0aGlzLnJobyA9IHJobyB8fCAwO1xufTtcblxuJGppdC5Qb2xhciA9IFBvbGFyO1xuXG5Qb2xhci5wcm90b3R5cGUgPSB7XG4gICAgLypcbiAgICAgICBNZXRob2Q6IGdldGNcbiAgICBcbiAgICAgICBSZXR1cm5zIGEgY29tcGxleCBudW1iZXIuXG4gICAgXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIHNpbXBsZSAtIF9vcHRpb25hbF8gSWYgKnRydWUqLCB0aGlzIG1ldGhvZCB3aWxsIHJldHVybiBvbmx5IGFuIG9iamVjdCBob2xkaW5nIHggYW5kIHkgcHJvcGVydGllcyBhbmQgbm90IGEgPENvbXBsZXg+IGluc3RhbmNlLiBEZWZhdWx0J3MgKmZhbHNlKi5cblxuICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgICBBIGNvbXBsZXggbnVtYmVyLlxuICAgICovXG4gICAgZ2V0YzogZnVuY3Rpb24oc2ltcGxlKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnRvQ29tcGxleChzaW1wbGUpO1xuICAgIH0sXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogZ2V0cFxuICAgIFxuICAgICAgIFJldHVybnMgYSA8UG9sYXI+IHJlcHJlc2VudGF0aW9uLlxuICAgIFxuICAgICAgIFJldHVybnM6XG4gICAgXG4gICAgICAgICAgQSB2YXJpYWJsZSBpbiBwb2xhciBjb29yZGluYXRlcy5cbiAgICAqL1xuICAgIGdldHA6IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogc2V0XG4gICAgXG4gICAgICAgU2V0cyBhIG51bWJlci5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICB2IC0gQSA8Q29tcGxleD4gb3IgPFBvbGFyPiBpbnN0YW5jZS5cbiAgICBcbiAgICAqL1xuICAgIHNldDogZnVuY3Rpb24odikge1xuICAgICAgICB2ID0gdi5nZXRwKCk7XG4gICAgICAgIHRoaXMudGhldGEgPSB2LnRoZXRhOyB0aGlzLnJobyA9IHYucmhvO1xuICAgIH0sXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogc2V0Y1xuICAgIFxuICAgICAgIFNldHMgYSA8Q29tcGxleD4gbnVtYmVyLlxuXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIHggLSBBIDxDb21wbGV4PiBudW1iZXIgcmVhbCBwYXJ0LlxuICAgICAgIHkgLSBBIDxDb21wbGV4PiBudW1iZXIgaW1hZ2luYXJ5IHBhcnQuXG4gICAgXG4gICAgKi9cbiAgICBzZXRjOiBmdW5jdGlvbih4LCB5KSB7XG4gICAgICAgIHRoaXMucmhvID0gTWF0aC5zcXJ0KHggKiB4ICsgeSAqIHkpO1xuICAgICAgICB0aGlzLnRoZXRhID0gTWF0aC5hdGFuMih5LCB4KTtcbiAgICAgICAgaWYodGhpcy50aGV0YSA8IDApIHRoaXMudGhldGEgKz0gTWF0aC5QSSAqIDI7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBzZXRwXG4gICAgXG4gICAgICAgU2V0cyBhIHBvbGFyIG51bWJlci5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICB0aGV0YSAtIEEgPFBvbGFyPiBudW1iZXIgYW5nbGUgcHJvcGVydHkuXG4gICAgICAgcmhvIC0gQSA8UG9sYXI+IG51bWJlciByaG8gcHJvcGVydHkuXG4gICAgXG4gICAgKi9cbiAgICBzZXRwOiBmdW5jdGlvbih0aGV0YSwgcmhvKSB7XG4gICAgICAgIHRoaXMudGhldGEgPSB0aGV0YTsgXG4gICAgICAgIHRoaXMucmhvID0gcmhvO1xuICAgIH0sXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogY2xvbmVcbiAgICBcbiAgICAgICBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBvYmplY3QuXG4gICAgXG4gICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgICBBIGNvcHkgb2YgdGhlIHJlYWwgb2JqZWN0LlxuICAgICovXG4gICAgY2xvbmU6IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gbmV3IFBvbGFyKHRoaXMudGhldGEsIHRoaXMucmhvKTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHRvQ29tcGxleFxuICAgIFxuICAgICAgICBUcmFuc2xhdGVzIGZyb20gcG9sYXIgdG8gY2FydGVzaWFuIGNvb3JkaW5hdGVzIGFuZCByZXR1cm5zIGEgbmV3IDxDb21wbGV4PiBpbnN0YW5jZS5cbiAgICBcbiAgICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgICBzaW1wbGUgLSBfb3B0aW9uYWxfIElmICp0cnVlKiB0aGlzIG1ldGhvZCB3aWxsIG9ubHkgcmV0dXJuIGFuIG9iamVjdCB3aXRoIHggYW5kIHkgcHJvcGVydGllcyAoYW5kIG5vdCB0aGUgd2hvbGUgPENvbXBsZXg+IGluc3RhbmNlKS4gRGVmYXVsdCdzICpmYWxzZSouXG4gXG4gICAgICAgIFJldHVybnM6XG4gICAgXG4gICAgICAgICAgQSBuZXcgPENvbXBsZXg+IGluc3RhbmNlLlxuICAgICovXG4gICAgdG9Db21wbGV4OiBmdW5jdGlvbihzaW1wbGUpIHtcbiAgICAgICAgdmFyIHggPSBNYXRoLmNvcyh0aGlzLnRoZXRhKSAqIHRoaXMucmhvO1xuICAgICAgICB2YXIgeSA9IE1hdGguc2luKHRoaXMudGhldGEpICogdGhpcy5yaG87XG4gICAgICAgIGlmKHNpbXBsZSkgcmV0dXJuIHsgJ3gnOiB4LCAneSc6IHl9O1xuICAgICAgICByZXR1cm4gbmV3IENvbXBsZXgoeCwgeSk7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBhZGRcbiAgICBcbiAgICAgICAgQWRkcyB0d28gPFBvbGFyPiBpbnN0YW5jZXMuXG4gICAgXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIHBvbGFyIC0gQSA8UG9sYXI+IG51bWJlci5cblxuICAgICAgIFJldHVybnM6XG4gICAgXG4gICAgICAgICAgQSBuZXcgUG9sYXIgaW5zdGFuY2UuXG4gICAgKi9cbiAgICBhZGQ6IGZ1bmN0aW9uKHBvbGFyKSB7XG4gICAgICAgIHJldHVybiBuZXcgUG9sYXIodGhpcy50aGV0YSArIHBvbGFyLnRoZXRhLCB0aGlzLnJobyArIHBvbGFyLnJobyk7XG4gICAgfSxcbiAgICBcbiAgICAvKlxuICAgICAgIE1ldGhvZDogc2NhbGVcbiAgICBcbiAgICAgICAgU2NhbGVzIGEgcG9sYXIgbm9ybS5cbiAgICBcbiAgICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgICBudW1iZXIgLSBBIHNjYWxlIGZhY3Rvci5cbiAgICAgICAgXG4gICAgICAgIFJldHVybnM6XG4gICAgXG4gICAgICAgICAgQSBuZXcgUG9sYXIgaW5zdGFuY2UuXG4gICAgKi9cbiAgICBzY2FsZTogZnVuY3Rpb24obnVtYmVyKSB7XG4gICAgICAgIHJldHVybiBuZXcgUG9sYXIodGhpcy50aGV0YSwgdGhpcy5yaG8gKiBudW1iZXIpO1xuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6IGVxdWFsc1xuICAgIFxuICAgICAgIENvbXBhcmlzb24gbWV0aG9kLlxuXG4gICAgICAgUmV0dXJucyAqdHJ1ZSogaWYgdGhlIHRoZXRhIGFuZCByaG8gcHJvcGVydGllcyBhcmUgZXF1YWwuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgYyAtIEEgPFBvbGFyPiBudW1iZXIuXG5cbiAgICAgICBSZXR1cm5zOlxuXG4gICAgICAgKnRydWUqIGlmIHRoZSB0aGV0YSBhbmQgcmhvIHBhcmFtZXRlcnMgZm9yIHRoZXNlIG9iamVjdHMgYXJlIGVxdWFsLiAqZmFsc2UqIG90aGVyd2lzZS5cbiAgICAqL1xuICAgIGVxdWFsczogZnVuY3Rpb24oYykge1xuICAgICAgICByZXR1cm4gdGhpcy50aGV0YSA9PSBjLnRoZXRhICYmIHRoaXMucmhvID09IGMucmhvO1xuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6ICRhZGRcbiAgICBcbiAgICAgICAgQWRkcyB0d28gPFBvbGFyPiBpbnN0YW5jZXMgYWZmZWN0aW5nIHRoZSBjdXJyZW50IG9iamVjdC5cbiAgICBcbiAgICAgICBQYXJhbXRlcnM6XG5cbiAgICAgICBwb2xhciAtIEEgPFBvbGFyPiBpbnN0YW5jZS5cblxuICAgICAgIFJldHVybnM6XG4gICAgXG4gICAgICAgICAgVGhlIGNoYW5nZWQgb2JqZWN0LlxuICAgICovXG4gICAgJGFkZDogZnVuY3Rpb24ocG9sYXIpIHtcbiAgICAgICAgdGhpcy50aGV0YSA9IHRoaXMudGhldGEgKyBwb2xhci50aGV0YTsgdGhpcy5yaG8gKz0gcG9sYXIucmhvO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6ICRtYWRkXG4gICAgXG4gICAgICAgIEFkZHMgdHdvIDxQb2xhcj4gaW5zdGFuY2VzIGFmZmVjdGluZyB0aGUgY3VycmVudCBvYmplY3QuIFRoZSByZXN1bHRpbmcgdGhldGEgYW5nbGUgaXMgbW9kdWxvIDJwaS5cbiAgICBcbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgcG9sYXIgLSBBIDxQb2xhcj4gaW5zdGFuY2UuXG5cbiAgICAgICBSZXR1cm5zOlxuICAgIFxuICAgICAgICAgIFRoZSBjaGFuZ2VkIG9iamVjdC5cbiAgICAqL1xuICAgICRtYWRkOiBmdW5jdGlvbihwb2xhcikge1xuICAgICAgICB0aGlzLnRoZXRhID0gKHRoaXMudGhldGEgKyBwb2xhci50aGV0YSkgJSAoTWF0aC5QSSAqIDIpOyB0aGlzLnJobyArPSBwb2xhci5yaG87XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBcbiAgICAvKlxuICAgICAgIE1ldGhvZDogJHNjYWxlXG4gICAgXG4gICAgICAgIFNjYWxlcyBhIHBvbGFyIGluc3RhbmNlIGFmZmVjdGluZyB0aGUgb2JqZWN0LlxuICAgIFxuICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgbnVtYmVyIC0gQSBzY2FsaW5nIGZhY3Rvci5cblxuICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgICBUaGUgY2hhbmdlZCBvYmplY3QuXG4gICAgKi9cbiAgICAkc2NhbGU6IGZ1bmN0aW9uKG51bWJlcikge1xuICAgICAgICB0aGlzLnJobyAqPSBudW1iZXI7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgIE1ldGhvZDogaXNaZXJvXG4gICBcbiAgICAgIFJldHVybnMgKnRydWUqIGlmIHRoZSBudW1iZXIgaXMgemVyby5cbiAgIFxuICAgKi9cbiAgICBpc1plcm86IGZ1bmN0aW9uICgpIHtcbiAgICAgIHZhciBhbG1vc3RaZXJvID0gMC4wMDAxLCBhYnMgPSBNYXRoLmFicztcbiAgICAgIHJldHVybiBhYnModGhpcy50aGV0YSkgPCBhbG1vc3RaZXJvICYmIGFicyh0aGlzLnJobykgPCBhbG1vc3RaZXJvO1xuICAgIH0sXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogaW50ZXJwb2xhdGVcbiAgICBcbiAgICAgICAgQ2FsY3VsYXRlcyBhIHBvbGFyIGludGVycG9sYXRpb24gYmV0d2VlbiB0d28gcG9pbnRzIGF0IGEgZ2l2ZW4gZGVsdGEgbW9tZW50LlxuXG4gICAgICAgIFBhcmFtZXRlcnM6XG4gICAgICBcbiAgICAgICAgZWxlbSAtIEEgPFBvbGFyPiBpbnN0YW5jZS5cbiAgICAgICAgZGVsdGEgLSBBIGRlbHRhIGZhY3RvciByYW5naW5nIFswLCAxXS5cbiAgICBcbiAgICAgICBSZXR1cm5zOlxuICAgIFxuICAgICAgICAgIEEgbmV3IDxQb2xhcj4gaW5zdGFuY2UgcmVwcmVzZW50aW5nIGFuIGludGVycG9sYXRpb24gYmV0d2VlbiBfdGhpc18gYW5kIF9lbGVtX1xuICAgICovXG4gICAgaW50ZXJwb2xhdGU6IGZ1bmN0aW9uKGVsZW0sIGRlbHRhKSB7XG4gICAgICAgIHZhciBwaSA9IE1hdGguUEksIHBpMiA9IHBpICogMjtcbiAgICAgICAgdmFyIGNoID0gZnVuY3Rpb24odCkge1xuICAgICAgICAgICAgdmFyIGEgPSAgKHQgPCAwKT8gKHQgJSBwaTIpICsgcGkyIDogdCAlIHBpMjtcbiAgICAgICAgICAgIHJldHVybiBhO1xuICAgICAgICB9O1xuICAgICAgICB2YXIgdHQgPSB0aGlzLnRoZXRhLCBldCA9IGVsZW0udGhldGE7XG4gICAgICAgIHZhciBzdW0sIGRpZmYgPSBNYXRoLmFicyh0dCAtIGV0KTtcbiAgICAgICAgaWYoZGlmZiA9PSBwaSkge1xuICAgICAgICAgIGlmKHR0ID4gZXQpIHtcbiAgICAgICAgICAgIHN1bSA9IGNoKChldCArICgodHQgLSBwaTIpIC0gZXQpICogZGVsdGEpKSA7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHN1bSA9IGNoKChldCAtIHBpMiArICh0dCAtIChldCkpICogZGVsdGEpKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZihkaWZmID49IHBpKSB7XG4gICAgICAgICAgaWYodHQgPiBldCkge1xuICAgICAgICAgICAgc3VtID0gY2goKGV0ICsgKCh0dCAtIHBpMikgLSBldCkgKiBkZWx0YSkpIDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgc3VtID0gY2goKGV0IC0gcGkyICsgKHR0IC0gKGV0IC0gcGkyKSkgKiBkZWx0YSkpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHsgIFxuICAgICAgICAgIHN1bSA9IGNoKChldCArICh0dCAtIGV0KSAqIGRlbHRhKSkgO1xuICAgICAgICB9XG4gICAgICAgIHZhciByID0gKHRoaXMucmhvIC0gZWxlbS5yaG8pICogZGVsdGEgKyBlbGVtLnJobztcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAndGhldGEnOiBzdW0sXG4gICAgICAgICAgJ3Jobyc6IHJcbiAgICAgICAgfTtcbiAgICB9XG59O1xuXG5cbnZhciAkUCA9IGZ1bmN0aW9uKGEsIGIpIHsgcmV0dXJuIG5ldyBQb2xhcihhLCBiKTsgfTtcblxuUG9sYXIuS0VSID0gJFAoMCwgMCk7XG5cblxuXG4vKlxuICogRmlsZTogQ29tcGxleC5qc1xuICogXG4gKiBEZWZpbmVzIHRoZSA8Q29tcGxleD4gY2xhc3MuXG4gKlxuICogRGVzY3JpcHRpb246XG4gKlxuICogVGhlIDxDb21wbGV4PiBjbGFzcywganVzdCBsaWtlIHRoZSA8UG9sYXI+IGNsYXNzLCBpcyB1c2VkIGJ5IHRoZSA8SHlwZXJ0cmVlPiwgPFNUPiBhbmQgPFJHcmFwaD4gYXMgYSAyRCBwb2ludCByZXByZXNlbnRhdGlvbi5cbiAqXG4gKiBTZWUgYWxzbzpcbiAqXG4gKiA8aHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db21wbGV4X251bWJlcj5cbiAqXG4qL1xuXG4vKlxuICAgQ2xhc3M6IENvbXBsZXhcbiAgICBcbiAgIEEgbXVsdGktcHVycG9zZSBDb21wbGV4IENsYXNzIHdpdGggY29tbW9uIG1ldGhvZHMuXG4gXG4gICBEZXNjcmlwdGlvbjpcbiBcbiAgIFRoZSA8Q29tcGxleD4gY2xhc3MsIGp1c3QgbGlrZSB0aGUgPFBvbGFyPiBjbGFzcywgaXMgdXNlZCBieSB0aGUgPEh5cGVydHJlZT4sIDxTVD4gYW5kIDxSR3JhcGg+IGFzIGEgMkQgcG9pbnQgcmVwcmVzZW50YXRpb24uXG4gXG4gICBTZWUgYWxzbzpcbiBcbiAgIDxodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvbXBsZXhfbnVtYmVyPlxuXG4gICBQYXJhbWV0ZXJzOlxuXG4gICB4IC0gX29wdGlvbmFsXyBBIENvbXBsZXggbnVtYmVyIHJlYWwgcGFydC5cbiAgIHkgLSBfb3B0aW9uYWxfIEEgQ29tcGxleCBudW1iZXIgaW1hZ2luYXJ5IHBhcnQuXG4gXG4qL1xuXG52YXIgQ29tcGxleCA9IGZ1bmN0aW9uKHgsIHkpIHtcbiAgdGhpcy54ID0geCB8fCAwO1xuICB0aGlzLnkgPSB5IHx8IDA7XG59O1xuXG4kaml0LkNvbXBsZXggPSBDb21wbGV4O1xuXG5Db21wbGV4LnByb3RvdHlwZSA9IHtcbiAgICAvKlxuICAgICAgIE1ldGhvZDogZ2V0Y1xuICAgIFxuICAgICAgIFJldHVybnMgYSBjb21wbGV4IG51bWJlci5cbiAgICBcbiAgICAgICBSZXR1cm5zOlxuICAgIFxuICAgICAgICAgIEEgY29tcGxleCBudW1iZXIuXG4gICAgKi9cbiAgICBnZXRjOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBnZXRwXG4gICAgXG4gICAgICAgUmV0dXJucyBhIDxQb2xhcj4gcmVwcmVzZW50YXRpb24gb2YgdGhpcyBudW1iZXIuXG4gICAgXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIHNpbXBsZSAtIF9vcHRpb25hbF8gSWYgKnRydWUqLCB0aGlzIG1ldGhvZCB3aWxsIHJldHVybiBvbmx5IGFuIG9iamVjdCBob2xkaW5nIHRoZXRhIGFuZCByaG8gcHJvcGVydGllcyBhbmQgbm90IGEgPFBvbGFyPiBpbnN0YW5jZS4gRGVmYXVsdCdzICpmYWxzZSouXG5cbiAgICAgICBSZXR1cm5zOlxuICAgIFxuICAgICAgICAgIEEgdmFyaWFibGUgaW4gPFBvbGFyPiBjb29yZGluYXRlcy5cbiAgICAqL1xuICAgIGdldHA6IGZ1bmN0aW9uKHNpbXBsZSkge1xuICAgICAgICByZXR1cm4gdGhpcy50b1BvbGFyKHNpbXBsZSk7XG4gICAgfSxcblxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHNldFxuICAgIFxuICAgICAgIFNldHMgYSBudW1iZXIuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgYyAtIEEgPENvbXBsZXg+IG9yIDxQb2xhcj4gaW5zdGFuY2UuXG4gICAgXG4gICAgKi9cbiAgICBzZXQ6IGZ1bmN0aW9uKGMpIHtcbiAgICAgIGMgPSBjLmdldGModHJ1ZSk7XG4gICAgICB0aGlzLnggPSBjLng7IFxuICAgICAgdGhpcy55ID0gYy55O1xuICAgIH0sXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogc2V0Y1xuICAgIFxuICAgICAgIFNldHMgYSBjb21wbGV4IG51bWJlci5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICB4IC0gQSA8Q29tcGxleD4gbnVtYmVyIFJlYWwgcGFydC5cbiAgICAgICB5IC0gQSA8Q29tcGxleD4gbnVtYmVyIEltYWdpbmFyeSBwYXJ0LlxuICAgIFxuICAgICovXG4gICAgc2V0YzogZnVuY3Rpb24oeCwgeSkge1xuICAgICAgICB0aGlzLnggPSB4OyBcbiAgICAgICAgdGhpcy55ID0geTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHNldHBcbiAgICBcbiAgICAgICBTZXRzIGEgcG9sYXIgbnVtYmVyLlxuXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIHRoZXRhIC0gQSA8UG9sYXI+IG51bWJlciB0aGV0YSBwcm9wZXJ0eS5cbiAgICAgICByaG8gLSBBIDxQb2xhcj4gbnVtYmVyIHJobyBwcm9wZXJ0eS5cbiAgICBcbiAgICAqL1xuICAgIHNldHA6IGZ1bmN0aW9uKHRoZXRhLCByaG8pIHtcbiAgICAgICAgdGhpcy54ID0gTWF0aC5jb3ModGhldGEpICogcmhvO1xuICAgICAgICB0aGlzLnkgPSBNYXRoLnNpbih0aGV0YSkgKiByaG87XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBjbG9uZVxuICAgIFxuICAgICAgIFJldHVybnMgYSBjb3B5IG9mIHRoZSBjdXJyZW50IG9iamVjdC5cbiAgICBcbiAgICAgICBSZXR1cm5zOlxuICAgIFxuICAgICAgICAgIEEgY29weSBvZiB0aGUgcmVhbCBvYmplY3QuXG4gICAgKi9cbiAgICBjbG9uZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBuZXcgQ29tcGxleCh0aGlzLngsIHRoaXMueSk7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiB0b1BvbGFyXG4gICAgXG4gICAgICAgVHJhbnNmb3JtcyBjYXJ0ZXNpYW4gdG8gcG9sYXIgY29vcmRpbmF0ZXMuXG4gICAgXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIHNpbXBsZSAtIF9vcHRpb25hbF8gSWYgKnRydWUqIHRoaXMgbWV0aG9kIHdpbGwgb25seSByZXR1cm4gYW4gb2JqZWN0IHdpdGggdGhldGEgYW5kIHJobyBwcm9wZXJ0aWVzIChhbmQgbm90IHRoZSB3aG9sZSA8UG9sYXI+IGluc3RhbmNlKS4gRGVmYXVsdCdzICpmYWxzZSouXG4gICAgICAgXG4gICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgICBBIG5ldyA8UG9sYXI+IGluc3RhbmNlLlxuICAgICovXG4gICAgXG4gICAgdG9Qb2xhcjogZnVuY3Rpb24oc2ltcGxlKSB7XG4gICAgICAgIHZhciByaG8gPSB0aGlzLm5vcm0oKTtcbiAgICAgICAgdmFyIGF0YW4gPSBNYXRoLmF0YW4yKHRoaXMueSwgdGhpcy54KTtcbiAgICAgICAgaWYoYXRhbiA8IDApIGF0YW4gKz0gTWF0aC5QSSAqIDI7XG4gICAgICAgIGlmKHNpbXBsZSkgcmV0dXJuIHsgJ3RoZXRhJzogYXRhbiwgJ3Jobyc6IHJobyB9O1xuICAgICAgICByZXR1cm4gbmV3IFBvbGFyKGF0YW4sIHJobyk7XG4gICAgfSxcbiAgICAvKlxuICAgICAgIE1ldGhvZDogbm9ybVxuICAgIFxuICAgICAgIENhbGN1bGF0ZXMgYSA8Q29tcGxleD4gbnVtYmVyIG5vcm0uXG4gICAgXG4gICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgICBBIHJlYWwgbnVtYmVyIHJlcHJlc2VudGluZyB0aGUgY29tcGxleCBub3JtLlxuICAgICovXG4gICAgbm9ybTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gTWF0aC5zcXJ0KHRoaXMuc3F1YXJlZE5vcm0oKSk7XG4gICAgfSxcbiAgICBcbiAgICAvKlxuICAgICAgIE1ldGhvZDogc3F1YXJlZE5vcm1cbiAgICBcbiAgICAgICBDYWxjdWxhdGVzIGEgPENvbXBsZXg+IG51bWJlciBzcXVhcmVkIG5vcm0uXG4gICAgXG4gICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgICBBIHJlYWwgbnVtYmVyIHJlcHJlc2VudGluZyB0aGUgY29tcGxleCBzcXVhcmVkIG5vcm0uXG4gICAgKi9cbiAgICBzcXVhcmVkTm9ybTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy54KnRoaXMueCArIHRoaXMueSp0aGlzLnk7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBhZGRcbiAgICBcbiAgICAgICBSZXR1cm5zIHRoZSByZXN1bHQgb2YgYWRkaW5nIHR3byBjb21wbGV4IG51bWJlcnMuXG4gICAgICAgXG4gICAgICAgRG9lcyBub3QgYWx0ZXIgdGhlIG9yaWdpbmFsIG9iamVjdC5cblxuICAgICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgICAgICAgcG9zIC0gQSA8Q29tcGxleD4gaW5zdGFuY2UuXG4gICAgXG4gICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgIFRoZSByZXN1bHQgb2YgYWRkaW5nIHR3byBjb21wbGV4IG51bWJlcnMuXG4gICAgKi9cbiAgICBhZGQ6IGZ1bmN0aW9uKHBvcykge1xuICAgICAgICByZXR1cm4gbmV3IENvbXBsZXgodGhpcy54ICsgcG9zLngsIHRoaXMueSArIHBvcy55KTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHByb2RcbiAgICBcbiAgICAgICBSZXR1cm5zIHRoZSByZXN1bHQgb2YgbXVsdGlwbHlpbmcgdHdvIDxDb21wbGV4PiBudW1iZXJzLlxuICAgICAgIFxuICAgICAgIERvZXMgbm90IGFsdGVyIHRoZSBvcmlnaW5hbCBvYmplY3QuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgICAgICAgIHBvcyAtIEEgPENvbXBsZXg+IGluc3RhbmNlLlxuICAgIFxuICAgICAgIFJldHVybnM6XG4gICAgXG4gICAgICAgICBUaGUgcmVzdWx0IG9mIG11bHRpcGx5aW5nIHR3byBjb21wbGV4IG51bWJlcnMuXG4gICAgKi9cbiAgICBwcm9kOiBmdW5jdGlvbihwb3MpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBDb21wbGV4KHRoaXMueCpwb3MueCAtIHRoaXMueSpwb3MueSwgdGhpcy55KnBvcy54ICsgdGhpcy54KnBvcy55KTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IGNvbmp1Z2F0ZVxuICAgIFxuICAgICAgIFJldHVybnMgdGhlIGNvbmp1Z2F0ZSBvZiB0aGlzIDxDb21wbGV4PiBudW1iZXIuXG5cbiAgICAgICBEb2VzIG5vdCBhbHRlciB0aGUgb3JpZ2luYWwgb2JqZWN0LlxuXG4gICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgIFRoZSBjb25qdWdhdGUgb2YgdGhpcyA8Q29tcGxleD4gbnVtYmVyLlxuICAgICovXG4gICAgY29uanVnYXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBDb21wbGV4KHRoaXMueCwgLXRoaXMueSk7XG4gICAgfSxcblxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHNjYWxlXG4gICAgXG4gICAgICAgUmV0dXJucyB0aGUgcmVzdWx0IG9mIHNjYWxpbmcgYSA8Q29tcGxleD4gaW5zdGFuY2UuXG4gICAgICAgXG4gICAgICAgRG9lcyBub3QgYWx0ZXIgdGhlIG9yaWdpbmFsIG9iamVjdC5cblxuICAgICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgICAgICAgZmFjdG9yIC0gQSBzY2FsZSBmYWN0b3IuXG4gICAgXG4gICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgIFRoZSByZXN1bHQgb2Ygc2NhbGluZyB0aGlzIGNvbXBsZXggdG8gYSBmYWN0b3IuXG4gICAgKi9cbiAgICBzY2FsZTogZnVuY3Rpb24oZmFjdG9yKSB7XG4gICAgICAgIHJldHVybiBuZXcgQ29tcGxleCh0aGlzLnggKiBmYWN0b3IsIHRoaXMueSAqIGZhY3Rvcik7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBlcXVhbHNcbiAgICBcbiAgICAgICBDb21wYXJpc29uIG1ldGhvZC5cblxuICAgICAgIFJldHVybnMgKnRydWUqIGlmIGJvdGggcmVhbCBhbmQgaW1hZ2luYXJ5IHBhcnRzIGFyZSBlcXVhbC5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICBjIC0gQSA8Q29tcGxleD4gaW5zdGFuY2UuXG5cbiAgICAgICBSZXR1cm5zOlxuXG4gICAgICAgQSBib29sZWFuIGluc3RhbmNlIGluZGljYXRpbmcgaWYgYm90aCA8Q29tcGxleD4gbnVtYmVycyBhcmUgZXF1YWwuXG4gICAgKi9cbiAgICBlcXVhbHM6IGZ1bmN0aW9uKGMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMueCA9PSBjLnggJiYgdGhpcy55ID09IGMueTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6ICRhZGRcbiAgICBcbiAgICAgICBSZXR1cm5zIHRoZSByZXN1bHQgb2YgYWRkaW5nIHR3byA8Q29tcGxleD4gbnVtYmVycy5cbiAgICAgICBcbiAgICAgICBBbHRlcnMgdGhlIG9yaWdpbmFsIG9iamVjdC5cblxuICAgICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgICAgICAgcG9zIC0gQSA8Q29tcGxleD4gaW5zdGFuY2UuXG4gICAgXG4gICAgICAgUmV0dXJuczpcbiAgICBcbiAgICAgICAgIFRoZSByZXN1bHQgb2YgYWRkaW5nIHR3byBjb21wbGV4IG51bWJlcnMuXG4gICAgKi9cbiAgICAkYWRkOiBmdW5jdGlvbihwb3MpIHtcbiAgICAgICAgdGhpcy54ICs9IHBvcy54OyB0aGlzLnkgKz0gcG9zLnk7XG4gICAgICAgIHJldHVybiB0aGlzOyAgICBcbiAgICB9LFxuICAgIFxuICAgIC8qXG4gICAgICAgTWV0aG9kOiAkcHJvZFxuICAgIFxuICAgICAgIFJldHVybnMgdGhlIHJlc3VsdCBvZiBtdWx0aXBseWluZyB0d28gPENvbXBsZXg+IG51bWJlcnMuXG4gICAgICAgXG4gICAgICAgQWx0ZXJzIHRoZSBvcmlnaW5hbCBvYmplY3QuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgICAgICAgIHBvcyAtIEEgPENvbXBsZXg+IGluc3RhbmNlLlxuICAgIFxuICAgICAgIFJldHVybnM6XG4gICAgXG4gICAgICAgICBUaGUgcmVzdWx0IG9mIG11bHRpcGx5aW5nIHR3byBjb21wbGV4IG51bWJlcnMuXG4gICAgKi9cbiAgICAkcHJvZDpmdW5jdGlvbihwb3MpIHtcbiAgICAgICAgdmFyIHggPSB0aGlzLngsIHkgPSB0aGlzLnk7XG4gICAgICAgIHRoaXMueCA9IHgqcG9zLnggLSB5KnBvcy55O1xuICAgICAgICB0aGlzLnkgPSB5KnBvcy54ICsgeCpwb3MueTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcbiAgICBcbiAgICAvKlxuICAgICAgIE1ldGhvZDogJGNvbmp1Z2F0ZVxuICAgIFxuICAgICAgIFJldHVybnMgdGhlIGNvbmp1Z2F0ZSBmb3IgdGhpcyA8Q29tcGxleD4uXG4gICAgICAgXG4gICAgICAgQWx0ZXJzIHRoZSBvcmlnaW5hbCBvYmplY3QuXG5cbiAgICAgICBSZXR1cm5zOlxuICAgIFxuICAgICAgICAgVGhlIGNvbmp1Z2F0ZSBmb3IgdGhpcyBjb21wbGV4LlxuICAgICovXG4gICAgJGNvbmp1Z2F0ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIHRoaXMueSA9IC10aGlzLnk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6ICRzY2FsZVxuICAgIFxuICAgICAgIFJldHVybnMgdGhlIHJlc3VsdCBvZiBzY2FsaW5nIGEgPENvbXBsZXg+IGluc3RhbmNlLlxuICAgICAgIFxuICAgICAgIEFsdGVycyB0aGUgb3JpZ2luYWwgb2JqZWN0LlxuXG4gICAgICAgUGFyYW1ldGVyczpcbiAgICBcbiAgICAgICAgICBmYWN0b3IgLSBBIHNjYWxlIGZhY3Rvci5cbiAgICBcbiAgICAgICBSZXR1cm5zOlxuICAgIFxuICAgICAgICAgVGhlIHJlc3VsdCBvZiBzY2FsaW5nIHRoaXMgY29tcGxleCB0byBhIGZhY3Rvci5cbiAgICAqL1xuICAgICRzY2FsZTogZnVuY3Rpb24oZmFjdG9yKSB7XG4gICAgICAgIHRoaXMueCAqPSBmYWN0b3I7IHRoaXMueSAqPSBmYWN0b3I7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6ICRkaXZcbiAgICBcbiAgICAgICBSZXR1cm5zIHRoZSBkaXZpc2lvbiBvZiB0d28gPENvbXBsZXg+IG51bWJlcnMuXG4gICAgICAgXG4gICAgICAgQWx0ZXJzIHRoZSBvcmlnaW5hbCBvYmplY3QuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgICAgICAgIHBvcyAtIEEgPENvbXBsZXg+IG51bWJlci5cbiAgICBcbiAgICAgICBSZXR1cm5zOlxuICAgIFxuICAgICAgICAgVGhlIHJlc3VsdCBvZiBzY2FsaW5nIHRoaXMgY29tcGxleCB0byBhIGZhY3Rvci5cbiAgICAqL1xuICAgICRkaXY6IGZ1bmN0aW9uKHBvcykge1xuICAgICAgICB2YXIgeCA9IHRoaXMueCwgeSA9IHRoaXMueTtcbiAgICAgICAgdmFyIHNxID0gcG9zLnNxdWFyZWROb3JtKCk7XG4gICAgICAgIHRoaXMueCA9IHggKiBwb3MueCArIHkgKiBwb3MueTsgdGhpcy55ID0geSAqIHBvcy54IC0geCAqIHBvcy55O1xuICAgICAgICByZXR1cm4gdGhpcy4kc2NhbGUoMSAvIHNxKTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgIE1ldGhvZDogaXNaZXJvXG4gICBcbiAgICAgIFJldHVybnMgKnRydWUqIGlmIHRoZSBudW1iZXIgaXMgemVyby5cbiAgIFxuICAgKi9cbiAgICBpc1plcm86IGZ1bmN0aW9uICgpIHtcbiAgICAgIHZhciBhbG1vc3RaZXJvID0gMC4wMDAxLCBhYnMgPSBNYXRoLmFicztcbiAgICAgIHJldHVybiBhYnModGhpcy54KSA8IGFsbW9zdFplcm8gJiYgYWJzKHRoaXMueSkgPCBhbG1vc3RaZXJvO1xuICAgIH1cbn07XG5cbnZhciAkQyA9IGZ1bmN0aW9uKGEsIGIpIHsgcmV0dXJuIG5ldyBDb21wbGV4KGEsIGIpOyB9O1xuXG5Db21wbGV4LktFUiA9ICRDKDAsIDApO1xuXG5cblxuLypcbiAqIEZpbGU6IEdyYXBoLmpzXG4gKlxuKi9cblxuLypcbiBDbGFzczogR3JhcGhcblxuIEEgR3JhcGggQ2xhc3MgdGhhdCBwcm92aWRlcyB1c2VmdWwgbWFuaXB1bGF0aW9uIGZ1bmN0aW9ucy4gWW91IGNhbiBmaW5kIG1vcmUgbWFuaXB1bGF0aW9uIG1ldGhvZHMgaW4gdGhlIDxHcmFwaC5VdGlsPiBvYmplY3QuXG5cbiBBbiBpbnN0YW5jZSBvZiB0aGlzIGNsYXNzIGNhbiBiZSBhY2Nlc3NlZCBieSB1c2luZyB0aGUgKmdyYXBoKiBwYXJhbWV0ZXIgb2YgYW55IHRyZWUgb3IgZ3JhcGggdmlzdWFsaXphdGlvbi5cbiBcbiBFeGFtcGxlOlxuXG4gKHN0YXJ0IGNvZGUganMpXG4gICAvL2NyZWF0ZSBuZXcgdmlzdWFsaXphdGlvblxuICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgIC8vbG9hZCBKU09OIGRhdGFcbiAgIHZpei5sb2FkSlNPTihqc29uKTtcbiAgIC8vYWNjZXNzIG1vZGVsXG4gICB2aXouZ3JhcGg7IC8vPEdyYXBoPiBpbnN0YW5jZVxuIChlbmQgY29kZSlcbiBcbiBJbXBsZW1lbnRzOlxuIFxuIFRoZSBmb2xsb3dpbmcgPEdyYXBoLlV0aWw+IG1ldGhvZHMgYXJlIGltcGxlbWVudGVkIGluIDxHcmFwaD5cbiBcbiAgLSA8R3JhcGguVXRpbC5nZXROb2RlPlxuICAtIDxHcmFwaC5VdGlsLmVhY2hOb2RlPlxuICAtIDxHcmFwaC5VdGlsLmNvbXB1dGVMZXZlbHM+XG4gIC0gPEdyYXBoLlV0aWwuZWFjaEJGUz5cbiAgLSA8R3JhcGguVXRpbC5jbGVhbj5cbiAgLSA8R3JhcGguVXRpbC5nZXRDbG9zZXN0Tm9kZVRvUG9zPlxuICAtIDxHcmFwaC5VdGlsLmdldENsb3Nlc3ROb2RlVG9PcmlnaW4+XG4gXG4qLyAgXG5cbiRqaXQuR3JhcGggPSBuZXcgQ2xhc3Moe1xuXG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uKG9wdCwgTm9kZSwgRWRnZSwgTGFiZWwpIHtcbiAgICB2YXIgaW5uZXJPcHRpb25zID0ge1xuICAgICdrbGFzcyc6IENvbXBsZXgsXG4gICAgJ05vZGUnOiB7fVxuICAgIH07XG4gICAgdGhpcy5Ob2RlID0gTm9kZTtcbiAgICB0aGlzLkVkZ2UgPSBFZGdlO1xuICAgIHRoaXMuTGFiZWwgPSBMYWJlbDtcbiAgICB0aGlzLm9wdCA9ICQubWVyZ2UoaW5uZXJPcHRpb25zLCBvcHQgfHwge30pO1xuICAgIHRoaXMubm9kZXMgPSB7fTtcbiAgICB0aGlzLmVkZ2VzID0ge307XG4gICAgXG4gICAgLy9hZGQgbm9kZUxpc3QgbWV0aG9kc1xuICAgIHZhciB0aGF0ID0gdGhpcztcbiAgICB0aGlzLm5vZGVMaXN0ID0ge307XG4gICAgZm9yKHZhciBwIGluIEFjY2Vzc29ycykge1xuICAgICAgdGhhdC5ub2RlTGlzdFtwXSA9IChmdW5jdGlvbihwKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICAgICAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gICAgICAgICAgdGhhdC5lYWNoTm9kZShmdW5jdGlvbihuKSB7XG4gICAgICAgICAgICBuW3BdLmFwcGx5KG4sIGFyZ3MpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuICAgICAgfSkocCk7XG4gICAgfVxuXG4gfSxcblxuLypcbiAgICAgTWV0aG9kOiBnZXROb2RlXG4gICAgXG4gICAgIFJldHVybnMgYSA8R3JhcGguTm9kZT4gYnkgKmlkKi5cblxuICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgIGlkIC0gKHN0cmluZykgQSA8R3JhcGguTm9kZT4gaWQuXG5cbiAgICAgRXhhbXBsZTpcblxuICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICB2YXIgbm9kZSA9IGdyYXBoLmdldE5vZGUoJ25vZGVJZCcpO1xuICAgICAoZW5kIGNvZGUpXG4qLyAgXG4gZ2V0Tm9kZTogZnVuY3Rpb24oaWQpIHtcbiAgICBpZih0aGlzLmhhc05vZGUoaWQpKSByZXR1cm4gdGhpcy5ub2Rlc1tpZF07XG4gICAgcmV0dXJuIGZhbHNlO1xuIH0sXG5cbiAvKlxuICAgICBNZXRob2Q6IGdldFxuICAgIFxuICAgICBBbiBhbGlhcyBmb3IgPEdyYXBoLlV0aWwuZ2V0Tm9kZT4uIFJldHVybnMgYSBub2RlIGJ5ICppZCouXG4gICAgXG4gICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgIGlkIC0gKHN0cmluZykgQSA8R3JhcGguTm9kZT4gaWQuXG4gICAgXG4gICAgIEV4YW1wbGU6XG4gICAgXG4gICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgIHZhciBub2RlID0gZ3JhcGguZ2V0KCdub2RlSWQnKTtcbiAgICAgKGVuZCBjb2RlKVxuKi8gIFxuICBnZXQ6IGZ1bmN0aW9uKGlkKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0Tm9kZShpZCk7XG4gIH0sXG5cbiAvKlxuICAgTWV0aG9kOiBnZXRCeU5hbWVcbiAgXG4gICBSZXR1cm5zIGEgPEdyYXBoLk5vZGU+IGJ5ICpuYW1lKi5cbiAgXG4gICBQYXJhbWV0ZXJzOlxuICBcbiAgIG5hbWUgLSAoc3RyaW5nKSBBIDxHcmFwaC5Ob2RlPiBuYW1lLlxuICBcbiAgIEV4YW1wbGU6XG4gIFxuICAgKHN0YXJ0IGNvZGUganMpXG4gICAgIHZhciBub2RlID0gZ3JhcGguZ2V0QnlOYW1lKCdzb21lTmFtZScpO1xuICAgKGVuZCBjb2RlKVxuICAqLyAgXG4gIGdldEJ5TmFtZTogZnVuY3Rpb24obmFtZSkge1xuICAgIGZvcih2YXIgaWQgaW4gdGhpcy5ub2Rlcykge1xuICAgICAgdmFyIG4gPSB0aGlzLm5vZGVzW2lkXTtcbiAgICAgIGlmKG4ubmFtZSA9PSBuYW1lKSByZXR1cm4gbjtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9LFxuXG4vKlxuICAgTWV0aG9kOiBnZXRBZGphY2VuY2VcbiAgXG4gICBSZXR1cm5zIGEgPEdyYXBoLkFkamFjZW5jZT4gb2JqZWN0IGNvbm5lY3Rpbmcgbm9kZXMgd2l0aCBpZHMgKmlkKiBhbmQgKmlkMiouXG5cbiAgIFBhcmFtZXRlcnM6XG5cbiAgIGlkIC0gKHN0cmluZykgQSA8R3JhcGguTm9kZT4gaWQuXG4gICBpZDIgLSAoc3RyaW5nKSBBIDxHcmFwaC5Ob2RlPiBpZC5cbiovICBcbiAgZ2V0QWRqYWNlbmNlOiBmdW5jdGlvbiAoaWQsIGlkMikge1xuICAgIGlmKGlkIGluIHRoaXMuZWRnZXMpIHtcbiAgICAgIHJldHVybiB0aGlzLmVkZ2VzW2lkXVtpZDJdO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gfSxcblxuICAgIC8qXG4gICAgIE1ldGhvZDogYWRkTm9kZVxuICAgIFxuICAgICBBZGRzIGEgbm9kZS5cbiAgICAgXG4gICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgICBvYmogLSBBbiBvYmplY3Qgd2l0aCB0aGUgcHJvcGVydGllcyBkZXNjcmliZWQgYmVsb3dcblxuICAgICAgaWQgLSAoc3RyaW5nKSBBIG5vZGUgaWRcbiAgICAgIG5hbWUgLSAoc3RyaW5nKSBBIG5vZGUncyBuYW1lXG4gICAgICBkYXRhIC0gKG9iamVjdCkgQSBub2RlJ3MgZGF0YSBoYXNoXG5cbiAgICBTZWUgYWxzbzpcbiAgICA8R3JhcGguTm9kZT5cblxuICAqLyAgXG4gIGFkZE5vZGU6IGZ1bmN0aW9uKG9iaikgeyBcbiAgIGlmKCF0aGlzLm5vZGVzW29iai5pZF0pIHsgIFxuICAgICB2YXIgZWRnZXMgPSB0aGlzLmVkZ2VzW29iai5pZF0gPSB7fTtcbiAgICAgdGhpcy5ub2Rlc1tvYmouaWRdID0gbmV3IEdyYXBoLk5vZGUoJC5leHRlbmQoe1xuICAgICAgICAnaWQnOiBvYmouaWQsXG4gICAgICAgICduYW1lJzogb2JqLm5hbWUsXG4gICAgICAgICdkYXRhJzogJC5tZXJnZShvYmouZGF0YSB8fCB7fSwge30pLFxuICAgICAgICAnYWRqYWNlbmNpZXMnOiBlZGdlcyBcbiAgICAgIH0sIHRoaXMub3B0Lk5vZGUpLCBcbiAgICAgIHRoaXMub3B0LmtsYXNzLCBcbiAgICAgIHRoaXMuTm9kZSwgXG4gICAgICB0aGlzLkVkZ2UsXG4gICAgICB0aGlzLkxhYmVsKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMubm9kZXNbb2JqLmlkXTtcbiAgfSxcbiAgXG4gICAgLypcbiAgICAgTWV0aG9kOiBhZGRBZGphY2VuY2VcbiAgICBcbiAgICAgQ29ubmVjdHMgbm9kZXMgc3BlY2lmaWVkIGJ5ICpvYmoqIGFuZCAqb2JqMiouIElmIG5vdCBmb3VuZCwgbm9kZXMgYXJlIGNyZWF0ZWQuXG4gICAgIFxuICAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgICAgb2JqIC0gKG9iamVjdCkgQSA8R3JhcGguTm9kZT4gb2JqZWN0LlxuICAgICAgb2JqMiAtIChvYmplY3QpIEFub3RoZXIgPEdyYXBoLk5vZGU+IG9iamVjdC5cbiAgICAgIGRhdGEgLSAob2JqZWN0KSBBIGRhdGEgb2JqZWN0LiBVc2VkIHRvIHN0b3JlIHNvbWUgZXh0cmEgaW5mb3JtYXRpb24gaW4gdGhlIDxHcmFwaC5BZGphY2VuY2U+IG9iamVjdCBjcmVhdGVkLlxuXG4gICAgU2VlIGFsc286XG5cbiAgICA8R3JhcGguTm9kZT4sIDxHcmFwaC5BZGphY2VuY2U+XG4gICAgKi8gIFxuICBhZGRBZGphY2VuY2U6IGZ1bmN0aW9uIChvYmosIG9iajIsIGRhdGEpIHtcbiAgICBpZighdGhpcy5oYXNOb2RlKG9iai5pZCkpIHsgdGhpcy5hZGROb2RlKG9iaik7IH1cbiAgICBpZighdGhpcy5oYXNOb2RlKG9iajIuaWQpKSB7IHRoaXMuYWRkTm9kZShvYmoyKTsgfVxuICAgIG9iaiA9IHRoaXMubm9kZXNbb2JqLmlkXTsgb2JqMiA9IHRoaXMubm9kZXNbb2JqMi5pZF07XG4gICAgaWYoIW9iai5hZGphY2VudFRvKG9iajIpKSB7XG4gICAgICB2YXIgYWRqc09iaiA9IHRoaXMuZWRnZXNbb2JqLmlkXSA9IHRoaXMuZWRnZXNbb2JqLmlkXSB8fCB7fTtcbiAgICAgIHZhciBhZGpzT2JqMiA9IHRoaXMuZWRnZXNbb2JqMi5pZF0gPSB0aGlzLmVkZ2VzW29iajIuaWRdIHx8IHt9O1xuICAgICAgYWRqc09ialtvYmoyLmlkXSA9IGFkanNPYmoyW29iai5pZF0gPSBuZXcgR3JhcGguQWRqYWNlbmNlKG9iaiwgb2JqMiwgZGF0YSwgdGhpcy5FZGdlLCB0aGlzLkxhYmVsKTtcbiAgICAgIHJldHVybiBhZGpzT2JqW29iajIuaWRdO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5lZGdlc1tvYmouaWRdW29iajIuaWRdO1xuIH0sXG5cbiAgICAvKlxuICAgICBNZXRob2Q6IHJlbW92ZU5vZGVcbiAgICBcbiAgICAgUmVtb3ZlcyBhIDxHcmFwaC5Ob2RlPiBtYXRjaGluZyB0aGUgc3BlY2lmaWVkICppZCouXG5cbiAgICAgUGFyYW1ldGVyczpcblxuICAgICBpZCAtIChzdHJpbmcpIEEgbm9kZSdzIGlkLlxuXG4gICAgKi8gIFxuICByZW1vdmVOb2RlOiBmdW5jdGlvbihpZCkge1xuICAgIGlmKHRoaXMuaGFzTm9kZShpZCkpIHtcbiAgICAgIGRlbGV0ZSB0aGlzLm5vZGVzW2lkXTtcbiAgICAgIHZhciBhZGpzID0gdGhpcy5lZGdlc1tpZF07XG4gICAgICBmb3IodmFyIHRvIGluIGFkanMpIHtcbiAgICAgICAgZGVsZXRlIHRoaXMuZWRnZXNbdG9dW2lkXTtcbiAgICAgIH1cbiAgICAgIGRlbGV0ZSB0aGlzLmVkZ2VzW2lkXTtcbiAgICB9XG4gIH0sXG4gIFxuLypcbiAgICAgTWV0aG9kOiByZW1vdmVBZGphY2VuY2VcbiAgICBcbiAgICAgUmVtb3ZlcyBhIDxHcmFwaC5BZGphY2VuY2U+IG1hdGNoaW5nICppZDEqIGFuZCAqaWQyKi5cblxuICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgIGlkMSAtIChzdHJpbmcpIEEgPEdyYXBoLk5vZGU+IGlkLlxuICAgICBpZDIgLSAoc3RyaW5nKSBBIDxHcmFwaC5Ob2RlPiBpZC5cbiovICBcbiAgcmVtb3ZlQWRqYWNlbmNlOiBmdW5jdGlvbihpZDEsIGlkMikge1xuICAgIGRlbGV0ZSB0aGlzLmVkZ2VzW2lkMV1baWQyXTtcbiAgICBkZWxldGUgdGhpcy5lZGdlc1tpZDJdW2lkMV07XG4gIH0sXG5cbiAgIC8qXG4gICAgIE1ldGhvZDogaGFzTm9kZVxuICAgIFxuICAgICBSZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIGlmIHRoZSBub2RlIGJlbG9uZ3MgdG8gdGhlIDxHcmFwaD4gb3Igbm90LlxuICAgICBcbiAgICAgUGFyYW1ldGVyczpcbiAgICBcbiAgICAgICAgaWQgLSAoc3RyaW5nKSBOb2RlIGlkLlxuICAgKi8gIFxuICBoYXNOb2RlOiBmdW5jdGlvbihpZCkge1xuICAgIHJldHVybiBpZCBpbiB0aGlzLm5vZGVzO1xuICB9LFxuICBcbiAgLypcbiAgICBNZXRob2Q6IGVtcHR5XG5cbiAgICBFbXB0aWVzIHRoZSBHcmFwaFxuXG4gICovXG4gIGVtcHR5OiBmdW5jdGlvbigpIHsgdGhpcy5ub2RlcyA9IHt9OyB0aGlzLmVkZ2VzID0ge307fVxuXG59KTtcblxudmFyIEdyYXBoID0gJGppdC5HcmFwaDtcblxuLypcbiBPYmplY3Q6IEFjY2Vzc29yc1xuIFxuIERlZmluZXMgYSBzZXQgb2YgbWV0aG9kcyBmb3IgZGF0YSwgY2FudmFzIGFuZCBsYWJlbCBzdHlsZXMgbWFuaXB1bGF0aW9uIGltcGxlbWVudGVkIGJ5IDxHcmFwaC5Ob2RlPiBhbmQgPEdyYXBoLkFkamFjZW5jZT4gaW5zdGFuY2VzLlxuIFxuICovXG52YXIgQWNjZXNzb3JzO1xuXG4oZnVuY3Rpb24gKCkge1xuICB2YXIgZ2V0RGF0YUludGVybmFsID0gZnVuY3Rpb24ocHJlZml4LCBwcm9wLCB0eXBlLCBmb3JjZSwgcHJlZml4Q29uZmlnKSB7XG4gICAgdmFyIGRhdGE7XG4gICAgdHlwZSA9IHR5cGUgfHwgJ2N1cnJlbnQnO1xuICAgIHByZWZpeCA9IFwiJFwiICsgKHByZWZpeCA/IHByZWZpeCArIFwiLVwiIDogXCJcIik7XG5cbiAgICBpZih0eXBlID09ICdjdXJyZW50Jykge1xuICAgICAgZGF0YSA9IHRoaXMuZGF0YTtcbiAgICB9IGVsc2UgaWYodHlwZSA9PSAnc3RhcnQnKSB7XG4gICAgICBkYXRhID0gdGhpcy5zdGFydERhdGE7XG4gICAgfSBlbHNlIGlmKHR5cGUgPT0gJ2VuZCcpIHtcbiAgICAgIGRhdGEgPSB0aGlzLmVuZERhdGE7XG4gICAgfVxuXG4gICAgdmFyIGRvbGxhciA9IHByZWZpeCArIHByb3A7XG5cbiAgICBpZihmb3JjZSkge1xuICAgICAgcmV0dXJuIGRhdGFbZG9sbGFyXTtcbiAgICB9XG5cbiAgICBpZighdGhpcy5Db25maWcub3ZlcnJpZGFibGUpXG4gICAgICByZXR1cm4gcHJlZml4Q29uZmlnW3Byb3BdIHx8IDA7XG5cbiAgICByZXR1cm4gKGRvbGxhciBpbiBkYXRhKSA/XG4gICAgICBkYXRhW2RvbGxhcl0gOiAoKGRvbGxhciBpbiB0aGlzLmRhdGEpID8gdGhpcy5kYXRhW2RvbGxhcl0gOiAocHJlZml4Q29uZmlnW3Byb3BdIHx8IDApKTtcbiAgfVxuXG4gIHZhciBzZXREYXRhSW50ZXJuYWwgPSBmdW5jdGlvbihwcmVmaXgsIHByb3AsIHZhbHVlLCB0eXBlKSB7XG4gICAgdHlwZSA9IHR5cGUgfHwgJ2N1cnJlbnQnO1xuICAgIHByZWZpeCA9ICckJyArIChwcmVmaXggPyBwcmVmaXggKyAnLScgOiAnJyk7XG5cbiAgICB2YXIgZGF0YTtcblxuICAgIGlmKHR5cGUgPT0gJ2N1cnJlbnQnKSB7XG4gICAgICBkYXRhID0gdGhpcy5kYXRhO1xuICAgIH0gZWxzZSBpZih0eXBlID09ICdzdGFydCcpIHtcbiAgICAgIGRhdGEgPSB0aGlzLnN0YXJ0RGF0YTtcbiAgICB9IGVsc2UgaWYodHlwZSA9PSAnZW5kJykge1xuICAgICAgZGF0YSA9IHRoaXMuZW5kRGF0YTtcbiAgICB9XG5cbiAgICBkYXRhW3ByZWZpeCArIHByb3BdID0gdmFsdWU7XG4gIH1cblxuICB2YXIgcmVtb3ZlRGF0YUludGVybmFsID0gZnVuY3Rpb24ocHJlZml4LCBwcm9wZXJ0aWVzKSB7XG4gICAgcHJlZml4ID0gJyQnICsgKHByZWZpeCA/IHByZWZpeCArICctJyA6ICcnKTtcbiAgICB2YXIgdGhhdCA9IHRoaXM7XG4gICAgJC5lYWNoKHByb3BlcnRpZXMsIGZ1bmN0aW9uKHByb3ApIHtcbiAgICAgIHZhciBwcmVmID0gcHJlZml4ICsgcHJvcDtcbiAgICAgIGRlbGV0ZSB0aGF0LmRhdGFbcHJlZl07XG4gICAgICBkZWxldGUgdGhhdC5lbmREYXRhW3ByZWZdO1xuICAgICAgZGVsZXRlIHRoYXQuc3RhcnREYXRhW3ByZWZdO1xuICAgIH0pO1xuICB9XG5cbiAgQWNjZXNzb3JzID0ge1xuICAgIC8qXG4gICAgTWV0aG9kOiBnZXREYXRhXG5cbiAgICBSZXR1cm5zIHRoZSBzcGVjaWZpZWQgZGF0YSB2YWx1ZSBwcm9wZXJ0eS5cbiAgICBUaGlzIGlzIHVzZWZ1bCBmb3IgcXVlcnlpbmcgc3BlY2lhbC9yZXNlcnZlZCA8R3JhcGguTm9kZT4gZGF0YSBwcm9wZXJ0aWVzXG4gICAgKGkuZSBkb2xsYXIgcHJlZml4ZWQgcHJvcGVydGllcykuXG5cbiAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICBwcm9wICAtIChzdHJpbmcpIFRoZSBuYW1lIG9mIHRoZSBwcm9wZXJ0eS4gVGhlIGRvbGxhciBzaWduIGlzIG5vdCBuZWVkZWQuIEZvclxuICAgICAgICAgICAgICBleGFtcGxlICpnZXREYXRhKHdpZHRoKSogd2lsbCByZXR1cm4gKmRhdGEuJHdpZHRoKi5cbiAgICAgIHR5cGUgIC0gKHN0cmluZykgVGhlIHR5cGUgb2YgdGhlIGRhdGEgcHJvcGVydHkgcXVlcmllZC4gRGVmYXVsdCdzIFwiY3VycmVudFwiLiBZb3UgY2FuIGFjY2VzcyAqc3RhcnQqIGFuZCAqZW5kKiBcbiAgICAgICAgICAgICAgZGF0YSBwcm9wZXJ0aWVzIGFsc28uIFRoZXNlIHByb3BlcnRpZXMgYXJlIHVzZWQgd2hlbiBtYWtpbmcgYW5pbWF0aW9ucy5cbiAgICAgIGZvcmNlIC0gKGJvb2xlYW4pIFdoZXRoZXIgdG8gb2J0YWluIHRoZSB0cnVlIHZhbHVlIG9mIHRoZSBwcm9wZXJ0eSAoZXF1aXZhbGVudCB0b1xuICAgICAgICAgICAgICAqZGF0YS4kcHJvcCopIG9yIHRvIGNoZWNrIGZvciAqbm9kZS5vdmVycmlkYWJsZSA9IHRydWUqIGZpcnN0LlxuXG4gICAgUmV0dXJuczpcblxuICAgICAgVGhlIHZhbHVlIG9mIHRoZSBkb2xsYXIgcHJlZml4ZWQgcHJvcGVydHkgb3IgdGhlIGdsb2JhbCBOb2RlL0VkZ2UgcHJvcGVydHlcbiAgICAgIHZhbHVlIGlmICpvdmVycmlkYWJsZT1mYWxzZSpcblxuICAgIEV4YW1wbGU6XG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgIG5vZGUuZ2V0RGF0YSgnd2lkdGgnKTsgLy93aWxsIHJldHVybiBub2RlLmRhdGEuJHdpZHRoIGlmIE5vZGUub3ZlcnJpZGFibGU9dHJ1ZTtcbiAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBnZXREYXRhOiBmdW5jdGlvbihwcm9wLCB0eXBlLCBmb3JjZSkge1xuICAgICAgcmV0dXJuIGdldERhdGFJbnRlcm5hbC5jYWxsKHRoaXMsIFwiXCIsIHByb3AsIHR5cGUsIGZvcmNlLCB0aGlzLkNvbmZpZyk7XG4gICAgfSxcblxuXG4gICAgLypcbiAgICBNZXRob2Q6IHNldERhdGFcblxuICAgIFNldHMgdGhlIGN1cnJlbnQgZGF0YSBwcm9wZXJ0eSB3aXRoIHNvbWUgc3BlY2lmaWMgdmFsdWUuXG4gICAgVGhpcyBtZXRob2QgaXMgb25seSB1c2VmdWwgZm9yIHJlc2VydmVkIChkb2xsYXIgcHJlZml4ZWQpIHByb3BlcnRpZXMuXG5cbiAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICBwcm9wICAtIChzdHJpbmcpIFRoZSBuYW1lIG9mIHRoZSBwcm9wZXJ0eS4gVGhlIGRvbGxhciBzaWduIGlzIG5vdCBuZWNlc3NhcnkuIEZvclxuICAgICAgICAgICAgICBleGFtcGxlICpzZXREYXRhKHdpZHRoKSogd2lsbCBzZXQgKmRhdGEuJHdpZHRoKi5cbiAgICAgIHZhbHVlIC0gKG1peGVkKSBUaGUgdmFsdWUgdG8gc3RvcmUuXG4gICAgICB0eXBlICAtIChzdHJpbmcpIFRoZSB0eXBlIG9mIHRoZSBkYXRhIHByb3BlcnR5IHRvIHN0b3JlLiBEZWZhdWx0J3MgXCJjdXJyZW50XCIgYnV0XG4gICAgICAgICAgICAgIGNhbiBhbHNvIGJlIFwic3RhcnRcIiBvciBcImVuZFwiLlxuXG4gICAgRXhhbXBsZTpcbiAgICBcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgbm9kZS5zZXREYXRhKCd3aWR0aCcsIDMwKTtcbiAgICAoZW5kIGNvZGUpXG4gICAgXG4gICAgSWYgd2Ugd2VyZSB0byBtYWtlIGFuIGFuaW1hdGlvbiBvZiBhIG5vZGUvZWRnZSB3aWR0aCB0aGVuIHdlIGNvdWxkIGRvXG4gICAgXG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICB2YXIgbm9kZSA9IHZpei5nZXROb2RlKCdub2RlSWQnKTtcbiAgICAgIC8vc2V0IHN0YXJ0IGFuZCBlbmQgdmFsdWVzXG4gICAgICBub2RlLnNldERhdGEoJ3dpZHRoJywgMTAsICdzdGFydCcpO1xuICAgICAgbm9kZS5zZXREYXRhKCd3aWR0aCcsIDMwLCAnZW5kJyk7XG4gICAgICAvL3dpbGwgYW5pbWF0ZSBub2RlcyB3aWR0aCBwcm9wZXJ0eVxuICAgICAgdml6LmZ4LmFuaW1hdGUoe1xuICAgICAgICBtb2RlczogWydub2RlLXByb3BlcnR5OndpZHRoJ10sXG4gICAgICAgIGR1cmF0aW9uOiAxMDAwXG4gICAgICB9KTtcbiAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBzZXREYXRhOiBmdW5jdGlvbihwcm9wLCB2YWx1ZSwgdHlwZSkge1xuICAgICAgc2V0RGF0YUludGVybmFsLmNhbGwodGhpcywgXCJcIiwgcHJvcCwgdmFsdWUsIHR5cGUpO1xuICAgIH0sXG5cbiAgICAvKlxuICAgIE1ldGhvZDogc2V0RGF0YXNldFxuXG4gICAgQ29udmVuaWVuY2UgbWV0aG9kIHRvIHNldCBtdWx0aXBsZSBkYXRhIHZhbHVlcyBhdCBvbmNlLlxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgdHlwZXMgLSAoYXJyYXl8c3RyaW5nKSBBIHNldCBvZiAnY3VycmVudCcsICdlbmQnIG9yICdzdGFydCcgdmFsdWVzLlxuICAgIG9iaiAtIChvYmplY3QpIEEgaGFzaCBjb250YWluaW5nIHRoZSBuYW1lcyBhbmQgdmFsdWVzIG9mIHRoZSBwcm9wZXJ0aWVzIHRvIGJlIGFsdGVyZWQuXG5cbiAgICBFeGFtcGxlOlxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgbm9kZS5zZXREYXRhc2V0KFsnY3VycmVudCcsICdlbmQnXSwge1xuICAgICAgICAnd2lkdGgnOiBbMTAwLCA1XSxcbiAgICAgICAgJ2NvbG9yJzogWycjZmZmJywgJyNjY2MnXVxuICAgICAgfSk7XG4gICAgICAvLy4uLm9yIGFsc29cbiAgICAgIG5vZGUuc2V0RGF0YXNldCgnZW5kJywge1xuICAgICAgICAnd2lkdGgnOiA1LFxuICAgICAgICAnY29sb3InOiAnI2NjYydcbiAgICAgIH0pO1xuICAgIChlbmQgY29kZSlcbiAgICBcbiAgICBTZWUgYWxzbzogXG4gICAgXG4gICAgPEFjY2Vzc29ycy5zZXREYXRhPlxuICAgIFxuICAgICovXG4gICAgc2V0RGF0YXNldDogZnVuY3Rpb24odHlwZXMsIG9iaikge1xuICAgICAgdHlwZXMgPSAkLnNwbGF0KHR5cGVzKTtcbiAgICAgIGZvcih2YXIgYXR0ciBpbiBvYmopIHtcbiAgICAgICAgZm9yKHZhciBpPTAsIHZhbCA9ICQuc3BsYXQob2JqW2F0dHJdKSwgbD10eXBlcy5sZW5ndGg7IGk8bDsgaSsrKSB7XG4gICAgICAgICAgdGhpcy5zZXREYXRhKGF0dHIsIHZhbFtpXSwgdHlwZXNbaV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSxcbiAgICBcbiAgICAvKlxuICAgIE1ldGhvZDogcmVtb3ZlRGF0YVxuXG4gICAgUmVtb3ZlIGRhdGEgcHJvcGVydGllcy5cblxuICAgIFBhcmFtZXRlcnM6XG5cbiAgICBPbmUgb3IgbW9yZSBwcm9wZXJ0eSBuYW1lcyBhcyBhcmd1bWVudHMuIFRoZSBkb2xsYXIgc2lnbiBpcyBub3QgbmVlZGVkLlxuXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICBub2RlLnJlbW92ZURhdGEoJ3dpZHRoJyk7IC8vbm93IHRoZSBkZWZhdWx0IHdpZHRoIHZhbHVlIGlzIHJldHVybmVkXG4gICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgcmVtb3ZlRGF0YTogZnVuY3Rpb24oKSB7XG4gICAgICByZW1vdmVEYXRhSW50ZXJuYWwuY2FsbCh0aGlzLCBcIlwiLCBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpKTtcbiAgICB9LFxuXG4gICAgLypcbiAgICBNZXRob2Q6IGdldENhbnZhc1N0eWxlXG5cbiAgICBSZXR1cm5zIHRoZSBzcGVjaWZpZWQgY2FudmFzIHN0eWxlIGRhdGEgdmFsdWUgcHJvcGVydHkuIFRoaXMgaXMgdXNlZnVsIGZvclxuICAgIHF1ZXJ5aW5nIHNwZWNpYWwvcmVzZXJ2ZWQgPEdyYXBoLk5vZGU+IGNhbnZhcyBzdHlsZSBkYXRhIHByb3BlcnRpZXMgKGkuZS5cbiAgICBkb2xsYXIgcHJlZml4ZWQgcHJvcGVydGllcyB0aGF0IG1hdGNoIHdpdGggJGNhbnZhcy08bmFtZSBvZiBjYW52YXMgc3R5bGU+KS5cblxuICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgIHByb3AgIC0gKHN0cmluZykgVGhlIG5hbWUgb2YgdGhlIHByb3BlcnR5LiBUaGUgZG9sbGFyIHNpZ24gaXMgbm90IG5lZWRlZC4gRm9yXG4gICAgICAgICAgICAgIGV4YW1wbGUgKmdldENhbnZhc1N0eWxlKHNoYWRvd0JsdXIpKiB3aWxsIHJldHVybiAqZGF0YVskY2FudmFzLXNoYWRvd0JsdXJdKi5cbiAgICAgIHR5cGUgIC0gKHN0cmluZykgVGhlIHR5cGUgb2YgdGhlIGRhdGEgcHJvcGVydHkgcXVlcmllZC4gRGVmYXVsdCdzICpjdXJyZW50Ki4gWW91IGNhbiBhY2Nlc3MgKnN0YXJ0KiBhbmQgKmVuZCogXG4gICAgICAgICAgICAgIGRhdGEgcHJvcGVydGllcyBhbHNvLlxuICAgICAgICAgICAgICBcbiAgICBFeGFtcGxlOlxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgbm9kZS5nZXRDYW52YXNTdHlsZSgnc2hhZG93Qmx1cicpO1xuICAgIChlbmQgY29kZSlcbiAgICBcbiAgICBTZWUgYWxzbzpcbiAgICBcbiAgICA8QWNjZXNzb3JzLmdldERhdGE+XG4gICAgKi9cbiAgICBnZXRDYW52YXNTdHlsZTogZnVuY3Rpb24ocHJvcCwgdHlwZSwgZm9yY2UpIHtcbiAgICAgIHJldHVybiBnZXREYXRhSW50ZXJuYWwuY2FsbChcbiAgICAgICAgICB0aGlzLCAnY2FudmFzJywgcHJvcCwgdHlwZSwgZm9yY2UsIHRoaXMuQ29uZmlnLkNhbnZhc1N0eWxlcyk7XG4gICAgfSxcblxuICAgIC8qXG4gICAgTWV0aG9kOiBzZXRDYW52YXNTdHlsZVxuXG4gICAgU2V0cyB0aGUgY2FudmFzIHN0eWxlIGRhdGEgcHJvcGVydHkgd2l0aCBzb21lIHNwZWNpZmljIHZhbHVlLlxuICAgIFRoaXMgbWV0aG9kIGlzIG9ubHkgdXNlZnVsIGZvciByZXNlcnZlZCAoZG9sbGFyIHByZWZpeGVkKSBwcm9wZXJ0aWVzLlxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgcHJvcCAtIChzdHJpbmcpIE5hbWUgb2YgdGhlIHByb3BlcnR5LiBDYW4gYmUgYW55IGNhbnZhcyBwcm9wZXJ0eSBsaWtlICdzaGFkb3dCbHVyJywgJ3NoYWRvd0NvbG9yJywgJ3N0cm9rZVN0eWxlJywgZXRjLlxuICAgIHZhbHVlIC0gKG1peGVkKSBUaGUgdmFsdWUgdG8gc2V0IHRvIHRoZSBwcm9wZXJ0eS5cbiAgICB0eXBlIC0gKHN0cmluZykgRGVmYXVsdCdzICpjdXJyZW50Ki4gV2hldGhlciB0byBzZXQgKnN0YXJ0KiwgKmN1cnJlbnQqIG9yICplbmQqIHR5cGUgcHJvcGVydGllcy5cbiAgICBcbiAgICBFeGFtcGxlOlxuICAgIFxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgICBub2RlLnNldENhbnZhc1N0eWxlKCdzaGFkb3dCbHVyJywgMzApO1xuICAgIChlbmQgY29kZSlcbiAgICBcbiAgICBJZiB3ZSB3ZXJlIHRvIG1ha2UgYW4gYW5pbWF0aW9uIG9mIGEgbm9kZS9lZGdlIHNoYWRvd0JsdXIgY2FudmFzIHN0eWxlIHRoZW4gd2UgY291bGQgZG9cbiAgICBcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgIHZhciBub2RlID0gdml6LmdldE5vZGUoJ25vZGVJZCcpO1xuICAgICAgLy9zZXQgc3RhcnQgYW5kIGVuZCB2YWx1ZXNcbiAgICAgIG5vZGUuc2V0Q2FudmFzU3R5bGUoJ3NoYWRvd0JsdXInLCAxMCwgJ3N0YXJ0Jyk7XG4gICAgICBub2RlLnNldENhbnZhc1N0eWxlKCdzaGFkb3dCbHVyJywgMzAsICdlbmQnKTtcbiAgICAgIC8vd2lsbCBhbmltYXRlIG5vZGVzIGNhbnZhcyBzdHlsZSBwcm9wZXJ0eSBmb3Igbm9kZXNcbiAgICAgIHZpei5meC5hbmltYXRlKHtcbiAgICAgICAgbW9kZXM6IFsnbm9kZS1zdHlsZTpzaGFkb3dCbHVyJ10sXG4gICAgICAgIGR1cmF0aW9uOiAxMDAwXG4gICAgICB9KTtcbiAgICAoZW5kIGNvZGUpXG4gICAgXG4gICAgU2VlIGFsc286XG4gICAgXG4gICAgPEFjY2Vzc29ycy5zZXREYXRhPi5cbiAgICAqL1xuICAgIHNldENhbnZhc1N0eWxlOiBmdW5jdGlvbihwcm9wLCB2YWx1ZSwgdHlwZSkge1xuICAgICAgc2V0RGF0YUludGVybmFsLmNhbGwodGhpcywgJ2NhbnZhcycsIHByb3AsIHZhbHVlLCB0eXBlKTtcbiAgICB9LFxuXG4gICAgLypcbiAgICBNZXRob2Q6IHNldENhbnZhc1N0eWxlc1xuXG4gICAgQ29udmVuaWVuY2UgbWV0aG9kIHRvIHNldCBtdWx0aXBsZSBzdHlsZXMgYXQgb25jZS5cblxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgdHlwZXMgLSAoYXJyYXl8c3RyaW5nKSBBIHNldCBvZiAnY3VycmVudCcsICdlbmQnIG9yICdzdGFydCcgdmFsdWVzLlxuICAgIG9iaiAtIChvYmplY3QpIEEgaGFzaCBjb250YWluaW5nIHRoZSBuYW1lcyBhbmQgdmFsdWVzIG9mIHRoZSBwcm9wZXJ0aWVzIHRvIGJlIGFsdGVyZWQuXG5cbiAgICBTZWUgYWxzbzpcbiAgICBcbiAgICA8QWNjZXNzb3JzLnNldERhdGFzZXQ+LlxuICAgICovXG4gICAgc2V0Q2FudmFzU3R5bGVzOiBmdW5jdGlvbih0eXBlcywgb2JqKSB7XG4gICAgICB0eXBlcyA9ICQuc3BsYXQodHlwZXMpO1xuICAgICAgZm9yKHZhciBhdHRyIGluIG9iaikge1xuICAgICAgICBmb3IodmFyIGk9MCwgdmFsID0gJC5zcGxhdChvYmpbYXR0cl0pLCBsPXR5cGVzLmxlbmd0aDsgaTxsOyBpKyspIHtcbiAgICAgICAgICB0aGlzLnNldENhbnZhc1N0eWxlKGF0dHIsIHZhbFtpXSwgdHlwZXNbaV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSxcblxuICAgIC8qXG4gICAgTWV0aG9kOiByZW1vdmVDYW52YXNTdHlsZVxuXG4gICAgUmVtb3ZlIGNhbnZhcyBzdHlsZSBwcm9wZXJ0aWVzIGZyb20gZGF0YS5cblxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgQSB2YXJpYWJsZSBudW1iZXIgb2YgY2FudmFzIHN0eWxlIHN0cmluZ3MuXG5cbiAgICBTZWUgYWxzbzpcbiAgICBcbiAgICA8QWNjZXNzb3JzLnJlbW92ZURhdGE+LlxuICAgICovXG4gICAgcmVtb3ZlQ2FudmFzU3R5bGU6IGZ1bmN0aW9uKCkge1xuICAgICAgcmVtb3ZlRGF0YUludGVybmFsLmNhbGwodGhpcywgJ2NhbnZhcycsIEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpO1xuICAgIH0sXG5cbiAgICAvKlxuICAgIE1ldGhvZDogZ2V0TGFiZWxEYXRhXG5cbiAgICBSZXR1cm5zIHRoZSBzcGVjaWZpZWQgbGFiZWwgZGF0YSB2YWx1ZSBwcm9wZXJ0eS4gVGhpcyBpcyB1c2VmdWwgZm9yXG4gICAgcXVlcnlpbmcgc3BlY2lhbC9yZXNlcnZlZCA8R3JhcGguTm9kZT4gbGFiZWwgb3B0aW9ucyAoaS5lLlxuICAgIGRvbGxhciBwcmVmaXhlZCBwcm9wZXJ0aWVzIHRoYXQgbWF0Y2ggd2l0aCAkbGFiZWwtPG5hbWUgb2YgbGFiZWwgc3R5bGU+KS5cblxuICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgIHByb3AgIC0gKHN0cmluZykgVGhlIG5hbWUgb2YgdGhlIHByb3BlcnR5LiBUaGUgZG9sbGFyIHNpZ24gcHJlZml4IGlzIG5vdCBuZWVkZWQuIEZvclxuICAgICAgICAgICAgICBleGFtcGxlICpnZXRMYWJlbERhdGEoc2l6ZSkqIHdpbGwgcmV0dXJuICpkYXRhWyRsYWJlbC1zaXplXSouXG4gICAgICB0eXBlICAtIChzdHJpbmcpIFRoZSB0eXBlIG9mIHRoZSBkYXRhIHByb3BlcnR5IHF1ZXJpZWQuIERlZmF1bHQncyAqY3VycmVudCouIFlvdSBjYW4gYWNjZXNzICpzdGFydCogYW5kICplbmQqIFxuICAgICAgICAgICAgICBkYXRhIHByb3BlcnRpZXMgYWxzby5cbiAgICAgICAgICAgICAgXG4gICAgU2VlIGFsc286XG4gICAgXG4gICAgPEFjY2Vzc29ycy5nZXREYXRhPi5cbiAgICAqL1xuICAgIGdldExhYmVsRGF0YTogZnVuY3Rpb24ocHJvcCwgdHlwZSwgZm9yY2UpIHtcbiAgICAgIHJldHVybiBnZXREYXRhSW50ZXJuYWwuY2FsbChcbiAgICAgICAgICB0aGlzLCAnbGFiZWwnLCBwcm9wLCB0eXBlLCBmb3JjZSwgdGhpcy5MYWJlbCk7XG4gICAgfSxcblxuICAgIC8qXG4gICAgTWV0aG9kOiBzZXRMYWJlbERhdGFcblxuICAgIFNldHMgdGhlIGN1cnJlbnQgbGFiZWwgZGF0YSB3aXRoIHNvbWUgc3BlY2lmaWMgdmFsdWUuXG4gICAgVGhpcyBtZXRob2QgaXMgb25seSB1c2VmdWwgZm9yIHJlc2VydmVkIChkb2xsYXIgcHJlZml4ZWQpIHByb3BlcnRpZXMuXG5cbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIHByb3AgLSAoc3RyaW5nKSBOYW1lIG9mIHRoZSBwcm9wZXJ0eS4gQ2FuIGJlIGFueSBjYW52YXMgcHJvcGVydHkgbGlrZSAnc2hhZG93Qmx1cicsICdzaGFkb3dDb2xvcicsICdzdHJva2VTdHlsZScsIGV0Yy5cbiAgICB2YWx1ZSAtIChtaXhlZCkgVGhlIHZhbHVlIHRvIHNldCB0byB0aGUgcHJvcGVydHkuXG4gICAgdHlwZSAtIChzdHJpbmcpIERlZmF1bHQncyAqY3VycmVudCouIFdoZXRoZXIgdG8gc2V0ICpzdGFydCosICpjdXJyZW50KiBvciAqZW5kKiB0eXBlIHByb3BlcnRpZXMuXG4gICAgXG4gICAgRXhhbXBsZTpcbiAgICBcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgbm9kZS5zZXRMYWJlbERhdGEoJ3NpemUnLCAzMCk7XG4gICAgKGVuZCBjb2RlKVxuICAgIFxuICAgIElmIHdlIHdlcmUgdG8gbWFrZSBhbiBhbmltYXRpb24gb2YgYSBub2RlIGxhYmVsIHNpemUgdGhlbiB3ZSBjb3VsZCBkb1xuICAgIFxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgdmFyIG5vZGUgPSB2aXouZ2V0Tm9kZSgnbm9kZUlkJyk7XG4gICAgICAvL3NldCBzdGFydCBhbmQgZW5kIHZhbHVlc1xuICAgICAgbm9kZS5zZXRMYWJlbERhdGEoJ3NpemUnLCAxMCwgJ3N0YXJ0Jyk7XG4gICAgICBub2RlLnNldExhYmVsRGF0YSgnc2l6ZScsIDMwLCAnZW5kJyk7XG4gICAgICAvL3dpbGwgYW5pbWF0ZSBub2RlcyBsYWJlbCBzaXplXG4gICAgICB2aXouZnguYW5pbWF0ZSh7XG4gICAgICAgIG1vZGVzOiBbJ2xhYmVsLXByb3BlcnR5OnNpemUnXSxcbiAgICAgICAgZHVyYXRpb246IDEwMDBcbiAgICAgIH0pO1xuICAgIChlbmQgY29kZSlcbiAgICBcbiAgICBTZWUgYWxzbzpcbiAgICBcbiAgICA8QWNjZXNzb3JzLnNldERhdGE+LlxuICAgICovXG4gICAgc2V0TGFiZWxEYXRhOiBmdW5jdGlvbihwcm9wLCB2YWx1ZSwgdHlwZSkge1xuICAgICAgc2V0RGF0YUludGVybmFsLmNhbGwodGhpcywgJ2xhYmVsJywgcHJvcCwgdmFsdWUsIHR5cGUpO1xuICAgIH0sXG5cbiAgICAvKlxuICAgIE1ldGhvZDogc2V0TGFiZWxEYXRhc2V0XG5cbiAgICBDb252ZW5pZW5jZSBmdW5jdGlvbiB0byBzZXQgbXVsdGlwbGUgbGFiZWwgZGF0YSBhdCBvbmNlLlxuXG4gICAgUGFyYW1ldGVyczpcbiAgICBcbiAgICB0eXBlcyAtIChhcnJheXxzdHJpbmcpIEEgc2V0IG9mICdjdXJyZW50JywgJ2VuZCcgb3IgJ3N0YXJ0JyB2YWx1ZXMuXG4gICAgb2JqIC0gKG9iamVjdCkgQSBoYXNoIGNvbnRhaW5pbmcgdGhlIG5hbWVzIGFuZCB2YWx1ZXMgb2YgdGhlIHByb3BlcnRpZXMgdG8gYmUgYWx0ZXJlZC5cblxuICAgIFNlZSBhbHNvOlxuICAgIFxuICAgIDxBY2Nlc3NvcnMuc2V0RGF0YXNldD4uXG4gICAgKi9cbiAgICBzZXRMYWJlbERhdGFzZXQ6IGZ1bmN0aW9uKHR5cGVzLCBvYmopIHtcbiAgICAgIHR5cGVzID0gJC5zcGxhdCh0eXBlcyk7XG4gICAgICBmb3IodmFyIGF0dHIgaW4gb2JqKSB7XG4gICAgICAgIGZvcih2YXIgaT0wLCB2YWwgPSAkLnNwbGF0KG9ialthdHRyXSksIGw9dHlwZXMubGVuZ3RoOyBpPGw7IGkrKykge1xuICAgICAgICAgIHRoaXMuc2V0TGFiZWxEYXRhKGF0dHIsIHZhbFtpXSwgdHlwZXNbaV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSxcblxuICAgIC8qXG4gICAgTWV0aG9kOiByZW1vdmVMYWJlbERhdGFcblxuICAgIFJlbW92ZSBsYWJlbCBwcm9wZXJ0aWVzIGZyb20gZGF0YS5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIEEgdmFyaWFibGUgbnVtYmVyIG9mIGxhYmVsIHByb3BlcnR5IHN0cmluZ3MuXG5cbiAgICBTZWUgYWxzbzpcbiAgICBcbiAgICA8QWNjZXNzb3JzLnJlbW92ZURhdGE+LlxuICAgICovXG4gICAgcmVtb3ZlTGFiZWxEYXRhOiBmdW5jdGlvbigpIHtcbiAgICAgIHJlbW92ZURhdGFJbnRlcm5hbC5jYWxsKHRoaXMsICdsYWJlbCcsIEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpO1xuICAgIH1cbiAgfTtcbn0pKCk7XG5cbi8qXG4gICAgIENsYXNzOiBHcmFwaC5Ob2RlXG5cbiAgICAgQSA8R3JhcGg+IG5vZGUuXG4gICAgIFxuICAgICBJbXBsZW1lbnRzOlxuICAgICBcbiAgICAgPEFjY2Vzc29ycz4gbWV0aG9kcy5cbiAgICAgXG4gICAgIFRoZSBmb2xsb3dpbmcgPEdyYXBoLlV0aWw+IG1ldGhvZHMgYXJlIGltcGxlbWVudGVkIGJ5IDxHcmFwaC5Ob2RlPlxuICAgICBcbiAgICAtIDxHcmFwaC5VdGlsLmVhY2hBZGphY2VuY3k+XG4gICAgLSA8R3JhcGguVXRpbC5lYWNoTGV2ZWw+XG4gICAgLSA8R3JhcGguVXRpbC5lYWNoU3ViZ3JhcGg+XG4gICAgLSA8R3JhcGguVXRpbC5lYWNoU3Vibm9kZT5cbiAgICAtIDxHcmFwaC5VdGlsLmFueVN1Ym5vZGU+XG4gICAgLSA8R3JhcGguVXRpbC5nZXRTdWJub2Rlcz5cbiAgICAtIDxHcmFwaC5VdGlsLmdldFBhcmVudHM+XG4gICAgLSA8R3JhcGguVXRpbC5pc0Rlc2NlbmRhbnRPZj4gICAgIFxuKi9cbkdyYXBoLk5vZGUgPSBuZXcgQ2xhc3Moe1xuICAgIFxuICBpbml0aWFsaXplOiBmdW5jdGlvbihvcHQsIGtsYXNzLCBOb2RlLCBFZGdlLCBMYWJlbCkge1xuICAgIHZhciBpbm5lck9wdGlvbnMgPSB7XG4gICAgICAnaWQnOiAnJyxcbiAgICAgICduYW1lJzogJycsXG4gICAgICAnZGF0YSc6IHt9LFxuICAgICAgJ3N0YXJ0RGF0YSc6IHt9LFxuICAgICAgJ2VuZERhdGEnOiB7fSxcbiAgICAgICdhZGphY2VuY2llcyc6IHt9LFxuXG4gICAgICAnc2VsZWN0ZWQnOiBmYWxzZSxcbiAgICAgICdkcmF3bic6IGZhbHNlLFxuICAgICAgJ2V4aXN0JzogZmFsc2UsXG5cbiAgICAgICdhbmdsZVNwYW4nOiB7XG4gICAgICAgICdiZWdpbic6IDAsXG4gICAgICAgICdlbmQnIDogMFxuICAgICAgfSxcblxuICAgICAgJ3Bvcyc6IG5ldyBrbGFzcyxcbiAgICAgICdzdGFydFBvcyc6IG5ldyBrbGFzcyxcbiAgICAgICdlbmRQb3MnOiBuZXcga2xhc3NcbiAgICB9O1xuICAgIFxuICAgICQuZXh0ZW5kKHRoaXMsICQuZXh0ZW5kKGlubmVyT3B0aW9ucywgb3B0KSk7XG4gICAgdGhpcy5Db25maWcgPSB0aGlzLk5vZGUgPSBOb2RlO1xuICAgIHRoaXMuRWRnZSA9IEVkZ2U7XG4gICAgdGhpcy5MYWJlbCA9IExhYmVsO1xuICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IGFkamFjZW50VG9cbiAgICBcbiAgICAgICBJbmRpY2F0ZXMgaWYgdGhlIG5vZGUgaXMgYWRqYWNlbnQgdG8gdGhlIG5vZGUgc3BlY2lmaWVkIGJ5IGlkXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgICAgICAgIGlkIC0gKHN0cmluZykgQSBub2RlIGlkLlxuICAgIFxuICAgICAgIEV4YW1wbGU6XG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgIG5vZGUuYWRqYWNlbnRUbygnbm9kZUlkJykgPT0gdHJ1ZTtcbiAgICAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBhZGphY2VudFRvOiBmdW5jdGlvbihub2RlKSB7XG4gICAgICAgIHJldHVybiBub2RlLmlkIGluIHRoaXMuYWRqYWNlbmNpZXM7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBnZXRBZGphY2VuY3lcbiAgICBcbiAgICAgICBSZXR1cm5zIGEgPEdyYXBoLkFkamFjZW5jZT4gb2JqZWN0IGNvbm5lY3RpbmcgdGhlIGN1cnJlbnQgPEdyYXBoLk5vZGU+IGFuZCB0aGUgbm9kZSBoYXZpbmcgKmlkKiBhcyBpZC5cblxuICAgICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgICAgICAgaWQgLSAoc3RyaW5nKSBBIG5vZGUgaWQuXG4gICAgKi8gIFxuICAgIGdldEFkamFjZW5jeTogZnVuY3Rpb24oaWQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYWRqYWNlbmNpZXNbaWRdO1xuICAgIH0sXG5cbiAgICAvKlxuICAgICAgTWV0aG9kOiBnZXRQb3NcbiAgIFxuICAgICAgUmV0dXJucyB0aGUgcG9zaXRpb24gb2YgdGhlIG5vZGUuXG4gIFxuICAgICAgUGFyYW1ldGVyczpcbiAgIFxuICAgICAgICAgdHlwZSAtIChzdHJpbmcpIERlZmF1bHQncyAqY3VycmVudCouIFBvc3NpYmxlIHZhbHVlcyBhcmUgXCJzdGFydFwiLCBcImVuZFwiIG9yIFwiY3VycmVudFwiLlxuICAgXG4gICAgICBSZXR1cm5zOlxuICAgXG4gICAgICAgIEEgPENvbXBsZXg+IG9yIDxQb2xhcj4gaW5zdGFuY2UuXG4gIFxuICAgICAgRXhhbXBsZTpcbiAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgIHZhciBwb3MgPSBub2RlLmdldFBvcygnZW5kJyk7XG4gICAgICAoZW5kIGNvZGUpXG4gICAqL1xuICAgZ2V0UG9zOiBmdW5jdGlvbih0eXBlKSB7XG4gICAgICAgdHlwZSA9IHR5cGUgfHwgXCJjdXJyZW50XCI7XG4gICAgICAgaWYodHlwZSA9PSBcImN1cnJlbnRcIikge1xuICAgICAgICAgcmV0dXJuIHRoaXMucG9zO1xuICAgICAgIH0gZWxzZSBpZih0eXBlID09IFwiZW5kXCIpIHtcbiAgICAgICAgIHJldHVybiB0aGlzLmVuZFBvcztcbiAgICAgICB9IGVsc2UgaWYodHlwZSA9PSBcInN0YXJ0XCIpIHtcbiAgICAgICAgIHJldHVybiB0aGlzLnN0YXJ0UG9zO1xuICAgICAgIH1cbiAgIH0sXG4gICAvKlxuICAgICBNZXRob2Q6IHNldFBvc1xuICBcbiAgICAgU2V0cyB0aGUgbm9kZSdzIHBvc2l0aW9uLlxuICBcbiAgICAgUGFyYW1ldGVyczpcbiAgXG4gICAgICAgIHZhbHVlIC0gKG9iamVjdCkgQSA8Q29tcGxleD4gb3IgPFBvbGFyPiBpbnN0YW5jZS5cbiAgICAgICAgdHlwZSAtIChzdHJpbmcpIERlZmF1bHQncyAqY3VycmVudCouIFBvc3NpYmxlIHZhbHVlcyBhcmUgXCJzdGFydFwiLCBcImVuZFwiIG9yIFwiY3VycmVudFwiLlxuICBcbiAgICAgRXhhbXBsZTpcbiAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICBub2RlLnNldFBvcyhuZXcgJGppdC5Db21wbGV4KDAsIDApLCAnZW5kJyk7XG4gICAgIChlbmQgY29kZSlcbiAgKi9cbiAgc2V0UG9zOiBmdW5jdGlvbih2YWx1ZSwgdHlwZSkge1xuICAgICAgdHlwZSA9IHR5cGUgfHwgXCJjdXJyZW50XCI7XG4gICAgICB2YXIgcG9zO1xuICAgICAgaWYodHlwZSA9PSBcImN1cnJlbnRcIikge1xuICAgICAgICBwb3MgPSB0aGlzLnBvcztcbiAgICAgIH0gZWxzZSBpZih0eXBlID09IFwiZW5kXCIpIHtcbiAgICAgICAgcG9zID0gdGhpcy5lbmRQb3M7XG4gICAgICB9IGVsc2UgaWYodHlwZSA9PSBcInN0YXJ0XCIpIHtcbiAgICAgICAgcG9zID0gdGhpcy5zdGFydFBvcztcbiAgICAgIH1cbiAgICAgIHBvcy5zZXQodmFsdWUpO1xuICB9XG59KTtcblxuR3JhcGguTm9kZS5pbXBsZW1lbnQoQWNjZXNzb3JzKTtcblxuLypcbiAgICAgQ2xhc3M6IEdyYXBoLkFkamFjZW5jZVxuXG4gICAgIEEgPEdyYXBoPiBhZGphY2VuY2UgKG9yIGVkZ2UpIGNvbm5lY3RpbmcgdHdvIDxHcmFwaC5Ob2Rlcz4uXG4gICAgIFxuICAgICBJbXBsZW1lbnRzOlxuICAgICBcbiAgICAgPEFjY2Vzc29ycz4gbWV0aG9kcy5cblxuICAgICBTZWUgYWxzbzpcblxuICAgICA8R3JhcGg+LCA8R3JhcGguTm9kZT5cblxuICAgICBQcm9wZXJ0aWVzOlxuICAgICBcbiAgICAgIG5vZGVGcm9tIC0gQSA8R3JhcGguTm9kZT4gY29ubmVjdGVkIGJ5IHRoaXMgZWRnZS5cbiAgICAgIG5vZGVUbyAtIEFub3RoZXIgIDxHcmFwaC5Ob2RlPiBjb25uZWN0ZWQgYnkgdGhpcyBlZGdlLlxuICAgICAgZGF0YSAtIE5vZGUgZGF0YSBwcm9wZXJ0eSBjb250YWluaW5nIGEgaGFzaCAoaS5lIHt9KSB3aXRoIGN1c3RvbSBvcHRpb25zLlxuKi9cbkdyYXBoLkFkamFjZW5jZSA9IG5ldyBDbGFzcyh7XG4gIFxuICBpbml0aWFsaXplOiBmdW5jdGlvbihub2RlRnJvbSwgbm9kZVRvLCBkYXRhLCBFZGdlLCBMYWJlbCkge1xuICAgIHRoaXMubm9kZUZyb20gPSBub2RlRnJvbTtcbiAgICB0aGlzLm5vZGVUbyA9IG5vZGVUbztcbiAgICB0aGlzLmRhdGEgPSBkYXRhIHx8IHt9O1xuICAgIHRoaXMuc3RhcnREYXRhID0ge307XG4gICAgdGhpcy5lbmREYXRhID0ge307XG4gICAgdGhpcy5Db25maWcgPSB0aGlzLkVkZ2UgPSBFZGdlO1xuICAgIHRoaXMuTGFiZWwgPSBMYWJlbDtcbiAgfVxufSk7XG5cbkdyYXBoLkFkamFjZW5jZS5pbXBsZW1lbnQoQWNjZXNzb3JzKTtcblxuLypcbiAgIE9iamVjdDogR3JhcGguVXRpbFxuXG4gICA8R3JhcGg+IHRyYXZlcnNhbCBhbmQgcHJvY2Vzc2luZyB1dGlsaXR5IG9iamVjdC5cbiAgIFxuICAgTm90ZTpcbiAgIFxuICAgRm9yIHlvdXIgY29udmVuaWVuY2Ugc29tZSBvZiB0aGVzZSBtZXRob2RzIGhhdmUgYWxzbyBiZWVuIGFwcGVuZGVkIHRvIDxHcmFwaD4gYW5kIDxHcmFwaC5Ob2RlPiBjbGFzc2VzLlxuKi9cbkdyYXBoLlV0aWwgPSB7XG4gICAgLypcbiAgICAgICBmaWx0ZXJcbiAgICBcbiAgICAgICBGb3IgaW50ZXJuYWwgdXNlIG9ubHkuIFByb3ZpZGVzIGEgZmlsdGVyaW5nIGZ1bmN0aW9uIGJhc2VkIG9uIGZsYWdzLlxuICAgICovXG4gICAgZmlsdGVyOiBmdW5jdGlvbihwYXJhbSkge1xuICAgICAgICBpZighcGFyYW0gfHwgISgkLnR5cGUocGFyYW0pID09ICdzdHJpbmcnKSkgcmV0dXJuIGZ1bmN0aW9uKCkgeyByZXR1cm4gdHJ1ZTsgfTtcbiAgICAgICAgdmFyIHByb3BzID0gcGFyYW0uc3BsaXQoXCIgXCIpO1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24oZWxlbSkge1xuICAgICAgICAgICAgZm9yKHZhciBpPTA7IGk8cHJvcHMubGVuZ3RoOyBpKyspIHsgXG4gICAgICAgICAgICAgIGlmKGVsZW1bcHJvcHNbaV1dKSB7IFxuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTsgXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9O1xuICAgIH0sXG4gICAgLypcbiAgICAgICBNZXRob2Q6IGdldE5vZGVcbiAgICBcbiAgICAgICBSZXR1cm5zIGEgPEdyYXBoLk5vZGU+IGJ5ICppZCouXG4gICAgICAgXG4gICAgICAgQWxzbyBpbXBsZW1lbnRlZCBieTpcbiAgICAgICBcbiAgICAgICA8R3JhcGg+XG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgZ3JhcGggLSAob2JqZWN0KSBBIDxHcmFwaD4gaW5zdGFuY2UuXG4gICAgICAgaWQgLSAoc3RyaW5nKSBBIDxHcmFwaC5Ob2RlPiBpZC5cblxuICAgICAgIEV4YW1wbGU6XG5cbiAgICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgICRqaXQuR3JhcGguVXRpbC5nZXROb2RlKGdyYXBoLCAnbm9kZWlkJyk7XG4gICAgICAgICAvL29yLi4uXG4gICAgICAgICBncmFwaC5nZXROb2RlKCdub2RlaWQnKTtcbiAgICAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBnZXROb2RlOiBmdW5jdGlvbihncmFwaCwgaWQpIHtcbiAgICAgICAgcmV0dXJuIGdyYXBoLm5vZGVzW2lkXTtcbiAgICB9LFxuICAgIFxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBlYWNoTm9kZVxuICAgIFxuICAgICAgIEl0ZXJhdGVzIG92ZXIgPEdyYXBoPiBub2RlcyBwZXJmb3JtaW5nIGFuICphY3Rpb24qLlxuICAgICAgIFxuICAgICAgIEFsc28gaW1wbGVtZW50ZWQgYnk6XG4gICAgICAgXG4gICAgICAgPEdyYXBoPi5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICBncmFwaCAtIChvYmplY3QpIEEgPEdyYXBoPiBpbnN0YW5jZS5cbiAgICAgICBhY3Rpb24gLSAoZnVuY3Rpb24pIEEgY2FsbGJhY2sgZnVuY3Rpb24gaGF2aW5nIGEgPEdyYXBoLk5vZGU+IGFzIGZpcnN0IGZvcm1hbCBwYXJhbWV0ZXIuXG5cbiAgICAgICBFeGFtcGxlOlxuICAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICAgJGppdC5HcmFwaC5VdGlsLmVhY2hOb2RlKGdyYXBoLCBmdW5jdGlvbihub2RlKSB7XG4gICAgICAgICAgYWxlcnQobm9kZS5uYW1lKTtcbiAgICAgICAgIH0pO1xuICAgICAgICAgLy9vci4uLlxuICAgICAgICAgZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obm9kZSkge1xuICAgICAgICAgICBhbGVydChub2RlLm5hbWUpO1xuICAgICAgICAgfSk7XG4gICAgICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgZWFjaE5vZGU6IGZ1bmN0aW9uKGdyYXBoLCBhY3Rpb24sIGZsYWdzKSB7XG4gICAgICAgIHZhciBmaWx0ZXIgPSB0aGlzLmZpbHRlcihmbGFncyk7XG4gICAgICAgIGZvcih2YXIgaSBpbiBncmFwaC5ub2Rlcykge1xuICAgICAgICAgIGlmKGZpbHRlcihncmFwaC5ub2Rlc1tpXSkpIGFjdGlvbihncmFwaC5ub2Rlc1tpXSk7XG4gICAgICAgIH0gXG4gICAgfSxcbiAgICBcbiAgICAvKlxuICAgICAgTWV0aG9kOiBlYWNoXG4gICBcbiAgICAgIEl0ZXJhdGVzIG92ZXIgPEdyYXBoPiBub2RlcyBwZXJmb3JtaW5nIGFuICphY3Rpb24qLiBJdCdzIGFuIGFsaWFzIGZvciA8R3JhcGguVXRpbC5lYWNoTm9kZT4uXG4gICAgICBcbiAgICAgIEFsc28gaW1wbGVtZW50ZWQgYnk6XG4gICAgICBcbiAgICAgIDxHcmFwaD4uXG4gIFxuICAgICAgUGFyYW1ldGVyczpcbiAgXG4gICAgICBncmFwaCAtIChvYmplY3QpIEEgPEdyYXBoPiBpbnN0YW5jZS5cbiAgICAgIGFjdGlvbiAtIChmdW5jdGlvbikgQSBjYWxsYmFjayBmdW5jdGlvbiBoYXZpbmcgYSA8R3JhcGguTm9kZT4gYXMgZmlyc3QgZm9ybWFsIHBhcmFtZXRlci5cbiAgXG4gICAgICBFeGFtcGxlOlxuICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgICRqaXQuR3JhcGguVXRpbC5lYWNoKGdyYXBoLCBmdW5jdGlvbihub2RlKSB7XG4gICAgICAgICBhbGVydChub2RlLm5hbWUpO1xuICAgICAgICB9KTtcbiAgICAgICAgLy9vci4uLlxuICAgICAgICBncmFwaC5lYWNoKGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgICAgICBhbGVydChub2RlLm5hbWUpO1xuICAgICAgICB9KTtcbiAgICAgIChlbmQgY29kZSlcbiAgICovXG4gICBlYWNoOiBmdW5jdGlvbihncmFwaCwgYWN0aW9uLCBmbGFncykge1xuICAgICAgdGhpcy5lYWNoTm9kZShncmFwaCwgYWN0aW9uLCBmbGFncyk7IFxuICAgfSxcblxuIC8qXG4gICAgICAgTWV0aG9kOiBlYWNoQWRqYWNlbmN5XG4gICAgXG4gICAgICAgSXRlcmF0ZXMgb3ZlciA8R3JhcGguTm9kZT4gYWRqYWNlbmNpZXMgYXBwbHlpbmcgdGhlICphY3Rpb24qIGZ1bmN0aW9uLlxuICAgICAgIFxuICAgICAgIEFsc28gaW1wbGVtZW50ZWQgYnk6XG4gICAgICAgXG4gICAgICAgPEdyYXBoLk5vZGU+LlxuXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIG5vZGUgLSAob2JqZWN0KSBBIDxHcmFwaC5Ob2RlPi5cbiAgICAgICBhY3Rpb24gLSAoZnVuY3Rpb24pIEEgY2FsbGJhY2sgZnVuY3Rpb24gaGF2aW5nIDxHcmFwaC5BZGphY2VuY2U+IGFzIGZpcnN0IGZvcm1hbCBwYXJhbWV0ZXIuXG5cbiAgICAgICBFeGFtcGxlOlxuICAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICAgJGppdC5HcmFwaC5VdGlsLmVhY2hBZGphY2VuY3kobm9kZSwgZnVuY3Rpb24oYWRqKSB7XG4gICAgICAgICAgYWxlcnQoYWRqLm5vZGVUby5uYW1lKTtcbiAgICAgICAgIH0pO1xuICAgICAgICAgLy9vci4uLlxuICAgICAgICAgbm9kZS5lYWNoQWRqYWNlbmN5KGZ1bmN0aW9uKGFkaikge1xuICAgICAgICAgICBhbGVydChhZGoubm9kZVRvLm5hbWUpO1xuICAgICAgICAgfSk7XG4gICAgICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgZWFjaEFkamFjZW5jeTogZnVuY3Rpb24obm9kZSwgYWN0aW9uLCBmbGFncykge1xuICAgICAgICB2YXIgYWRqID0gbm9kZS5hZGphY2VuY2llcywgZmlsdGVyID0gdGhpcy5maWx0ZXIoZmxhZ3MpO1xuICAgICAgICBmb3IodmFyIGlkIGluIGFkaikge1xuICAgICAgICAgIHZhciBhID0gYWRqW2lkXTtcbiAgICAgICAgICBpZihmaWx0ZXIoYSkpIHtcbiAgICAgICAgICAgIGlmKGEubm9kZUZyb20gIT0gbm9kZSkge1xuICAgICAgICAgICAgICB2YXIgdG1wID0gYS5ub2RlRnJvbTtcbiAgICAgICAgICAgICAgYS5ub2RlRnJvbSA9IGEubm9kZVRvO1xuICAgICAgICAgICAgICBhLm5vZGVUbyA9IHRtcDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGFjdGlvbihhLCBpZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSxcblxuICAgICAvKlxuICAgICAgIE1ldGhvZDogY29tcHV0ZUxldmVsc1xuICAgIFxuICAgICAgIFBlcmZvcm1zIGEgQkZTIHRyYXZlcnNhbCBzZXR0aW5nIHRoZSBjb3JyZWN0IGRlcHRoIGZvciBlYWNoIG5vZGUuXG4gICAgICAgIFxuICAgICAgIEFsc28gaW1wbGVtZW50ZWQgYnk6XG4gICAgICAgXG4gICAgICAgPEdyYXBoPi5cbiAgICAgICBcbiAgICAgICBOb3RlOlxuICAgICAgIFxuICAgICAgIFRoZSBkZXB0aCBvZiBlYWNoIG5vZGUgY2FuIHRoZW4gYmUgYWNjZXNzZWQgYnkgXG4gICAgICAgPm5vZGUuX2RlcHRoXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgZ3JhcGggLSAob2JqZWN0KSBBIDxHcmFwaD4uXG4gICAgICAgaWQgLSAoc3RyaW5nKSBBIHN0YXJ0aW5nIG5vZGUgaWQgZm9yIHRoZSBCRlMgdHJhdmVyc2FsLlxuICAgICAgIHN0YXJ0RGVwdGggLSAob3B0aW9uYWx8bnVtYmVyKSBBIG1pbmltdW0gZGVwdGggdmFsdWUuIERlZmF1bHQncyAwLlxuXG4gICAgKi9cbiAgICBjb21wdXRlTGV2ZWxzOiBmdW5jdGlvbihncmFwaCwgaWQsIHN0YXJ0RGVwdGgsIGZsYWdzKSB7XG4gICAgICAgIHN0YXJ0RGVwdGggPSBzdGFydERlcHRoIHx8IDA7XG4gICAgICAgIHZhciBmaWx0ZXIgPSB0aGlzLmZpbHRlcihmbGFncyk7XG4gICAgICAgIHRoaXMuZWFjaE5vZGUoZ3JhcGgsIGZ1bmN0aW9uKGVsZW0pIHtcbiAgICAgICAgICAgIGVsZW0uX2ZsYWcgPSBmYWxzZTtcbiAgICAgICAgICAgIGVsZW0uX2RlcHRoID0gLTE7XG4gICAgICAgIH0sIGZsYWdzKTtcbiAgICAgICAgdmFyIHJvb3QgPSBncmFwaC5nZXROb2RlKGlkKTtcbiAgICAgICAgcm9vdC5fZGVwdGggPSBzdGFydERlcHRoO1xuICAgICAgICB2YXIgcXVldWUgPSBbcm9vdF07XG4gICAgICAgIHdoaWxlKHF1ZXVlLmxlbmd0aCAhPSAwKSB7XG4gICAgICAgICAgICB2YXIgbm9kZSA9IHF1ZXVlLnBvcCgpO1xuICAgICAgICAgICAgbm9kZS5fZmxhZyA9IHRydWU7XG4gICAgICAgICAgICB0aGlzLmVhY2hBZGphY2VuY3kobm9kZSwgZnVuY3Rpb24oYWRqKSB7XG4gICAgICAgICAgICAgICAgdmFyIG4gPSBhZGoubm9kZVRvO1xuICAgICAgICAgICAgICAgIGlmKG4uX2ZsYWcgPT0gZmFsc2UgJiYgZmlsdGVyKG4pKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmKG4uX2RlcHRoIDwgMCkgbi5fZGVwdGggPSBub2RlLl9kZXB0aCArIDEgKyBzdGFydERlcHRoO1xuICAgICAgICAgICAgICAgICAgICBxdWV1ZS51bnNoaWZ0KG4pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sIGZsYWdzKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogZWFjaEJGU1xuICAgIFxuICAgICAgIFBlcmZvcm1zIGEgQkZTIHRyYXZlcnNhbCBhcHBseWluZyAqYWN0aW9uKiB0byBlYWNoIDxHcmFwaC5Ob2RlPi5cbiAgICAgICBcbiAgICAgICBBbHNvIGltcGxlbWVudGVkIGJ5OlxuICAgICAgIFxuICAgICAgIDxHcmFwaD4uXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgZ3JhcGggLSAob2JqZWN0KSBBIDxHcmFwaD4uXG4gICAgICAgaWQgLSAoc3RyaW5nKSBBIHN0YXJ0aW5nIG5vZGUgaWQgZm9yIHRoZSBCRlMgdHJhdmVyc2FsLlxuICAgICAgIGFjdGlvbiAtIChmdW5jdGlvbikgQSBjYWxsYmFjayBmdW5jdGlvbiBoYXZpbmcgYSA8R3JhcGguTm9kZT4gYXMgZmlyc3QgZm9ybWFsIHBhcmFtZXRlci5cblxuICAgICAgIEV4YW1wbGU6XG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgICAkaml0LkdyYXBoLlV0aWwuZWFjaEJGUyhncmFwaCwgJ215bm9kZWlkJywgZnVuY3Rpb24obm9kZSkge1xuICAgICAgICAgIGFsZXJ0KG5vZGUubmFtZSk7XG4gICAgICAgICB9KTtcbiAgICAgICAgIC8vb3IuLi5cbiAgICAgICAgIGdyYXBoLmVhY2hCRlMoJ215bm9kZWlkJywgZnVuY3Rpb24obm9kZSkge1xuICAgICAgICAgICBhbGVydChub2RlLm5hbWUpO1xuICAgICAgICAgfSk7XG4gICAgICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgZWFjaEJGUzogZnVuY3Rpb24oZ3JhcGgsIGlkLCBhY3Rpb24sIGZsYWdzKSB7XG4gICAgICAgIHZhciBmaWx0ZXIgPSB0aGlzLmZpbHRlcihmbGFncyk7XG4gICAgICAgIHRoaXMuY2xlYW4oZ3JhcGgpO1xuICAgICAgICB2YXIgcXVldWUgPSBbZ3JhcGguZ2V0Tm9kZShpZCldO1xuICAgICAgICB3aGlsZShxdWV1ZS5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgdmFyIG5vZGUgPSBxdWV1ZS5wb3AoKTtcbiAgICAgICAgICAgIG5vZGUuX2ZsYWcgPSB0cnVlO1xuICAgICAgICAgICAgYWN0aW9uKG5vZGUsIG5vZGUuX2RlcHRoKTtcbiAgICAgICAgICAgIHRoaXMuZWFjaEFkamFjZW5jeShub2RlLCBmdW5jdGlvbihhZGopIHtcbiAgICAgICAgICAgICAgICB2YXIgbiA9IGFkai5ub2RlVG87XG4gICAgICAgICAgICAgICAgaWYobi5fZmxhZyA9PSBmYWxzZSAmJiBmaWx0ZXIobikpIHtcbiAgICAgICAgICAgICAgICAgICAgbi5fZmxhZyA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHF1ZXVlLnVuc2hpZnQobik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSwgZmxhZ3MpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBcbiAgICAvKlxuICAgICAgIE1ldGhvZDogZWFjaExldmVsXG4gICAgXG4gICAgICAgSXRlcmF0ZXMgb3ZlciBhIG5vZGUncyBzdWJncmFwaCBhcHBseWluZyAqYWN0aW9uKiB0byB0aGUgbm9kZXMgb2YgcmVsYXRpdmUgZGVwdGggYmV0d2VlbiAqbGV2ZWxCZWdpbiogYW5kICpsZXZlbEVuZCouXG4gICAgICAgXG4gICAgICAgQWxzbyBpbXBsZW1lbnRlZCBieTpcbiAgICAgICBcbiAgICAgICA8R3JhcGguTm9kZT4uXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuICAgICAgIFxuICAgICAgIG5vZGUgLSAob2JqZWN0KSBBIDxHcmFwaC5Ob2RlPi5cbiAgICAgICBsZXZlbEJlZ2luIC0gKG51bWJlcikgQSByZWxhdGl2ZSBsZXZlbCB2YWx1ZS5cbiAgICAgICBsZXZlbEVuZCAtIChudW1iZXIpIEEgcmVsYXRpdmUgbGV2ZWwgdmFsdWUuXG4gICAgICAgYWN0aW9uIC0gKGZ1bmN0aW9uKSBBIGNhbGxiYWNrIGZ1bmN0aW9uIGhhdmluZyBhIDxHcmFwaC5Ob2RlPiBhcyBmaXJzdCBmb3JtYWwgcGFyYW1ldGVyLlxuXG4gICAgKi9cbiAgICBlYWNoTGV2ZWw6IGZ1bmN0aW9uKG5vZGUsIGxldmVsQmVnaW4sIGxldmVsRW5kLCBhY3Rpb24sIGZsYWdzKSB7XG4gICAgICAgIHZhciBkID0gbm9kZS5fZGVwdGgsIGZpbHRlciA9IHRoaXMuZmlsdGVyKGZsYWdzKSwgdGhhdCA9IHRoaXM7XG4gICAgICAgIGxldmVsRW5kID0gbGV2ZWxFbmQgPT09IGZhbHNlPyBOdW1iZXIuTUFYX1ZBTFVFIC1kIDogbGV2ZWxFbmQ7XG4gICAgICAgIChmdW5jdGlvbiBsb29wTGV2ZWwobm9kZSwgbGV2ZWxCZWdpbiwgbGV2ZWxFbmQpIHtcbiAgICAgICAgICAgIHZhciBkID0gbm9kZS5fZGVwdGg7XG4gICAgICAgICAgICBpZihkID49IGxldmVsQmVnaW4gJiYgZCA8PSBsZXZlbEVuZCAmJiBmaWx0ZXIobm9kZSkpIGFjdGlvbihub2RlLCBkKTtcbiAgICAgICAgICAgIGlmKGQgPCBsZXZlbEVuZCkge1xuICAgICAgICAgICAgICAgIHRoYXQuZWFjaEFkamFjZW5jeShub2RlLCBmdW5jdGlvbihhZGopIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG4gPSBhZGoubm9kZVRvO1xuICAgICAgICAgICAgICAgICAgICBpZihuLl9kZXB0aCA+IGQpIGxvb3BMZXZlbChuLCBsZXZlbEJlZ2luLCBsZXZlbEVuZCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pKG5vZGUsIGxldmVsQmVnaW4gKyBkLCBsZXZlbEVuZCArIGQpOyAgICAgIFxuICAgIH0sXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogZWFjaFN1YmdyYXBoXG4gICAgXG4gICAgICAgSXRlcmF0ZXMgb3ZlciBhIG5vZGUncyBjaGlsZHJlbiByZWN1cnNpdmVseS5cbiAgICAgICBcbiAgICAgICBBbHNvIGltcGxlbWVudGVkIGJ5OlxuICAgICAgIFxuICAgICAgIDxHcmFwaC5Ob2RlPi5cblxuICAgICAgIFBhcmFtZXRlcnM6XG4gICAgICAgbm9kZSAtIChvYmplY3QpIEEgPEdyYXBoLk5vZGU+LlxuICAgICAgIGFjdGlvbiAtIChmdW5jdGlvbikgQSBjYWxsYmFjayBmdW5jdGlvbiBoYXZpbmcgYSA8R3JhcGguTm9kZT4gYXMgZmlyc3QgZm9ybWFsIHBhcmFtZXRlci5cblxuICAgICAgIEV4YW1wbGU6XG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgICAkaml0LkdyYXBoLlV0aWwuZWFjaFN1YmdyYXBoKG5vZGUsIGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgICAgICAgYWxlcnQobm9kZS5uYW1lKTtcbiAgICAgICAgIH0pO1xuICAgICAgICAgLy9vci4uLlxuICAgICAgICAgbm9kZS5lYWNoU3ViZ3JhcGgoZnVuY3Rpb24obm9kZSkge1xuICAgICAgICAgICBhbGVydChub2RlLm5hbWUpO1xuICAgICAgICAgfSk7XG4gICAgICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgZWFjaFN1YmdyYXBoOiBmdW5jdGlvbihub2RlLCBhY3Rpb24sIGZsYWdzKSB7XG4gICAgICB0aGlzLmVhY2hMZXZlbChub2RlLCAwLCBmYWxzZSwgYWN0aW9uLCBmbGFncyk7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBlYWNoU3Vibm9kZVxuICAgIFxuICAgICAgIEl0ZXJhdGVzIG92ZXIgYSBub2RlJ3MgY2hpbGRyZW4gKHdpdGhvdXQgZGVlcGVyIHJlY3Vyc2lvbikuXG4gICAgICAgXG4gICAgICAgQWxzbyBpbXBsZW1lbnRlZCBieTpcbiAgICAgICBcbiAgICAgICA8R3JhcGguTm9kZT4uXG4gICAgICAgXG4gICAgICAgUGFyYW1ldGVyczpcbiAgICAgICBub2RlIC0gKG9iamVjdCkgQSA8R3JhcGguTm9kZT4uXG4gICAgICAgYWN0aW9uIC0gKGZ1bmN0aW9uKSBBIGNhbGxiYWNrIGZ1bmN0aW9uIGhhdmluZyBhIDxHcmFwaC5Ob2RlPiBhcyBmaXJzdCBmb3JtYWwgcGFyYW1ldGVyLlxuXG4gICAgICAgRXhhbXBsZTpcbiAgICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgICRqaXQuR3JhcGguVXRpbC5lYWNoU3Vibm9kZShub2RlLCBmdW5jdGlvbihub2RlKSB7XG4gICAgICAgICAgYWxlcnQobm9kZS5uYW1lKTtcbiAgICAgICAgIH0pO1xuICAgICAgICAgLy9vci4uLlxuICAgICAgICAgbm9kZS5lYWNoU3Vibm9kZShmdW5jdGlvbihub2RlKSB7XG4gICAgICAgICAgIGFsZXJ0KG5vZGUubmFtZSk7XG4gICAgICAgICB9KTtcbiAgICAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBlYWNoU3Vibm9kZTogZnVuY3Rpb24obm9kZSwgYWN0aW9uLCBmbGFncykge1xuICAgICAgICB0aGlzLmVhY2hMZXZlbChub2RlLCAxLCAxLCBhY3Rpb24sIGZsYWdzKTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IGFueVN1Ym5vZGVcbiAgICBcbiAgICAgICBSZXR1cm5zICp0cnVlKiBpZiBhbnkgc3Vibm9kZSBtYXRjaGVzIHRoZSBnaXZlbiBjb25kaXRpb24uXG4gICAgICAgXG4gICAgICAgQWxzbyBpbXBsZW1lbnRlZCBieTpcbiAgICAgICBcbiAgICAgICA8R3JhcGguTm9kZT4uXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuICAgICAgIG5vZGUgLSAob2JqZWN0KSBBIDxHcmFwaC5Ob2RlPi5cbiAgICAgICBjb25kIC0gKGZ1bmN0aW9uKSBBIGNhbGxiYWNrIGZ1bmN0aW9uIHJldHVybmluZyBhIEJvb2xlYW4gaW5zdGFuY2UuIFRoaXMgZnVuY3Rpb24gaGFzIGFzIGZpcnN0IGZvcm1hbCBwYXJhbWV0ZXIgYSA8R3JhcGguTm9kZT4uXG5cbiAgICAgICBFeGFtcGxlOlxuICAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICAgJGppdC5HcmFwaC5VdGlsLmFueVN1Ym5vZGUobm9kZSwgZnVuY3Rpb24obm9kZSkgeyByZXR1cm4gbm9kZS5uYW1lID09IFwibXlub2RlbmFtZVwiOyB9KTtcbiAgICAgICAgIC8vb3IuLi5cbiAgICAgICAgIG5vZGUuYW55U3Vibm9kZShmdW5jdGlvbihub2RlKSB7IHJldHVybiBub2RlLm5hbWUgPT0gJ215bm9kZW5hbWUnOyB9KTtcbiAgICAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBhbnlTdWJub2RlOiBmdW5jdGlvbihub2RlLCBjb25kLCBmbGFncykge1xuICAgICAgdmFyIGZsYWcgPSBmYWxzZTtcbiAgICAgIGNvbmQgPSBjb25kIHx8ICQubGFtYmRhKHRydWUpO1xuICAgICAgdmFyIGMgPSAkLnR5cGUoY29uZCkgPT0gJ3N0cmluZyc/IGZ1bmN0aW9uKG4pIHsgcmV0dXJuIG5bY29uZF07IH0gOiBjb25kO1xuICAgICAgdGhpcy5lYWNoU3Vibm9kZShub2RlLCBmdW5jdGlvbihlbGVtKSB7XG4gICAgICAgIGlmKGMoZWxlbSkpIGZsYWcgPSB0cnVlO1xuICAgICAgfSwgZmxhZ3MpO1xuICAgICAgcmV0dXJuIGZsYWc7XG4gICAgfSxcbiAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6IGdldFN1Ym5vZGVzXG4gICAgXG4gICAgICAgQ29sbGVjdHMgYWxsIHN1Ym5vZGVzIGZvciBhIHNwZWNpZmllZCBub2RlLiBcbiAgICAgICBUaGUgKmxldmVsKiBwYXJhbWV0ZXIgZmlsdGVycyBub2RlcyBoYXZpbmcgcmVsYXRpdmUgZGVwdGggb2YgKmxldmVsKiBmcm9tIHRoZSByb290IG5vZGUuIFxuICAgICAgIFxuICAgICAgIEFsc28gaW1wbGVtZW50ZWQgYnk6XG4gICAgICAgXG4gICAgICAgPEdyYXBoLk5vZGU+LlxuXG4gICAgICAgUGFyYW1ldGVyczpcbiAgICAgICBub2RlIC0gKG9iamVjdCkgQSA8R3JhcGguTm9kZT4uXG4gICAgICAgbGV2ZWwgLSAob3B0aW9uYWx8bnVtYmVyKSBEZWZhdWx0J3MgKjAqLiBBIHN0YXJ0aW5nIHJlbGF0aXZlIGRlcHRoIGZvciBjb2xsZWN0aW5nIG5vZGVzLlxuXG4gICAgICAgUmV0dXJuczpcbiAgICAgICBBbiBhcnJheSBvZiBub2Rlcy5cblxuICAgICovXG4gICAgZ2V0U3Vibm9kZXM6IGZ1bmN0aW9uKG5vZGUsIGxldmVsLCBmbGFncykge1xuICAgICAgICB2YXIgYW5zID0gW10sIHRoYXQgPSB0aGlzO1xuICAgICAgICBsZXZlbCA9IGxldmVsIHx8IDA7XG4gICAgICAgIHZhciBsZXZlbFN0YXJ0LCBsZXZlbEVuZDtcbiAgICAgICAgaWYoJC50eXBlKGxldmVsKSA9PSAnYXJyYXknKSB7XG4gICAgICAgICAgICBsZXZlbFN0YXJ0ID0gbGV2ZWxbMF07XG4gICAgICAgICAgICBsZXZlbEVuZCA9IGxldmVsWzFdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbGV2ZWxTdGFydCA9IGxldmVsO1xuICAgICAgICAgICAgbGV2ZWxFbmQgPSBOdW1iZXIuTUFYX1ZBTFVFIC0gbm9kZS5fZGVwdGg7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5lYWNoTGV2ZWwobm9kZSwgbGV2ZWxTdGFydCwgbGV2ZWxFbmQsIGZ1bmN0aW9uKG4pIHtcbiAgICAgICAgICAgIGFucy5wdXNoKG4pO1xuICAgICAgICB9LCBmbGFncyk7XG4gICAgICAgIHJldHVybiBhbnM7XG4gICAgfSxcbiAgXG4gIFxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBnZXRQYXJlbnRzXG4gICAgXG4gICAgICAgUmV0dXJucyBhbiBBcnJheSBvZiA8R3JhcGguTm9kZXM+IHdoaWNoIGFyZSBwYXJlbnRzIG9mIHRoZSBnaXZlbiBub2RlLlxuICAgICAgIFxuICAgICAgIEFsc28gaW1wbGVtZW50ZWQgYnk6XG4gICAgICAgXG4gICAgICAgPEdyYXBoLk5vZGU+LlxuXG4gICAgICAgUGFyYW1ldGVyczpcbiAgICAgICBub2RlIC0gKG9iamVjdCkgQSA8R3JhcGguTm9kZT4uXG5cbiAgICAgICBSZXR1cm5zOlxuICAgICAgIEFuIEFycmF5IG9mIDxHcmFwaC5Ob2Rlcz4uXG5cbiAgICAgICBFeGFtcGxlOlxuICAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICAgdmFyIHBhcnMgPSAkaml0LkdyYXBoLlV0aWwuZ2V0UGFyZW50cyhub2RlKTtcbiAgICAgICAgIC8vb3IuLi5cbiAgICAgICAgIHZhciBwYXJzID0gbm9kZS5nZXRQYXJlbnRzKCk7XG4gICAgICAgICBcbiAgICAgICAgIGlmKHBhcnMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAvL2RvIHN0dWZmIHdpdGggcGFyZW50c1xuICAgICAgICAgfVxuICAgICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgIGdldFBhcmVudHM6IGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgICAgdmFyIGFucyA9IFtdO1xuICAgICAgICB0aGlzLmVhY2hBZGphY2VuY3kobm9kZSwgZnVuY3Rpb24oYWRqKSB7XG4gICAgICAgICAgICB2YXIgbiA9IGFkai5ub2RlVG87XG4gICAgICAgICAgICBpZihuLl9kZXB0aCA8IG5vZGUuX2RlcHRoKSBhbnMucHVzaChuKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBhbnM7XG4gICAgfSxcbiAgICBcbiAgICAvKlxuICAgIE1ldGhvZDogaXNEZXNjZW5kYW50T2ZcbiBcbiAgICBSZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIGlmIHNvbWUgbm9kZSBpcyBkZXNjZW5kYW50IG9mIHRoZSBub2RlIHdpdGggdGhlIGdpdmVuIGlkLiBcblxuICAgIEFsc28gaW1wbGVtZW50ZWQgYnk6XG4gICAgXG4gICAgPEdyYXBoLk5vZGU+LlxuICAgIFxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICAgbm9kZSAtIChvYmplY3QpIEEgPEdyYXBoLk5vZGU+LlxuICAgIGlkIC0gKHN0cmluZykgQSA8R3JhcGguTm9kZT4gaWQuXG5cbiAgICBFeGFtcGxlOlxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgJGppdC5HcmFwaC5VdGlsLmlzRGVzY2VuZGFudE9mKG5vZGUsIFwibm9kZWlkXCIpOyAvL3RydWV8ZmFsc2VcbiAgICAgIC8vb3IuLi5cbiAgICAgIG5vZGUuaXNEZXNjZW5kYW50T2YoJ25vZGVpZCcpOy8vdHJ1ZXxmYWxzZVxuICAgIChlbmQgY29kZSlcbiAqL1xuIGlzRGVzY2VuZGFudE9mOiBmdW5jdGlvbihub2RlLCBpZCkge1xuICAgIGlmKG5vZGUuaWQgPT0gaWQpIHJldHVybiB0cnVlO1xuICAgIHZhciBwYXJzID0gdGhpcy5nZXRQYXJlbnRzKG5vZGUpLCBhbnMgPSBmYWxzZTtcbiAgICBmb3IgKCB2YXIgaSA9IDA7ICFhbnMgJiYgaSA8IHBhcnMubGVuZ3RoOyBpKyspIHtcbiAgICBhbnMgPSBhbnMgfHwgdGhpcy5pc0Rlc2NlbmRhbnRPZihwYXJzW2ldLCBpZCk7XG4gIH1cbiAgICByZXR1cm4gYW5zO1xuIH0sXG5cbiAvKlxuICAgICBNZXRob2Q6IGNsZWFuXG4gIFxuICAgICBDbGVhbnMgZmxhZ3MgZnJvbSBub2Rlcy5cblxuICAgICBBbHNvIGltcGxlbWVudGVkIGJ5OlxuICAgICBcbiAgICAgPEdyYXBoPi5cbiAgICAgXG4gICAgIFBhcmFtZXRlcnM6XG4gICAgIGdyYXBoIC0gQSA8R3JhcGg+IGluc3RhbmNlLlxuICAqL1xuICBjbGVhbjogZnVuY3Rpb24oZ3JhcGgpIHsgdGhpcy5lYWNoTm9kZShncmFwaCwgZnVuY3Rpb24oZWxlbSkgeyBlbGVtLl9mbGFnID0gZmFsc2U7IH0pOyB9LFxuICBcbiAgLyogXG4gICAgTWV0aG9kOiBnZXRDbG9zZXN0Tm9kZVRvT3JpZ2luIFxuICBcbiAgICBSZXR1cm5zIHRoZSBjbG9zZXN0IG5vZGUgdG8gdGhlIGNlbnRlciBvZiBjYW52YXMuXG4gIFxuICAgIEFsc28gaW1wbGVtZW50ZWQgYnk6XG4gICAgXG4gICAgPEdyYXBoPi5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgXG4gICAgIGdyYXBoIC0gKG9iamVjdCkgQSA8R3JhcGg+IGluc3RhbmNlLlxuICAgICBwcm9wIC0gKG9wdGlvbmFsfHN0cmluZykgRGVmYXVsdCdzICdjdXJyZW50Jy4gQSA8R3JhcGguTm9kZT4gcG9zaXRpb24gcHJvcGVydHkuIFBvc3NpYmxlIHByb3BlcnRpZXMgYXJlICdzdGFydCcsICdjdXJyZW50JyBvciAnZW5kJy5cbiAgXG4gICovXG4gIGdldENsb3Nlc3ROb2RlVG9PcmlnaW46IGZ1bmN0aW9uKGdyYXBoLCBwcm9wLCBmbGFncykge1xuICAgcmV0dXJuIHRoaXMuZ2V0Q2xvc2VzdE5vZGVUb1BvcyhncmFwaCwgUG9sYXIuS0VSLCBwcm9wLCBmbGFncyk7XG4gIH0sXG4gIFxuICAvKiBcbiAgICBNZXRob2Q6IGdldENsb3Nlc3ROb2RlVG9Qb3NcbiAgXG4gICAgUmV0dXJucyB0aGUgY2xvc2VzdCBub2RlIHRvIHRoZSBnaXZlbiBwb3NpdGlvbi5cbiAgXG4gICAgQWxzbyBpbXBsZW1lbnRlZCBieTpcbiAgICBcbiAgICA8R3JhcGg+LlxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICBcbiAgICAgZ3JhcGggLSAob2JqZWN0KSBBIDxHcmFwaD4gaW5zdGFuY2UuXG4gICAgIHBvcyAtIChvYmplY3QpIEEgPENvbXBsZXg+IG9yIDxQb2xhcj4gaW5zdGFuY2UuXG4gICAgIHByb3AgLSAob3B0aW9uYWx8c3RyaW5nKSBEZWZhdWx0J3MgKmN1cnJlbnQqLiBBIDxHcmFwaC5Ob2RlPiBwb3NpdGlvbiBwcm9wZXJ0eS4gUG9zc2libGUgcHJvcGVydGllcyBhcmUgJ3N0YXJ0JywgJ2N1cnJlbnQnIG9yICdlbmQnLlxuICBcbiAgKi9cbiAgZ2V0Q2xvc2VzdE5vZGVUb1BvczogZnVuY3Rpb24oZ3JhcGgsIHBvcywgcHJvcCwgZmxhZ3MpIHtcbiAgIHZhciBub2RlID0gbnVsbDtcbiAgIHByb3AgPSBwcm9wIHx8ICdjdXJyZW50JztcbiAgIHBvcyA9IHBvcyAmJiBwb3MuZ2V0Yyh0cnVlKSB8fCBDb21wbGV4LktFUjtcbiAgIHZhciBkaXN0YW5jZSA9IGZ1bmN0aW9uKGEsIGIpIHtcbiAgICAgdmFyIGQxID0gYS54IC0gYi54LCBkMiA9IGEueSAtIGIueTtcbiAgICAgcmV0dXJuIGQxICogZDEgKyBkMiAqIGQyO1xuICAgfTtcbiAgIHRoaXMuZWFjaE5vZGUoZ3JhcGgsIGZ1bmN0aW9uKGVsZW0pIHtcbiAgICAgbm9kZSA9IChub2RlID09IG51bGwgfHwgZGlzdGFuY2UoZWxlbS5nZXRQb3MocHJvcCkuZ2V0Yyh0cnVlKSwgcG9zKSA8IGRpc3RhbmNlKFxuICAgICAgICAgbm9kZS5nZXRQb3MocHJvcCkuZ2V0Yyh0cnVlKSwgcG9zKSkgPyBlbGVtIDogbm9kZTtcbiAgIH0sIGZsYWdzKTtcbiAgIHJldHVybiBub2RlO1xuICB9IFxufTtcblxuLy9BcHBlbmQgZ3JhcGggbWV0aG9kcyB0byA8R3JhcGg+XG4kLmVhY2goWydnZXQnLCAnZ2V0Tm9kZScsICdlYWNoJywgJ2VhY2hOb2RlJywgJ2NvbXB1dGVMZXZlbHMnLCAnZWFjaEJGUycsICdjbGVhbicsICdnZXRDbG9zZXN0Tm9kZVRvUG9zJywgJ2dldENsb3Nlc3ROb2RlVG9PcmlnaW4nXSwgZnVuY3Rpb24obSkge1xuICBHcmFwaC5wcm90b3R5cGVbbV0gPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gR3JhcGguVXRpbFttXS5hcHBseShHcmFwaC5VdGlsLCBbdGhpc10uY29uY2F0KEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpKTtcbiAgfTtcbn0pO1xuXG4vL0FwcGVuZCBub2RlIG1ldGhvZHMgdG8gPEdyYXBoLk5vZGU+XG4kLmVhY2goWydlYWNoQWRqYWNlbmN5JywgJ2VhY2hMZXZlbCcsICdlYWNoU3ViZ3JhcGgnLCAnZWFjaFN1Ym5vZGUnLCAnYW55U3Vibm9kZScsICdnZXRTdWJub2RlcycsICdnZXRQYXJlbnRzJywgJ2lzRGVzY2VuZGFudE9mJ10sIGZ1bmN0aW9uKG0pIHtcbiAgR3JhcGguTm9kZS5wcm90b3R5cGVbbV0gPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gR3JhcGguVXRpbFttXS5hcHBseShHcmFwaC5VdGlsLCBbdGhpc10uY29uY2F0KEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpKTtcbiAgfTtcbn0pO1xuXG4vKlxuICogRmlsZTogR3JhcGguT3AuanNcbiAqXG4qL1xuXG4vKlxuICAgT2JqZWN0OiBHcmFwaC5PcFxuXG4gICBQZXJmb3JtIDxHcmFwaD4gb3BlcmF0aW9ucyBsaWtlIGFkZGluZy9yZW1vdmluZyA8R3JhcGguTm9kZXM+IG9yIDxHcmFwaC5BZGphY2VuY2VzPiwgXG4gICBtb3JwaGluZyBhIDxHcmFwaD4gaW50byBhbm90aGVyIDxHcmFwaD4sIGNvbnRyYWN0aW5nIG9yIGV4cGFuZGluZyBzdWJ0cmVlcywgZXRjLlxuXG4qL1xuR3JhcGguT3AgPSB7XG5cbiAgICBvcHRpb25zOiB7XG4gICAgICB0eXBlOiAnbm90aGluZycsXG4gICAgICBkdXJhdGlvbjogMjAwMCxcbiAgICAgIGhpZGVMYWJlbHM6IHRydWUsXG4gICAgICBmcHM6MzBcbiAgICB9LFxuICAgIFxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKHZpeikge1xuICAgICAgdGhpcy52aXogPSB2aXo7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiByZW1vdmVOb2RlXG4gICAgXG4gICAgICAgUmVtb3ZlcyBvbmUgb3IgbW9yZSA8R3JhcGguTm9kZXM+IGZyb20gdGhlIHZpc3VhbGl6YXRpb24uIFxuICAgICAgIEl0IGNhbiBhbHNvIHBlcmZvcm0gc2V2ZXJhbCBhbmltYXRpb25zIGxpa2UgZmFkaW5nIHNlcXVlbnRpYWxseSwgZmFkaW5nIGNvbmN1cnJlbnRseSwgaXRlcmF0aW5nIG9yIHJlcGxvdHRpbmcuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgICAgICBub2RlIC0gKHN0cmluZ3xhcnJheSkgVGhlIG5vZGUncyBpZC4gQ2FuIGFsc28gYmUgYW4gYXJyYXkgaGF2aW5nIG1hbnkgaWRzLlxuICAgICAgICBvcHQgLSAob2JqZWN0KSBBbmltYXRpb24gb3B0aW9ucy4gSXQncyBhbiBvYmplY3Qgd2l0aCBvcHRpb25hbCBwcm9wZXJ0aWVzIGRlc2NyaWJlZCBiZWxvd1xuICAgICAgICB0eXBlIC0gKHN0cmluZykgRGVmYXVsdCdzICpub3RoaW5nKi4gVHlwZSBvZiB0aGUgYW5pbWF0aW9uLiBDYW4gYmUgXCJub3RoaW5nXCIsIFwicmVwbG90XCIsIFwiZmFkZTpzZXFcIiwgIFwiZmFkZTpjb25cIiBvciBcIml0ZXJcIi5cbiAgICAgICAgZHVyYXRpb24gLSBEZXNjcmliZWQgaW4gPE9wdGlvbnMuRng+LlxuICAgICAgICBmcHMgLSBEZXNjcmliZWQgaW4gPE9wdGlvbnMuRng+LlxuICAgICAgICB0cmFuc2l0aW9uIC0gRGVzY3JpYmVkIGluIDxPcHRpb25zLkZ4Pi5cbiAgICAgICAgaGlkZUxhYmVscyAtIChib29sZWFuKSBEZWZhdWx0J3MgKnRydWUqLiBIaWRlIGxhYmVscyBkdXJpbmcgdGhlIGFuaW1hdGlvbi5cbiAgIFxuICAgICAgRXhhbXBsZTpcbiAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICB2YXIgdml6ID0gbmV3ICRqaXQuVml6KG9wdGlvbnMpO1xuICAgICAgICB2aXoub3AucmVtb3ZlTm9kZSgnbm9kZUlkJywge1xuICAgICAgICAgIHR5cGU6ICdmYWRlOnNlcScsXG4gICAgICAgICAgZHVyYXRpb246IDEwMDAsXG4gICAgICAgICAgaGlkZUxhYmVsczogZmFsc2UsXG4gICAgICAgICAgdHJhbnNpdGlvbjogJGppdC5UcmFucy5RdWFydC5lYXNlT3V0XG4gICAgICAgIH0pO1xuICAgICAgICAvL29yIGFsc29cbiAgICAgICAgdml6Lm9wLnJlbW92ZU5vZGUoWydzb21lSWQnLCAnb3RoZXJJZCddLCB7XG4gICAgICAgICAgdHlwZTogJ2ZhZGU6Y29uJyxcbiAgICAgICAgICBkdXJhdGlvbjogMTUwMFxuICAgICAgICB9KTtcbiAgICAgIChlbmQgY29kZSlcbiAgICAqL1xuICBcbiAgICByZW1vdmVOb2RlOiBmdW5jdGlvbihub2RlLCBvcHQpIHtcbiAgICAgICAgdmFyIHZpeiA9IHRoaXMudml6O1xuICAgICAgICB2YXIgb3B0aW9ucyA9ICQubWVyZ2UodGhpcy5vcHRpb25zLCB2aXouY29udHJvbGxlciwgb3B0KTtcbiAgICAgICAgdmFyIG4gPSAkLnNwbGF0KG5vZGUpO1xuICAgICAgICB2YXIgaSwgdGhhdCwgbm9kZU9iajtcbiAgICAgICAgc3dpdGNoKG9wdGlvbnMudHlwZSkge1xuICAgICAgICAgICAgY2FzZSAnbm90aGluZyc6XG4gICAgICAgICAgICAgICAgZm9yKGk9MDsgaTxuLmxlbmd0aDsgaSsrKSB2aXouZ3JhcGgucmVtb3ZlTm9kZShuW2ldKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgY2FzZSAncmVwbG90JzpcbiAgICAgICAgICAgICAgICB0aGlzLnJlbW92ZU5vZGUobiwgeyB0eXBlOiAnbm90aGluZycgfSk7XG4gICAgICAgICAgICAgICAgdml6LmxhYmVscy5jbGVhckxhYmVscygpO1xuICAgICAgICAgICAgICAgIHZpei5yZWZyZXNoKHRydWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBjYXNlICdmYWRlOnNlcSc6IGNhc2UgJ2ZhZGUnOlxuICAgICAgICAgICAgICAgIHRoYXQgPSB0aGlzO1xuICAgICAgICAgICAgICAgIC8vc2V0IGFscGhhIHRvIDAgZm9yIG5vZGVzIHRvIHJlbW92ZS5cbiAgICAgICAgICAgICAgICBmb3IoaT0wOyBpPG4ubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgbm9kZU9iaiA9IHZpei5ncmFwaC5nZXROb2RlKG5baV0pO1xuICAgICAgICAgICAgICAgICAgICBub2RlT2JqLnNldERhdGEoJ2FscGhhJywgMCwgJ2VuZCcpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2aXouZnguYW5pbWF0ZSgkLm1lcmdlKG9wdGlvbnMsIHtcbiAgICAgICAgICAgICAgICAgICAgbW9kZXM6IFsnbm9kZS1wcm9wZXJ0eTphbHBoYSddLFxuICAgICAgICAgICAgICAgICAgICBvbkNvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoYXQucmVtb3ZlTm9kZShuLCB7IHR5cGU6ICdub3RoaW5nJyB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZpei5sYWJlbHMuY2xlYXJMYWJlbHMoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZpei5yZXBvc2l0aW9uKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2aXouZnguYW5pbWF0ZSgkLm1lcmdlKG9wdGlvbnMsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlczogWydsaW5lYXInXVxuICAgICAgICAgICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBjYXNlICdmYWRlOmNvbic6XG4gICAgICAgICAgICAgICAgdGhhdCA9IHRoaXM7XG4gICAgICAgICAgICAgICAgLy9zZXQgYWxwaGEgdG8gMCBmb3Igbm9kZXMgdG8gcmVtb3ZlLiBUYWcgdGhlbSBmb3IgYmVpbmcgaWdub3JlZCBvbiBjb21wdXRpbmcgcG9zaXRpb25zLlxuICAgICAgICAgICAgICAgIGZvcihpPTA7IGk8bi5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICBub2RlT2JqID0gdml6LmdyYXBoLmdldE5vZGUobltpXSk7XG4gICAgICAgICAgICAgICAgICAgIG5vZGVPYmouc2V0RGF0YSgnYWxwaGEnLCAwLCAnZW5kJyk7XG4gICAgICAgICAgICAgICAgICAgIG5vZGVPYmouaWdub3JlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdml6LnJlcG9zaXRpb24oKTtcbiAgICAgICAgICAgICAgICB2aXouZnguYW5pbWF0ZSgkLm1lcmdlKG9wdGlvbnMsIHtcbiAgICAgICAgICAgICAgICAgICAgbW9kZXM6IFsnbm9kZS1wcm9wZXJ0eTphbHBoYScsICdsaW5lYXInXSxcbiAgICAgICAgICAgICAgICAgICAgb25Db21wbGV0ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGF0LnJlbW92ZU5vZGUobiwgeyB0eXBlOiAnbm90aGluZycgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBvcHRpb25zLm9uQ29tcGxldGUgJiYgb3B0aW9ucy5vbkNvbXBsZXRlKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGNhc2UgJ2l0ZXInOlxuICAgICAgICAgICAgICAgIHRoYXQgPSB0aGlzO1xuICAgICAgICAgICAgICAgIHZpei5meC5zZXF1ZW5jZSh7XG4gICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbjogZnVuY3Rpb24oKSB7IHJldHVybiBuLmxlbmd0aCAhPSAwOyB9LFxuICAgICAgICAgICAgICAgICAgICBzdGVwOiBmdW5jdGlvbigpIHsgdGhhdC5yZW1vdmVOb2RlKG4uc2hpZnQoKSwgeyB0eXBlOiAnbm90aGluZycgfSk7ICB2aXoubGFiZWxzLmNsZWFyTGFiZWxzKCk7IH0sXG4gICAgICAgICAgICAgICAgICAgIG9uQ29tcGxldGU6IGZ1bmN0aW9uKCkgeyBvcHRpb25zLm9uQ29tcGxldGUgJiYgb3B0aW9ucy5vbkNvbXBsZXRlKCk7IH0sXG4gICAgICAgICAgICAgICAgICAgIGR1cmF0aW9uOiBNYXRoLmNlaWwob3B0aW9ucy5kdXJhdGlvbiAvIG4ubGVuZ3RoKVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgZGVmYXVsdDogdGhpcy5kb0Vycm9yKCk7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIFxuICAgIC8qXG4gICAgICAgTWV0aG9kOiByZW1vdmVFZGdlXG4gICAgXG4gICAgICAgUmVtb3ZlcyBvbmUgb3IgbW9yZSA8R3JhcGguQWRqYWNlbmNlcz4gZnJvbSB0aGUgdmlzdWFsaXphdGlvbi4gXG4gICAgICAgSXQgY2FuIGFsc28gcGVyZm9ybSBzZXZlcmFsIGFuaW1hdGlvbnMgbGlrZSBmYWRpbmcgc2VxdWVudGlhbGx5LCBmYWRpbmcgY29uY3VycmVudGx5LCBpdGVyYXRpbmcgb3IgcmVwbG90dGluZy5cblxuICAgICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgICAgdmVydGV4IC0gKGFycmF5KSBBbiBhcnJheSBoYXZpbmcgdHdvIHN0cmluZ3Mgd2hpY2ggYXJlIHRoZSBpZHMgb2YgdGhlIG5vZGVzIGNvbm5lY3RlZCBieSB0aGlzIGVkZ2UgKGkuZSBbJ2lkMScsICdpZDInXSkuIENhbiBhbHNvIGJlIGEgdHdvIGRpbWVuc2lvbmFsIGFycmF5IGhvbGRpbmcgbWFueSBlZGdlcyAoaS5lIFtbJ2lkMScsICdpZDInXSwgWydpZDMnLCAnaWQ0J10sIC4uLl0pLlxuICAgICAgIG9wdCAtIChvYmplY3QpIEFuaW1hdGlvbiBvcHRpb25zLiBJdCdzIGFuIG9iamVjdCB3aXRoIG9wdGlvbmFsIHByb3BlcnRpZXMgZGVzY3JpYmVkIGJlbG93XG4gICAgICAgdHlwZSAtIChzdHJpbmcpIERlZmF1bHQncyAqbm90aGluZyouIFR5cGUgb2YgdGhlIGFuaW1hdGlvbi4gQ2FuIGJlIFwibm90aGluZ1wiLCBcInJlcGxvdFwiLCBcImZhZGU6c2VxXCIsICBcImZhZGU6Y29uXCIgb3IgXCJpdGVyXCIuXG4gICAgICAgZHVyYXRpb24gLSBEZXNjcmliZWQgaW4gPE9wdGlvbnMuRng+LlxuICAgICAgIGZwcyAtIERlc2NyaWJlZCBpbiA8T3B0aW9ucy5GeD4uXG4gICAgICAgdHJhbnNpdGlvbiAtIERlc2NyaWJlZCBpbiA8T3B0aW9ucy5GeD4uXG4gICAgICAgaGlkZUxhYmVscyAtIChib29sZWFuKSBEZWZhdWx0J3MgKnRydWUqLiBIaWRlIGxhYmVscyBkdXJpbmcgdGhlIGFuaW1hdGlvbi5cbiAgIFxuICAgICAgRXhhbXBsZTpcbiAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICB2YXIgdml6ID0gbmV3ICRqaXQuVml6KG9wdGlvbnMpO1xuICAgICAgICB2aXoub3AucmVtb3ZlRWRnZShbJ25vZGVJZCcsICdvdGhlcklkJ10sIHtcbiAgICAgICAgICB0eXBlOiAnZmFkZTpzZXEnLFxuICAgICAgICAgIGR1cmF0aW9uOiAxMDAwLFxuICAgICAgICAgIGhpZGVMYWJlbHM6IGZhbHNlLFxuICAgICAgICAgIHRyYW5zaXRpb246ICRqaXQuVHJhbnMuUXVhcnQuZWFzZU91dFxuICAgICAgICB9KTtcbiAgICAgICAgLy9vciBhbHNvXG4gICAgICAgIHZpei5vcC5yZW1vdmVFZGdlKFtbJ3NvbWVJZCcsICdvdGhlcklkJ10sIFsnaWQzJywgJ2lkNCddXSwge1xuICAgICAgICAgIHR5cGU6ICdmYWRlOmNvbicsXG4gICAgICAgICAgZHVyYXRpb246IDE1MDBcbiAgICAgICAgfSk7XG4gICAgICAoZW5kIGNvZGUpXG4gICAgXG4gICAgKi9cbiAgICByZW1vdmVFZGdlOiBmdW5jdGlvbih2ZXJ0ZXgsIG9wdCkge1xuICAgICAgICB2YXIgdml6ID0gdGhpcy52aXo7XG4gICAgICAgIHZhciBvcHRpb25zID0gJC5tZXJnZSh0aGlzLm9wdGlvbnMsIHZpei5jb250cm9sbGVyLCBvcHQpO1xuICAgICAgICB2YXIgdiA9ICgkLnR5cGUodmVydGV4WzBdKSA9PSAnc3RyaW5nJyk/IFt2ZXJ0ZXhdIDogdmVydGV4O1xuICAgICAgICB2YXIgaSwgdGhhdCwgYWRqO1xuICAgICAgICBzd2l0Y2gob3B0aW9ucy50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdub3RoaW5nJzpcbiAgICAgICAgICAgICAgICBmb3IoaT0wOyBpPHYubGVuZ3RoOyBpKyspICAgdml6LmdyYXBoLnJlbW92ZUFkamFjZW5jZSh2W2ldWzBdLCB2W2ldWzFdKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgY2FzZSAncmVwbG90JzpcbiAgICAgICAgICAgICAgICB0aGlzLnJlbW92ZUVkZ2UodiwgeyB0eXBlOiAnbm90aGluZycgfSk7XG4gICAgICAgICAgICAgICAgdml6LnJlZnJlc2godHJ1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGNhc2UgJ2ZhZGU6c2VxJzogY2FzZSAnZmFkZSc6XG4gICAgICAgICAgICAgICAgdGhhdCA9IHRoaXM7XG4gICAgICAgICAgICAgICAgLy9zZXQgYWxwaGEgdG8gMCBmb3IgZWRnZXMgdG8gcmVtb3ZlLlxuICAgICAgICAgICAgICAgIGZvcihpPTA7IGk8di5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICBhZGogPSB2aXouZ3JhcGguZ2V0QWRqYWNlbmNlKHZbaV1bMF0sIHZbaV1bMV0pO1xuICAgICAgICAgICAgICAgICAgICBpZihhZGopIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFkai5zZXREYXRhKCdhbHBoYScsIDAsJ2VuZCcpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHZpei5meC5hbmltYXRlKCQubWVyZ2Uob3B0aW9ucywge1xuICAgICAgICAgICAgICAgICAgICBtb2RlczogWydlZGdlLXByb3BlcnR5OmFscGhhJ10sXG4gICAgICAgICAgICAgICAgICAgIG9uQ29tcGxldGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhhdC5yZW1vdmVFZGdlKHYsIHsgdHlwZTogJ25vdGhpbmcnIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgdml6LnJlcG9zaXRpb24oKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZpei5meC5hbmltYXRlKCQubWVyZ2Uob3B0aW9ucywge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVzOiBbJ2xpbmVhciddXG4gICAgICAgICAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGNhc2UgJ2ZhZGU6Y29uJzpcbiAgICAgICAgICAgICAgICB0aGF0ID0gdGhpcztcbiAgICAgICAgICAgICAgICAvL3NldCBhbHBoYSB0byAwIGZvciBub2RlcyB0byByZW1vdmUuIFRhZyB0aGVtIGZvciBiZWluZyBpZ25vcmVkIHdoZW4gY29tcHV0aW5nIHBvc2l0aW9ucy5cbiAgICAgICAgICAgICAgICBmb3IoaT0wOyBpPHYubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgYWRqID0gdml6LmdyYXBoLmdldEFkamFjZW5jZSh2W2ldWzBdLCB2W2ldWzFdKTtcbiAgICAgICAgICAgICAgICAgICAgaWYoYWRqKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBhZGouc2V0RGF0YSgnYWxwaGEnLDAgLCdlbmQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFkai5pZ25vcmUgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHZpei5yZXBvc2l0aW9uKCk7XG4gICAgICAgICAgICAgICAgdml6LmZ4LmFuaW1hdGUoJC5tZXJnZShvcHRpb25zLCB7XG4gICAgICAgICAgICAgICAgICAgIG1vZGVzOiBbJ2VkZ2UtcHJvcGVydHk6YWxwaGEnLCAnbGluZWFyJ10sXG4gICAgICAgICAgICAgICAgICAgIG9uQ29tcGxldGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhhdC5yZW1vdmVFZGdlKHYsIHsgdHlwZTogJ25vdGhpbmcnIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5vbkNvbXBsZXRlICYmIG9wdGlvbnMub25Db21wbGV0ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBjYXNlICdpdGVyJzpcbiAgICAgICAgICAgICAgICB0aGF0ID0gdGhpcztcbiAgICAgICAgICAgICAgICB2aXouZnguc2VxdWVuY2Uoe1xuICAgICAgICAgICAgICAgICAgICBjb25kaXRpb246IGZ1bmN0aW9uKCkgeyByZXR1cm4gdi5sZW5ndGggIT0gMDsgfSxcbiAgICAgICAgICAgICAgICAgICAgc3RlcDogZnVuY3Rpb24oKSB7IHRoYXQucmVtb3ZlRWRnZSh2LnNoaWZ0KCksIHsgdHlwZTogJ25vdGhpbmcnIH0pOyB2aXoubGFiZWxzLmNsZWFyTGFiZWxzKCk7IH0sXG4gICAgICAgICAgICAgICAgICAgIG9uQ29tcGxldGU6IGZ1bmN0aW9uKCkgeyBvcHRpb25zLm9uQ29tcGxldGUoKTsgfSxcbiAgICAgICAgICAgICAgICAgICAgZHVyYXRpb246IE1hdGguY2VpbChvcHRpb25zLmR1cmF0aW9uIC8gdi5sZW5ndGgpXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICBkZWZhdWx0OiB0aGlzLmRvRXJyb3IoKTtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHN1bVxuICAgIFxuICAgICAgIEFkZHMgYSBuZXcgZ3JhcGggdG8gdGhlIHZpc3VhbGl6YXRpb24uIFxuICAgICAgIFRoZSBKU09OIGdyYXBoIChvciB0cmVlKSBtdXN0IGF0IGxlYXN0IGhhdmUgYSBjb21tb24gbm9kZSB3aXRoIHRoZSBjdXJyZW50IGdyYXBoIHBsb3R0ZWQgYnkgdGhlIHZpc3VhbGl6YXRpb24uIFxuICAgICAgIFRoZSByZXN1bHRpbmcgZ3JhcGggY2FuIGJlIGRlZmluZWQgYXMgZm9sbG93cyA8aHR0cDovL21hdGh3b3JsZC53b2xmcmFtLmNvbS9HcmFwaFN1bS5odG1sPlxuXG4gICAgICAgUGFyYW1ldGVyczpcbiAgICBcbiAgICAgICBqc29uIC0gKG9iamVjdCkgQSBqc29uIHRyZWUgb3IgZ3JhcGggc3RydWN0dXJlLiBTZWUgYWxzbyA8TG9hZGVyLmxvYWRKU09OPi5cbiAgICAgICBvcHQgLSAob2JqZWN0KSBBbmltYXRpb24gb3B0aW9ucy4gSXQncyBhbiBvYmplY3Qgd2l0aCBvcHRpb25hbCBwcm9wZXJ0aWVzIGRlc2NyaWJlZCBiZWxvd1xuICAgICAgIHR5cGUgLSAoc3RyaW5nKSBEZWZhdWx0J3MgKm5vdGhpbmcqLiBUeXBlIG9mIHRoZSBhbmltYXRpb24uIENhbiBiZSBcIm5vdGhpbmdcIiwgXCJyZXBsb3RcIiwgXCJmYWRlOnNlcVwiLCAgXCJmYWRlOmNvblwiLlxuICAgICAgIGR1cmF0aW9uIC0gRGVzY3JpYmVkIGluIDxPcHRpb25zLkZ4Pi5cbiAgICAgICBmcHMgLSBEZXNjcmliZWQgaW4gPE9wdGlvbnMuRng+LlxuICAgICAgIHRyYW5zaXRpb24gLSBEZXNjcmliZWQgaW4gPE9wdGlvbnMuRng+LlxuICAgICAgIGhpZGVMYWJlbHMgLSAoYm9vbGVhbikgRGVmYXVsdCdzICp0cnVlKi4gSGlkZSBsYWJlbHMgZHVyaW5nIHRoZSBhbmltYXRpb24uXG4gICBcbiAgICAgIEV4YW1wbGU6XG4gICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgLy8uLi5qc29uIGNvbnRhaW5zIGEgdHJlZSBvciBncmFwaCBzdHJ1Y3R1cmUuLi5cblxuICAgICAgICB2YXIgdml6ID0gbmV3ICRqaXQuVml6KG9wdGlvbnMpO1xuICAgICAgICB2aXoub3Auc3VtKGpzb24sIHtcbiAgICAgICAgICB0eXBlOiAnZmFkZTpzZXEnLFxuICAgICAgICAgIGR1cmF0aW9uOiAxMDAwLFxuICAgICAgICAgIGhpZGVMYWJlbHM6IGZhbHNlLFxuICAgICAgICAgIHRyYW5zaXRpb246ICRqaXQuVHJhbnMuUXVhcnQuZWFzZU91dFxuICAgICAgICB9KTtcbiAgICAgICAgLy9vciBhbHNvXG4gICAgICAgIHZpei5vcC5zdW0oanNvbiwge1xuICAgICAgICAgIHR5cGU6ICdmYWRlOmNvbicsXG4gICAgICAgICAgZHVyYXRpb246IDE1MDBcbiAgICAgICAgfSk7XG4gICAgICAoZW5kIGNvZGUpXG4gICAgXG4gICAgKi9cbiAgICBzdW06IGZ1bmN0aW9uKGpzb24sIG9wdCkge1xuICAgICAgICB2YXIgdml6ID0gdGhpcy52aXo7XG4gICAgICAgIHZhciBvcHRpb25zID0gJC5tZXJnZSh0aGlzLm9wdGlvbnMsIHZpei5jb250cm9sbGVyLCBvcHQpLCByb290ID0gdml6LnJvb3Q7XG4gICAgICAgIHZhciBncmFwaDtcbiAgICAgICAgdml6LnJvb3QgPSBvcHQuaWQgfHwgdml6LnJvb3Q7XG4gICAgICAgIHN3aXRjaChvcHRpb25zLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ25vdGhpbmcnOlxuICAgICAgICAgICAgICAgIGdyYXBoID0gdml6LmNvbnN0cnVjdChqc29uKTtcbiAgICAgICAgICAgICAgICBncmFwaC5lYWNoTm9kZShmdW5jdGlvbihlbGVtKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uZWFjaEFkamFjZW5jeShmdW5jdGlvbihhZGopIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZpei5ncmFwaC5hZGRBZGphY2VuY2UoYWRqLm5vZGVGcm9tLCBhZGoubm9kZVRvLCBhZGouZGF0YSk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBjYXNlICdyZXBsb3QnOlxuICAgICAgICAgICAgICAgIHZpei5yZWZyZXNoKHRydWUpO1xuICAgICAgICAgICAgICAgIHRoaXMuc3VtKGpzb24sIHsgdHlwZTogJ25vdGhpbmcnIH0pO1xuICAgICAgICAgICAgICAgIHZpei5yZWZyZXNoKHRydWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBjYXNlICdmYWRlOnNlcSc6IGNhc2UgJ2ZhZGUnOiBjYXNlICdmYWRlOmNvbic6XG4gICAgICAgICAgICAgICAgLy8gU1RBUlQgTUVUQU1BUFMgQ09ERVxuICAgICAgICAgICAgICAgIHZhciB0aGF0ID0gdGhpcztcbiAgICAgICAgICAgICAgICAvLyBPUklHSU5BTCBDT0RFOlxuICAgICAgICAgICAgICAgIC8vIHRoYXQgPSB0aGlzO1xuICAgICAgICAgICAgICAgIC8vIEVORCBNRVRBTUFQUyBDT0RFXG4gICAgICAgICAgICAgICAgZ3JhcGggPSB2aXouY29uc3RydWN0KGpzb24pO1xuXG4gICAgICAgICAgICAgICAgLy9zZXQgYWxwaGEgdG8gMCBmb3Igbm9kZXMgdG8gYWRkLlxuICAgICAgICAgICAgICAgIHZhciBmYWRlRWRnZXMgPSB0aGlzLnByZXByb2Nlc3NTdW0oZ3JhcGgpO1xuICAgICAgICAgICAgICAgIHZhciBtb2RlcyA9ICFmYWRlRWRnZXM/IFsnbm9kZS1wcm9wZXJ0eTphbHBoYSddIDogWydub2RlLXByb3BlcnR5OmFscGhhJywgJ2VkZ2UtcHJvcGVydHk6YWxwaGEnXTtcbiAgICAgICAgICAgICAgICB2aXoucmVwb3NpdGlvbigpO1xuICAgICAgICAgICAgICAgIGlmKG9wdGlvbnMudHlwZSAhPSAnZmFkZTpjb24nKSB7XG4gICAgICAgICAgICAgICAgICAgIHZpei5meC5hbmltYXRlKCQubWVyZ2Uob3B0aW9ucywge1xuICAgICAgICAgICAgICAgICAgICAgICAgbW9kZXM6IFsnbGluZWFyJ10sXG4gICAgICAgICAgICAgICAgICAgICAgICBvbkNvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aXouZnguYW5pbWF0ZSgkLm1lcmdlKG9wdGlvbnMsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZXM6IG1vZGVzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbkNvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdGlvbnMub25Db21wbGV0ZSgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdml6LmdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKGVsZW0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlbGVtLmlkICE9IHJvb3QgJiYgZWxlbS5wb3MuaXNaZXJvKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5wb3Muc2V0KGVsZW0uZW5kUG9zKTsgXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uc3RhcnRQb3Muc2V0KGVsZW0uZW5kUG9zKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIHZpei5meC5hbmltYXRlKCQubWVyZ2Uob3B0aW9ucywge1xuICAgICAgICAgICAgICAgICAgICAgICAgbW9kZXM6IFsnbGluZWFyJ10uY29uY2F0KG1vZGVzKVxuICAgICAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgICBkZWZhdWx0OiB0aGlzLmRvRXJyb3IoKTtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6IG1vcnBoXG4gICAgXG4gICAgICAgVGhpcyBtZXRob2Qgd2lsbCB0cmFuc2Zvcm0gdGhlIGN1cnJlbnQgdmlzdWFsaXplZCBncmFwaCBpbnRvIHRoZSBuZXcgSlNPTiByZXByZXNlbnRhdGlvbiBwYXNzZWQgaW4gdGhlIG1ldGhvZC4gXG4gICAgICAgVGhlIEpTT04gb2JqZWN0IG11c3QgYXQgbGVhc3QgaGF2ZSB0aGUgcm9vdCBub2RlIGluIGNvbW1vbiB3aXRoIHRoZSBjdXJyZW50IHZpc3VhbGl6ZWQgZ3JhcGguXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgICAgIGpzb24gLSAob2JqZWN0KSBBIGpzb24gdHJlZSBvciBncmFwaCBzdHJ1Y3R1cmUuIFNlZSBhbHNvIDxMb2FkZXIubG9hZEpTT04+LlxuICAgICAgIG9wdCAtIChvYmplY3QpIEFuaW1hdGlvbiBvcHRpb25zLiBJdCdzIGFuIG9iamVjdCB3aXRoIG9wdGlvbmFsIHByb3BlcnRpZXMgZGVzY3JpYmVkIGJlbG93XG4gICAgICAgdHlwZSAtIChzdHJpbmcpIERlZmF1bHQncyAqbm90aGluZyouIFR5cGUgb2YgdGhlIGFuaW1hdGlvbi4gQ2FuIGJlIFwibm90aGluZ1wiLCBcInJlcGxvdFwiLCBcImZhZGU6Y29uXCIuXG4gICAgICAgZHVyYXRpb24gLSBEZXNjcmliZWQgaW4gPE9wdGlvbnMuRng+LlxuICAgICAgIGZwcyAtIERlc2NyaWJlZCBpbiA8T3B0aW9ucy5GeD4uXG4gICAgICAgdHJhbnNpdGlvbiAtIERlc2NyaWJlZCBpbiA8T3B0aW9ucy5GeD4uXG4gICAgICAgaGlkZUxhYmVscyAtIChib29sZWFuKSBEZWZhdWx0J3MgKnRydWUqLiBIaWRlIGxhYmVscyBkdXJpbmcgdGhlIGFuaW1hdGlvbi5cbiAgICAgICBpZCAtIChzdHJpbmcpIFRoZSBzaGFyZWQgPEdyYXBoLk5vZGU+IGlkIGJldHdlZW4gYm90aCBncmFwaHMuXG4gICAgICAgXG4gICAgICAgZXh0cmFNb2RlcyAtIChvcHRpb25hbHxvYmplY3QpIFdoZW4gbW9ycGhpbmcgd2l0aCBhbiBhbmltYXRpb24sIGRvbGxhciBwcmVmaXhlZCBkYXRhIHBhcmFtZXRlcnMgYXJlIGFkZGVkIHRvIFxuICAgICAgICAgICAgICAgICAgICAqZW5kRGF0YSogYW5kIG5vdCAqZGF0YSogaXRzZWxmLiBUaGlzIHdheSB5b3UgY2FuIGFuaW1hdGUgZG9sbGFyIHByZWZpeGVkIHBhcmFtZXRlcnMgZHVyaW5nIHlvdXIgbW9ycGhpbmcgb3BlcmF0aW9uLiBcbiAgICAgICAgICAgICAgICAgICAgRm9yIGFuaW1hdGluZyB0aGVzZSBleHRyYS1wYXJhbWV0ZXJzIHlvdSBoYXZlIHRvIHNwZWNpZnkgYW4gb2JqZWN0IHRoYXQgaGFzIGFuaW1hdGlvbiBncm91cHMgYXMga2V5cyBhbmQgYW5pbWF0aW9uIFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzIGFzIHZhbHVlcywganVzdCBsaWtlIHNwZWNpZmllZCBpbiA8R3JhcGguUGxvdC5hbmltYXRlPi5cbiAgIFxuICAgICAgRXhhbXBsZTpcbiAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICAvLy4uLmpzb24gY29udGFpbnMgYSB0cmVlIG9yIGdyYXBoIHN0cnVjdHVyZS4uLlxuXG4gICAgICAgIHZhciB2aXogPSBuZXcgJGppdC5WaXoob3B0aW9ucyk7XG4gICAgICAgIHZpei5vcC5tb3JwaChqc29uLCB7XG4gICAgICAgICAgdHlwZTogJ2ZhZGUnLFxuICAgICAgICAgIGR1cmF0aW9uOiAxMDAwLFxuICAgICAgICAgIGhpZGVMYWJlbHM6IGZhbHNlLFxuICAgICAgICAgIHRyYW5zaXRpb246ICRqaXQuVHJhbnMuUXVhcnQuZWFzZU91dFxuICAgICAgICB9KTtcbiAgICAgICAgLy9vciBhbHNvXG4gICAgICAgIHZpei5vcC5tb3JwaChqc29uLCB7XG4gICAgICAgICAgdHlwZTogJ2ZhZGUnLFxuICAgICAgICAgIGR1cmF0aW9uOiAxNTAwXG4gICAgICAgIH0pO1xuICAgICAgICAvL2lmIHRoZSBqc29uIGRhdGEgY29udGFpbnMgZG9sbGFyIHByZWZpeGVkIHBhcmFtc1xuICAgICAgICAvL2xpa2UgJHdpZHRoIG9yICRoZWlnaHQgdGhlc2UgdG9vIGNhbiBiZSBhbmltYXRlZFxuICAgICAgICB2aXoub3AubW9ycGgoanNvbiwge1xuICAgICAgICAgIHR5cGU6ICdmYWRlJyxcbiAgICAgICAgICBkdXJhdGlvbjogMTUwMFxuICAgICAgICB9LCB7XG4gICAgICAgICAgJ25vZGUtcHJvcGVydHknOiBbJ3dpZHRoJywgJ2hlaWdodCddXG4gICAgICAgIH0pO1xuICAgICAgKGVuZCBjb2RlKVxuICAgIFxuICAgICovXG4gICAgbW9ycGg6IGZ1bmN0aW9uKGpzb24sIG9wdCwgZXh0cmFNb2Rlcykge1xuICAgICAgICBleHRyYU1vZGVzID0gZXh0cmFNb2RlcyB8fCB7fTtcbiAgICAgICAgdmFyIHZpeiA9IHRoaXMudml6O1xuICAgICAgICB2YXIgb3B0aW9ucyA9ICQubWVyZ2UodGhpcy5vcHRpb25zLCB2aXouY29udHJvbGxlciwgb3B0KSwgcm9vdCA9IHZpei5yb290O1xuICAgICAgICB2YXIgZ3JhcGg7XG4gICAgICAgIC8vVE9ETyhuaWNvKSB0aGlzIGhhY2sgbWFrZXMgbW9ycGhpbmcgd29yayB3aXRoIHRoZSBIeXBlcnRyZWUuIFxuICAgICAgICAvL05lZWQgdG8gY2hlY2sgaWYgaXQgaGFzIGJlZW4gc29sdmVkIGFuZCB0aGlzIGNhbiBiZSByZW1vdmVkLlxuICAgICAgICB2aXoucm9vdCA9IG9wdC5pZCB8fCB2aXoucm9vdDtcbiAgICAgICAgc3dpdGNoKG9wdGlvbnMudHlwZSkge1xuICAgICAgICAgICAgY2FzZSAnbm90aGluZyc6XG4gICAgICAgICAgICAgICAgZ3JhcGggPSB2aXouY29uc3RydWN0KGpzb24pO1xuICAgICAgICAgICAgICAgIGdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKGVsZW0pIHtcbiAgICAgICAgICAgICAgICAgIHZhciBub2RlRXhpc3RzID0gdml6LmdyYXBoLmhhc05vZGUoZWxlbS5pZCk7ICBcbiAgICAgICAgICAgICAgICAgIGVsZW0uZWFjaEFkamFjZW5jeShmdW5jdGlvbihhZGopIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGFkakV4aXN0cyA9ICEhdml6LmdyYXBoLmdldEFkamFjZW5jZShhZGoubm9kZUZyb20uaWQsIGFkai5ub2RlVG8uaWQpO1xuICAgICAgICAgICAgICAgICAgICB2aXouZ3JhcGguYWRkQWRqYWNlbmNlKGFkai5ub2RlRnJvbSwgYWRqLm5vZGVUbywgYWRqLmRhdGEpO1xuICAgICAgICAgICAgICAgICAgICAvL1VwZGF0ZSBkYXRhIHByb3BlcnRpZXMgaWYgdGhlIG5vZGUgZXhpc3RlZFxuICAgICAgICAgICAgICAgICAgICBpZihhZGpFeGlzdHMpIHtcbiAgICAgICAgICAgICAgICAgICAgICB2YXIgYWRkZWRBZGogPSB2aXouZ3JhcGguZ2V0QWRqYWNlbmNlKGFkai5ub2RlRnJvbS5pZCwgYWRqLm5vZGVUby5pZCk7XG4gICAgICAgICAgICAgICAgICAgICAgZm9yKHZhciBwcm9wIGluIChhZGouZGF0YSB8fCB7fSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFkZGVkQWRqLmRhdGFbcHJvcF0gPSBhZGouZGF0YVtwcm9wXTtcbiAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgLy9VcGRhdGUgZGF0YSBwcm9wZXJ0aWVzIGlmIHRoZSBub2RlIGV4aXN0ZWRcbiAgICAgICAgICAgICAgICAgIGlmKG5vZGVFeGlzdHMpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGFkZGVkTm9kZSA9IHZpei5ncmFwaC5nZXROb2RlKGVsZW0uaWQpO1xuICAgICAgICAgICAgICAgICAgICBmb3IodmFyIHByb3AgaW4gKGVsZW0uZGF0YSB8fCB7fSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICBhZGRlZE5vZGUuZGF0YVtwcm9wXSA9IGVsZW0uZGF0YVtwcm9wXTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHZpei5ncmFwaC5lYWNoTm9kZShmdW5jdGlvbihlbGVtKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uZWFjaEFkamFjZW5jeShmdW5jdGlvbihhZGopIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKCFncmFwaC5nZXRBZGphY2VuY2UoYWRqLm5vZGVGcm9tLmlkLCBhZGoubm9kZVRvLmlkKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpei5ncmFwaC5yZW1vdmVBZGphY2VuY2UoYWRqLm5vZGVGcm9tLmlkLCBhZGoubm9kZVRvLmlkKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIGlmKCFncmFwaC5oYXNOb2RlKGVsZW0uaWQpKSB2aXouZ3JhcGgucmVtb3ZlTm9kZShlbGVtLmlkKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgY2FzZSAncmVwbG90JzpcbiAgICAgICAgICAgICAgICB2aXoubGFiZWxzLmNsZWFyTGFiZWxzKHRydWUpO1xuICAgICAgICAgICAgICAgIHRoaXMubW9ycGgoanNvbiwgeyB0eXBlOiAnbm90aGluZycgfSk7XG4gICAgICAgICAgICAgICAgdml6LnJlZnJlc2godHJ1ZSk7XG4gICAgICAgICAgICAgICAgdml6LnJlZnJlc2godHJ1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICBjYXNlICdmYWRlOnNlcSc6IGNhc2UgJ2ZhZGUnOiBjYXNlICdmYWRlOmNvbic6XG4gICAgICAgICAgICAgICAgLy8gU1RBUlQgTUVUQU1BUFMgQ09ERVxuICAgICAgICAgICAgICAgIHZhciB0aGF0ID0gdGhpcztcbiAgICAgICAgICAgICAgICAvLyBPUklHSU5BTCBDT0RFOlxuICAgICAgICAgICAgICAgIC8vIHRoYXQgPSB0aGlzO1xuICAgICAgICAgICAgICAgIC8vIEVORCBNRVRBTUFQUyBDT0RFXG4gICAgICAgICAgICAgICAgZ3JhcGggPSB2aXouY29uc3RydWN0KGpzb24pO1xuICAgICAgICAgICAgICAgIC8vcHJlcHJvY2Vzc2luZyBmb3Igbm9kZXMgdG8gZGVsZXRlLlxuICAgICAgICAgICAgICAgIC8vZ2V0IG5vZGUgcHJvcGVydHkgbW9kZXMgdG8gaW50ZXJwb2xhdGVcbiAgICAgICAgICAgICAgICB2YXIgbm9kZU1vZGVzID0gKCdub2RlLXByb3BlcnR5JyBpbiBleHRyYU1vZGVzKSBcbiAgICAgICAgICAgICAgICAgICYmICQubWFwKCQuc3BsYXQoZXh0cmFNb2Rlc1snbm9kZS1wcm9wZXJ0eSddKSwgXG4gICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24obikgeyByZXR1cm4gJyQnICsgbjsgfSk7XG4gICAgICAgICAgICAgICAgdml6LmdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKGVsZW0pIHtcbiAgICAgICAgICAgICAgICAgIHZhciBncmFwaE5vZGUgPSBncmFwaC5nZXROb2RlKGVsZW0uaWQpOyAgIFxuICAgICAgICAgICAgICAgICAgaWYoIWdyYXBoTm9kZSkge1xuICAgICAgICAgICAgICAgICAgICAgIGVsZW0uc2V0RGF0YSgnYWxwaGEnLCAxKTtcbiAgICAgICAgICAgICAgICAgICAgICBlbGVtLnNldERhdGEoJ2FscGhhJywgMSwgJ3N0YXJ0Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgZWxlbS5zZXREYXRhKCdhbHBoYScsIDAsICdlbmQnKTtcbiAgICAgICAgICAgICAgICAgICAgICBlbGVtLmlnbm9yZSA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgLy9VcGRhdGUgbm9kZSBkYXRhIGluZm9ybWF0aW9uXG4gICAgICAgICAgICAgICAgICAgICAgdmFyIGdyYXBoTm9kZURhdGEgPSBncmFwaE5vZGUuZGF0YTtcbiAgICAgICAgICAgICAgICAgICAgICBmb3IodmFyIHByb3AgaW4gZ3JhcGhOb2RlRGF0YSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYobm9kZU1vZGVzICYmICgkLmluZGV4T2Yobm9kZU1vZGVzLCBwcm9wKSA+IC0xKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmVuZERhdGFbcHJvcF0gPSBncmFwaE5vZGVEYXRhW3Byb3BdO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5kYXRhW3Byb3BdID0gZ3JhcGhOb2RlRGF0YVtwcm9wXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTsgXG4gICAgICAgICAgICAgICAgdml6LmdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKGVsZW0pIHtcbiAgICAgICAgICAgICAgICAgICAgaWYoZWxlbS5pZ25vcmUpIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS5lYWNoQWRqYWNlbmN5KGZ1bmN0aW9uKGFkaikge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYoYWRqLm5vZGVGcm9tLmlnbm9yZSB8fCBhZGoubm9kZVRvLmlnbm9yZSkgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIG5vZGVGcm9tID0gZ3JhcGguZ2V0Tm9kZShhZGoubm9kZUZyb20uaWQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIG5vZGVUbyA9IGdyYXBoLmdldE5vZGUoYWRqLm5vZGVUby5pZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZighbm9kZUZyb20uYWRqYWNlbnRUbyhub2RlVG8pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFkaiA9IHZpei5ncmFwaC5nZXRBZGphY2VuY2Uobm9kZUZyb20uaWQsIG5vZGVUby5pZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFkZUVkZ2VzID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGouc2V0RGF0YSgnYWxwaGEnLCAxKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGouc2V0RGF0YSgnYWxwaGEnLCAxLCAnc3RhcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGouc2V0RGF0YSgnYWxwaGEnLCAwLCAnZW5kJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0pOyBcbiAgICAgICAgICAgICAgICAvL3ByZXByb2Nlc3NpbmcgZm9yIGFkZGluZyBub2Rlcy5cbiAgICAgICAgICAgICAgICB2YXIgZmFkZUVkZ2VzID0gdGhpcy5wcmVwcm9jZXNzU3VtKGdyYXBoKTtcblxuICAgICAgICAgICAgICAgIHZhciBtb2RlcyA9ICFmYWRlRWRnZXM/IFsnbm9kZS1wcm9wZXJ0eTphbHBoYSddIDogXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWydub2RlLXByb3BlcnR5OmFscGhhJywgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdlZGdlLXByb3BlcnR5OmFscGhhJ107XG4gICAgICAgICAgICAgICAgLy9BcHBlbmQgZXh0cmEgbm9kZS1wcm9wZXJ0eSBhbmltYXRpb25zIChpZiBhbnkpXG4gICAgICAgICAgICAgICAgbW9kZXNbMF0gPSBtb2Rlc1swXSArICgoJ25vZGUtcHJvcGVydHknIGluIGV4dHJhTW9kZXMpPyBcbiAgICAgICAgICAgICAgICAgICAgKCc6JyArICQuc3BsYXQoZXh0cmFNb2Rlc1snbm9kZS1wcm9wZXJ0eSddKS5qb2luKCc6JykpIDogJycpO1xuICAgICAgICAgICAgICAgIC8vQXBwZW5kIGV4dHJhIGVkZ2UtcHJvcGVydHkgYW5pbWF0aW9ucyAoaWYgYW55KVxuICAgICAgICAgICAgICAgIG1vZGVzWzFdID0gKG1vZGVzWzFdIHx8ICdlZGdlLXByb3BlcnR5OmFscGhhJykgKyAoKCdlZGdlLXByb3BlcnR5JyBpbiBleHRyYU1vZGVzKT8gXG4gICAgICAgICAgICAgICAgICAgICgnOicgKyAkLnNwbGF0KGV4dHJhTW9kZXNbJ2VkZ2UtcHJvcGVydHknXSkuam9pbignOicpKSA6ICcnKTtcbiAgICAgICAgICAgICAgICAvL0FkZCBsYWJlbC1wcm9wZXJ0eSBhbmltYXRpb25zIChpZiBhbnkpXG4gICAgICAgICAgICAgICAgaWYoJ2xhYmVsLXByb3BlcnR5JyBpbiBleHRyYU1vZGVzKSB7XG4gICAgICAgICAgICAgICAgICBtb2Rlcy5wdXNoKCdsYWJlbC1wcm9wZXJ0eTonICsgJC5zcGxhdChleHRyYU1vZGVzWydsYWJlbC1wcm9wZXJ0eSddKS5qb2luKCc6JykpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vb25seSB1c2UgcmVwb3NpdGlvbiBpZiBpdHMgaW1wbGVtZW50ZWQuXG4gICAgICAgICAgICAgICAgaWYgKHZpei5yZXBvc2l0aW9uKSB7XG4gICAgICAgICAgICAgICAgICB2aXoucmVwb3NpdGlvbigpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICB2aXouY29tcHV0ZSgnZW5kJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHZpei5ncmFwaC5lYWNoTm9kZShmdW5jdGlvbihlbGVtKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChlbGVtLmlkICE9IHJvb3QgJiYgZWxlbS5wb3MuZ2V0cCgpLmVxdWFscyhQb2xhci5LRVIpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgZWxlbS5wb3Muc2V0KGVsZW0uZW5kUG9zKTsgZWxlbS5zdGFydFBvcy5zZXQoZWxlbS5lbmRQb3MpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgdml6LmZ4LmFuaW1hdGUoJC5tZXJnZShvcHRpb25zLCB7XG4gICAgICAgICAgICAgICAgICAgIG1vZGVzOiBbZXh0cmFNb2Rlcy5wb3NpdGlvbiB8fCAncG9sYXInXS5jb25jYXQobW9kZXMpLFxuICAgICAgICAgICAgICAgICAgICBvbkNvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZpei5ncmFwaC5lYWNoTm9kZShmdW5jdGlvbihlbGVtKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoZWxlbS5pZ25vcmUpIHZpei5ncmFwaC5yZW1vdmVOb2RlKGVsZW0uaWQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2aXouZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24oZWxlbSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uZWFjaEFkamFjZW5jeShmdW5jdGlvbihhZGopIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoYWRqLmlnbm9yZSkgdml6LmdyYXBoLnJlbW92ZUFkamFjZW5jZShhZGoubm9kZUZyb20uaWQsIGFkai5ub2RlVG8uaWQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBvcHRpb25zLm9uQ29tcGxldGUoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgICAgZGVmYXVsdDo7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgXG4gIC8qXG4gICAgTWV0aG9kOiBjb250cmFjdFxuIFxuICAgIENvbGxhcHNlcyB0aGUgc3VidHJlZSBvZiB0aGUgZ2l2ZW4gbm9kZS4gVGhlIG5vZGUgd2lsbCBoYXZlIGEgX2NvbGxhcHNlZD10cnVlXyBwcm9wZXJ0eS5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuIFxuICAgIG5vZGUgLSAob2JqZWN0KSBBIDxHcmFwaC5Ob2RlPi5cbiAgICBvcHQgLSAob2JqZWN0KSBBbiBvYmplY3QgY29udGFpbmluZyBvcHRpb25zIGRlc2NyaWJlZCBiZWxvd1xuICAgIHR5cGUgLSAoc3RyaW5nKSBXaGV0aGVyIHRvICdyZXBsb3QnIG9yICdhbmltYXRlJyB0aGUgY29udHJhY3Rpb24uXG4gICBcbiAgICBUaGVyZSBhcmUgYWxzbyBhIG51bWJlciBvZiBBbmltYXRpb24gb3B0aW9ucy4gRm9yIG1vcmUgaW5mb3JtYXRpb24gc2VlIDxPcHRpb25zLkZ4Pi5cblxuICAgIEV4YW1wbGU6XG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgIHZhciB2aXogPSBuZXcgJGppdC5WaXoob3B0aW9ucyk7XG4gICAgIHZpei5vcC5jb250cmFjdChub2RlLCB7XG4gICAgICAgdHlwZTogJ2FuaW1hdGUnLFxuICAgICAgIGR1cmF0aW9uOiAxMDAwLFxuICAgICAgIGhpZGVMYWJlbHM6IHRydWUsXG4gICAgICAgdHJhbnNpdGlvbjogJGppdC5UcmFucy5RdWFydC5lYXNlT3V0XG4gICAgIH0pO1xuICAgKGVuZCBjb2RlKVxuIFxuICAgKi9cbiAgICBjb250cmFjdDogZnVuY3Rpb24obm9kZSwgb3B0KSB7XG4gICAgICB2YXIgdml6ID0gdGhpcy52aXo7XG4gICAgICBpZihub2RlLmNvbGxhcHNlZCB8fCAhbm9kZS5hbnlTdWJub2RlKCQubGFtYmRhKHRydWUpKSkgcmV0dXJuO1xuICAgICAgb3B0ID0gJC5tZXJnZSh0aGlzLm9wdGlvbnMsIHZpei5jb25maWcsIG9wdCB8fCB7fSwge1xuICAgICAgICAnbW9kZXMnOiBbJ25vZGUtcHJvcGVydHk6YWxwaGE6c3BhbicsICdsaW5lYXInXVxuICAgICAgfSk7XG4gICAgICBub2RlLmNvbGxhcHNlZCA9IHRydWU7XG4gICAgICAoZnVuY3Rpb24gc3VibihuKSB7XG4gICAgICAgIG4uZWFjaFN1Ym5vZGUoZnVuY3Rpb24oY2gpIHtcbiAgICAgICAgICBjaC5pZ25vcmUgPSB0cnVlO1xuICAgICAgICAgIGNoLnNldERhdGEoJ2FscGhhJywgMCwgb3B0LnR5cGUgPT0gJ2FuaW1hdGUnPyAnZW5kJyA6ICdjdXJyZW50Jyk7XG4gICAgICAgICAgc3VibihjaCk7XG4gICAgICAgIH0pO1xuICAgICAgfSkobm9kZSk7XG4gICAgICBpZihvcHQudHlwZSA9PSAnYW5pbWF0ZScpIHtcbiAgICAgICAgdml6LmNvbXB1dGUoJ2VuZCcpO1xuICAgICAgICBpZih2aXoucm90YXRlZCkge1xuICAgICAgICAgIHZpei5yb3RhdGUodml6LnJvdGF0ZWQsICdub25lJywge1xuICAgICAgICAgICAgJ3Byb3BlcnR5JzonZW5kJ1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIChmdW5jdGlvbiBzdWJuKG4pIHtcbiAgICAgICAgICBuLmVhY2hTdWJub2RlKGZ1bmN0aW9uKGNoKSB7XG4gICAgICAgICAgICBjaC5zZXRQb3Mobm9kZS5nZXRQb3MoJ2VuZCcpLCAnZW5kJyk7XG4gICAgICAgICAgICBzdWJuKGNoKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSkobm9kZSk7XG4gICAgICAgIHZpei5meC5hbmltYXRlKG9wdCk7XG4gICAgICB9IGVsc2UgaWYob3B0LnR5cGUgPT0gJ3JlcGxvdCcpe1xuICAgICAgICB2aXoucmVmcmVzaCgpO1xuICAgICAgfVxuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICBNZXRob2Q6IGV4cGFuZFxuIFxuICAgIEV4cGFuZHMgdGhlIHByZXZpb3VzbHkgY29udHJhY3RlZCBzdWJ0cmVlLiBUaGUgZ2l2ZW4gbm9kZSBtdXN0IGhhdmUgdGhlIF9jb2xsYXBzZWQ9dHJ1ZV8gcHJvcGVydHkuXG4gICAgXG4gICAgUGFyYW1ldGVyczpcbiBcbiAgICBub2RlIC0gKG9iamVjdCkgQSA8R3JhcGguTm9kZT4uXG4gICAgb3B0IC0gKG9iamVjdCkgQW4gb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBkZXNjcmliZWQgYmVsb3dcbiAgICB0eXBlIC0gKHN0cmluZykgV2hldGhlciB0byAncmVwbG90JyBvciAnYW5pbWF0ZScuXG4gICAgIFxuICAgIFRoZXJlIGFyZSBhbHNvIGEgbnVtYmVyIG9mIEFuaW1hdGlvbiBvcHRpb25zLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBzZWUgPE9wdGlvbnMuRng+LlxuXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgIHZhciB2aXogPSBuZXcgJGppdC5WaXoob3B0aW9ucyk7XG4gICAgICB2aXoub3AuZXhwYW5kKG5vZGUsIHtcbiAgICAgICAgdHlwZTogJ2FuaW1hdGUnLFxuICAgICAgICBkdXJhdGlvbjogMTAwMCxcbiAgICAgICAgaGlkZUxhYmVsczogdHJ1ZSxcbiAgICAgICAgdHJhbnNpdGlvbjogJGppdC5UcmFucy5RdWFydC5lYXNlT3V0XG4gICAgICB9KTtcbiAgICAoZW5kIGNvZGUpXG4gXG4gICAqL1xuICAgIGV4cGFuZDogZnVuY3Rpb24obm9kZSwgb3B0KSB7XG4gICAgICBpZighKCdjb2xsYXBzZWQnIGluIG5vZGUpKSByZXR1cm47XG4gICAgICB2YXIgdml6ID0gdGhpcy52aXo7XG4gICAgICBvcHQgPSAkLm1lcmdlKHRoaXMub3B0aW9ucywgdml6LmNvbmZpZywgb3B0IHx8IHt9LCB7XG4gICAgICAgICdtb2Rlcyc6IFsnbm9kZS1wcm9wZXJ0eTphbHBoYTpzcGFuJywgJ2xpbmVhciddXG4gICAgICB9KTtcbiAgICAgIGRlbGV0ZSBub2RlLmNvbGxhcHNlZDtcbiAgICAgIChmdW5jdGlvbiBzdWJuKG4pIHtcbiAgICAgICAgbi5lYWNoU3Vibm9kZShmdW5jdGlvbihjaCkge1xuICAgICAgICAgIGRlbGV0ZSBjaC5pZ25vcmU7XG4gICAgICAgICAgY2guc2V0RGF0YSgnYWxwaGEnLCAxLCBvcHQudHlwZSA9PSAnYW5pbWF0ZSc/ICdlbmQnIDogJ2N1cnJlbnQnKTtcbiAgICAgICAgICBzdWJuKGNoKTtcbiAgICAgICAgfSk7XG4gICAgICB9KShub2RlKTtcbiAgICAgIGlmKG9wdC50eXBlID09ICdhbmltYXRlJykge1xuICAgICAgICB2aXouY29tcHV0ZSgnZW5kJyk7XG4gICAgICAgIGlmKHZpei5yb3RhdGVkKSB7XG4gICAgICAgICAgdml6LnJvdGF0ZSh2aXoucm90YXRlZCwgJ25vbmUnLCB7XG4gICAgICAgICAgICAncHJvcGVydHknOidlbmQnXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgdml6LmZ4LmFuaW1hdGUob3B0KTtcbiAgICAgIH0gZWxzZSBpZihvcHQudHlwZSA9PSAncmVwbG90Jyl7XG4gICAgICAgIHZpei5yZWZyZXNoKCk7XG4gICAgICB9XG4gICAgfSxcblxuICAgIHByZXByb2Nlc3NTdW06IGZ1bmN0aW9uKGdyYXBoKSB7XG4gICAgICAgIHZhciB2aXogPSB0aGlzLnZpejtcbiAgICAgICAgZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24oZWxlbSkge1xuICAgICAgICAgICAgaWYoIXZpei5ncmFwaC5oYXNOb2RlKGVsZW0uaWQpKSB7XG4gICAgICAgICAgICAgICAgdml6LmdyYXBoLmFkZE5vZGUoZWxlbSk7XG4gICAgICAgICAgICAgICAgdmFyIG4gPSB2aXouZ3JhcGguZ2V0Tm9kZShlbGVtLmlkKTtcbiAgICAgICAgICAgICAgICBuLnNldERhdGEoJ2FscGhhJywgMCk7XG4gICAgICAgICAgICAgICAgbi5zZXREYXRhKCdhbHBoYScsIDAsICdzdGFydCcpO1xuICAgICAgICAgICAgICAgIG4uc2V0RGF0YSgnYWxwaGEnLCAxLCAnZW5kJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pOyBcbiAgICAgICAgdmFyIGZhZGVFZGdlcyA9IGZhbHNlO1xuICAgICAgICBncmFwaC5lYWNoTm9kZShmdW5jdGlvbihlbGVtKSB7XG4gICAgICAgICAgICBlbGVtLmVhY2hBZGphY2VuY3koZnVuY3Rpb24oYWRqKSB7XG4gICAgICAgICAgICAgICAgdmFyIG5vZGVGcm9tID0gdml6LmdyYXBoLmdldE5vZGUoYWRqLm5vZGVGcm9tLmlkKTtcbiAgICAgICAgICAgICAgICB2YXIgbm9kZVRvID0gdml6LmdyYXBoLmdldE5vZGUoYWRqLm5vZGVUby5pZCk7XG4gICAgICAgICAgICAgICAgaWYoIW5vZGVGcm9tLmFkamFjZW50VG8obm9kZVRvKSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgYWRqID0gdml6LmdyYXBoLmFkZEFkamFjZW5jZShub2RlRnJvbSwgbm9kZVRvLCBhZGouZGF0YSk7XG4gICAgICAgICAgICAgICAgICAgIGlmKG5vZGVGcm9tLnN0YXJ0QWxwaGEgPT0gbm9kZUZyb20uZW5kQWxwaGEgXG4gICAgICAgICAgICAgICAgICAgICYmIG5vZGVUby5zdGFydEFscGhhID09IG5vZGVUby5lbmRBbHBoYSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZmFkZUVkZ2VzID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFkai5zZXREYXRhKCdhbHBoYScsIDApO1xuICAgICAgICAgICAgICAgICAgICAgICAgYWRqLnNldERhdGEoJ2FscGhhJywgMCwgJ3N0YXJ0Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBhZGouc2V0RGF0YSgnYWxwaGEnLCAxLCAnZW5kJyk7XG4gICAgICAgICAgICAgICAgICAgIH0gXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pOyBcbiAgICAgICAgcmV0dXJuIGZhZGVFZGdlcztcbiAgICB9XG59O1xuXG5cblxuLypcbiAgIEZpbGU6IEhlbHBlcnMuanNcbiBcbiAgIEhlbHBlcnMgYXJlIG9iamVjdHMgdGhhdCBjb250YWluIHJlbmRlcmluZyBwcmltaXRpdmVzIChsaWtlIHJlY3RhbmdsZXMsIGVsbGlwc2VzLCBldGMpLCBmb3IgcGxvdHRpbmcgbm9kZXMgYW5kIGVkZ2VzLlxuICAgSGVscGVycyBhbHNvIGNvbnRhaW4gaW1wbGVtZW50YXRpb25zIG9mIHRoZSAqY29udGFpbnMqIG1ldGhvZCwgYSBtZXRob2QgcmV0dXJuaW5nIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIG1vdXNlXG4gICBwb3NpdGlvbiBpcyBvdmVyIHRoZSByZW5kZXJlZCBzaGFwZS5cbiAgIFxuICAgSGVscGVycyBhcmUgdmVyeSB1c2VmdWwgd2hlbiBpbXBsZW1lbnRpbmcgbmV3IE5vZGVUeXBlcywgc2luY2UgeW91IGNhbiBhY2Nlc3MgdGhlbSB0aHJvdWdoICp0aGlzLm5vZGVIZWxwZXIqIGFuZCBcbiAgICp0aGlzLmVkZ2VIZWxwZXIqIDxHcmFwaC5QbG90PiBwcm9wZXJ0aWVzLCBwcm92aWRpbmcgeW91IHdpdGggc2ltcGxlIHByaW1pdGl2ZXMgYW5kIG1vdXNlLXBvc2l0aW9uIGNoZWNrIGZ1bmN0aW9ucy5cbiAgIFxuICAgRXhhbXBsZTpcbiAgIChzdGFydCBjb2RlIGpzKVxuICAgLy9pbXBsZW1lbnQgYSBuZXcgbm9kZSB0eXBlXG4gICAkaml0LlZpei5QbG90Lk5vZGVUeXBlcy5pbXBsZW1lbnQoe1xuICAgICAnY3VzdG9tTm9kZVR5cGUnOiB7XG4gICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcykge1xuICAgICAgICAgdGhpcy5ub2RlSGVscGVyLmNpcmNsZS5yZW5kZXIgLi4uXG4gICAgICAgfSxcbiAgICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3MpIHtcbiAgICAgICAgIHRoaXMubm9kZUhlbHBlci5jaXJjbGUuY29udGFpbnMgLi4uXG4gICAgICAgfVxuICAgICB9XG4gICB9KTtcbiAgIC8vaW1wbGVtZW50IGFuIGVkZ2UgdHlwZVxuICAgJGppdC5WaXouUGxvdC5FZGdlVHlwZXMuaW1wbGVtZW50KHtcbiAgICAgJ2N1c3RvbU5vZGVUeXBlJzoge1xuICAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpIHtcbiAgICAgICAgIHRoaXMuZWRnZUhlbHBlci5jaXJjbGUucmVuZGVyIC4uLlxuICAgICAgIH0sXG4gICAgICAgLy9vcHRpb25hbFxuICAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5vZGUsIHBvcykge1xuICAgICAgICAgdGhpcy5lZGdlSGVscGVyLmNpcmNsZS5jb250YWlucyAuLi5cbiAgICAgICB9XG4gICAgIH1cbiAgIH0pO1xuICAgKGVuZCBjb2RlKVxuXG4qL1xuXG4vKlxuICAgT2JqZWN0OiBOb2RlSGVscGVyXG4gICBcbiAgIENvbnRhaW5zIHJlbmRlcmluZyBhbmQgb3RoZXIgdHlwZSBvZiBwcmltaXRpdmVzIGZvciBzaW1wbGUgc2hhcGVzLlxuICovXG52YXIgTm9kZUhlbHBlciA9IHtcbiAgJ25vbmUnOiB7XG4gICAgJ3JlbmRlcic6ICQuZW1wdHksXG4gICAgJ2NvbnRhaW5zJzogJC5sYW1iZGEoZmFsc2UpXG4gIH0sXG4gIC8qXG4gICBPYmplY3Q6IE5vZGVIZWxwZXIuY2lyY2xlXG4gICAqL1xuICAnY2lyY2xlJzoge1xuICAgIC8qXG4gICAgIE1ldGhvZDogcmVuZGVyXG4gICAgIFxuICAgICBSZW5kZXJzIGEgY2lyY2xlIGludG8gdGhlIGNhbnZhcy5cbiAgICAgXG4gICAgIFBhcmFtZXRlcnM6XG4gICAgIFxuICAgICB0eXBlIC0gKHN0cmluZykgUG9zc2libGUgb3B0aW9ucyBhcmUgJ2ZpbGwnIG9yICdzdHJva2UnLlxuICAgICBwb3MgLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgcG9zaXRpb24gb2YgdGhlIGNlbnRlciBvZiB0aGUgY2lyY2xlLlxuICAgICByYWRpdXMgLSAobnVtYmVyKSBUaGUgcmFkaXVzIG9mIHRoZSBjaXJjbGUgdG8gYmUgcmVuZGVyZWQuXG4gICAgIGNhbnZhcyAtIChvYmplY3QpIEEgPENhbnZhcz4gaW5zdGFuY2UuXG4gICAgIFxuICAgICBFeGFtcGxlOlxuICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgTm9kZUhlbHBlci5jaXJjbGUucmVuZGVyKCdmaWxsJywgeyB4OiAxMCwgeTogMzAgfSwgMzAsIHZpei5jYW52YXMpO1xuICAgICAoZW5kIGNvZGUpXG4gICAgICovXG4gICAgJ3JlbmRlcic6IGZ1bmN0aW9uKHR5cGUsIHBvcywgcmFkaXVzLCBjYW52YXMpe1xuICAgICAgdmFyIGN0eCA9IGNhbnZhcy5nZXRDdHgoKTtcbiAgICAgIGN0eC5iZWdpblBhdGgoKTtcbiAgICAgIGN0eC5hcmMocG9zLngsIHBvcy55LCByYWRpdXMsIDAsIE1hdGguUEkgKiAyLCB0cnVlKTtcbiAgICAgIGN0eC5jbG9zZVBhdGgoKTtcbiAgICAgIGN0eFt0eXBlXSgpO1xuICAgIH0sXG4gICAgLypcbiAgICBNZXRob2Q6IGNvbnRhaW5zXG4gICAgXG4gICAgUmV0dXJucyAqdHJ1ZSogaWYgKnBvcyogaXMgY29udGFpbmVkIGluIHRoZSBhcmVhIG9mIHRoZSBzaGFwZS4gUmV0dXJucyAqZmFsc2UqIG90aGVyd2lzZS5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIG5wb3MgLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgPEdyYXBoLk5vZGU+IHBvc2l0aW9uLlxuICAgIHBvcyAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBwb3NpdGlvbiB0byBjaGVjay5cbiAgICByYWRpdXMgLSAobnVtYmVyKSBUaGUgcmFkaXVzIG9mIHRoZSByZW5kZXJlZCBjaXJjbGUuXG4gICAgXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICBOb2RlSGVscGVyLmNpcmNsZS5jb250YWlucyh7IHg6IDEwLCB5OiAzMCB9LCB7IHg6IDE1LCB5OiAzNSB9LCAzMCk7IC8vdHJ1ZVxuICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5wb3MsIHBvcywgcmFkaXVzKXtcbiAgICAgIHZhciBkaWZmeCA9IG5wb3MueCAtIHBvcy54LCBcbiAgICAgICAgICBkaWZmeSA9IG5wb3MueSAtIHBvcy55LCBcbiAgICAgICAgICBkaWZmID0gZGlmZnggKiBkaWZmeCArIGRpZmZ5ICogZGlmZnk7XG4gICAgICByZXR1cm4gZGlmZiA8PSByYWRpdXMgKiByYWRpdXM7XG4gICAgfVxuICB9LFxuICAvKlxuICBPYmplY3Q6IE5vZGVIZWxwZXIuZWxsaXBzZVxuICAqL1xuICAnZWxsaXBzZSc6IHtcbiAgICAvKlxuICAgIE1ldGhvZDogcmVuZGVyXG4gICAgXG4gICAgUmVuZGVycyBhbiBlbGxpcHNlIGludG8gdGhlIGNhbnZhcy5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIHR5cGUgLSAoc3RyaW5nKSBQb3NzaWJsZSBvcHRpb25zIGFyZSAnZmlsbCcgb3IgJ3N0cm9rZScuXG4gICAgcG9zIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggdGhlIHBvc2l0aW9uIG9mIHRoZSBjZW50ZXIgb2YgdGhlIGVsbGlwc2UuXG4gICAgd2lkdGggLSAobnVtYmVyKSBUaGUgd2lkdGggb2YgdGhlIGVsbGlwc2UuXG4gICAgaGVpZ2h0IC0gKG51bWJlcikgVGhlIGhlaWdodCBvZiB0aGUgZWxsaXBzZS5cbiAgICBjYW52YXMgLSAob2JqZWN0KSBBIDxDYW52YXM+IGluc3RhbmNlLlxuICAgIFxuICAgIEV4YW1wbGU6XG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgTm9kZUhlbHBlci5lbGxpcHNlLnJlbmRlcignZmlsbCcsIHsgeDogMTAsIHk6IDMwIH0sIDMwLCA0MCwgdml6LmNhbnZhcyk7XG4gICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgJ3JlbmRlcic6IGZ1bmN0aW9uKHR5cGUsIHBvcywgd2lkdGgsIGhlaWdodCwgY2FudmFzKXtcbiAgICAgIHZhciBjdHggPSBjYW52YXMuZ2V0Q3R4KCksXG4gICAgICAgICAgc2NhbGV4ID0gMSxcbiAgICAgICAgICBzY2FsZXkgPSAxLFxuICAgICAgICAgIHNjYWxlcG9zeCA9IDEsXG4gICAgICAgICAgc2NhbGVwb3N5ID0gMSxcbiAgICAgICAgICByYWRpdXMgPSAwO1xuXG4gICAgICBpZiAod2lkdGggPiBoZWlnaHQpIHtcbiAgICAgICAgICByYWRpdXMgPSB3aWR0aCAvIDI7XG4gICAgICAgICAgc2NhbGV5ID0gaGVpZ2h0IC8gd2lkdGg7XG4gICAgICAgICAgc2NhbGVwb3N5ID0gd2lkdGggLyBoZWlnaHQ7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJhZGl1cyA9IGhlaWdodCAvIDI7XG4gICAgICAgICAgc2NhbGV4ID0gd2lkdGggLyBoZWlnaHQ7XG4gICAgICAgICAgc2NhbGVwb3N4ID0gaGVpZ2h0IC8gd2lkdGg7XG4gICAgICB9XG5cbiAgICAgIGN0eC5zYXZlKCk7XG4gICAgICBjdHguc2NhbGUoc2NhbGV4LCBzY2FsZXkpO1xuICAgICAgY3R4LmJlZ2luUGF0aCgpO1xuICAgICAgY3R4LmFyYyhwb3MueCAqIHNjYWxlcG9zeCwgcG9zLnkgKiBzY2FsZXBvc3ksIHJhZGl1cywgMCwgTWF0aC5QSSAqIDIsIHRydWUpO1xuICAgICAgY3R4LmNsb3NlUGF0aCgpO1xuICAgICAgY3R4W3R5cGVdKCk7XG4gICAgICBjdHgucmVzdG9yZSgpO1xuICAgIH0sXG4gICAgLypcbiAgICBNZXRob2Q6IGNvbnRhaW5zXG4gICAgXG4gICAgUmV0dXJucyAqdHJ1ZSogaWYgKnBvcyogaXMgY29udGFpbmVkIGluIHRoZSBhcmVhIG9mIHRoZSBzaGFwZS4gUmV0dXJucyAqZmFsc2UqIG90aGVyd2lzZS5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIG5wb3MgLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgPEdyYXBoLk5vZGU+IHBvc2l0aW9uLlxuICAgIHBvcyAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBwb3NpdGlvbiB0byBjaGVjay5cbiAgICB3aWR0aCAtIChudW1iZXIpIFRoZSB3aWR0aCBvZiB0aGUgcmVuZGVyZWQgZWxsaXBzZS5cbiAgICBoZWlnaHQgLSAobnVtYmVyKSBUaGUgaGVpZ2h0IG9mIHRoZSByZW5kZXJlZCBlbGxpcHNlLlxuICAgIFxuICAgIEV4YW1wbGU6XG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgTm9kZUhlbHBlci5lbGxpcHNlLmNvbnRhaW5zKHsgeDogMTAsIHk6IDMwIH0sIHsgeDogMTUsIHk6IDM1IH0sIDMwLCA0MCk7XG4gICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obnBvcywgcG9zLCB3aWR0aCwgaGVpZ2h0KXtcbiAgICAgIHZhciByYWRpdXMgPSAwLFxuICAgICAgICAgIHNjYWxleCA9IDEsXG4gICAgICAgICAgc2NhbGV5ID0gMSxcbiAgICAgICAgICBkaWZmeCA9IDAsXG4gICAgICAgICAgZGlmZnkgPSAwLFxuICAgICAgICAgIGRpZmYgPSAwO1xuXG4gICAgICBpZiAod2lkdGggPiBoZWlnaHQpIHtcblx0ICAgICAgcmFkaXVzID0gd2lkdGggLyAyO1xuXHQgICAgICBzY2FsZXkgPSBoZWlnaHQgLyB3aWR0aDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmFkaXVzID0gaGVpZ2h0IC8gMjtcbiAgICAgICAgICBzY2FsZXggPSB3aWR0aCAvIGhlaWdodDtcbiAgICAgIH1cblxuICAgICAgZGlmZnggPSAobnBvcy54IC0gcG9zLngpICogKDEgLyBzY2FsZXgpO1xuICAgICAgZGlmZnkgPSAobnBvcy55IC0gcG9zLnkpICogKDEgLyBzY2FsZXkpO1xuICAgICAgZGlmZiA9IGRpZmZ4ICogZGlmZnggKyBkaWZmeSAqIGRpZmZ5O1xuICAgICAgcmV0dXJuIGRpZmYgPD0gcmFkaXVzICogcmFkaXVzO1xuICAgIH1cbiAgfSxcbiAgLypcbiAgT2JqZWN0OiBOb2RlSGVscGVyLnNxdWFyZVxuICAqL1xuICAnc3F1YXJlJzoge1xuICAgIC8qXG4gICAgTWV0aG9kOiByZW5kZXJcbiAgICBcbiAgICBSZW5kZXJzIGEgc3F1YXJlIGludG8gdGhlIGNhbnZhcy5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIHR5cGUgLSAoc3RyaW5nKSBQb3NzaWJsZSBvcHRpb25zIGFyZSAnZmlsbCcgb3IgJ3N0cm9rZScuXG4gICAgcG9zIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggdGhlIHBvc2l0aW9uIG9mIHRoZSBjZW50ZXIgb2YgdGhlIHNxdWFyZS5cbiAgICBkaW0gLSAobnVtYmVyKSBUaGUgcmFkaXVzIChvciBoYWxmLWRpYW1ldGVyKSBvZiB0aGUgc3F1YXJlLlxuICAgIGNhbnZhcyAtIChvYmplY3QpIEEgPENhbnZhcz4gaW5zdGFuY2UuXG4gICAgXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICBOb2RlSGVscGVyLnNxdWFyZS5yZW5kZXIoJ3N0cm9rZScsIHsgeDogMTAsIHk6IDMwIH0sIDQwLCB2aXouY2FudmFzKTtcbiAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICAncmVuZGVyJzogZnVuY3Rpb24odHlwZSwgcG9zLCBkaW0sIGNhbnZhcyl7XG4gICAgICBjYW52YXMuZ2V0Q3R4KClbdHlwZSArIFwiUmVjdFwiXShwb3MueCAtIGRpbSwgcG9zLnkgLSBkaW0sIDIqZGltLCAyKmRpbSk7XG4gICAgfSxcbiAgICAvKlxuICAgIE1ldGhvZDogY29udGFpbnNcbiAgICBcbiAgICBSZXR1cm5zICp0cnVlKiBpZiAqcG9zKiBpcyBjb250YWluZWQgaW4gdGhlIGFyZWEgb2YgdGhlIHNoYXBlLiBSZXR1cm5zICpmYWxzZSogb3RoZXJ3aXNlLlxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgbnBvcyAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSA8R3JhcGguTm9kZT4gcG9zaXRpb24uXG4gICAgcG9zIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggdGhlIHBvc2l0aW9uIHRvIGNoZWNrLlxuICAgIGRpbSAtIChudW1iZXIpIFRoZSByYWRpdXMgKG9yIGhhbGYtZGlhbWV0ZXIpIG9mIHRoZSBzcXVhcmUuXG4gICAgXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICBOb2RlSGVscGVyLnNxdWFyZS5jb250YWlucyh7IHg6IDEwLCB5OiAzMCB9LCB7IHg6IDE1LCB5OiAzNSB9LCAzMCk7XG4gICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obnBvcywgcG9zLCBkaW0pe1xuICAgICAgcmV0dXJuIE1hdGguYWJzKHBvcy54IC0gbnBvcy54KSA8PSBkaW0gJiYgTWF0aC5hYnMocG9zLnkgLSBucG9zLnkpIDw9IGRpbTtcbiAgICB9XG4gIH0sXG4gIC8qXG4gIE9iamVjdDogTm9kZUhlbHBlci5yZWN0YW5nbGVcbiAgKi9cbiAgJ3JlY3RhbmdsZSc6IHtcbiAgICAvKlxuICAgIE1ldGhvZDogcmVuZGVyXG4gICAgXG4gICAgUmVuZGVycyBhIHJlY3RhbmdsZSBpbnRvIHRoZSBjYW52YXMuXG4gICAgXG4gICAgUGFyYW1ldGVyczpcbiAgICBcbiAgICB0eXBlIC0gKHN0cmluZykgUG9zc2libGUgb3B0aW9ucyBhcmUgJ2ZpbGwnIG9yICdzdHJva2UnLlxuICAgIHBvcyAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBwb3NpdGlvbiBvZiB0aGUgY2VudGVyIG9mIHRoZSByZWN0YW5nbGUuXG4gICAgd2lkdGggLSAobnVtYmVyKSBUaGUgd2lkdGggb2YgdGhlIHJlY3RhbmdsZS5cbiAgICBoZWlnaHQgLSAobnVtYmVyKSBUaGUgaGVpZ2h0IG9mIHRoZSByZWN0YW5nbGUuXG4gICAgY2FudmFzIC0gKG9iamVjdCkgQSA8Q2FudmFzPiBpbnN0YW5jZS5cbiAgICBcbiAgICBFeGFtcGxlOlxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgIE5vZGVIZWxwZXIucmVjdGFuZ2xlLnJlbmRlcignZmlsbCcsIHsgeDogMTAsIHk6IDMwIH0sIDMwLCA0MCwgdml6LmNhbnZhcyk7XG4gICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgJ3JlbmRlcic6IGZ1bmN0aW9uKHR5cGUsIHBvcywgd2lkdGgsIGhlaWdodCwgY2FudmFzKXtcbiAgICAgIGNhbnZhcy5nZXRDdHgoKVt0eXBlICsgXCJSZWN0XCJdKHBvcy54IC0gd2lkdGggLyAyLCBwb3MueSAtIGhlaWdodCAvIDIsIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCwgaGVpZ2h0KTtcbiAgICB9LFxuICAgIC8qXG4gICAgTWV0aG9kOiBjb250YWluc1xuICAgIFxuICAgIFJldHVybnMgKnRydWUqIGlmICpwb3MqIGlzIGNvbnRhaW5lZCBpbiB0aGUgYXJlYSBvZiB0aGUgc2hhcGUuIFJldHVybnMgKmZhbHNlKiBvdGhlcndpc2UuXG4gICAgXG4gICAgUGFyYW1ldGVyczpcbiAgICBcbiAgICBucG9zIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggdGhlIDxHcmFwaC5Ob2RlPiBwb3NpdGlvbi5cbiAgICBwb3MgLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgcG9zaXRpb24gdG8gY2hlY2suXG4gICAgd2lkdGggLSAobnVtYmVyKSBUaGUgd2lkdGggb2YgdGhlIHJlbmRlcmVkIHJlY3RhbmdsZS5cbiAgICBoZWlnaHQgLSAobnVtYmVyKSBUaGUgaGVpZ2h0IG9mIHRoZSByZW5kZXJlZCByZWN0YW5nbGUuXG4gICAgXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICBOb2RlSGVscGVyLnJlY3RhbmdsZS5jb250YWlucyh7IHg6IDEwLCB5OiAzMCB9LCB7IHg6IDE1LCB5OiAzNSB9LCAzMCwgNDApO1xuICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5wb3MsIHBvcywgd2lkdGgsIGhlaWdodCl7XG4gICAgICByZXR1cm4gTWF0aC5hYnMocG9zLnggLSBucG9zLngpIDw9IHdpZHRoIC8gMlxuICAgICAgICAgICYmIE1hdGguYWJzKHBvcy55IC0gbnBvcy55KSA8PSBoZWlnaHQgLyAyO1xuICAgIH1cbiAgfSxcbiAgLypcbiAgT2JqZWN0OiBOb2RlSGVscGVyLnRyaWFuZ2xlXG4gICovXG4gICd0cmlhbmdsZSc6IHtcbiAgICAvKlxuICAgIE1ldGhvZDogcmVuZGVyXG4gICAgXG4gICAgUmVuZGVycyBhIHRyaWFuZ2xlIGludG8gdGhlIGNhbnZhcy5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIHR5cGUgLSAoc3RyaW5nKSBQb3NzaWJsZSBvcHRpb25zIGFyZSAnZmlsbCcgb3IgJ3N0cm9rZScuXG4gICAgcG9zIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggdGhlIHBvc2l0aW9uIG9mIHRoZSBjZW50ZXIgb2YgdGhlIHRyaWFuZ2xlLlxuICAgIGRpbSAtIChudW1iZXIpIEhhbGYgdGhlIGJhc2UgYW5kIGhhbGYgdGhlIGhlaWdodCBvZiB0aGUgdHJpYW5nbGUuXG4gICAgY2FudmFzIC0gKG9iamVjdCkgQSA8Q2FudmFzPiBpbnN0YW5jZS5cbiAgICBcbiAgICBFeGFtcGxlOlxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgIE5vZGVIZWxwZXIudHJpYW5nbGUucmVuZGVyKCdzdHJva2UnLCB7IHg6IDEwLCB5OiAzMCB9LCA0MCwgdml6LmNhbnZhcyk7XG4gICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgJ3JlbmRlcic6IGZ1bmN0aW9uKHR5cGUsIHBvcywgZGltLCBjYW52YXMpe1xuICAgICAgdmFyIGN0eCA9IGNhbnZhcy5nZXRDdHgoKSwgXG4gICAgICAgICAgYzF4ID0gcG9zLngsIFxuICAgICAgICAgIGMxeSA9IHBvcy55IC0gZGltLCBcbiAgICAgICAgICBjMnggPSBjMXggLSBkaW0sIFxuICAgICAgICAgIGMyeSA9IHBvcy55ICsgZGltLCBcbiAgICAgICAgICBjM3ggPSBjMXggKyBkaW0sIFxuICAgICAgICAgIGMzeSA9IGMyeTtcbiAgICAgIGN0eC5iZWdpblBhdGgoKTtcbiAgICAgIGN0eC5tb3ZlVG8oYzF4LCBjMXkpO1xuICAgICAgY3R4LmxpbmVUbyhjMngsIGMyeSk7XG4gICAgICBjdHgubGluZVRvKGMzeCwgYzN5KTtcbiAgICAgIGN0eC5jbG9zZVBhdGgoKTtcbiAgICAgIGN0eFt0eXBlXSgpO1xuICAgIH0sXG4gICAgLypcbiAgICBNZXRob2Q6IGNvbnRhaW5zXG4gICAgXG4gICAgUmV0dXJucyAqdHJ1ZSogaWYgKnBvcyogaXMgY29udGFpbmVkIGluIHRoZSBhcmVhIG9mIHRoZSBzaGFwZS4gUmV0dXJucyAqZmFsc2UqIG90aGVyd2lzZS5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIG5wb3MgLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgPEdyYXBoLk5vZGU+IHBvc2l0aW9uLlxuICAgIHBvcyAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBwb3NpdGlvbiB0byBjaGVjay5cbiAgICBkaW0gLSAobnVtYmVyKSBIYWxmIHRoZSBiYXNlIGFuZCBoYWxmIHRoZSBoZWlnaHQgb2YgdGhlIHRyaWFuZ2xlLlxuICAgIFxuICAgIEV4YW1wbGU6XG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgTm9kZUhlbHBlci50cmlhbmdsZS5jb250YWlucyh7IHg6IDEwLCB5OiAzMCB9LCB7IHg6IDE1LCB5OiAzNSB9LCAzMCk7XG4gICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obnBvcywgcG9zLCBkaW0pIHtcbiAgICAgIHJldHVybiBOb2RlSGVscGVyLmNpcmNsZS5jb250YWlucyhucG9zLCBwb3MsIGRpbSk7XG4gICAgfVxuICB9LFxuICAvKlxuICBPYmplY3Q6IE5vZGVIZWxwZXIuc3RhclxuICAqL1xuICAnc3Rhcic6IHtcbiAgICAvKlxuICAgIE1ldGhvZDogcmVuZGVyXG4gICAgXG4gICAgUmVuZGVycyBhIHN0YXIgKGNvbmNhdmUgZGVjYWdvbikgaW50byB0aGUgY2FudmFzLlxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgdHlwZSAtIChzdHJpbmcpIFBvc3NpYmxlIG9wdGlvbnMgYXJlICdmaWxsJyBvciAnc3Ryb2tlJy5cbiAgICBwb3MgLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgcG9zaXRpb24gb2YgdGhlIGNlbnRlciBvZiB0aGUgc3Rhci5cbiAgICBkaW0gLSAobnVtYmVyKSBUaGUgbGVuZ3RoIG9mIGEgc2lkZSBvZiBhIGNvbmNhdmUgZGVjYWdvbi5cbiAgICBjYW52YXMgLSAob2JqZWN0KSBBIDxDYW52YXM+IGluc3RhbmNlLlxuICAgIFxuICAgIEV4YW1wbGU6XG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgTm9kZUhlbHBlci5zdGFyLnJlbmRlcignc3Ryb2tlJywgeyB4OiAxMCwgeTogMzAgfSwgNDAsIHZpei5jYW52YXMpO1xuICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgICdyZW5kZXInOiBmdW5jdGlvbih0eXBlLCBwb3MsIGRpbSwgY2FudmFzKXtcbiAgICAgIHZhciBjdHggPSBjYW52YXMuZ2V0Q3R4KCksIFxuICAgICAgICAgIHBpNSA9IE1hdGguUEkgLyA1O1xuICAgICAgY3R4LnNhdmUoKTtcbiAgICAgIGN0eC50cmFuc2xhdGUocG9zLngsIHBvcy55KTtcbiAgICAgIGN0eC5iZWdpblBhdGgoKTtcbiAgICAgIGN0eC5tb3ZlVG8oZGltLCAwKTtcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgOTsgaSsrKSB7XG4gICAgICAgIGN0eC5yb3RhdGUocGk1KTtcbiAgICAgICAgaWYgKGkgJSAyID09IDApIHtcbiAgICAgICAgICBjdHgubGluZVRvKChkaW0gLyAwLjUyNTczMSkgKiAwLjIwMDgxMSwgMCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY3R4LmxpbmVUbyhkaW0sIDApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjdHguY2xvc2VQYXRoKCk7XG4gICAgICBjdHhbdHlwZV0oKTtcbiAgICAgIGN0eC5yZXN0b3JlKCk7XG4gICAgfSxcbiAgICAvKlxuICAgIE1ldGhvZDogY29udGFpbnNcbiAgICBcbiAgICBSZXR1cm5zICp0cnVlKiBpZiAqcG9zKiBpcyBjb250YWluZWQgaW4gdGhlIGFyZWEgb2YgdGhlIHNoYXBlLiBSZXR1cm5zICpmYWxzZSogb3RoZXJ3aXNlLlxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgbnBvcyAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSA8R3JhcGguTm9kZT4gcG9zaXRpb24uXG4gICAgcG9zIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggdGhlIHBvc2l0aW9uIHRvIGNoZWNrLlxuICAgIGRpbSAtIChudW1iZXIpIFRoZSBsZW5ndGggb2YgYSBzaWRlIG9mIGEgY29uY2F2ZSBkZWNhZ29uLlxuICAgIFxuICAgIEV4YW1wbGU6XG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgTm9kZUhlbHBlci5zdGFyLmNvbnRhaW5zKHsgeDogMTAsIHk6IDMwIH0sIHsgeDogMTUsIHk6IDM1IH0sIDMwKTtcbiAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICAnY29udGFpbnMnOiBmdW5jdGlvbihucG9zLCBwb3MsIGRpbSkge1xuICAgICAgcmV0dXJuIE5vZGVIZWxwZXIuY2lyY2xlLmNvbnRhaW5zKG5wb3MsIHBvcywgZGltKTtcbiAgICB9XG4gIH1cbn07XG5cbi8qXG4gIE9iamVjdDogRWRnZUhlbHBlclxuICBcbiAgQ29udGFpbnMgcmVuZGVyaW5nIHByaW1pdGl2ZXMgZm9yIHNpbXBsZSBlZGdlIHNoYXBlcy5cbiovXG52YXIgRWRnZUhlbHBlciA9IHtcbiAgLypcbiAgICBPYmplY3Q6IEVkZ2VIZWxwZXIubGluZVxuICAqL1xuICAnbGluZSc6IHtcbiAgICAgIC8qXG4gICAgICBNZXRob2Q6IHJlbmRlclxuICAgICAgXG4gICAgICBSZW5kZXJzIGEgbGluZSBpbnRvIHRoZSBjYW52YXMuXG4gICAgICBcbiAgICAgIFBhcmFtZXRlcnM6XG4gICAgICBcbiAgICAgIGZyb20gLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgc3RhcnRpbmcgcG9zaXRpb24gb2YgdGhlIGxpbmUuXG4gICAgICB0byAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBlbmRpbmcgcG9zaXRpb24gb2YgdGhlIGxpbmUuXG4gICAgICBjYW52YXMgLSAob2JqZWN0KSBBIDxDYW52YXM+IGluc3RhbmNlLlxuICAgICAgXG4gICAgICBFeGFtcGxlOlxuICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICBFZGdlSGVscGVyLmxpbmUucmVuZGVyKHsgeDogMTAsIHk6IDMwIH0sIHsgeDogMTAsIHk6IDUwIH0sIHZpei5jYW52YXMpO1xuICAgICAgKGVuZCBjb2RlKVxuICAgICAgKi9cbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihmcm9tLCB0bywgY2FudmFzKXtcbiAgICAgICAgdmFyIGN0eCA9IGNhbnZhcy5nZXRDdHgoKTtcbiAgICAgICAgY3R4LmJlZ2luUGF0aCgpO1xuICAgICAgICBjdHgubW92ZVRvKGZyb20ueCwgZnJvbS55KTtcbiAgICAgICAgY3R4LmxpbmVUbyh0by54LCB0by55KTtcbiAgICAgICAgY3R4LnN0cm9rZSgpO1xuICAgICAgfSxcbiAgICAgIC8qXG4gICAgICBNZXRob2Q6IGNvbnRhaW5zXG4gICAgICBcbiAgICAgIFJldHVybnMgKnRydWUqIGlmICpwb3MqIGlzIGNvbnRhaW5lZCBpbiB0aGUgYXJlYSBvZiB0aGUgc2hhcGUuIFJldHVybnMgKmZhbHNlKiBvdGhlcndpc2UuXG4gICAgICBcbiAgICAgIFBhcmFtZXRlcnM6XG4gICAgICBcbiAgICAgIHBvc0Zyb20gLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCBhIDxHcmFwaC5Ob2RlPiBwb3NpdGlvbi5cbiAgICAgIHBvc1RvIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggYSA8R3JhcGguTm9kZT4gcG9zaXRpb24uXG4gICAgICBwb3MgLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgcG9zaXRpb24gdG8gY2hlY2suXG4gICAgICBlcHNpbG9uIC0gKG51bWJlcikgVGhlIGRpbWVuc2lvbiBvZiB0aGUgc2hhcGUuXG4gICAgICBcbiAgICAgIEV4YW1wbGU6XG4gICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgIEVkZ2VIZWxwZXIubGluZS5jb250YWlucyh7IHg6IDEwLCB5OiAzMCB9LCB7IHg6IDE1LCB5OiAzNSB9LCB7IHg6IDE1LCB5OiAzNSB9LCAzMCk7XG4gICAgICAoZW5kIGNvZGUpXG4gICAgICAqL1xuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24ocG9zRnJvbSwgcG9zVG8sIHBvcywgZXBzaWxvbikge1xuICAgICAgICB2YXIgbWluID0gTWF0aC5taW4sIFxuICAgICAgICAgICAgbWF4ID0gTWF0aC5tYXgsXG4gICAgICAgICAgICBtaW5Qb3NYID0gbWluKHBvc0Zyb20ueCwgcG9zVG8ueCksXG4gICAgICAgICAgICBtYXhQb3NYID0gbWF4KHBvc0Zyb20ueCwgcG9zVG8ueCksXG4gICAgICAgICAgICBtaW5Qb3NZID0gbWluKHBvc0Zyb20ueSwgcG9zVG8ueSksXG4gICAgICAgICAgICBtYXhQb3NZID0gbWF4KHBvc0Zyb20ueSwgcG9zVG8ueSk7XG4gICAgICAgICAgXG4gICAgICAgIGlmKHBvcy54ID49IG1pblBvc1ggJiYgcG9zLnggPD0gbWF4UG9zWCBcbiAgICAgICAgICAgICYmIHBvcy55ID49IG1pblBvc1kgJiYgcG9zLnkgPD0gbWF4UG9zWSkge1xuICAgICAgICAgIGlmKE1hdGguYWJzKHBvc1RvLnggLSBwb3NGcm9tLngpIDw9IGVwc2lsb24pIHtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICAgIHZhciBkaXN0ID0gKHBvc1RvLnkgLSBwb3NGcm9tLnkpIC8gKHBvc1RvLnggLSBwb3NGcm9tLngpICogKHBvcy54IC0gcG9zRnJvbS54KSArIHBvc0Zyb20ueTtcbiAgICAgICAgICAgIFxuICAgICAgICAgIHJldHVybiBNYXRoLmFicyhkaXN0IC0gcG9zLnkpIDw9IGVwc2lsb247XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgIH0sXG4gIC8qXG4gICAgT2JqZWN0OiBFZGdlSGVscGVyLmFycm93XG4gICovXG4gICdhcnJvdyc6IHtcbiAgICAgIC8qXG4gICAgICBNZXRob2Q6IHJlbmRlclxuICAgICAgXG4gICAgICBSZW5kZXJzIGFuIGFycm93IGludG8gdGhlIGNhbnZhcy5cbiAgICAgIFxuICAgICAgUGFyYW1ldGVyczpcbiAgICAgIFxuICAgICAgZnJvbSAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBzdGFydGluZyBwb3NpdGlvbiBvZiB0aGUgYXJyb3cuXG4gICAgICB0byAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBlbmRpbmcgcG9zaXRpb24gb2YgdGhlIGFycm93LlxuICAgICAgZGltIC0gKG51bWJlcikgVGhlIGRpbWVuc2lvbiBvZiB0aGUgYXJyb3cuXG4gICAgICBzd2FwIC0gKGJvb2xlYW4pIFdoZXRoZXIgdG8gc2V0IHRoZSBhcnJvdyBwb2ludGluZyB0byB0aGUgc3RhcnRpbmcgcG9zaXRpb24gb3IgdGhlIGVuZGluZyBwb3NpdGlvbi5cbiAgICAgIGNhbnZhcyAtIChvYmplY3QpIEEgPENhbnZhcz4gaW5zdGFuY2UuXG4gICAgICBcbiAgICAgIEV4YW1wbGU6XG4gICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgIEVkZ2VIZWxwZXIuYXJyb3cucmVuZGVyKHsgeDogMTAsIHk6IDMwIH0sIHsgeDogMTAsIHk6IDUwIH0sIDEzLCBmYWxzZSwgdml6LmNhbnZhcyk7XG4gICAgICAoZW5kIGNvZGUpXG4gICAgICAqL1xuICAgICdyZW5kZXInOiBmdW5jdGlvbihmcm9tLCB0bywgZGltLCBzd2FwLCBjYW52YXMpe1xuICAgICAgICB2YXIgY3R4ID0gY2FudmFzLmdldEN0eCgpO1xuICAgICAgICAvLyBpbnZlcnQgZWRnZSBkaXJlY3Rpb25cbiAgICAgICAgaWYgKHN3YXApIHtcbiAgICAgICAgICB2YXIgdG1wID0gZnJvbTtcbiAgICAgICAgICBmcm9tID0gdG87XG4gICAgICAgICAgdG8gPSB0bXA7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIHZlY3QgPSBuZXcgQ29tcGxleCh0by54IC0gZnJvbS54LCB0by55IC0gZnJvbS55KTtcbiAgICAgICAgdmVjdC4kc2NhbGUoZGltIC8gdmVjdC5ub3JtKCkpO1xuICAgICAgICB2YXIgaW50ZXJtZWRpYXRlUG9pbnQgPSBuZXcgQ29tcGxleCh0by54IC0gdmVjdC54LCB0by55IC0gdmVjdC55KSxcbiAgICAgICAgICAgIG5vcm1hbCA9IG5ldyBDb21wbGV4KC12ZWN0LnkgLyAyLCB2ZWN0LnggLyAyKSxcbiAgICAgICAgICAgIHYxID0gaW50ZXJtZWRpYXRlUG9pbnQuYWRkKG5vcm1hbCksIFxuICAgICAgICAgICAgdjIgPSBpbnRlcm1lZGlhdGVQb2ludC4kYWRkKG5vcm1hbC4kc2NhbGUoLTEpKTtcbiAgICAgICAgXG4gICAgICAgIGN0eC5iZWdpblBhdGgoKTtcbiAgICAgICAgY3R4Lm1vdmVUbyhmcm9tLngsIGZyb20ueSk7XG4gICAgICAgIGN0eC5saW5lVG8odG8ueCwgdG8ueSk7XG4gICAgICAgIGN0eC5zdHJva2UoKTtcbiAgICAgICAgY3R4LmJlZ2luUGF0aCgpO1xuICAgICAgICBjdHgubW92ZVRvKHYxLngsIHYxLnkpO1xuICAgICAgICBjdHgubGluZVRvKHYyLngsIHYyLnkpO1xuICAgICAgICBjdHgubGluZVRvKHRvLngsIHRvLnkpO1xuICAgICAgICBjdHguY2xvc2VQYXRoKCk7XG4gICAgICAgIGN0eC5maWxsKCk7XG4gICAgfSxcbiAgICAvKlxuICAgIE1ldGhvZDogY29udGFpbnNcbiAgICBcbiAgICBSZXR1cm5zICp0cnVlKiBpZiAqcG9zKiBpcyBjb250YWluZWQgaW4gdGhlIGFyZWEgb2YgdGhlIHNoYXBlLiBSZXR1cm5zICpmYWxzZSogb3RoZXJ3aXNlLlxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgcG9zRnJvbSAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIGEgPEdyYXBoLk5vZGU+IHBvc2l0aW9uLlxuICAgIHBvc1RvIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggYSA8R3JhcGguTm9kZT4gcG9zaXRpb24uXG4gICAgcG9zIC0gKG9iamVjdCkgQW4gKngqLCAqeSogb2JqZWN0IHdpdGggdGhlIHBvc2l0aW9uIHRvIGNoZWNrLlxuICAgIGVwc2lsb24gLSAobnVtYmVyKSBUaGUgZGltZW5zaW9uIG9mIHRoZSBzaGFwZS5cbiAgICBcbiAgICBFeGFtcGxlOlxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgIEVkZ2VIZWxwZXIuYXJyb3cuY29udGFpbnMoeyB4OiAxMCwgeTogMzAgfSwgeyB4OiAxNSwgeTogMzUgfSwgeyB4OiAxNSwgeTogMzUgfSwgMzApO1xuICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgICdjb250YWlucyc6IGZ1bmN0aW9uKHBvc0Zyb20sIHBvc1RvLCBwb3MsIGVwc2lsb24pIHtcbiAgICAgIHJldHVybiBFZGdlSGVscGVyLmxpbmUuY29udGFpbnMocG9zRnJvbSwgcG9zVG8sIHBvcywgZXBzaWxvbik7XG4gICAgfVxuICB9LFxuICAvKlxuICAgIE9iamVjdDogRWRnZUhlbHBlci5oeXBlcmxpbmVcbiAgKi9cbiAgJ2h5cGVybGluZSc6IHtcbiAgICAvKlxuICAgIE1ldGhvZDogcmVuZGVyXG4gICAgXG4gICAgUmVuZGVycyBhIGh5cGVybGluZSBpbnRvIHRoZSBjYW52YXMuIEEgaHlwZXJsaW5lIGFyZSB0aGUgbGluZXMgZHJhd24gZm9yIHRoZSA8SHlwZXJ0cmVlPiB2aXN1YWxpemF0aW9uLlxuICAgIFxuICAgIFBhcmFtZXRlcnM6XG4gICAgXG4gICAgZnJvbSAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBzdGFydGluZyBwb3NpdGlvbiBvZiB0aGUgaHlwZXJsaW5lLiAqeCogYW5kICp5KiBtdXN0IGJlbG9uZyB0byBbMCwgMSkuXG4gICAgdG8gLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCB0aGUgZW5kaW5nIHBvc2l0aW9uIG9mIHRoZSBoeXBlcmxpbmUuICp4KiBhbmQgKnkqIG11c3QgYmVsb25nIHRvIFswLCAxKS5cbiAgICByIC0gKG51bWJlcikgVGhlIHNjYWxpbmcgZmFjdG9yLlxuICAgIGNhbnZhcyAtIChvYmplY3QpIEEgPENhbnZhcz4gaW5zdGFuY2UuXG4gICAgXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICBFZGdlSGVscGVyLmh5cGVybGluZS5yZW5kZXIoeyB4OiAxMCwgeTogMzAgfSwgeyB4OiAxMCwgeTogNTAgfSwgMTAwLCB2aXouY2FudmFzKTtcbiAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICAncmVuZGVyJzogZnVuY3Rpb24oZnJvbSwgdG8sIHIsIGNhbnZhcyl7XG4gICAgICB2YXIgY3R4ID0gY2FudmFzLmdldEN0eCgpOyAgXG4gICAgICB2YXIgY2VudGVyT2ZDaXJjbGUgPSBjb21wdXRlQXJjVGhyb3VnaFR3b1BvaW50cyhmcm9tLCB0byk7XG4gICAgICBpZiAoY2VudGVyT2ZDaXJjbGUuYSA+IDEwMDAgfHwgY2VudGVyT2ZDaXJjbGUuYiA+IDEwMDBcbiAgICAgICAgICB8fCBjZW50ZXJPZkNpcmNsZS5yYXRpbyA8IDApIHtcbiAgICAgICAgY3R4LmJlZ2luUGF0aCgpO1xuICAgICAgICBjdHgubW92ZVRvKGZyb20ueCAqIHIsIGZyb20ueSAqIHIpO1xuICAgICAgICBjdHgubGluZVRvKHRvLnggKiByLCB0by55ICogcik7XG4gICAgICAgIGN0eC5zdHJva2UoKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBhbmdsZUJlZ2luID0gTWF0aC5hdGFuMih0by55IC0gY2VudGVyT2ZDaXJjbGUueSwgdG8ueFxuICAgICAgICAgICAgLSBjZW50ZXJPZkNpcmNsZS54KTtcbiAgICAgICAgdmFyIGFuZ2xlRW5kID0gTWF0aC5hdGFuMihmcm9tLnkgLSBjZW50ZXJPZkNpcmNsZS55LCBmcm9tLnhcbiAgICAgICAgICAgIC0gY2VudGVyT2ZDaXJjbGUueCk7XG4gICAgICAgIHZhciBzZW5zZSA9IHNlbnNlKGFuZ2xlQmVnaW4sIGFuZ2xlRW5kKTtcbiAgICAgICAgY3R4LmJlZ2luUGF0aCgpO1xuICAgICAgICBjdHguYXJjKGNlbnRlck9mQ2lyY2xlLnggKiByLCBjZW50ZXJPZkNpcmNsZS55ICogciwgY2VudGVyT2ZDaXJjbGUucmF0aW9cbiAgICAgICAgICAgICogciwgYW5nbGVCZWdpbiwgYW5nbGVFbmQsIHNlbnNlKTtcbiAgICAgICAgY3R4LnN0cm9rZSgpO1xuICAgICAgfVxuICAgICAgLyogICAgICBcbiAgICAgICAgQ2FsY3VsYXRlcyB0aGUgYXJjIHBhcmFtZXRlcnMgdGhyb3VnaCB0d28gcG9pbnRzLlxuICAgICAgICBcbiAgICAgICAgTW9yZSBpbmZvcm1hdGlvbiBpbiA8aHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Qb2luY2FyJUMzJUE5X2Rpc2NfbW9kZWwjQW5hbHl0aWNfZ2VvbWV0cnlfY29uc3RydWN0aW9uc19pbl90aGVfaHlwZXJib2xpY19wbGFuZT4gXG4gICAgICBcbiAgICAgICAgUGFyYW1ldGVyczpcbiAgICAgIFxuICAgICAgICBwMSAtIEEgPENvbXBsZXg+IGluc3RhbmNlLlxuICAgICAgICBwMiAtIEEgPENvbXBsZXg+IGluc3RhbmNlLlxuICAgICAgICBzY2FsZSAtIFRoZSBEaXNrJ3MgZGlhbWV0ZXIuXG4gICAgICBcbiAgICAgICAgUmV0dXJuczpcbiAgICAgIFxuICAgICAgICBBbiBvYmplY3QgY29udGFpbmluZyBzb21lIGFyYyBwcm9wZXJ0aWVzLlxuICAgICAgKi9cbiAgICAgIGZ1bmN0aW9uIGNvbXB1dGVBcmNUaHJvdWdoVHdvUG9pbnRzKHAxLCBwMil7XG4gICAgICAgIHZhciBhRGVuID0gKHAxLnggKiBwMi55IC0gcDEueSAqIHAyLngpLCBiRGVuID0gYURlbjtcbiAgICAgICAgdmFyIHNxMSA9IHAxLnNxdWFyZWROb3JtKCksIHNxMiA9IHAyLnNxdWFyZWROb3JtKCk7XG4gICAgICAgIC8vIEZhbGwgYmFjayB0byBhIHN0cmFpZ2h0IGxpbmVcbiAgICAgICAgaWYgKGFEZW4gPT0gMClcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgeDogMCxcbiAgICAgICAgICAgIHk6IDAsXG4gICAgICAgICAgICByYXRpbzogLTFcbiAgICAgICAgICB9O1xuICAgIFxuICAgICAgICB2YXIgYSA9IChwMS55ICogc3EyIC0gcDIueSAqIHNxMSArIHAxLnkgLSBwMi55KSAvIGFEZW47XG4gICAgICAgIHZhciBiID0gKHAyLnggKiBzcTEgLSBwMS54ICogc3EyICsgcDIueCAtIHAxLngpIC8gYkRlbjtcbiAgICAgICAgdmFyIHggPSAtYSAvIDI7XG4gICAgICAgIHZhciB5ID0gLWIgLyAyO1xuICAgICAgICB2YXIgc3F1YXJlZFJhdGlvID0gKGEgKiBhICsgYiAqIGIpIC8gNCAtIDE7XG4gICAgICAgIC8vIEZhbGwgYmFjayB0byBhIHN0cmFpZ2h0IGxpbmVcbiAgICAgICAgaWYgKHNxdWFyZWRSYXRpbyA8IDApXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHg6IDAsXG4gICAgICAgICAgICB5OiAwLFxuICAgICAgICAgICAgcmF0aW86IC0xXG4gICAgICAgICAgfTtcbiAgICAgICAgdmFyIHJhdGlvID0gTWF0aC5zcXJ0KHNxdWFyZWRSYXRpbyk7XG4gICAgICAgIHZhciBvdXQgPSB7XG4gICAgICAgICAgeDogeCxcbiAgICAgICAgICB5OiB5LFxuICAgICAgICAgIHJhdGlvOiByYXRpbyA+IDEwMDA/IC0xIDogcmF0aW8sXG4gICAgICAgICAgYTogYSxcbiAgICAgICAgICBiOiBiXG4gICAgICAgIH07XG4gICAgXG4gICAgICAgIHJldHVybiBvdXQ7XG4gICAgICB9XG4gICAgICAvKiAgICAgIFxuICAgICAgICBTZXRzIGFuZ2xlIGRpcmVjdGlvbiB0byBjbG9ja3dpc2UgKHRydWUpIG9yIGNvdW50ZXJjbG9ja3dpc2UgKGZhbHNlKS4gXG4gICAgICAgICBcbiAgICAgICAgUGFyYW1ldGVyczogXG4gICAgICBcbiAgICAgICAgICAgYW5nbGVCZWdpbiAtIFN0YXJ0aW5nIGFuZ2xlIGZvciBkcmF3aW5nIHRoZSBhcmMuIFxuICAgICAgICAgICBhbmdsZUVuZCAtIFRoZSBIeXBlckxpbmUgd2lsbCBiZSBkcmF3biBmcm9tIGFuZ2xlQmVnaW4gdG8gYW5nbGVFbmQuIFxuICAgICAgXG4gICAgICAgIFJldHVybnM6IFxuICAgICAgXG4gICAgICAgICAgIEEgQm9vbGVhbiBpbnN0YW5jZSBkZXNjcmliaW5nIHRoZSBzZW5zZSBmb3IgZHJhd2luZyB0aGUgSHlwZXJMaW5lLiBcbiAgICAgICovXG4gICAgICBmdW5jdGlvbiBzZW5zZShhbmdsZUJlZ2luLCBhbmdsZUVuZCl7XG4gICAgICAgIHJldHVybiAoYW5nbGVCZWdpbiA8IGFuZ2xlRW5kKT8gKChhbmdsZUJlZ2luICsgTWF0aC5QSSA+IGFuZ2xlRW5kKT8gZmFsc2VcbiAgICAgICAgICAgIDogdHJ1ZSkgOiAoKGFuZ2xlRW5kICsgTWF0aC5QSSA+IGFuZ2xlQmVnaW4pPyB0cnVlIDogZmFsc2UpO1xuICAgICAgfVxuICAgIH0sXG4gICAgLypcbiAgICBNZXRob2Q6IGNvbnRhaW5zXG4gICAgXG4gICAgTm90IEltcGxlbWVudGVkXG4gICAgXG4gICAgUmV0dXJucyAqdHJ1ZSogaWYgKnBvcyogaXMgY29udGFpbmVkIGluIHRoZSBhcmVhIG9mIHRoZSBzaGFwZS4gUmV0dXJucyAqZmFsc2UqIG90aGVyd2lzZS5cbiAgICBcbiAgICBQYXJhbWV0ZXJzOlxuICAgIFxuICAgIHBvc0Zyb20gLSAob2JqZWN0KSBBbiAqeCosICp5KiBvYmplY3Qgd2l0aCBhIDxHcmFwaC5Ob2RlPiBwb3NpdGlvbi5cbiAgICBwb3NUbyAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIGEgPEdyYXBoLk5vZGU+IHBvc2l0aW9uLlxuICAgIHBvcyAtIChvYmplY3QpIEFuICp4KiwgKnkqIG9iamVjdCB3aXRoIHRoZSBwb3NpdGlvbiB0byBjaGVjay5cbiAgICBlcHNpbG9uIC0gKG51bWJlcikgVGhlIGRpbWVuc2lvbiBvZiB0aGUgc2hhcGUuXG4gICAgXG4gICAgRXhhbXBsZTpcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICBFZGdlSGVscGVyLmh5cGVybGluZS5jb250YWlucyh7IHg6IDEwLCB5OiAzMCB9LCB7IHg6IDE1LCB5OiAzNSB9LCB7IHg6IDE1LCB5OiAzNSB9LCAzMCk7XG4gICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgJ2NvbnRhaW5zJzogJC5sYW1iZGEoZmFsc2UpXG4gIH1cbn07XG5cblxuLypcbiAqIEZpbGU6IEdyYXBoLlBsb3QuanNcbiAqL1xuXG4vKlxuICAgT2JqZWN0OiBHcmFwaC5QbG90XG5cbiAgIDxHcmFwaD4gcmVuZGVyaW5nIGFuZCBhbmltYXRpb24gbWV0aG9kcy5cbiAgIFxuICAgUHJvcGVydGllczpcbiAgIFxuICAgbm9kZUhlbHBlciAtIDxOb2RlSGVscGVyPiBvYmplY3QuXG4gICBlZGdlSGVscGVyIC0gPEVkZ2VIZWxwZXI+IG9iamVjdC5cbiovXG5HcmFwaC5QbG90ID0ge1xuICAgIC8vRGVmYXVsdCBpbml0aWFsaXplclxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKHZpeiwga2xhc3Mpe1xuICAgICAgdGhpcy52aXogPSB2aXo7XG4gICAgICB0aGlzLmNvbmZpZyA9IHZpei5jb25maWc7XG4gICAgICB0aGlzLm5vZGUgPSB2aXouY29uZmlnLk5vZGU7XG4gICAgICB0aGlzLmVkZ2UgPSB2aXouY29uZmlnLkVkZ2U7XG4gICAgICB0aGlzLmFuaW1hdGlvbiA9IG5ldyBBbmltYXRpb247XG4gICAgICB0aGlzLm5vZGVUeXBlcyA9IG5ldyBrbGFzcy5QbG90Lk5vZGVUeXBlcztcbiAgICAgIHRoaXMuZWRnZVR5cGVzID0gbmV3IGtsYXNzLlBsb3QuRWRnZVR5cGVzO1xuICAgICAgdGhpcy5sYWJlbHMgPSB2aXoubGFiZWxzO1xuICAgfSxcblxuICAgIC8vQWRkIGhlbHBlcnNcbiAgICBub2RlSGVscGVyOiBOb2RlSGVscGVyLFxuICAgIGVkZ2VIZWxwZXI6IEVkZ2VIZWxwZXIsXG4gICAgXG4gICAgSW50ZXJwb2xhdG9yOiB7XG4gICAgICAgIC8vbm9kZS9lZGdlIHByb3BlcnR5IHBhcnNlcnNcbiAgICAgICAgJ21hcCc6IHtcbiAgICAgICAgICAnYm9yZGVyJzogJ2NvbG9yJyxcbiAgICAgICAgICAnY29sb3InOiAnY29sb3InLFxuICAgICAgICAgICd3aWR0aCc6ICdudW1iZXInLFxuICAgICAgICAgICdoZWlnaHQnOiAnbnVtYmVyJyxcbiAgICAgICAgICAnZGltJzogJ251bWJlcicsXG4gICAgICAgICAgJ2FscGhhJzogJ251bWJlcicsXG4gICAgICAgICAgJ2xpbmVXaWR0aCc6ICdudW1iZXInLFxuICAgICAgICAgICdhbmd1bGFyV2lkdGgnOidudW1iZXInLFxuICAgICAgICAgICdzcGFuJzonbnVtYmVyJyxcbiAgICAgICAgICAndmFsdWVBcnJheSc6J2FycmF5LW51bWJlcicsXG4gICAgICAgICAgJ2RpbUFycmF5JzonYXJyYXktbnVtYmVyJ1xuICAgICAgICAgIC8vJ2NvbG9yQXJyYXknOidhcnJheS1jb2xvcidcbiAgICAgICAgfSxcbiAgICAgICAgXG4gICAgICAgIC8vY2FudmFzIHNwZWNpZmljIHBhcnNlcnNcbiAgICAgICAgJ2NhbnZhcyc6IHtcbiAgICAgICAgICAnZ2xvYmFsQWxwaGEnOiAnbnVtYmVyJyxcbiAgICAgICAgICAnZmlsbFN0eWxlJzogJ2NvbG9yJyxcbiAgICAgICAgICAnc3Ryb2tlU3R5bGUnOiAnY29sb3InLFxuICAgICAgICAgICdsaW5lV2lkdGgnOiAnbnVtYmVyJyxcbiAgICAgICAgICAnc2hhZG93Qmx1cic6ICdudW1iZXInLFxuICAgICAgICAgICdzaGFkb3dDb2xvcic6ICdjb2xvcicsXG4gICAgICAgICAgJ3NoYWRvd09mZnNldFgnOiAnbnVtYmVyJyxcbiAgICAgICAgICAnc2hhZG93T2Zmc2V0WSc6ICdudW1iZXInLFxuICAgICAgICAgICdtaXRlckxpbWl0JzogJ251bWJlcidcbiAgICAgICAgfSxcbiAgXG4gICAgICAgIC8vbGFiZWwgcGFyc2Vyc1xuICAgICAgICAnbGFiZWwnOiB7XG4gICAgICAgICAgJ3NpemUnOiAnbnVtYmVyJyxcbiAgICAgICAgICAnY29sb3InOiAnY29sb3InXG4gICAgICAgIH0sXG4gIFxuICAgICAgICAvL051bWJlciBpbnRlcnBvbGF0b3JcbiAgICAgICAgJ2NvbXB1dGUnOiBmdW5jdGlvbihmcm9tLCB0bywgZGVsdGEpIHtcbiAgICAgICAgICByZXR1cm4gZnJvbSArICh0byAtIGZyb20pICogZGVsdGE7XG4gICAgICAgIH0sXG4gICAgICAgIFxuICAgICAgICAvL1Bvc2l0aW9uIGludGVycG9sYXRvcnNcbiAgICAgICAgJ21vZWJpdXMnOiBmdW5jdGlvbihlbGVtLCBwcm9wcywgZGVsdGEsIHZlY3Rvcikge1xuICAgICAgICAgIHZhciB2ID0gdmVjdG9yLnNjYWxlKC1kZWx0YSk7ICBcbiAgICAgICAgICBpZih2Lm5vcm0oKSA8IDEpIHtcbiAgICAgICAgICAgICAgdmFyIHggPSB2LngsIHkgPSB2Lnk7XG4gICAgICAgICAgICAgIHZhciBhbnMgPSBlbGVtLnN0YXJ0UG9zXG4gICAgICAgICAgICAgICAgLmdldGMoKS5tb2ViaXVzVHJhbnNmb3JtYXRpb24odik7XG4gICAgICAgICAgICAgIGVsZW0ucG9zLnNldGMoYW5zLngsIGFucy55KTtcbiAgICAgICAgICAgICAgdi54ID0geDsgdi55ID0geTtcbiAgICAgICAgICAgIH0gICAgICAgICAgIFxuICAgICAgICB9LFxuXG4gICAgICAgICdsaW5lYXInOiBmdW5jdGlvbihlbGVtLCBwcm9wcywgZGVsdGEpIHtcbiAgICAgICAgICAgIHZhciBmcm9tID0gZWxlbS5zdGFydFBvcy5nZXRjKHRydWUpO1xuICAgICAgICAgICAgdmFyIHRvID0gZWxlbS5lbmRQb3MuZ2V0Yyh0cnVlKTtcbiAgICAgICAgICAgIGVsZW0ucG9zLnNldGModGhpcy5jb21wdXRlKGZyb20ueCwgdG8ueCwgZGVsdGEpLCBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5jb21wdXRlKGZyb20ueSwgdG8ueSwgZGVsdGEpKTtcbiAgICAgICAgfSxcblxuICAgICAgICAncG9sYXInOiBmdW5jdGlvbihlbGVtLCBwcm9wcywgZGVsdGEpIHtcbiAgICAgICAgICB2YXIgZnJvbSA9IGVsZW0uc3RhcnRQb3MuZ2V0cCh0cnVlKTtcbiAgICAgICAgICB2YXIgdG8gPSBlbGVtLmVuZFBvcy5nZXRwKCk7XG4gICAgICAgICAgdmFyIGFucyA9IHRvLmludGVycG9sYXRlKGZyb20sIGRlbHRhKTtcbiAgICAgICAgICBlbGVtLnBvcy5zZXRwKGFucy50aGV0YSwgYW5zLnJobyk7XG4gICAgICAgIH0sXG4gICAgICAgIFxuICAgICAgICAvL0dyYXBoJ3MgTm9kZS9FZGdlIGludGVycG9sYXRvcnNcbiAgICAgICAgJ251bWJlcic6IGZ1bmN0aW9uKGVsZW0sIHByb3AsIGRlbHRhLCBnZXR0ZXIsIHNldHRlcikge1xuICAgICAgICAgIHZhciBmcm9tID0gZWxlbVtnZXR0ZXJdKHByb3AsICdzdGFydCcpO1xuICAgICAgICAgIHZhciB0byA9IGVsZW1bZ2V0dGVyXShwcm9wLCAnZW5kJyk7XG4gICAgICAgICAgZWxlbVtzZXR0ZXJdKHByb3AsIHRoaXMuY29tcHV0ZShmcm9tLCB0bywgZGVsdGEpKTtcbiAgICAgICAgfSxcblxuICAgICAgICAnY29sb3InOiBmdW5jdGlvbihlbGVtLCBwcm9wLCBkZWx0YSwgZ2V0dGVyLCBzZXR0ZXIpIHtcbiAgICAgICAgICB2YXIgZnJvbSA9ICQuaGV4VG9SZ2IoZWxlbVtnZXR0ZXJdKHByb3AsICdzdGFydCcpKTtcbiAgICAgICAgICB2YXIgdG8gPSAkLmhleFRvUmdiKGVsZW1bZ2V0dGVyXShwcm9wLCAnZW5kJykpO1xuICAgICAgICAgIHZhciBjb21wID0gdGhpcy5jb21wdXRlO1xuICAgICAgICAgIHZhciB2YWwgPSAkLnJnYlRvSGV4KFtwYXJzZUludChjb21wKGZyb21bMF0sIHRvWzBdLCBkZWx0YSkpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJzZUludChjb21wKGZyb21bMV0sIHRvWzFdLCBkZWx0YSkpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJzZUludChjb21wKGZyb21bMl0sIHRvWzJdLCBkZWx0YSkpXSk7XG4gICAgICAgICAgXG4gICAgICAgICAgZWxlbVtzZXR0ZXJdKHByb3AsIHZhbCk7XG4gICAgICAgIH0sXG4gICAgICAgIFxuICAgICAgICAnYXJyYXktbnVtYmVyJzogZnVuY3Rpb24oZWxlbSwgcHJvcCwgZGVsdGEsIGdldHRlciwgc2V0dGVyKSB7XG4gICAgICAgICAgdmFyIGZyb20gPSBlbGVtW2dldHRlcl0ocHJvcCwgJ3N0YXJ0JyksXG4gICAgICAgICAgICAgIHRvID0gZWxlbVtnZXR0ZXJdKHByb3AsICdlbmQnKSxcbiAgICAgICAgICAgICAgY3VyID0gW107XG4gICAgICAgICAgZm9yKHZhciBpPTAsIGw9ZnJvbS5sZW5ndGg7IGk8bDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgZnJvbWkgPSBmcm9tW2ldLCB0b2kgPSB0b1tpXTtcbiAgICAgICAgICAgIGlmKGZyb21pLmxlbmd0aCkge1xuICAgICAgICAgICAgICBmb3IodmFyIGo9MCwgbGVuPWZyb21pLmxlbmd0aCwgY3VyaT1bXTsgajxsZW47IGorKykge1xuICAgICAgICAgICAgICAgIGN1cmkucHVzaCh0aGlzLmNvbXB1dGUoZnJvbWlbal0sIHRvaVtqXSwgZGVsdGEpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjdXIucHVzaChjdXJpKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGN1ci5wdXNoKHRoaXMuY29tcHV0ZShmcm9taSwgdG9pLCBkZWx0YSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBlbGVtW3NldHRlcl0ocHJvcCwgY3VyKTtcbiAgICAgICAgfSxcbiAgICAgICAgXG4gICAgICAgICdub2RlJzogZnVuY3Rpb24oZWxlbSwgcHJvcHMsIGRlbHRhLCBtYXAsIGdldHRlciwgc2V0dGVyKSB7XG4gICAgICAgICAgbWFwID0gdGhpc1ttYXBdO1xuICAgICAgICAgIGlmKHByb3BzKSB7XG4gICAgICAgICAgICB2YXIgbGVuID0gcHJvcHMubGVuZ3RoO1xuICAgICAgICAgICAgZm9yKHZhciBpPTA7IGk8bGVuOyBpKyspIHtcbiAgICAgICAgICAgICAgdmFyIHBpID0gcHJvcHNbaV07XG4gICAgICAgICAgICAgIHRoaXNbbWFwW3BpXV0oZWxlbSwgcGksIGRlbHRhLCBnZXR0ZXIsIHNldHRlcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGZvcih2YXIgcGkgaW4gbWFwKSB7XG4gICAgICAgICAgICAgIHRoaXNbbWFwW3BpXV0oZWxlbSwgcGksIGRlbHRhLCBnZXR0ZXIsIHNldHRlcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBcbiAgICAgICAgJ2VkZ2UnOiBmdW5jdGlvbihlbGVtLCBwcm9wcywgZGVsdGEsIG1hcEtleSwgZ2V0dGVyLCBzZXR0ZXIpIHtcbiAgICAgICAgICAgIHZhciBhZGpzID0gZWxlbS5hZGphY2VuY2llcztcbiAgICAgICAgICAgIGZvcih2YXIgaWQgaW4gYWRqcykgdGhpc1snbm9kZSddKGFkanNbaWRdLCBwcm9wcywgZGVsdGEsIG1hcEtleSwgZ2V0dGVyLCBzZXR0ZXIpO1xuICAgICAgICB9LFxuICAgICAgICBcbiAgICAgICAgJ25vZGUtcHJvcGVydHknOiBmdW5jdGlvbihlbGVtLCBwcm9wcywgZGVsdGEpIHtcbiAgICAgICAgICB0aGlzWydub2RlJ10oZWxlbSwgcHJvcHMsIGRlbHRhLCAnbWFwJywgJ2dldERhdGEnLCAnc2V0RGF0YScpO1xuICAgICAgICB9LFxuICAgICAgICBcbiAgICAgICAgJ2VkZ2UtcHJvcGVydHknOiBmdW5jdGlvbihlbGVtLCBwcm9wcywgZGVsdGEpIHtcbiAgICAgICAgICB0aGlzWydlZGdlJ10oZWxlbSwgcHJvcHMsIGRlbHRhLCAnbWFwJywgJ2dldERhdGEnLCAnc2V0RGF0YScpOyAgXG4gICAgICAgIH0sXG5cbiAgICAgICAgJ2xhYmVsLXByb3BlcnR5JzogZnVuY3Rpb24oZWxlbSwgcHJvcHMsIGRlbHRhKSB7XG4gICAgICAgICAgdGhpc1snbm9kZSddKGVsZW0sIHByb3BzLCBkZWx0YSwgJ2xhYmVsJywgJ2dldExhYmVsRGF0YScsICdzZXRMYWJlbERhdGEnKTtcbiAgICAgICAgfSxcbiAgICAgICAgXG4gICAgICAgICdub2RlLXN0eWxlJzogZnVuY3Rpb24oZWxlbSwgcHJvcHMsIGRlbHRhKSB7XG4gICAgICAgICAgdGhpc1snbm9kZSddKGVsZW0sIHByb3BzLCBkZWx0YSwgJ2NhbnZhcycsICdnZXRDYW52YXNTdHlsZScsICdzZXRDYW52YXNTdHlsZScpO1xuICAgICAgICB9LFxuICAgICAgICBcbiAgICAgICAgJ2VkZ2Utc3R5bGUnOiBmdW5jdGlvbihlbGVtLCBwcm9wcywgZGVsdGEpIHtcbiAgICAgICAgICB0aGlzWydlZGdlJ10oZWxlbSwgcHJvcHMsIGRlbHRhLCAnY2FudmFzJywgJ2dldENhbnZhc1N0eWxlJywgJ3NldENhbnZhc1N0eWxlJyk7ICBcbiAgICAgICAgfVxuICAgIH0sXG4gICAgXG4gIFxuICAgIC8qXG4gICAgICAgc2VxdWVuY2VcbiAgICBcbiAgICAgICBJdGVyYXRpdmVseSBwZXJmb3JtcyBhbiBhY3Rpb24gd2hpbGUgcmVmcmVzaGluZyB0aGUgc3RhdGUgb2YgdGhlIHZpc3VhbGl6YXRpb24uXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgb3B0aW9ucyAtIChvYmplY3QpIEFuIG9iamVjdCBjb250YWluaW5nIHNvbWUgc2VxdWVuY2Ugb3B0aW9ucyBkZXNjcmliZWQgYmVsb3dcbiAgICAgICBjb25kaXRpb24gLSAoZnVuY3Rpb24pIEEgZnVuY3Rpb24gcmV0dXJuaW5nIGEgYm9vbGVhbiBpbnN0YW5jZSBpbiBvcmRlciB0byBzdG9wIGl0ZXJhdGlvbnMuXG4gICAgICAgc3RlcCAtIChmdW5jdGlvbikgQSBmdW5jdGlvbiB0byBleGVjdXRlIG9uIGVhY2ggc3RlcCBvZiB0aGUgaXRlcmF0aW9uLlxuICAgICAgIG9uQ29tcGxldGUgLSAoZnVuY3Rpb24pIEEgZnVuY3Rpb24gdG8gZXhlY3V0ZSB3aGVuIHRoZSBzZXF1ZW5jZSBmaW5pc2hlcy5cbiAgICAgICBkdXJhdGlvbiAtIChudW1iZXIpIER1cmF0aW9uIChpbiBtaWxsaXNlY29uZHMpIG9mIGVhY2ggc3RlcC5cblxuICAgICAgRXhhbXBsZTpcbiAgICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgdmFyIHJnID0gbmV3ICRqaXQuUkdyYXBoKG9wdGlvbnMpO1xuICAgICAgICB2YXIgaSA9IDA7XG4gICAgICAgIHJnLmZ4LnNlcXVlbmNlKHtcbiAgICAgICAgICBjb25kaXRpb246IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICByZXR1cm4gaSA9PSAxMDtcbiAgICAgICAgICB9LFxuICAgICAgICAgIHN0ZXA6IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgYWxlcnQoaSsrKTtcbiAgICAgICAgICB9LFxuICAgICAgICAgIG9uQ29tcGxldGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICBhbGVydCgnZG9uZSEnKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgIChlbmQgY29kZSlcblxuICAgICovXG4gICAgc2VxdWVuY2U6IGZ1bmN0aW9uKG9wdGlvbnMpIHtcbiAgICAgICAgdmFyIHRoYXQgPSB0aGlzO1xuICAgICAgICBvcHRpb25zID0gJC5tZXJnZSh7XG4gICAgICAgICAgY29uZGl0aW9uOiAkLmxhbWJkYShmYWxzZSksXG4gICAgICAgICAgc3RlcDogJC5lbXB0eSxcbiAgICAgICAgICBvbkNvbXBsZXRlOiAkLmVtcHR5LFxuICAgICAgICAgIGR1cmF0aW9uOiAyMDBcbiAgICAgICAgfSwgb3B0aW9ucyB8fCB7fSk7XG5cbiAgICAgICAgdmFyIGludGVydmFsID0gc2V0SW50ZXJ2YWwoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgaWYob3B0aW9ucy5jb25kaXRpb24oKSkge1xuICAgICAgICAgICAgb3B0aW9ucy5zdGVwKCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwoaW50ZXJ2YWwpO1xuICAgICAgICAgICAgb3B0aW9ucy5vbkNvbXBsZXRlKCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoYXQudml6LnJlZnJlc2godHJ1ZSk7XG4gICAgICAgIH0sIG9wdGlvbnMuZHVyYXRpb24pO1xuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgIHByZXBhcmVcbiBcbiAgICAgIFByZXBhcmUgZ3JhcGggcG9zaXRpb24gYW5kIG90aGVyIGF0dHJpYnV0ZSB2YWx1ZXMgYmVmb3JlIHBlcmZvcm1pbmcgYW4gQW5pbWF0aW9uLiBcbiAgICAgIFRoaXMgbWV0aG9kIGlzIHVzZWQgaW50ZXJuYWxseSBieSB0aGUgVG9vbGtpdC5cbiAgICAgIFxuICAgICAgU2VlIGFsc286XG4gICAgICAgXG4gICAgICAgPEFuaW1hdGlvbj4sIDxHcmFwaC5QbG90LmFuaW1hdGU+XG5cbiAgICAqL1xuICAgIHByZXBhcmU6IGZ1bmN0aW9uKG1vZGVzKSB7XG4gICAgICB2YXIgZ3JhcGggPSB0aGlzLnZpei5ncmFwaCxcbiAgICAgICAgICBhY2Nlc3NvcnMgPSB7XG4gICAgICAgICAgICAnbm9kZS1wcm9wZXJ0eSc6IHtcbiAgICAgICAgICAgICAgJ2dldHRlcic6ICdnZXREYXRhJyxcbiAgICAgICAgICAgICAgJ3NldHRlcic6ICdzZXREYXRhJ1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICdlZGdlLXByb3BlcnR5Jzoge1xuICAgICAgICAgICAgICAnZ2V0dGVyJzogJ2dldERhdGEnLFxuICAgICAgICAgICAgICAnc2V0dGVyJzogJ3NldERhdGEnXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgJ25vZGUtc3R5bGUnOiB7XG4gICAgICAgICAgICAgICdnZXR0ZXInOiAnZ2V0Q2FudmFzU3R5bGUnLFxuICAgICAgICAgICAgICAnc2V0dGVyJzogJ3NldENhbnZhc1N0eWxlJ1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICdlZGdlLXN0eWxlJzoge1xuICAgICAgICAgICAgICAnZ2V0dGVyJzogJ2dldENhbnZhc1N0eWxlJyxcbiAgICAgICAgICAgICAgJ3NldHRlcic6ICdzZXRDYW52YXNTdHlsZSdcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9O1xuXG4gICAgICAvL3BhcnNlIG1vZGVzXG4gICAgICB2YXIgbSA9IHt9O1xuICAgICAgaWYoJC50eXBlKG1vZGVzKSA9PSAnYXJyYXknKSB7XG4gICAgICAgIGZvcih2YXIgaT0wLCBsZW49bW9kZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgICB2YXIgZWxlbXMgPSBtb2Rlc1tpXS5zcGxpdCgnOicpO1xuICAgICAgICAgIG1bZWxlbXMuc2hpZnQoKV0gPSBlbGVtcztcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZm9yKHZhciBwIGluIG1vZGVzKSB7XG4gICAgICAgICAgaWYocCA9PSAncG9zaXRpb24nKSB7XG4gICAgICAgICAgICBtW21vZGVzLnBvc2l0aW9uXSA9IFtdO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBtW3BdID0gJC5zcGxhdChtb2Rlc1twXSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBcbiAgICAgIGdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKG5vZGUpIHsgXG4gICAgICAgIG5vZGUuc3RhcnRQb3Muc2V0KG5vZGUucG9zKTtcbiAgICAgICAgJC5lYWNoKFsnbm9kZS1wcm9wZXJ0eScsICdub2RlLXN0eWxlJ10sIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgICBpZihwIGluIG0pIHtcbiAgICAgICAgICAgIHZhciBwcm9wID0gbVtwXTtcbiAgICAgICAgICAgIGZvcih2YXIgaT0wLCBsPXByb3AubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgICAgIG5vZGVbYWNjZXNzb3JzW3BdLnNldHRlcl0ocHJvcFtpXSwgbm9kZVthY2Nlc3NvcnNbcF0uZ2V0dGVyXShwcm9wW2ldKSwgJ3N0YXJ0Jyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgJC5lYWNoKFsnZWRnZS1wcm9wZXJ0eScsICdlZGdlLXN0eWxlJ10sIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgICBpZihwIGluIG0pIHtcbiAgICAgICAgICAgIHZhciBwcm9wID0gbVtwXTtcbiAgICAgICAgICAgIG5vZGUuZWFjaEFkamFjZW5jeShmdW5jdGlvbihhZGopIHtcbiAgICAgICAgICAgICAgZm9yKHZhciBpPTAsIGw9cHJvcC5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICAgICAgICBhZGpbYWNjZXNzb3JzW3BdLnNldHRlcl0ocHJvcFtpXSwgYWRqW2FjY2Vzc29yc1twXS5nZXR0ZXJdKHByb3BbaV0pLCAnc3RhcnQnKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIG07XG4gICAgfSxcbiAgICBcbiAgICAvKlxuICAgICAgIE1ldGhvZDogYW5pbWF0ZVxuICAgIFxuICAgICAgIEFuaW1hdGVzIGEgPEdyYXBoPiBieSBpbnRlcnBvbGF0aW5nIHNvbWUgPEdyYXBoLk5vZGU+LCA8R3JhcGguQWRqYWNlbmNlPiBvciA8R3JhcGguTGFiZWw+IHByb3BlcnRpZXMuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgb3B0IC0gKG9iamVjdCkgQW5pbWF0aW9uIG9wdGlvbnMuIFRoZSBvYmplY3QgcHJvcGVydGllcyBhcmUgZGVzY3JpYmVkIGJlbG93XG4gICAgICAgZHVyYXRpb24gLSAob3B0aW9uYWwpIERlc2NyaWJlZCBpbiA8T3B0aW9ucy5GeD4uXG4gICAgICAgZnBzIC0gKG9wdGlvbmFsKSBEZXNjcmliZWQgaW4gPE9wdGlvbnMuRng+LlxuICAgICAgIGhpZGVMYWJlbHMgLSAob3B0aW9uYWx8Ym9vbGVhbikgV2hldGhlciB0byBoaWRlIGxhYmVscyBkdXJpbmcgdGhlIGFuaW1hdGlvbi5cbiAgICAgICBtb2RlcyAtIChyZXF1aXJlZHxvYmplY3QpIEFuIG9iamVjdCB3aXRoIGFuaW1hdGlvbiBtb2RlcyAoZGVzY3JpYmVkIGJlbG93KS5cblxuICAgICAgIEFuaW1hdGlvbiBtb2RlczpcbiAgICAgICBcbiAgICAgICBBbmltYXRpb24gbW9kZXMgYXJlIHN0cmluZ3MgcmVwcmVzZW50aW5nIGRpZmZlcmVudCBub2RlL2VkZ2UgYW5kIGdyYXBoIHByb3BlcnRpZXMgdGhhdCB5b3UnZCBsaWtlIHRvIGFuaW1hdGUuIFxuICAgICAgIFRoZXkgYXJlIHJlcHJlc2VudGVkIGJ5IGFuIG9iamVjdCB0aGF0IGhhcyBhcyBrZXlzIG1haW4gY2F0ZWdvcmllcyBvZiBwcm9wZXJ0aWVzIHRvIGFuaW1hdGUgYW5kIGFzIHZhbHVlcyBhIGxpc3QgXG4gICAgICAgb2YgdGhlc2Ugc3BlY2lmaWMgcHJvcGVydGllcy4gVGhlIHByb3BlcnRpZXMgYXJlIGRlc2NyaWJlZCBiZWxvd1xuICAgICAgIFxuICAgICAgIHBvc2l0aW9uIC0gRGVzY3JpYmVzIHRoZSB3YXkgbm9kZXMnIHBvc2l0aW9ucyBtdXN0IGJlIGludGVycG9sYXRlZC4gUG9zc2libGUgdmFsdWVzIGFyZSAnbGluZWFyJywgJ3BvbGFyJyBvciAnbW9lYml1cycuXG4gICAgICAgbm9kZS1wcm9wZXJ0eSAtIERlc2NyaWJlcyB3aGljaCBOb2RlIHByb3BlcnRpZXMgd2lsbCBiZSBpbnRlcnBvbGF0ZWQuIFRoZXNlIHByb3BlcnRpZXMgY2FuIGJlIGFueSBvZiB0aGUgb25lcyBkZWZpbmVkIGluIDxPcHRpb25zLk5vZGU+LlxuICAgICAgIGVkZ2UtcHJvcGVydHkgLSBEZXNjcmliZXMgd2hpY2ggRWRnZSBwcm9wZXJ0aWVzIHdpbGwgYmUgaW50ZXJwb2xhdGVkLiBUaGVzZSBwcm9wZXJ0aWVzIGNhbiBiZSBhbnkgdGhlIG9uZXMgZGVmaW5lZCBpbiA8T3B0aW9ucy5FZGdlPi5cbiAgICAgICBsYWJlbC1wcm9wZXJ0eSAtIERlc2NyaWJlcyB3aGljaCBMYWJlbCBwcm9wZXJ0aWVzIHdpbGwgYmUgaW50ZXJwb2xhdGVkLiBUaGVzZSBwcm9wZXJ0aWVzIGNhbiBiZSBhbnkgb2YgdGhlIG9uZXMgZGVmaW5lZCBpbiA8T3B0aW9ucy5MYWJlbD4gbGlrZSBjb2xvciBvciBzaXplLlxuICAgICAgIG5vZGUtc3R5bGUgLSBEZXNjcmliZXMgd2hpY2ggTm9kZSBDYW52YXMgU3R5bGVzIHdpbGwgYmUgaW50ZXJwb2xhdGVkLiBUaGVzZSBhcmUgc3BlY2lmaWMgY2FudmFzIHByb3BlcnRpZXMgbGlrZSBmaWxsU3R5bGUsIHN0cm9rZVN0eWxlLCBsaW5lV2lkdGgsIHNoYWRvd0JsdXIsIHNoYWRvd0NvbG9yLCBzaGFkb3dPZmZzZXRYLCBzaGFkb3dPZmZzZXRZLCBldGMuXG4gICAgICAgZWRnZS1zdHlsZSAtIERlc2NyaWJlcyB3aGljaCBFZGdlIENhbnZhcyBTdHlsZXMgd2lsbCBiZSBpbnRlcnBvbGF0ZWQuIFRoZXNlIGFyZSBzcGVjaWZpYyBjYW52YXMgcHJvcGVydGllcyBsaWtlIGZpbGxTdHlsZSwgc3Ryb2tlU3R5bGUsIGxpbmVXaWR0aCwgc2hhZG93Qmx1ciwgc2hhZG93Q29sb3IsIHNoYWRvd09mZnNldFgsIHNoYWRvd09mZnNldFksIGV0Yy5cblxuICAgICAgIEV4YW1wbGU6XG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgICAgICAvLy4uLnR3ZWFrIHNvbWUgRGF0YSwgQ2FudmFzU3R5bGVzIG9yIExhYmVsRGF0YSBwcm9wZXJ0aWVzLi4uXG4gICAgICAgdml6LmZ4LmFuaW1hdGUoe1xuICAgICAgICAgbW9kZXM6IHtcbiAgICAgICAgICAgJ3Bvc2l0aW9uJzogJ2xpbmVhcicsXG4gICAgICAgICAgICdub2RlLXByb3BlcnR5JzogWyd3aWR0aCcsICdoZWlnaHQnXSxcbiAgICAgICAgICAgJ25vZGUtc3R5bGUnOiAnc2hhZG93Q29sb3InLFxuICAgICAgICAgICAnbGFiZWwtcHJvcGVydHknOiAnc2l6ZSdcbiAgICAgICAgIH0sXG4gICAgICAgICBoaWRlTGFiZWxzOiBmYWxzZVxuICAgICAgIH0pO1xuICAgICAgIC8vLi4uY2FuIGFsc28gYmUgd3JpdHRlbiBsaWtlIHRoaXMuLi5cbiAgICAgICB2aXouZnguYW5pbWF0ZSh7XG4gICAgICAgICBtb2RlczogWydsaW5lYXInLFxuICAgICAgICAgICAgICAgICAnbm9kZS1wcm9wZXJ0eTp3aWR0aDpoZWlnaHQnLFxuICAgICAgICAgICAgICAgICAnbm9kZS1zdHlsZTpzaGFkb3dDb2xvcicsXG4gICAgICAgICAgICAgICAgICdsYWJlbC1wcm9wZXJ0eTpzaXplJ10sXG4gICAgICAgICBoaWRlTGFiZWxzOiBmYWxzZVxuICAgICAgIH0pO1xuICAgICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgIGFuaW1hdGU6IGZ1bmN0aW9uKG9wdCwgdmVyc29yKSB7XG4gICAgICBvcHQgPSAkLm1lcmdlKHRoaXMudml6LmNvbmZpZywgb3B0IHx8IHt9KTtcbiAgICAgIHZhciB0aGF0ID0gdGhpcyxcbiAgICAgICAgICB2aXogPSB0aGlzLnZpeixcbiAgICAgICAgICBncmFwaCAgPSB2aXouZ3JhcGgsXG4gICAgICAgICAgaW50ZXJwID0gdGhpcy5JbnRlcnBvbGF0b3IsXG4gICAgICAgICAgYW5pbWF0aW9uID0gIG9wdC50eXBlID09PSAnbm9kZWZ4Jz8gdGhpcy5ub2RlRnhBbmltYXRpb24gOiB0aGlzLmFuaW1hdGlvbjtcbiAgICAgIC8vcHJlcGFyZSBncmFwaCB2YWx1ZXNcbiAgICAgIHZhciBtID0gdGhpcy5wcmVwYXJlKG9wdC5tb2Rlcyk7XG4gICAgICBcbiAgICAgIC8vYW5pbWF0ZVxuICAgICAgaWYob3B0LmhpZGVMYWJlbHMpIHRoaXMubGFiZWxzLmhpZGVMYWJlbHModHJ1ZSk7XG4gICAgICBhbmltYXRpb24uc2V0T3B0aW9ucygkLmV4dGVuZChvcHQsIHtcbiAgICAgICAgJGFuaW1hdGluZzogZmFsc2UsXG4gICAgICAgIGNvbXB1dGU6IGZ1bmN0aW9uKGRlbHRhKSB7XG4gICAgICAgICAgZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obm9kZSkgeyBcbiAgICAgICAgICAgIGZvcih2YXIgcCBpbiBtKSB7XG4gICAgICAgICAgICAgIGludGVycFtwXShub2RlLCBtW3BdLCBkZWx0YSwgdmVyc29yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgICB0aGF0LnBsb3Qob3B0LCB0aGlzLiRhbmltYXRpbmcsIGRlbHRhKTtcbiAgICAgICAgICB0aGlzLiRhbmltYXRpbmcgPSB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBjb21wbGV0ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgICAgaWYob3B0LmhpZGVMYWJlbHMpIHRoYXQubGFiZWxzLmhpZGVMYWJlbHMoZmFsc2UpO1xuICAgICAgICAgIHRoYXQucGxvdChvcHQpO1xuICAgICAgICAgIG9wdC5vbkNvbXBsZXRlKCk7XG4gICAgICAgICAgLy9UT0RPKG5pY28pOiBUaGlzIHNob3VsZG4ndCBiZSBoZXJlIVxuICAgICAgICAgIC8vb3B0Lm9uQWZ0ZXJDb21wdXRlKCk7XG4gICAgICAgIH0gICAgICAgXG4gICAgICB9KSkuc3RhcnQoKTtcbiAgICB9LFxuICAgIFxuICAgIC8qXG4gICAgICBub2RlRnhcbiAgIFxuICAgICAgQXBwbHkgYW5pbWF0aW9uIHRvIG5vZGUgcHJvcGVydGllcyBsaWtlIGNvbG9yLCB3aWR0aCwgaGVpZ2h0LCBkaW0sIGV0Yy5cbiAgXG4gICAgICBQYXJhbWV0ZXJzOlxuICBcbiAgICAgIG9wdGlvbnMgLSBBbmltYXRpb24gb3B0aW9ucy4gVGhpcyBvYmplY3QgcHJvcGVydGllcyBpcyBkZXNjcmliZWQgYmVsb3dcbiAgICAgIGVsZW1lbnRzIC0gVGhlIEVsZW1lbnRzIHRvIGJlIHRyYW5zZm9ybWVkLiBUaGlzIGlzIGFuIG9iamVjdCB0aGF0IGhhcyBhIHByb3BlcnRpZXNcbiAgICAgIFxuICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAnZWxlbWVudHMnOiB7XG4gICAgICAgIC8vY2FuIGFsc28gYmUgYW4gYXJyYXkgb2YgaWRzXG4gICAgICAgICdpZCc6ICdpZC1vZi1ub2RlLXRvLXRyYW5zZm9ybScsXG4gICAgICAgIC8vcHJvcGVydGllcyB0byBiZSBtb2RpZmllZC4gQWxsIHByb3BlcnRpZXMgYXJlIG9wdGlvbmFsLlxuICAgICAgICAncHJvcGVydGllcyc6IHtcbiAgICAgICAgICAnY29sb3InOiAnI2NjYycsIC8vc29tZSBjb2xvclxuICAgICAgICAgICd3aWR0aCc6IDEwLCAvL3NvbWUgd2lkdGhcbiAgICAgICAgICAnaGVpZ2h0JzogMTAsIC8vc29tZSBoZWlnaHRcbiAgICAgICAgICAnZGltJzogMjAsIC8vc29tZSBkaW1cbiAgICAgICAgICAnbGluZVdpZHRoJzogMTAgLy9zb21lIGxpbmUgd2lkdGhcbiAgICAgICAgfSBcbiAgICAgIH1cbiAgICAgIChlbmQgY29kZSlcbiAgICAgIFxuICAgICAgLSBfcmVwb3NpdGlvbl8gV2hldGhlciB0byByZWNhbGN1bGF0ZSBwb3NpdGlvbnMgYW5kIGFkZCBhIG1vdGlvbiBhbmltYXRpb24uIFxuICAgICAgVGhpcyBtaWdodCBiZSB1c2VkIHdoZW4gY2hhbmdpbmcgX3dpZHRoXyBvciBfaGVpZ2h0XyBwcm9wZXJ0aWVzIGluIGEgPExheW91dHMuVHJlZT4gbGlrZSBsYXlvdXQuIERlZmF1bHQncyAqZmFsc2UqLlxuICAgICAgXG4gICAgICAtIF9vbkNvbXBsZXRlXyBBIG1ldGhvZCB0aGF0IGlzIGNhbGxlZCB3aGVuIHRoZSBhbmltYXRpb24gY29tcGxldGVzLlxuICAgICAgXG4gICAgICAuLi5hbmQgYWxsIG90aGVyIDxHcmFwaC5QbG90LmFuaW1hdGU+IG9wdGlvbnMgbGlrZSBfZHVyYXRpb25fLCBfZnBzXywgX3RyYW5zaXRpb25fLCBldGMuXG4gIFxuICAgICAgRXhhbXBsZTpcbiAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgIHZhciByZyA9IG5ldyBSR3JhcGgoY2FudmFzLCBjb25maWcpOyAvL2NhbiBiZSBhbHNvIEh5cGVydHJlZSBvciBTVFxuICAgICAgIHJnLmZ4Lm5vZGVGeCh7XG4gICAgICAgICAnZWxlbWVudHMnOiB7XG4gICAgICAgICAgICdpZCc6J215bm9kZWlkJyxcbiAgICAgICAgICAgJ3Byb3BlcnRpZXMnOiB7XG4gICAgICAgICAgICAgJ2NvbG9yJzonI2NjZidcbiAgICAgICAgICAgfSxcbiAgICAgICAgICAgJ3RyYW5zaXRpb24nOiBUcmFucy5RdWFydC5lYXNlT3V0XG4gICAgICAgICB9XG4gICAgICAgfSk7XG4gICAgICAoZW5kIGNvZGUpICAgIFxuICAgKi9cbiAgIG5vZGVGeDogZnVuY3Rpb24ob3B0KSB7XG4gICAgIHZhciB2aXogPSB0aGlzLnZpeixcbiAgICAgICAgIGdyYXBoICA9IHZpei5ncmFwaCxcbiAgICAgICAgIGFuaW1hdGlvbiA9IHRoaXMubm9kZUZ4QW5pbWF0aW9uLFxuICAgICAgICAgb3B0aW9ucyA9ICQubWVyZ2UodGhpcy52aXouY29uZmlnLCB7XG4gICAgICAgICAgICdlbGVtZW50cyc6IHtcbiAgICAgICAgICAgICAnaWQnOiBmYWxzZSxcbiAgICAgICAgICAgICAncHJvcGVydGllcyc6IHt9XG4gICAgICAgICAgIH0sXG4gICAgICAgICAgICdyZXBvc2l0aW9uJzogZmFsc2VcbiAgICAgICAgIH0pO1xuICAgICBvcHQgPSAkLm1lcmdlKG9wdGlvbnMsIG9wdCB8fCB7fSwge1xuICAgICAgIG9uQmVmb3JlQ29tcHV0ZTogJC5lbXB0eSxcbiAgICAgICBvbkFmdGVyQ29tcHV0ZTogJC5lbXB0eVxuICAgICB9KTtcbiAgICAgLy9jaGVjayBpZiBhbiBhbmltYXRpb24gaXMgcnVubmluZ1xuICAgICBhbmltYXRpb24uc3RvcFRpbWVyKCk7XG4gICAgIHZhciBwcm9wcyA9IG9wdC5lbGVtZW50cy5wcm9wZXJ0aWVzO1xuICAgICAvL3NldCBlbmQgdmFsdWVzIGZvciBub2Rlc1xuICAgICBpZighb3B0LmVsZW1lbnRzLmlkKSB7XG4gICAgICAgZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obikge1xuICAgICAgICAgZm9yKHZhciBwcm9wIGluIHByb3BzKSB7XG4gICAgICAgICAgIG4uc2V0RGF0YShwcm9wLCBwcm9wc1twcm9wXSwgJ2VuZCcpO1xuICAgICAgICAgfVxuICAgICAgIH0pO1xuICAgICB9IGVsc2Uge1xuICAgICAgIHZhciBpZHMgPSAkLnNwbGF0KG9wdC5lbGVtZW50cy5pZCk7XG4gICAgICAgJC5lYWNoKGlkcywgZnVuY3Rpb24oaWQpIHtcbiAgICAgICAgIHZhciBuID0gZ3JhcGguZ2V0Tm9kZShpZCk7XG4gICAgICAgICBpZihuKSB7XG4gICAgICAgICAgIGZvcih2YXIgcHJvcCBpbiBwcm9wcykge1xuICAgICAgICAgICAgIG4uc2V0RGF0YShwcm9wLCBwcm9wc1twcm9wXSwgJ2VuZCcpO1xuICAgICAgICAgICB9XG4gICAgICAgICB9XG4gICAgICAgfSk7XG4gICAgIH1cbiAgICAgLy9nZXQga2V5c1xuICAgICB2YXIgcHJvcG5hbWVzID0gW107XG4gICAgIGZvcih2YXIgcHJvcCBpbiBwcm9wcykgcHJvcG5hbWVzLnB1c2gocHJvcCk7XG4gICAgIC8vYWRkIG5vZGUgcHJvcGVydGllcyBtb2Rlc1xuICAgICB2YXIgbW9kZXMgPSBbJ25vZGUtcHJvcGVydHk6JyArIHByb3BuYW1lcy5qb2luKCc6JyldO1xuICAgICAvL3NldCBuZXcgbm9kZSBwb3NpdGlvbnNcbiAgICAgaWYob3B0LnJlcG9zaXRpb24pIHtcbiAgICAgICBtb2Rlcy5wdXNoKCdsaW5lYXInKTtcbiAgICAgICB2aXouY29tcHV0ZSgnZW5kJyk7XG4gICAgIH1cbiAgICAgLy9hbmltYXRlXG4gICAgIHRoaXMuYW5pbWF0ZSgkLm1lcmdlKG9wdCwge1xuICAgICAgIG1vZGVzOiBtb2RlcyxcbiAgICAgICB0eXBlOiAnbm9kZWZ4J1xuICAgICB9KSk7XG4gICB9LFxuXG4gICAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHBsb3RcbiAgICBcbiAgICAgICBQbG90cyBhIDxHcmFwaD4uXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgb3B0IC0gKG9wdGlvbmFsKSBQbG90dGluZyBvcHRpb25zLiBNb3N0IG9mIHRoZW0gYXJlIGRlc2NyaWJlZCBpbiA8T3B0aW9ucy5GeD4uXG5cbiAgICAgICBFeGFtcGxlOlxuXG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgICAgICB2aXouZngucGxvdCgpOyBcbiAgICAgICAoZW5kIGNvZGUpXG5cbiAgICAqL1xuICAgcGxvdDogZnVuY3Rpb24ob3B0LCBhbmltYXRpbmcpIHtcbiAgICAgdmFyIHZpeiA9IHRoaXMudml6LCBcbiAgICAgICAgIGFHcmFwaCA9IHZpei5ncmFwaCwgXG4gICAgICAgICBjYW52YXMgPSB2aXouY2FudmFzLCBcbiAgICAgICAgIGlkID0gdml6LnJvb3QsIFxuICAgICAgICAgdGhhdCA9IHRoaXMsIFxuICAgICAgICAgY3R4ID0gY2FudmFzLmdldEN0eCgpLCBcbiAgICAgICAgIG1pbiA9IE1hdGgubWluLFxuICAgICAgICAgb3B0ID0gb3B0IHx8IHRoaXMudml6LmNvbnRyb2xsZXI7XG4gICAgIFxuICAgICBvcHQuY2xlYXJDYW52YXMgJiYgY2FudmFzLmNsZWFyKCk7XG4gICAgICAgXG4gICAgIHZhciByb290ID0gYUdyYXBoLmdldE5vZGUoaWQpO1xuICAgICBpZighcm9vdCkgcmV0dXJuO1xuICAgICBcbiAgICAgdmFyIFQgPSAhIXJvb3QudmlzaXRlZDtcbiAgICBcbiAgICAvL1NUQVJUIE1FVEFNQVBTIENPREVcbiAgICBpZiAoTWV0YW1hcHMuTW91c2Uuc3luYXBzZVN0YXJ0Q29vcmRpbmF0ZXMubGVuZ3RoID4gMCAmJiBNZXRhbWFwcy5Nb3VzZS5zeW5hcHNlRW5kQ29vcmRpbmF0ZXMpIHtcbiAgICAgICAgY3R4LnNhdmUoKTtcbiAgICAgICAgdmFyIHN0YXJ0O1xuICAgICAgICB2YXIgZW5kID0gTWV0YW1hcHMuTW91c2Uuc3luYXBzZUVuZENvb3JkaW5hdGVzO1xuICAgICAgICBcbiAgICAgICAgdmFyIGwgPSBNZXRhbWFwcy5Nb3VzZS5zeW5hcHNlU3RhcnRDb29yZGluYXRlcy5sZW5ndGg7XG4gICAgICAgIGZvciAodmFyIGkgPSBsIC0gMTsgaSA+PSAwOyBpIC09IDEpIHtcbiAgICAgICAgICAgIHN0YXJ0ID0gTWV0YW1hcHMuTW91c2Uuc3luYXBzZVN0YXJ0Q29vcmRpbmF0ZXNbaV07XG4gICAgICAgICAgICBNZXRhbWFwcy5KSVQucmVuZGVyTWlkQXJyb3coc3RhcnQsIGVuZCwgMTMsIGZhbHNlLCBjYW52YXMsIDAuMywgdHJ1ZSk7XG4gICAgICAgICAgICBNZXRhbWFwcy5KSVQucmVuZGVyTWlkQXJyb3coc3RhcnQsIGVuZCwgMTMsIGZhbHNlLCBjYW52YXMsIDAuNywgdHJ1ZSk7XG4gICAgICAgIH1cbiAgICAgICAgY3R4LnJlc3RvcmUoKTtcbiAgICB9XG5cbiAgICBpZiAoTWV0YW1hcHMuTW91c2UuZm9jdXNOb2RlQ29vcmRzKSB7XG4gICAgICAgIGN0eC5zYXZlKCk7XG4gICAgICAgIE1ldGFtYXBzLkpJVC5yZW5kZXJNaWRBcnJvdyhNZXRhbWFwcy5Nb3VzZS5mb2N1c05vZGVDb29yZHMsIE1ldGFtYXBzLk1vdXNlLm5ld05vZGVDb29yZHMsIDEzLCBmYWxzZSwgY2FudmFzLCAwLjMsIHRydWUpO1xuICAgICAgICBNZXRhbWFwcy5KSVQucmVuZGVyTWlkQXJyb3coTWV0YW1hcHMuTW91c2UuZm9jdXNOb2RlQ29vcmRzLCBNZXRhbWFwcy5Nb3VzZS5uZXdOb2RlQ29vcmRzLCAxMywgZmFsc2UsIGNhbnZhcywgMC43LCB0cnVlKTtcbiAgICAgICAgY3R4LnJlc3RvcmUoKTtcbiAgICB9XG5cbiAgICBpZiAoTWV0YW1hcHMuTW91c2UuYm94U3RhcnRDb29yZGluYXRlcyAmJiBNZXRhbWFwcy5Nb3VzZS5ib3hFbmRDb29yZGluYXRlcykge1xuICAgICAgY3R4LnNhdmUoKTtcbiAgICAgIGN0eC5iZWdpblBhdGgoKVxuICAgICAgY3R4Lm1vdmVUbyhNZXRhbWFwcy5Nb3VzZS5ib3hTdGFydENvb3JkaW5hdGVzLngsIE1ldGFtYXBzLk1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMueSlcbiAgICAgIGN0eC5saW5lVG8oTWV0YW1hcHMuTW91c2UuYm94U3RhcnRDb29yZGluYXRlcy54LCBNZXRhbWFwcy5Nb3VzZS5ib3hFbmRDb29yZGluYXRlcy55KVxuICAgICAgY3R4LmxpbmVUbyhNZXRhbWFwcy5Nb3VzZS5ib3hFbmRDb29yZGluYXRlcy54LCBNZXRhbWFwcy5Nb3VzZS5ib3hFbmRDb29yZGluYXRlcy55KVxuICAgICAgY3R4LmxpbmVUbyhNZXRhbWFwcy5Nb3VzZS5ib3hFbmRDb29yZGluYXRlcy54LCBNZXRhbWFwcy5Nb3VzZS5ib3hTdGFydENvb3JkaW5hdGVzLnkpXG4gICAgICBjdHgubGluZVRvKE1ldGFtYXBzLk1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMueCwgTWV0YW1hcHMuTW91c2UuYm94U3RhcnRDb29yZGluYXRlcy55KVxuICAgICAgY3R4LnN0cm9rZVN0eWxlID0gJ2JsYWNrJ1xuICAgICAgY3R4LnN0cm9rZSgpXG4gICAgICBjdHgucmVzdG9yZSgpXG4gICAgfVxuICAgIC8vRU5EIE1FVEFNQVBTIENPREUgIFxuXG4gICAgIGFHcmFwaC5lYWNoTm9kZShmdW5jdGlvbihub2RlKSB7XG4gICAgICAgdmFyIG5vZGVBbHBoYSA9IG5vZGUuZ2V0RGF0YSgnYWxwaGEnKTtcbiAgICAgICBub2RlLmVhY2hBZGphY2VuY3koZnVuY3Rpb24oYWRqKSB7XG4gICAgICAgICB2YXIgbm9kZVRvID0gYWRqLm5vZGVUbztcbiAgICAgICAgIGlmKCEhbm9kZVRvLnZpc2l0ZWQgPT09IFQgJiYgbm9kZS5kcmF3biAmJiBub2RlVG8uZHJhd24pIHtcbiAgICAgICAgICAgIWFuaW1hdGluZyAmJiBvcHQub25CZWZvcmVQbG90TGluZShhZGopO1xuICAgICAgICAgICB0aGF0LnBsb3RMaW5lKGFkaiwgY2FudmFzLCBhbmltYXRpbmcpO1xuICAgICAgICAgICAhYW5pbWF0aW5nICYmIG9wdC5vbkFmdGVyUGxvdExpbmUoYWRqKTtcbiAgICAgICAgIH1cbiAgICAgICB9KTtcbiAgICAgICBpZihub2RlLmRyYXduKSB7XG4gICAgICAgICAhYW5pbWF0aW5nICYmIG9wdC5vbkJlZm9yZVBsb3ROb2RlKG5vZGUpO1xuICAgICAgICAgdGhhdC5wbG90Tm9kZShub2RlLCBjYW52YXMsIGFuaW1hdGluZyk7XG4gICAgICAgICAhYW5pbWF0aW5nICYmIG9wdC5vbkFmdGVyUGxvdE5vZGUobm9kZSk7XG4gICAgICAgfVxuICAgICAgIGlmKCF0aGF0LmxhYmVsc0hpZGRlbiAmJiBvcHQud2l0aExhYmVscykge1xuICAgICAgICAgaWYobm9kZS5kcmF3biAmJiBub2RlQWxwaGEgPj0gMC45NSkge1xuICAgICAgICAgICB0aGF0LmxhYmVscy5wbG90TGFiZWwoY2FudmFzLCBub2RlLCBvcHQpO1xuICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgdGhhdC5sYWJlbHMuaGlkZUxhYmVsKG5vZGUsIGZhbHNlKTtcbiAgICAgICAgIH1cbiAgICAgICB9XG4gICAgICAgbm9kZS52aXNpdGVkID0gIVQ7XG4gICAgIH0pO1xuICAgIH0sXG5cbiAgLypcbiAgICAgIFBsb3RzIGEgU3VidHJlZS5cbiAgICovXG4gICBwbG90VHJlZTogZnVuY3Rpb24obm9kZSwgb3B0LCBhbmltYXRpbmcpIHtcbiAgICAgICB2YXIgdGhhdCA9IHRoaXMsIFxuICAgICAgIHZpeiA9IHRoaXMudml6LCBcbiAgICAgICBjYW52YXMgPSB2aXouY2FudmFzLFxuICAgICAgIGNvbmZpZyA9IHRoaXMuY29uZmlnLFxuICAgICAgIGN0eCA9IGNhbnZhcy5nZXRDdHgoKTtcbiAgICAgICB2YXIgbm9kZUFscGhhID0gbm9kZS5nZXREYXRhKCdhbHBoYScpO1xuICAgICAgIG5vZGUuZWFjaFN1Ym5vZGUoZnVuY3Rpb24oZWxlbSkge1xuICAgICAgICAgaWYob3B0LnBsb3RTdWJ0cmVlKG5vZGUsIGVsZW0pICYmIGVsZW0uZXhpc3QgJiYgZWxlbS5kcmF3bikge1xuICAgICAgICAgICAgIHZhciBhZGogPSBub2RlLmdldEFkamFjZW5jeShlbGVtLmlkKTtcbiAgICAgICAgICAgICAhYW5pbWF0aW5nICYmIG9wdC5vbkJlZm9yZVBsb3RMaW5lKGFkaik7XG4gICAgICAgICAgICAgdGhhdC5wbG90TGluZShhZGosIGNhbnZhcywgYW5pbWF0aW5nKTtcbiAgICAgICAgICAgICAhYW5pbWF0aW5nICYmIG9wdC5vbkFmdGVyUGxvdExpbmUoYWRqKTtcbiAgICAgICAgICAgICB0aGF0LnBsb3RUcmVlKGVsZW0sIG9wdCwgYW5pbWF0aW5nKTtcbiAgICAgICAgIH1cbiAgICAgICB9KTtcbiAgICAgICBpZihub2RlLmRyYXduKSB7XG4gICAgICAgICAgICFhbmltYXRpbmcgJiYgb3B0Lm9uQmVmb3JlUGxvdE5vZGUobm9kZSk7XG4gICAgICAgICAgIHRoaXMucGxvdE5vZGUobm9kZSwgY2FudmFzLCBhbmltYXRpbmcpO1xuICAgICAgICAgICAhYW5pbWF0aW5nICYmIG9wdC5vbkFmdGVyUGxvdE5vZGUobm9kZSk7XG4gICAgICAgICAgIGlmKCFvcHQuaGlkZUxhYmVscyAmJiBvcHQud2l0aExhYmVscyAmJiBub2RlQWxwaGEgPj0gMC45NSkgXG4gICAgICAgICAgICAgICB0aGlzLmxhYmVscy5wbG90TGFiZWwoY2FudmFzLCBub2RlLCBvcHQpO1xuICAgICAgICAgICBlbHNlIFxuICAgICAgICAgICAgICAgdGhpcy5sYWJlbHMuaGlkZUxhYmVsKG5vZGUsIGZhbHNlKTtcbiAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICB0aGlzLmxhYmVscy5oaWRlTGFiZWwobm9kZSwgdHJ1ZSk7XG4gICAgICAgfVxuICAgfSxcblxuICAvKlxuICAgICAgIE1ldGhvZDogcGxvdE5vZGVcbiAgICBcbiAgICAgICBQbG90cyBhIDxHcmFwaC5Ob2RlPi5cblxuICAgICAgIFBhcmFtZXRlcnM6XG4gICAgICAgXG4gICAgICAgbm9kZSAtIChvYmplY3QpIEEgPEdyYXBoLk5vZGU+LlxuICAgICAgIGNhbnZhcyAtIChvYmplY3QpIEEgPENhbnZhcz4gZWxlbWVudC5cblxuICAgICovXG4gICAgcGxvdE5vZGU6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcywgYW5pbWF0aW5nKSB7XG4gICAgICAgIHZhciBmID0gbm9kZS5nZXREYXRhKCd0eXBlJyksIFxuICAgICAgICAgICAgY3R4T2JqID0gdGhpcy5ub2RlLkNhbnZhc1N0eWxlcztcbiAgICAgICAgaWYoZiAhPSAnbm9uZScpIHtcbiAgICAgICAgICB2YXIgd2lkdGggPSBub2RlLmdldERhdGEoJ2xpbmVXaWR0aCcpLFxuICAgICAgICAgICAgICBjb2xvciA9IG5vZGUuZ2V0RGF0YSgnY29sb3InKSxcbiAgICAgICAgICAgICAgYWxwaGEgPSBub2RlLmdldERhdGEoJ2FscGhhJyksXG4gICAgICAgICAgICAgIGN0eCA9IGNhbnZhcy5nZXRDdHgoKTtcbiAgICAgICAgICBjdHguc2F2ZSgpO1xuICAgICAgICAgIGN0eC5saW5lV2lkdGggPSB3aWR0aDtcbiAgICAgICAgICBjdHguZmlsbFN0eWxlID0gY3R4LnN0cm9rZVN0eWxlID0gY29sb3I7XG4gICAgICAgICAgY3R4Lmdsb2JhbEFscGhhID0gYWxwaGE7XG4gICAgICAgICAgXG4gICAgICAgICAgZm9yKHZhciBzIGluIGN0eE9iaikge1xuICAgICAgICAgICAgY3R4W3NdID0gbm9kZS5nZXRDYW52YXNTdHlsZShzKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB0aGlzLm5vZGVUeXBlc1tmXS5yZW5kZXIuY2FsbCh0aGlzLCBub2RlLCBjYW52YXMsIGFuaW1hdGluZyk7XG4gICAgICAgICAgY3R4LnJlc3RvcmUoKTtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHBsb3RMaW5lXG4gICAgXG4gICAgICAgUGxvdHMgYSA8R3JhcGguQWRqYWNlbmNlPi5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICBhZGogLSAob2JqZWN0KSBBIDxHcmFwaC5BZGphY2VuY2U+LlxuICAgICAgIGNhbnZhcyAtIChvYmplY3QpIEEgPENhbnZhcz4gaW5zdGFuY2UuXG5cbiAgICAqL1xuICAgIHBsb3RMaW5lOiBmdW5jdGlvbihhZGosIGNhbnZhcywgYW5pbWF0aW5nKSB7XG4gICAgICB2YXIgZiA9IGFkai5nZXREYXRhKCd0eXBlJyksXG4gICAgICAgICAgY3R4T2JqID0gdGhpcy5lZGdlLkNhbnZhc1N0eWxlcztcbiAgICAgIGlmKGYgIT0gJ25vbmUnKSB7XG4gICAgICAgIHZhciB3aWR0aCA9IGFkai5nZXREYXRhKCdsaW5lV2lkdGgnKSxcbiAgICAgICAgICAgIGNvbG9yID0gYWRqLmdldERhdGEoJ2NvbG9yJyksXG4gICAgICAgICAgICBjdHggPSBjYW52YXMuZ2V0Q3R4KCksXG4gICAgICAgICAgICBub2RlRnJvbSA9IGFkai5ub2RlRnJvbSxcbiAgICAgICAgICAgIG5vZGVUbyA9IGFkai5ub2RlVG87XG4gICAgICAgIFxuICAgICAgICBjdHguc2F2ZSgpO1xuICAgICAgICBjdHgubGluZVdpZHRoID0gd2lkdGg7XG4gICAgICAgIGN0eC5maWxsU3R5bGUgPSBjdHguc3Ryb2tlU3R5bGUgPSBjb2xvcjtcbiAgICAgICAgY3R4Lmdsb2JhbEFscGhhID0gTWF0aC5taW4obm9kZUZyb20uZ2V0RGF0YSgnYWxwaGEnKSwgXG4gICAgICAgICAgICBub2RlVG8uZ2V0RGF0YSgnYWxwaGEnKSwgXG4gICAgICAgICAgICBhZGouZ2V0RGF0YSgnYWxwaGEnKSk7XG4gICAgICAgIFxuICAgICAgICBmb3IodmFyIHMgaW4gY3R4T2JqKSB7XG4gICAgICAgICAgY3R4W3NdID0gYWRqLmdldENhbnZhc1N0eWxlKHMpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5lZGdlVHlwZXNbZl0ucmVuZGVyLmNhbGwodGhpcywgYWRqLCBjYW52YXMsIGFuaW1hdGluZyk7XG4gICAgICAgIGN0eC5yZXN0b3JlKCk7XG4gICAgICB9XG4gICAgfSAgICBcbiAgXG59O1xuXG4vKlxuICBPYmplY3Q6IEdyYXBoLlBsb3QzRFxuICBcbiAgPEdyYXBoPiAzRCByZW5kZXJpbmcgYW5kIGFuaW1hdGlvbiBtZXRob2RzLlxuICBcbiAgUHJvcGVydGllczpcbiAgXG4gIG5vZGVIZWxwZXIgLSA8Tm9kZUhlbHBlcj4gb2JqZWN0LlxuICBlZGdlSGVscGVyIC0gPEVkZ2VIZWxwZXI+IG9iamVjdC5cblxuKi9cbkdyYXBoLlBsb3QzRCA9ICQubWVyZ2UoR3JhcGguUGxvdCwge1xuICBJbnRlcnBvbGF0b3I6IHtcbiAgICAnbGluZWFyJzogZnVuY3Rpb24oZWxlbSwgcHJvcHMsIGRlbHRhKSB7XG4gICAgICB2YXIgZnJvbSA9IGVsZW0uc3RhcnRQb3MuZ2V0Yyh0cnVlKTtcbiAgICAgIHZhciB0byA9IGVsZW0uZW5kUG9zLmdldGModHJ1ZSk7XG4gICAgICBlbGVtLnBvcy5zZXRjKHRoaXMuY29tcHV0ZShmcm9tLngsIHRvLngsIGRlbHRhKSwgXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29tcHV0ZShmcm9tLnksIHRvLnksIGRlbHRhKSxcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb21wdXRlKGZyb20ueiwgdG8ueiwgZGVsdGEpKTtcbiAgICB9XG4gIH0sXG4gIFxuICBwbG90Tm9kZTogZnVuY3Rpb24obm9kZSwgY2FudmFzKSB7XG4gICAgaWYobm9kZS5nZXREYXRhKCd0eXBlJykgPT0gJ25vbmUnKSByZXR1cm47XG4gICAgdGhpcy5wbG90RWxlbWVudChub2RlLCBjYW52YXMsIHtcbiAgICAgIGdldEFscGhhOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIG5vZGUuZ2V0RGF0YSgnYWxwaGEnKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSxcbiAgXG4gIHBsb3RMaW5lOiBmdW5jdGlvbihhZGosIGNhbnZhcykge1xuICAgIGlmKGFkai5nZXREYXRhKCd0eXBlJykgPT0gJ25vbmUnKSByZXR1cm47XG4gICAgdGhpcy5wbG90RWxlbWVudChhZGosIGNhbnZhcywge1xuICAgICAgZ2V0QWxwaGE6IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gTWF0aC5taW4oYWRqLm5vZGVGcm9tLmdldERhdGEoJ2FscGhhJyksXG4gICAgICAgICAgICAgICAgICAgICAgICBhZGoubm9kZVRvLmdldERhdGEoJ2FscGhhJyksXG4gICAgICAgICAgICAgICAgICAgICAgICBhZGouZ2V0RGF0YSgnYWxwaGEnKSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0sXG4gIFxuICBwbG90RWxlbWVudDogZnVuY3Rpb24oZWxlbSwgY2FudmFzLCBvcHQpIHtcbiAgICB2YXIgZ2wgPSBjYW52YXMuZ2V0Q3R4KCksXG4gICAgICAgIHZpZXdNYXRyaXggPSBuZXcgTWF0cml4NCxcbiAgICAgICAgbGlnaHRpbmcgPSBjYW52YXMuY29uZmlnLlNjZW5lLkxpZ2h0aW5nLFxuICAgICAgICB3Y2FudmFzID0gY2FudmFzLmNhbnZhc2VzWzBdLFxuICAgICAgICBwcm9ncmFtID0gd2NhbnZhcy5wcm9ncmFtLFxuICAgICAgICBjYW1lcmEgPSB3Y2FudmFzLmNhbWVyYTtcbiAgICBcbiAgICBpZighZWxlbS5nZW9tZXRyeSkge1xuICAgICAgZWxlbS5nZW9tZXRyeSA9IG5ldyBPM0RbZWxlbS5nZXREYXRhKCd0eXBlJyldO1xuICAgIH1cbiAgICBlbGVtLmdlb21ldHJ5LnVwZGF0ZShlbGVtKTtcbiAgICBpZighZWxlbS53ZWJHTFZlcnRleEJ1ZmZlcikge1xuICAgICAgdmFyIHZlcnRpY2VzID0gW10sXG4gICAgICAgICAgZmFjZXMgPSBbXSxcbiAgICAgICAgICBub3JtYWxzID0gW10sXG4gICAgICAgICAgdmVydGV4SW5kZXggPSAwLFxuICAgICAgICAgIGdlb20gPSBlbGVtLmdlb21ldHJ5O1xuICAgICAgXG4gICAgICBmb3IodmFyIGk9MCwgdnM9Z2VvbS52ZXJ0aWNlcywgZnM9Z2VvbS5mYWNlcywgZnNsPWZzLmxlbmd0aDsgaTxmc2w7IGkrKykge1xuICAgICAgICB2YXIgZmFjZSA9IGZzW2ldLFxuICAgICAgICAgICAgdjEgPSB2c1tmYWNlLmFdLFxuICAgICAgICAgICAgdjIgPSB2c1tmYWNlLmJdLFxuICAgICAgICAgICAgdjMgPSB2c1tmYWNlLmNdLFxuICAgICAgICAgICAgdjQgPSBmYWNlLmQ/IHZzW2ZhY2UuZF0gOiBmYWxzZSxcbiAgICAgICAgICAgIG4gPSBmYWNlLm5vcm1hbDtcbiAgICAgICAgXG4gICAgICAgIHZlcnRpY2VzLnB1c2godjEueCwgdjEueSwgdjEueik7XG4gICAgICAgIHZlcnRpY2VzLnB1c2godjIueCwgdjIueSwgdjIueik7XG4gICAgICAgIHZlcnRpY2VzLnB1c2godjMueCwgdjMueSwgdjMueik7XG4gICAgICAgIGlmKHY0KSB2ZXJ0aWNlcy5wdXNoKHY0LngsIHY0LnksIHY0LnopO1xuICAgICAgICAgICAgXG4gICAgICAgIG5vcm1hbHMucHVzaChuLngsIG4ueSwgbi56KTtcbiAgICAgICAgbm9ybWFscy5wdXNoKG4ueCwgbi55LCBuLnopO1xuICAgICAgICBub3JtYWxzLnB1c2gobi54LCBuLnksIG4ueik7XG4gICAgICAgIGlmKHY0KSBub3JtYWxzLnB1c2gobi54LCBuLnksIG4ueik7XG4gICAgICAgICAgICBcbiAgICAgICAgZmFjZXMucHVzaCh2ZXJ0ZXhJbmRleCwgdmVydGV4SW5kZXggKzEsIHZlcnRleEluZGV4ICsyKTtcbiAgICAgICAgaWYodjQpIHtcbiAgICAgICAgICBmYWNlcy5wdXNoKHZlcnRleEluZGV4LCB2ZXJ0ZXhJbmRleCArMiwgdmVydGV4SW5kZXggKzMpO1xuICAgICAgICAgIHZlcnRleEluZGV4ICs9IDQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdmVydGV4SW5kZXggKz0gMztcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy9jcmVhdGUgYW5kIHN0b3JlIHZlcnRleCBkYXRhXG4gICAgICBlbGVtLndlYkdMVmVydGV4QnVmZmVyID0gZ2wuY3JlYXRlQnVmZmVyKCk7XG4gICAgICBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgZWxlbS53ZWJHTFZlcnRleEJ1ZmZlcik7XG4gICAgICBnbC5idWZmZXJEYXRhKGdsLkFSUkFZX0JVRkZFUiwgbmV3IEZsb2F0MzJBcnJheSh2ZXJ0aWNlcyksIGdsLlNUQVRJQ19EUkFXKTtcbiAgICAgIC8vY3JlYXRlIGFuZCBzdG9yZSBmYWNlcyBpbmRleCBkYXRhXG4gICAgICBlbGVtLndlYkdMRmFjZUJ1ZmZlciA9IGdsLmNyZWF0ZUJ1ZmZlcigpO1xuICAgICAgZ2wuYmluZEJ1ZmZlcihnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgZWxlbS53ZWJHTEZhY2VCdWZmZXIpO1xuICAgICAgZ2wuYnVmZmVyRGF0YShnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgbmV3IFVpbnQxNkFycmF5KGZhY2VzKSwgZ2wuU1RBVElDX0RSQVcpO1xuICAgICAgZWxlbS53ZWJHTEZhY2VDb3VudCA9IGZhY2VzLmxlbmd0aDtcbiAgICAgIC8vY2FsY3VsYXRlIHZlcnRleCBub3JtYWxzIGFuZCBzdG9yZSB0aGVtXG4gICAgICBlbGVtLndlYkdMTm9ybWFsQnVmZmVyID0gZ2wuY3JlYXRlQnVmZmVyKCk7XG4gICAgICBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgZWxlbS53ZWJHTE5vcm1hbEJ1ZmZlcik7XG4gICAgICBnbC5idWZmZXJEYXRhKGdsLkFSUkFZX0JVRkZFUiwgbmV3IEZsb2F0MzJBcnJheShub3JtYWxzKSwgZ2wuU1RBVElDX0RSQVcpO1xuICAgIH1cbiAgICB2aWV3TWF0cml4Lm11bHRpcGx5KGNhbWVyYS5tYXRyaXgsIGVsZW0uZ2VvbWV0cnkubWF0cml4KTtcbiAgICAvL3NlbmQgbWF0cml4IGRhdGFcbiAgICBnbC51bmlmb3JtTWF0cml4NGZ2KHByb2dyYW0udmlld01hdHJpeCwgZmFsc2UsIHZpZXdNYXRyaXguZmxhdHRlbigpKTtcbiAgICBnbC51bmlmb3JtTWF0cml4NGZ2KHByb2dyYW0ucHJvamVjdGlvbk1hdHJpeCwgZmFsc2UsIGNhbWVyYS5wcm9qZWN0aW9uTWF0cml4LmZsYXR0ZW4oKSk7XG4gICAgLy9zZW5kIG5vcm1hbCBtYXRyaXggZm9yIGxpZ2h0aW5nXG4gICAgdmFyIG5vcm1hbE1hdHJpeCA9IE1hdHJpeDQubWFrZUludmVydCh2aWV3TWF0cml4KTtcbiAgICBub3JtYWxNYXRyaXguJHRyYW5zcG9zZSgpO1xuICAgIGdsLnVuaWZvcm1NYXRyaXg0ZnYocHJvZ3JhbS5ub3JtYWxNYXRyaXgsIGZhbHNlLCBub3JtYWxNYXRyaXguZmxhdHRlbigpKTtcbiAgICAvL3NlbmQgY29sb3IgZGF0YVxuICAgIHZhciBjb2xvciA9ICQuaGV4VG9SZ2IoZWxlbS5nZXREYXRhKCdjb2xvcicpKTtcbiAgICBjb2xvci5wdXNoKG9wdC5nZXRBbHBoYSgpKTtcbiAgICBnbC51bmlmb3JtNGYocHJvZ3JhbS5jb2xvciwgY29sb3JbMF0gLyAyNTUsIGNvbG9yWzFdIC8gMjU1LCBjb2xvclsyXSAvIDI1NSwgY29sb3JbM10pO1xuICAgIC8vc2VuZCBsaWdodGluZyBkYXRhXG4gICAgZ2wudW5pZm9ybTFpKHByb2dyYW0uZW5hYmxlTGlnaHRpbmcsIGxpZ2h0aW5nLmVuYWJsZSk7XG4gICAgaWYobGlnaHRpbmcuZW5hYmxlKSB7XG4gICAgICAvL3NldCBhbWJpZW50IGxpZ2h0IGNvbG9yXG4gICAgICBpZihsaWdodGluZy5hbWJpZW50KSB7XG4gICAgICAgIHZhciBhY29sb3IgPSBsaWdodGluZy5hbWJpZW50O1xuICAgICAgICBnbC51bmlmb3JtM2YocHJvZ3JhbS5hbWJpZW50Q29sb3IsIGFjb2xvclswXSwgYWNvbG9yWzFdLCBhY29sb3JbMl0pO1xuICAgICAgfVxuICAgICAgLy9zZXQgZGlyZWN0aW9uYWwgbGlnaHRcbiAgICAgIGlmKGxpZ2h0aW5nLmRpcmVjdGlvbmFsKSB7XG4gICAgICAgIHZhciBkaXIgPSBsaWdodGluZy5kaXJlY3Rpb25hbCxcbiAgICAgICAgICAgIGNvbG9yID0gZGlyLmNvbG9yLFxuICAgICAgICAgICAgcG9zID0gZGlyLmRpcmVjdGlvbixcbiAgICAgICAgICAgIHZkID0gbmV3IFZlY3RvcjMocG9zLngsIHBvcy55LCBwb3Mueikubm9ybWFsaXplKCkuJHNjYWxlKC0xKTtcbiAgICAgICAgZ2wudW5pZm9ybTNmKHByb2dyYW0ubGlnaHRpbmdEaXJlY3Rpb24sIHZkLngsIHZkLnksIHZkLnopO1xuICAgICAgICBnbC51bmlmb3JtM2YocHJvZ3JhbS5kaXJlY3Rpb25hbENvbG9yLCBjb2xvclswXSwgY29sb3JbMV0sIGNvbG9yWzJdKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy9zZW5kIHZlcnRpY2VzIGRhdGFcbiAgICBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgZWxlbS53ZWJHTFZlcnRleEJ1ZmZlcik7XG4gICAgZ2wudmVydGV4QXR0cmliUG9pbnRlcihwcm9ncmFtLnBvc2l0aW9uLCAzLCBnbC5GTE9BVCwgZmFsc2UsIDAsIDApO1xuICAgIC8vc2VuZCBub3JtYWxzIGRhdGFcbiAgICBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgZWxlbS53ZWJHTE5vcm1hbEJ1ZmZlcik7XG4gICAgZ2wudmVydGV4QXR0cmliUG9pbnRlcihwcm9ncmFtLm5vcm1hbCwgMywgZ2wuRkxPQVQsIGZhbHNlLCAwLCAwKTtcbiAgICAvL2RyYXchXG4gICAgZ2wuYmluZEJ1ZmZlcihnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgZWxlbS53ZWJHTEZhY2VCdWZmZXIgKTtcbiAgICBnbC5kcmF3RWxlbWVudHMoZ2wuVFJJQU5HTEVTLCBlbGVtLndlYkdMRmFjZUNvdW50LCBnbC5VTlNJR05FRF9TSE9SVCwgMCk7XG4gIH1cbn0pO1xuXG5cbi8qXG4gKiBGaWxlOiBHcmFwaC5MYWJlbC5qc1xuICpcbiovXG5cbi8qXG4gICBPYmplY3Q6IEdyYXBoLkxhYmVsXG5cbiAgIEFuIGludGVyZmFjZSBmb3IgcGxvdHRpbmcvaGlkaW5nL3Nob3dpbmcgbGFiZWxzLlxuXG4gICBEZXNjcmlwdGlvbjpcblxuICAgVGhpcyBpcyBhIGdlbmVyaWMgaW50ZXJmYWNlIGZvciBwbG90dGluZy9oaWRpbmcvc2hvd2luZyBsYWJlbHMuXG4gICBUaGUgPEdyYXBoLkxhYmVsPiBpbnRlcmZhY2UgaXMgaW1wbGVtZW50ZWQgaW4gbXVsdGlwbGUgd2F5cyB0byBwcm92aWRlXG4gICBkaWZmZXJlbnQgbGFiZWwgdHlwZXMuXG5cbiAgIEZvciBleGFtcGxlLCB0aGUgR3JhcGguTGFiZWwgaW50ZXJmYWNlIGlzIGltcGxlbWVudGVkIGFzIDxHcmFwaC5MYWJlbC5IVE1MPiB0byBwcm92aWRlXG4gICBIVE1MIGxhYmVsIGVsZW1lbnRzLiBBbHNvIHdlIHByb3ZpZGUgdGhlIDxHcmFwaC5MYWJlbC5TVkc+IGludGVyZmFjZSBmb3IgU1ZHIHR5cGUgbGFiZWxzLiBcbiAgIFRoZSA8R3JhcGguTGFiZWwuTmF0aXZlPiBpbnRlcmZhY2UgaW1wbGVtZW50cyB0aGVzZSBtZXRob2RzIHdpdGggdGhlIG5hdGl2ZSBDYW52YXMgdGV4dCByZW5kZXJpbmcgZnVuY3Rpb25zLlxuICAgXG4gICBBbGwgc3ViY2xhc3NlcyAoPEdyYXBoLkxhYmVsLkhUTUw+LCA8R3JhcGguTGFiZWwuU1ZHPiBhbmQgPEdyYXBoLkxhYmVsLk5hdGl2ZT4pIGltcGxlbWVudCB0aGUgbWV0aG9kIHBsb3RMYWJlbC5cbiovXG5cbkdyYXBoLkxhYmVsID0ge307XG5cbi8qXG4gICBDbGFzczogR3JhcGguTGFiZWwuTmF0aXZlXG5cbiAgIEltcGxlbWVudHMgbGFiZWxzIG5hdGl2ZWx5LCB1c2luZyB0aGUgQ2FudmFzIHRleHQgQVBJLlxuKi9cbkdyYXBoLkxhYmVsLk5hdGl2ZSA9IG5ldyBDbGFzcyh7XG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24odml6KSB7XG4gICAgICB0aGlzLnZpeiA9IHZpejtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHBsb3RMYWJlbFxuXG4gICAgICAgUGxvdHMgYSBsYWJlbCBmb3IgYSBnaXZlbiBub2RlLlxuXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIGNhbnZhcyAtIChvYmplY3QpIEEgPENhbnZhcz4gaW5zdGFuY2UuXG4gICAgICAgbm9kZSAtIChvYmplY3QpIEEgPEdyYXBoLk5vZGU+LlxuICAgICAgIGNvbnRyb2xsZXIgLSAob2JqZWN0KSBBIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxuICAgICAgIFxuICAgICAgIEV4YW1wbGU6XG4gICAgICAgXG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgICAgICB2YXIgbm9kZSA9IHZpei5ncmFwaC5nZXROb2RlKCdub2RlSWQnKTtcbiAgICAgICB2aXoubGFiZWxzLnBsb3RMYWJlbCh2aXouY2FudmFzLCBub2RlLCB2aXouY29uZmlnKTtcbiAgICAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBwbG90TGFiZWw6IGZ1bmN0aW9uKGNhbnZhcywgbm9kZSwgY29udHJvbGxlcikge1xuICAgICAgXG4gICAgICB2YXIgY3R4ID0gY2FudmFzLmdldEN0eCgpO1xuICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSk7XG5cbiAgICAgIGN0eC5mb250ID0gbm9kZS5nZXRMYWJlbERhdGEoJ3N0eWxlJykgKyAnICcgKyBub2RlLmdldExhYmVsRGF0YSgnc2l6ZScpICsgJ3B4ICcgKyBub2RlLmdldExhYmVsRGF0YSgnZmFtaWx5Jyk7XG4gICAgICBjdHgudGV4dEFsaWduID0gbm9kZS5nZXRMYWJlbERhdGEoJ3RleHRBbGlnbicpO1xuICAgICAgLy8gT1JJR0lOQUwgQ09ERSBjdHguZmlsbFN0eWxlID0gY3R4LnN0cm9rZVN0eWxlID0gbm9kZS5nZXRMYWJlbERhdGEoJ2NvbG9yJyk7XG4gICAgICBjdHgudGV4dEJhc2VsaW5lID0gbm9kZS5nZXRMYWJlbERhdGEoJ3RleHRCYXNlbGluZScpO1xuICAgICAgXG4gICAgICAvL1NUQVJUIE1FVEFNQVBTIENPREVcbiAgICAgIFxuICAgICAgdmFyIGFycmF5T2ZMYWJlbExpbmVzID0gTWV0YW1hcHMuVXRpbC5zcGxpdExpbmUobm9kZS5uYW1lLCAyNSkuc3BsaXQoJ1xcbicpO1xuICAgICAgLy9yZW5kZXIgYmFja2dyb3VuZFxuICAgICAgICAgICAgY3R4LmZpbGxTdHlsZSA9IGN0eC5zdHJva2VTdHlsZSA9IE1ldGFtYXBzLlNldHRpbmdzLmNvbG9ycy5sYWJlbHMuYmFja2dyb3VuZDtcbiAgICAgICAgICAgIGN0eC5saW5lV2lkdGggPSAyO1xuICAgICAgICAgICAgdmFyIGhlaWdodCA9IDI1ICogYXJyYXlPZkxhYmVsTGluZXMubGVuZ3RoOyAvL2ZvbnQgc2l6ZSArIG1hcmdpblxuICAgICAgICAgICAgXG4gICAgICAgICAgICB2YXIgaW5kZXgsIGxpbmVXaWR0aHMgPSBbXTtcbiAgICAgICAgICAgIGZvciAoaW5kZXggPSAwOyBpbmRleCA8IGFycmF5T2ZMYWJlbExpbmVzLmxlbmd0aDsgKytpbmRleCkge1xuICAgICAgICAgICAgICBsaW5lV2lkdGhzLnB1c2goIGN0eC5tZWFzdXJlVGV4dCggYXJyYXlPZkxhYmVsTGluZXNbaW5kZXhdICkud2lkdGggKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHdpZHRoID0gTWF0aC5tYXguYXBwbHkobnVsbCwgbGluZVdpZHRocykgKyA4O1xuICAgICAgICAgICAgdmFyIHggPSBwb3MueCAtIHdpZHRoLzI7XG4gICAgICAgICAgICB2YXIgeSA9IHBvcy55ICsgbm9kZS5nZXREYXRhKFwiaGVpZ2h0XCIpICsgNTtcbiAgICAgICAgICAgIHZhciByYWRpdXMgPSA1O1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAgIGN0eC5iZWdpblBhdGgoKTtcbiAgICAgICAgICAgICAgY3R4Lm1vdmVUbyh4ICsgcmFkaXVzLCB5KTtcbiAgICAgICAgICAgICAgY3R4LmxpbmVUbyh4ICsgd2lkdGggLSByYWRpdXMsIHkpO1xuICAgICAgICAgICAgICBjdHgucXVhZHJhdGljQ3VydmVUbyh4ICsgd2lkdGgsIHksIHggKyB3aWR0aCwgeSArIHJhZGl1cyk7XG4gICAgICAgICAgICAgIGN0eC5saW5lVG8oeCArIHdpZHRoLCB5ICsgaGVpZ2h0IC0gcmFkaXVzKTtcbiAgICAgICAgICAgICAgY3R4LnF1YWRyYXRpY0N1cnZlVG8oeCArIHdpZHRoLCB5ICsgaGVpZ2h0LCB4ICsgd2lkdGggLSByYWRpdXMsIHkgKyBoZWlnaHQpO1xuICAgICAgICAgICAgICBjdHgubGluZVRvKHggKyByYWRpdXMsIHkgKyBoZWlnaHQpO1xuICAgICAgICAgICAgICBjdHgucXVhZHJhdGljQ3VydmVUbyh4LCB5ICsgaGVpZ2h0LCB4LCB5ICsgaGVpZ2h0IC0gcmFkaXVzKTtcbiAgICAgICAgICAgICAgY3R4LmxpbmVUbyh4LCB5ICsgcmFkaXVzKTtcbiAgICAgICAgICAgICAgY3R4LnF1YWRyYXRpY0N1cnZlVG8oeCwgeSwgeCArIHJhZGl1cywgeSk7XG4gICAgICAgICAgICAgIGN0eC5jbG9zZVBhdGgoKTtcbiAgICAgICAgICAgICAgY3R4LmZpbGwoKTtcbiAgICAgICAgICAgICAgLy9jdHguc3Ryb2tlKCk7XG4gICAgICAgXG4gICAgICAgY3R4LmZpbGxTdHlsZSA9IGN0eC5zdHJva2VTdHlsZSA9IG5vZGUuZ2V0TGFiZWxEYXRhKCdjb2xvcicpO1xuXG4gICAgICB0aGlzLnJlbmRlckxhYmVsKGFycmF5T2ZMYWJlbExpbmVzLCBjYW52YXMsIG5vZGUsIGNvbnRyb2xsZXIpO1xuICAgICAgLy8gRU5EIE1FVEFNQVBTIENPREVcbiAgICAgIC8vIE9SSUdJTkFMIENPREUgIHRoaXMucmVuZGVyTGFiZWwoY2FudmFzLCBub2RlLCBjb250cm9sbGVyKTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICByZW5kZXJMYWJlbFxuXG4gICAgICAgRG9lcyB0aGUgYWN0dWFsIHJlbmRlcmluZyBvZiB0aGUgbGFiZWwgaW4gdGhlIGNhbnZhcy4gVGhlIGRlZmF1bHRcbiAgICAgICBpbXBsZW1lbnRhdGlvbiByZW5kZXJzIHRoZSBsYWJlbCBjbG9zZSB0byB0aGUgcG9zaXRpb24gb2YgdGhlIG5vZGUsIHRoaXNcbiAgICAgICBtZXRob2Qgc2hvdWxkIGJlIG92ZXJyaWRlbiB0byBwb3NpdGlvbiB0aGUgbGFiZWxzIGRpZmZlcmVudGx5LlxuXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIGNhbnZhcyAtIEEgPENhbnZhcz4gaW5zdGFuY2UuXG4gICAgICAgbm9kZSAtIEEgPEdyYXBoLk5vZGU+LlxuICAgICAgIGNvbnRyb2xsZXIgLSBBIGNvbmZpZ3VyYXRpb24gb2JqZWN0LiBTZWUgYWxzbyA8SHlwZXJ0cmVlPiwgPFJHcmFwaD4sIDxTVD4uXG4gICAgKi9cbiAgICByZW5kZXJMYWJlbDogZnVuY3Rpb24oY3VzdG9tTGFiZWwsIGNhbnZhcywgbm9kZSwgY29udHJvbGxlcikge1xuICAgICAgdmFyIGN0eCA9IGNhbnZhcy5nZXRDdHgoKTtcbiAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpO1xuICAgICAgLy9jdHguZmlsbFRleHQobm9kZS5uYW1lLCBwb3MueCwgcG9zLnkgKyBub2RlLmdldERhdGEoXCJoZWlnaHRcIikgLyAyKTtcbiAgICAgIC8vIFNUQVJUIE1FVEFNQVBTIENPREVcbiAgICAgIHZhciBpbmRleDtcbiAgICAgIGZvciAoaW5kZXggPSAwOyBpbmRleCA8IGN1c3RvbUxhYmVsLmxlbmd0aDsgKytpbmRleCkge1xuICAgICAgICBjdHguZmlsbFRleHQoY3VzdG9tTGFiZWxbaW5kZXhdLCBwb3MueCwgcG9zLnkgKyBub2RlLmdldERhdGEoXCJoZWlnaHRcIikgKyAyMyArICgyNSppbmRleCkpO1xuICAgICAgfVxuICAgICAgLy8gRU5EIE1FVEFNQVBTIENPREVcbiAgICB9LFxuXG4gICAgaGlkZUxhYmVsOiAkLmVtcHR5LFxuICAgIGhpZGVMYWJlbHM6ICQuZW1wdHlcbn0pO1xuXG4vKlxuICAgQ2xhc3M6IEdyYXBoLkxhYmVsLkRPTVxuXG4gICBBYnN0cmFjdCBDbGFzcyBpbXBsZW1lbnRpbmcgc29tZSBET00gbGFiZWwgbWV0aG9kcy5cblxuICAgSW1wbGVtZW50ZWQgYnk6XG5cbiAgIDxHcmFwaC5MYWJlbC5IVE1MPiBhbmQgPEdyYXBoLkxhYmVsLlNWRz4uXG5cbiovXG5HcmFwaC5MYWJlbC5ET00gPSBuZXcgQ2xhc3Moe1xuICAgIC8vQSBmbGFnIHZhbHVlIGluZGljYXRpbmcgaWYgbm9kZSBsYWJlbHMgYXJlIGJlaW5nIGRpc3BsYXllZCBvciBub3QuXG4gICAgbGFiZWxzSGlkZGVuOiBmYWxzZSxcbiAgICAvL0xhYmVsIGNvbnRhaW5lclxuICAgIGxhYmVsQ29udGFpbmVyOiBmYWxzZSxcbiAgICAvL0xhYmVsIGVsZW1lbnRzIGhhc2guXG4gICAgbGFiZWxzOiB7fSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBnZXRMYWJlbENvbnRhaW5lclxuXG4gICAgICAgTGF6eSBmZXRjaGVyIGZvciB0aGUgbGFiZWwgY29udGFpbmVyLlxuXG4gICAgICAgUmV0dXJuczpcblxuICAgICAgIFRoZSBsYWJlbCBjb250YWluZXIgRE9NIGVsZW1lbnQuXG5cbiAgICAgICBFeGFtcGxlOlxuXG4gICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgICAgICAgdmFyIGxhYmVsQ29udGFpbmVyID0gdml6LmxhYmVscy5nZXRMYWJlbENvbnRhaW5lcigpO1xuICAgICAgICBhbGVydChsYWJlbENvbnRhaW5lci5pbm5lckhUTUwpO1xuICAgICAgKGVuZCBjb2RlKVxuICAgICovXG4gICAgZ2V0TGFiZWxDb250YWluZXI6IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMubGFiZWxDb250YWluZXIgP1xuICAgICAgICB0aGlzLmxhYmVsQ29udGFpbmVyIDpcbiAgICAgICAgdGhpcy5sYWJlbENvbnRhaW5lciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMudml6LmNvbmZpZy5sYWJlbENvbnRhaW5lcik7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBnZXRMYWJlbFxuXG4gICAgICAgTGF6eSBmZXRjaGVyIGZvciB0aGUgbGFiZWwgZWxlbWVudC5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICBpZCAtIChzdHJpbmcpIFRoZSBsYWJlbCBpZCAod2hpY2ggaXMgYWxzbyBhIDxHcmFwaC5Ob2RlPiBpZCkuXG5cbiAgICAgICBSZXR1cm5zOlxuXG4gICAgICAgVGhlIGxhYmVsIGVsZW1lbnQuXG5cbiAgICAgICBFeGFtcGxlOlxuXG4gICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgICAgICAgdmFyIGxhYmVsID0gdml6LmxhYmVscy5nZXRMYWJlbCgnc29tZWlkJyk7XG4gICAgICAgIGFsZXJ0KGxhYmVsLmlubmVySFRNTCk7XG4gICAgICAoZW5kIGNvZGUpXG5cbiAgICAqL1xuICAgIGdldExhYmVsOiBmdW5jdGlvbihpZCkge1xuICAgICAgcmV0dXJuIChpZCBpbiB0aGlzLmxhYmVscyAmJiB0aGlzLmxhYmVsc1tpZF0gIT0gbnVsbCkgP1xuICAgICAgICB0aGlzLmxhYmVsc1tpZF0gOlxuICAgICAgICB0aGlzLmxhYmVsc1tpZF0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCk7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBoaWRlTGFiZWxzXG5cbiAgICAgICBIaWRlcyBhbGwgbGFiZWxzIChieSBoaWRpbmcgdGhlIGxhYmVsIGNvbnRhaW5lcikuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgaGlkZSAtIChib29sZWFuKSBBIGJvb2xlYW4gdmFsdWUgaW5kaWNhdGluZyBpZiB0aGUgbGFiZWwgY29udGFpbmVyIG11c3QgYmUgaGlkZGVuIG9yIG5vdC5cblxuICAgICAgIEV4YW1wbGU6XG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgIHZhciB2aXogPSBuZXcgJGppdC5WaXoob3B0aW9ucyk7XG4gICAgICAgIHJnLmxhYmVscy5oaWRlTGFiZWxzKHRydWUpO1xuICAgICAgIChlbmQgY29kZSlcblxuICAgICovXG4gICAgaGlkZUxhYmVsczogZnVuY3Rpb24gKGhpZGUpIHtcbiAgICAgIHZhciBjb250YWluZXIgPSB0aGlzLmdldExhYmVsQ29udGFpbmVyKCk7XG4gICAgICBpZihoaWRlKVxuICAgICAgICBjb250YWluZXIuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICAgIGVsc2VcbiAgICAgICAgY29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnJztcbiAgICAgIHRoaXMubGFiZWxzSGlkZGVuID0gaGlkZTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IGNsZWFyTGFiZWxzXG5cbiAgICAgICBDbGVhcnMgdGhlIGxhYmVsIGNvbnRhaW5lci5cblxuICAgICAgIFVzZWZ1bCB3aGVuIHVzaW5nIGEgbmV3IHZpc3VhbGl6YXRpb24gd2l0aCB0aGUgc2FtZSBjYW52YXMgZWxlbWVudC93aWRnZXQuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgZm9yY2UgLSAoYm9vbGVhbikgRm9yY2VzIGRlbGV0aW9uIG9mIGFsbCBsYWJlbHMuXG5cbiAgICAgICBFeGFtcGxlOlxuICAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICB2YXIgdml6ID0gbmV3ICRqaXQuVml6KG9wdGlvbnMpO1xuICAgICAgICB2aXoubGFiZWxzLmNsZWFyTGFiZWxzKCk7XG4gICAgICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgIGNsZWFyTGFiZWxzOiBmdW5jdGlvbihmb3JjZSkge1xuICAgICAgZm9yKHZhciBpZCBpbiB0aGlzLmxhYmVscykge1xuICAgICAgICBpZiAoZm9yY2UgfHwgIXRoaXMudml6LmdyYXBoLmhhc05vZGUoaWQpKSB7XG4gICAgICAgICAgdGhpcy5kaXNwb3NlTGFiZWwoaWQpO1xuICAgICAgICAgIGRlbGV0ZSB0aGlzLmxhYmVsc1tpZF07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IGRpc3Bvc2VMYWJlbFxuXG4gICAgICAgUmVtb3ZlcyBhIGxhYmVsLlxuXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIGlkIC0gKHN0cmluZykgQSBsYWJlbCBpZCAod2hpY2ggZ2VuZXJhbGx5IGlzIGFsc28gYSA8R3JhcGguTm9kZT4gaWQpLlxuXG4gICAgICAgRXhhbXBsZTpcbiAgICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgICAgICAgdml6LmxhYmVscy5kaXNwb3NlTGFiZWwoJ2xhYmVsaWQnKTtcbiAgICAgICAoZW5kIGNvZGUpXG4gICAgKi9cbiAgICBkaXNwb3NlTGFiZWw6IGZ1bmN0aW9uKGlkKSB7XG4gICAgICB2YXIgZWxlbSA9IHRoaXMuZ2V0TGFiZWwoaWQpO1xuICAgICAgaWYoZWxlbSAmJiBlbGVtLnBhcmVudE5vZGUpIHtcbiAgICAgICAgZWxlbS5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGVsZW0pO1xuICAgICAgfVxuICAgIH0sXG5cbiAgICAvKlxuICAgICAgIE1ldGhvZDogaGlkZUxhYmVsXG5cbiAgICAgICBIaWRlcyB0aGUgY29ycmVzcG9uZGluZyA8R3JhcGguTm9kZT4gbGFiZWwuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgbm9kZSAtIChvYmplY3QpIEEgPEdyYXBoLk5vZGU+LiBDYW4gYWxzbyBiZSBhbiBhcnJheSBvZiA8R3JhcGguTm9kZXM+LlxuICAgICAgIHNob3cgLSAoYm9vbGVhbikgSWYgKnRydWUqLCBub2RlcyB3aWxsIGJlIHNob3duLiBPdGhlcndpc2Ugbm9kZXMgd2lsbCBiZSBoaWRkZW4uXG5cbiAgICAgICBFeGFtcGxlOlxuICAgICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgICB2YXIgcmcgPSBuZXcgJGppdC5WaXoob3B0aW9ucyk7XG4gICAgICAgIHZpei5sYWJlbHMuaGlkZUxhYmVsKHZpei5ncmFwaC5nZXROb2RlKCdzb21laWQnKSwgZmFsc2UpO1xuICAgICAgIChlbmQgY29kZSlcbiAgICAqL1xuICAgIGhpZGVMYWJlbDogZnVuY3Rpb24obm9kZSwgc2hvdykge1xuICAgICAgbm9kZSA9ICQuc3BsYXQobm9kZSk7XG4gICAgICB2YXIgc3QgPSBzaG93ID8gXCJcIiA6IFwibm9uZVwiLCBsYWIsIHRoYXQgPSB0aGlzO1xuICAgICAgJC5lYWNoKG5vZGUsIGZ1bmN0aW9uKG4pIHtcbiAgICAgICAgdmFyIGxhYiA9IHRoYXQuZ2V0TGFiZWwobi5pZCk7XG4gICAgICAgIGlmIChsYWIpIHtcbiAgICAgICAgICBsYWIuc3R5bGUuZGlzcGxheSA9IHN0O1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgICBmaXRzSW5DYW52YXNcblxuICAgICAgIFJldHVybnMgX3RydWVfIG9yIF9mYWxzZV8gaWYgdGhlIGxhYmVsIGZvciB0aGUgbm9kZSBpcyBjb250YWluZWQgaW4gdGhlIGNhbnZhcyBkb20gZWxlbWVudCBvciBub3QuXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgcG9zIC0gQSA8Q29tcGxleD4gaW5zdGFuY2UgKEknbSBkb2luZyBkdWNrIHR5cGluZyBoZXJlIHNvIGFueSBvYmplY3Qgd2l0aCBfeF8gYW5kIF95XyBwYXJhbWV0ZXJzIHdpbGwgZG8pLlxuICAgICAgIGNhbnZhcyAtIEEgPENhbnZhcz4gaW5zdGFuY2UuXG5cbiAgICAgICBSZXR1cm5zOlxuXG4gICAgICAgQSBib29sZWFuIHZhbHVlIHNwZWNpZnlpbmcgaWYgdGhlIGxhYmVsIGlzIGNvbnRhaW5lZCBpbiB0aGUgPENhbnZhcz4gRE9NIGVsZW1lbnQgb3Igbm90LlxuXG4gICAgKi9cbiAgICBmaXRzSW5DYW52YXM6IGZ1bmN0aW9uKHBvcywgY2FudmFzKSB7XG4gICAgICB2YXIgc2l6ZSA9IGNhbnZhcy5nZXRTaXplKCk7XG4gICAgICBpZihwb3MueCA+PSBzaXplLndpZHRoIHx8IHBvcy54IDwgMFxuICAgICAgICAgfHwgcG9zLnkgPj0gc2l6ZS5oZWlnaHQgfHwgcG9zLnkgPCAwKSByZXR1cm4gZmFsc2U7XG4gICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxufSk7XG5cbi8qXG4gICBDbGFzczogR3JhcGguTGFiZWwuSFRNTFxuXG4gICBJbXBsZW1lbnRzIEhUTUwgbGFiZWxzLlxuXG4gICBFeHRlbmRzOlxuXG4gICBBbGwgPEdyYXBoLkxhYmVsLkRPTT4gbWV0aG9kcy5cblxuKi9cbkdyYXBoLkxhYmVsLkhUTUwgPSBuZXcgQ2xhc3Moe1xuICAgIEltcGxlbWVudHM6IEdyYXBoLkxhYmVsLkRPTSxcblxuICAgIC8qXG4gICAgICAgTWV0aG9kOiBwbG90TGFiZWxcblxuICAgICAgIFBsb3RzIGEgbGFiZWwgZm9yIGEgZ2l2ZW4gbm9kZS5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICBjYW52YXMgLSAob2JqZWN0KSBBIDxDYW52YXM+IGluc3RhbmNlLlxuICAgICAgIG5vZGUgLSAob2JqZWN0KSBBIDxHcmFwaC5Ob2RlPi5cbiAgICAgICBjb250cm9sbGVyIC0gKG9iamVjdCkgQSBjb25maWd1cmF0aW9uIG9iamVjdC5cbiAgICAgICBcbiAgICAgIEV4YW1wbGU6XG4gICAgICAgXG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgICAgICB2YXIgbm9kZSA9IHZpei5ncmFwaC5nZXROb2RlKCdub2RlSWQnKTtcbiAgICAgICB2aXoubGFiZWxzLnBsb3RMYWJlbCh2aXouY2FudmFzLCBub2RlLCB2aXouY29uZmlnKTtcbiAgICAgICAoZW5kIGNvZGUpXG5cblxuICAgICovXG4gICAgcGxvdExhYmVsOiBmdW5jdGlvbihjYW52YXMsIG5vZGUsIGNvbnRyb2xsZXIpIHtcbiAgICAgIHZhciBpZCA9IG5vZGUuaWQsIHRhZyA9IHRoaXMuZ2V0TGFiZWwoaWQpO1xuXG4gICAgICBpZighdGFnICYmICEodGFnID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoaWQpKSkge1xuICAgICAgICB0YWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IHRoaXMuZ2V0TGFiZWxDb250YWluZXIoKTtcbiAgICAgICAgdGFnLmlkID0gaWQ7XG4gICAgICAgIHRhZy5jbGFzc05hbWUgPSAnbm9kZSc7XG4gICAgICAgIHRhZy5zdHlsZS5wb3NpdGlvbiA9ICdhYnNvbHV0ZSc7XG4gICAgICAgIGNvbnRyb2xsZXIub25DcmVhdGVMYWJlbCh0YWcsIG5vZGUpO1xuICAgICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQodGFnKTtcbiAgICAgICAgdGhpcy5sYWJlbHNbbm9kZS5pZF0gPSB0YWc7XG4gICAgICB9XG5cbiAgICAgIHRoaXMucGxhY2VMYWJlbCh0YWcsIG5vZGUsIGNvbnRyb2xsZXIpO1xuICAgIH1cbn0pO1xuXG4vKlxuICAgQ2xhc3M6IEdyYXBoLkxhYmVsLlNWR1xuXG4gICBJbXBsZW1lbnRzIFNWRyBsYWJlbHMuXG5cbiAgIEV4dGVuZHM6XG5cbiAgIEFsbCA8R3JhcGguTGFiZWwuRE9NPiBtZXRob2RzLlxuKi9cbkdyYXBoLkxhYmVsLlNWRyA9IG5ldyBDbGFzcyh7XG4gICAgSW1wbGVtZW50czogR3JhcGguTGFiZWwuRE9NLFxuXG4gICAgLypcbiAgICAgICBNZXRob2Q6IHBsb3RMYWJlbFxuXG4gICAgICAgUGxvdHMgYSBsYWJlbCBmb3IgYSBnaXZlbiBub2RlLlxuXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIGNhbnZhcyAtIChvYmplY3QpIEEgPENhbnZhcz4gaW5zdGFuY2UuXG4gICAgICAgbm9kZSAtIChvYmplY3QpIEEgPEdyYXBoLk5vZGU+LlxuICAgICAgIGNvbnRyb2xsZXIgLSAob2JqZWN0KSBBIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxuICAgICAgIFxuICAgICAgIEV4YW1wbGU6XG4gICAgICAgXG4gICAgICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICAgdmFyIHZpeiA9IG5ldyAkaml0LlZpeihvcHRpb25zKTtcbiAgICAgICB2YXIgbm9kZSA9IHZpei5ncmFwaC5nZXROb2RlKCdub2RlSWQnKTtcbiAgICAgICB2aXoubGFiZWxzLnBsb3RMYWJlbCh2aXouY2FudmFzLCBub2RlLCB2aXouY29uZmlnKTtcbiAgICAgICAoZW5kIGNvZGUpXG5cblxuICAgICovXG4gICAgcGxvdExhYmVsOiBmdW5jdGlvbihjYW52YXMsIG5vZGUsIGNvbnRyb2xsZXIpIHtcbiAgICAgIHZhciBpZCA9IG5vZGUuaWQsIHRhZyA9IHRoaXMuZ2V0TGFiZWwoaWQpO1xuICAgICAgaWYoIXRhZyAmJiAhKHRhZyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKSkpIHtcbiAgICAgICAgdmFyIG5zID0gJ2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJztcbiAgICAgICAgICB0YWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMobnMsICdzdmc6dGV4dCcpO1xuICAgICAgICB2YXIgdHNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMobnMsICdzdmc6dHNwYW4nKTtcbiAgICAgICAgdGFnLmFwcGVuZENoaWxkKHRzcGFuKTtcbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IHRoaXMuZ2V0TGFiZWxDb250YWluZXIoKTtcbiAgICAgICAgdGFnLnNldEF0dHJpYnV0ZSgnaWQnLCBpZCk7XG4gICAgICAgIHRhZy5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgJ25vZGUnKTtcbiAgICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKHRhZyk7XG4gICAgICAgIGNvbnRyb2xsZXIub25DcmVhdGVMYWJlbCh0YWcsIG5vZGUpO1xuICAgICAgICB0aGlzLmxhYmVsc1tub2RlLmlkXSA9IHRhZztcbiAgICAgIH1cbiAgICAgIHRoaXMucGxhY2VMYWJlbCh0YWcsIG5vZGUsIGNvbnRyb2xsZXIpO1xuICAgIH1cbn0pO1xuXG5cblxuLypcbiAqIEZpbGU6IExvYWRlci5qc1xuICogXG4gKi9cblxuLypcbiAgIE9iamVjdDogTG9hZGVyXG5cbiAgIFByb3ZpZGVzIG1ldGhvZHMgZm9yIGxvYWRpbmcgYW5kIHNlcnZpbmcgSlNPTiBkYXRhLlxuKi9cbnZhciBMb2FkZXIgPSB7XG4gICAgIGNvbnN0cnVjdDogZnVuY3Rpb24oanNvbikge1xuICAgICAgICB2YXIgaXNHcmFwaCA9ICgkLnR5cGUoanNvbikgPT0gJ2FycmF5Jyk7XG4gICAgICAgIHZhciBhbnMgPSBuZXcgR3JhcGgodGhpcy5ncmFwaE9wdGlvbnMsIHRoaXMuY29uZmlnLk5vZGUsIHRoaXMuY29uZmlnLkVkZ2UsIHRoaXMuY29uZmlnLkxhYmVsKTtcbiAgICAgICAgaWYoIWlzR3JhcGgpIFxuICAgICAgICAgICAgLy9tYWtlIHRyZWVcbiAgICAgICAgICAgIChmdW5jdGlvbiAoYW5zLCBqc29uKSB7XG4gICAgICAgICAgICAgICAgYW5zLmFkZE5vZGUoanNvbik7XG4gICAgICAgICAgICAgICAgaWYoanNvbi5jaGlsZHJlbikge1xuICAgICAgICAgICAgICAgICAgZm9yKHZhciBpPTAsIGNoID0ganNvbi5jaGlsZHJlbjsgaTxjaC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICBhbnMuYWRkQWRqYWNlbmNlKGpzb24sIGNoW2ldKTtcbiAgICAgICAgICAgICAgICAgICAgYXJndW1lbnRzLmNhbGxlZShhbnMsIGNoW2ldKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KShhbnMsIGpzb24pO1xuICAgICAgICBlbHNlXG4gICAgICAgICAgICAvL21ha2UgZ3JhcGhcbiAgICAgICAgICAgIChmdW5jdGlvbiAoYW5zLCBqc29uKSB7XG4gICAgICAgICAgICAgICAgdmFyIGdldE5vZGUgPSBmdW5jdGlvbihpZCkge1xuICAgICAgICAgICAgICAgICAgZm9yKHZhciBpPTAsIGw9anNvbi5sZW5ndGg7IGk8bDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmKGpzb25baV0uaWQgPT0gaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ganNvbltpXTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgLy8gVGhlIG5vZGUgd2FzIG5vdCBkZWZpbmVkIGluIHRoZSBKU09OXG4gICAgICAgICAgICAgICAgICAvLyBMZXQncyBjcmVhdGUgaXRcbiAgICAgICAgICAgICAgICAgIHZhciBuZXdOb2RlID0ge1xuICAgICAgICAgICAgICAgIFx0XHRcImlkXCIgOiBpZCxcbiAgICAgICAgICAgICAgICBcdFx0XCJuYW1lXCIgOiBpZFxuICAgICAgICAgICAgICAgIFx0fTtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBhbnMuYWRkTm9kZShuZXdOb2RlKTtcbiAgICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAgICAgZm9yKHZhciBpPTAsIGw9anNvbi5sZW5ndGg7IGk8bDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICBhbnMuYWRkTm9kZShqc29uW2ldKTtcbiAgICAgICAgICAgICAgICAgIHZhciBhZGogPSBqc29uW2ldLmFkamFjZW5jaWVzO1xuICAgICAgICAgICAgICAgICAgaWYgKGFkaikge1xuICAgICAgICAgICAgICAgICAgICBmb3IodmFyIGo9MCwgbGo9YWRqLmxlbmd0aDsgajxsajsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgdmFyIG5vZGUgPSBhZGpbal0sIGRhdGEgPSB7fTtcbiAgICAgICAgICAgICAgICAgICAgICBpZih0eXBlb2YgYWRqW2pdICE9ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gJC5tZXJnZShub2RlLmRhdGEsIHt9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5vZGUgPSBub2RlLm5vZGVUbztcbiAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgYW5zLmFkZEFkamFjZW5jZShqc29uW2ldLCBnZXROb2RlKG5vZGUpLCBkYXRhKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pKGFucywganNvbik7XG5cbiAgICAgICAgcmV0dXJuIGFucztcbiAgICB9LFxuXG4gICAgLypcbiAgICAgTWV0aG9kOiBsb2FkSlNPTlxuICAgIFxuICAgICBMb2FkcyBhIEpTT04gc3RydWN0dXJlIHRvIHRoZSB2aXN1YWxpemF0aW9uLiBUaGUgSlNPTiBzdHJ1Y3R1cmUgY2FuIGJlIGEgSlNPTiAqdHJlZSogb3IgKmdyYXBoKiBzdHJ1Y3R1cmUuXG4gICAgIFxuICAgICAgQSBKU09OIHRyZWUgb3IgZ3JhcGggc3RydWN0dXJlIGNvbnNpc3RzIG9mIG5vZGVzLCBlYWNoIGhhdmluZyBhcyBwcm9wZXJ0aWVzXG4gICAgICAgXG4gICAgICAgaWQgLSAoc3RyaW5nKSBBIHVuaXF1ZSBpZGVudGlmaWVyIGZvciB0aGUgbm9kZVxuICAgICAgIG5hbWUgLSAoc3RyaW5nKSBBIG5vZGUncyBuYW1lXG4gICAgICAgZGF0YSAtIChvYmplY3QpIFRoZSBkYXRhIG9wdGlvbmFsIHByb3BlcnR5IGNvbnRhaW5zIGEgaGFzaCAoaS5lIHt9KSBcbiAgICAgICB3aGVyZSB5b3UgY2FuIHN0b3JlIGFsbCB0aGUgaW5mb3JtYXRpb24geW91IHdhbnQgYWJvdXQgdGhpcyBub2RlLlxuICAgICAgICBcbiAgICAgIEZvciBKU09OICpUcmVlKiBzdHJ1Y3R1cmVzLCB0aGVyZSdzIGFuIGV4dHJhIG9wdGlvbmFsIHByb3BlcnR5ICpjaGlsZHJlbiogb2YgdHlwZSBBcnJheSB3aGljaCBjb250YWlucyB0aGUgbm9kZSdzIGNoaWxkcmVuLlxuICAgICAgXG4gICAgICBFeGFtcGxlOlxuXG4gICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgdmFyIGpzb24gPSB7ICBcbiAgICAgICAgICBcImlkXCI6IFwiYVVuaXF1ZUlkZW50aWZpZXJcIiwgIFxuICAgICAgICAgIFwibmFtZVwiOiBcInVzdWFsbHkgYSBub2RlcyBuYW1lXCIsICBcbiAgICAgICAgICBcImRhdGFcIjoge1xuICAgICAgICAgICAgXCJzb21lIGtleVwiOiBcInNvbWUgdmFsdWVcIixcbiAgICAgICAgICAgIFwic29tZSBvdGhlciBrZXlcIjogXCJzb21lIG90aGVyIHZhbHVlXCJcbiAgICAgICAgICAgfSwgIFxuICAgICAgICAgIFwiY2hpbGRyZW5cIjogWyAqb3RoZXIgbm9kZXMgb3IgZW1wdHkqIF0gIFxuICAgICAgICB9OyAgXG4gICAgICAoZW5kIGNvZGUpXG4gICAgICAgIFxuICAgICAgICBKU09OICpHcmFwaCogc3RydWN0dXJlcyBjb25zaXN0IG9mIGFuIGFycmF5IG9mIG5vZGVzLCBlYWNoIHNwZWNpZnlpbmcgdGhlIG5vZGVzIHRvIHdoaWNoIHRoZSBjdXJyZW50IG5vZGUgaXMgY29ubmVjdGVkLiBcbiAgICAgICAgRm9yIEpTT04gKkdyYXBoKiBzdHJ1Y3R1cmVzLCB0aGUgKmNoaWxkcmVuKiBwcm9wZXJ0eSBpcyByZXBsYWNlZCBieSB0aGUgKmFkamFjZW5jaWVzKiBwcm9wZXJ0eS5cbiAgICAgICAgXG4gICAgICAgIFRoZXJlIGFyZSB0d28gdHlwZXMgb2YgKkdyYXBoKiBzdHJ1Y3R1cmVzLCAqc2ltcGxlKiBhbmQgKmV4dGVuZGVkKiBncmFwaCBzdHJ1Y3R1cmVzLlxuICAgICAgICBcbiAgICAgICAgRm9yICpzaW1wbGUqIEdyYXBoIHN0cnVjdHVyZXMsIHRoZSBhZGphY2VuY2llcyBwcm9wZXJ0eSBjb250YWlucyBhbiBhcnJheSBvZiBzdHJpbmdzLCBlYWNoIHNwZWNpZnlpbmcgdGhlIFxuICAgICAgICBpZCBvZiB0aGUgbm9kZSBjb25uZWN0ZWQgdG8gdGhlIG1haW4gbm9kZS5cbiAgICAgICAgXG4gICAgICAgIEV4YW1wbGU6XG4gICAgICAgIFxuICAgICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgdmFyIGpzb24gPSBbICBcbiAgICAgICAgICB7ICBcbiAgICAgICAgICAgIFwiaWRcIjogXCJhVW5pcXVlSWRlbnRpZmllclwiLCAgXG4gICAgICAgICAgICBcIm5hbWVcIjogXCJ1c3VhbGx5IGEgbm9kZXMgbmFtZVwiLCAgXG4gICAgICAgICAgICBcImRhdGFcIjoge1xuICAgICAgICAgICAgICBcInNvbWUga2V5XCI6IFwic29tZSB2YWx1ZVwiLFxuICAgICAgICAgICAgICBcInNvbWUgb3RoZXIga2V5XCI6IFwic29tZSBvdGhlciB2YWx1ZVwiXG4gICAgICAgICAgICAgfSwgIFxuICAgICAgICAgICAgXCJhZGphY2VuY2llc1wiOiBbXCJhbm90aGVyVW5pcXVlSWRlbnRpZmllclwiLCBcInlldEFub3RoZXJVbmlxdWVJZGVudGlmaWVyXCIsICdldGMnXSAgXG4gICAgICAgICAgfSxcblxuICAgICAgICAgICdvdGhlciBub2RlcyBnbyBoZXJlLi4uJyBcbiAgICAgICAgXTsgICAgICAgICAgXG4gICAgICAgIChlbmQgY29kZSlcbiAgICAgICAgXG4gICAgICAgIEZvciAqZXh0ZW5kZWQgR3JhcGggc3RydWN0dXJlcyosIHRoZSBhZGphY2VuY2llcyBwcm9wZXJ0eSBjb250YWlucyBhbiBhcnJheSBvZiBBZGphY2VuY3kgb2JqZWN0cyB0aGF0IGhhdmUgYXMgcHJvcGVydGllc1xuICAgICAgICBcbiAgICAgICAgbm9kZVRvIC0gKHN0cmluZykgVGhlIG90aGVyIG5vZGUgY29ubmVjdGVkIGJ5IHRoaXMgYWRqYWNlbmN5LlxuICAgICAgICBkYXRhIC0gKG9iamVjdCkgQSBkYXRhIHByb3BlcnR5LCB3aGVyZSB3ZSBjYW4gc3RvcmUgY3VzdG9tIGtleS92YWx1ZSBpbmZvcm1hdGlvbi5cbiAgICAgICAgXG4gICAgICAgIEV4YW1wbGU6XG4gICAgICAgIFxuICAgICAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgICAgdmFyIGpzb24gPSBbICBcbiAgICAgICAgICB7ICBcbiAgICAgICAgICAgIFwiaWRcIjogXCJhVW5pcXVlSWRlbnRpZmllclwiLCAgXG4gICAgICAgICAgICBcIm5hbWVcIjogXCJ1c3VhbGx5IGEgbm9kZXMgbmFtZVwiLCAgXG4gICAgICAgICAgICBcImRhdGFcIjoge1xuICAgICAgICAgICAgICBcInNvbWUga2V5XCI6IFwic29tZSB2YWx1ZVwiLFxuICAgICAgICAgICAgICBcInNvbWUgb3RoZXIga2V5XCI6IFwic29tZSBvdGhlciB2YWx1ZVwiXG4gICAgICAgICAgICAgfSwgIFxuICAgICAgICAgICAgXCJhZGphY2VuY2llc1wiOiBbICBcbiAgICAgICAgICAgIHsgIFxuICAgICAgICAgICAgICBub2RlVG86XCJhTm9kZUlkXCIsICBcbiAgICAgICAgICAgICAgZGF0YToge30gLy9wdXQgd2hhdGV2ZXIgeW91IHdhbnQgaGVyZSAgXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgJ290aGVyIGFkamFjZW5jaWVzIGdvIGhlcmUuLi4nICBcbiAgICAgICAgICB9LFxuXG4gICAgICAgICAgJ290aGVyIG5vZGVzIGdvIGhlcmUuLi4nIFxuICAgICAgICBdOyAgICAgICAgICBcbiAgICAgICAgKGVuZCBjb2RlKVxuICAgICAgIFxuICAgICAgIEFib3V0IHRoZSBkYXRhIHByb3BlcnR5OlxuICAgICAgIFxuICAgICAgIEFzIGRlc2NyaWJlZCBiZWZvcmUsIHlvdSBjYW4gc3RvcmUgY3VzdG9tIGRhdGEgaW4gdGhlICpkYXRhKiBwcm9wZXJ0eSBvZiBKU09OICpub2RlcyogYW5kICphZGphY2VuY2llcyouIFxuICAgICAgIFlvdSBjYW4gdXNlIGFsbW9zdCBhbnkgc3RyaW5nIGFzIGtleSBmb3IgdGhlIGRhdGEgb2JqZWN0LiBTb21lIGtleXMgdGhvdWdoIGFyZSByZXNlcnZlZCBieSB0aGUgdG9vbGtpdCwgYW5kIFxuICAgICAgIGhhdmUgc3BlY2lhbCBtZWFuaW5ncy4gVGhpcyBpcyB0aGUgY2FzZSBmb3Iga2V5cyBzdGFydGluZyB3aXRoIGEgZG9sbGFyIHNpZ24sIGZvciBleGFtcGxlLCAqJHdpZHRoKi5cbiAgICAgICBcbiAgICAgICBGb3IgSlNPTiAqbm9kZSogb2JqZWN0cywgYWRkaW5nIGRvbGxhciBwcmVmaXhlZCBwcm9wZXJ0aWVzIHRoYXQgbWF0Y2ggdGhlIG5hbWVzIG9mIHRoZSBvcHRpb25zIGRlZmluZWQgaW4gXG4gICAgICAgPE9wdGlvbnMuTm9kZT4gd2lsbCBvdmVycmlkZSB0aGUgZ2VuZXJhbCB2YWx1ZSBmb3IgdGhhdCBvcHRpb24gd2l0aCB0aGF0IHBhcnRpY3VsYXIgdmFsdWUuIEZvciB0aGlzIHRvIHdvcmsgXG4gICAgICAgaG93ZXZlciwgeW91IGRvIGhhdmUgdG8gc2V0ICpvdmVycmlkYWJsZSA9IHRydWUqIGluIDxPcHRpb25zLk5vZGU+LlxuICAgICAgIFxuICAgICAgIFRoZSBzYW1lIHRoaW5nIGlzIHRydWUgZm9yIEpTT04gYWRqYWNlbmNpZXMuIERvbGxhciBwcmVmaXhlZCBkYXRhIHByb3BlcnRpZXMgd2lsbCBhbHRlciB2YWx1ZXMgc2V0IGluIDxPcHRpb25zLkVkZ2U+IFxuICAgICAgIGlmIDxPcHRpb25zLkVkZ2U+IGhhcyAqb3ZlcnJpZGFibGUgPSB0cnVlKi5cbiAgICAgICBcbiAgICAgICBXaGVuIGxvYWRpbmcgSlNPTiBkYXRhIGludG8gVHJlZU1hcHMsIHRoZSAqZGF0YSogcHJvcGVydHkgbXVzdCBjb250YWluIGEgdmFsdWUgZm9yIHRoZSAqJGFyZWEqIGtleSwgXG4gICAgICAgc2luY2UgdGhpcyBpcyB0aGUgdmFsdWUgd2hpY2ggd2lsbCBiZSB0YWtlbiBpbnRvIGFjY291bnQgd2hlbiBjcmVhdGluZyB0aGUgbGF5b3V0LiBcbiAgICAgICBUaGUgc2FtZSB0aGluZyBnb2VzIGZvciB0aGUgKiRjb2xvciogcGFyYW1ldGVyLlxuICAgICAgIFxuICAgICAgIEluIEpTT04gTm9kZXMgeW91IGNhbiB1c2UgYWxzbyAqJGxhYmVsLSogcHJlZml4ZWQgcHJvcGVydGllcyB0byByZWZlciB0byA8T3B0aW9ucy5MYWJlbD4gcHJvcGVydGllcy4gRm9yIGV4YW1wbGUsIFxuICAgICAgICokbGFiZWwtc2l6ZSogd2lsbCByZWZlciB0byA8T3B0aW9ucy5MYWJlbD4gc2l6ZSBwcm9wZXJ0eS4gQWxzbywgaW4gSlNPTiBub2RlcyBhbmQgYWRqYWNlbmNpZXMgeW91IGNhbiBzZXQgXG4gICAgICAgY2FudmFzIHNwZWNpZmljIHByb3BlcnRpZXMgaW5kaXZpZHVhbGx5IGJ5IHVzaW5nIHRoZSAqJGNhbnZhcy0qIHByZWZpeC4gRm9yIGV4YW1wbGUsICokY2FudmFzLXNoYWRvd0JsdXIqIHdpbGwgcmVmZXIgXG4gICAgICAgdG8gdGhlICpzaGFkb3dCbHVyKiBwcm9wZXJ0eS5cbiAgICAgICBcbiAgICAgICBUaGVzZSBwcm9wZXJ0aWVzIGNhbiBhbHNvIGJlIGFjY2Vzc2VkIGFmdGVyIGxvYWRpbmcgdGhlIEpTT04gZGF0YSBmcm9tIDxHcmFwaC5Ob2Rlcz4gYW5kIDxHcmFwaC5BZGphY2VuY2VzPiBcbiAgICAgICBieSB1c2luZyA8QWNjZXNzb3JzPi4gRm9yIG1vcmUgaW5mb3JtYXRpb24gdGFrZSBhIGxvb2sgYXQgdGhlIDxHcmFwaD4gYW5kIDxBY2Nlc3NvcnM+IGRvY3VtZW50YXRpb24uXG4gICAgICAgXG4gICAgICAgRmluYWxseSwgdGhlc2UgcHJvcGVydGllcyBjYW4gYWxzbyBiZSB1c2VkIHRvIGNyZWF0ZSBhZHZhbmNlZCBhbmltYXRpb25zIGxpa2Ugd2l0aCA8T3B0aW9ucy5Ob2RlU3R5bGVzPi4gRm9yIG1vcmUgXG4gICAgICAgaW5mb3JtYXRpb24gYWJvdXQgY3JlYXRpbmcgYW5pbWF0aW9ucyBwbGVhc2UgdGFrZSBhIGxvb2sgYXQgdGhlIDxHcmFwaC5QbG90PiBhbmQgPEdyYXBoLlBsb3QuYW5pbWF0ZT4gZG9jdW1lbnRhdGlvbi5cbiAgICAgICBcbiAgICAgICBsb2FkSlNPTiBQYXJhbWV0ZXJzOlxuICAgIFxuICAgICAgICBqc29uIC0gQSBKU09OIFRyZWUgb3IgR3JhcGggc3RydWN0dXJlLlxuICAgICAgICBpIC0gRm9yIEdyYXBoIHN0cnVjdHVyZXMgb25seS4gU2V0cyB0aGUgaW5kZXhlZCBub2RlIGFzIHJvb3QgZm9yIHRoZSB2aXN1YWxpemF0aW9uLlxuXG4gICAgKi9cbiAgICBsb2FkSlNPTjogZnVuY3Rpb24oanNvbiwgaSkge1xuICAgICAgdGhpcy5qc29uID0ganNvbjtcbiAgICAgIC8vaWYgdGhleSdyZSBjYW52YXMgbGFiZWxzIGVyYXNlIHRoZW0uXG4gICAgICBpZih0aGlzLmxhYmVscyAmJiB0aGlzLmxhYmVscy5jbGVhckxhYmVscykge1xuICAgICAgICB0aGlzLmxhYmVscy5jbGVhckxhYmVscyh0cnVlKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuZ3JhcGggPSB0aGlzLmNvbnN0cnVjdChqc29uKTtcbiAgICAgIGlmKCQudHlwZShqc29uKSAhPSAnYXJyYXknKXtcbiAgICAgICAgdGhpcy5yb290ID0ganNvbi5pZDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMucm9vdCA9IGpzb25baT8gaSA6IDBdLmlkO1xuICAgICAgfVxuICAgIH0sXG4gICAgXG4gICAgLypcbiAgICAgIE1ldGhvZDogdG9KU09OXG4gICBcbiAgICAgIFJldHVybnMgYSBKU09OIHRyZWUvZ3JhcGggc3RydWN0dXJlIGZyb20gdGhlIHZpc3VhbGl6YXRpb24ncyA8R3JhcGg+LiBcbiAgICAgIFNlZSA8TG9hZGVyLmxvYWRKU09OPiBmb3IgdGhlIGdyYXBoIGZvcm1hdHMgYXZhaWxhYmxlLlxuICAgICAgXG4gICAgICBTZWUgYWxzbzpcbiAgICAgIFxuICAgICAgPExvYWRlci5sb2FkSlNPTj5cbiAgICAgIFxuICAgICAgUGFyYW1ldGVyczpcbiAgICAgIFxuICAgICAgdHlwZSAtIChzdHJpbmcpIERlZmF1bHQncyBcInRyZWVcIi4gVGhlIHR5cGUgb2YgdGhlIEpTT04gc3RydWN0dXJlIHRvIGJlIHJldHVybmVkLiBcbiAgICAgIFBvc3NpYmxlIG9wdGlvbnMgYXJlIFwidHJlZVwiIG9yIFwiZ3JhcGhcIi5cbiAgICAqLyAgICBcbiAgICB0b0pTT046IGZ1bmN0aW9uKHR5cGUpIHtcbiAgICAgIHR5cGUgPSB0eXBlIHx8IFwidHJlZVwiO1xuICAgICAgaWYodHlwZSA9PSAndHJlZScpIHtcbiAgICAgICAgdmFyIGFucyA9IHt9O1xuICAgICAgICB2YXIgcm9vdE5vZGUgPSB0aGlzLmdyYXBoLmdldE5vZGUodGhpcy5yb290KTtcbiAgICAgICAgdmFyIGFucyA9IChmdW5jdGlvbiByZWNUcmVlKG5vZGUpIHtcbiAgICAgICAgICB2YXIgYW5zID0ge307XG4gICAgICAgICAgYW5zLmlkID0gbm9kZS5pZDtcbiAgICAgICAgICBhbnMubmFtZSA9IG5vZGUubmFtZTtcbiAgICAgICAgICBhbnMuZGF0YSA9IG5vZGUuZGF0YTtcbiAgICAgICAgICB2YXIgY2ggPVtdO1xuICAgICAgICAgIG5vZGUuZWFjaFN1Ym5vZGUoZnVuY3Rpb24obikge1xuICAgICAgICAgICAgY2gucHVzaChyZWNUcmVlKG4pKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBhbnMuY2hpbGRyZW4gPSBjaDtcbiAgICAgICAgICByZXR1cm4gYW5zO1xuICAgICAgICB9KShyb290Tm9kZSk7XG4gICAgICAgIHJldHVybiBhbnM7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgYW5zID0gW107XG4gICAgICAgIHZhciBUID0gISF0aGlzLmdyYXBoLmdldE5vZGUodGhpcy5yb290KS52aXNpdGVkO1xuICAgICAgICB0aGlzLmdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgICAgICB2YXIgYW5zTm9kZSA9IHt9O1xuICAgICAgICAgIGFuc05vZGUuaWQgPSBub2RlLmlkO1xuICAgICAgICAgIGFuc05vZGUubmFtZSA9IG5vZGUubmFtZTtcbiAgICAgICAgICBhbnNOb2RlLmRhdGEgPSBub2RlLmRhdGE7XG4gICAgICAgICAgdmFyIGFkanMgPSBbXTtcbiAgICAgICAgICBub2RlLmVhY2hBZGphY2VuY3koZnVuY3Rpb24oYWRqKSB7XG4gICAgICAgICAgICB2YXIgbm9kZVRvID0gYWRqLm5vZGVUbztcbiAgICAgICAgICAgIGlmKCEhbm9kZVRvLnZpc2l0ZWQgPT09IFQpIHtcbiAgICAgICAgICAgICAgdmFyIGFuc0FkaiA9IHt9O1xuICAgICAgICAgICAgICBhbnNBZGoubm9kZVRvID0gbm9kZVRvLmlkO1xuICAgICAgICAgICAgICBhbnNBZGouZGF0YSA9IGFkai5kYXRhO1xuICAgICAgICAgICAgICBhZGpzLnB1c2goYW5zQWRqKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgICBhbnNOb2RlLmFkamFjZW5jaWVzID0gYWRqcztcbiAgICAgICAgICBhbnMucHVzaChhbnNOb2RlKTtcbiAgICAgICAgICBub2RlLnZpc2l0ZWQgPSAhVDtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBhbnM7XG4gICAgICB9XG4gICAgfVxufTtcblxuXG5cbi8qXG4gKiBGaWxlOiBMYXlvdXRzLmpzXG4gKiBcbiAqIEltcGxlbWVudHMgYmFzZSBUcmVlIGFuZCBHcmFwaCBsYXlvdXRzLlxuICpcbiAqIERlc2NyaXB0aW9uOlxuICpcbiAqIEltcGxlbWVudHMgYmFzZSBUcmVlIGFuZCBHcmFwaCBsYXlvdXRzIGxpa2UgUmFkaWFsLCBUcmVlLCBldGMuXG4gKiBcbiAqL1xuXG4vKlxuICogT2JqZWN0OiBMYXlvdXRzXG4gKiBcbiAqIFBhcmVudCBvYmplY3QgZm9yIGNvbW1vbiBsYXlvdXRzLlxuICpcbiAqL1xudmFyIExheW91dHMgPSAkaml0LkxheW91dHMgPSB7fTtcblxuXG4vL1NvbWUgdXRpbCBzaGFyZWQgbGF5b3V0IGZ1bmN0aW9ucyBhcmUgZGVmaW5lZCBoZXJlLlxudmFyIE5vZGVEaW0gPSB7XG4gIGxhYmVsOiBudWxsLFxuICBcbiAgY29tcHV0ZTogZnVuY3Rpb24oZ3JhcGgsIHByb3AsIG9wdCkge1xuICAgIHRoaXMuaW5pdGlhbGl6ZUxhYmVsKG9wdCk7XG4gICAgdmFyIGxhYmVsID0gdGhpcy5sYWJlbCwgc3R5bGUgPSBsYWJlbC5zdHlsZTtcbiAgICBncmFwaC5lYWNoTm9kZShmdW5jdGlvbihuKSB7XG4gICAgICB2YXIgYXV0b1dpZHRoICA9IG4uZ2V0RGF0YSgnYXV0b1dpZHRoJyksXG4gICAgICAgICAgYXV0b0hlaWdodCA9IG4uZ2V0RGF0YSgnYXV0b0hlaWdodCcpO1xuICAgICAgaWYoYXV0b1dpZHRoIHx8IGF1dG9IZWlnaHQpIHtcbiAgICAgICAgLy9kZWxldGUgZGltZW5zaW9ucyBzaW5jZSB0aGVzZSBhcmVcbiAgICAgICAgLy9nb2luZyB0byBiZSBvdmVycmlkZGVuIG5vdy5cbiAgICAgICAgZGVsZXRlIG4uZGF0YS4kd2lkdGg7XG4gICAgICAgIGRlbGV0ZSBuLmRhdGEuJGhlaWdodDtcbiAgICAgICAgZGVsZXRlIG4uZGF0YS4kZGltO1xuICAgICAgICBcbiAgICAgICAgdmFyIHdpZHRoICA9IG4uZ2V0RGF0YSgnd2lkdGgnKSxcbiAgICAgICAgICAgIGhlaWdodCA9IG4uZ2V0RGF0YSgnaGVpZ2h0Jyk7XG4gICAgICAgIC8vcmVzZXQgbGFiZWwgZGltZW5zaW9uc1xuICAgICAgICBzdHlsZS53aWR0aCAgPSBhdXRvV2lkdGg/ICdhdXRvJyA6IHdpZHRoICsgJ3B4JztcbiAgICAgICAgc3R5bGUuaGVpZ2h0ID0gYXV0b0hlaWdodD8gJ2F1dG8nIDogaGVpZ2h0ICsgJ3B4JztcbiAgICAgICAgXG4gICAgICAgIC8vVE9ETyhuaWNvKSBzaG91bGQgbGV0IHRoZSB1c2VyIGNob29zZSB3aGF0IHRvIGluc2VydCBoZXJlLlxuICAgICAgICBsYWJlbC5pbm5lckhUTUwgPSBuLm5hbWU7XG4gICAgICAgIFxuICAgICAgICB2YXIgb2Zmc2V0V2lkdGggID0gbGFiZWwub2Zmc2V0V2lkdGgsXG4gICAgICAgICAgICBvZmZzZXRIZWlnaHQgPSBsYWJlbC5vZmZzZXRIZWlnaHQ7XG4gICAgICAgIHZhciB0eXBlID0gbi5nZXREYXRhKCd0eXBlJyk7XG4gICAgICAgIGlmKCQuaW5kZXhPZihbJ2NpcmNsZScsICdzcXVhcmUnLCAndHJpYW5nbGUnLCAnc3RhciddLCB0eXBlKSA9PT0gLTEpIHtcbiAgICAgICAgICBuLnNldERhdGEoJ3dpZHRoJywgb2Zmc2V0V2lkdGgpO1xuICAgICAgICAgIG4uc2V0RGF0YSgnaGVpZ2h0Jywgb2Zmc2V0SGVpZ2h0KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB2YXIgZGltID0gb2Zmc2V0V2lkdGggPiBvZmZzZXRIZWlnaHQ/IG9mZnNldFdpZHRoIDogb2Zmc2V0SGVpZ2h0O1xuICAgICAgICAgIG4uc2V0RGF0YSgnd2lkdGgnLCBkaW0pO1xuICAgICAgICAgIG4uc2V0RGF0YSgnaGVpZ2h0JywgZGltKTtcbiAgICAgICAgICBuLnNldERhdGEoJ2RpbScsIGRpbSk7IFxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH0sXG4gIFxuICBpbml0aWFsaXplTGFiZWw6IGZ1bmN0aW9uKG9wdCkge1xuICAgIGlmKCF0aGlzLmxhYmVsKSB7XG4gICAgICB0aGlzLmxhYmVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHRoaXMubGFiZWwpO1xuICAgIH1cbiAgICB0aGlzLnNldExhYmVsU3R5bGVzKG9wdCk7XG4gIH0sXG4gIFxuICBzZXRMYWJlbFN0eWxlczogZnVuY3Rpb24ob3B0KSB7XG4gICAgJC5leHRlbmQodGhpcy5sYWJlbC5zdHlsZSwge1xuICAgICAgJ3Zpc2liaWxpdHknOiAnaGlkZGVuJyxcbiAgICAgICdwb3NpdGlvbic6ICdhYnNvbHV0ZScsXG4gICAgICAnd2lkdGgnOiAnYXV0bycsXG4gICAgICAnaGVpZ2h0JzogJ2F1dG8nXG4gICAgfSk7XG4gICAgdGhpcy5sYWJlbC5jbGFzc05hbWUgPSAnaml0LWF1dG9hZGp1c3QtbGFiZWwnO1xuICB9XG59O1xuXG5cbi8qXG4gKiBDbGFzczogTGF5b3V0cy5SYWRpYWxcbiAqIFxuICogSW1wbGVtZW50cyBhIFJhZGlhbCBMYXlvdXQuXG4gKiBcbiAqIEltcGxlbWVudGVkIEJ5OlxuICogXG4gKiA8UkdyYXBoPiwgPEh5cGVydHJlZT5cbiAqIFxuICovXG5MYXlvdXRzLlJhZGlhbCA9IG5ldyBDbGFzcyh7XG5cbiAgLypcbiAgICogTWV0aG9kOiBjb21wdXRlXG4gICAqIFxuICAgKiBDb21wdXRlcyBub2RlcycgcG9zaXRpb25zLlxuICAgKiBcbiAgICogUGFyYW1ldGVyczpcbiAgICogXG4gICAqIHByb3BlcnR5IC0gX29wdGlvbmFsXyBBIDxHcmFwaC5Ob2RlPiBwb3NpdGlvbiBwcm9wZXJ0eSB0byBzdG9yZSB0aGUgbmV3XG4gICAqIHBvc2l0aW9ucy4gUG9zc2libGUgdmFsdWVzIGFyZSAncG9zJywgJ2VuZCcgb3IgJ3N0YXJ0Jy5cbiAgICogXG4gICAqL1xuICBjb21wdXRlIDogZnVuY3Rpb24ocHJvcGVydHkpIHtcbiAgICB2YXIgcHJvcCA9ICQuc3BsYXQocHJvcGVydHkgfHwgWyAnY3VycmVudCcsICdzdGFydCcsICdlbmQnIF0pO1xuICAgIE5vZGVEaW0uY29tcHV0ZSh0aGlzLmdyYXBoLCBwcm9wLCB0aGlzLmNvbmZpZyk7XG4gICAgdGhpcy5ncmFwaC5jb21wdXRlTGV2ZWxzKHRoaXMucm9vdCwgMCwgXCJpZ25vcmVcIik7XG4gICAgdmFyIGxlbmd0aEZ1bmMgPSB0aGlzLmNyZWF0ZUxldmVsRGlzdGFuY2VGdW5jKCk7IFxuICAgIHRoaXMuY29tcHV0ZUFuZ3VsYXJXaWR0aHMocHJvcCk7XG4gICAgdGhpcy5jb21wdXRlUG9zaXRpb25zKHByb3AsIGxlbmd0aEZ1bmMpO1xuICB9LFxuXG4gIC8qXG4gICAqIGNvbXB1dGVQb3NpdGlvbnNcbiAgICogXG4gICAqIFBlcmZvcm1zIHRoZSBtYWluIGFsZ29yaXRobSBmb3IgY29tcHV0aW5nIG5vZGUgcG9zaXRpb25zLlxuICAgKi9cbiAgY29tcHV0ZVBvc2l0aW9ucyA6IGZ1bmN0aW9uKHByb3BlcnR5LCBnZXRMZW5ndGgpIHtcbiAgICB2YXIgcHJvcEFycmF5ID0gcHJvcGVydHk7XG4gICAgdmFyIGdyYXBoID0gdGhpcy5ncmFwaDtcbiAgICB2YXIgcm9vdCA9IGdyYXBoLmdldE5vZGUodGhpcy5yb290KTtcbiAgICB2YXIgcGFyZW50ID0gdGhpcy5wYXJlbnQ7XG4gICAgdmFyIGNvbmZpZyA9IHRoaXMuY29uZmlnO1xuXG4gICAgZm9yICggdmFyIGk9MCwgbD1wcm9wQXJyYXkubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICB2YXIgcGkgPSBwcm9wQXJyYXlbaV07XG4gICAgICByb290LnNldFBvcygkUCgwLCAwKSwgcGkpO1xuICAgICAgcm9vdC5zZXREYXRhKCdzcGFuJywgTWF0aC5QSSAqIDIsIHBpKTtcbiAgICB9XG5cbiAgICByb290LmFuZ2xlU3BhbiA9IHtcbiAgICAgIGJlZ2luIDogMCxcbiAgICAgIGVuZCA6IDIgKiBNYXRoLlBJXG4gICAgfTtcblxuICAgIGdyYXBoLmVhY2hCRlModGhpcy5yb290LCBmdW5jdGlvbihlbGVtKSB7XG4gICAgICB2YXIgYW5nbGVTcGFuID0gZWxlbS5hbmdsZVNwYW4uZW5kIC0gZWxlbS5hbmdsZVNwYW4uYmVnaW47XG4gICAgICB2YXIgYW5nbGVJbml0ID0gZWxlbS5hbmdsZVNwYW4uYmVnaW47XG4gICAgICB2YXIgbGVuID0gZ2V0TGVuZ3RoKGVsZW0pO1xuICAgICAgLy9DYWxjdWxhdGUgdGhlIHN1bSBvZiBhbGwgYW5ndWxhciB3aWR0aHNcbiAgICAgIHZhciB0b3RhbEFuZ3VsYXJXaWR0aHMgPSAwLCBzdWJub2RlcyA9IFtdLCBtYXhEaW0gPSB7fTtcbiAgICAgIGVsZW0uZWFjaFN1Ym5vZGUoZnVuY3Rpb24oc2liKSB7XG4gICAgICAgIHRvdGFsQW5ndWxhcldpZHRocyArPSBzaWIuX3RyZWVBbmd1bGFyV2lkdGg7XG4gICAgICAgIC8vZ2V0IG1heCBkaW1cbiAgICAgICAgZm9yICggdmFyIGk9MCwgbD1wcm9wQXJyYXkubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgdmFyIHBpID0gcHJvcEFycmF5W2ldLCBkaW0gPSBzaWIuZ2V0RGF0YSgnZGltJywgcGkpO1xuICAgICAgICAgIG1heERpbVtwaV0gPSAocGkgaW4gbWF4RGltKT8gKGRpbSA+IG1heERpbVtwaV0/IGRpbSA6IG1heERpbVtwaV0pIDogZGltO1xuICAgICAgICB9XG4gICAgICAgIHN1Ym5vZGVzLnB1c2goc2liKTtcbiAgICAgIH0sIFwiaWdub3JlXCIpO1xuICAgICAgLy9NYWludGFpbiBjaGlsZHJlbiBvcmRlclxuICAgICAgLy9TZWNvbmQgY29uc3RyYWludCBmb3IgPGh0dHA6Ly9iYWlsYW5kby5zaW1zLmJlcmtlbGV5LmVkdS9wYXBlcnMvaW5mb3ZpczAxLmh0bT5cbiAgICAgIGlmIChwYXJlbnQgJiYgcGFyZW50LmlkID09IGVsZW0uaWQgJiYgc3Vibm9kZXMubGVuZ3RoID4gMFxuICAgICAgICAgICYmIHN1Ym5vZGVzWzBdLmRpc3QpIHtcbiAgICAgICAgc3Vibm9kZXMuc29ydChmdW5jdGlvbihhLCBiKSB7XG4gICAgICAgICAgcmV0dXJuIChhLmRpc3QgPj0gYi5kaXN0KSAtIChhLmRpc3QgPD0gYi5kaXN0KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICAvL0NhbGN1bGF0ZSBub2RlcyBwb3NpdGlvbnMuXG4gICAgICBmb3IgKHZhciBrID0gMCwgbHM9c3Vibm9kZXMubGVuZ3RoOyBrIDwgbHM7IGsrKykge1xuICAgICAgICB2YXIgY2hpbGQgPSBzdWJub2Rlc1trXTtcbiAgICAgICAgaWYgKCFjaGlsZC5fZmxhZykge1xuICAgICAgICAgIHZhciBhbmdsZVByb3BvcnRpb24gPSBjaGlsZC5fdHJlZUFuZ3VsYXJXaWR0aCAvIHRvdGFsQW5ndWxhcldpZHRocyAqIGFuZ2xlU3BhbjtcbiAgICAgICAgICB2YXIgdGhldGEgPSBhbmdsZUluaXQgKyBhbmdsZVByb3BvcnRpb24gLyAyO1xuXG4gICAgICAgICAgZm9yICggdmFyIGk9MCwgbD1wcm9wQXJyYXkubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgcGkgPSBwcm9wQXJyYXlbaV07XG4gICAgICAgICAgICBjaGlsZC5zZXRQb3MoJFAodGhldGEsIGxlbiksIHBpKTtcbiAgICAgICAgICAgIGNoaWxkLnNldERhdGEoJ3NwYW4nLCBhbmdsZVByb3BvcnRpb24sIHBpKTtcbiAgICAgICAgICAgIGNoaWxkLnNldERhdGEoJ2RpbS1xdW90aWVudCcsIGNoaWxkLmdldERhdGEoJ2RpbScsIHBpKSAvIG1heERpbVtwaV0sIHBpKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjaGlsZC5hbmdsZVNwYW4gPSB7XG4gICAgICAgICAgICBiZWdpbiA6IGFuZ2xlSW5pdCxcbiAgICAgICAgICAgIGVuZCA6IGFuZ2xlSW5pdCArIGFuZ2xlUHJvcG9ydGlvblxuICAgICAgICAgIH07XG4gICAgICAgICAgYW5nbGVJbml0ICs9IGFuZ2xlUHJvcG9ydGlvbjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0sIFwiaWdub3JlXCIpO1xuICB9LFxuXG4gIC8qXG4gICAqIE1ldGhvZDogc2V0QW5ndWxhcldpZHRoRm9yTm9kZXNcbiAgICogXG4gICAqIFNldHMgbm9kZXMgYW5ndWxhciB3aWR0aHMuXG4gICAqL1xuICBzZXRBbmd1bGFyV2lkdGhGb3JOb2RlcyA6IGZ1bmN0aW9uKHByb3ApIHtcbiAgICB0aGlzLmdyYXBoLmVhY2hCRlModGhpcy5yb290LCBmdW5jdGlvbihlbGVtLCBpKSB7XG4gICAgICB2YXIgZGlhbVZhbHVlID0gZWxlbS5nZXREYXRhKCdhbmd1bGFyV2lkdGgnLCBwcm9wWzBdKSB8fCA1O1xuICAgICAgZWxlbS5fYW5ndWxhcldpZHRoID0gZGlhbVZhbHVlIC8gaTtcbiAgICB9LCBcImlnbm9yZVwiKTtcbiAgfSxcblxuICAvKlxuICAgKiBNZXRob2Q6IHNldFN1YnRyZWVzQW5ndWxhcldpZHRoXG4gICAqIFxuICAgKiBTZXRzIHN1YnRyZWVzIGFuZ3VsYXIgd2lkdGhzLlxuICAgKi9cbiAgc2V0U3VidHJlZXNBbmd1bGFyV2lkdGggOiBmdW5jdGlvbigpIHtcbiAgICB2YXIgdGhhdCA9IHRoaXM7XG4gICAgdGhpcy5ncmFwaC5lYWNoTm9kZShmdW5jdGlvbihlbGVtKSB7XG4gICAgICB0aGF0LnNldFN1YnRyZWVBbmd1bGFyV2lkdGgoZWxlbSk7XG4gICAgfSwgXCJpZ25vcmVcIik7XG4gIH0sXG5cbiAgLypcbiAgICogTWV0aG9kOiBzZXRTdWJ0cmVlQW5ndWxhcldpZHRoXG4gICAqIFxuICAgKiBTZXRzIHRoZSBhbmd1bGFyIHdpZHRoIGZvciBhIHN1YnRyZWUuXG4gICAqL1xuICBzZXRTdWJ0cmVlQW5ndWxhcldpZHRoIDogZnVuY3Rpb24oZWxlbSkge1xuICAgIHZhciB0aGF0ID0gdGhpcywgbm9kZUFXID0gZWxlbS5fYW5ndWxhcldpZHRoLCBzdW1BVyA9IDA7XG4gICAgZWxlbS5lYWNoU3Vibm9kZShmdW5jdGlvbihjaGlsZCkge1xuICAgICAgdGhhdC5zZXRTdWJ0cmVlQW5ndWxhcldpZHRoKGNoaWxkKTtcbiAgICAgIHN1bUFXICs9IGNoaWxkLl90cmVlQW5ndWxhcldpZHRoO1xuICAgIH0sIFwiaWdub3JlXCIpO1xuICAgIGVsZW0uX3RyZWVBbmd1bGFyV2lkdGggPSBNYXRoLm1heChub2RlQVcsIHN1bUFXKTtcbiAgfSxcblxuICAvKlxuICAgKiBNZXRob2Q6IGNvbXB1dGVBbmd1bGFyV2lkdGhzXG4gICAqIFxuICAgKiBDb21wdXRlcyBub2RlcyBhbmQgc3VidHJlZXMgYW5ndWxhciB3aWR0aHMuXG4gICAqL1xuICBjb21wdXRlQW5ndWxhcldpZHRocyA6IGZ1bmN0aW9uKHByb3ApIHtcbiAgICB0aGlzLnNldEFuZ3VsYXJXaWR0aEZvck5vZGVzKHByb3ApO1xuICAgIHRoaXMuc2V0U3VidHJlZXNBbmd1bGFyV2lkdGgoKTtcbiAgfVxuXG59KTtcblxuXG4vKlxuICogRmlsZTogUkdyYXBoLmpzXG4gKlxuICovXG5cbi8qXG4gICBDbGFzczogUkdyYXBoXG4gICBcbiAgIEEgcmFkaWFsIGdyYXBoIHZpc3VhbGl6YXRpb24gd2l0aCBhZHZhbmNlZCBhbmltYXRpb25zLlxuICAgXG4gICBJbnNwaXJlZCBieTpcbiBcbiAgIEFuaW1hdGVkIEV4cGxvcmF0aW9uIG9mIER5bmFtaWMgR3JhcGhzIHdpdGggUmFkaWFsIExheW91dCAoS2EtUGluZyBZZWUsIERhbnllbCBGaXNoZXIsIFJhY2huYSBEaGFtaWphLCBNYXJ0aSBIZWFyc3QpIDxodHRwOi8vYmFpbGFuZG8uc2ltcy5iZXJrZWxleS5lZHUvcGFwZXJzL2luZm92aXMwMS5odG0+XG4gICBcbiAgIE5vdGU6XG4gICBcbiAgIFRoaXMgdmlzdWFsaXphdGlvbiB3YXMgYnVpbHQgYW5kIGVuZ2luZWVyZWQgZnJvbSBzY3JhdGNoLCB0YWtpbmcgb25seSB0aGUgcGFwZXIgYXMgaW5zcGlyYXRpb24sIGFuZCBvbmx5IHNoYXJlcyBzb21lIGZlYXR1cmVzIHdpdGggdGhlIHZpc3VhbGl6YXRpb24gZGVzY3JpYmVkIGluIHRoZSBwYXBlci5cbiAgIFxuICBJbXBsZW1lbnRzOlxuICBcbiAgQWxsIDxMb2FkZXI+IG1ldGhvZHNcbiAgXG4gICBDb25zdHJ1Y3RvciBPcHRpb25zOlxuICAgXG4gICBJbmhlcml0cyBvcHRpb25zIGZyb21cbiAgIFxuICAgLSA8T3B0aW9ucy5DYW52YXM+XG4gICAtIDxPcHRpb25zLkNvbnRyb2xsZXI+XG4gICAtIDxPcHRpb25zLk5vZGU+XG4gICAtIDxPcHRpb25zLkVkZ2U+XG4gICAtIDxPcHRpb25zLkxhYmVsPlxuICAgLSA8T3B0aW9ucy5FdmVudHM+XG4gICAtIDxPcHRpb25zLlRpcHM+XG4gICAtIDxPcHRpb25zLk5vZGVTdHlsZXM+XG4gICAtIDxPcHRpb25zLk5hdmlnYXRpb24+XG4gICBcbiAgIEFkZGl0aW9uYWxseSwgdGhlcmUgYXJlIG90aGVyIHBhcmFtZXRlcnMgYW5kIHNvbWUgZGVmYXVsdCB2YWx1ZXMgY2hhbmdlZFxuICAgXG4gICBpbnRlcnBvbGF0aW9uIC0gKHN0cmluZykgRGVmYXVsdCdzICpsaW5lYXIqLiBEZXNjcmliZXMgdGhlIHdheSBub2RlcyBhcmUgaW50ZXJwb2xhdGVkLiBQb3NzaWJsZSB2YWx1ZXMgYXJlICdsaW5lYXInIGFuZCAncG9sYXInLlxuICAgbGV2ZWxEaXN0YW5jZSAtIChudW1iZXIpIERlZmF1bHQncyAqMTAwKi4gVGhlIGRpc3RhbmNlIGJldHdlZW4gbGV2ZWxzIG9mIHRoZSB0cmVlLiBcbiAgICAgXG4gICBJbnN0YW5jZSBQcm9wZXJ0aWVzOlxuXG4gICBjYW52YXMgLSBBY2Nlc3MgYSA8Q2FudmFzPiBpbnN0YW5jZS5cbiAgIGdyYXBoIC0gQWNjZXNzIGEgPEdyYXBoPiBpbnN0YW5jZS5cbiAgIG9wIC0gQWNjZXNzIGEgPFJHcmFwaC5PcD4gaW5zdGFuY2UuXG4gICBmeCAtIEFjY2VzcyBhIDxSR3JhcGguUGxvdD4gaW5zdGFuY2UuXG4gICBsYWJlbHMgLSBBY2Nlc3MgYSA8UkdyYXBoLkxhYmVsPiBpbnRlcmZhY2UgaW1wbGVtZW50YXRpb24uICAgXG4qL1xuXG4kaml0LlJHcmFwaCA9IG5ldyBDbGFzcygge1xuXG4gIEltcGxlbWVudHM6IFtcbiAgICAgIExvYWRlciwgRXh0cmFzLCBMYXlvdXRzLlJhZGlhbFxuICBdLFxuXG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uKGNvbnRyb2xsZXIpe1xuICAgIHZhciAkUkdyYXBoID0gJGppdC5SR3JhcGg7XG5cbiAgICB2YXIgY29uZmlnID0ge1xuICAgICAgaW50ZXJwb2xhdGlvbjogJ2xpbmVhcicsXG4gICAgICBsZXZlbERpc3RhbmNlOiAxMDBcbiAgICB9O1xuXG4gICAgdGhpcy5jb250cm9sbGVyID0gdGhpcy5jb25maWcgPSAkLm1lcmdlKE9wdGlvbnMoXCJDYW52YXNcIiwgXCJOb2RlXCIsIFwiRWRnZVwiLFxuICAgICAgICBcIkZ4XCIsIFwiQ29udHJvbGxlclwiLCBcIlRpcHNcIiwgXCJOb2RlU3R5bGVzXCIsIFwiRXZlbnRzXCIsIFwiTmF2aWdhdGlvblwiLCBcIkxhYmVsXCIpLCBjb25maWcsIGNvbnRyb2xsZXIpO1xuXG4gICAgdmFyIGNhbnZhc0NvbmZpZyA9IHRoaXMuY29uZmlnO1xuICAgIGlmKGNhbnZhc0NvbmZpZy51c2VDYW52YXMpIHtcbiAgICAgIHRoaXMuY2FudmFzID0gY2FudmFzQ29uZmlnLnVzZUNhbnZhcztcbiAgICAgIHRoaXMuY29uZmlnLmxhYmVsQ29udGFpbmVyID0gdGhpcy5jYW52YXMuaWQgKyAnLWxhYmVsJztcbiAgICB9IGVsc2Uge1xuICAgICAgaWYoY2FudmFzQ29uZmlnLmJhY2tncm91bmQpIHtcbiAgICAgICAgY2FudmFzQ29uZmlnLmJhY2tncm91bmQgPSAkLm1lcmdlKHtcbiAgICAgICAgICB0eXBlOiAnQ2lyY2xlcydcbiAgICAgICAgfSwgY2FudmFzQ29uZmlnLmJhY2tncm91bmQpO1xuICAgICAgfVxuICAgICAgdGhpcy5jYW52YXMgPSBuZXcgQ2FudmFzKHRoaXMsIGNhbnZhc0NvbmZpZyk7XG4gICAgICB0aGlzLmNvbmZpZy5sYWJlbENvbnRhaW5lciA9ICh0eXBlb2YgY2FudmFzQ29uZmlnLmluamVjdEludG8gPT0gJ3N0cmluZyc/IGNhbnZhc0NvbmZpZy5pbmplY3RJbnRvIDogY2FudmFzQ29uZmlnLmluamVjdEludG8uaWQpICsgJy1sYWJlbCc7XG4gICAgfVxuXG4gICAgdGhpcy5ncmFwaE9wdGlvbnMgPSB7XG4gICAgICAna2xhc3MnOiBQb2xhcixcbiAgICAgICdOb2RlJzoge1xuICAgICAgICAnc2VsZWN0ZWQnOiBmYWxzZSxcbiAgICAgICAgJ2V4aXN0JzogdHJ1ZSxcbiAgICAgICAgJ2RyYXduJzogdHJ1ZVxuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5ncmFwaCA9IG5ldyBHcmFwaCh0aGlzLmdyYXBoT3B0aW9ucywgdGhpcy5jb25maWcuTm9kZSxcbiAgICAgICAgdGhpcy5jb25maWcuRWRnZSk7XG4gICAgdGhpcy5sYWJlbHMgPSBuZXcgJFJHcmFwaC5MYWJlbFtjYW52YXNDb25maWcuTGFiZWwudHlwZV0odGhpcyk7XG4gICAgdGhpcy5meCA9IG5ldyAkUkdyYXBoLlBsb3QodGhpcywgJFJHcmFwaCk7XG4gICAgdGhpcy5vcCA9IG5ldyAkUkdyYXBoLk9wKHRoaXMpO1xuICAgIHRoaXMuanNvbiA9IG51bGw7XG4gICAgdGhpcy5yb290ID0gbnVsbDtcbiAgICB0aGlzLmJ1c3kgPSBmYWxzZTtcbiAgICB0aGlzLnBhcmVudCA9IGZhbHNlO1xuICAgIC8vIGluaXRpYWxpemUgZXh0cmFzXG4gICAgdGhpcy5pbml0aWFsaXplRXh0cmFzKCk7XG4gIH0sXG5cbiAgLyogXG4gIFxuICAgIGNyZWF0ZUxldmVsRGlzdGFuY2VGdW5jIFxuICBcbiAgICBSZXR1cm5zIHRoZSBsZXZlbERpc3RhbmNlIGZ1bmN0aW9uIHVzZWQgZm9yIGNhbGN1bGF0aW5nIGEgbm9kZSBkaXN0YW5jZSBcbiAgICB0byBpdHMgb3JpZ2luLiBUaGlzIGZ1bmN0aW9uIHJldHVybnMgYSBmdW5jdGlvbiB0aGF0IGlzIGNvbXB1dGVkIFxuICAgIHBlciBsZXZlbCBhbmQgbm90IHBlciBub2RlLCBzdWNoIHRoYXQgYWxsIG5vZGVzIHdpdGggdGhlIHNhbWUgZGVwdGggd2lsbCBoYXZlIHRoZSBcbiAgICBzYW1lIGRpc3RhbmNlIHRvIHRoZSBvcmlnaW4uIFRoZSByZXN1bHRpbmcgZnVuY3Rpb24gZ2V0cyB0aGUgXG4gICAgcGFyZW50IG5vZGUgYXMgcGFyYW1ldGVyIGFuZCByZXR1cm5zIGEgZmxvYXQuXG5cbiAgICovXG4gIGNyZWF0ZUxldmVsRGlzdGFuY2VGdW5jOiBmdW5jdGlvbigpe1xuICAgIHZhciBsZCA9IHRoaXMuY29uZmlnLmxldmVsRGlzdGFuY2U7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKGVsZW0pe1xuICAgICAgcmV0dXJuIChlbGVtLl9kZXB0aCArIDEpICogbGQ7XG4gICAgfTtcbiAgfSxcblxuICAvKiBcbiAgICAgTWV0aG9kOiByZWZyZXNoIFxuICAgICBcbiAgICAgQ29tcHV0ZXMgcG9zaXRpb25zIGFuZCBwbG90cyB0aGUgdHJlZS5cblxuICAgKi9cbiAgcmVmcmVzaDogZnVuY3Rpb24oKXtcbiAgICBcbiAgICAvLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG4gICAgLy8gdGhpcy5jb21wdXRlKCk7XG4gICAgLy8gRU5EIE1FVEFNQVBTIENPREVcbiAgICAvLyBPUklHSU5BTCBDT0RFOiB0aGlzLmNvbXB1dGUoKTtcbiAgICB0aGlzLnBsb3QoKTtcbiAgfSxcblxuICByZXBvc2l0aW9uOiBmdW5jdGlvbigpe1xuICAgIHRoaXMuY29tcHV0ZSgnZW5kJyk7XG4gIH0sXG5cbiAgLypcbiAgIE1ldGhvZDogcGxvdFxuICBcbiAgIFBsb3RzIHRoZSBSR3JhcGguIFRoaXMgaXMgYSBzaG9ydGN1dCB0byAqZngucGxvdCouXG4gICovXG4gIHBsb3Q6IGZ1bmN0aW9uKCl7XG4gICAgdGhpcy5meC5wbG90KCk7XG4gIH0sXG4gIC8qXG4gICBnZXROb2RlQW5kUGFyZW50QW5nbGVcbiAgXG4gICBSZXR1cm5zIHRoZSBfcGFyZW50XyBvZiB0aGUgZ2l2ZW4gbm9kZSwgYWxzbyBjYWxjdWxhdGluZyBpdHMgYW5nbGUgc3Bhbi5cbiAgKi9cbiAgZ2V0Tm9kZUFuZFBhcmVudEFuZ2xlOiBmdW5jdGlvbihpZCl7XG4gICAgdmFyIHRoZXRhID0gZmFsc2U7XG4gICAgdmFyIG4gPSB0aGlzLmdyYXBoLmdldE5vZGUoaWQpO1xuICAgIHZhciBwcyA9IG4uZ2V0UGFyZW50cygpO1xuICAgIHZhciBwID0gKHBzLmxlbmd0aCA+IDApPyBwc1swXSA6IGZhbHNlO1xuICAgIGlmIChwKSB7XG4gICAgICB2YXIgcG9zUGFyZW50ID0gcC5wb3MuZ2V0YygpLCBwb3NDaGlsZCA9IG4ucG9zLmdldGMoKTtcbiAgICAgIHZhciBuZXdQb3MgPSBwb3NQYXJlbnQuYWRkKHBvc0NoaWxkLnNjYWxlKC0xKSk7XG4gICAgICB0aGV0YSA9IE1hdGguYXRhbjIobmV3UG9zLnksIG5ld1Bvcy54KTtcbiAgICAgIGlmICh0aGV0YSA8IDApXG4gICAgICAgIHRoZXRhICs9IDIgKiBNYXRoLlBJO1xuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgcGFyZW50OiBwLFxuICAgICAgdGhldGE6IHRoZXRhXG4gICAgfTtcbiAgfSxcbiAgLypcbiAgIHRhZ0NoaWxkcmVuXG4gIFxuICAgRW51bWVyYXRlcyB0aGUgY2hpbGRyZW4gaW4gb3JkZXIgdG8gbWFpbnRhaW4gY2hpbGQgb3JkZXJpbmcgKHNlY29uZCBjb25zdHJhaW50IG9mIHRoZSBwYXBlcikuXG4gICovXG4gIHRhZ0NoaWxkcmVuOiBmdW5jdGlvbihwYXIsIGlkKXtcbiAgICBpZiAocGFyLmFuZ2xlU3Bhbikge1xuICAgICAgdmFyIGFkanMgPSBbXTtcbiAgICAgIHBhci5lYWNoQWRqYWNlbmN5KGZ1bmN0aW9uKGVsZW0pe1xuICAgICAgICBhZGpzLnB1c2goZWxlbS5ub2RlVG8pO1xuICAgICAgfSwgXCJpZ25vcmVcIik7XG4gICAgICB2YXIgbGVuID0gYWRqcy5sZW5ndGg7XG4gICAgICBmb3IgKCB2YXIgaSA9IDA7IGkgPCBsZW4gJiYgaWQgIT0gYWRqc1tpXS5pZDsgaSsrKVxuICAgICAgICA7XG4gICAgICBmb3IgKCB2YXIgaiA9IChpICsgMSkgJSBsZW4sIGsgPSAwOyBpZCAhPSBhZGpzW2pdLmlkOyBqID0gKGogKyAxKSAlIGxlbikge1xuICAgICAgICBhZGpzW2pdLmRpc3QgPSBrKys7XG4gICAgICB9XG4gICAgfVxuICB9LFxuICAvKiBcbiAgTWV0aG9kOiBvbkNsaWNrIFxuICBcbiAgQW5pbWF0ZXMgdGhlIDxSR3JhcGg+IHRvIGNlbnRlciB0aGUgbm9kZSBzcGVjaWZpZWQgYnkgKmlkKi5cblxuICAgUGFyYW1ldGVyczpcblxuICAgaWQgLSBBIDxHcmFwaC5Ob2RlPiBpZC5cbiAgIG9wdCAtIChvcHRpb25hbHxvYmplY3QpIEFuIG9iamVjdCBjb250YWluaW5nIHNvbWUgZXh0cmEgcHJvcGVydGllcyBkZXNjcmliZWQgYmVsb3dcbiAgIGhpZGVMYWJlbHMgLSAoYm9vbGVhbikgRGVmYXVsdCdzICp0cnVlKi4gSGlkZSBsYWJlbHMgd2hlbiBwZXJmb3JtaW5nIHRoZSBhbmltYXRpb24uXG5cbiAgIEV4YW1wbGU6XG5cbiAgIChzdGFydCBjb2RlIGpzKVxuICAgICByZ3JhcGgub25DbGljaygnc29tZWlkJyk7XG4gICAgIC8vb3IgYWxzby4uLlxuICAgICByZ3JhcGgub25DbGljaygnc29tZWlkJywge1xuICAgICAgaGlkZUxhYmVsczogZmFsc2VcbiAgICAgfSk7XG4gICAgKGVuZCBjb2RlKVxuICAgIFxuICAqL1xuICBvbkNsaWNrOiBmdW5jdGlvbihpZCwgb3B0KXtcbiAgICBpZiAodGhpcy5yb290ICE9IGlkICYmICF0aGlzLmJ1c3kpIHtcbiAgICAgIHRoaXMuYnVzeSA9IHRydWU7XG4gICAgICB0aGlzLnJvb3QgPSBpZDtcbiAgICAgIHZhciB0aGF0ID0gdGhpcztcbiAgICAgIHRoaXMuY29udHJvbGxlci5vbkJlZm9yZUNvbXB1dGUodGhpcy5ncmFwaC5nZXROb2RlKGlkKSk7XG4gICAgICB2YXIgb2JqID0gdGhpcy5nZXROb2RlQW5kUGFyZW50QW5nbGUoaWQpO1xuXG4gICAgICAvLyBzZWNvbmQgY29uc3RyYWludFxuICAgICAgdGhpcy50YWdDaGlsZHJlbihvYmoucGFyZW50LCBpZCk7XG4gICAgICB0aGlzLnBhcmVudCA9IG9iai5wYXJlbnQ7XG4gICAgICB0aGlzLmNvbXB1dGUoJ2VuZCcpO1xuXG4gICAgICAvLyBmaXJzdCBjb25zdHJhaW50XG4gICAgICB2YXIgdGhldGFEaWZmID0gb2JqLnRoZXRhIC0gb2JqLnBhcmVudC5lbmRQb3MudGhldGE7XG4gICAgICB0aGlzLmdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKGVsZW0pe1xuICAgICAgICBlbGVtLmVuZFBvcy5zZXQoZWxlbS5lbmRQb3MuZ2V0cCgpLmFkZCgkUCh0aGV0YURpZmYsIDApKSk7XG4gICAgICB9KTtcblxuICAgICAgdmFyIG1vZGUgPSB0aGlzLmNvbmZpZy5pbnRlcnBvbGF0aW9uO1xuICAgICAgb3B0ID0gJC5tZXJnZSgge1xuICAgICAgICBvbkNvbXBsZXRlOiAkLmVtcHR5XG4gICAgICB9LCBvcHQgfHwge30pO1xuXG4gICAgICB0aGlzLmZ4LmFuaW1hdGUoJC5tZXJnZSgge1xuICAgICAgICBoaWRlTGFiZWxzOiB0cnVlLFxuICAgICAgICBtb2RlczogW1xuICAgICAgICAgIG1vZGVcbiAgICAgICAgXVxuICAgICAgfSwgb3B0LCB7XG4gICAgICAgIG9uQ29tcGxldGU6IGZ1bmN0aW9uKCl7XG4gICAgICAgICAgdGhhdC5idXN5ID0gZmFsc2U7XG4gICAgICAgICAgb3B0Lm9uQ29tcGxldGUoKTtcbiAgICAgICAgfVxuICAgICAgfSkpO1xuICAgIH1cbiAgfVxufSk7XG5cbiRqaXQuUkdyYXBoLiRleHRlbmQgPSB0cnVlO1xuXG4oZnVuY3Rpb24oUkdyYXBoKXtcblxuICAvKlxuICAgICBDbGFzczogUkdyYXBoLk9wXG4gICAgIFxuICAgICBDdXN0b20gZXh0ZW5zaW9uIG9mIDxHcmFwaC5PcD4uXG5cbiAgICAgRXh0ZW5kczpcblxuICAgICBBbGwgPEdyYXBoLk9wPiBtZXRob2RzXG4gICAgIFxuICAgICBTZWUgYWxzbzpcbiAgICAgXG4gICAgIDxHcmFwaC5PcD5cblxuICAqL1xuICBSR3JhcGguT3AgPSBuZXcgQ2xhc3MoIHtcblxuICAgIEltcGxlbWVudHM6IEdyYXBoLk9wXG5cbiAgfSk7XG5cbiAgLypcbiAgICAgQ2xhc3M6IFJHcmFwaC5QbG90XG4gICAgXG4gICAgQ3VzdG9tIGV4dGVuc2lvbiBvZiA8R3JhcGguUGxvdD4uXG4gIFxuICAgIEV4dGVuZHM6XG4gIFxuICAgIEFsbCA8R3JhcGguUGxvdD4gbWV0aG9kc1xuICAgIFxuICAgIFNlZSBhbHNvOlxuICAgIFxuICAgIDxHcmFwaC5QbG90PlxuICBcbiAgKi9cbiAgUkdyYXBoLlBsb3QgPSBuZXcgQ2xhc3MoIHtcblxuICAgIEltcGxlbWVudHM6IEdyYXBoLlBsb3RcblxuICB9KTtcblxuICAvKlxuICAgIE9iamVjdDogUkdyYXBoLkxhYmVsXG5cbiAgICBDdXN0b20gZXh0ZW5zaW9uIG9mIDxHcmFwaC5MYWJlbD4uIFxuICAgIENvbnRhaW5zIGN1c3RvbSA8R3JhcGguTGFiZWwuU1ZHPiwgPEdyYXBoLkxhYmVsLkhUTUw+IGFuZCA8R3JhcGguTGFiZWwuTmF0aXZlPiBleHRlbnNpb25zLlxuICBcbiAgICBFeHRlbmRzOlxuICBcbiAgICBBbGwgPEdyYXBoLkxhYmVsPiBtZXRob2RzIGFuZCBzdWJjbGFzc2VzLlxuICBcbiAgICBTZWUgYWxzbzpcbiAgXG4gICAgPEdyYXBoLkxhYmVsPiwgPEdyYXBoLkxhYmVsLk5hdGl2ZT4sIDxHcmFwaC5MYWJlbC5IVE1MPiwgPEdyYXBoLkxhYmVsLlNWRz4uXG4gIFxuICAgKi9cbiAgUkdyYXBoLkxhYmVsID0ge307XG5cbiAgLypcbiAgICAgUkdyYXBoLkxhYmVsLk5hdGl2ZVxuXG4gICAgIEN1c3RvbSBleHRlbnNpb24gb2YgPEdyYXBoLkxhYmVsLk5hdGl2ZT4uXG5cbiAgICAgRXh0ZW5kczpcblxuICAgICBBbGwgPEdyYXBoLkxhYmVsLk5hdGl2ZT4gbWV0aG9kc1xuXG4gICAgIFNlZSBhbHNvOlxuXG4gICAgIDxHcmFwaC5MYWJlbC5OYXRpdmU+XG5cbiAgKi9cbiAgUkdyYXBoLkxhYmVsLk5hdGl2ZSA9IG5ldyBDbGFzcygge1xuICAgIEltcGxlbWVudHM6IEdyYXBoLkxhYmVsLk5hdGl2ZVxuICB9KTtcblxuICAvKlxuICAgICBSR3JhcGguTGFiZWwuU1ZHXG4gICAgXG4gICAgQ3VzdG9tIGV4dGVuc2lvbiBvZiA8R3JhcGguTGFiZWwuU1ZHPi5cbiAgXG4gICAgRXh0ZW5kczpcbiAgXG4gICAgQWxsIDxHcmFwaC5MYWJlbC5TVkc+IG1ldGhvZHNcbiAgXG4gICAgU2VlIGFsc286XG4gIFxuICAgIDxHcmFwaC5MYWJlbC5TVkc+XG4gIFxuICAqL1xuICBSR3JhcGguTGFiZWwuU1ZHID0gbmV3IENsYXNzKCB7XG4gICAgSW1wbGVtZW50czogR3JhcGguTGFiZWwuU1ZHLFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24odml6KXtcbiAgICAgIHRoaXMudml6ID0gdml6O1xuICAgIH0sXG5cbiAgICAvKiBcbiAgICAgICBwbGFjZUxhYmVsXG5cbiAgICAgICBPdmVycmlkZXMgYWJzdHJhY3QgbWV0aG9kIHBsYWNlTGFiZWwgaW4gPEdyYXBoLlBsb3Q+LlxuXG4gICAgICAgUGFyYW1ldGVyczpcblxuICAgICAgIHRhZyAtIEEgRE9NIGxhYmVsIGVsZW1lbnQuXG4gICAgICAgbm9kZSAtIEEgPEdyYXBoLk5vZGU+LlxuICAgICAgIGNvbnRyb2xsZXIgLSBBIGNvbmZpZ3VyYXRpb24vY29udHJvbGxlciBvYmplY3QgcGFzc2VkIHRvIHRoZSB2aXN1YWxpemF0aW9uLlxuICAgICAgXG4gICAgICovXG4gICAgcGxhY2VMYWJlbDogZnVuY3Rpb24odGFnLCBub2RlLCBjb250cm9sbGVyKXtcbiAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICBjYW52YXMgPSB0aGlzLnZpei5jYW52YXMsXG4gICAgICAgICAgb3ggPSBjYW52YXMudHJhbnNsYXRlT2Zmc2V0WCxcbiAgICAgICAgICBveSA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRZLFxuICAgICAgICAgIHN4ID0gY2FudmFzLnNjYWxlT2Zmc2V0WCxcbiAgICAgICAgICBzeSA9IGNhbnZhcy5zY2FsZU9mZnNldFksXG4gICAgICAgICAgcmFkaXVzID0gY2FudmFzLmdldFNpemUoKTtcbiAgICAgIHZhciBsYWJlbFBvcyA9IHtcbiAgICAgICAgeDogTWF0aC5yb3VuZChwb3MueCAqIHN4ICsgb3ggKyByYWRpdXMud2lkdGggLyAyKSxcbiAgICAgICAgeTogTWF0aC5yb3VuZChwb3MueSAqIHN5ICsgb3kgKyByYWRpdXMuaGVpZ2h0IC8gMilcbiAgICAgIH07XG4gICAgICB0YWcuc2V0QXR0cmlidXRlKCd4JywgbGFiZWxQb3MueCk7XG4gICAgICB0YWcuc2V0QXR0cmlidXRlKCd5JywgbGFiZWxQb3MueSk7XG5cbiAgICAgIGNvbnRyb2xsZXIub25QbGFjZUxhYmVsKHRhZywgbm9kZSk7XG4gICAgfVxuICB9KTtcblxuICAvKlxuICAgICBSR3JhcGguTGFiZWwuSFRNTFxuXG4gICAgIEN1c3RvbSBleHRlbnNpb24gb2YgPEdyYXBoLkxhYmVsLkhUTUw+LlxuXG4gICAgIEV4dGVuZHM6XG5cbiAgICAgQWxsIDxHcmFwaC5MYWJlbC5IVE1MPiBtZXRob2RzLlxuXG4gICAgIFNlZSBhbHNvOlxuXG4gICAgIDxHcmFwaC5MYWJlbC5IVE1MPlxuXG4gICovXG4gIFJHcmFwaC5MYWJlbC5IVE1MID0gbmV3IENsYXNzKCB7XG4gICAgSW1wbGVtZW50czogR3JhcGguTGFiZWwuSFRNTCxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKHZpeil7XG4gICAgICB0aGlzLnZpeiA9IHZpejtcbiAgICB9LFxuICAgIC8qIFxuICAgICAgIHBsYWNlTGFiZWxcblxuICAgICAgIE92ZXJyaWRlcyBhYnN0cmFjdCBtZXRob2QgcGxhY2VMYWJlbCBpbiA8R3JhcGguUGxvdD4uXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgdGFnIC0gQSBET00gbGFiZWwgZWxlbWVudC5cbiAgICAgICBub2RlIC0gQSA8R3JhcGguTm9kZT4uXG4gICAgICAgY29udHJvbGxlciAtIEEgY29uZmlndXJhdGlvbi9jb250cm9sbGVyIG9iamVjdCBwYXNzZWQgdG8gdGhlIHZpc3VhbGl6YXRpb24uXG4gICAgICBcbiAgICAgKi9cbiAgICBwbGFjZUxhYmVsOiBmdW5jdGlvbih0YWcsIG5vZGUsIGNvbnRyb2xsZXIpe1xuICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgIGNhbnZhcyA9IHRoaXMudml6LmNhbnZhcyxcbiAgICAgICAgICBveCA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRYLFxuICAgICAgICAgIG95ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFksXG4gICAgICAgICAgc3ggPSBjYW52YXMuc2NhbGVPZmZzZXRYLFxuICAgICAgICAgIHN5ID0gY2FudmFzLnNjYWxlT2Zmc2V0WSxcbiAgICAgICAgICByYWRpdXMgPSBjYW52YXMuZ2V0U2l6ZSgpO1xuICAgICAgdmFyIGxhYmVsUG9zID0ge1xuICAgICAgICB4OiBNYXRoLnJvdW5kKHBvcy54ICogc3ggKyBveCArIHJhZGl1cy53aWR0aCAvIDIpLFxuICAgICAgICB5OiBNYXRoLnJvdW5kKHBvcy55ICogc3kgKyBveSArIHJhZGl1cy5oZWlnaHQgLyAyKVxuICAgICAgfTtcblxuICAgICAgdmFyIHN0eWxlID0gdGFnLnN0eWxlO1xuICAgICAgc3R5bGUubGVmdCA9IGxhYmVsUG9zLnggKyAncHgnO1xuICAgICAgc3R5bGUudG9wID0gbGFiZWxQb3MueSArICdweCc7XG4gICAgICBzdHlsZS5kaXNwbGF5ID0gdGhpcy5maXRzSW5DYW52YXMobGFiZWxQb3MsIGNhbnZhcyk/ICcnIDogJ25vbmUnO1xuXG4gICAgICBjb250cm9sbGVyLm9uUGxhY2VMYWJlbCh0YWcsIG5vZGUpO1xuICAgIH1cbiAgfSk7XG5cbiAgLypcbiAgICBDbGFzczogUkdyYXBoLlBsb3QuTm9kZVR5cGVzXG5cbiAgICBUaGlzIGNsYXNzIGNvbnRhaW5zIGEgbGlzdCBvZiA8R3JhcGguTm9kZT4gYnVpbHQtaW4gdHlwZXMuIFxuICAgIE5vZGUgdHlwZXMgaW1wbGVtZW50ZWQgYXJlICdub25lJywgJ2NpcmNsZScsICd0cmlhbmdsZScsICdyZWN0YW5nbGUnLCAnc3RhcicsICdlbGxpcHNlJyBhbmQgJ3NxdWFyZScuXG5cbiAgICBZb3UgY2FuIGFkZCB5b3VyIGN1c3RvbSBub2RlIHR5cGVzLCBjdXN0b21pemluZyB5b3VyIHZpc3VhbGl6YXRpb24gdG8gdGhlIGV4dHJlbWUuXG5cbiAgICBFeGFtcGxlOlxuXG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICBSR3JhcGguUGxvdC5Ob2RlVHlwZXMuaW1wbGVtZW50KHtcbiAgICAgICAgJ215U3BlY2lhbFR5cGUnOiB7XG4gICAgICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcykge1xuICAgICAgICAgICAgLy9wcmludCB5b3VyIGN1c3RvbSBub2RlIHRvIGNhbnZhc1xuICAgICAgICAgIH0sXG4gICAgICAgICAgLy9vcHRpb25hbFxuICAgICAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5vZGUsIHBvcykge1xuICAgICAgICAgICAgLy9yZXR1cm4gdHJ1ZSBpZiBwb3MgaXMgaW5zaWRlIHRoZSBub2RlIG9yIGZhbHNlIG90aGVyd2lzZVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgKGVuZCBjb2RlKVxuXG4gICovXG4gIFJHcmFwaC5QbG90Lk5vZGVUeXBlcyA9IG5ldyBDbGFzcyh7XG4gICAgJ25vbmUnOiB7XG4gICAgICAncmVuZGVyJzogJC5lbXB0eSxcbiAgICAgICdjb250YWlucyc6ICQubGFtYmRhKGZhbHNlKVxuICAgIH0sXG4gICAgJ2NpcmNsZSc6IHtcbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpe1xuICAgICAgICB2YXIgcG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpO1xuICAgICAgICB0aGlzLm5vZGVIZWxwZXIuY2lyY2xlLnJlbmRlcignZmlsbCcsIHBvcywgZGltLCBjYW52YXMpO1xuICAgICAgfSxcbiAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5vZGUsIHBvcyl7XG4gICAgICAgIHZhciBucG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpO1xuICAgICAgICByZXR1cm4gdGhpcy5ub2RlSGVscGVyLmNpcmNsZS5jb250YWlucyhucG9zLCBwb3MsIGRpbSk7XG4gICAgICB9XG4gICAgfSxcbiAgICAnZWxsaXBzZSc6IHtcbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpe1xuICAgICAgICB2YXIgcG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICB3aWR0aCA9IG5vZGUuZ2V0RGF0YSgnd2lkdGgnKSwgXG4gICAgICAgICAgICBoZWlnaHQgPSBub2RlLmdldERhdGEoJ2hlaWdodCcpO1xuICAgICAgICB0aGlzLm5vZGVIZWxwZXIuZWxsaXBzZS5yZW5kZXIoJ2ZpbGwnLCBwb3MsIHdpZHRoLCBoZWlnaHQsIGNhbnZhcyk7XG4gICAgICAgIH0sXG4gICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3Mpe1xuICAgICAgICB2YXIgbnBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgd2lkdGggPSBub2RlLmdldERhdGEoJ3dpZHRoJyksIFxuICAgICAgICAgICAgaGVpZ2h0ID0gbm9kZS5nZXREYXRhKCdoZWlnaHQnKTtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9kZUhlbHBlci5lbGxpcHNlLmNvbnRhaW5zKG5wb3MsIHBvcywgd2lkdGgsIGhlaWdodCk7XG4gICAgICB9XG4gICAgfSxcbiAgICAnc3F1YXJlJzoge1xuICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcyl7XG4gICAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJyk7XG4gICAgICAgIHRoaXMubm9kZUhlbHBlci5zcXVhcmUucmVuZGVyKCdmaWxsJywgcG9zLCBkaW0sIGNhbnZhcyk7XG4gICAgICB9LFxuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obm9kZSwgcG9zKXtcbiAgICAgICAgdmFyIG5wb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJyk7XG4gICAgICAgIHJldHVybiB0aGlzLm5vZGVIZWxwZXIuc3F1YXJlLmNvbnRhaW5zKG5wb3MsIHBvcywgZGltKTtcbiAgICAgIH1cbiAgICB9LFxuICAgICdyZWN0YW5nbGUnOiB7XG4gICAgICAncmVuZGVyJzogZnVuY3Rpb24obm9kZSwgY2FudmFzKXtcbiAgICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgd2lkdGggPSBub2RlLmdldERhdGEoJ3dpZHRoJyksIFxuICAgICAgICAgICAgaGVpZ2h0ID0gbm9kZS5nZXREYXRhKCdoZWlnaHQnKTtcbiAgICAgICAgdGhpcy5ub2RlSGVscGVyLnJlY3RhbmdsZS5yZW5kZXIoJ2ZpbGwnLCBwb3MsIHdpZHRoLCBoZWlnaHQsIGNhbnZhcyk7XG4gICAgICB9LFxuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obm9kZSwgcG9zKXtcbiAgICAgICAgdmFyIG5wb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIHdpZHRoID0gbm9kZS5nZXREYXRhKCd3aWR0aCcpLCBcbiAgICAgICAgICAgIGhlaWdodCA9IG5vZGUuZ2V0RGF0YSgnaGVpZ2h0Jyk7XG4gICAgICAgIHJldHVybiB0aGlzLm5vZGVIZWxwZXIucmVjdGFuZ2xlLmNvbnRhaW5zKG5wb3MsIHBvcywgd2lkdGgsIGhlaWdodCk7XG4gICAgICB9XG4gICAgfSxcbiAgICAndHJpYW5nbGUnOiB7XG4gICAgICAncmVuZGVyJzogZnVuY3Rpb24obm9kZSwgY2FudmFzKXtcbiAgICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgZGltID0gbm9kZS5nZXREYXRhKCdkaW0nKTtcbiAgICAgICAgdGhpcy5ub2RlSGVscGVyLnRyaWFuZ2xlLnJlbmRlcignZmlsbCcsIHBvcywgZGltLCBjYW52YXMpO1xuICAgICAgfSxcbiAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5vZGUsIHBvcykge1xuICAgICAgICB2YXIgbnBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgZGltID0gbm9kZS5nZXREYXRhKCdkaW0nKTtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9kZUhlbHBlci50cmlhbmdsZS5jb250YWlucyhucG9zLCBwb3MsIGRpbSk7XG4gICAgICB9XG4gICAgfSxcbiAgICAnc3Rhcic6IHtcbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpe1xuICAgICAgICB2YXIgcG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSxcbiAgICAgICAgICAgIGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJyk7XG4gICAgICAgIHRoaXMubm9kZUhlbHBlci5zdGFyLnJlbmRlcignZmlsbCcsIHBvcywgZGltLCBjYW52YXMpO1xuICAgICAgfSxcbiAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5vZGUsIHBvcykge1xuICAgICAgICB2YXIgbnBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksXG4gICAgICAgICAgICBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpO1xuICAgICAgICByZXR1cm4gdGhpcy5ub2RlSGVscGVyLnN0YXIuY29udGFpbnMobnBvcywgcG9zLCBkaW0pO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgLypcbiAgICBDbGFzczogUkdyYXBoLlBsb3QuRWRnZVR5cGVzXG5cbiAgICBUaGlzIGNsYXNzIGNvbnRhaW5zIGEgbGlzdCBvZiA8R3JhcGguQWRqYWNlbmNlPiBidWlsdC1pbiB0eXBlcy4gXG4gICAgRWRnZSB0eXBlcyBpbXBsZW1lbnRlZCBhcmUgJ25vbmUnLCAnbGluZScgYW5kICdhcnJvdycuXG4gIFxuICAgIFlvdSBjYW4gYWRkIHlvdXIgY3VzdG9tIGVkZ2UgdHlwZXMsIGN1c3RvbWl6aW5nIHlvdXIgdmlzdWFsaXphdGlvbiB0byB0aGUgZXh0cmVtZS5cbiAgXG4gICAgRXhhbXBsZTpcbiAgXG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICBSR3JhcGguUGxvdC5FZGdlVHlwZXMuaW1wbGVtZW50KHtcbiAgICAgICAgJ215U3BlY2lhbFR5cGUnOiB7XG4gICAgICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKGFkaiwgY2FudmFzKSB7XG4gICAgICAgICAgICAvL3ByaW50IHlvdXIgY3VzdG9tIGVkZ2UgdG8gY2FudmFzXG4gICAgICAgICAgfSxcbiAgICAgICAgICAvL29wdGlvbmFsXG4gICAgICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24oYWRqLCBwb3MpIHtcbiAgICAgICAgICAgIC8vcmV0dXJuIHRydWUgaWYgcG9zIGlzIGluc2lkZSB0aGUgYXJjIG9yIGZhbHNlIG90aGVyd2lzZVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgKGVuZCBjb2RlKVxuICBcbiAgKi9cbiAgUkdyYXBoLlBsb3QuRWRnZVR5cGVzID0gbmV3IENsYXNzKHtcbiAgICAnbm9uZSc6ICQuZW1wdHksXG4gICAgJ2xpbmUnOiB7XG4gICAgICAncmVuZGVyJzogZnVuY3Rpb24oYWRqLCBjYW52YXMpIHtcbiAgICAgICAgdmFyIGZyb20gPSBhZGoubm9kZUZyb20ucG9zLmdldGModHJ1ZSksXG4gICAgICAgICAgICB0byA9IGFkai5ub2RlVG8ucG9zLmdldGModHJ1ZSk7XG4gICAgICAgIHRoaXMuZWRnZUhlbHBlci5saW5lLnJlbmRlcihmcm9tLCB0bywgY2FudmFzKTtcbiAgICAgIH0sXG4gICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihhZGosIHBvcykge1xuICAgICAgICB2YXIgZnJvbSA9IGFkai5ub2RlRnJvbS5wb3MuZ2V0Yyh0cnVlKSxcbiAgICAgICAgICAgIHRvID0gYWRqLm5vZGVUby5wb3MuZ2V0Yyh0cnVlKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuZWRnZUhlbHBlci5saW5lLmNvbnRhaW5zKGZyb20sIHRvLCBwb3MsIHRoaXMuZWRnZS5lcHNpbG9uKTtcbiAgICAgIH1cbiAgICB9LFxuICAgICdhcnJvdyc6IHtcbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihhZGosIGNhbnZhcykge1xuICAgICAgICB2YXIgZnJvbSA9IGFkai5ub2RlRnJvbS5wb3MuZ2V0Yyh0cnVlKSxcbiAgICAgICAgICAgIHRvID0gYWRqLm5vZGVUby5wb3MuZ2V0Yyh0cnVlKSxcbiAgICAgICAgICAgIGRpbSA9IGFkai5nZXREYXRhKCdkaW0nKSxcbiAgICAgICAgICAgIGRpcmVjdGlvbiA9IGFkai5kYXRhLiRkaXJlY3Rpb24sXG4gICAgICAgICAgICBpbnYgPSAoZGlyZWN0aW9uICYmIGRpcmVjdGlvbi5sZW5ndGg+MSAmJiBkaXJlY3Rpb25bMF0gIT0gYWRqLm5vZGVGcm9tLmlkKTtcbiAgICAgICAgdGhpcy5lZGdlSGVscGVyLmFycm93LnJlbmRlcihmcm9tLCB0bywgZGltLCBpbnYsIGNhbnZhcyk7XG4gICAgICB9LFxuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24oYWRqLCBwb3MpIHtcbiAgICAgICAgdmFyIGZyb20gPSBhZGoubm9kZUZyb20ucG9zLmdldGModHJ1ZSksXG4gICAgICAgICAgICB0byA9IGFkai5ub2RlVG8ucG9zLmdldGModHJ1ZSk7XG4gICAgICAgIHJldHVybiB0aGlzLmVkZ2VIZWxwZXIuYXJyb3cuY29udGFpbnMoZnJvbSwgdG8sIHBvcywgdGhpcy5lZGdlLmVwc2lsb24pO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbn0pKCRqaXQuUkdyYXBoKTtcblxuXG4vKlxuICogRmlsZTogTGF5b3V0cy5Gb3JjZURpcmVjdGVkLmpzXG4gKlxuKi9cblxuLypcbiAqIENsYXNzOiBMYXlvdXRzLkZvcmNlRGlyZWN0ZWRcbiAqIFxuICogSW1wbGVtZW50cyBhIEZvcmNlIERpcmVjdGVkIExheW91dC5cbiAqIFxuICogSW1wbGVtZW50ZWQgQnk6XG4gKiBcbiAqIDxGb3JjZURpcmVjdGVkPlxuICogXG4gKiBDcmVkaXRzOlxuICogXG4gKiBNYXJjdXMgQ29iZGVuIDxodHRwOi8vbWFyY3VzY29iZGVuLmNvLnVrPlxuICogXG4gKi9cbkxheW91dHMuRm9yY2VEaXJlY3RlZCA9IG5ldyBDbGFzcyh7XG5cbiAgZ2V0T3B0aW9uczogZnVuY3Rpb24ocmFuZG9tKSB7XG4gICAgdmFyIHMgPSB0aGlzLmNhbnZhcy5nZXRTaXplKCk7XG4gICAgdmFyIHcgPSBzLndpZHRoLCBoID0gcy5oZWlnaHQ7XG4gICAgLy9jb3VudCBub2Rlc1xuICAgIHZhciBjb3VudCA9IDA7XG4gICAgdGhpcy5ncmFwaC5lYWNoTm9kZShmdW5jdGlvbihuKSB7IFxuICAgICAgY291bnQrKztcbiAgICB9KTtcbiAgICB2YXIgazIgPSB3ICogaCAvIGNvdW50LCBrID0gTWF0aC5zcXJ0KGsyKTtcbiAgICB2YXIgbCA9IHRoaXMuY29uZmlnLmxldmVsRGlzdGFuY2U7XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIHdpZHRoOiB3LFxuICAgICAgaGVpZ2h0OiBoLFxuICAgICAgdHN0YXJ0OiB3ICogMC4xLFxuICAgICAgbm9kZWY6IGZ1bmN0aW9uKHgpIHsgcmV0dXJuIGsyIC8gKHggfHwgMSk7IH0sXG4gICAgICBlZGdlZjogZnVuY3Rpb24oeCkgeyByZXR1cm4gLyogeCAqIHggLyBrOyAqLyBrICogKHggLSBsKTsgfVxuICAgIH07XG4gIH0sXG4gIFxuICBjb21wdXRlOiBmdW5jdGlvbihwcm9wZXJ0eSwgaW5jcmVtZW50YWwpIHtcbiAgICB2YXIgcHJvcCA9ICQuc3BsYXQocHJvcGVydHkgfHwgWydjdXJyZW50JywgJ3N0YXJ0JywgJ2VuZCddKTtcbiAgICB2YXIgb3B0ID0gdGhpcy5nZXRPcHRpb25zKCk7XG4gICAgTm9kZURpbS5jb21wdXRlKHRoaXMuZ3JhcGgsIHByb3AsIHRoaXMuY29uZmlnKTtcbiAgICB0aGlzLmdyYXBoLmNvbXB1dGVMZXZlbHModGhpcy5yb290LCAwLCBcImlnbm9yZVwiKTtcbiAgICB0aGlzLmdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKG4pIHtcbiAgICAgICQuZWFjaChwcm9wLCBmdW5jdGlvbihwKSB7XG4gICAgICAgIHZhciBwb3MgPSBuLmdldFBvcyhwKTtcbiAgICAgICAgaWYocG9zLmVxdWFscyhDb21wbGV4LktFUikpIHtcbiAgICAgICAgICBwb3MueCA9IG9wdC53aWR0aC81ICogKE1hdGgucmFuZG9tKCkgLSAwLjUpO1xuICAgICAgICAgIHBvcy55ID0gb3B0LmhlaWdodC81ICogKE1hdGgucmFuZG9tKCkgLSAwLjUpO1xuICAgICAgICB9XG4gICAgICAgIC8vaW5pdGlhbGl6ZSBkaXNwIHZlY3RvclxuICAgICAgICBuLmRpc3AgPSB7fTtcbiAgICAgICAgJC5lYWNoKHByb3AsIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgICBuLmRpc3BbcF0gPSAkQygwLCAwKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgICB0aGlzLmNvbXB1dGVQb3NpdGlvbnMocHJvcCwgb3B0LCBpbmNyZW1lbnRhbCk7XG4gIH0sXG4gIFxuICBjb21wdXRlUG9zaXRpb25zOiBmdW5jdGlvbihwcm9wZXJ0eSwgb3B0LCBpbmNyZW1lbnRhbCkge1xuICAgIHZhciB0aW1lcyA9IHRoaXMuY29uZmlnLml0ZXJhdGlvbnMsIGkgPSAwLCB0aGF0ID0gdGhpcztcbiAgICBpZihpbmNyZW1lbnRhbCkge1xuICAgICAgKGZ1bmN0aW9uIGl0ZXIoKSB7XG4gICAgICAgIGZvcih2YXIgdG90YWw9aW5jcmVtZW50YWwuaXRlciwgaj0wOyBqPHRvdGFsOyBqKyspIHtcbiAgICAgICAgICBvcHQudCA9IG9wdC50c3RhcnQ7XG4gICAgICAgICAgaWYodGltZXMpIG9wdC50ICo9ICgxIC0gaSsrLyh0aW1lcyAtMSkpO1xuICAgICAgICAgIHRoYXQuY29tcHV0ZVBvc2l0aW9uU3RlcChwcm9wZXJ0eSwgb3B0KTtcbiAgICAgICAgICBpZih0aW1lcyAmJiBpID49IHRpbWVzKSB7XG4gICAgICAgICAgICBpbmNyZW1lbnRhbC5vbkNvbXBsZXRlKCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGluY3JlbWVudGFsLm9uU3RlcChNYXRoLnJvdW5kKGkgLyAodGltZXMgLTEpICogMTAwKSk7XG4gICAgICAgIHNldFRpbWVvdXQoaXRlciwgMSk7XG4gICAgICB9KSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBmb3IoOyBpIDwgdGltZXM7IGkrKykge1xuICAgICAgICBvcHQudCA9IG9wdC50c3RhcnQgKiAoMSAtIGkvKHRpbWVzIC0xKSk7XG4gICAgICAgIHRoaXMuY29tcHV0ZVBvc2l0aW9uU3RlcChwcm9wZXJ0eSwgb3B0KTtcbiAgICAgIH1cbiAgICB9XG4gIH0sXG4gIFxuICBjb21wdXRlUG9zaXRpb25TdGVwOiBmdW5jdGlvbihwcm9wZXJ0eSwgb3B0KSB7XG4gICAgdmFyIGdyYXBoID0gdGhpcy5ncmFwaDtcbiAgICB2YXIgbWluID0gTWF0aC5taW4sIG1heCA9IE1hdGgubWF4O1xuICAgIHZhciBkcG9zID0gJEMoMCwgMCk7XG4gICAgLy9jYWxjdWxhdGUgcmVwdWxzaXZlIGZvcmNlc1xuICAgIGdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKHYpIHtcbiAgICAgIC8vaW5pdGlhbGl6ZSBkaXNwXG4gICAgICAkLmVhY2gocHJvcGVydHksIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgdi5kaXNwW3BdLnggPSAwOyB2LmRpc3BbcF0ueSA9IDA7XG4gICAgICB9KTtcbiAgICAgIGdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKHUpIHtcbiAgICAgICAgaWYodS5pZCAhPSB2LmlkKSB7XG4gICAgICAgICAgJC5lYWNoKHByb3BlcnR5LCBmdW5jdGlvbihwKSB7XG4gICAgICAgICAgICB2YXIgdnAgPSB2LmdldFBvcyhwKSwgdXAgPSB1LmdldFBvcyhwKTtcbiAgICAgICAgICAgIGRwb3MueCA9IHZwLnggLSB1cC54O1xuICAgICAgICAgICAgZHBvcy55ID0gdnAueSAtIHVwLnk7XG4gICAgICAgICAgICB2YXIgbm9ybSA9IGRwb3Mubm9ybSgpIHx8IDE7XG4gICAgICAgICAgICB2LmRpc3BbcF0uJGFkZChkcG9zXG4gICAgICAgICAgICAgICAgLiRzY2FsZShvcHQubm9kZWYobm9ybSkgLyBub3JtKSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICAgIC8vY2FsY3VsYXRlIGF0dHJhY3RpdmUgZm9yY2VzXG4gICAgdmFyIFQgPSAhIWdyYXBoLmdldE5vZGUodGhpcy5yb290KS52aXNpdGVkO1xuICAgIGdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgIG5vZGUuZWFjaEFkamFjZW5jeShmdW5jdGlvbihhZGopIHtcbiAgICAgICAgdmFyIG5vZGVUbyA9IGFkai5ub2RlVG87XG4gICAgICAgIGlmKCEhbm9kZVRvLnZpc2l0ZWQgPT09IFQpIHtcbiAgICAgICAgICAkLmVhY2gocHJvcGVydHksIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgICAgIHZhciB2cCA9IG5vZGUuZ2V0UG9zKHApLCB1cCA9IG5vZGVUby5nZXRQb3MocCk7XG4gICAgICAgICAgICBkcG9zLnggPSB2cC54IC0gdXAueDtcbiAgICAgICAgICAgIGRwb3MueSA9IHZwLnkgLSB1cC55O1xuICAgICAgICAgICAgdmFyIG5vcm0gPSBkcG9zLm5vcm0oKSB8fCAxO1xuICAgICAgICAgICAgbm9kZS5kaXNwW3BdLiRhZGQoZHBvcy4kc2NhbGUoLW9wdC5lZGdlZihub3JtKSAvIG5vcm0pKTtcbiAgICAgICAgICAgIG5vZGVUby5kaXNwW3BdLiRhZGQoZHBvcy4kc2NhbGUoLTEpKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICBub2RlLnZpc2l0ZWQgPSAhVDtcbiAgICB9KTtcbiAgICAvL2FycmFuZ2UgcG9zaXRpb25zIHRvIGZpdCB0aGUgY2FudmFzXG4gICAgdmFyIHQgPSBvcHQudCwgdzIgPSBvcHQud2lkdGggLyAyLCBoMiA9IG9wdC5oZWlnaHQgLyAyO1xuICAgIGdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKHUpIHtcbiAgICAgICQuZWFjaChwcm9wZXJ0eSwgZnVuY3Rpb24ocCkge1xuICAgICAgICB2YXIgZGlzcCA9IHUuZGlzcFtwXTtcbiAgICAgICAgdmFyIG5vcm0gPSBkaXNwLm5vcm0oKSB8fCAxO1xuICAgICAgICB2YXIgcCA9IHUuZ2V0UG9zKHApO1xuICAgICAgICBwLiRhZGQoJEMoZGlzcC54ICogbWluKE1hdGguYWJzKGRpc3AueCksIHQpIC8gbm9ybSwgXG4gICAgICAgICAgICBkaXNwLnkgKiBtaW4oTWF0aC5hYnMoZGlzcC55KSwgdCkgLyBub3JtKSk7XG4gICAgICAgIHAueCA9IG1pbih3MiwgbWF4KC13MiwgcC54KSk7XG4gICAgICAgIHAueSA9IG1pbihoMiwgbWF4KC1oMiwgcC55KSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxufSk7XG5cbi8qXG4gKiBGaWxlOiBGb3JjZURpcmVjdGVkLmpzXG4gKi9cblxuLypcbiAgIENsYXNzOiBGb3JjZURpcmVjdGVkXG4gICAgICBcbiAgIEEgdmlzdWFsaXphdGlvbiB0aGF0IGxheXMgZ3JhcGhzIHVzaW5nIGEgRm9yY2UtRGlyZWN0ZWQgbGF5b3V0IGFsZ29yaXRobS5cbiAgIFxuICAgSW5zcGlyZWQgYnk6XG4gIFxuICAgRm9yY2UtRGlyZWN0ZWQgRHJhd2luZyBBbGdvcml0aG1zIChTdGVwaGVuIEcuIEtvYm91cm92KSA8aHR0cDovL3d3dy5jcy5icm93bi5lZHUvfnJ0L2dkaGFuZGJvb2svY2hhcHRlcnMvZm9yY2UtZGlyZWN0ZWQucGRmPlxuICAgXG4gIEltcGxlbWVudHM6XG4gIFxuICBBbGwgPExvYWRlcj4gbWV0aG9kc1xuICBcbiAgIENvbnN0cnVjdG9yIE9wdGlvbnM6XG4gICBcbiAgIEluaGVyaXRzIG9wdGlvbnMgZnJvbVxuICAgXG4gICAtIDxPcHRpb25zLkNhbnZhcz5cbiAgIC0gPE9wdGlvbnMuQ29udHJvbGxlcj5cbiAgIC0gPE9wdGlvbnMuTm9kZT5cbiAgIC0gPE9wdGlvbnMuRWRnZT5cbiAgIC0gPE9wdGlvbnMuTGFiZWw+XG4gICAtIDxPcHRpb25zLkV2ZW50cz5cbiAgIC0gPE9wdGlvbnMuVGlwcz5cbiAgIC0gPE9wdGlvbnMuTm9kZVN0eWxlcz5cbiAgIC0gPE9wdGlvbnMuTmF2aWdhdGlvbj5cbiAgIFxuICAgQWRkaXRpb25hbGx5LCB0aGVyZSBhcmUgdHdvIHBhcmFtZXRlcnNcbiAgIFxuICAgbGV2ZWxEaXN0YW5jZSAtIChudW1iZXIpIERlZmF1bHQncyAqNTAqLiBUaGUgbmF0dXJhbCBsZW5ndGggZGVzaXJlZCBmb3IgdGhlIGVkZ2VzLlxuICAgaXRlcmF0aW9ucyAtIChudW1iZXIpIERlZmF1bHQncyAqNTAqLiBUaGUgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgZm9yIHRoZSBzcHJpbmcgbGF5b3V0IHNpbXVsYXRpb24uIERlcGVuZGluZyBvbiB0aGUgYnJvd3NlcidzIHNwZWVkIHlvdSBjb3VsZCBzZXQgdGhpcyB0byBhIG1vcmUgJ2ludGVyZXN0aW5nJyBudW1iZXIsIGxpa2UgKjIwMCouIFxuICAgICBcbiAgIEluc3RhbmNlIFByb3BlcnRpZXM6XG5cbiAgIGNhbnZhcyAtIEFjY2VzcyBhIDxDYW52YXM+IGluc3RhbmNlLlxuICAgZ3JhcGggLSBBY2Nlc3MgYSA8R3JhcGg+IGluc3RhbmNlLlxuICAgb3AgLSBBY2Nlc3MgYSA8Rm9yY2VEaXJlY3RlZC5PcD4gaW5zdGFuY2UuXG4gICBmeCAtIEFjY2VzcyBhIDxGb3JjZURpcmVjdGVkLlBsb3Q+IGluc3RhbmNlLlxuICAgbGFiZWxzIC0gQWNjZXNzIGEgPEZvcmNlRGlyZWN0ZWQuTGFiZWw+IGludGVyZmFjZSBpbXBsZW1lbnRhdGlvbi5cblxuKi9cblxuJGppdC5Gb3JjZURpcmVjdGVkID0gbmV3IENsYXNzKCB7XG5cbiAgSW1wbGVtZW50czogWyBMb2FkZXIsIEV4dHJhcywgTGF5b3V0cy5Gb3JjZURpcmVjdGVkIF0sXG5cbiAgaW5pdGlhbGl6ZTogZnVuY3Rpb24oY29udHJvbGxlcikge1xuICAgIHZhciAkRm9yY2VEaXJlY3RlZCA9ICRqaXQuRm9yY2VEaXJlY3RlZDtcblxuICAgIHZhciBjb25maWcgPSB7XG4gICAgICBpdGVyYXRpb25zOiA1MCxcbiAgICAgIGxldmVsRGlzdGFuY2U6IDUwXG4gICAgfTtcblxuICAgIHRoaXMuY29udHJvbGxlciA9IHRoaXMuY29uZmlnID0gJC5tZXJnZShPcHRpb25zKFwiQ2FudmFzXCIsIFwiTm9kZVwiLCBcIkVkZ2VcIixcbiAgICAgICAgXCJGeFwiLCBcIlRpcHNcIiwgXCJOb2RlU3R5bGVzXCIsIFwiRXZlbnRzXCIsIFwiTmF2aWdhdGlvblwiLCBcIkNvbnRyb2xsZXJcIiwgXCJMYWJlbFwiKSwgY29uZmlnLCBjb250cm9sbGVyKTtcblxuICAgIHZhciBjYW52YXNDb25maWcgPSB0aGlzLmNvbmZpZztcbiAgICBpZihjYW52YXNDb25maWcudXNlQ2FudmFzKSB7XG4gICAgICB0aGlzLmNhbnZhcyA9IGNhbnZhc0NvbmZpZy51c2VDYW52YXM7XG4gICAgICB0aGlzLmNvbmZpZy5sYWJlbENvbnRhaW5lciA9IHRoaXMuY2FudmFzLmlkICsgJy1sYWJlbCc7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmKGNhbnZhc0NvbmZpZy5iYWNrZ3JvdW5kKSB7XG4gICAgICAgIGNhbnZhc0NvbmZpZy5iYWNrZ3JvdW5kID0gJC5tZXJnZSh7XG4gICAgICAgICAgdHlwZTogJ0NpcmNsZXMnXG4gICAgICAgIH0sIGNhbnZhc0NvbmZpZy5iYWNrZ3JvdW5kKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuY2FudmFzID0gbmV3IENhbnZhcyh0aGlzLCBjYW52YXNDb25maWcpO1xuICAgICAgdGhpcy5jb25maWcubGFiZWxDb250YWluZXIgPSAodHlwZW9mIGNhbnZhc0NvbmZpZy5pbmplY3RJbnRvID09ICdzdHJpbmcnPyBjYW52YXNDb25maWcuaW5qZWN0SW50byA6IGNhbnZhc0NvbmZpZy5pbmplY3RJbnRvLmlkKSArICctbGFiZWwnO1xuICAgIH1cblxuICAgIHRoaXMuZ3JhcGhPcHRpb25zID0ge1xuICAgICAgJ2tsYXNzJzogQ29tcGxleCxcbiAgICAgICdOb2RlJzoge1xuICAgICAgICAnc2VsZWN0ZWQnOiBmYWxzZSxcbiAgICAgICAgJ2V4aXN0JzogdHJ1ZSxcbiAgICAgICAgJ2RyYXduJzogdHJ1ZVxuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5ncmFwaCA9IG5ldyBHcmFwaCh0aGlzLmdyYXBoT3B0aW9ucywgdGhpcy5jb25maWcuTm9kZSxcbiAgICAgICAgdGhpcy5jb25maWcuRWRnZSk7XG4gICAgdGhpcy5sYWJlbHMgPSBuZXcgJEZvcmNlRGlyZWN0ZWQuTGFiZWxbY2FudmFzQ29uZmlnLkxhYmVsLnR5cGVdKHRoaXMpO1xuICAgIHRoaXMuZnggPSBuZXcgJEZvcmNlRGlyZWN0ZWQuUGxvdCh0aGlzLCAkRm9yY2VEaXJlY3RlZCk7XG4gICAgdGhpcy5vcCA9IG5ldyAkRm9yY2VEaXJlY3RlZC5PcCh0aGlzKTtcbiAgICB0aGlzLmpzb24gPSBudWxsO1xuICAgIHRoaXMuYnVzeSA9IGZhbHNlO1xuICAgIC8vIGluaXRpYWxpemUgZXh0cmFzXG4gICAgdGhpcy5pbml0aWFsaXplRXh0cmFzKCk7XG4gIH0sXG5cbiAgLyogXG4gICAgTWV0aG9kOiByZWZyZXNoIFxuICAgIFxuICAgIENvbXB1dGVzIHBvc2l0aW9ucyBhbmQgcGxvdHMgdGhlIHRyZWUuXG4gICovXG4gIHJlZnJlc2g6IGZ1bmN0aW9uKCkge1xuICAgIC8vIFNUQVJUIE1FVEFNQVBTIENPREVcbiAgICAvLyB0aGlzLmNvbXB1dGUoKTtcbiAgICAvLyBFTkQgTUVUQU1BUFMgQ09ERVxuICAgIC8vIE9SSUdJTkFMIENPREU6IHRoaXMuY29tcHV0ZSgpO1xuICAgIHRoaXMucGxvdCgpO1xuICB9LFxuXG4gIHJlcG9zaXRpb246IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMuY29tcHV0ZSgnZW5kJyk7XG4gIH0sXG5cbi8qXG4gIE1ldGhvZDogY29tcHV0ZUluY3JlbWVudGFsXG4gIFxuICBQZXJmb3JtcyB0aGUgRm9yY2UgRGlyZWN0ZWQgYWxnb3JpdGhtIGluY3JlbWVudGFsbHkuXG4gIFxuICBEZXNjcmlwdGlvbjpcbiAgXG4gIEZvcmNlRGlyZWN0ZWQgYWxnb3JpdGhtcyBjYW4gcGVyZm9ybSBtYW55IGNvbXB1dGF0aW9ucyBhbmQgbGVhZCB0byBKYXZhU2NyaXB0IHRha2luZyB0b28gbXVjaCB0aW1lIHRvIGNvbXBsZXRlLiBcbiAgVGhpcyBtZXRob2Qgc3BsaXRzIHRoZSBhbGdvcml0aG0gaW50byBzbWFsbGVyIHBhcnRzIGFsbG93aW5nIHRoZSB1c2VyIHRvIHRyYWNrIHRoZSBldm9sdXRpb24gb2YgdGhlIGFsZ29yaXRobSBhbmQgXG4gIGF2b2lkaW5nIGJyb3dzZXIgbWVzc2FnZXMgc3VjaCBhcyBcIlRoaXMgc2NyaXB0IGlzIHRha2luZyB0b28gbG9uZyB0byBjb21wbGV0ZVwiLlxuICBcbiAgUGFyYW1ldGVyczpcbiAgXG4gIG9wdCAtIChvYmplY3QpIFRoZSBvYmplY3QgcHJvcGVydGllcyBhcmUgZGVzY3JpYmVkIGJlbG93XG4gIFxuICBpdGVyIC0gKG51bWJlcikgRGVmYXVsdCdzICoyMCouIFNwbGl0IHRoZSBhbGdvcml0aG0gaW50byBwaWVjZXMgb2YgX2l0ZXJfIGl0ZXJhdGlvbnMuIEZvciBleGFtcGxlLCBpZiB0aGUgX2l0ZXJhdGlvbnNfIGNvbmZpZ3VyYXRpb24gcHJvcGVydHkgXG4gIG9mIHlvdXIgPEZvcmNlRGlyZWN0ZWQ+IGNsYXNzIGlzIDEwMCwgdGhlbiB5b3UgY291bGQgc2V0IF9pdGVyXyB0byAyMCB0byBzcGxpdCB0aGUgbWFpbiBhbGdvcml0aG0gaW50byA1IHNtYWxsZXIgcGllY2VzLlxuICBcbiAgcHJvcGVydHkgLSAoc3RyaW5nKSBEZWZhdWx0J3MgKmVuZCouIFdoZXRoZXIgdG8gdXBkYXRlIHN0YXJ0aW5nLCBjdXJyZW50IG9yIGVuZGluZyBub2RlIHBvc2l0aW9ucy4gUG9zc2libGUgdmFsdWVzIGFyZSAnZW5kJywgJ3N0YXJ0JywgJ2N1cnJlbnQnLiBcbiAgWW91IGNhbiBhbHNvIHNldCBhbiBhcnJheSBvZiB0aGVzZSBwcm9wZXJ0aWVzLiBJZiB5b3UnZCBsaWtlIHRvIGtlZXAgdGhlIGN1cnJlbnQgbm9kZSBwb3NpdGlvbnMgYnV0IHRvIHBlcmZvcm0gdGhlc2UgXG4gIGNvbXB1dGF0aW9ucyBmb3IgZmluYWwgYW5pbWF0aW9uIHBvc2l0aW9ucyB0aGVuIHlvdSBjYW4ganVzdCBjaG9vc2UgJ2VuZCcuXG4gIFxuICBvblN0ZXAgLSAoZnVuY3Rpb24pIEEgY2FsbGJhY2sgZnVuY3Rpb24gY2FsbGVkIHdoZW4gZWFjaCBcInNtYWxsIHBhcnRcIiBvZiB0aGUgYWxnb3JpdGhtIGNvbXBsZXRlZC4gVGhpcyBmdW5jdGlvbiBnZXRzIGFzIGZpcnN0IGZvcm1hbCBcbiAgcGFyYW1ldGVyIGEgcGVyY2VudGFnZSB2YWx1ZS5cbiAgXG4gIG9uQ29tcGxldGUgLSBBIGNhbGxiYWNrIGZ1bmN0aW9uIGNhbGxlZCB3aGVuIHRoZSBhbGdvcml0aG0gY29tcGxldGVkLlxuICBcbiAgRXhhbXBsZTpcbiAgXG4gIEluIHRoaXMgZXhhbXBsZSBJIGNhbGN1bGF0ZSB0aGUgZW5kIHBvc2l0aW9ucyBhbmQgdGhlbiBhbmltYXRlIHRoZSBncmFwaCB0byB0aG9zZSBwb3NpdGlvbnNcbiAgXG4gIChzdGFydCBjb2RlIGpzKVxuICB2YXIgZmQgPSBuZXcgJGppdC5Gb3JjZURpcmVjdGVkKC4uLik7XG4gIGZkLmNvbXB1dGVJbmNyZW1lbnRhbCh7XG4gICAgaXRlcjogMjAsXG4gICAgcHJvcGVydHk6ICdlbmQnLFxuICAgIG9uU3RlcDogZnVuY3Rpb24ocGVyYykge1xuICAgICAgTG9nLndyaXRlKFwibG9hZGluZyBcIiArIHBlcmMgKyBcIiVcIik7XG4gICAgfSxcbiAgICBvbkNvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgIExvZy53cml0ZShcImRvbmVcIik7XG4gICAgICBmZC5hbmltYXRlKCk7XG4gICAgfVxuICB9KTtcbiAgKGVuZCBjb2RlKVxuICBcbiAgSW4gdGhpcyBleGFtcGxlIEkgY2FsY3VsYXRlIGFsbCBwb3NpdGlvbnMgYW5kIChyZSlwbG90IHRoZSBncmFwaFxuICBcbiAgKHN0YXJ0IGNvZGUganMpXG4gIHZhciBmZCA9IG5ldyBGb3JjZURpcmVjdGVkKC4uLik7XG4gIGZkLmNvbXB1dGVJbmNyZW1lbnRhbCh7XG4gICAgaXRlcjogMjAsXG4gICAgcHJvcGVydHk6IFsnZW5kJywgJ3N0YXJ0JywgJ2N1cnJlbnQnXSxcbiAgICBvblN0ZXA6IGZ1bmN0aW9uKHBlcmMpIHtcbiAgICAgIExvZy53cml0ZShcImxvYWRpbmcgXCIgKyBwZXJjICsgXCIlXCIpO1xuICAgIH0sXG4gICAgb25Db21wbGV0ZTogZnVuY3Rpb24oKSB7XG4gICAgICBMb2cud3JpdGUoXCJkb25lXCIpO1xuICAgICAgZmQucGxvdCgpO1xuICAgIH1cbiAgfSk7XG4gIChlbmQgY29kZSlcbiAgXG4gICovXG4gIGNvbXB1dGVJbmNyZW1lbnRhbDogZnVuY3Rpb24ob3B0KSB7XG4gICAgb3B0ID0gJC5tZXJnZSgge1xuICAgICAgaXRlcjogMjAsXG4gICAgICBwcm9wZXJ0eTogJ2VuZCcsXG4gICAgICBvblN0ZXA6ICQuZW1wdHksXG4gICAgICBvbkNvbXBsZXRlOiAkLmVtcHR5XG4gICAgfSwgb3B0IHx8IHt9KTtcblxuICAgIHRoaXMuY29uZmlnLm9uQmVmb3JlQ29tcHV0ZSh0aGlzLmdyYXBoLmdldE5vZGUodGhpcy5yb290KSk7XG4gICAgdGhpcy5jb21wdXRlKG9wdC5wcm9wZXJ0eSwgb3B0KTtcbiAgfSxcblxuICAvKlxuICAgIE1ldGhvZDogcGxvdFxuICAgXG4gICAgUGxvdHMgdGhlIEZvcmNlRGlyZWN0ZWQgZ3JhcGguIFRoaXMgaXMgYSBzaG9ydGN1dCB0byAqZngucGxvdCouXG4gICAqL1xuICBwbG90OiBmdW5jdGlvbigpIHtcbiAgICB0aGlzLmZ4LnBsb3QoKTtcbiAgfSxcblxuICAvKlxuICAgICBNZXRob2Q6IGFuaW1hdGVcbiAgICBcbiAgICAgQW5pbWF0ZXMgdGhlIGdyYXBoIGZyb20gdGhlIGN1cnJlbnQgcG9zaXRpb25zIHRvIHRoZSAnZW5kJyBub2RlIHBvc2l0aW9ucy5cbiAgKi9cbiAgYW5pbWF0ZTogZnVuY3Rpb24ob3B0KSB7XG4gICAgdGhpcy5meC5hbmltYXRlKCQubWVyZ2UoIHtcbiAgICAgIG1vZGVzOiBbICdsaW5lYXInIF1cbiAgICB9LCBvcHQgfHwge30pKTtcbiAgfVxufSk7XG5cbiRqaXQuRm9yY2VEaXJlY3RlZC4kZXh0ZW5kID0gdHJ1ZTtcblxuKGZ1bmN0aW9uKEZvcmNlRGlyZWN0ZWQpIHtcblxuICAvKlxuICAgICBDbGFzczogRm9yY2VEaXJlY3RlZC5PcFxuICAgICBcbiAgICAgQ3VzdG9tIGV4dGVuc2lvbiBvZiA8R3JhcGguT3A+LlxuXG4gICAgIEV4dGVuZHM6XG5cbiAgICAgQWxsIDxHcmFwaC5PcD4gbWV0aG9kc1xuICAgICBcbiAgICAgU2VlIGFsc286XG4gICAgIFxuICAgICA8R3JhcGguT3A+XG5cbiAgKi9cbiAgRm9yY2VEaXJlY3RlZC5PcCA9IG5ldyBDbGFzcygge1xuXG4gICAgSW1wbGVtZW50czogR3JhcGguT3BcblxuICB9KTtcblxuICAvKlxuICAgIENsYXNzOiBGb3JjZURpcmVjdGVkLlBsb3RcbiAgICBcbiAgICBDdXN0b20gZXh0ZW5zaW9uIG9mIDxHcmFwaC5QbG90Pi5cbiAgXG4gICAgRXh0ZW5kczpcbiAgXG4gICAgQWxsIDxHcmFwaC5QbG90PiBtZXRob2RzXG4gICAgXG4gICAgU2VlIGFsc286XG4gICAgXG4gICAgPEdyYXBoLlBsb3Q+XG4gIFxuICAqL1xuICBGb3JjZURpcmVjdGVkLlBsb3QgPSBuZXcgQ2xhc3MoIHtcblxuICAgIEltcGxlbWVudHM6IEdyYXBoLlBsb3RcblxuICB9KTtcblxuICAvKlxuICAgIENsYXNzOiBGb3JjZURpcmVjdGVkLkxhYmVsXG4gICAgXG4gICAgQ3VzdG9tIGV4dGVuc2lvbiBvZiA8R3JhcGguTGFiZWw+LiBcbiAgICBDb250YWlucyBjdXN0b20gPEdyYXBoLkxhYmVsLlNWRz4sIDxHcmFwaC5MYWJlbC5IVE1MPiBhbmQgPEdyYXBoLkxhYmVsLk5hdGl2ZT4gZXh0ZW5zaW9ucy5cbiAgXG4gICAgRXh0ZW5kczpcbiAgXG4gICAgQWxsIDxHcmFwaC5MYWJlbD4gbWV0aG9kcyBhbmQgc3ViY2xhc3Nlcy5cbiAgXG4gICAgU2VlIGFsc286XG4gIFxuICAgIDxHcmFwaC5MYWJlbD4sIDxHcmFwaC5MYWJlbC5OYXRpdmU+LCA8R3JhcGguTGFiZWwuSFRNTD4sIDxHcmFwaC5MYWJlbC5TVkc+LlxuICBcbiAgKi9cbiAgRm9yY2VEaXJlY3RlZC5MYWJlbCA9IHt9O1xuXG4gIC8qXG4gICAgIEZvcmNlRGlyZWN0ZWQuTGFiZWwuTmF0aXZlXG4gICAgIFxuICAgICBDdXN0b20gZXh0ZW5zaW9uIG9mIDxHcmFwaC5MYWJlbC5OYXRpdmU+LlxuXG4gICAgIEV4dGVuZHM6XG5cbiAgICAgQWxsIDxHcmFwaC5MYWJlbC5OYXRpdmU+IG1ldGhvZHNcblxuICAgICBTZWUgYWxzbzpcblxuICAgICA8R3JhcGguTGFiZWwuTmF0aXZlPlxuXG4gICovXG4gIEZvcmNlRGlyZWN0ZWQuTGFiZWwuTmF0aXZlID0gbmV3IENsYXNzKCB7XG4gICAgSW1wbGVtZW50czogR3JhcGguTGFiZWwuTmF0aXZlXG4gIH0pO1xuXG4gIC8qXG4gICAgRm9yY2VEaXJlY3RlZC5MYWJlbC5TVkdcbiAgICBcbiAgICBDdXN0b20gZXh0ZW5zaW9uIG9mIDxHcmFwaC5MYWJlbC5TVkc+LlxuICBcbiAgICBFeHRlbmRzOlxuICBcbiAgICBBbGwgPEdyYXBoLkxhYmVsLlNWRz4gbWV0aG9kc1xuICBcbiAgICBTZWUgYWxzbzpcbiAgXG4gICAgPEdyYXBoLkxhYmVsLlNWRz5cbiAgXG4gICovXG4gIEZvcmNlRGlyZWN0ZWQuTGFiZWwuU1ZHID0gbmV3IENsYXNzKCB7XG4gICAgSW1wbGVtZW50czogR3JhcGguTGFiZWwuU1ZHLFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24odml6KSB7XG4gICAgICB0aGlzLnZpeiA9IHZpejtcbiAgICB9LFxuXG4gICAgLyogXG4gICAgICAgcGxhY2VMYWJlbFxuXG4gICAgICAgT3ZlcnJpZGVzIGFic3RyYWN0IG1ldGhvZCBwbGFjZUxhYmVsIGluIDxHcmFwaC5MYWJlbD4uXG5cbiAgICAgICBQYXJhbWV0ZXJzOlxuXG4gICAgICAgdGFnIC0gQSBET00gbGFiZWwgZWxlbWVudC5cbiAgICAgICBub2RlIC0gQSA8R3JhcGguTm9kZT4uXG4gICAgICAgY29udHJvbGxlciAtIEEgY29uZmlndXJhdGlvbi9jb250cm9sbGVyIG9iamVjdCBwYXNzZWQgdG8gdGhlIHZpc3VhbGl6YXRpb24uXG4gICAgICBcbiAgICAgKi9cbiAgICBwbGFjZUxhYmVsOiBmdW5jdGlvbih0YWcsIG5vZGUsIGNvbnRyb2xsZXIpIHtcbiAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICBjYW52YXMgPSB0aGlzLnZpei5jYW52YXMsXG4gICAgICAgICAgb3ggPSBjYW52YXMudHJhbnNsYXRlT2Zmc2V0WCxcbiAgICAgICAgICBveSA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRZLFxuICAgICAgICAgIHN4ID0gY2FudmFzLnNjYWxlT2Zmc2V0WCxcbiAgICAgICAgICBzeSA9IGNhbnZhcy5zY2FsZU9mZnNldFksXG4gICAgICAgICAgcmFkaXVzID0gY2FudmFzLmdldFNpemUoKTtcbiAgICAgIHZhciBsYWJlbFBvcyA9IHtcbiAgICAgICAgeDogTWF0aC5yb3VuZChwb3MueCAqIHN4ICsgb3ggKyByYWRpdXMud2lkdGggLyAyKSxcbiAgICAgICAgeTogTWF0aC5yb3VuZChwb3MueSAqIHN5ICsgb3kgKyByYWRpdXMuaGVpZ2h0IC8gMilcbiAgICAgIH07XG4gICAgICB0YWcuc2V0QXR0cmlidXRlKCd4JywgbGFiZWxQb3MueCk7XG4gICAgICB0YWcuc2V0QXR0cmlidXRlKCd5JywgbGFiZWxQb3MueSk7XG5cbiAgICAgIGNvbnRyb2xsZXIub25QbGFjZUxhYmVsKHRhZywgbm9kZSk7XG4gICAgfVxuICB9KTtcblxuICAvKlxuICAgICBGb3JjZURpcmVjdGVkLkxhYmVsLkhUTUxcbiAgICAgXG4gICAgIEN1c3RvbSBleHRlbnNpb24gb2YgPEdyYXBoLkxhYmVsLkhUTUw+LlxuXG4gICAgIEV4dGVuZHM6XG5cbiAgICAgQWxsIDxHcmFwaC5MYWJlbC5IVE1MPiBtZXRob2RzLlxuXG4gICAgIFNlZSBhbHNvOlxuXG4gICAgIDxHcmFwaC5MYWJlbC5IVE1MPlxuXG4gICovXG4gIEZvcmNlRGlyZWN0ZWQuTGFiZWwuSFRNTCA9IG5ldyBDbGFzcygge1xuICAgIEltcGxlbWVudHM6IEdyYXBoLkxhYmVsLkhUTUwsXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbih2aXopIHtcbiAgICAgIHRoaXMudml6ID0gdml6O1xuICAgIH0sXG4gICAgLyogXG4gICAgICAgcGxhY2VMYWJlbFxuXG4gICAgICAgT3ZlcnJpZGVzIGFic3RyYWN0IG1ldGhvZCBwbGFjZUxhYmVsIGluIDxHcmFwaC5QbG90Pi5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICB0YWcgLSBBIERPTSBsYWJlbCBlbGVtZW50LlxuICAgICAgIG5vZGUgLSBBIDxHcmFwaC5Ob2RlPi5cbiAgICAgICBjb250cm9sbGVyIC0gQSBjb25maWd1cmF0aW9uL2NvbnRyb2xsZXIgb2JqZWN0IHBhc3NlZCB0byB0aGUgdmlzdWFsaXphdGlvbi5cbiAgICAgIFxuICAgICAqL1xuICAgIHBsYWNlTGFiZWw6IGZ1bmN0aW9uKHRhZywgbm9kZSwgY29udHJvbGxlcikge1xuICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgIGNhbnZhcyA9IHRoaXMudml6LmNhbnZhcyxcbiAgICAgICAgICBveCA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRYLFxuICAgICAgICAgIG95ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFksXG4gICAgICAgICAgc3ggPSBjYW52YXMuc2NhbGVPZmZzZXRYLFxuICAgICAgICAgIHN5ID0gY2FudmFzLnNjYWxlT2Zmc2V0WSxcbiAgICAgICAgICByYWRpdXMgPSBjYW52YXMuZ2V0U2l6ZSgpO1xuICAgICAgdmFyIGxhYmVsUG9zID0ge1xuICAgICAgICB4OiBNYXRoLnJvdW5kKHBvcy54ICogc3ggKyBveCArIHJhZGl1cy53aWR0aCAvIDIpLFxuICAgICAgICB5OiBNYXRoLnJvdW5kKHBvcy55ICogc3kgKyBveSArIHJhZGl1cy5oZWlnaHQgLyAyKVxuICAgICAgfTtcbiAgICAgIHZhciBzdHlsZSA9IHRhZy5zdHlsZTtcbiAgICAgIHN0eWxlLmxlZnQgPSBsYWJlbFBvcy54ICsgJ3B4JztcbiAgICAgIHN0eWxlLnRvcCA9IGxhYmVsUG9zLnkgKyAncHgnO1xuICAgICAgc3R5bGUuZGlzcGxheSA9IHRoaXMuZml0c0luQ2FudmFzKGxhYmVsUG9zLCBjYW52YXMpID8gJycgOiAnbm9uZSc7XG5cbiAgICAgIGNvbnRyb2xsZXIub25QbGFjZUxhYmVsKHRhZywgbm9kZSk7XG4gICAgfVxuICB9KTtcblxuICAvKlxuICAgIENsYXNzOiBGb3JjZURpcmVjdGVkLlBsb3QuTm9kZVR5cGVzXG5cbiAgICBUaGlzIGNsYXNzIGNvbnRhaW5zIGEgbGlzdCBvZiA8R3JhcGguTm9kZT4gYnVpbHQtaW4gdHlwZXMuIFxuICAgIE5vZGUgdHlwZXMgaW1wbGVtZW50ZWQgYXJlICdub25lJywgJ2NpcmNsZScsICd0cmlhbmdsZScsICdyZWN0YW5nbGUnLCAnc3RhcicsICdlbGxpcHNlJyBhbmQgJ3NxdWFyZScuXG5cbiAgICBZb3UgY2FuIGFkZCB5b3VyIGN1c3RvbSBub2RlIHR5cGVzLCBjdXN0b21pemluZyB5b3VyIHZpc3VhbGl6YXRpb24gdG8gdGhlIGV4dHJlbWUuXG5cbiAgICBFeGFtcGxlOlxuXG4gICAgKHN0YXJ0IGNvZGUganMpXG4gICAgICBGb3JjZURpcmVjdGVkLlBsb3QuTm9kZVR5cGVzLmltcGxlbWVudCh7XG4gICAgICAgICdteVNwZWNpYWxUeXBlJzoge1xuICAgICAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpIHtcbiAgICAgICAgICAgIC8vcHJpbnQgeW91ciBjdXN0b20gbm9kZSB0byBjYW52YXNcbiAgICAgICAgICB9LFxuICAgICAgICAgIC8vb3B0aW9uYWxcbiAgICAgICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3MpIHtcbiAgICAgICAgICAgIC8vcmV0dXJuIHRydWUgaWYgcG9zIGlzIGluc2lkZSB0aGUgbm9kZSBvciBmYWxzZSBvdGhlcndpc2VcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIChlbmQgY29kZSlcblxuICAqL1xuICBGb3JjZURpcmVjdGVkLlBsb3QuTm9kZVR5cGVzID0gbmV3IENsYXNzKHtcbiAgICAnbm9uZSc6IHtcbiAgICAgICdyZW5kZXInOiAkLmVtcHR5LFxuICAgICAgJ2NvbnRhaW5zJzogJC5sYW1iZGEoZmFsc2UpXG4gICAgfSxcbiAgICAnY2lyY2xlJzoge1xuICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcyl7XG4gICAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJyk7XG4gICAgICAgIHRoaXMubm9kZUhlbHBlci5jaXJjbGUucmVuZGVyKCdmaWxsJywgcG9zLCBkaW0sIGNhbnZhcyk7XG4gICAgICB9LFxuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obm9kZSwgcG9zKXtcbiAgICAgICAgdmFyIG5wb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJyk7XG4gICAgICAgIHJldHVybiB0aGlzLm5vZGVIZWxwZXIuY2lyY2xlLmNvbnRhaW5zKG5wb3MsIHBvcywgZGltKTtcbiAgICAgIH1cbiAgICB9LFxuICAgICdlbGxpcHNlJzoge1xuICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcyl7XG4gICAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIHdpZHRoID0gbm9kZS5nZXREYXRhKCd3aWR0aCcpLCBcbiAgICAgICAgICAgIGhlaWdodCA9IG5vZGUuZ2V0RGF0YSgnaGVpZ2h0Jyk7XG4gICAgICAgIHRoaXMubm9kZUhlbHBlci5lbGxpcHNlLnJlbmRlcignZmlsbCcsIHBvcywgd2lkdGgsIGhlaWdodCwgY2FudmFzKTtcbiAgICAgICAgfSxcbiAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5vZGUsIHBvcyl7XG4gICAgICAgIHZhciBucG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICB3aWR0aCA9IG5vZGUuZ2V0RGF0YSgnd2lkdGgnKSwgXG4gICAgICAgICAgICBoZWlnaHQgPSBub2RlLmdldERhdGEoJ2hlaWdodCcpO1xuICAgICAgICByZXR1cm4gdGhpcy5ub2RlSGVscGVyLmVsbGlwc2UuY29udGFpbnMobnBvcywgcG9zLCB3aWR0aCwgaGVpZ2h0KTtcbiAgICAgIH1cbiAgICB9LFxuICAgICdzcXVhcmUnOiB7XG4gICAgICAncmVuZGVyJzogZnVuY3Rpb24obm9kZSwgY2FudmFzKXtcbiAgICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgZGltID0gbm9kZS5nZXREYXRhKCdkaW0nKTtcbiAgICAgICAgdGhpcy5ub2RlSGVscGVyLnNxdWFyZS5yZW5kZXIoJ2ZpbGwnLCBwb3MsIGRpbSwgY2FudmFzKTtcbiAgICAgIH0sXG4gICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3Mpe1xuICAgICAgICB2YXIgbnBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgZGltID0gbm9kZS5nZXREYXRhKCdkaW0nKTtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9kZUhlbHBlci5zcXVhcmUuY29udGFpbnMobnBvcywgcG9zLCBkaW0pO1xuICAgICAgfVxuICAgIH0sXG4gICAgJ3JlY3RhbmdsZSc6IHtcbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpe1xuICAgICAgICB2YXIgcG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICB3aWR0aCA9IG5vZGUuZ2V0RGF0YSgnd2lkdGgnKSwgXG4gICAgICAgICAgICBoZWlnaHQgPSBub2RlLmdldERhdGEoJ2hlaWdodCcpO1xuICAgICAgICB0aGlzLm5vZGVIZWxwZXIucmVjdGFuZ2xlLnJlbmRlcignZmlsbCcsIHBvcywgd2lkdGgsIGhlaWdodCwgY2FudmFzKTtcbiAgICAgIH0sXG4gICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3Mpe1xuICAgICAgICB2YXIgbnBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgd2lkdGggPSBub2RlLmdldERhdGEoJ3dpZHRoJyksIFxuICAgICAgICAgICAgaGVpZ2h0ID0gbm9kZS5nZXREYXRhKCdoZWlnaHQnKTtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9kZUhlbHBlci5yZWN0YW5nbGUuY29udGFpbnMobnBvcywgcG9zLCB3aWR0aCwgaGVpZ2h0KTtcbiAgICAgIH1cbiAgICB9LFxuICAgICd0cmlhbmdsZSc6IHtcbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpe1xuICAgICAgICB2YXIgcG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpO1xuICAgICAgICB0aGlzLm5vZGVIZWxwZXIudHJpYW5nbGUucmVuZGVyKCdmaWxsJywgcG9zLCBkaW0sIGNhbnZhcyk7XG4gICAgICB9LFxuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obm9kZSwgcG9zKSB7XG4gICAgICAgIHZhciBucG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpO1xuICAgICAgICByZXR1cm4gdGhpcy5ub2RlSGVscGVyLnRyaWFuZ2xlLmNvbnRhaW5zKG5wb3MsIHBvcywgZGltKTtcbiAgICAgIH1cbiAgICB9LFxuICAgICdzdGFyJzoge1xuICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcyl7XG4gICAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLFxuICAgICAgICAgICAgZGltID0gbm9kZS5nZXREYXRhKCdkaW0nKTtcbiAgICAgICAgdGhpcy5ub2RlSGVscGVyLnN0YXIucmVuZGVyKCdmaWxsJywgcG9zLCBkaW0sIGNhbnZhcyk7XG4gICAgICB9LFxuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obm9kZSwgcG9zKSB7XG4gICAgICAgIHZhciBucG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSxcbiAgICAgICAgICAgIGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJyk7XG4gICAgICAgIHJldHVybiB0aGlzLm5vZGVIZWxwZXIuc3Rhci5jb250YWlucyhucG9zLCBwb3MsIGRpbSk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICAvKlxuICAgIENsYXNzOiBGb3JjZURpcmVjdGVkLlBsb3QuRWRnZVR5cGVzXG4gIFxuICAgIFRoaXMgY2xhc3MgY29udGFpbnMgYSBsaXN0IG9mIDxHcmFwaC5BZGphY2VuY2U+IGJ1aWx0LWluIHR5cGVzLiBcbiAgICBFZGdlIHR5cGVzIGltcGxlbWVudGVkIGFyZSAnbm9uZScsICdsaW5lJyBhbmQgJ2Fycm93Jy5cbiAgXG4gICAgWW91IGNhbiBhZGQgeW91ciBjdXN0b20gZWRnZSB0eXBlcywgY3VzdG9taXppbmcgeW91ciB2aXN1YWxpemF0aW9uIHRvIHRoZSBleHRyZW1lLlxuICBcbiAgICBFeGFtcGxlOlxuICBcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgIEZvcmNlRGlyZWN0ZWQuUGxvdC5FZGdlVHlwZXMuaW1wbGVtZW50KHtcbiAgICAgICAgJ215U3BlY2lhbFR5cGUnOiB7XG4gICAgICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKGFkaiwgY2FudmFzKSB7XG4gICAgICAgICAgICAvL3ByaW50IHlvdXIgY3VzdG9tIGVkZ2UgdG8gY2FudmFzXG4gICAgICAgICAgfSxcbiAgICAgICAgICAvL29wdGlvbmFsXG4gICAgICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24oYWRqLCBwb3MpIHtcbiAgICAgICAgICAgIC8vcmV0dXJuIHRydWUgaWYgcG9zIGlzIGluc2lkZSB0aGUgYXJjIG9yIGZhbHNlIG90aGVyd2lzZVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgKGVuZCBjb2RlKVxuICBcbiAgKi9cbiAgRm9yY2VEaXJlY3RlZC5QbG90LkVkZ2VUeXBlcyA9IG5ldyBDbGFzcyh7XG4gICAgJ25vbmUnOiAkLmVtcHR5LFxuICAgICdsaW5lJzoge1xuICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKGFkaiwgY2FudmFzKSB7XG4gICAgICAgIHZhciBmcm9tID0gYWRqLm5vZGVGcm9tLnBvcy5nZXRjKHRydWUpLFxuICAgICAgICAgICAgdG8gPSBhZGoubm9kZVRvLnBvcy5nZXRjKHRydWUpO1xuICAgICAgICB0aGlzLmVkZ2VIZWxwZXIubGluZS5yZW5kZXIoZnJvbSwgdG8sIGNhbnZhcyk7XG4gICAgICB9LFxuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24oYWRqLCBwb3MpIHtcbiAgICAgICAgdmFyIGZyb20gPSBhZGoubm9kZUZyb20ucG9zLmdldGModHJ1ZSksXG4gICAgICAgICAgICB0byA9IGFkai5ub2RlVG8ucG9zLmdldGModHJ1ZSk7XG4gICAgICAgIHJldHVybiB0aGlzLmVkZ2VIZWxwZXIubGluZS5jb250YWlucyhmcm9tLCB0bywgcG9zLCB0aGlzLmVkZ2UuZXBzaWxvbik7XG4gICAgICB9XG4gICAgfSxcbiAgICAnYXJyb3cnOiB7XG4gICAgICAncmVuZGVyJzogZnVuY3Rpb24oYWRqLCBjYW52YXMpIHtcbiAgICAgICAgdmFyIGZyb20gPSBhZGoubm9kZUZyb20ucG9zLmdldGModHJ1ZSksXG4gICAgICAgICAgICB0byA9IGFkai5ub2RlVG8ucG9zLmdldGModHJ1ZSksXG4gICAgICAgICAgICBkaW0gPSBhZGouZ2V0RGF0YSgnZGltJyksXG4gICAgICAgICAgICBkaXJlY3Rpb24gPSBhZGouZGF0YS4kZGlyZWN0aW9uLFxuICAgICAgICAgICAgaW52ID0gKGRpcmVjdGlvbiAmJiBkaXJlY3Rpb24ubGVuZ3RoPjEgJiYgZGlyZWN0aW9uWzBdICE9IGFkai5ub2RlRnJvbS5pZCk7XG4gICAgICAgIHRoaXMuZWRnZUhlbHBlci5hcnJvdy5yZW5kZXIoZnJvbSwgdG8sIGRpbSwgaW52LCBjYW52YXMpO1xuICAgICAgfSxcbiAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKGFkaiwgcG9zKSB7XG4gICAgICAgIHZhciBmcm9tID0gYWRqLm5vZGVGcm9tLnBvcy5nZXRjKHRydWUpLFxuICAgICAgICAgICAgdG8gPSBhZGoubm9kZVRvLnBvcy5nZXRjKHRydWUpO1xuICAgICAgICByZXR1cm4gdGhpcy5lZGdlSGVscGVyLmFycm93LmNvbnRhaW5zKGZyb20sIHRvLCBwb3MsIHRoaXMuZWRnZS5lcHNpbG9uKTtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuXG59KSgkaml0LkZvcmNlRGlyZWN0ZWQpO1xuXG5cbi8qXG4gKiBWZWN0b3IzIGNsYXNzIGJhc2VkIG9uIHRocmVlLmpzIGh0dHA6Ly9naXRodWIuY29tL21yZG9vYi90aHJlZS5qcywgQ29weXJpZ2h0IChjKSBNci5kb29iIGh0dHA6Ly9tcmRvb2IuY29tLywgTUlUIExpY2Vuc2UgaHR0cDovL2dpdGh1Yi5jb20vbXJkb29iL3RocmVlLmpzL2Jsb2IvbWFzdGVyL0xJQ0VOU0UgXG4gKi9cblxudmFyIFZlY3RvcjMgPSBmdW5jdGlvbih4LCB5LCB6KSB7XG5cdHRoaXMueCA9IHggfHwgMDtcblx0dGhpcy55ID0geSB8fCAwO1xuXHR0aGlzLnogPSB6IHx8IDA7XG59O1xuXG4kaml0LlZlY3RvcjMgPSBWZWN0b3IzO1xuXG5WZWN0b3IzLnByb3RvdHlwZSA9IHtcbiAgc2V0OiBmdW5jdGlvbih2KSB7XG4gICAgdGhpcy54ID0gdi54O1xuICAgIHRoaXMueSA9IHYueTtcbiAgICB0aGlzLnogPSB2Lno7XG4gIH0sXG5cbiAgc2V0YzogZnVuY3Rpb24oeCwgeSwgeikge1xuXHRcdHRoaXMueCA9IHg7XG5cdFx0dGhpcy55ID0geTtcblx0XHR0aGlzLnogPSB6O1xuXHR9LFxuXHRcblx0Z2V0YzogZnVuY3Rpb24oKSB7XG5cdCAgcmV0dXJuIHRoaXM7XG5cdH0sXG5cdFxuXHQvL1RPRE8obmljbyk6IGdldHBcblxuXHRhZGQ6IGZ1bmN0aW9uKHYxLCB2Mikge1xuXHRcdHRoaXMueCA9IHYxLnggKyB2Mi54O1xuXHRcdHRoaXMueSA9IHYxLnkgKyB2Mi55O1xuXHRcdHRoaXMueiA9IHYxLnogKyB2Mi56O1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdCRhZGQ6IGZ1bmN0aW9uKHYpIHtcblx0XHR0aGlzLnggKz0gdi54O1xuXHRcdHRoaXMueSArPSB2Lnk7XG5cdFx0dGhpcy56ICs9IHYuejtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRhZGRTY2FsYXI6IGZ1bmN0aW9uKHMpIHtcblx0XHR0aGlzLnggKz0gcztcblx0XHR0aGlzLnkgKz0gcztcblx0XHR0aGlzLnogKz0gcztcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRzdWI6IGZ1bmN0aW9uKHYxLCB2Mikge1xuXHRcdHRoaXMueCA9IHYxLnggLSB2Mi54O1xuXHRcdHRoaXMueSA9IHYxLnkgLSB2Mi55O1xuXHRcdHRoaXMueiA9IHYxLnogLSB2Mi56O1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdCRzdWI6IGZ1bmN0aW9uKHYpIHtcblx0XHR0aGlzLnggLT0gdi54O1xuXHRcdHRoaXMueSAtPSB2Lnk7XG5cdFx0dGhpcy56IC09IHYuejtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRjcm9zczogZnVuY3Rpb24odjEsIHYyKSB7XG5cdFx0dGhpcy54ID0gdjEueSAqIHYyLnogLSB2MS56ICogdjIueTtcblx0XHR0aGlzLnkgPSB2MS56ICogdjIueCAtIHYxLnggKiB2Mi56O1xuXHRcdHRoaXMueiA9IHYxLnggKiB2Mi55IC0gdjEueSAqIHYyLng7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0JGNyb3NzOiBmdW5jdGlvbih2KSB7XG5cdFx0dmFyIHR4ID0gdGhpcy54LCB0eSA9IHRoaXMueSwgdHogPSB0aGlzLno7XG5cblx0XHR0aGlzLnggPSB0eSAqIHYueiAtIHR6ICogdi55O1xuXHRcdHRoaXMueSA9IHR6ICogdi54IC0gdHggKiB2Lno7XG5cdFx0dGhpcy56ID0gdHggKiB2LnkgLSB0eSAqIHYueDtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQkbXVsdGlwbHk6IGZ1bmN0aW9uKHYpIHtcblx0XHR0aGlzLnggKj0gdi54O1xuXHRcdHRoaXMueSAqPSB2Lnk7XG5cdFx0dGhpcy56ICo9IHYuejtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQkc2NhbGU6IGZ1bmN0aW9uKHMpIHtcblx0XHR0aGlzLnggKj0gcztcblx0XHR0aGlzLnkgKj0gcztcblx0XHR0aGlzLnogKj0gcztcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRkb3Q6IGZ1bmN0aW9uKHYpIHtcblx0XHRyZXR1cm4gdGhpcy54ICogdi54ICsgdGhpcy55ICogdi55ICsgdGhpcy56ICogdi56O1xuXHR9LFxuXG5cdGRpc3RhbmNlVG86IGZ1bmN0aW9uKHYpIHtcblx0XHRyZXR1cm4gTWF0aC5zcXJ0KHRoaXMuZGlzdGFuY2VUb1NxdWFyZWQodikpO1xuXHR9LFxuXG5cdGRpc3RhbmNlVG9TcXVhcmVkOiBmdW5jdGlvbih2KSB7XG5cdFx0dmFyIGR4ID0gdGhpcy54IC0gdi54LCBkeSA9IHRoaXMueSAtIHYueSwgZHogPSB0aGlzLnogLSB2Lno7XG5cdFx0cmV0dXJuIGR4ICogZHggKyBkeSAqIGR5ICsgZHogKiBkejtcblx0fSxcblxuXHRub3JtOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gTWF0aC5zcXJ0KCB0aGlzLnggKiB0aGlzLnggKyB0aGlzLnkgKiB0aGlzLnkgKyB0aGlzLnogKiB0aGlzLnogKTtcblx0fSxcblxuXHRub3JtU3F1YXJlZDogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMueCAqIHRoaXMueCArIHRoaXMueSAqIHRoaXMueSArIHRoaXMueiAqIHRoaXMuejtcblx0fSxcblxuXHRuZWdhdGU6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMueCA9IC0gdGhpcy54O1xuXHRcdHRoaXMueSA9IC0gdGhpcy55O1xuXHRcdHRoaXMueiA9IC0gdGhpcy56O1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdG5vcm1hbGl6ZTogZnVuY3Rpb24oKSB7XG5cdCAgdmFyIGxlbiA9IHRoaXMubm9ybSgpO1xuXHRcdGlmICggbGVuID4gMCApIHtcblx0XHRcdHRoaXMuJHNjYWxlKDEgLyBsZW4pO1xuXHRcdH0gXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0aXNaZXJvOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgYWxtb3N0WmVybyA9IDAuMDAwMSxcblx0XHQgICAgYWJzID0gTWF0aC5hYnM7XG5cdFx0XG5cdFx0cmV0dXJuIGFicyh0aGlzLngpIDwgYWxtb3N0WmVybyAmJiBhYnModGhpcy55KSA8IGFsbW9zdFplcm8gJiYgYWJzKHRoaXMueikgPCBhbG1vc3RaZXJvO1xuXHR9LFxuXG5cdGNsb25lOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gbmV3IFZlY3RvcjModGhpcy54LCB0aGlzLnksIHRoaXMueik7XG5cdH1cbn07XG5cbnZhciAkVjMgPSBmdW5jdGlvbihhLCBiLCBjKSB7IHJldHVybiBuZXcgVmVjdG9yMyhhLCBiLCBjKTsgfTtcblxuLypcbiAqIE1hdHJpeDQgY2xhc3MgYmFzZWQgb24gdGhyZWUuanMgaHR0cDovL2dpdGh1Yi5jb20vbXJkb29iL3RocmVlLmpzLCBDb3B5cmlnaHQgKGMpIE1yLmRvb2IgaHR0cDovL21yZG9vYi5jb20vLCBNSVQgTGljZW5zZSBodHRwOi8vZ2l0aHViLmNvbS9tcmRvb2IvdGhyZWUuanMvYmxvYi9tYXN0ZXIvTElDRU5TRSBcbiAqL1xuXG52YXIgTWF0cml4NCA9IGZ1bmN0aW9uKCkge1xuXHR0aGlzLl94ID0gbmV3IFZlY3RvcjMoKTtcblx0dGhpcy5feSA9IG5ldyBWZWN0b3IzKCk7XG5cdHRoaXMuX3ogPSBuZXcgVmVjdG9yMygpO1xufTtcblxuJGppdC5NYXRyaXg0ID0gTWF0cml4NDtcblxuTWF0cml4NC5wcm90b3R5cGUgPSB7XG5cblx0bjExOiAxLCBuMTI6IDAsIG4xMzogMCwgbjE0OiAwLFxuXHRuMjE6IDAsIG4yMjogMSwgbjIzOiAwLCBuMjQ6IDAsXG5cdG4zMTogMCwgbjMyOiAwLCBuMzM6IDEsIG4zNDogMCxcblx0bjQxOiAwLCBuNDI6IDAsIG40MzogMCwgbjQ0OiAxLFxuXG5cdGlkZW50aXR5OiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLm4xMSA9IDE7IHRoaXMubjEyID0gMDsgdGhpcy5uMTMgPSAwOyB0aGlzLm4xNCA9IDA7XG5cdFx0dGhpcy5uMjEgPSAwOyB0aGlzLm4yMiA9IDE7IHRoaXMubjIzID0gMDsgdGhpcy5uMjQgPSAwO1xuXHRcdHRoaXMubjMxID0gMDsgdGhpcy5uMzIgPSAwOyB0aGlzLm4zMyA9IDE7IHRoaXMubjM0ID0gMDtcblx0XHR0aGlzLm40MSA9IDA7IHRoaXMubjQyID0gMDsgdGhpcy5uNDMgPSAwOyB0aGlzLm40NCA9IDE7XG5cdH0sXG5cblx0bG9va0F0OiBmdW5jdGlvbihleWUsIGNlbnRlciwgdXApIHtcblx0XHR2YXIgeCA9IHRoaXMuX3gsIHkgPSB0aGlzLl95LCB6ID0gdGhpcy5fejtcblxuXHRcdHouc3ViKGV5ZSwgY2VudGVyKTtcblx0XHR6Lm5vcm1hbGl6ZSgpO1xuXG5cdFx0eC5jcm9zcyh1cCwgeik7XG5cdFx0eC5ub3JtYWxpemUoKTtcblxuXHRcdHkuY3Jvc3MoeiwgeCk7XG5cdFx0eS5ub3JtYWxpemUoKTtcblxuXHRcdHRoaXMubjExID0geC54OyB0aGlzLm4xMiA9IHgueTsgdGhpcy5uMTMgPSB4Lno7IHRoaXMubjE0ID0gLSB4LmRvdCggZXllICk7XG5cdFx0dGhpcy5uMjEgPSB5Lng7IHRoaXMubjIyID0geS55OyB0aGlzLm4yMyA9IHkuejsgdGhpcy5uMjQgPSAtIHkuZG90KCBleWUgKTtcblx0XHR0aGlzLm4zMSA9IHoueDsgdGhpcy5uMzIgPSB6Lnk7IHRoaXMubjMzID0gei56OyB0aGlzLm4zNCA9IC0gei5kb3QoIGV5ZSApO1xuXHR9LFxuXG5cdHRyYW5zZm9ybTogZnVuY3Rpb24odikge1xuXHRcdHZhciB2eCA9IHYueCwgdnkgPSB2LnksIHZ6ID0gdi56LCB2dyA9IHYudyA/IHYudyA6IDEuMDtcblxuXHRcdHYueCA9IHRoaXMubjExICogdnggKyB0aGlzLm4xMiAqIHZ5ICsgdGhpcy5uMTMgKiB2eiArIHRoaXMubjE0ICogdnc7XG5cdFx0di55ID0gdGhpcy5uMjEgKiB2eCArIHRoaXMubjIyICogdnkgKyB0aGlzLm4yMyAqIHZ6ICsgdGhpcy5uMjQgKiB2dztcblx0XHR2LnogPSB0aGlzLm4zMSAqIHZ4ICsgdGhpcy5uMzIgKiB2eSArIHRoaXMubjMzICogdnogKyB0aGlzLm4zNCAqIHZ3O1xuXG5cdFx0dncgPSB0aGlzLm40MSAqIHZ4ICsgdGhpcy5uNDIgKiB2eSArIHRoaXMubjQzICogdnogKyB0aGlzLm40NCAqIHZ3O1xuXG5cdFx0aWYodi53KSB7XG5cdFx0XHR2LncgPSB2dztcblx0XHR9IGVsc2Uge1xuXHRcdFx0di54ID0gdi54IC8gdnc7XG5cdFx0XHR2LnkgPSB2LnkgLyB2dztcblx0XHRcdHYueiA9IHYueiAvIHZ3O1xuXHRcdH1cblx0fSxcblxuXHRtdWx0aXBseTogZnVuY3Rpb24oYSwgYikge1xuXHRcdHRoaXMubjExID0gYS5uMTEgKiBiLm4xMSArIGEubjEyICogYi5uMjEgKyBhLm4xMyAqIGIubjMxICsgYS5uMTQgKiBiLm40MTtcblx0XHR0aGlzLm4xMiA9IGEubjExICogYi5uMTIgKyBhLm4xMiAqIGIubjIyICsgYS5uMTMgKiBiLm4zMiArIGEubjE0ICogYi5uNDI7XG5cdFx0dGhpcy5uMTMgPSBhLm4xMSAqIGIubjEzICsgYS5uMTIgKiBiLm4yMyArIGEubjEzICogYi5uMzMgKyBhLm4xNCAqIGIubjQzO1xuXHRcdHRoaXMubjE0ID0gYS5uMTEgKiBiLm4xNCArIGEubjEyICogYi5uMjQgKyBhLm4xMyAqIGIubjM0ICsgYS5uMTQgKiBiLm40NDtcblxuXHRcdHRoaXMubjIxID0gYS5uMjEgKiBiLm4xMSArIGEubjIyICogYi5uMjEgKyBhLm4yMyAqIGIubjMxICsgYS5uMjQgKiBiLm40MTtcblx0XHR0aGlzLm4yMiA9IGEubjIxICogYi5uMTIgKyBhLm4yMiAqIGIubjIyICsgYS5uMjMgKiBiLm4zMiArIGEubjI0ICogYi5uNDI7XG5cdFx0dGhpcy5uMjMgPSBhLm4yMSAqIGIubjEzICsgYS5uMjIgKiBiLm4yMyArIGEubjIzICogYi5uMzMgKyBhLm4yNCAqIGIubjQzO1xuXHRcdHRoaXMubjI0ID0gYS5uMjEgKiBiLm4xNCArIGEubjIyICogYi5uMjQgKyBhLm4yMyAqIGIubjM0ICsgYS5uMjQgKiBiLm40NDtcblxuXHRcdHRoaXMubjMxID0gYS5uMzEgKiBiLm4xMSArIGEubjMyICogYi5uMjEgKyBhLm4zMyAqIGIubjMxICsgYS5uMzQgKiBiLm40MTtcblx0XHR0aGlzLm4zMiA9IGEubjMxICogYi5uMTIgKyBhLm4zMiAqIGIubjIyICsgYS5uMzMgKiBiLm4zMiArIGEubjM0ICogYi5uNDI7XG5cdFx0dGhpcy5uMzMgPSBhLm4zMSAqIGIubjEzICsgYS5uMzIgKiBiLm4yMyArIGEubjMzICogYi5uMzMgKyBhLm4zNCAqIGIubjQzO1xuXHRcdHRoaXMubjM0ID0gYS5uMzEgKiBiLm4xNCArIGEubjMyICogYi5uMjQgKyBhLm4zMyAqIGIubjM0ICsgYS5uMzQgKiBiLm40NDtcblxuXHRcdHRoaXMubjQxID0gYS5uNDEgKiBiLm4xMSArIGEubjQyICogYi5uMjEgKyBhLm40MyAqIGIubjMxICsgYS5uNDQgKiBiLm40MTtcblx0XHR0aGlzLm40MiA9IGEubjQxICogYi5uMTIgKyBhLm40MiAqIGIubjIyICsgYS5uNDMgKiBiLm4zMiArIGEubjQ0ICogYi5uNDI7XG5cdFx0dGhpcy5uNDMgPSBhLm40MSAqIGIubjEzICsgYS5uNDIgKiBiLm4yMyArIGEubjQzICogYi5uMzMgKyBhLm40NCAqIGIubjQzO1xuXHRcdHRoaXMubjQ0ID0gYS5uNDEgKiBiLm4xNCArIGEubjQyICogYi5uMjQgKyBhLm40MyAqIGIubjM0ICsgYS5uNDQgKiBiLm40NDtcblx0fSxcblxuXHQkbXVsdGlwbHk6IGZ1bmN0aW9uKG0pIHtcblx0XHR2YXIgbjExID0gdGhpcy5uMTEsIG4xMiA9IHRoaXMubjEyLCBuMTMgPSB0aGlzLm4xMywgbjE0ID0gdGhpcy5uMTQsXG4gICAgXHRcdG4yMSA9IHRoaXMubjIxLCBuMjIgPSB0aGlzLm4yMiwgbjIzID0gdGhpcy5uMjMsIG4yNCA9IHRoaXMubjI0LFxuICAgIFx0XHRuMzEgPSB0aGlzLm4zMSwgbjMyID0gdGhpcy5uMzIsIG4zMyA9IHRoaXMubjMzLCBuMzQgPSB0aGlzLm4zNCxcbiAgICBcdFx0bjQxID0gdGhpcy5uNDEsIG40MiA9IHRoaXMubjQyLCBuNDMgPSB0aGlzLm40MywgbjQ0ID0gdGhpcy5uNDQ7XG5cblx0XHR0aGlzLm4xMSA9IG4xMSAqIG0ubjExICsgbjEyICogbS5uMjEgKyBuMTMgKiBtLm4zMSArIG4xNCAqIG0ubjQxO1xuXHRcdHRoaXMubjEyID0gbjExICogbS5uMTIgKyBuMTIgKiBtLm4yMiArIG4xMyAqIG0ubjMyICsgbjE0ICogbS5uNDI7XG5cdFx0dGhpcy5uMTMgPSBuMTEgKiBtLm4xMyArIG4xMiAqIG0ubjIzICsgbjEzICogbS5uMzMgKyBuMTQgKiBtLm40Mztcblx0XHR0aGlzLm4xNCA9IG4xMSAqIG0ubjE0ICsgbjEyICogbS5uMjQgKyBuMTMgKiBtLm4zNCArIG4xNCAqIG0ubjQ0O1xuXG5cdFx0dGhpcy5uMjEgPSBuMjEgKiBtLm4xMSArIG4yMiAqIG0ubjIxICsgbjIzICogbS5uMzEgKyBuMjQgKiBtLm40MTtcblx0XHR0aGlzLm4yMiA9IG4yMSAqIG0ubjEyICsgbjIyICogbS5uMjIgKyBuMjMgKiBtLm4zMiArIG4yNCAqIG0ubjQyO1xuXHRcdHRoaXMubjIzID0gbjIxICogbS5uMTMgKyBuMjIgKiBtLm4yMyArIG4yMyAqIG0ubjMzICsgbjI0ICogbS5uNDM7XG5cdFx0dGhpcy5uMjQgPSBuMjEgKiBtLm4xNCArIG4yMiAqIG0ubjI0ICsgbjIzICogbS5uMzQgKyBuMjQgKiBtLm40NDtcblxuXHRcdHRoaXMubjMxID0gbjMxICogbS5uMTEgKyBuMzIgKiBtLm4yMSArIG4zMyAqIG0ubjMxICsgbjM0ICogbS5uNDE7XG5cdFx0dGhpcy5uMzIgPSBuMzEgKiBtLm4xMiArIG4zMiAqIG0ubjIyICsgbjMzICogbS5uMzIgKyBuMzQgKiBtLm40Mjtcblx0XHR0aGlzLm4zMyA9IG4zMSAqIG0ubjEzICsgbjMyICogbS5uMjMgKyBuMzMgKiBtLm4zMyArIG4zNCAqIG0ubjQzO1xuXHRcdHRoaXMubjM0ID0gbjMxICogbS5uMTQgKyBuMzIgKiBtLm4yNCArIG4zMyAqIG0ubjM0ICsgbjM0ICogbS5uNDQ7XG5cblx0XHR0aGlzLm40MSA9IG40MSAqIG0ubjExICsgbjQyICogbS5uMjEgKyBuNDMgKiBtLm4zMSArIG40NCAqIG0ubjQxO1xuXHRcdHRoaXMubjQyID0gbjQxICogbS5uMTIgKyBuNDIgKiBtLm4yMiArIG40MyAqIG0ubjMyICsgbjQ0ICogbS5uNDI7XG5cdFx0dGhpcy5uNDMgPSBuNDEgKiBtLm4xMyArIG40MiAqIG0ubjIzICsgbjQzICogbS5uMzMgKyBuNDQgKiBtLm40Mztcblx0XHR0aGlzLm40NCA9IG40MSAqIG0ubjE0ICsgbjQyICogbS5uMjQgKyBuNDMgKiBtLm4zNCArIG40NCAqIG0ubjQ0O1xuXHR9LFxuXG5cdCRzY2FsZTogZnVuY3Rpb24ocykge1xuXHRcdHRoaXMubjExICo9IHM7IHRoaXMubjEyICo9IHM7IHRoaXMubjEzICo9IHM7IHRoaXMubjE0ICo9IHM7XG5cdFx0dGhpcy5uMjEgKj0gczsgdGhpcy5uMjIgKj0gczsgdGhpcy5uMjMgKj0gczsgdGhpcy5uMjQgKj0gcztcblx0XHR0aGlzLm4zMSAqPSBzOyB0aGlzLm4zMiAqPSBzOyB0aGlzLm4zMyAqPSBzOyB0aGlzLm4zNCAqPSBzO1xuXHRcdHRoaXMubjQxICo9IHM7IHRoaXMubjQyICo9IHM7IHRoaXMubjQzICo9IHM7IHRoaXMubjQ0ICo9IHM7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cdFxuXHQkYWRkOiBmdW5jdGlvbihtKSB7XG5cdCAgdGhpcy5uMTEgKz0gbS5uMTE7XG5cdCAgdGhpcy5uMTIgKz0gbS5uMTI7XG5cdCAgdGhpcy5uMTMgKz0gbS5uMTM7XG5cdCAgdGhpcy5uMTQgKz0gbS5uMTQ7XG5cdCAgdGhpcy5uMjEgKz0gbS5uMjE7XG5cdCAgdGhpcy5uMjIgKz0gbS5uMjI7XG5cdCAgdGhpcy5uMjMgKz0gbS5uMjM7XG5cdCAgdGhpcy5uMjQgKz0gbS5uMjQ7XG5cdCAgdGhpcy5uMzEgKz0gbS5uMzE7XG5cdCAgdGhpcy5uMzIgKz0gbS5uMzI7XG5cdCAgdGhpcy5uMzMgKz0gbS5uMzM7XG5cdCAgdGhpcy5uMzQgKz0gbS5uMzQ7XG5cdCAgdGhpcy5uNDEgKz0gbS5uNDE7XG5cdCAgdGhpcy5uNDIgKz0gbS5uNDI7XG5cdCAgdGhpcy5uNDMgKz0gbS5uNDM7XG5cdCAgdGhpcy5uNDQgKz0gbS5uNDQ7XG5cdCAgcmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0ZGV0ZXJtaW5hbnQ6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiAoXG5cdFx0XHR0aGlzLm4xNCAqIHRoaXMubjIzICogdGhpcy5uMzIgKiB0aGlzLm40MS1cblx0XHRcdHRoaXMubjEzICogdGhpcy5uMjQgKiB0aGlzLm4zMiAqIHRoaXMubjQxLVxuXHRcdFx0dGhpcy5uMTQgKiB0aGlzLm4yMiAqIHRoaXMubjMzICogdGhpcy5uNDErXG5cdFx0XHR0aGlzLm4xMiAqIHRoaXMubjI0ICogdGhpcy5uMzMgKiB0aGlzLm40MStcblxuXHRcdFx0dGhpcy5uMTMgKiB0aGlzLm4yMiAqIHRoaXMubjM0ICogdGhpcy5uNDEtXG5cdFx0XHR0aGlzLm4xMiAqIHRoaXMubjIzICogdGhpcy5uMzQgKiB0aGlzLm40MS1cblx0XHRcdHRoaXMubjE0ICogdGhpcy5uMjMgKiB0aGlzLm4zMSAqIHRoaXMubjQyK1xuXHRcdFx0dGhpcy5uMTMgKiB0aGlzLm4yNCAqIHRoaXMubjMxICogdGhpcy5uNDIrXG5cblx0XHRcdHRoaXMubjE0ICogdGhpcy5uMjEgKiB0aGlzLm4zMyAqIHRoaXMubjQyLVxuXHRcdFx0dGhpcy5uMTEgKiB0aGlzLm4yNCAqIHRoaXMubjMzICogdGhpcy5uNDItXG5cdFx0XHR0aGlzLm4xMyAqIHRoaXMubjIxICogdGhpcy5uMzQgKiB0aGlzLm40Mitcblx0XHRcdHRoaXMubjExICogdGhpcy5uMjMgKiB0aGlzLm4zNCAqIHRoaXMubjQyK1xuXG5cdFx0XHR0aGlzLm4xNCAqIHRoaXMubjIyICogdGhpcy5uMzEgKiB0aGlzLm40My1cblx0XHRcdHRoaXMubjEyICogdGhpcy5uMjQgKiB0aGlzLm4zMSAqIHRoaXMubjQzLVxuXHRcdFx0dGhpcy5uMTQgKiB0aGlzLm4yMSAqIHRoaXMubjMyICogdGhpcy5uNDMrXG5cdFx0XHR0aGlzLm4xMSAqIHRoaXMubjI0ICogdGhpcy5uMzIgKiB0aGlzLm40MytcblxuXHRcdFx0dGhpcy5uMTIgKiB0aGlzLm4yMSAqIHRoaXMubjM0ICogdGhpcy5uNDMtXG5cdFx0XHR0aGlzLm4xMSAqIHRoaXMubjIyICogdGhpcy5uMzQgKiB0aGlzLm40My1cblx0XHRcdHRoaXMubjEzICogdGhpcy5uMjIgKiB0aGlzLm4zMSAqIHRoaXMubjQ0K1xuXHRcdFx0dGhpcy5uMTIgKiB0aGlzLm4yMyAqIHRoaXMubjMxICogdGhpcy5uNDQrXG5cblx0XHRcdHRoaXMubjEzICogdGhpcy5uMjEgKiB0aGlzLm4zMiAqIHRoaXMubjQ0LVxuXHRcdFx0dGhpcy5uMTEgKiB0aGlzLm4yMyAqIHRoaXMubjMyICogdGhpcy5uNDQtXG5cdFx0XHR0aGlzLm4xMiAqIHRoaXMubjIxICogdGhpcy5uMzMgKiB0aGlzLm40NCtcblx0XHRcdHRoaXMubjExICogdGhpcy5uMjIgKiB0aGlzLm4zMyAqIHRoaXMubjQ0ICk7XG5cdH0sXG5cbiAgJHRyYW5zcG9zZTogZnVuY3Rpb24oKSB7XG5cdCAgZnVuY3Rpb24gc3dhcChvYmosIHAxLCBwMikge1xuXHQgICAgdmFyIGF1eCA9IG9ialtwMV07XG5cdCAgICBvYmpbcDFdID0gb2JqW3AyXTtcblx0ICAgIG9ialtwMl0gPSBhdXg7XG5cdCAgfVxuXHQgIFxuXHQgIHN3YXAodGhpcywgJ24yMScsICduMTInKTtcblx0ICBzd2FwKHRoaXMsICduMzEnLCAnbjEzJyk7XG5cdCAgc3dhcCh0aGlzLCAnbjMyJywgJ24yMycpO1xuXHQgIHN3YXAodGhpcywgJ240MScsICduMTQnKTtcblx0ICBzd2FwKHRoaXMsICduNDInLCAnbjI0Jyk7XG5cdCAgc3dhcCh0aGlzLCAnbjQzJywgJ24zNCcpO1xuXHQgIHJldHVybiB0aGlzO1xuICB9LFxuICBcblx0Y2xvbmU6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBtID0gbmV3IE1hdHJpeDQoKTtcblx0XHRtLm4xMSA9IHRoaXMubjExOyBtLm4xMiA9IHRoaXMubjEyOyBtLm4xMyA9IHRoaXMubjEzOyBtLm4xNCA9IHRoaXMubjE0O1xuXHRcdG0ubjIxID0gdGhpcy5uMjE7IG0ubjIyID0gdGhpcy5uMjI7IG0ubjIzID0gdGhpcy5uMjM7IG0ubjI0ID0gdGhpcy5uMjQ7XG5cdFx0bS5uMzEgPSB0aGlzLm4zMTsgbS5uMzIgPSB0aGlzLm4zMjsgbS5uMzMgPSB0aGlzLm4zMzsgbS5uMzQgPSB0aGlzLm4zNDtcblx0XHRtLm40MSA9IHRoaXMubjQxOyBtLm40MiA9IHRoaXMubjQyOyBtLm40MyA9IHRoaXMubjQzOyBtLm40NCA9IHRoaXMubjQ0O1xuXHRcdHJldHVybiBtO1xuXHR9LFxuXHRcblx0ZmxhdHRlbjogZnVuY3Rpb24oKSB7XG5cdCAgcmV0dXJuIFt0aGlzLm4xMSwgdGhpcy5uMjEsIHRoaXMubjMxLCB0aGlzLm40MSxcblx0ICAgICAgdGhpcy5uMTIsIHRoaXMubjIyLCB0aGlzLm4zMiwgdGhpcy5uNDIsXG5cdCAgICAgIHRoaXMubjEzLCB0aGlzLm4yMywgdGhpcy5uMzMsIHRoaXMubjQzLFxuXHQgICAgICB0aGlzLm4xNCwgdGhpcy5uMjQsIHRoaXMubjM0LCB0aGlzLm40NF07XG5cdH1cbn07XG5cbk1hdHJpeDQudHJhbnNsYXRpb25NYXRyaXggPSBmdW5jdGlvbih4LCB5LCB6KSB7XG5cdHZhciBtID0gbmV3IE1hdHJpeDQoKTtcblxuXHRtLm4xNCA9IHg7XG5cdG0ubjI0ID0geTtcblx0bS5uMzQgPSB6O1xuXG5cdHJldHVybiBtO1xufTtcblxuTWF0cml4NC5zY2FsZU1hdHJpeCA9IGZ1bmN0aW9uKHgsIHksIHopIHtcblx0dmFyIG0gPSBuZXcgTWF0cml4NCgpO1xuXG5cdG0ubjExID0geDtcblx0bS5uMjIgPSB5O1xuXHRtLm4zMyA9IHo7XG5cblx0cmV0dXJuIG07XG59O1xuXG5NYXRyaXg0LnJvdGF0aW9uWE1hdHJpeCA9IGZ1bmN0aW9uKHRoZXRhKSB7XG5cdHZhciByb3QgPSBuZXcgTWF0cml4NCgpO1xuXG5cdHJvdC5uMjIgPSByb3QubjMzID0gTWF0aC5jb3MoIHRoZXRhICk7XG5cdHJvdC5uMzIgPSBNYXRoLnNpbiggdGhldGEgKTtcblx0cm90Lm4yMyA9IC0gcm90Lm4zMjtcblxuXHRyZXR1cm4gcm90O1xufTtcblxuTWF0cml4NC5yb3RhdGlvbllNYXRyaXggPSBmdW5jdGlvbih0aGV0YSkge1xuXHR2YXIgcm90ID0gbmV3IE1hdHJpeDQoKTtcblxuXHRyb3QubjExID0gcm90Lm4zMyA9IE1hdGguY29zKCB0aGV0YSApO1xuXHRyb3QubjEzID0gTWF0aC5zaW4oIHRoZXRhICk7XG5cdHJvdC5uMzEgPSAtIHJvdC5uMTM7XG5cblx0cmV0dXJuIHJvdDtcbn07XG5cbk1hdHJpeDQucm90YXRpb25aTWF0cml4ID0gZnVuY3Rpb24odGhldGEpIHtcblx0dmFyIHJvdCA9IG5ldyBNYXRyaXg0KCk7XG5cblx0cm90Lm4xMSA9IHJvdC5uMjIgPSBNYXRoLmNvcyggdGhldGEgKTtcblx0cm90Lm4yMSA9IE1hdGguc2luKCB0aGV0YSApO1xuXHRyb3QubjEyID0gLSByb3QubjIxO1xuXG5cdHJldHVybiByb3Q7XG59O1xuXG5NYXRyaXg0Lm1ha2VJbnZlcnQgPSBmdW5jdGlvbihtMSkge1xuXHR2YXIgbTIgPSBuZXcgTWF0cml4NCgpO1xuXG5cdG0yLm4xMSA9IG0xLm4yMyptMS5uMzQqbTEubjQyIC0gbTEubjI0Km0xLm4zMyptMS5uNDIgKyBtMS5uMjQqbTEubjMyKm0xLm40MyAtIG0xLm4yMiptMS5uMzQqbTEubjQzIC0gbTEubjIzKm0xLm4zMiptMS5uNDQgKyBtMS5uMjIqbTEubjMzKm0xLm40NDtcblx0bTIubjEyID0gbTEubjE0Km0xLm4zMyptMS5uNDIgLSBtMS5uMTMqbTEubjM0Km0xLm40MiAtIG0xLm4xNCptMS5uMzIqbTEubjQzICsgbTEubjEyKm0xLm4zNCptMS5uNDMgKyBtMS5uMTMqbTEubjMyKm0xLm40NCAtIG0xLm4xMiptMS5uMzMqbTEubjQ0O1xuXHRtMi5uMTMgPSBtMS5uMTMqbTEubjI0Km0xLm40MiAtIG0xLm4xNCptMS5uMjMqbTEubjQyICsgbTEubjE0Km0xLm4yMiptMS5uNDMgLSBtMS5uMTIqbTEubjI0Km0xLm40MyAtIG0xLm4xMyptMS5uMjIqbTEubjQ0ICsgbTEubjEyKm0xLm4yMyptMS5uNDQ7XG5cdG0yLm4xNCA9IG0xLm4xNCptMS5uMjMqbTEubjMyIC0gbTEubjEzKm0xLm4yNCptMS5uMzIgLSBtMS5uMTQqbTEubjIyKm0xLm4zMyArIG0xLm4xMiptMS5uMjQqbTEubjMzICsgbTEubjEzKm0xLm4yMiptMS5uMzQgLSBtMS5uMTIqbTEubjIzKm0xLm4zNDtcblx0bTIubjIxID0gbTEubjI0Km0xLm4zMyptMS5uNDEgLSBtMS5uMjMqbTEubjM0Km0xLm40MSAtIG0xLm4yNCptMS5uMzEqbTEubjQzICsgbTEubjIxKm0xLm4zNCptMS5uNDMgKyBtMS5uMjMqbTEubjMxKm0xLm40NCAtIG0xLm4yMSptMS5uMzMqbTEubjQ0O1xuXHRtMi5uMjIgPSBtMS5uMTMqbTEubjM0Km0xLm40MSAtIG0xLm4xNCptMS5uMzMqbTEubjQxICsgbTEubjE0Km0xLm4zMSptMS5uNDMgLSBtMS5uMTEqbTEubjM0Km0xLm40MyAtIG0xLm4xMyptMS5uMzEqbTEubjQ0ICsgbTEubjExKm0xLm4zMyptMS5uNDQ7XG5cdG0yLm4yMyA9IG0xLm4xNCptMS5uMjMqbTEubjQxIC0gbTEubjEzKm0xLm4yNCptMS5uNDEgLSBtMS5uMTQqbTEubjIxKm0xLm40MyArIG0xLm4xMSptMS5uMjQqbTEubjQzICsgbTEubjEzKm0xLm4yMSptMS5uNDQgLSBtMS5uMTEqbTEubjIzKm0xLm40NDtcblx0bTIubjI0ID0gbTEubjEzKm0xLm4yNCptMS5uMzEgLSBtMS5uMTQqbTEubjIzKm0xLm4zMSArIG0xLm4xNCptMS5uMjEqbTEubjMzIC0gbTEubjExKm0xLm4yNCptMS5uMzMgLSBtMS5uMTMqbTEubjIxKm0xLm4zNCArIG0xLm4xMSptMS5uMjMqbTEubjM0O1xuXHRtMi5uMzEgPSBtMS5uMjIqbTEubjM0Km0xLm40MSAtIG0xLm4yNCptMS5uMzIqbTEubjQxICsgbTEubjI0Km0xLm4zMSptMS5uNDIgLSBtMS5uMjEqbTEubjM0Km0xLm40MiAtIG0xLm4yMiptMS5uMzEqbTEubjQ0ICsgbTEubjIxKm0xLm4zMiptMS5uNDQ7XG5cdG0yLm4zMiA9IG0xLm4xNCptMS5uMzIqbTEubjQxIC0gbTEubjEyKm0xLm4zNCptMS5uNDEgLSBtMS5uMTQqbTEubjMxKm0xLm40MiArIG0xLm4xMSptMS5uMzQqbTEubjQyICsgbTEubjEyKm0xLm4zMSptMS5uNDQgLSBtMS5uMTEqbTEubjMyKm0xLm40NDtcblx0bTIubjMzID0gbTEubjEzKm0xLm4yNCptMS5uNDEgLSBtMS5uMTQqbTEubjIyKm0xLm40MSArIG0xLm4xNCptMS5uMjEqbTEubjQyIC0gbTEubjExKm0xLm4yNCptMS5uNDIgLSBtMS5uMTIqbTEubjIxKm0xLm40NCArIG0xLm4xMSptMS5uMjIqbTEubjQ0O1xuXHRtMi5uMzQgPSBtMS5uMTQqbTEubjIyKm0xLm4zMSAtIG0xLm4xMiptMS5uMjQqbTEubjMxIC0gbTEubjE0Km0xLm4yMSptMS5uMzIgKyBtMS5uMTEqbTEubjI0Km0xLm4zMiArIG0xLm4xMiptMS5uMjEqbTEubjM0IC0gbTEubjExKm0xLm4yMiptMS5uMzQ7XG5cdG0yLm40MSA9IG0xLm4yMyptMS5uMzIqbTEubjQxIC0gbTEubjIyKm0xLm4zMyptMS5uNDEgLSBtMS5uMjMqbTEubjMxKm0xLm40MiArIG0xLm4yMSptMS5uMzMqbTEubjQyICsgbTEubjIyKm0xLm4zMSptMS5uNDMgLSBtMS5uMjEqbTEubjMyKm0xLm40Mztcblx0bTIubjQyID0gbTEubjEyKm0xLm4zMyptMS5uNDEgLSBtMS5uMTMqbTEubjMyKm0xLm40MSArIG0xLm4xMyptMS5uMzEqbTEubjQyIC0gbTEubjExKm0xLm4zMyptMS5uNDIgLSBtMS5uMTIqbTEubjMxKm0xLm40MyArIG0xLm4xMSptMS5uMzIqbTEubjQzO1xuXHRtMi5uNDMgPSBtMS5uMTMqbTEubjIyKm0xLm40MSAtIG0xLm4xMiptMS5uMjMqbTEubjQxIC0gbTEubjEzKm0xLm4yMSptMS5uNDIgKyBtMS5uMTEqbTEubjIzKm0xLm40MiArIG0xLm4xMiptMS5uMjEqbTEubjQzIC0gbTEubjExKm0xLm4yMiptMS5uNDM7XG5cdG0yLm40NCA9IG0xLm4xMiptMS5uMjMqbTEubjMxIC0gbTEubjEzKm0xLm4yMiptMS5uMzEgKyBtMS5uMTMqbTEubjIxKm0xLm4zMiAtIG0xLm4xMSptMS5uMjMqbTEubjMyIC0gbTEubjEyKm0xLm4yMSptMS5uMzMgKyBtMS5uMTEqbTEubjIyKm0xLm4zMztcblx0bTIuJHNjYWxlKDEgLyBtMS5kZXRlcm1pbmFudCgpKTtcblxuXHRyZXR1cm4gbTI7XG59O1xuXG5NYXRyaXg0Lm1ha2VGcnVzdHVtID0gZnVuY3Rpb24oIGxlZnQsIHJpZ2h0LCBib3R0b20sIHRvcCwgbmVhciwgZmFyICkge1xuXHR2YXIgbSwgeCwgeSwgYSwgYiwgYywgZDtcblxuXHRtID0gbmV3IE1hdHJpeDQoKTtcblx0eCA9IDIgKiBuZWFyIC8gKCByaWdodCAtIGxlZnQgKTtcblx0eSA9IDIgKiBuZWFyIC8gKCB0b3AgLSBib3R0b20gKTtcblx0YSA9ICggcmlnaHQgKyBsZWZ0ICkgLyAoIHJpZ2h0IC0gbGVmdCApO1xuXHRiID0gKCB0b3AgKyBib3R0b20gKSAvICggdG9wIC0gYm90dG9tICk7XG5cdGMgPSAtICggZmFyICsgbmVhciApIC8gKCBmYXIgLSBuZWFyICk7XG5cdGQgPSAtIDIgKiBmYXIgKiBuZWFyIC8gKCBmYXIgLSBuZWFyICk7XG5cblx0bS5uMTEgPSB4OyAgbS5uMTIgPSAwOyAgbS5uMTMgPSBhOyAgIG0ubjE0ID0gMDtcblx0bS5uMjEgPSAwOyAgbS5uMjIgPSB5OyAgbS5uMjMgPSBiOyAgIG0ubjI0ID0gMDtcblx0bS5uMzEgPSAwOyAgbS5uMzIgPSAwOyAgbS5uMzMgPSBjOyAgIG0ubjM0ID0gZDtcblx0bS5uNDEgPSAwOyAgbS5uNDIgPSAwOyAgbS5uNDMgPSAtIDE7IG0ubjQ0ID0gMDtcblxuXHRyZXR1cm4gbTtcbn07XG5cbk1hdHJpeDQubWFrZVBlcnNwZWN0aXZlID0gZnVuY3Rpb24oIGZvdiwgYXNwZWN0LCBuZWFyLCBmYXIgKSB7XG5cdHZhciB5bWF4LCB5bWluLCB4bWluLCB4bWF4O1xuXG5cdHltYXggPSBuZWFyICogTWF0aC50YW4oIGZvdiAqIE1hdGguUEkgLyAzNjAgKTtcblx0eW1pbiA9IC0geW1heDtcblx0eG1pbiA9IHltaW4gKiBhc3BlY3Q7XG5cdHhtYXggPSB5bWF4ICogYXNwZWN0O1xuXG5cdHJldHVybiBNYXRyaXg0Lm1ha2VGcnVzdHVtKCB4bWluLCB4bWF4LCB5bWluLCB5bWF4LCBuZWFyLCBmYXIgKTtcbn07XG5cbk1hdHJpeDQubWFrZU9ydGhvID0gZnVuY3Rpb24oIGxlZnQsIHJpZ2h0LCB0b3AsIGJvdHRvbSwgbmVhciwgZmFyICkge1xuXHR2YXIgbSwgeCwgeSwgeiwgdywgaCwgcDtcblxuXHRtID0gbmV3IE1hdHJpeDQoKTtcblx0dyA9IHJpZ2h0IC0gbGVmdDtcblx0aCA9IGJvdHRvbSAtIHRvcDtcblx0cCA9IGZhciAtIG5lYXI7XG5cdHggPSAoIHJpZ2h0ICsgbGVmdCApIC8gdztcblx0eSA9ICggYm90dG9tICsgdG9wICkgLyBoO1xuXHR6ID0gKCBmYXIgKyBuZWFyICkgLyBwO1xuXG5cdG0ubjExID0gMiAvIHc7IG0ubjEyID0gMDsgICAgIG0ubjEzID0gMDsgICAgICBtLm4xNCA9IC14O1xuXHRtLm4yMSA9IDA7ICAgICBtLm4yMiA9IDIgLyBoOyBtLm4yMyA9IDA7ICAgICAgbS5uMjQgPSAteTtcblx0bS5uMzEgPSAwOyAgICAgbS5uMzIgPSAwOyAgICAgbS5uMzMgPSAtMiAvIHA7IG0ubjM0ID0gLXo7XG5cdG0ubjQxID0gMDsgICAgIG0ubjQyID0gMDsgICAgIG0ubjQzID0gMDsgICAgICBtLm40NCA9IDE7XG5cblx0cmV0dXJuIG07XG59O1xuXG5cbi8qXG4gKiBDYW1lcmEgY2xhc3MgYmFzZWQgb24gdGhyZWUuanMgaHR0cDovL2dpdGh1Yi5jb20vbXJkb29iL3RocmVlLmpzLCBDb3B5cmlnaHQgKGMpIE1yLmRvb2IgaHR0cDovL21yZG9vYi5jb20vLCBNSVQgTGljZW5zZSBodHRwOi8vZ2l0aHViLmNvbS9tcmRvb2IvdGhyZWUuanMvYmxvYi9tYXN0ZXIvTElDRU5TRSBcbiAqL1xuXG52YXIgQ2FtZXJhID0gZnVuY3Rpb24gKGZvdiwgYXNwZWN0LCBuZWFyLCBmYXIpIHtcblx0dGhpcy5wcm9qZWN0aW9uTWF0cml4ID0gTWF0cml4NC5tYWtlUGVyc3BlY3RpdmUoZm92LCBhc3BlY3QsIG5lYXIsIGZhcik7XG59O1xuXG5DYW1lcmEucHJvdG90eXBlID0ge1xuICBwb3NpdGlvbjogbmV3IFZlY3RvcjMsXG4gIHRhcmdldDoge1xuICAgIHBvc2l0aW9uOiBuZXcgVmVjdG9yM1xuICB9LFxuICB1cDogbmV3IFZlY3RvcjMoMCwgMSwgMCksXG4gIG1hdHJpeDogbmV3IE1hdHJpeDQsXG4gIFxuICB1cGRhdGVNYXRyaXg6IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMubWF0cml4Lmxvb2tBdCh0aGlzLnBvc2l0aW9uLCB0aGlzLnRhcmdldC5wb3NpdGlvbiwgdGhpcy51cCk7XG4gIH1cbn07XG5cblxuQ2FudmFzLkJhc2VbJzNEJ10gPSBuZXcgQ2xhc3Moe1xuICBJbXBsZW1lbnRzOiBDYW52YXMuQmFzZVsnMkQnXSxcbiAgXG4gIHByb2dyYW06IG51bGwsXG4gIGNhbWVyYTogbnVsbCxcbiAgXG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uKHZpeikge1xuICAgIHRoaXMudml6ID0gdml6O1xuICAgIHRoaXMub3B0ID0gdml6LmNvbmZpZztcbiAgICB0aGlzLnNpemUgPSBmYWxzZTtcbiAgICB0aGlzLmNyZWF0ZUNhbnZhcygpO1xuICAgIHRoaXMuaW5pdFdlYkdMKCk7XG4gICAgdGhpcy5pbml0Q2FtZXJhKCk7XG4gIH0sXG4gIFxuICBpbml0V2ViR0w6IGZ1bmN0aW9uKCkge1xuICAgIC8vaW5pdGlhbGl6ZSBjb250ZXh0XG4gICAgdmFyIGdsID0gdGhpcy5nZXRDdHgoKTtcbiAgICAvL2dldCB2aWV3cG9ydCBzaXplXG4gICAgdmFyIHNpemUgPSB0aGlzLmdldFNpemUoKTtcbiAgICAvL2NvbXBpbGUgYW5kIGdldCBzaGFkZXJzXG4gICAgdmFyIGZyYWdtZW50U2hhZGVyID0gdGhpcy5nZXRTaGFkZXIoQ2FudmFzLkJhc2VbJzNEJ10uRnJhZ21lbnRTaGFkZXIsIGdsLkZSQUdNRU5UX1NIQURFUik7XG4gICAgdmFyIHZlcnRleFNoYWRlciA9IHRoaXMuZ2V0U2hhZGVyKENhbnZhcy5CYXNlWyczRCddLlZlcnRleFNoYWRlciwgZ2wuVkVSVEVYX1NIQURFUik7XG4gICAgLy9jcmVhdGUgcHJvZ3JhbSBhbmQgbGluayBzaGFkZXJzXG4gICAgdmFyIHByb2dyYW0gPSBnbC5jcmVhdGVQcm9ncmFtKCk7XG4gICAgZ2wuYXR0YWNoU2hhZGVyKHByb2dyYW0sIHZlcnRleFNoYWRlcik7XG4gICAgZ2wuYXR0YWNoU2hhZGVyKHByb2dyYW0sIGZyYWdtZW50U2hhZGVyKTtcbiAgICBnbC5saW5rUHJvZ3JhbShwcm9ncmFtKTtcbiAgICBpZiAoIWdsLmdldFByb2dyYW1QYXJhbWV0ZXIocHJvZ3JhbSwgZ2wuTElOS19TVEFUVVMpKSB7XG4gICAgICB0aHJvdyBcIkNvdWxkIG5vdCBsaW5rIHNoYWRlcnNcIjtcbiAgICB9XG4gICAgZ2wudXNlUHJvZ3JhbShwcm9ncmFtKTtcbiAgICAvL2JpbmQgbmFtZSB0byB2YXJpYWJsZSBsb2NhdGlvbiBpbiBzaGFkZXJzXG4gICAgJC5leHRlbmQocHJvZ3JhbSwge1xuICAgICAgJ3ZpZXdNYXRyaXgnOiBnbC5nZXRVbmlmb3JtTG9jYXRpb24ocHJvZ3JhbSwgJ3ZpZXdNYXRyaXgnKSxcbiAgICAgICdwcm9qZWN0aW9uTWF0cml4JzogZ2wuZ2V0VW5pZm9ybUxvY2F0aW9uKHByb2dyYW0sICdwcm9qZWN0aW9uTWF0cml4JyksXG4gICAgICAnbm9ybWFsTWF0cml4JzogZ2wuZ2V0VW5pZm9ybUxvY2F0aW9uKHByb2dyYW0sICdub3JtYWxNYXRyaXgnKSxcbiAgICAgICdjb2xvcic6IGdsLmdldFVuaWZvcm1Mb2NhdGlvbihwcm9ncmFtLCAnY29sb3InKSxcbiAgICAgIFxuICAgICAgJ2VuYWJsZUxpZ2h0aW5nJzogZ2wuZ2V0VW5pZm9ybUxvY2F0aW9uKHByb2dyYW0sICdlbmFibGVMaWdodGluZycpLFxuICAgICAgJ2FtYmllbnRDb2xvcic6IGdsLmdldFVuaWZvcm1Mb2NhdGlvbihwcm9ncmFtLCAnYW1iaWVudENvbG9yJyksXG4gICAgICAnZGlyZWN0aW9uYWxDb2xvcic6IGdsLmdldFVuaWZvcm1Mb2NhdGlvbihwcm9ncmFtLCAnZGlyZWN0aW9uYWxDb2xvcicpLFxuICAgICAgJ2xpZ2h0aW5nRGlyZWN0aW9uJzogZ2wuZ2V0VW5pZm9ybUxvY2F0aW9uKHByb2dyYW0sICdsaWdodGluZ0RpcmVjdGlvbicpLFxuICAgICAgXG4gICAgICAncG9zaXRpb24nOiBnbC5nZXRBdHRyaWJMb2NhdGlvbihwcm9ncmFtLCAncG9zaXRpb24nKSxcbiAgICAgICdub3JtYWwnOiBnbC5nZXRBdHRyaWJMb2NhdGlvbihwcm9ncmFtLCAnbm9ybWFsJyksXG4gICAgfSk7XG4gICAgZ2wuZW5hYmxlVmVydGV4QXR0cmliQXJyYXkocHJvZ3JhbS5wb3NpdGlvbik7XG4gICAgZ2wuZW5hYmxlVmVydGV4QXR0cmliQXJyYXkocHJvZ3JhbS5ub3JtYWwpO1xuICAgIHRoaXMucHJvZ3JhbSA9IHByb2dyYW07XG4gICAgLy9zZXQgZ2VuZXJhbCByZW5kZXJpbmcgb3B0aW9uc1xuICAgIGdsLmNsZWFyQ29sb3IoMCwgMCwgMCwgMCk7XG4gICAgZ2wuY2xlYXJEZXB0aCgxKTtcblxuICAgIGdsLmVuYWJsZShnbC5ERVBUSF9URVNUKTtcbiAgICBnbC5kZXB0aEZ1bmMoZ2wuTEVRVUFMKTtcblxuICAgIGdsLmVuYWJsZShnbC5CTEVORCk7XG4gICAgZ2wuYmxlbmRGdW5jKGdsLlNSQ19BTFBIQSwgZ2wuT05FX01JTlVTX1NSQ19BTFBIQSk7XG4gICAgXG4gICAgZ2wudmlld3BvcnQoMCwgMCwgc2l6ZS53aWR0aCwgc2l6ZS5oZWlnaHQpO1xuICB9LFxuICBcbiAgaW5pdENhbWVyYTogZnVuY3Rpb24oKSB7XG4gICAgdmFyIHNpemUgPSB0aGlzLmdldFNpemUoKTtcbiAgICB2YXIgY2FtZXJhID0gbmV3IENhbWVyYSg3NSwgc2l6ZS53aWR0aCAvIHNpemUuaGVpZ2h0LCAxLCAxMDAwKTtcbiAgICBjYW1lcmEucG9zaXRpb24ueiA9IDUwMDtcbiAgICB0aGlzLmNhbWVyYSA9IGNhbWVyYTtcbiAgfSxcbiAgXG4gIGdldFNoYWRlcjogZnVuY3Rpb24oc3JjLCB0eXBlKSB7XG4gICAgdmFyIGdsID0gdGhpcy5jdHg7XG4gICAgdmFyIHNoYWRlciA9IGdsLmNyZWF0ZVNoYWRlcih0eXBlKTtcbiAgICBnbC5zaGFkZXJTb3VyY2Uoc2hhZGVyLCBzcmMpO1xuICAgIGdsLmNvbXBpbGVTaGFkZXIoc2hhZGVyKTtcbiAgICBpZiAoIWdsLmdldFNoYWRlclBhcmFtZXRlcihzaGFkZXIsIGdsLkNPTVBJTEVfU1RBVFVTKSkge1xuICAgICAgdmFyIGluZm8gPSBnbC5nZXRTaGFkZXJJbmZvTG9nKHNoYWRlcik7XG4gICAgICB0aHJvdyBcIkNvdWxkIG5vdCBjb21waWxlIHNoYWRlciBzcmM6IFwiICsgaW5mbztcbiAgICB9XG4gICAgcmV0dXJuIHNoYWRlcjtcbiAgfSxcblxuICBnZXRDdHg6IGZ1bmN0aW9uKCkge1xuICAgIGlmKCF0aGlzLmN0eCkgXG4gICAgICByZXR1cm4gdGhpcy5jdHggPSB0aGlzLmNhbnZhcy5nZXRDb250ZXh0KCdleHBlcmltZW50YWwtd2ViZ2wnKTtcbiAgICByZXR1cm4gdGhpcy5jdHg7XG4gIH0sXG4gIFxuICByZXNpemU6IGZ1bmN0aW9uKHdpZHRoLCBoZWlnaHQpIHtcbiAgICB2YXIgc2l6ZSA9IHRoaXMuZ2V0U2l6ZSgpLFxuICAgICAgICBjYW52YXMgPSB0aGlzLmNhbnZhcyxcbiAgICAgICAgc3R5bGVzID0gY2FudmFzLnN0eWxlLFxuICAgICAgICBnbCA9IHRoaXMuZ2V0Q3R4KCk7XG4gICAgdGhpcy5zaXplID0gZmFsc2U7XG4gICAgY2FudmFzLndpZHRoID0gd2lkdGg7XG4gICAgY2FudmFzLmhlaWdodCA9IGhlaWdodDtcbiAgICBzdHlsZXMud2lkdGggPSB3aWR0aCArIFwicHhcIjtcbiAgICBzdHlsZXMuaGVpZ2h0ID0gaGVpZ2h0ICsgXCJweFwiO1xuICAgIGdsLnZpZXdwb3J0KDAsIDAsIHdpZHRoLCBoZWlnaHQpO1xuXG4gICAgdGhpcy50cmFuc2xhdGVPZmZzZXRYID1cbiAgICAgIHRoaXMudHJhbnNsYXRlT2Zmc2V0WSA9IDA7XG4gICAgdGhpcy5zY2FsZU9mZnNldFggPSBcbiAgICAgIHRoaXMuc2NhbGVPZmZzZXRZID0gMTtcbiAgICB0aGlzLmNsZWFyKCk7XG4gICAgdGhpcy52aXoucmVzaXplKHdpZHRoLCBoZWlnaHQsIHRoaXMpO1xuICB9LFxuXG4gIHRyYW5zbGF0ZVRvQ2VudGVyOiAkLmVtcHR5LFxuICBzY2FsZTogJC5lbXB0eSxcbiAgXG4gIHRyYW5zbGF0ZTogZnVuY3Rpb24oeCwgeSwgeiwgZGlzYWJsZVBsb3QpIHtcbiAgICB2YXIgc3ggPSB0aGlzLnNjYWxlT2Zmc2V0WCxcbiAgICAgICAgc3kgPSB0aGlzLnNjYWxlT2Zmc2V0WTtcbiAgICB0aGlzLnRyYW5zbGF0ZU9mZnNldFggKz0geCpzeDtcbiAgICB0aGlzLnRyYW5zbGF0ZU9mZnNldFkgKz0geSpzeTtcbiAgICB2YXIgcG9zID0gdGhpcy5jYW1lcmEucG9zaXRpb247XG4gICAgcG9zLnggKz0geDtcbiAgICBwb3MueSArPSB5O1xuICAgIHBvcy56ICs9IHo7XG4gICAgIWRpc2FibGVQbG90ICYmIHRoaXMucGxvdCgpO1xuICB9LFxuXG4gIGNsZWFyOiBmdW5jdGlvbigpe1xuICAgIHZhciBnbCA9IHRoaXMuZ2V0Q3R4KCk7XG4gICAgZ2wuY2xlYXIoZ2wuQ09MT1JfQlVGRkVSX0JJVCB8IGdsLkRFUFRIX0JVRkZFUl9CSVQpO1xuICAgIC8vVE9ETyhuaWNvKSBpcyB0aGlzIE9LPyBJIG1lYW4sIHRvIHB1dCB0aGlzIGxpbmUgaGVyZS5cbiAgICB0aGlzLmNhbWVyYS51cGRhdGVNYXRyaXgoKTtcbiAgfSxcbiAgXG4gIHBsb3Q6IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMuY2xlYXIoKTtcbiAgICB0aGlzLnZpei5wbG90KHRoaXMpO1xuICB9XG59KTtcblxuLy9TaGFkZXJzIGNvZGVcbkNhbnZhcy5CYXNlWyczRCddLkZyYWdtZW50U2hhZGVyID0gW1xuICBcIiNpZmRlZiBHTF9FU1wiLFxuICBcInByZWNpc2lvbiBoaWdocCBmbG9hdDtcIixcbiAgXCIjZW5kaWZcIixcbiAgXG4gIFwidmFyeWluZyB2ZWM0IHZjb2xvcjtcIixcbiAgXCJ2YXJ5aW5nIHZlYzMgbGlnaHRXZWlnaHRpbmc7XCIsXG4gIFxuICBcInZvaWQgbWFpbigpe1wiLFxuICBcbiAgICBcImdsX0ZyYWdDb2xvciA9IHZlYzQodmNvbG9yLnJnYiAqIGxpZ2h0V2VpZ2h0aW5nLCB2Y29sb3IuYSk7XCIsXG4gIFxuICBcIn1cIlxuXS5qb2luKFwiXFxuXCIpO1xuXG5DYW52YXMuQmFzZVsnM0QnXS5WZXJ0ZXhTaGFkZXIgPSBbXG4gIFwiYXR0cmlidXRlIHZlYzMgcG9zaXRpb247XCIsXG4gIFwiYXR0cmlidXRlIHZlYzMgbm9ybWFsO1wiLFxuICBcInVuaWZvcm0gdmVjNCBjb2xvcjtcIixcbiAgXG4gIFwidW5pZm9ybSBtYXQ0IHZpZXdNYXRyaXg7XCIsXG4gIFwidW5pZm9ybSBtYXQ0IHByb2plY3Rpb25NYXRyaXg7XCIsXG4gIFwidW5pZm9ybSBtYXQ0IG5vcm1hbE1hdHJpeDtcIixcblxuICBcInVuaWZvcm0gYm9vbCBlbmFibGVMaWdodGluZztcIixcbiAgXCJ1bmlmb3JtIHZlYzMgYW1iaWVudENvbG9yO1wiLFxuICBcInVuaWZvcm0gdmVjMyBkaXJlY3Rpb25hbENvbG9yO1wiLFxuICBcInVuaWZvcm0gdmVjMyBsaWdodGluZ0RpcmVjdGlvbjtcIixcbiAgXG4gIFwidmFyeWluZyB2ZWM0IHZjb2xvcjtcIixcbiAgXCJ2YXJ5aW5nIHZlYzMgbGlnaHRXZWlnaHRpbmc7XCIsXG4gIFxuICBcInZvaWQgbWFpbih2b2lkKSB7XCIsXG4gIFxuICAgIFwiaWYoIWVuYWJsZUxpZ2h0aW5nKSB7XCIsXG4gICAgICBcImxpZ2h0V2VpZ2h0aW5nID0gdmVjMygxLjAsIDEuMCwgMS4wKTtcIixcbiAgICBcIn0gZWxzZSB7XCIsXG4gICAgICBcInZlYzQgdHJhbnNmb3JtZWROb3JtYWwgPSBub3JtYWxNYXRyaXggKiB2ZWM0KG5vcm1hbCwgMS4wKTtcIixcbiAgICAgIFwiZmxvYXQgZGlyZWN0aW9uYWxMaWdodFdlaWdodGluZyA9IG1heChkb3QodHJhbnNmb3JtZWROb3JtYWwueHl6LCBsaWdodGluZ0RpcmVjdGlvbiksIDAuMCk7XCIsXG4gICAgICBcImxpZ2h0V2VpZ2h0aW5nID0gYW1iaWVudENvbG9yICsgZGlyZWN0aW9uYWxDb2xvciAqIGRpcmVjdGlvbmFsTGlnaHRXZWlnaHRpbmc7XCIsXG4gICAgXCJ9XCIsXG4gICAgXG4gICAgXCJ2Y29sb3IgPSBjb2xvcjtcIixcbiAgICBcImdsX1Bvc2l0aW9uID0gcHJvamVjdGlvbk1hdHJpeCAqIHZpZXdNYXRyaXggKiB2ZWM0KCBwb3NpdGlvbiwgMS4wICk7XCIsXG4gIFxuICBcIn1cIlxuXS5qb2luKFwiXFxuXCIpO1xuXG4vKlxuICogU29tZSBvZiB0aGUgZ2VvbWV0cmllcyB3aGVyZSBpbnNwaXJlZCBieSB0aHJlZS5qcyBodHRwOi8vZ2l0aHViLmNvbS9tcmRvb2IvdGhyZWUuanMsIENvcHlyaWdodCAoYykgTXIuZG9vYiBodHRwOi8vbXJkb29iLmNvbS8sIE1JVCBMaWNlbnNlIGh0dHA6Ly9naXRodWIuY29tL21yZG9vYi90aHJlZS5qcy9ibG9iL21hc3Rlci9MSUNFTlNFIFxuICovXG5cbnZhciBPM0QgPSB7fTtcblxuJGppdC5PM0QgPSBPM0Q7XG5cbk8zRC5iYXNlID0gbmV3IENsYXNzKHtcbiAgLy9hcnJheSBvZiB7IHgsIHksIHogfSBvZiBmbG9hdFxuICB2ZXJ0aWNlczogW10sXG4gIC8vYXJyYXkgb2YgeyBhLCBiLCBjLCBkPyB9IG9mIGludFxuICBmYWNlczogW10sXG4gIC8vdXBkYXRlZCBvbiBwbG90Tm9kZS9wbG90RWRnZVxuICBwb3NpdGlvbjogbmV3IFZlY3RvcjMsXG4gIHJvdGF0aW9uOiBuZXcgVmVjdG9yMyxcbiAgc2NhbGU6IG5ldyBWZWN0b3IzKDEsIDEsIDEpLFxuICAvL2ludHJpbnNpYyBjb29yZGluYXRlc1xuICBtYXRyaXg6IG5ldyBNYXRyaXg0LFxuICBcbiAgdXBkYXRlOiBmdW5jdGlvbihlbGVtKSB7XG4gICAgaWYoZWxlbS5ub2RlRnJvbSAmJiBlbGVtLm5vZGVUbykge1xuICAgICAgdGhpcy51cGRhdGVFZGdlKGVsZW0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnVwZGF0ZU5vZGUoZWxlbSk7XG4gICAgfVxuICB9LFxuICBcbiAgdXBkYXRlTm9kZTogJC5lbXB0eSxcbiAgXG4gIHVwZGF0ZUVkZ2U6IGZ1bmN0aW9uKGVsZW0pIHtcbiAgICB0aGlzLnVwZGF0ZU5vZGUoZWxlbSk7XG4gIH0sXG5cbiAgdXBkYXRlTWF0cml4OiBmdW5jdGlvbigpIHtcbiAgICB2YXIgcG9zID0gdGhpcy5wb3NpdGlvbixcbiAgICAgICAgcm90ID0gdGhpcy5yb3RhdGlvbixcbiAgICAgICAgc2NhbGUgPSB0aGlzLnNjYWxlLFxuICAgICAgICBtYXRyaXggPSB0aGlzLm1hdHJpeDtcbiAgICBcbiAgICBtYXRyaXguaWRlbnRpdHkoKTtcbiAgXG4gICAgbWF0cml4LiRtdWx0aXBseSggTWF0cml4NC50cmFuc2xhdGlvbk1hdHJpeCggcG9zLngsIHBvcy55LCBwb3MueiApICk7XG4gICAgbWF0cml4LiRtdWx0aXBseSggTWF0cml4NC5yb3RhdGlvblhNYXRyaXgoIHJvdC54ICkgKTtcbiAgICBtYXRyaXguJG11bHRpcGx5KCBNYXRyaXg0LnJvdGF0aW9uWU1hdHJpeCggcm90LnkgKSApO1xuICAgIG1hdHJpeC4kbXVsdGlwbHkoIE1hdHJpeDQucm90YXRpb25aTWF0cml4KCByb3QueiApICk7XG4gICAgbWF0cml4LiRtdWx0aXBseSggTWF0cml4NC5zY2FsZU1hdHJpeCggc2NhbGUueCwgc2NhbGUueSwgc2NhbGUueiApICk7XG4gIH0sXG4gIC8vY29tcHV0ZSBmYWNlcyBub3JtYWxzXG4gIGNvbXB1dGVOb3JtYWxzOiBmdW5jdGlvbiAoKSB7XG4gICAgZm9yICh2YXIgZj0wLCB2cz10aGlzLnZlcnRpY2VzLCBmcz10aGlzLmZhY2VzLCBsZW49ZnMubGVuZ3RoOyBmIDwgbGVuOyBmKyspIHtcbiAgICAgIHZhciB2YSA9IHZzW2ZzW2ZdLmFdLFxuICAgICAgICAgIHZiID0gdnNbZnNbZl0uYl0sXG4gICAgICAgICAgdmMgPSB2c1tmc1tmXS5jXSxcbiAgICAgICAgICBjYiA9IG5ldyBWZWN0b3IzLFxuICAgICAgICAgIGFiID0gbmV3IFZlY3RvcjM7XG4gICAgICBcbiAgICAgIGNiLnN1Yih2YywgdmIpO1xuICAgICAgYWIuc3ViKHZhLCB2Yik7XG4gICAgICBjYi4kY3Jvc3MoYWIpO1xuXG4gICAgICBpZiAoIWNiLmlzWmVybygpKSBjYi5ub3JtYWxpemUoKTtcbiAgICAgIFxuICAgICAgZnNbZl0ubm9ybWFsID0gY2I7XG4gICAgfVxuICB9XG59KTtcblxuLy9Jc29DdWJlXG5mdW5jdGlvbiBJc29DdWJlKCkge1xuICB2YXIgdnMgPSB0aGlzLnZlcnRpY2VzLFxuICAgICAgZjQgPSB0aGlzLmZhY2VzLFxuICAgICAgdnNwID0gZnVuY3Rpb24oeCwgeSwgeikgeyB2cy5wdXNoKHsgeDogeCwgeTogeSwgejogeiB9KTsgfSxcbiAgICAgIGY0cCA9IGZ1bmN0aW9uKGEsIGIsIGMsIGQpIHsgZjQucHVzaCh7IGE6IGEsIGI6IGIsIGM6IGMsIGQ6IGQgfSk7IH07XG4gIFxuICB2c3AoIDEsICAxLCAtMSk7XG4gIHZzcCggMSwgLTEsIC0xKTtcbiAgdnNwKC0xLCAtMSwgLTEpO1xuICB2c3AoLTEsICAxLCAtMSk7XG4gIHZzcCggMSwgIDEsICAxKTtcbiAgdnNwKCAxLCAtMSwgIDEpO1xuICB2c3AoLTEsIC0xLCAgMSk7XG4gIHZzcCgtMSwgIDEsICAxKTtcbiAgXG4gIGY0cCgwLCAxLCAyLCAzKTtcbiAgZjRwKDQsIDcsIDYsIDUpO1xuICBmNHAoMCwgNCwgNSwgMSk7XG4gIGY0cCgxLCA1LCA2LCAyKTtcbiAgZjRwKDIsIDYsIDcsIDMpO1xuICBmNHAoNCwgMCwgMywgNyk7XG59XG5cbi8vQ3ViZVxuTzNELmN1YmUgPSBuZXcgQ2xhc3Moe1xuICBJbXBsZW1lbnRzOiBPM0QuYmFzZSxcbiAgXG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uKCkge1xuICAgIElzb0N1YmUuY2FsbCh0aGlzKTtcbiAgICB0aGlzLmNvbXB1dGVOb3JtYWxzKCk7XG4gIH0sXG4gIFxuICB1cGRhdGVOb2RlOiBmdW5jdGlvbihvYmopIHtcbiAgICB2YXIgZGltID0gb2JqLmdldERhdGEoJ2RpbScpLFxuICAgICAgICBwb3MgPSBvYmoucG9zO1xuICAgIFxuICAgIHRoaXMucG9zaXRpb24uc2V0Yyhwb3MueCwgcG9zLnksIHBvcy56KTtcbiAgICB0aGlzLnNjYWxlLnNldGMoZGltLCBkaW0sIGRpbSk7XG4gICAgdGhpcy51cGRhdGVNYXRyaXgoKTtcbiAgfVxufSk7XG5cbk8zRC5zcGhlcmUgPSBuZXcgQ2xhc3Moe1xuICBJbXBsZW1lbnRzOiBPM0QuYmFzZSxcbiAgXG4gIHJhZGl1czogMSxcbiAgc2VnbWVudHNfd2lkdGg6IDEwLFxuICBzZWdtZW50c19oZWlnaHQ6IDEwLFxuICBcbiAgaW5pdGlhbGl6ZTogZnVuY3Rpb24oKSB7XG4gICAgdmFyIHJhZGl1cyA9IHRoaXMucmFkaXVzLFxuICAgICAgICBzZWdtZW50c193aWR0aCA9IHRoaXMuc2VnbWVudHNfd2lkdGgsXG4gICAgICAgIHNlZ21lbnRzX2hlaWdodCA9IHRoaXMuc2VnbWVudHNfaGVpZ2h0LFxuICAgICAgICBncmlkWCA9IHNlZ21lbnRzX3dpZHRoIHx8IDgsXG4gICAgICAgIGdyaWRZID0gc2VnbWVudHNfaGVpZ2h0IHx8IDYsXG4gICAgICAgIGNvcyA9IE1hdGguY29zLFxuICAgICAgICBzaW4gPSBNYXRoLnNpbixcbiAgICAgICAgbWF4ID0gTWF0aC5tYXgsXG4gICAgICAgIHBpID0gTWF0aC5QSTtcbiAgXG4gICAgdmFyIGlIb3IgPSBtYXgoMywgZ3JpZFgpLFxuICAgICAgICBpVmVyID0gbWF4KDIsIGdyaWRZKSxcbiAgICAgICAgYVZ0YyA9IFtdO1xuICBcbiAgICBmb3IodmFyIGo9MDsgaiA8IChpVmVyICsgMSkgOyBqKyspIHtcbiAgICAgIHZhciBmUmFkMSA9IGogLyBpVmVyLFxuICAgICAgICAgIGZaID0gcmFkaXVzICogY29zKGZSYWQxICogcGkpLFxuICAgICAgICAgIGZSZHMgPSByYWRpdXMgKiBzaW4oZlJhZDEgKiBwaSksXG4gICAgICAgICAgYVJvdyA9IFtdLFxuICAgICAgICAgIG9WdHggPSAwO1xuXG4gICAgICBmb3IodmFyIGk9MDsgaTxpSG9yOyBpKyspIHtcbiAgICAgICAgdmFyIGZSYWQyID0gMiAqIGkgLyBpSG9yLFxuICAgICAgICAgICAgZlggPSBmUmRzICogTWF0aC5zaW4oZlJhZDIgKiBwaSksXG4gICAgICAgICAgICBmWSA9IGZSZHMgKiBNYXRoLmNvcyhmUmFkMiAqIHBpKTtcbiAgICAgICAgaWYgKCEoKCBqID09IDAgfHwgaiA9PSBpVmVyKSAmJiBpID4gMCkpIHtcbiAgICAgICAgICBvVnR4ID0gdGhpcy52ZXJ0aWNlcy5wdXNoKHsgeDogZlksIHk6IGZaLCB6OiBmWH0pIC0gMTtcbiAgICAgICAgfVxuICAgICAgICBhUm93LnB1c2gob1Z0eCk7XG4gICAgICB9XG4gICAgICBhVnRjLnB1c2goYVJvdyk7XG4gICAgfVxuICBcbiAgICB2YXIgaVZlck51bSA9IGFWdGMubGVuZ3RoO1xuICAgIGZvciAodmFyIGo9MDsgajxpVmVyTnVtOyBqKyspIHtcbiAgICAgIHZhciBpSG9yTnVtID0gYVZ0Y1tqXS5sZW5ndGg7XG4gICAgICBpZiAoaiA+IDApIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGk8aUhvck51bTsgaSsrICkge1xuICAgICAgICAgIHZhciBiRW5kID0gaSA9PSAoIGlIb3JOdW0gLSAxICk7XG4gICAgICAgICAgdmFyIGFQMSA9IGFWdGNbal1bIGJFbmQgPyAwIDogaSArIDEgXTtcbiAgICAgICAgICB2YXIgYVAyID0gYVZ0Y1tqXVsgKCBiRW5kID8gaUhvck51bSAtIDEgOiBpICkgXTtcbiAgICAgICAgICB2YXIgYVAzID0gYVZ0Y1tqIC0xXVsgKCBiRW5kID8gaUhvck51bSAtIDEgOiBpICkgXTtcbiAgICAgICAgICB2YXIgYVA0ID0gYVZ0Y1tqIC0xXVsgYkVuZCA/IDAgOiBpICsgMSBdO1xuICBcbiAgICAgICAgICBpZihqIDwgKCBhVnRjLmxlbmd0aCAtIDEpKSB7XG4gICAgICAgICAgICB0aGlzLmZhY2VzLnB1c2goeyBhOiBhUDEsIGI6IGFQMiwgYzogYVAzIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZihqID4gMSkge1xuICAgICAgICAgICAgdGhpcy5mYWNlcy5wdXNoKHsgYTogYVAxLCBiOiBhUDMsIGM6IGFQNCB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5jb21wdXRlTm9ybWFscygpO1xuICB9LFxuICBcbiAgdXBkYXRlTm9kZTogZnVuY3Rpb24ob2JqKSB7XG4gICAgdmFyIGRpbSA9IG9iai5nZXREYXRhKCdkaW0nKSxcbiAgICAgICAgcG9zID0gb2JqLnBvcztcbiAgICBcbiAgICB0aGlzLnBvc2l0aW9uLnNldGMocG9zLngsIHBvcy55LCBwb3Mueik7XG4gICAgdGhpcy5zY2FsZS5zZXRjKGRpbSwgZGltLCBkaW0pO1xuICAgIHRoaXMudXBkYXRlTWF0cml4KCk7XG4gIH1cblxufSk7XG5cblxuTzNELnR1YmUgPSBuZXcgQ2xhc3Moe1xuICBJbXBsZW1lbnRzOiBPM0QuYmFzZSxcbiAgXG4gIG51bVNlZ3M6IDEwLFxuICBkaW06IDEsXG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uKCkge1xuICAgIHZhciB2cyA9IHRoaXMudmVydGljZXMsXG4gICAgICAgIGY0ID0gdGhpcy5mYWNlcyxcbiAgICAgICAgdnNwID0gZnVuY3Rpb24oeCwgeSwgeikgeyB2cy5wdXNoKHsgeDogeCwgeTogeSwgejogeiB9KTsgfSxcbiAgICAgICAgZjRwID0gZnVuY3Rpb24oYSwgYiwgYywgZCkgeyBmNC5wdXNoKHsgYTogYSwgYjogYiwgYzogYywgZDogZCB9KTsgfTtcblxuICAgIHZhciBzY29wZSA9IHRoaXMsXG4gICAgICAgIHNpbiA9IE1hdGguc2luLFxuICAgICAgICBjb3MgPSBNYXRoLmNvcyxcbiAgICAgICAgcGkgPSBNYXRoLlBJLFxuICAgICAgICBwaTIgPSBwaSAqIDIsXG4gICAgICAgIG51bVNlZ3MgPSB0aGlzLm51bVNlZ3MsXG4gICAgICAgIHRvcFJhZCA9IHRoaXMuZGltLFxuICAgICAgICBib3RSYWQgPSB0aGlzLmRpbTtcbiAgXG4gICAgLy8gVG9wIGNpcmNsZSB2ZXJ0aWNlc1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbnVtU2VnczsgaSsrKSB7XG4gICAgICB2c3Aoc2luKHBpMiAqIGkgLyBudW1TZWdzKSAqIHRvcFJhZCwgY29zKHBpMiAqIGkgLyBudW1TZWdzKSAqIHRvcFJhZCwgLTAuNSk7XG4gICAgfVxuICAgIC8vIEJvdHRvbSBjaXJjbGUgdmVydGljZXNcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG51bVNlZ3M7IGkrKykge1xuICAgICAgdnNwKHNpbihwaTIgKiBpIC8gbnVtU2VncykgKiBib3RSYWQsIGNvcyhwaTIgKiBpIC8gbnVtU2VncykgKiBib3RSYWQsIDAuNSk7XG4gICAgfVxuICAgIC8vIEJvZHkgXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBudW1TZWdzOyBpKyspIHtcbiAgICAgIGY0cChpLCAoaSArIDEpICUgbnVtU2VncywgbnVtU2VncyArIChpICsgMSkgJSBudW1TZWdzLCBpICsgbnVtU2Vncyk7XG4gICAgfVxuICAgIHRoaXMuY29tcHV0ZU5vcm1hbHMoKTtcbiAgfSxcbiAgXG4gIHVwZGF0ZUVkZ2U6IGZ1bmN0aW9uKG9iaikge1xuICAgIHZhciBsaW5lV2lkdGggPSBvYmouZ2V0RGF0YSgnbGluZVdpZHRoJyksXG4gICAgICAgIG5vZGVGcm9tID0gb2JqLm5vZGVGcm9tLFxuICAgICAgICBub2RlVG8gPSBvYmoubm9kZVRvLFxuICAgICAgICBub2RlRnJvbVBvcyA9IG5vZGVGcm9tLnBvcyxcbiAgICAgICAgbm9kZVRvUG9zID0gbm9kZVRvLnBvcyxcbiAgICAgICAgZGlzdCA9IG5vZGVGcm9tUG9zLmRpc3RhbmNlVG8obm9kZVRvUG9zKSxcbiAgICAgICAgbWlkZGxlID0gbmV3IFZlY3RvcjMsXG4gICAgICAgIGN1cnJlbnREaXIgPSBuZXcgVmVjdG9yMygwLCAwLCAxKSxcbiAgICAgICAgZHZlYyA9IG5ldyBWZWN0b3IzO1xuICAgIFxuICAgIG1pZGRsZS5hZGQobm9kZUZyb21Qb3MsIG5vZGVUb1BvcykuJHNjYWxlKDAuNSk7XG4gICAgZHZlYy5zdWIobm9kZVRvUG9zLCBub2RlRnJvbVBvcykubm9ybWFsaXplKCk7XG4gICAgXG4gICAgdmFyIGMgPSBkdmVjLmRvdChjdXJyZW50RGlyKSxcbiAgICAgICAgeGMgPSBkdmVjLmRvdChuZXcgVmVjdG9yMygxLCAwLCAwKSksXG4gICAgICAgIHljID0gZHZlYy5kb3QobmV3IFZlY3RvcjMoMCwgMSwgMCkpLFxuICAgICAgICB0ID0gMSAtIGMsXG4gICAgICAgIHJvdEFuZ2xlID0gTWF0aC5hY29zKGMpLFxuICAgICAgICBzID0gTWF0aC5zaW4ocm90QW5nbGUpLFxuICAgICAgICByb3RBeGlzID0gY3VycmVudERpci4kY3Jvc3MoZHZlYykubm9ybWFsaXplKCksXG4gICAgICAgIHggPSByb3RBeGlzLngsXG4gICAgICAgIHkgPSByb3RBeGlzLnksXG4gICAgICAgIHogPSByb3RBeGlzLno7XG4gICAgXG4gICAgdmFyIHJvdCA9IG5ldyBNYXRyaXg0KCk7XG4gICAgcm90Lm4xMSA9IHQgKiB4ICogeCArIGM7XG4gICAgcm90Lm4xMiA9IHQgKiB4ICogeSAtIHMgKiB6O1xuICAgIHJvdC5uMTMgPSB0ICogeCAqIHogKyBzICogeTtcbiAgICByb3QubjIxID0gdCAqIHggKiB5ICsgcyAqIHo7XG4gICAgcm90Lm4yMiA9IHQgKiB5ICogeSArIGM7XG4gICAgcm90Lm4yMyA9IHQgKiB5ICogeiAtIHMgKiB4O1xuICAgIHJvdC5uMzEgPSB0ICogeCAqIHogLSBzICogeTtcbiAgICByb3QubjMyID0gdCAqIHkgKiB6ICsgcyAqIHg7XG4gICAgcm90Lm4zMyA9IHQgKiB6ICogeiArIGM7XG4gICAgdGhpcy5yb3RhdGlvbk1hdHJpeCA9IHJvdDtcbiAgICB0aGlzLnNjYWxlLnNldGMobGluZVdpZHRoLCBsaW5lV2lkdGgsIGRpc3QpO1xuICAgIHRoaXMucG9zaXRpb24uc2V0YyhtaWRkbGUueCwgbWlkZGxlLnksIG1pZGRsZS56KTtcbiAgICB0aGlzLnVwZGF0ZU1hdHJpeCgpO1xuICB9LFxuICBcbiAgdXBkYXRlTWF0cml4OiBmdW5jdGlvbigpIHtcbiAgICB2YXIgcG9zID0gdGhpcy5wb3NpdGlvbixcbiAgICAgICAgc2NhbGUgPSB0aGlzLnNjYWxlLFxuICAgICAgICBtYXRyaXggPSB0aGlzLm1hdHJpeDtcbiAgICBcbiAgICBtYXRyaXguaWRlbnRpdHkoKTtcbiAgXG4gICAgbWF0cml4LiRtdWx0aXBseSggTWF0cml4NC50cmFuc2xhdGlvbk1hdHJpeCggcG9zLngsIHBvcy55LCBwb3MueiApICk7XG4gICAgbWF0cml4LiRtdWx0aXBseSggdGhpcy5yb3RhdGlvbk1hdHJpeCApO1xuICAgIG1hdHJpeC4kbXVsdGlwbHkoIE1hdHJpeDQuc2NhbGVNYXRyaXgoIHNjYWxlLngsIHNjYWxlLnksIHNjYWxlLnogKSApO1xuICB9XG5cbn0pOyBcblxuXG4vKlxuICogRmlsZTogTGF5b3V0cy5Gb3JjZURpcmVjdGVkM0QuanNcbiAqXG4qL1xuXG4vKlxuICogQ2xhc3M6IExheW91dHMuRm9yY2VEaXJlY3RlZDNEXG4gKiBcbiAqIEltcGxlbWVudHMgYSBGb3JjZSBEaXJlY3RlZCBMYXlvdXQuXG4gKiBcbiAqIEltcGxlbWVudGVkIEJ5OlxuICogXG4gKiA8Rm9yY2VEaXJlY3RlZDNEPlxuICogXG4gKi9cbkxheW91dHMuRm9yY2VEaXJlY3RlZDNEID0gbmV3IENsYXNzKHtcblxuICBnZXRPcHRpb25zOiBmdW5jdGlvbigpIHtcbiAgICB2YXIgcyA9IHRoaXMuY2FudmFzLmdldFNpemUoKTtcbiAgICB2YXIgdyA9IHMud2lkdGgsIGggPSBzLmhlaWdodDtcbiAgICAvL2NvdW50IG5vZGVzXG4gICAgdmFyIGNvdW50ID0gMDtcbiAgICB0aGlzLmdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKG4pIHsgXG4gICAgICBjb3VudCsrO1xuICAgIH0pO1xuICAgIHZhciBrMiA9IHcgKiBoIC8gY291bnQsIGsgPSBNYXRoLnNxcnQoazIpO1xuICAgIHZhciBsID0gdGhpcy5jb25maWcubGV2ZWxEaXN0YW5jZTtcbiAgICBcbiAgICByZXR1cm4ge1xuICAgICAgd2lkdGg6IHcsXG4gICAgICBoZWlnaHQ6IGgsXG4gICAgICB0c3RhcnQ6IHcgKiAwLjEsXG4gICAgICBub2RlZjogZnVuY3Rpb24oeCkgeyByZXR1cm4gazIgLyAoeCB8fCAxKTsgfSxcbiAgICAgIGVkZ2VmOiBmdW5jdGlvbih4KSB7IHJldHVybiAvKiB4ICogeCAvIGs7ICovIGsgKiAoeCAtIGwpOyB9XG4gICAgfTtcbiAgfSxcbiAgXG4gIGNvbXB1dGU6IGZ1bmN0aW9uKHByb3BlcnR5LCBpbmNyZW1lbnRhbCkge1xuICAgIHZhciBwcm9wID0gJC5zcGxhdChwcm9wZXJ0eSB8fCBbJ2N1cnJlbnQnLCAnc3RhcnQnLCAnZW5kJ10pO1xuICAgIHZhciBvcHQgPSB0aGlzLmdldE9wdGlvbnMoKTtcbiAgICBOb2RlRGltLmNvbXB1dGUodGhpcy5ncmFwaCwgcHJvcCwgdGhpcy5jb25maWcpO1xuICAgIHRoaXMuZ3JhcGguY29tcHV0ZUxldmVscyh0aGlzLnJvb3QsIDAsIFwiaWdub3JlXCIpO1xuICAgIHRoaXMuZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obikge1xuICAgICAgJC5lYWNoKHByb3AsIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgdmFyIHBvcyA9IG4uZ2V0UG9zKHApO1xuICAgICAgICBpZihwb3MuaXNaZXJvKCkpIHtcbiAgICAgICAgICBwb3MueCA9IG9wdC53aWR0aC81ICogKE1hdGgucmFuZG9tKCkgLSAwLjUpO1xuICAgICAgICAgIHBvcy55ID0gb3B0LmhlaWdodC81ICogKE1hdGgucmFuZG9tKCkgLSAwLjUpO1xuICAgICAgICAgIHBvcy56ID0gMjAwICogKE1hdGgucmFuZG9tKCkgLSAwLjUpO1xuICAgICAgICB9XG4gICAgICAgIC8vaW5pdGlhbGl6ZSBkaXNwIHZlY3RvclxuICAgICAgICBuLmRpc3AgPSB7fTtcbiAgICAgICAgJC5lYWNoKHByb3AsIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgICBuLmRpc3BbcF0gPSAkVjMoMCwgMCwgMCk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfSk7XG4gICAgdGhpcy5jb21wdXRlUG9zaXRpb25zKHByb3AsIG9wdCwgaW5jcmVtZW50YWwpO1xuICB9LFxuICBcbiAgY29tcHV0ZVBvc2l0aW9uczogZnVuY3Rpb24ocHJvcGVydHksIG9wdCwgaW5jcmVtZW50YWwpIHtcbiAgICB2YXIgdGltZXMgPSB0aGlzLmNvbmZpZy5pdGVyYXRpb25zLCBpID0gMCwgdGhhdCA9IHRoaXM7XG4gICAgaWYoaW5jcmVtZW50YWwpIHtcbiAgICAgIChmdW5jdGlvbiBpdGVyKCkge1xuICAgICAgICBmb3IodmFyIHRvdGFsPWluY3JlbWVudGFsLml0ZXIsIGo9MDsgajx0b3RhbDsgaisrKSB7XG4gICAgICAgICAgb3B0LnQgPSBvcHQudHN0YXJ0ICogKDEgLSBpKysvKHRpbWVzIC0xKSk7XG4gICAgICAgICAgdGhhdC5jb21wdXRlUG9zaXRpb25TdGVwKHByb3BlcnR5LCBvcHQpO1xuICAgICAgICAgIGlmKGkgPj0gdGltZXMpIHtcbiAgICAgICAgICAgIGluY3JlbWVudGFsLm9uQ29tcGxldGUoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaW5jcmVtZW50YWwub25TdGVwKE1hdGgucm91bmQoaSAvICh0aW1lcyAtMSkgKiAxMDApKTtcbiAgICAgICAgc2V0VGltZW91dChpdGVyLCAxKTtcbiAgICAgIH0pKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGZvcig7IGkgPCB0aW1lczsgaSsrKSB7XG4gICAgICAgIG9wdC50ID0gb3B0LnRzdGFydCAqICgxIC0gaS8odGltZXMgLTEpKTtcbiAgICAgICAgdGhpcy5jb21wdXRlUG9zaXRpb25TdGVwKHByb3BlcnR5LCBvcHQpO1xuICAgICAgfVxuICAgIH1cbiAgfSxcbiAgXG4gIGNvbXB1dGVQb3NpdGlvblN0ZXA6IGZ1bmN0aW9uKHByb3BlcnR5LCBvcHQpIHtcbiAgICB2YXIgZ3JhcGggPSB0aGlzLmdyYXBoO1xuICAgIHZhciBtaW4gPSBNYXRoLm1pbiwgbWF4ID0gTWF0aC5tYXg7XG4gICAgdmFyIGRwb3MgPSAkVjMoMCwgMCwgMCk7XG4gICAgLy9jYWxjdWxhdGUgcmVwdWxzaXZlIGZvcmNlc1xuICAgIGdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKHYpIHtcbiAgICAgIC8vaW5pdGlhbGl6ZSBkaXNwXG4gICAgICAkLmVhY2gocHJvcGVydHksIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgdi5kaXNwW3BdLnggPSAwOyBcbiAgICAgICAgdi5kaXNwW3BdLnkgPSAwO1xuICAgICAgICB2LmRpc3BbcF0ueiA9IDA7XG4gICAgICB9KTtcbiAgICAgIGdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKHUpIHtcbiAgICAgICAgaWYodS5pZCAhPSB2LmlkKSB7XG4gICAgICAgICAgJC5lYWNoKHByb3BlcnR5LCBmdW5jdGlvbihwKSB7XG4gICAgICAgICAgICB2YXIgdnAgPSB2LmdldFBvcyhwKSwgdXAgPSB1LmdldFBvcyhwKTtcbiAgICAgICAgICAgIGRwb3MueCA9IHZwLnggLSB1cC54O1xuICAgICAgICAgICAgZHBvcy55ID0gdnAueSAtIHVwLnk7XG4gICAgICAgICAgICBkcG9zLnogPSB2cC56IC0gdXAuejtcbiAgICAgICAgICAgIHZhciBub3JtID0gZHBvcy5ub3JtKCkgfHwgMTtcbiAgICAgICAgICAgIHYuZGlzcFtwXS4kYWRkKGRwb3NcbiAgICAgICAgICAgICAgICAuJHNjYWxlKG9wdC5ub2RlZihub3JtKSAvIG5vcm0pKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG4gICAgLy9jYWxjdWxhdGUgYXR0cmFjdGl2ZSBmb3JjZXNcbiAgICB2YXIgVCA9ICEhZ3JhcGguZ2V0Tm9kZSh0aGlzLnJvb3QpLnZpc2l0ZWQ7XG4gICAgZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obm9kZSkge1xuICAgICAgbm9kZS5lYWNoQWRqYWNlbmN5KGZ1bmN0aW9uKGFkaikge1xuICAgICAgICB2YXIgbm9kZVRvID0gYWRqLm5vZGVUbztcbiAgICAgICAgaWYoISFub2RlVG8udmlzaXRlZCA9PT0gVCkge1xuICAgICAgICAgICQuZWFjaChwcm9wZXJ0eSwgZnVuY3Rpb24ocCkge1xuICAgICAgICAgICAgdmFyIHZwID0gbm9kZS5nZXRQb3MocCksIHVwID0gbm9kZVRvLmdldFBvcyhwKTtcbiAgICAgICAgICAgIGRwb3MueCA9IHZwLnggLSB1cC54O1xuICAgICAgICAgICAgZHBvcy55ID0gdnAueSAtIHVwLnk7XG4gICAgICAgICAgICBkcG9zLnogPSB2cC56IC0gdXAuejtcbiAgICAgICAgICAgIHZhciBub3JtID0gZHBvcy5ub3JtKCkgfHwgMTtcbiAgICAgICAgICAgIG5vZGUuZGlzcFtwXS4kYWRkKGRwb3MuJHNjYWxlKC1vcHQuZWRnZWYobm9ybSkgLyBub3JtKSk7XG4gICAgICAgICAgICBub2RlVG8uZGlzcFtwXS4kYWRkKGRwb3MuJHNjYWxlKC0xKSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgbm9kZS52aXNpdGVkID0gIVQ7XG4gICAgfSk7XG4gICAgLy9hcnJhbmdlIHBvc2l0aW9ucyB0byBmaXQgdGhlIGNhbnZhc1xuICAgIHZhciB0ID0gb3B0LnQsIHcyID0gb3B0LndpZHRoIC8gMiwgaDIgPSBvcHQuaGVpZ2h0IC8gMjtcbiAgICBncmFwaC5lYWNoTm9kZShmdW5jdGlvbih1KSB7XG4gICAgICAkLmVhY2gocHJvcGVydHksIGZ1bmN0aW9uKHApIHtcbiAgICAgICAgdmFyIGRpc3AgPSB1LmRpc3BbcF07XG4gICAgICAgIHZhciBub3JtID0gZGlzcC5ub3JtKCkgfHwgMTtcbiAgICAgICAgdmFyIHAgPSB1LmdldFBvcyhwKTtcbiAgICAgICAgcC4kYWRkKCRWMyhkaXNwLnggKiBtaW4oTWF0aC5hYnMoZGlzcC54KSwgdCkgLyBub3JtLCBcbiAgICAgICAgICAgICAgICAgICBkaXNwLnkgKiBtaW4oTWF0aC5hYnMoZGlzcC55KSwgdCkgLyBub3JtLFxuICAgICAgICAgICAgICAgICAgIGRpc3AueiAqIG1pbihNYXRoLmFicyhkaXNwLnopLCB0KSAvIG5vcm0pKTtcbiAgICAgICAgcC54ID0gbWluKHcyLCBtYXgoLXcyLCBwLngpKTtcbiAgICAgICAgcC55ID0gbWluKGgyLCBtYXgoLWgyLCBwLnkpKTtcbiAgICAgICAgcC56ID0gbWluKGgyLCBtYXgoLWgyLCBwLnopKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG59KTtcblxuJGppdC5Gb3JjZURpcmVjdGVkM0QgPSBuZXcgQ2xhc3MoIHtcblxuICBJbXBsZW1lbnRzOiBbIExvYWRlciwgRXh0cmFzLCBMYXlvdXRzLkZvcmNlRGlyZWN0ZWQzRCBdLFxuXG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uKGNvbnRyb2xsZXIpIHtcbiAgICB2YXIgJEZvcmNlRGlyZWN0ZWQzRCA9ICRqaXQuRm9yY2VEaXJlY3RlZDNEO1xuXG4gICAgdmFyIGNvbmZpZyA9IHtcbiAgICAgIGl0ZXJhdGlvbnM6IDUwLFxuICAgICAgbGV2ZWxEaXN0YW5jZTogNTBcbiAgICB9O1xuXG4gICAgdGhpcy5jb250cm9sbGVyID0gdGhpcy5jb25maWcgPSAkLm1lcmdlKE9wdGlvbnMoXCJDYW52YXNcIiwgXCJOb2RlXCIsIFwiRWRnZVwiLFxuICAgICAgICBcIkZ4XCIsIFwiVGlwc1wiLCBcIk5vZGVTdHlsZXNcIiwgXCJFdmVudHNcIiwgXCJOYXZpZ2F0aW9uXCIsIFwiQ29udHJvbGxlclwiLCBcIkxhYmVsXCIpLCBjb25maWcsIGNvbnRyb2xsZXIpO1xuXG4gICAgdmFyIGNhbnZhc0NvbmZpZyA9IHRoaXMuY29uZmlnO1xuICAgIGlmKGNhbnZhc0NvbmZpZy51c2VDYW52YXMpIHtcbiAgICAgIHRoaXMuY2FudmFzID0gY2FudmFzQ29uZmlnLnVzZUNhbnZhcztcbiAgICAgIHRoaXMuY29uZmlnLmxhYmVsQ29udGFpbmVyID0gdGhpcy5jYW52YXMuaWQgKyAnLWxhYmVsJztcbiAgICB9IGVsc2Uge1xuICAgICAgaWYoY2FudmFzQ29uZmlnLmJhY2tncm91bmQpIHtcbiAgICAgICAgY2FudmFzQ29uZmlnLmJhY2tncm91bmQgPSAkLm1lcmdlKHtcbiAgICAgICAgICB0eXBlOiAnQ2lyY2xlcydcbiAgICAgICAgfSwgY2FudmFzQ29uZmlnLmJhY2tncm91bmQpO1xuICAgICAgfVxuICAgICAgdGhpcy5jYW52YXMgPSBuZXcgQ2FudmFzKHRoaXMsIGNhbnZhc0NvbmZpZyk7XG4gICAgICB0aGlzLmNvbmZpZy5sYWJlbENvbnRhaW5lciA9ICh0eXBlb2YgY2FudmFzQ29uZmlnLmluamVjdEludG8gPT0gJ3N0cmluZyc/IGNhbnZhc0NvbmZpZy5pbmplY3RJbnRvIDogY2FudmFzQ29uZmlnLmluamVjdEludG8uaWQpICsgJy1sYWJlbCc7XG4gICAgfVxuXG4gICAgdGhpcy5ncmFwaE9wdGlvbnMgPSB7XG4gICAgICAna2xhc3MnOiBWZWN0b3IzLFxuICAgICAgJ05vZGUnOiB7XG4gICAgICAgICdzZWxlY3RlZCc6IGZhbHNlLFxuICAgICAgICAnZXhpc3QnOiB0cnVlLFxuICAgICAgICAnZHJhd24nOiB0cnVlXG4gICAgICB9XG4gICAgfTtcbiAgICB0aGlzLmdyYXBoID0gbmV3IEdyYXBoKHRoaXMuZ3JhcGhPcHRpb25zLCB0aGlzLmNvbmZpZy5Ob2RlLFxuICAgICAgICB0aGlzLmNvbmZpZy5FZGdlKTtcbiAgICB0aGlzLmxhYmVscyA9IG5ldyAkRm9yY2VEaXJlY3RlZDNELkxhYmVsW2NhbnZhc0NvbmZpZy5MYWJlbC50eXBlXSh0aGlzKTtcbiAgICB0aGlzLmZ4ID0gbmV3ICRGb3JjZURpcmVjdGVkM0QuUGxvdCh0aGlzLCAkRm9yY2VEaXJlY3RlZDNEKTtcbiAgICB0aGlzLm9wID0gbmV3ICRGb3JjZURpcmVjdGVkM0QuT3AodGhpcyk7XG4gICAgdGhpcy5qc29uID0gbnVsbDtcbiAgICB0aGlzLmJ1c3kgPSBmYWxzZTtcbiAgICAvLyBpbml0aWFsaXplIGV4dHJhc1xuICAgIHRoaXMuaW5pdGlhbGl6ZUV4dHJhcygpO1xuICB9LFxuXG4gIC8qIFxuICAgIHJlZnJlc2ggXG4gICAgXG4gICAgQ29tcHV0ZXMgcG9zaXRpb25zIGFuZCBwbG90cyB0aGUgdHJlZS5cbiAgKi9cbiAgcmVmcmVzaDogZnVuY3Rpb24oKSB7XG4gICAgdGhpcy5jb21wdXRlKCk7XG4gICAgdGhpcy5wbG90KCk7XG4gIH0sXG5cbiAgcmVwb3NpdGlvbjogZnVuY3Rpb24oKSB7XG4gICAgdGhpcy5jb21wdXRlKCdlbmQnKTtcbiAgfSxcblxuLypcbiAgY29tcHV0ZUluY3JlbWVudGFsXG4gIFxuICBQZXJmb3JtcyB0aGUgRm9yY2UgRGlyZWN0ZWQgYWxnb3JpdGhtIGluY3JlbWVudGFsbHkuXG4gIFxuICBEZXNjcmlwdGlvbjpcbiAgXG4gIEZvcmNlRGlyZWN0ZWQzRCBhbGdvcml0aG1zIGNhbiBwZXJmb3JtIG1hbnkgY29tcHV0YXRpb25zIGFuZCBsZWFkIHRvIEphdmFTY3JpcHQgdGFraW5nIHRvbyBtdWNoIHRpbWUgdG8gY29tcGxldGUuIFxuICBUaGlzIG1ldGhvZCBzcGxpdHMgdGhlIGFsZ29yaXRobSBpbnRvIHNtYWxsZXIgcGFydHMgYWxsb3dpbmcgdGhlIHVzZXIgdG8gdHJhY2sgdGhlIGV2b2x1dGlvbiBvZiB0aGUgYWxnb3JpdGhtIGFuZCBcbiAgYXZvaWRpbmcgYnJvd3NlciBtZXNzYWdlcyBzdWNoIGFzIFwiVGhpcyBzY3JpcHQgaXMgdGFraW5nIHRvbyBsb25nIHRvIGNvbXBsZXRlXCIuXG4gIFxuICBQYXJhbWV0ZXJzOlxuICBcbiAgb3B0IC0gKG9iamVjdCkgVGhlIG9iamVjdCBwcm9wZXJ0aWVzIGFyZSBkZXNjcmliZWQgYmVsb3dcbiAgXG4gIGl0ZXIgLSAobnVtYmVyKSBEZWZhdWx0J3MgKjIwKi4gU3BsaXQgdGhlIGFsZ29yaXRobSBpbnRvIHBpZWNlcyBvZiBfaXRlcl8gaXRlcmF0aW9ucy4gRm9yIGV4YW1wbGUsIGlmIHRoZSBfaXRlcmF0aW9uc18gY29uZmlndXJhdGlvbiBwcm9wZXJ0eSBcbiAgb2YgeW91ciA8Rm9yY2VEaXJlY3RlZDNEPiBjbGFzcyBpcyAxMDAsIHRoZW4geW91IGNvdWxkIHNldCBfaXRlcl8gdG8gMjAgdG8gc3BsaXQgdGhlIG1haW4gYWxnb3JpdGhtIGludG8gNSBzbWFsbGVyIHBpZWNlcy5cbiAgXG4gIHByb3BlcnR5IC0gKHN0cmluZykgRGVmYXVsdCdzICplbmQqLiBXaGV0aGVyIHRvIHVwZGF0ZSBzdGFydGluZywgY3VycmVudCBvciBlbmRpbmcgbm9kZSBwb3NpdGlvbnMuIFBvc3NpYmxlIHZhbHVlcyBhcmUgJ2VuZCcsICdzdGFydCcsICdjdXJyZW50Jy4gXG4gIFlvdSBjYW4gYWxzbyBzZXQgYW4gYXJyYXkgb2YgdGhlc2UgcHJvcGVydGllcy4gSWYgeW91J2QgbGlrZSB0byBrZWVwIHRoZSBjdXJyZW50IG5vZGUgcG9zaXRpb25zIGJ1dCB0byBwZXJmb3JtIHRoZXNlIFxuICBjb21wdXRhdGlvbnMgZm9yIGZpbmFsIGFuaW1hdGlvbiBwb3NpdGlvbnMgdGhlbiB5b3UgY2FuIGp1c3QgY2hvb3NlICdlbmQnLlxuICBcbiAgb25TdGVwIC0gKGZ1bmN0aW9uKSBBIGNhbGxiYWNrIGZ1bmN0aW9uIGNhbGxlZCB3aGVuIGVhY2ggXCJzbWFsbCBwYXJ0XCIgb2YgdGhlIGFsZ29yaXRobSBjb21wbGV0ZWQuIFRoaXMgZnVuY3Rpb24gZ2V0cyBhcyBmaXJzdCBmb3JtYWwgXG4gIHBhcmFtZXRlciBhIHBlcmNlbnRhZ2UgdmFsdWUuXG4gIFxuICBvbkNvbXBsZXRlIC0gQSBjYWxsYmFjayBmdW5jdGlvbiBjYWxsZWQgd2hlbiB0aGUgYWxnb3JpdGhtIGNvbXBsZXRlZC5cbiAgXG4gIEV4YW1wbGU6XG4gIFxuICBJbiB0aGlzIGV4YW1wbGUgSSBjYWxjdWxhdGUgdGhlIGVuZCBwb3NpdGlvbnMgYW5kIHRoZW4gYW5pbWF0ZSB0aGUgZ3JhcGggdG8gdGhvc2UgcG9zaXRpb25zXG4gIFxuICAoc3RhcnQgY29kZSBqcylcbiAgdmFyIGZkID0gbmV3ICRqaXQuRm9yY2VEaXJlY3RlZDNEKC4uLik7XG4gIGZkLmNvbXB1dGVJbmNyZW1lbnRhbCh7XG4gICAgaXRlcjogMjAsXG4gICAgcHJvcGVydHk6ICdlbmQnLFxuICAgIG9uU3RlcDogZnVuY3Rpb24ocGVyYykge1xuICAgICAgTG9nLndyaXRlKFwibG9hZGluZyBcIiArIHBlcmMgKyBcIiVcIik7XG4gICAgfSxcbiAgICBvbkNvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgIExvZy53cml0ZShcImRvbmVcIik7XG4gICAgICBmZC5hbmltYXRlKCk7XG4gICAgfVxuICB9KTtcbiAgKGVuZCBjb2RlKVxuICBcbiAgSW4gdGhpcyBleGFtcGxlIEkgY2FsY3VsYXRlIGFsbCBwb3NpdGlvbnMgYW5kIChyZSlwbG90IHRoZSBncmFwaFxuICBcbiAgKHN0YXJ0IGNvZGUganMpXG4gIHZhciBmZCA9IG5ldyBGb3JjZURpcmVjdGVkM0QoLi4uKTtcbiAgZmQuY29tcHV0ZUluY3JlbWVudGFsKHtcbiAgICBpdGVyOiAyMCxcbiAgICBwcm9wZXJ0eTogWydlbmQnLCAnc3RhcnQnLCAnY3VycmVudCddLFxuICAgIG9uU3RlcDogZnVuY3Rpb24ocGVyYykge1xuICAgICAgTG9nLndyaXRlKFwibG9hZGluZyBcIiArIHBlcmMgKyBcIiVcIik7XG4gICAgfSxcbiAgICBvbkNvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgIExvZy53cml0ZShcImRvbmVcIik7XG4gICAgICBmZC5wbG90KCk7XG4gICAgfVxuICB9KTtcbiAgKGVuZCBjb2RlKVxuICBcbiAgKi9cbiAgY29tcHV0ZUluY3JlbWVudGFsOiBmdW5jdGlvbihvcHQpIHtcbiAgICBvcHQgPSAkLm1lcmdlKCB7XG4gICAgICBpdGVyOiAyMCxcbiAgICAgIHByb3BlcnR5OiAnZW5kJyxcbiAgICAgIG9uU3RlcDogJC5lbXB0eSxcbiAgICAgIG9uQ29tcGxldGU6ICQuZW1wdHlcbiAgICB9LCBvcHQgfHwge30pO1xuXG4gICAgdGhpcy5jb25maWcub25CZWZvcmVDb21wdXRlKHRoaXMuZ3JhcGguZ2V0Tm9kZSh0aGlzLnJvb3QpKTtcbiAgICB0aGlzLmNvbXB1dGUob3B0LnByb3BlcnR5LCBvcHQpO1xuICB9LFxuXG4gIC8qXG4gICAgcGxvdFxuICAgXG4gICAgUGxvdHMgdGhlIEZvcmNlRGlyZWN0ZWQzRCBncmFwaC4gVGhpcyBpcyBhIHNob3J0Y3V0IHRvICpmeC5wbG90Ki5cbiAgICovXG4gIHBsb3Q6IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMuZngucGxvdCgpO1xuICB9LFxuXG4gIC8qXG4gICAgIGFuaW1hdGVcbiAgICBcbiAgICAgQW5pbWF0ZXMgdGhlIGdyYXBoIGZyb20gdGhlIGN1cnJlbnQgcG9zaXRpb25zIHRvIHRoZSAnZW5kJyBub2RlIHBvc2l0aW9ucy5cbiAgKi9cbiAgYW5pbWF0ZTogZnVuY3Rpb24ob3B0KSB7XG4gICAgdGhpcy5meC5hbmltYXRlKCQubWVyZ2UoIHtcbiAgICAgIG1vZGVzOiBbICdsaW5lYXInIF1cbiAgICB9LCBvcHQgfHwge30pKTtcbiAgfVxufSk7XG5cbiRqaXQuRm9yY2VEaXJlY3RlZDNELiRleHRlbmQgPSB0cnVlO1xuXG4oZnVuY3Rpb24oRm9yY2VEaXJlY3RlZDNEKSB7XG5cbiAgLypcbiAgICAgRm9yY2VEaXJlY3RlZDNELk9wXG4gICAgIFxuICAgICBDdXN0b20gZXh0ZW5zaW9uIG9mIDxHcmFwaC5PcD4uXG5cbiAgICAgRXh0ZW5kczpcblxuICAgICBBbGwgPEdyYXBoLk9wPiBtZXRob2RzXG4gICAgIFxuICAgICBTZWUgYWxzbzpcbiAgICAgXG4gICAgIDxHcmFwaC5PcD5cblxuICAqL1xuICBGb3JjZURpcmVjdGVkM0QuT3AgPSBuZXcgQ2xhc3MoIHtcblxuICAgIEltcGxlbWVudHM6IEdyYXBoLk9wXG5cbiAgfSk7XG5cbiAgLypcbiAgICBGb3JjZURpcmVjdGVkM0QuUGxvdFxuICAgIFxuICAgIEN1c3RvbSBleHRlbnNpb24gb2YgPEdyYXBoLlBsb3Q+LlxuICBcbiAgICBFeHRlbmRzOlxuICBcbiAgICBBbGwgPEdyYXBoLlBsb3Q+IG1ldGhvZHNcbiAgICBcbiAgICBTZWUgYWxzbzpcbiAgICBcbiAgICA8R3JhcGguUGxvdD5cbiAgXG4gICovXG4gIEZvcmNlRGlyZWN0ZWQzRC5QbG90ID0gbmV3IENsYXNzKCB7XG5cbiAgICBJbXBsZW1lbnRzOiBHcmFwaC5QbG90M0RcblxuICB9KTtcblxuICAvKlxuICAgIEZvcmNlRGlyZWN0ZWQzRC5MYWJlbFxuICAgIFxuICAgIEN1c3RvbSBleHRlbnNpb24gb2YgPEdyYXBoLkxhYmVsPi4gXG4gICAgQ29udGFpbnMgY3VzdG9tIDxHcmFwaC5MYWJlbC5TVkc+LCA8R3JhcGguTGFiZWwuSFRNTD4gYW5kIDxHcmFwaC5MYWJlbC5OYXRpdmU+IGV4dGVuc2lvbnMuXG4gIFxuICAgIEV4dGVuZHM6XG4gIFxuICAgIEFsbCA8R3JhcGguTGFiZWw+IG1ldGhvZHMgYW5kIHN1YmNsYXNzZXMuXG4gIFxuICAgIFNlZSBhbHNvOlxuICBcbiAgICA8R3JhcGguTGFiZWw+LCA8R3JhcGguTGFiZWwuTmF0aXZlPiwgPEdyYXBoLkxhYmVsLkhUTUw+LCA8R3JhcGguTGFiZWwuU1ZHPi5cbiAgXG4gICovXG4gIEZvcmNlRGlyZWN0ZWQzRC5MYWJlbCA9IHt9O1xuXG4gIC8qXG4gICAgIEZvcmNlRGlyZWN0ZWQzRC5MYWJlbC5OYXRpdmVcbiAgICAgXG4gICAgIEN1c3RvbSBleHRlbnNpb24gb2YgPEdyYXBoLkxhYmVsLk5hdGl2ZT4uXG5cbiAgICAgRXh0ZW5kczpcblxuICAgICBBbGwgPEdyYXBoLkxhYmVsLk5hdGl2ZT4gbWV0aG9kc1xuXG4gICAgIFNlZSBhbHNvOlxuXG4gICAgIDxHcmFwaC5MYWJlbC5OYXRpdmU+XG5cbiAgKi9cbiAgRm9yY2VEaXJlY3RlZDNELkxhYmVsLk5hdGl2ZSA9IG5ldyBDbGFzcygge1xuICAgIEltcGxlbWVudHM6IEdyYXBoLkxhYmVsLk5hdGl2ZVxuICB9KTtcblxuICAvKlxuICAgIEZvcmNlRGlyZWN0ZWQzRC5MYWJlbC5TVkdcbiAgICBcbiAgICBDdXN0b20gZXh0ZW5zaW9uIG9mIDxHcmFwaC5MYWJlbC5TVkc+LlxuICBcbiAgICBFeHRlbmRzOlxuICBcbiAgICBBbGwgPEdyYXBoLkxhYmVsLlNWRz4gbWV0aG9kc1xuICBcbiAgICBTZWUgYWxzbzpcbiAgXG4gICAgPEdyYXBoLkxhYmVsLlNWRz5cbiAgXG4gICovXG4gIEZvcmNlRGlyZWN0ZWQzRC5MYWJlbC5TVkcgPSBuZXcgQ2xhc3MoIHtcbiAgICBJbXBsZW1lbnRzOiBHcmFwaC5MYWJlbC5TVkcsXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbih2aXopIHtcbiAgICAgIHRoaXMudml6ID0gdml6O1xuICAgIH0sXG5cbiAgICAvKiBcbiAgICAgICBwbGFjZUxhYmVsXG5cbiAgICAgICBPdmVycmlkZXMgYWJzdHJhY3QgbWV0aG9kIHBsYWNlTGFiZWwgaW4gPEdyYXBoLkxhYmVsPi5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICB0YWcgLSBBIERPTSBsYWJlbCBlbGVtZW50LlxuICAgICAgIG5vZGUgLSBBIDxHcmFwaC5Ob2RlPi5cbiAgICAgICBjb250cm9sbGVyIC0gQSBjb25maWd1cmF0aW9uL2NvbnRyb2xsZXIgb2JqZWN0IHBhc3NlZCB0byB0aGUgdmlzdWFsaXphdGlvbi5cbiAgICAgIFxuICAgICAqL1xuICAgIHBsYWNlTGFiZWw6IGZ1bmN0aW9uKHRhZywgbm9kZSwgY29udHJvbGxlcikge1xuICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgIGNhbnZhcyA9IHRoaXMudml6LmNhbnZhcyxcbiAgICAgICAgICBveCA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRYLFxuICAgICAgICAgIG95ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFksXG4gICAgICAgICAgc3ggPSBjYW52YXMuc2NhbGVPZmZzZXRYLFxuICAgICAgICAgIHN5ID0gY2FudmFzLnNjYWxlT2Zmc2V0WSxcbiAgICAgICAgICByYWRpdXMgPSBjYW52YXMuZ2V0U2l6ZSgpO1xuICAgICAgdmFyIGxhYmVsUG9zID0ge1xuICAgICAgICB4OiBNYXRoLnJvdW5kKHBvcy54ICogc3ggKyBveCArIHJhZGl1cy53aWR0aCAvIDIpLFxuICAgICAgICB5OiBNYXRoLnJvdW5kKHBvcy55ICogc3kgKyBveSArIHJhZGl1cy5oZWlnaHQgLyAyKVxuICAgICAgfTtcbiAgICAgIHRhZy5zZXRBdHRyaWJ1dGUoJ3gnLCBsYWJlbFBvcy54KTtcbiAgICAgIHRhZy5zZXRBdHRyaWJ1dGUoJ3knLCBsYWJlbFBvcy55KTtcblxuICAgICAgY29udHJvbGxlci5vblBsYWNlTGFiZWwodGFnLCBub2RlKTtcbiAgICB9XG4gIH0pO1xuXG4gIC8qXG4gICAgIEZvcmNlRGlyZWN0ZWQzRC5MYWJlbC5IVE1MXG4gICAgIFxuICAgICBDdXN0b20gZXh0ZW5zaW9uIG9mIDxHcmFwaC5MYWJlbC5IVE1MPi5cblxuICAgICBFeHRlbmRzOlxuXG4gICAgIEFsbCA8R3JhcGguTGFiZWwuSFRNTD4gbWV0aG9kcy5cblxuICAgICBTZWUgYWxzbzpcblxuICAgICA8R3JhcGguTGFiZWwuSFRNTD5cblxuICAqL1xuICBGb3JjZURpcmVjdGVkM0QuTGFiZWwuSFRNTCA9IG5ldyBDbGFzcygge1xuICAgIEltcGxlbWVudHM6IEdyYXBoLkxhYmVsLkhUTUwsXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbih2aXopIHtcbiAgICAgIHRoaXMudml6ID0gdml6O1xuICAgIH0sXG4gICAgLyogXG4gICAgICAgcGxhY2VMYWJlbFxuXG4gICAgICAgT3ZlcnJpZGVzIGFic3RyYWN0IG1ldGhvZCBwbGFjZUxhYmVsIGluIDxHcmFwaC5QbG90Pi5cblxuICAgICAgIFBhcmFtZXRlcnM6XG5cbiAgICAgICB0YWcgLSBBIERPTSBsYWJlbCBlbGVtZW50LlxuICAgICAgIG5vZGUgLSBBIDxHcmFwaC5Ob2RlPi5cbiAgICAgICBjb250cm9sbGVyIC0gQSBjb25maWd1cmF0aW9uL2NvbnRyb2xsZXIgb2JqZWN0IHBhc3NlZCB0byB0aGUgdmlzdWFsaXphdGlvbi5cbiAgICAgIFxuICAgICAqL1xuICAgIHBsYWNlTGFiZWw6IGZ1bmN0aW9uKHRhZywgbm9kZSwgY29udHJvbGxlcikge1xuICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgIGNhbnZhcyA9IHRoaXMudml6LmNhbnZhcyxcbiAgICAgICAgICBveCA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRYLFxuICAgICAgICAgIG95ID0gY2FudmFzLnRyYW5zbGF0ZU9mZnNldFksXG4gICAgICAgICAgc3ggPSBjYW52YXMuc2NhbGVPZmZzZXRYLFxuICAgICAgICAgIHN5ID0gY2FudmFzLnNjYWxlT2Zmc2V0WSxcbiAgICAgICAgICByYWRpdXMgPSBjYW52YXMuZ2V0U2l6ZSgpO1xuICAgICAgdmFyIGxhYmVsUG9zID0ge1xuICAgICAgICB4OiBNYXRoLnJvdW5kKHBvcy54ICogc3ggKyBveCArIHJhZGl1cy53aWR0aCAvIDIpLFxuICAgICAgICB5OiBNYXRoLnJvdW5kKHBvcy55ICogc3kgKyBveSArIHJhZGl1cy5oZWlnaHQgLyAyKVxuICAgICAgfTtcbiAgICAgIHZhciBzdHlsZSA9IHRhZy5zdHlsZTtcbiAgICAgIHN0eWxlLmxlZnQgPSBsYWJlbFBvcy54ICsgJ3B4JztcbiAgICAgIHN0eWxlLnRvcCA9IGxhYmVsUG9zLnkgKyAncHgnO1xuICAgICAgc3R5bGUuZGlzcGxheSA9IHRoaXMuZml0c0luQ2FudmFzKGxhYmVsUG9zLCBjYW52YXMpID8gJycgOiAnbm9uZSc7XG5cbiAgICAgIGNvbnRyb2xsZXIub25QbGFjZUxhYmVsKHRhZywgbm9kZSk7XG4gICAgfVxuICB9KTtcblxuICAvKlxuICAgIEZvcmNlRGlyZWN0ZWQzRC5QbG90Lk5vZGVUeXBlc1xuXG4gICAgVGhpcyBjbGFzcyBjb250YWlucyBhIGxpc3Qgb2YgPEdyYXBoLk5vZGU+IGJ1aWx0LWluIHR5cGVzLiBcbiAgICBOb2RlIHR5cGVzIGltcGxlbWVudGVkIGFyZSAnbm9uZScsICdjaXJjbGUnLCAndHJpYW5nbGUnLCAncmVjdGFuZ2xlJywgJ3N0YXInLCAnZWxsaXBzZScgYW5kICdzcXVhcmUnLlxuXG4gICAgWW91IGNhbiBhZGQgeW91ciBjdXN0b20gbm9kZSB0eXBlcywgY3VzdG9taXppbmcgeW91ciB2aXN1YWxpemF0aW9uIHRvIHRoZSBleHRyZW1lLlxuXG4gICAgRXhhbXBsZTpcblxuICAgIChzdGFydCBjb2RlIGpzKVxuICAgICAgRm9yY2VEaXJlY3RlZDNELlBsb3QuTm9kZVR5cGVzLmltcGxlbWVudCh7XG4gICAgICAgICdteVNwZWNpYWxUeXBlJzoge1xuICAgICAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpIHtcbiAgICAgICAgICAgIC8vcHJpbnQgeW91ciBjdXN0b20gbm9kZSB0byBjYW52YXNcbiAgICAgICAgICB9LFxuICAgICAgICAgIC8vb3B0aW9uYWxcbiAgICAgICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3MpIHtcbiAgICAgICAgICAgIC8vcmV0dXJuIHRydWUgaWYgcG9zIGlzIGluc2lkZSB0aGUgbm9kZSBvciBmYWxzZSBvdGhlcndpc2VcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIChlbmQgY29kZSlcblxuICAqL1xuICBGb3JjZURpcmVjdGVkM0QuUGxvdC5Ob2RlVHlwZXMgPSBuZXcgQ2xhc3Moe1xuICAgICdub25lJzoge1xuICAgICAgJ3JlbmRlcic6ICQuZW1wdHksXG4gICAgICAnY29udGFpbnMnOiAkLmxhbWJkYShmYWxzZSlcbiAgICB9LFxuICAgICdjaXJjbGUnOiB7XG4gICAgICAncmVuZGVyJzogZnVuY3Rpb24obm9kZSwgY2FudmFzKXtcbiAgICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgZGltID0gbm9kZS5nZXREYXRhKCdkaW0nKTtcbiAgICAgICAgdGhpcy5ub2RlSGVscGVyLmNpcmNsZS5yZW5kZXIoJ2ZpbGwnLCBwb3MsIGRpbSwgY2FudmFzKTtcbiAgICAgIH0sXG4gICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3Mpe1xuICAgICAgICB2YXIgbnBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgZGltID0gbm9kZS5nZXREYXRhKCdkaW0nKTtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9kZUhlbHBlci5jaXJjbGUuY29udGFpbnMobnBvcywgcG9zLCBkaW0pO1xuICAgICAgfVxuICAgIH0sXG4gICAgJ2VsbGlwc2UnOiB7XG4gICAgICAncmVuZGVyJzogZnVuY3Rpb24obm9kZSwgY2FudmFzKXtcbiAgICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksIFxuICAgICAgICAgICAgd2lkdGggPSBub2RlLmdldERhdGEoJ3dpZHRoJyksIFxuICAgICAgICAgICAgaGVpZ2h0ID0gbm9kZS5nZXREYXRhKCdoZWlnaHQnKTtcbiAgICAgICAgdGhpcy5ub2RlSGVscGVyLmVsbGlwc2UucmVuZGVyKCdmaWxsJywgcG9zLCB3aWR0aCwgaGVpZ2h0LCBjYW52YXMpO1xuICAgICAgICB9LFxuICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obm9kZSwgcG9zKXtcbiAgICAgICAgdmFyIG5wb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIHdpZHRoID0gbm9kZS5nZXREYXRhKCd3aWR0aCcpLCBcbiAgICAgICAgICAgIGhlaWdodCA9IG5vZGUuZ2V0RGF0YSgnaGVpZ2h0Jyk7XG4gICAgICAgIHJldHVybiB0aGlzLm5vZGVIZWxwZXIuZWxsaXBzZS5jb250YWlucyhucG9zLCBwb3MsIHdpZHRoLCBoZWlnaHQpO1xuICAgICAgfVxuICAgIH0sXG4gICAgJ3NxdWFyZSc6IHtcbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihub2RlLCBjYW52YXMpe1xuICAgICAgICB2YXIgcG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpO1xuICAgICAgICB0aGlzLm5vZGVIZWxwZXIuc3F1YXJlLnJlbmRlcignZmlsbCcsIHBvcywgZGltLCBjYW52YXMpO1xuICAgICAgfSxcbiAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5vZGUsIHBvcyl7XG4gICAgICAgIHZhciBucG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpO1xuICAgICAgICByZXR1cm4gdGhpcy5ub2RlSGVscGVyLnNxdWFyZS5jb250YWlucyhucG9zLCBwb3MsIGRpbSk7XG4gICAgICB9XG4gICAgfSxcbiAgICAncmVjdGFuZ2xlJzoge1xuICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcyl7XG4gICAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIHdpZHRoID0gbm9kZS5nZXREYXRhKCd3aWR0aCcpLCBcbiAgICAgICAgICAgIGhlaWdodCA9IG5vZGUuZ2V0RGF0YSgnaGVpZ2h0Jyk7XG4gICAgICAgIHRoaXMubm9kZUhlbHBlci5yZWN0YW5nbGUucmVuZGVyKCdmaWxsJywgcG9zLCB3aWR0aCwgaGVpZ2h0LCBjYW52YXMpO1xuICAgICAgfSxcbiAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKG5vZGUsIHBvcyl7XG4gICAgICAgIHZhciBucG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKSwgXG4gICAgICAgICAgICB3aWR0aCA9IG5vZGUuZ2V0RGF0YSgnd2lkdGgnKSwgXG4gICAgICAgICAgICBoZWlnaHQgPSBub2RlLmdldERhdGEoJ2hlaWdodCcpO1xuICAgICAgICByZXR1cm4gdGhpcy5ub2RlSGVscGVyLnJlY3RhbmdsZS5jb250YWlucyhucG9zLCBwb3MsIHdpZHRoLCBoZWlnaHQpO1xuICAgICAgfVxuICAgIH0sXG4gICAgJ3RyaWFuZ2xlJzoge1xuICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKG5vZGUsIGNhbnZhcyl7XG4gICAgICAgIHZhciBwb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJyk7XG4gICAgICAgIHRoaXMubm9kZUhlbHBlci50cmlhbmdsZS5yZW5kZXIoJ2ZpbGwnLCBwb3MsIGRpbSwgY2FudmFzKTtcbiAgICAgIH0sXG4gICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3MpIHtcbiAgICAgICAgdmFyIG5wb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLCBcbiAgICAgICAgICAgIGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJyk7XG4gICAgICAgIHJldHVybiB0aGlzLm5vZGVIZWxwZXIudHJpYW5nbGUuY29udGFpbnMobnBvcywgcG9zLCBkaW0pO1xuICAgICAgfVxuICAgIH0sXG4gICAgJ3N0YXInOiB7XG4gICAgICAncmVuZGVyJzogZnVuY3Rpb24obm9kZSwgY2FudmFzKXtcbiAgICAgICAgdmFyIHBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSksXG4gICAgICAgICAgICBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpO1xuICAgICAgICB0aGlzLm5vZGVIZWxwZXIuc3Rhci5yZW5kZXIoJ2ZpbGwnLCBwb3MsIGRpbSwgY2FudmFzKTtcbiAgICAgIH0sXG4gICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihub2RlLCBwb3MpIHtcbiAgICAgICAgdmFyIG5wb3MgPSBub2RlLnBvcy5nZXRjKHRydWUpLFxuICAgICAgICAgICAgZGltID0gbm9kZS5nZXREYXRhKCdkaW0nKTtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9kZUhlbHBlci5zdGFyLmNvbnRhaW5zKG5wb3MsIHBvcywgZGltKTtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuXG4gIC8qXG4gICAgRm9yY2VEaXJlY3RlZDNELlBsb3QuRWRnZVR5cGVzXG4gIFxuICAgIFRoaXMgY2xhc3MgY29udGFpbnMgYSBsaXN0IG9mIDxHcmFwaC5BZGphY2VuY2U+IGJ1aWx0LWluIHR5cGVzLiBcbiAgICBFZGdlIHR5cGVzIGltcGxlbWVudGVkIGFyZSAnbm9uZScsICdsaW5lJyBhbmQgJ2Fycm93Jy5cbiAgXG4gICAgWW91IGNhbiBhZGQgeW91ciBjdXN0b20gZWRnZSB0eXBlcywgY3VzdG9taXppbmcgeW91ciB2aXN1YWxpemF0aW9uIHRvIHRoZSBleHRyZW1lLlxuICBcbiAgICBFeGFtcGxlOlxuICBcbiAgICAoc3RhcnQgY29kZSBqcylcbiAgICAgIEZvcmNlRGlyZWN0ZWQzRC5QbG90LkVkZ2VUeXBlcy5pbXBsZW1lbnQoe1xuICAgICAgICAnbXlTcGVjaWFsVHlwZSc6IHtcbiAgICAgICAgICAncmVuZGVyJzogZnVuY3Rpb24oYWRqLCBjYW52YXMpIHtcbiAgICAgICAgICAgIC8vcHJpbnQgeW91ciBjdXN0b20gZWRnZSB0byBjYW52YXNcbiAgICAgICAgICB9LFxuICAgICAgICAgIC8vb3B0aW9uYWxcbiAgICAgICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihhZGosIHBvcykge1xuICAgICAgICAgICAgLy9yZXR1cm4gdHJ1ZSBpZiBwb3MgaXMgaW5zaWRlIHRoZSBhcmMgb3IgZmFsc2Ugb3RoZXJ3aXNlXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAoZW5kIGNvZGUpXG4gIFxuICAqL1xuICBGb3JjZURpcmVjdGVkM0QuUGxvdC5FZGdlVHlwZXMgPSBuZXcgQ2xhc3Moe1xuICAgICdub25lJzogJC5lbXB0eSxcbiAgICAnbGluZSc6IHtcbiAgICAgICdyZW5kZXInOiBmdW5jdGlvbihhZGosIGNhbnZhcykge1xuICAgICAgICB2YXIgZnJvbSA9IGFkai5ub2RlRnJvbS5wb3MuZ2V0Yyh0cnVlKSxcbiAgICAgICAgICAgIHRvID0gYWRqLm5vZGVUby5wb3MuZ2V0Yyh0cnVlKTtcbiAgICAgICAgdGhpcy5lZGdlSGVscGVyLmxpbmUucmVuZGVyKGZyb20sIHRvLCBjYW52YXMpO1xuICAgICAgfSxcbiAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKGFkaiwgcG9zKSB7XG4gICAgICAgIHZhciBmcm9tID0gYWRqLm5vZGVGcm9tLnBvcy5nZXRjKHRydWUpLFxuICAgICAgICAgICAgdG8gPSBhZGoubm9kZVRvLnBvcy5nZXRjKHRydWUpO1xuICAgICAgICByZXR1cm4gdGhpcy5lZGdlSGVscGVyLmxpbmUuY29udGFpbnMoZnJvbSwgdG8sIHBvcywgdGhpcy5lZGdlLmVwc2lsb24pO1xuICAgICAgfVxuICAgIH0sXG4gICAgJ2Fycm93Jzoge1xuICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKGFkaiwgY2FudmFzKSB7XG4gICAgICAgIHZhciBmcm9tID0gYWRqLm5vZGVGcm9tLnBvcy5nZXRjKHRydWUpLFxuICAgICAgICAgICAgdG8gPSBhZGoubm9kZVRvLnBvcy5nZXRjKHRydWUpLFxuICAgICAgICAgICAgZGltID0gYWRqLmdldERhdGEoJ2RpbScpLFxuICAgICAgICAgICAgZGlyZWN0aW9uID0gYWRqLmRhdGEuJGRpcmVjdGlvbixcbiAgICAgICAgICAgIGludiA9IChkaXJlY3Rpb24gJiYgZGlyZWN0aW9uLmxlbmd0aD4xICYmIGRpcmVjdGlvblswXSAhPSBhZGoubm9kZUZyb20uaWQpO1xuICAgICAgICB0aGlzLmVkZ2VIZWxwZXIuYXJyb3cucmVuZGVyKGZyb20sIHRvLCBkaW0sIGludiwgY2FudmFzKTtcbiAgICAgIH0sXG4gICAgICAnY29udGFpbnMnOiBmdW5jdGlvbihhZGosIHBvcykge1xuICAgICAgICB2YXIgZnJvbSA9IGFkai5ub2RlRnJvbS5wb3MuZ2V0Yyh0cnVlKSxcbiAgICAgICAgICAgIHRvID0gYWRqLm5vZGVUby5wb3MuZ2V0Yyh0cnVlKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuZWRnZUhlbHBlci5hcnJvdy5jb250YWlucyhmcm9tLCB0bywgcG9zLCB0aGlzLmVkZ2UuZXBzaWxvbik7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxufSkoJGppdC5Gb3JjZURpcmVjdGVkM0QpO1xuXG4vLyBTVEFSVCBNRVRBTUFQUyBDT0RFXG5leHBvcnQgZGVmYXVsdCAkaml0XG4vLyBFTkQgTUVUQU1BUFMgQ09ERVxuXG5cblxuLy8gV0VCUEFDSyBGT09URVIgLy9cbi8vIGZyb250ZW5kL3NyYy9wYXRjaGVkL0pJVC5qcyJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBdUJBOzs7OztBQUtBOzs7Ozs7O0FBT0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7QUFZQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQWNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7O0FBT0E7QUFDQTs7Ozs7Ozs7Ozs7QUFXQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7OztBQWVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW1CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FBSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTBDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBMkRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWtEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXVKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQUlBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXlDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBNENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF3Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBcUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBd0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFxRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7QUFXQTs7Ozs7Ozs7O0FBU0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7OztBQU9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7OztBQVFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FBYUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW1DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFqU0E7QUFDQTtBQUNBO0FBbVNBOzs7OztBQUtBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7O0FBV0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7OztBQVlBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQWNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7OztBQWlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWtCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFrQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWtCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFtQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FBYUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7O0FBU0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7O0FBU0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQWFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7QUFhQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FBYUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQWFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQWFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FBYUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7QUFNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7QUFTQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FBYUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7O0FBV0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7O0FBU0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7QUFTQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7QUFTQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7QUFNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7OztBQWVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7QUFTQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7QUFNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7O0FBTUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUEwQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUE0QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF1QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQWFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWtCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW9DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQWNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FBYUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBb0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7O0FBU0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7OztBQWVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFtQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7OztBQVNBO0FBQ0E7Ozs7O0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFzQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBeUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF5QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXFCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBMEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7OztBQWlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBd0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW9CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFrQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBMEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFxQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7OztBQVlBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7OztBQWdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTs7Ozs7OztBQU9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWlDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWlDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBaURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBeUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBeUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXVDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBaUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpQkE7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBaUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7OztBQWdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTtBQUNBOzs7QUFHQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7O0FBZ0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBaUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFrQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7OztBQWlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7OztBQWlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7OztBQWVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7OztBQVlBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW1CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUFJQTs7Ozs7Ozs7OztBQVVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFaQTtBQUNBO0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBK0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7QUFXQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUE4Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBaUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7QUFXQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7O0FBV0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0JBO0FBQ0E7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFtQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7QUFhQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7OztBQWlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXNCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7OztBQWdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7OztBQWVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7QUFVQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBcUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7QUFTQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBcUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW9IQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7OztBQWVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7QUFXQTs7Ozs7O0FBTUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQVdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUE2Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7O0FBTUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBc0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7OztBQWVBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQWNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7OztBQVlBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7OztBQVlBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF5QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBeUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FBSUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTBDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWdFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUFLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7O0FBZUE7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQWNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7O0FBWUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7O0FBWUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF5QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBeUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUFJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FBSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUFJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUFJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBOzs7Ozs7Ozs7O0FBVUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBZ0VBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7O0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQWNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQWNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7QUFlQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUFjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7QUFZQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQWNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7QUFZQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF5QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=");

TODO found
Open

    eval("/**\n * Copyright 2013-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n *\n */\n\n'use strict';\n\nvar ReactRef = __webpack_require__(233);\nvar ReactInstrumentation = __webpack_require__(235);\n\nvar warning = __webpack_require__(183);\n\n/**\n * Helper to call ReactRef.attachRefs with this composite component, split out\n * to avoid allocations in the transaction mount-ready queue.\n */\nfunction attachRefs() {\n  ReactRef.attachRefs(this, this._currentElement);\n}\n\nvar ReactReconciler = {\n\n  /**\n   * Initializes the component, renders markup, and registers event listeners.\n   *\n   * @param {ReactComponent} internalInstance\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {?object} the containing host component instance\n   * @param {?object} info about the host container\n   * @return {?string} Rendered markup to be inserted into the DOM.\n   * @final\n   * @internal\n   */\n  mountComponent: function (internalInstance, transaction, hostParent, hostContainerInfo, context, parentDebugID // 0 in production and for roots\n  ) {\n    if (true) {\n      if (internalInstance._debugID !== 0) {\n        ReactInstrumentation.debugTool.onBeforeMountComponent(internalInstance._debugID, internalInstance._currentElement, parentDebugID);\n      }\n    }\n    var markup = internalInstance.mountComponent(transaction, hostParent, hostContainerInfo, context, parentDebugID);\n    if (internalInstance._currentElement && internalInstance._currentElement.ref != null) {\n      transaction.getReactMountReady().enqueue(attachRefs, internalInstance);\n    }\n    if (true) {\n      if (internalInstance._debugID !== 0) {\n        ReactInstrumentation.debugTool.onMountComponent(internalInstance._debugID);\n      }\n    }\n    return markup;\n  },\n\n  /**\n   * Returns a value that can be passed to\n   * ReactComponentEnvironment.replaceNodeWithMarkup.\n   */\n  getHostNode: function (internalInstance) {\n    return internalInstance.getHostNode();\n  },\n\n  /**\n   * Releases any resources allocated by `mountComponent`.\n   *\n   * @final\n   * @internal\n   */\n  unmountComponent: function (internalInstance, safely) {\n    if (true) {\n      if (internalInstance._debugID !== 0) {\n        ReactInstrumentation.debugTool.onBeforeUnmountComponent(internalInstance._debugID);\n      }\n    }\n    ReactRef.detachRefs(internalInstance, internalInstance._currentElement);\n    internalInstance.unmountComponent(safely);\n    if (true) {\n      if (internalInstance._debugID !== 0) {\n        ReactInstrumentation.debugTool.onUnmountComponent(internalInstance._debugID);\n      }\n    }\n  },\n\n  /**\n   * Update a component using a new element.\n   *\n   * @param {ReactComponent} internalInstance\n   * @param {ReactElement} nextElement\n   * @param {ReactReconcileTransaction} transaction\n   * @param {object} context\n   * @internal\n   */\n  receiveComponent: function (internalInstance, nextElement, transaction, context) {\n    var prevElement = internalInstance._currentElement;\n\n    if (nextElement === prevElement && context === internalInstance._context) {\n      // Since elements are immutable after the owner is rendered,\n      // we can do a cheap identity compare here to determine if this is a\n      // superfluous reconcile. It's possible for state to be mutable but such\n      // change should trigger an update of the owner which would recreate\n      // the element. We explicitly check for the existence of an owner since\n      // it's possible for an element created outside a composite to be\n      // deeply mutated and reused.\n\n      // TODO: Bailing out early is just a perf optimization right?\n      // TODO: Removing the return statement should affect correctness?\n      return;\n    }\n\n    if (true) {\n      if (internalInstance._debugID !== 0) {\n        ReactInstrumentation.debugTool.onBeforeUpdateComponent(internalInstance._debugID, nextElement);\n      }\n    }\n\n    var refsChanged = ReactRef.shouldUpdateRefs(prevElement, nextElement);\n\n    if (refsChanged) {\n      ReactRef.detachRefs(internalInstance, prevElement);\n    }\n\n    internalInstance.receiveComponent(nextElement, transaction, context);\n\n    if (refsChanged && internalInstance._currentElement && internalInstance._currentElement.ref != null) {\n      transaction.getReactMountReady().enqueue(attachRefs, internalInstance);\n    }\n\n    if (true) {\n      if (internalInstance._debugID !== 0) {\n        ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);\n      }\n    }\n  },\n\n  /**\n   * Flush any dirty changes in a component.\n   *\n   * @param {ReactComponent} internalInstance\n   * @param {ReactReconcileTransaction} transaction\n   * @internal\n   */\n  performUpdateIfNecessary: function (internalInstance, transaction, updateBatchNumber) {\n    if (internalInstance._updateBatchNumber !== updateBatchNumber) {\n      // The component's enqueued batch number should always be the current\n      // batch or the following one.\n       true ? warning(internalInstance._updateBatchNumber == null || internalInstance._updateBatchNumber === updateBatchNumber + 1, 'performUpdateIfNecessary: Unexpected batch number (current %s, ' + 'pending %s)', updateBatchNumber, internalInstance._updateBatchNumber) : void 0;\n      return;\n    }\n    if (true) {\n      if (internalInstance._debugID !== 0) {\n        ReactInstrumentation.debugTool.onBeforeUpdateComponent(internalInstance._debugID, internalInstance._currentElement);\n      }\n    }\n    internalInstance.performUpdateIfNecessary(transaction);\n    if (true) {\n      if (internalInstance._debugID !== 0) {\n        ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);\n      }\n    }\n  }\n\n};\n\nmodule.exports = ReactReconciler;//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMjMyLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9yZWFjdC1kb20vbGliL1JlYWN0UmVjb25jaWxlci5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvcHlyaWdodCAyMDEzLXByZXNlbnQsIEZhY2Vib29rLCBJbmMuXG4gKiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIEJTRC1zdHlsZSBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLiBBbiBhZGRpdGlvbmFsIGdyYW50XG4gKiBvZiBwYXRlbnQgcmlnaHRzIGNhbiBiZSBmb3VuZCBpbiB0aGUgUEFURU5UUyBmaWxlIGluIHRoZSBzYW1lIGRpcmVjdG9yeS5cbiAqXG4gKi9cblxuJ3VzZSBzdHJpY3QnO1xuXG52YXIgUmVhY3RSZWYgPSByZXF1aXJlKCcuL1JlYWN0UmVmJyk7XG52YXIgUmVhY3RJbnN0cnVtZW50YXRpb24gPSByZXF1aXJlKCcuL1JlYWN0SW5zdHJ1bWVudGF0aW9uJyk7XG5cbnZhciB3YXJuaW5nID0gcmVxdWlyZSgnZmJqcy9saWIvd2FybmluZycpO1xuXG4vKipcbiAqIEhlbHBlciB0byBjYWxsIFJlYWN0UmVmLmF0dGFjaFJlZnMgd2l0aCB0aGlzIGNvbXBvc2l0ZSBjb21wb25lbnQsIHNwbGl0IG91dFxuICogdG8gYXZvaWQgYWxsb2NhdGlvbnMgaW4gdGhlIHRyYW5zYWN0aW9uIG1vdW50LXJlYWR5IHF1ZXVlLlxuICovXG5mdW5jdGlvbiBhdHRhY2hSZWZzKCkge1xuICBSZWFjdFJlZi5hdHRhY2hSZWZzKHRoaXMsIHRoaXMuX2N1cnJlbnRFbGVtZW50KTtcbn1cblxudmFyIFJlYWN0UmVjb25jaWxlciA9IHtcblxuICAvKipcbiAgICogSW5pdGlhbGl6ZXMgdGhlIGNvbXBvbmVudCwgcmVuZGVycyBtYXJrdXAsIGFuZCByZWdpc3RlcnMgZXZlbnQgbGlzdGVuZXJzLlxuICAgKlxuICAgKiBAcGFyYW0ge1JlYWN0Q29tcG9uZW50fSBpbnRlcm5hbEluc3RhbmNlXG4gICAqIEBwYXJhbSB7UmVhY3RSZWNvbmNpbGVUcmFuc2FjdGlvbnxSZWFjdFNlcnZlclJlbmRlcmluZ1RyYW5zYWN0aW9ufSB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0gez9vYmplY3R9IHRoZSBjb250YWluaW5nIGhvc3QgY29tcG9uZW50IGluc3RhbmNlXG4gICAqIEBwYXJhbSB7P29iamVjdH0gaW5mbyBhYm91dCB0aGUgaG9zdCBjb250YWluZXJcbiAgICogQHJldHVybiB7P3N0cmluZ30gUmVuZGVyZWQgbWFya3VwIHRvIGJlIGluc2VydGVkIGludG8gdGhlIERPTS5cbiAgICogQGZpbmFsXG4gICAqIEBpbnRlcm5hbFxuICAgKi9cbiAgbW91bnRDb21wb25lbnQ6IGZ1bmN0aW9uIChpbnRlcm5hbEluc3RhbmNlLCB0cmFuc2FjdGlvbiwgaG9zdFBhcmVudCwgaG9zdENvbnRhaW5lckluZm8sIGNvbnRleHQsIHBhcmVudERlYnVnSUQgLy8gMCBpbiBwcm9kdWN0aW9uIGFuZCBmb3Igcm9vdHNcbiAgKSB7XG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgIGlmIChpbnRlcm5hbEluc3RhbmNlLl9kZWJ1Z0lEICE9PSAwKSB7XG4gICAgICAgIFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vbkJlZm9yZU1vdW50Q29tcG9uZW50KGludGVybmFsSW5zdGFuY2UuX2RlYnVnSUQsIGludGVybmFsSW5zdGFuY2UuX2N1cnJlbnRFbGVtZW50LCBwYXJlbnREZWJ1Z0lEKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdmFyIG1hcmt1cCA9IGludGVybmFsSW5zdGFuY2UubW91bnRDb21wb25lbnQodHJhbnNhY3Rpb24sIGhvc3RQYXJlbnQsIGhvc3RDb250YWluZXJJbmZvLCBjb250ZXh0LCBwYXJlbnREZWJ1Z0lEKTtcbiAgICBpZiAoaW50ZXJuYWxJbnN0YW5jZS5fY3VycmVudEVsZW1lbnQgJiYgaW50ZXJuYWxJbnN0YW5jZS5fY3VycmVudEVsZW1lbnQucmVmICE9IG51bGwpIHtcbiAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoYXR0YWNoUmVmcywgaW50ZXJuYWxJbnN0YW5jZSk7XG4gICAgfVxuICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICBpZiAoaW50ZXJuYWxJbnN0YW5jZS5fZGVidWdJRCAhPT0gMCkge1xuICAgICAgICBSZWFjdEluc3RydW1lbnRhdGlvbi5kZWJ1Z1Rvb2wub25Nb3VudENvbXBvbmVudChpbnRlcm5hbEluc3RhbmNlLl9kZWJ1Z0lEKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1hcmt1cDtcbiAgfSxcblxuICAvKipcbiAgICogUmV0dXJucyBhIHZhbHVlIHRoYXQgY2FuIGJlIHBhc3NlZCB0b1xuICAgKiBSZWFjdENvbXBvbmVudEVudmlyb25tZW50LnJlcGxhY2VOb2RlV2l0aE1hcmt1cC5cbiAgICovXG4gIGdldEhvc3ROb2RlOiBmdW5jdGlvbiAoaW50ZXJuYWxJbnN0YW5jZSkge1xuICAgIHJldHVybiBpbnRlcm5hbEluc3RhbmNlLmdldEhvc3ROb2RlKCk7XG4gIH0sXG5cbiAgLyoqXG4gICAqIFJlbGVhc2VzIGFueSByZXNvdXJjZXMgYWxsb2NhdGVkIGJ5IGBtb3VudENvbXBvbmVudGAuXG4gICAqXG4gICAqIEBmaW5hbFxuICAgKiBAaW50ZXJuYWxcbiAgICovXG4gIHVubW91bnRDb21wb25lbnQ6IGZ1bmN0aW9uIChpbnRlcm5hbEluc3RhbmNlLCBzYWZlbHkpIHtcbiAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJykge1xuICAgICAgaWYgKGludGVybmFsSW5zdGFuY2UuX2RlYnVnSUQgIT09IDApIHtcbiAgICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uQmVmb3JlVW5tb3VudENvbXBvbmVudChpbnRlcm5hbEluc3RhbmNlLl9kZWJ1Z0lEKTtcbiAgICAgIH1cbiAgICB9XG4gICAgUmVhY3RSZWYuZGV0YWNoUmVmcyhpbnRlcm5hbEluc3RhbmNlLCBpbnRlcm5hbEluc3RhbmNlLl9jdXJyZW50RWxlbWVudCk7XG4gICAgaW50ZXJuYWxJbnN0YW5jZS51bm1vdW50Q29tcG9uZW50KHNhZmVseSk7XG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgIGlmIChpbnRlcm5hbEluc3RhbmNlLl9kZWJ1Z0lEICE9PSAwKSB7XG4gICAgICAgIFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vblVubW91bnRDb21wb25lbnQoaW50ZXJuYWxJbnN0YW5jZS5fZGVidWdJRCk7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIC8qKlxuICAgKiBVcGRhdGUgYSBjb21wb25lbnQgdXNpbmcgYSBuZXcgZWxlbWVudC5cbiAgICpcbiAgICogQHBhcmFtIHtSZWFjdENvbXBvbmVudH0gaW50ZXJuYWxJbnN0YW5jZVxuICAgKiBAcGFyYW0ge1JlYWN0RWxlbWVudH0gbmV4dEVsZW1lbnRcbiAgICogQHBhcmFtIHtSZWFjdFJlY29uY2lsZVRyYW5zYWN0aW9ufSB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge29iamVjdH0gY29udGV4dFxuICAgKiBAaW50ZXJuYWxcbiAgICovXG4gIHJlY2VpdmVDb21wb25lbnQ6IGZ1bmN0aW9uIChpbnRlcm5hbEluc3RhbmNlLCBuZXh0RWxlbWVudCwgdHJhbnNhY3Rpb24sIGNvbnRleHQpIHtcbiAgICB2YXIgcHJldkVsZW1lbnQgPSBpbnRlcm5hbEluc3RhbmNlLl9jdXJyZW50RWxlbWVudDtcblxuICAgIGlmIChuZXh0RWxlbWVudCA9PT0gcHJldkVsZW1lbnQgJiYgY29udGV4dCA9PT0gaW50ZXJuYWxJbnN0YW5jZS5fY29udGV4dCkge1xuICAgICAgLy8gU2luY2UgZWxlbWVudHMgYXJlIGltbXV0YWJsZSBhZnRlciB0aGUgb3duZXIgaXMgcmVuZGVyZWQsXG4gICAgICAvLyB3ZSBjYW4gZG8gYSBjaGVhcCBpZGVudGl0eSBjb21wYXJlIGhlcmUgdG8gZGV0ZXJtaW5lIGlmIHRoaXMgaXMgYVxuICAgICAgLy8gc3VwZXJmbHVvdXMgcmVjb25jaWxlLiBJdCdzIHBvc3NpYmxlIGZvciBzdGF0ZSB0byBiZSBtdXRhYmxlIGJ1dCBzdWNoXG4gICAgICAvLyBjaGFuZ2Ugc2hvdWxkIHRyaWdnZXIgYW4gdXBkYXRlIG9mIHRoZSBvd25lciB3aGljaCB3b3VsZCByZWNyZWF0ZVxuICAgICAgLy8gdGhlIGVsZW1lbnQuIFdlIGV4cGxpY2l0bHkgY2hlY2sgZm9yIHRoZSBleGlzdGVuY2Ugb2YgYW4gb3duZXIgc2luY2VcbiAgICAgIC8vIGl0J3MgcG9zc2libGUgZm9yIGFuIGVsZW1lbnQgY3JlYXRlZCBvdXRzaWRlIGEgY29tcG9zaXRlIHRvIGJlXG4gICAgICAvLyBkZWVwbHkgbXV0YXRlZCBhbmQgcmV1c2VkLlxuXG4gICAgICAvLyBUT0RPOiBCYWlsaW5nIG91dCBlYXJseSBpcyBqdXN0IGEgcGVyZiBvcHRpbWl6YXRpb24gcmlnaHQ/XG4gICAgICAvLyBUT0RPOiBSZW1vdmluZyB0aGUgcmV0dXJuIHN0YXRlbWVudCBzaG91bGQgYWZmZWN0IGNvcnJlY3RuZXNzP1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICBpZiAoaW50ZXJuYWxJbnN0YW5jZS5fZGVidWdJRCAhPT0gMCkge1xuICAgICAgICBSZWFjdEluc3RydW1lbnRhdGlvbi5kZWJ1Z1Rvb2wub25CZWZvcmVVcGRhdGVDb21wb25lbnQoaW50ZXJuYWxJbnN0YW5jZS5fZGVidWdJRCwgbmV4dEVsZW1lbnQpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHZhciByZWZzQ2hhbmdlZCA9IFJlYWN0UmVmLnNob3VsZFVwZGF0ZVJlZnMocHJldkVsZW1lbnQsIG5leHRFbGVtZW50KTtcblxuICAgIGlmIChyZWZzQ2hhbmdlZCkge1xuICAgICAgUmVhY3RSZWYuZGV0YWNoUmVmcyhpbnRlcm5hbEluc3RhbmNlLCBwcmV2RWxlbWVudCk7XG4gICAgfVxuXG4gICAgaW50ZXJuYWxJbnN0YW5jZS5yZWNlaXZlQ29tcG9uZW50KG5leHRFbGVtZW50LCB0cmFuc2FjdGlvbiwgY29udGV4dCk7XG5cbiAgICBpZiAocmVmc0NoYW5nZWQgJiYgaW50ZXJuYWxJbnN0YW5jZS5fY3VycmVudEVsZW1lbnQgJiYgaW50ZXJuYWxJbnN0YW5jZS5fY3VycmVudEVsZW1lbnQucmVmICE9IG51bGwpIHtcbiAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoYXR0YWNoUmVmcywgaW50ZXJuYWxJbnN0YW5jZSk7XG4gICAgfVxuXG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgIGlmIChpbnRlcm5hbEluc3RhbmNlLl9kZWJ1Z0lEICE9PSAwKSB7XG4gICAgICAgIFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vblVwZGF0ZUNvbXBvbmVudChpbnRlcm5hbEluc3RhbmNlLl9kZWJ1Z0lEKTtcbiAgICAgIH1cbiAgICB9XG4gIH0sXG5cbiAgLyoqXG4gICAqIEZsdXNoIGFueSBkaXJ0eSBjaGFuZ2VzIGluIGEgY29tcG9uZW50LlxuICAgKlxuICAgKiBAcGFyYW0ge1JlYWN0Q29tcG9uZW50fSBpbnRlcm5hbEluc3RhbmNlXG4gICAqIEBwYXJhbSB7UmVhY3RSZWNvbmNpbGVUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQGludGVybmFsXG4gICAqL1xuICBwZXJmb3JtVXBkYXRlSWZOZWNlc3Nhcnk6IGZ1bmN0aW9uIChpbnRlcm5hbEluc3RhbmNlLCB0cmFuc2FjdGlvbiwgdXBkYXRlQmF0Y2hOdW1iZXIpIHtcbiAgICBpZiAoaW50ZXJuYWxJbnN0YW5jZS5fdXBkYXRlQmF0Y2hOdW1iZXIgIT09IHVwZGF0ZUJhdGNoTnVtYmVyKSB7XG4gICAgICAvLyBUaGUgY29tcG9uZW50J3MgZW5xdWV1ZWQgYmF0Y2ggbnVtYmVyIHNob3VsZCBhbHdheXMgYmUgdGhlIGN1cnJlbnRcbiAgICAgIC8vIGJhdGNoIG9yIHRoZSBmb2xsb3dpbmcgb25lLlxuICAgICAgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IHdhcm5pbmcoaW50ZXJuYWxJbnN0YW5jZS5fdXBkYXRlQmF0Y2hOdW1iZXIgPT0gbnVsbCB8fCBpbnRlcm5hbEluc3RhbmNlLl91cGRhdGVCYXRjaE51bWJlciA9PT0gdXBkYXRlQmF0Y2hOdW1iZXIgKyAxLCAncGVyZm9ybVVwZGF0ZUlmTmVjZXNzYXJ5OiBVbmV4cGVjdGVkIGJhdGNoIG51bWJlciAoY3VycmVudCAlcywgJyArICdwZW5kaW5nICVzKScsIHVwZGF0ZUJhdGNoTnVtYmVyLCBpbnRlcm5hbEluc3RhbmNlLl91cGRhdGVCYXRjaE51bWJlcikgOiB2b2lkIDA7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICBpZiAoaW50ZXJuYWxJbnN0YW5jZS5fZGVidWdJRCAhPT0gMCkge1xuICAgICAgICBSZWFjdEluc3RydW1lbnRhdGlvbi5kZWJ1Z1Rvb2wub25CZWZvcmVVcGRhdGVDb21wb25lbnQoaW50ZXJuYWxJbnN0YW5jZS5fZGVidWdJRCwgaW50ZXJuYWxJbnN0YW5jZS5fY3VycmVudEVsZW1lbnQpO1xuICAgICAgfVxuICAgIH1cbiAgICBpbnRlcm5hbEluc3RhbmNlLnBlcmZvcm1VcGRhdGVJZk5lY2Vzc2FyeSh0cmFuc2FjdGlvbik7XG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgIGlmIChpbnRlcm5hbEluc3RhbmNlLl9kZWJ1Z0lEICE9PSAwKSB7XG4gICAgICAgIFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vblVwZGF0ZUNvbXBvbmVudChpbnRlcm5hbEluc3RhbmNlLl9kZWJ1Z0lEKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBSZWFjdFJlY29uY2lsZXI7XG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9+L3JlYWN0LWRvbS9saWIvUmVhY3RSZWNvbmNpbGVyLmpzXG4vLyBtb2R1bGUgaWQgPSAyMzJcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlUm9vdCI6IiJ9");

TODO found
Open

    eval("/**\n * Copyright 2013-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n *\n */\n\n/* global hasOwnProperty:true */\n\n'use strict';\n\nvar _prodInvariant = __webpack_require__(208),\n    _assign = __webpack_require__(176);\n\nvar AutoFocusUtils = __webpack_require__(266);\nvar CSSPropertyOperations = __webpack_require__(268);\nvar DOMLazyTree = __webpack_require__(254);\nvar DOMNamespaces = __webpack_require__(255);\nvar DOMProperty = __webpack_require__(209);\nvar DOMPropertyOperations = __webpack_require__(276);\nvar EventPluginHub = __webpack_require__(215);\nvar EventPluginRegistry = __webpack_require__(216);\nvar ReactBrowserEventEmitter = __webpack_require__(278);\nvar ReactDOMComponentFlags = __webpack_require__(210);\nvar ReactDOMComponentTree = __webpack_require__(207);\nvar ReactDOMInput = __webpack_require__(281);\nvar ReactDOMOption = __webpack_require__(284);\nvar ReactDOMSelect = __webpack_require__(285);\nvar ReactDOMTextarea = __webpack_require__(286);\nvar ReactInstrumentation = __webpack_require__(235);\nvar ReactMultiChild = __webpack_require__(287);\nvar ReactServerRenderingTransaction = __webpack_require__(306);\n\nvar emptyFunction = __webpack_require__(184);\nvar escapeTextContentForBrowser = __webpack_require__(259);\nvar invariant = __webpack_require__(180);\nvar isEventSupported = __webpack_require__(243);\nvar shallowEqual = __webpack_require__(296);\nvar validateDOMNesting = __webpack_require__(309);\nvar warning = __webpack_require__(183);\n\nvar Flags = ReactDOMComponentFlags;\nvar deleteListener = EventPluginHub.deleteListener;\nvar getNode = ReactDOMComponentTree.getNodeFromInstance;\nvar listenTo = ReactBrowserEventEmitter.listenTo;\nvar registrationNameModules = EventPluginRegistry.registrationNameModules;\n\n// For quickly matching children type, to test if can be treated as content.\nvar CONTENT_TYPES = { 'string': true, 'number': true };\n\nvar STYLE = 'style';\nvar HTML = '__html';\nvar RESERVED_PROPS = {\n  children: null,\n  dangerouslySetInnerHTML: null,\n  suppressContentEditableWarning: null\n};\n\n// Node type for document fragments (Node.DOCUMENT_FRAGMENT_NODE).\nvar DOC_FRAGMENT_TYPE = 11;\n\nfunction getDeclarationErrorAddendum(internalInstance) {\n  if (internalInstance) {\n    var owner = internalInstance._currentElement._owner || null;\n    if (owner) {\n      var name = owner.getName();\n      if (name) {\n        return ' This DOM node was rendered by `' + name + '`.';\n      }\n    }\n  }\n  return '';\n}\n\nfunction friendlyStringify(obj) {\n  if (typeof obj === 'object') {\n    if (Array.isArray(obj)) {\n      return '[' + obj.map(friendlyStringify).join(', ') + ']';\n    } else {\n      var pairs = [];\n      for (var key in obj) {\n        if (Object.prototype.hasOwnProperty.call(obj, key)) {\n          var keyEscaped = /^[a-z$_][\\w$_]*$/i.test(key) ? key : JSON.stringify(key);\n          pairs.push(keyEscaped + ': ' + friendlyStringify(obj[key]));\n        }\n      }\n      return '{' + pairs.join(', ') + '}';\n    }\n  } else if (typeof obj === 'string') {\n    return JSON.stringify(obj);\n  } else if (typeof obj === 'function') {\n    return '[function object]';\n  }\n  // Differs from JSON.stringify in that undefined because undefined and that\n  // inf and nan don't become null\n  return String(obj);\n}\n\nvar styleMutationWarning = {};\n\nfunction checkAndWarnForMutatedStyle(style1, style2, component) {\n  if (style1 == null || style2 == null) {\n    return;\n  }\n  if (shallowEqual(style1, style2)) {\n    return;\n  }\n\n  var componentName = component._tag;\n  var owner = component._currentElement._owner;\n  var ownerName;\n  if (owner) {\n    ownerName = owner.getName();\n  }\n\n  var hash = ownerName + '|' + componentName;\n\n  if (styleMutationWarning.hasOwnProperty(hash)) {\n    return;\n  }\n\n  styleMutationWarning[hash] = true;\n\n   true ? warning(false, '`%s` was passed a style object that has previously been mutated. ' + 'Mutating `style` is deprecated. Consider cloning it beforehand. Check ' + 'the `render` %s. Previous style: %s. Mutated style: %s.', componentName, owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>', friendlyStringify(style1), friendlyStringify(style2)) : void 0;\n}\n\n/**\n * @param {object} component\n * @param {?object} props\n */\nfunction assertValidProps(component, props) {\n  if (!props) {\n    return;\n  }\n  // Note the use of `==` which checks for null or undefined.\n  if (voidElementTags[component._tag]) {\n    !(props.children == null && props.dangerouslySetInnerHTML == null) ?  true ? invariant(false, '%s is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.%s', component._tag, component._currentElement._owner ? ' Check the render method of ' + component._currentElement._owner.getName() + '.' : '') : _prodInvariant('137', component._tag, component._currentElement._owner ? ' Check the render method of ' + component._currentElement._owner.getName() + '.' : '') : void 0;\n  }\n  if (props.dangerouslySetInnerHTML != null) {\n    !(props.children == null) ?  true ? invariant(false, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.') : _prodInvariant('60') : void 0;\n    !(typeof props.dangerouslySetInnerHTML === 'object' && HTML in props.dangerouslySetInnerHTML) ?  true ? invariant(false, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit https://fb.me/react-invariant-dangerously-set-inner-html for more information.') : _prodInvariant('61') : void 0;\n  }\n  if (true) {\n     true ? warning(props.innerHTML == null, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.') : void 0;\n     true ? warning(props.suppressContentEditableWarning || !props.contentEditable || props.children == null, 'A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.') : void 0;\n     true ? warning(props.onFocusIn == null && props.onFocusOut == null, 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' + 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' + 'are not needed/supported by React.') : void 0;\n  }\n  !(props.style == null || typeof props.style === 'object') ?  true ? invariant(false, 'The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + \\'em\\'}} when using JSX.%s', getDeclarationErrorAddendum(component)) : _prodInvariant('62', getDeclarationErrorAddendum(component)) : void 0;\n}\n\nfunction enqueuePutListener(inst, registrationName, listener, transaction) {\n  if (transaction instanceof ReactServerRenderingTransaction) {\n    return;\n  }\n  if (true) {\n    // IE8 has no API for event capturing and the `onScroll` event doesn't\n    // bubble.\n     true ? warning(registrationName !== 'onScroll' || isEventSupported('scroll', true), 'This browser doesn\\'t support the `onScroll` event') : void 0;\n  }\n  var containerInfo = inst._hostContainerInfo;\n  var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;\n  var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;\n  listenTo(registrationName, doc);\n  transaction.getReactMountReady().enqueue(putListener, {\n    inst: inst,\n    registrationName: registrationName,\n    listener: listener\n  });\n}\n\nfunction putListener() {\n  var listenerToPut = this;\n  EventPluginHub.putListener(listenerToPut.inst, listenerToPut.registrationName, listenerToPut.listener);\n}\n\nfunction inputPostMount() {\n  var inst = this;\n  ReactDOMInput.postMountWrapper(inst);\n}\n\nfunction textareaPostMount() {\n  var inst = this;\n  ReactDOMTextarea.postMountWrapper(inst);\n}\n\nfunction optionPostMount() {\n  var inst = this;\n  ReactDOMOption.postMountWrapper(inst);\n}\n\nvar setAndValidateContentChildDev = emptyFunction;\nif (true) {\n  setAndValidateContentChildDev = function (content) {\n    var hasExistingContent = this._contentDebugID != null;\n    var debugID = this._debugID;\n    // This ID represents the inlined child that has no backing instance:\n    var contentDebugID = -debugID;\n\n    if (content == null) {\n      if (hasExistingContent) {\n        ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);\n      }\n      this._contentDebugID = null;\n      return;\n    }\n\n    validateDOMNesting(null, String(content), this, this._ancestorInfo);\n    this._contentDebugID = contentDebugID;\n    if (hasExistingContent) {\n      ReactInstrumentation.debugTool.onBeforeUpdateComponent(contentDebugID, content);\n      ReactInstrumentation.debugTool.onUpdateComponent(contentDebugID);\n    } else {\n      ReactInstrumentation.debugTool.onBeforeMountComponent(contentDebugID, content, debugID);\n      ReactInstrumentation.debugTool.onMountComponent(contentDebugID);\n      ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]);\n    }\n  };\n}\n\n// There are so many media events, it makes sense to just\n// maintain a list rather than create a `trapBubbledEvent` for each\nvar mediaEvents = {\n  topAbort: 'abort',\n  topCanPlay: 'canplay',\n  topCanPlayThrough: 'canplaythrough',\n  topDurationChange: 'durationchange',\n  topEmptied: 'emptied',\n  topEncrypted: 'encrypted',\n  topEnded: 'ended',\n  topError: 'error',\n  topLoadedData: 'loadeddata',\n  topLoadedMetadata: 'loadedmetadata',\n  topLoadStart: 'loadstart',\n  topPause: 'pause',\n  topPlay: 'play',\n  topPlaying: 'playing',\n  topProgress: 'progress',\n  topRateChange: 'ratechange',\n  topSeeked: 'seeked',\n  topSeeking: 'seeking',\n  topStalled: 'stalled',\n  topSuspend: 'suspend',\n  topTimeUpdate: 'timeupdate',\n  topVolumeChange: 'volumechange',\n  topWaiting: 'waiting'\n};\n\nfunction trapBubbledEventsLocal() {\n  var inst = this;\n  // If a component renders to null or if another component fatals and causes\n  // the state of the tree to be corrupted, `node` here can be null.\n  !inst._rootNodeID ?  true ? invariant(false, 'Must be mounted to trap events') : _prodInvariant('63') : void 0;\n  var node = getNode(inst);\n  !node ?  true ? invariant(false, 'trapBubbledEvent(...): Requires node to be rendered.') : _prodInvariant('64') : void 0;\n\n  switch (inst._tag) {\n    case 'iframe':\n    case 'object':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topLoad', 'load', node)];\n      break;\n    case 'video':\n    case 'audio':\n\n      inst._wrapperState.listeners = [];\n      // Create listener for each media event\n      for (var event in mediaEvents) {\n        if (mediaEvents.hasOwnProperty(event)) {\n          inst._wrapperState.listeners.push(ReactBrowserEventEmitter.trapBubbledEvent(event, mediaEvents[event], node));\n        }\n      }\n      break;\n    case 'source':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topError', 'error', node)];\n      break;\n    case 'img':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topError', 'error', node), ReactBrowserEventEmitter.trapBubbledEvent('topLoad', 'load', node)];\n      break;\n    case 'form':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topReset', 'reset', node), ReactBrowserEventEmitter.trapBubbledEvent('topSubmit', 'submit', node)];\n      break;\n    case 'input':\n    case 'select':\n    case 'textarea':\n      inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent('topInvalid', 'invalid', node)];\n      break;\n  }\n}\n\nfunction postUpdateSelectWrapper() {\n  ReactDOMSelect.postUpdateWrapper(this);\n}\n\n// For HTML, certain tags should omit their close tag. We keep a whitelist for\n// those special-case tags.\n\nvar omittedCloseTags = {\n  'area': true,\n  'base': true,\n  'br': true,\n  'col': true,\n  'embed': true,\n  'hr': true,\n  'img': true,\n  'input': true,\n  'keygen': true,\n  'link': true,\n  'meta': true,\n  'param': true,\n  'source': true,\n  'track': true,\n  'wbr': true\n};\n\nvar newlineEatingTags = {\n  'listing': true,\n  'pre': true,\n  'textarea': true\n};\n\n// For HTML, certain tags cannot have children. This has the same purpose as\n// `omittedCloseTags` except that `menuitem` should still have its closing tag.\n\nvar voidElementTags = _assign({\n  'menuitem': true\n}, omittedCloseTags);\n\n// We accept any tag to be rendered but since this gets injected into arbitrary\n// HTML, we want to make sure that it's a safe tag.\n// http://www.w3.org/TR/REC-xml/#NT-Name\n\nvar VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\\.\\-\\d]*$/; // Simplified subset\nvar validatedTagCache = {};\nvar hasOwnProperty = {}.hasOwnProperty;\n\nfunction validateDangerousTag(tag) {\n  if (!hasOwnProperty.call(validatedTagCache, tag)) {\n    !VALID_TAG_REGEX.test(tag) ?  true ? invariant(false, 'Invalid tag: %s', tag) : _prodInvariant('65', tag) : void 0;\n    validatedTagCache[tag] = true;\n  }\n}\n\nfunction isCustomComponent(tagName, props) {\n  return tagName.indexOf('-') >= 0 || props.is != null;\n}\n\nvar globalIdCounter = 1;\n\n/**\n * Creates a new React class that is idempotent and capable of containing other\n * React components. It accepts event listeners and DOM properties that are\n * valid according to `DOMProperty`.\n *\n *  - Event listeners: `onClick`, `onMouseDown`, etc.\n *  - DOM properties: `className`, `name`, `title`, etc.\n *\n * The `style` property functions differently from the DOM API. It accepts an\n * object mapping of style properties to values.\n *\n * @constructor ReactDOMComponent\n * @extends ReactMultiChild\n */\nfunction ReactDOMComponent(element) {\n  var tag = element.type;\n  validateDangerousTag(tag);\n  this._currentElement = element;\n  this._tag = tag.toLowerCase();\n  this._namespaceURI = null;\n  this._renderedChildren = null;\n  this._previousStyle = null;\n  this._previousStyleCopy = null;\n  this._hostNode = null;\n  this._hostParent = null;\n  this._rootNodeID = 0;\n  this._domID = 0;\n  this._hostContainerInfo = null;\n  this._wrapperState = null;\n  this._topLevelWrapper = null;\n  this._flags = 0;\n  if (true) {\n    this._ancestorInfo = null;\n    setAndValidateContentChildDev.call(this, null);\n  }\n}\n\nReactDOMComponent.displayName = 'ReactDOMComponent';\n\nReactDOMComponent.Mixin = {\n\n  /**\n   * Generates root tag markup then recurses. This method has side effects and\n   * is not idempotent.\n   *\n   * @internal\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {?ReactDOMComponent} the parent component instance\n   * @param {?object} info about the host container\n   * @param {object} context\n   * @return {string} The computed markup.\n   */\n  mountComponent: function (transaction, hostParent, hostContainerInfo, context) {\n    this._rootNodeID = globalIdCounter++;\n    this._domID = hostContainerInfo._idCounter++;\n    this._hostParent = hostParent;\n    this._hostContainerInfo = hostContainerInfo;\n\n    var props = this._currentElement.props;\n\n    switch (this._tag) {\n      case 'audio':\n      case 'form':\n      case 'iframe':\n      case 'img':\n      case 'link':\n      case 'object':\n      case 'source':\n      case 'video':\n        this._wrapperState = {\n          listeners: null\n        };\n        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);\n        break;\n      case 'input':\n        ReactDOMInput.mountWrapper(this, props, hostParent);\n        props = ReactDOMInput.getHostProps(this, props);\n        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);\n        break;\n      case 'option':\n        ReactDOMOption.mountWrapper(this, props, hostParent);\n        props = ReactDOMOption.getHostProps(this, props);\n        break;\n      case 'select':\n        ReactDOMSelect.mountWrapper(this, props, hostParent);\n        props = ReactDOMSelect.getHostProps(this, props);\n        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);\n        break;\n      case 'textarea':\n        ReactDOMTextarea.mountWrapper(this, props, hostParent);\n        props = ReactDOMTextarea.getHostProps(this, props);\n        transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);\n        break;\n    }\n\n    assertValidProps(this, props);\n\n    // We create tags in the namespace of their parent container, except HTML\n    // tags get no namespace.\n    var namespaceURI;\n    var parentTag;\n    if (hostParent != null) {\n      namespaceURI = hostParent._namespaceURI;\n      parentTag = hostParent._tag;\n    } else if (hostContainerInfo._tag) {\n      namespaceURI = hostContainerInfo._namespaceURI;\n      parentTag = hostContainerInfo._tag;\n    }\n    if (namespaceURI == null || namespaceURI === DOMNamespaces.svg && parentTag === 'foreignobject') {\n      namespaceURI = DOMNamespaces.html;\n    }\n    if (namespaceURI === DOMNamespaces.html) {\n      if (this._tag === 'svg') {\n        namespaceURI = DOMNamespaces.svg;\n      } else if (this._tag === 'math') {\n        namespaceURI = DOMNamespaces.mathml;\n      }\n    }\n    this._namespaceURI = namespaceURI;\n\n    if (true) {\n      var parentInfo;\n      if (hostParent != null) {\n        parentInfo = hostParent._ancestorInfo;\n      } else if (hostContainerInfo._tag) {\n        parentInfo = hostContainerInfo._ancestorInfo;\n      }\n      if (parentInfo) {\n        // parentInfo should always be present except for the top-level\n        // component when server rendering\n        validateDOMNesting(this._tag, null, this, parentInfo);\n      }\n      this._ancestorInfo = validateDOMNesting.updatedAncestorInfo(parentInfo, this._tag, this);\n    }\n\n    var mountImage;\n    if (transaction.useCreateElement) {\n      var ownerDocument = hostContainerInfo._ownerDocument;\n      var el;\n      if (namespaceURI === DOMNamespaces.html) {\n        if (this._tag === 'script') {\n          // Create the script via .innerHTML so its \"parser-inserted\" flag is\n          // set to true and it does not execute\n          var div = ownerDocument.createElement('div');\n          var type = this._currentElement.type;\n          div.innerHTML = '<' + type + '></' + type + '>';\n          el = div.removeChild(div.firstChild);\n        } else if (props.is) {\n          el = ownerDocument.createElement(this._currentElement.type, props.is);\n        } else {\n          // Separate else branch instead of using `props.is || undefined` above becuase of a Firefox bug.\n          // See discussion in https://github.com/facebook/react/pull/6896\n          // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240\n          el = ownerDocument.createElement(this._currentElement.type);\n        }\n      } else {\n        el = ownerDocument.createElementNS(namespaceURI, this._currentElement.type);\n      }\n      ReactDOMComponentTree.precacheNode(this, el);\n      this._flags |= Flags.hasCachedChildNodes;\n      if (!this._hostParent) {\n        DOMPropertyOperations.setAttributeForRoot(el);\n      }\n      this._updateDOMProperties(null, props, transaction);\n      var lazyTree = DOMLazyTree(el);\n      this._createInitialChildren(transaction, props, context, lazyTree);\n      mountImage = lazyTree;\n    } else {\n      var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);\n      var tagContent = this._createContentMarkup(transaction, props, context);\n      if (!tagContent && omittedCloseTags[this._tag]) {\n        mountImage = tagOpen + '/>';\n      } else {\n        mountImage = tagOpen + '>' + tagContent + '</' + this._currentElement.type + '>';\n      }\n    }\n\n    switch (this._tag) {\n      case 'input':\n        transaction.getReactMountReady().enqueue(inputPostMount, this);\n        if (props.autoFocus) {\n          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);\n        }\n        break;\n      case 'textarea':\n        transaction.getReactMountReady().enqueue(textareaPostMount, this);\n        if (props.autoFocus) {\n          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);\n        }\n        break;\n      case 'select':\n        if (props.autoFocus) {\n          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);\n        }\n        break;\n      case 'button':\n        if (props.autoFocus) {\n          transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);\n        }\n        break;\n      case 'option':\n        transaction.getReactMountReady().enqueue(optionPostMount, this);\n        break;\n    }\n\n    return mountImage;\n  },\n\n  /**\n   * Creates markup for the open tag and all attributes.\n   *\n   * This method has side effects because events get registered.\n   *\n   * Iterating over object properties is faster than iterating over arrays.\n   * @see http://jsperf.com/obj-vs-arr-iteration\n   *\n   * @private\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {object} props\n   * @return {string} Markup of opening tag.\n   */\n  _createOpenTagMarkupAndPutListeners: function (transaction, props) {\n    var ret = '<' + this._currentElement.type;\n\n    for (var propKey in props) {\n      if (!props.hasOwnProperty(propKey)) {\n        continue;\n      }\n      var propValue = props[propKey];\n      if (propValue == null) {\n        continue;\n      }\n      if (registrationNameModules.hasOwnProperty(propKey)) {\n        if (propValue) {\n          enqueuePutListener(this, propKey, propValue, transaction);\n        }\n      } else {\n        if (propKey === STYLE) {\n          if (propValue) {\n            if (true) {\n              // See `_updateDOMProperties`. style block\n              this._previousStyle = propValue;\n            }\n            propValue = this._previousStyleCopy = _assign({}, props.style);\n          }\n          propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this);\n        }\n        var markup = null;\n        if (this._tag != null && isCustomComponent(this._tag, props)) {\n          if (!RESERVED_PROPS.hasOwnProperty(propKey)) {\n            markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey, propValue);\n          }\n        } else {\n          markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);\n        }\n        if (markup) {\n          ret += ' ' + markup;\n        }\n      }\n    }\n\n    // For static pages, no need to put React ID and checksum. Saves lots of\n    // bytes.\n    if (transaction.renderToStaticMarkup) {\n      return ret;\n    }\n\n    if (!this._hostParent) {\n      ret += ' ' + DOMPropertyOperations.createMarkupForRoot();\n    }\n    ret += ' ' + DOMPropertyOperations.createMarkupForID(this._domID);\n    return ret;\n  },\n\n  /**\n   * Creates markup for the content between the tags.\n   *\n   * @private\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {object} props\n   * @param {object} context\n   * @return {string} Content markup.\n   */\n  _createContentMarkup: function (transaction, props, context) {\n    var ret = '';\n\n    // Intentional use of != to avoid catching zero/false.\n    var innerHTML = props.dangerouslySetInnerHTML;\n    if (innerHTML != null) {\n      if (innerHTML.__html != null) {\n        ret = innerHTML.__html;\n      }\n    } else {\n      var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;\n      var childrenToUse = contentToUse != null ? null : props.children;\n      if (contentToUse != null) {\n        // TODO: Validate that text is allowed as a child of this node\n        ret = escapeTextContentForBrowser(contentToUse);\n        if (true) {\n          setAndValidateContentChildDev.call(this, contentToUse);\n        }\n      } else if (childrenToUse != null) {\n        var mountImages = this.mountChildren(childrenToUse, transaction, context);\n        ret = mountImages.join('');\n      }\n    }\n    if (newlineEatingTags[this._tag] && ret.charAt(0) === '\\n') {\n      // text/html ignores the first character in these tags if it's a newline\n      // Prefer to break application/xml over text/html (for now) by adding\n      // a newline specifically to get eaten by the parser. (Alternately for\n      // textareas, replacing \"^\\n\" with \"\\r\\n\" doesn't get eaten, and the first\n      // \\r is normalized out by HTMLTextAreaElement#value.)\n      // See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>\n      // See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>\n      // See: <http://www.w3.org/TR/html5/syntax.html#newlines>\n      // See: Parsing of \"textarea\" \"listing\" and \"pre\" elements\n      //  from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>\n      return '\\n' + ret;\n    } else {\n      return ret;\n    }\n  },\n\n  _createInitialChildren: function (transaction, props, context, lazyTree) {\n    // Intentional use of != to avoid catching zero/false.\n    var innerHTML = props.dangerouslySetInnerHTML;\n    if (innerHTML != null) {\n      if (innerHTML.__html != null) {\n        DOMLazyTree.queueHTML(lazyTree, innerHTML.__html);\n      }\n    } else {\n      var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;\n      var childrenToUse = contentToUse != null ? null : props.children;\n      // TODO: Validate that text is allowed as a child of this node\n      if (contentToUse != null) {\n        // Avoid setting textContent when the text is empty. In IE11 setting\n        // textContent on a text area will cause the placeholder to not\n        // show within the textarea until it has been focused and blurred again.\n        // https://github.com/facebook/react/issues/6731#issuecomment-254874553\n        if (contentToUse !== '') {\n          if (true) {\n            setAndValidateContentChildDev.call(this, contentToUse);\n          }\n          DOMLazyTree.queueText(lazyTree, contentToUse);\n        }\n      } else if (childrenToUse != null) {\n        var mountImages = this.mountChildren(childrenToUse, transaction, context);\n        for (var i = 0; i < mountImages.length; i++) {\n          DOMLazyTree.queueChild(lazyTree, mountImages[i]);\n        }\n      }\n    }\n  },\n\n  /**\n   * Receives a next element and updates the component.\n   *\n   * @internal\n   * @param {ReactElement} nextElement\n   * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction\n   * @param {object} context\n   */\n  receiveComponent: function (nextElement, transaction, context) {\n    var prevElement = this._currentElement;\n    this._currentElement = nextElement;\n    this.updateComponent(transaction, prevElement, nextElement, context);\n  },\n\n  /**\n   * Updates a DOM component after it has already been allocated and\n   * attached to the DOM. Reconciles the root DOM node, then recurses.\n   *\n   * @param {ReactReconcileTransaction} transaction\n   * @param {ReactElement} prevElement\n   * @param {ReactElement} nextElement\n   * @internal\n   * @overridable\n   */\n  updateComponent: function (transaction, prevElement, nextElement, context) {\n    var lastProps = prevElement.props;\n    var nextProps = this._currentElement.props;\n\n    switch (this._tag) {\n      case 'input':\n        lastProps = ReactDOMInput.getHostProps(this, lastProps);\n        nextProps = ReactDOMInput.getHostProps(this, nextProps);\n        break;\n      case 'option':\n        lastProps = ReactDOMOption.getHostProps(this, lastProps);\n        nextProps = ReactDOMOption.getHostProps(this, nextProps);\n        break;\n      case 'select':\n        lastProps = ReactDOMSelect.getHostProps(this, lastProps);\n        nextProps = ReactDOMSelect.getHostProps(this, nextProps);\n        break;\n      case 'textarea':\n        lastProps = ReactDOMTextarea.getHostProps(this, lastProps);\n        nextProps = ReactDOMTextarea.getHostProps(this, nextProps);\n        break;\n    }\n\n    assertValidProps(this, nextProps);\n    this._updateDOMProperties(lastProps, nextProps, transaction);\n    this._updateDOMChildren(lastProps, nextProps, transaction, context);\n\n    switch (this._tag) {\n      case 'input':\n        // Update the wrapper around inputs *after* updating props. This has to\n        // happen after `_updateDOMProperties`. Otherwise HTML5 input validations\n        // raise warnings and prevent the new value from being assigned.\n        ReactDOMInput.updateWrapper(this);\n        break;\n      case 'textarea':\n        ReactDOMTextarea.updateWrapper(this);\n        break;\n      case 'select':\n        // <select> value update needs to occur after <option> children\n        // reconciliation\n        transaction.getReactMountReady().enqueue(postUpdateSelectWrapper, this);\n        break;\n    }\n  },\n\n  /**\n   * Reconciles the properties by detecting differences in property values and\n   * updating the DOM as necessary. This function is probably the single most\n   * critical path for performance optimization.\n   *\n   * TODO: Benchmark whether checking for changed values in memory actually\n   *       improves performance (especially statically positioned elements).\n   * TODO: Benchmark the effects of putting this at the top since 99% of props\n   *       do not change for a given reconciliation.\n   * TODO: Benchmark areas that can be improved with caching.\n   *\n   * @private\n   * @param {object} lastProps\n   * @param {object} nextProps\n   * @param {?DOMElement} node\n   */\n  _updateDOMProperties: function (lastProps, nextProps, transaction) {\n    var propKey;\n    var styleName;\n    var styleUpdates;\n    for (propKey in lastProps) {\n      if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) {\n        continue;\n      }\n      if (propKey === STYLE) {\n        var lastStyle = this._previousStyleCopy;\n        for (styleName in lastStyle) {\n          if (lastStyle.hasOwnProperty(styleName)) {\n            styleUpdates = styleUpdates || {};\n            styleUpdates[styleName] = '';\n          }\n        }\n        this._previousStyleCopy = null;\n      } else if (registrationNameModules.hasOwnProperty(propKey)) {\n        if (lastProps[propKey]) {\n          // Only call deleteListener if there was a listener previously or\n          // else willDeleteListener gets called when there wasn't actually a\n          // listener (e.g., onClick={null})\n          deleteListener(this, propKey);\n        }\n      } else if (isCustomComponent(this._tag, lastProps)) {\n        if (!RESERVED_PROPS.hasOwnProperty(propKey)) {\n          DOMPropertyOperations.deleteValueForAttribute(getNode(this), propKey);\n        }\n      } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {\n        DOMPropertyOperations.deleteValueForProperty(getNode(this), propKey);\n      }\n    }\n    for (propKey in nextProps) {\n      var nextProp = nextProps[propKey];\n      var lastProp = propKey === STYLE ? this._previousStyleCopy : lastProps != null ? lastProps[propKey] : undefined;\n      if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp == null && lastProp == null) {\n        continue;\n      }\n      if (propKey === STYLE) {\n        if (nextProp) {\n          if (true) {\n            checkAndWarnForMutatedStyle(this._previousStyleCopy, this._previousStyle, this);\n            this._previousStyle = nextProp;\n          }\n          nextProp = this._previousStyleCopy = _assign({}, nextProp);\n        } else {\n          this._previousStyleCopy = null;\n        }\n        if (lastProp) {\n          // Unset styles on `lastProp` but not on `nextProp`.\n          for (styleName in lastProp) {\n            if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) {\n              styleUpdates = styleUpdates || {};\n              styleUpdates[styleName] = '';\n            }\n          }\n          // Update styles that changed since `lastProp`.\n          for (styleName in nextProp) {\n            if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) {\n              styleUpdates = styleUpdates || {};\n              styleUpdates[styleName] = nextProp[styleName];\n            }\n          }\n        } else {\n          // Relies on `updateStylesByID` not mutating `styleUpdates`.\n          styleUpdates = nextProp;\n        }\n      } else if (registrationNameModules.hasOwnProperty(propKey)) {\n        if (nextProp) {\n          enqueuePutListener(this, propKey, nextProp, transaction);\n        } else if (lastProp) {\n          deleteListener(this, propKey);\n        }\n      } else if (isCustomComponent(this._tag, nextProps)) {\n        if (!RESERVED_PROPS.hasOwnProperty(propKey)) {\n          DOMPropertyOperations.setValueForAttribute(getNode(this), propKey, nextProp);\n        }\n      } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {\n        var node = getNode(this);\n        // If we're updating to null or undefined, we should remove the property\n        // from the DOM node instead of inadvertently setting to a string. This\n        // brings us in line with the same behavior we have on initial render.\n        if (nextProp != null) {\n          DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);\n        } else {\n          DOMPropertyOperations.deleteValueForProperty(node, propKey);\n        }\n      }\n    }\n    if (styleUpdates) {\n      CSSPropertyOperations.setValueForStyles(getNode(this), styleUpdates, this);\n    }\n  },\n\n  /**\n   * Reconciles the children with the various properties that affect the\n   * children content.\n   *\n   * @param {object} lastProps\n   * @param {object} nextProps\n   * @param {ReactReconcileTransaction} transaction\n   * @param {object} context\n   */\n  _updateDOMChildren: function (lastProps, nextProps, transaction, context) {\n    var lastContent = CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;\n    var nextContent = CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;\n\n    var lastHtml = lastProps.dangerouslySetInnerHTML && lastProps.dangerouslySetInnerHTML.__html;\n    var nextHtml = nextProps.dangerouslySetInnerHTML && nextProps.dangerouslySetInnerHTML.__html;\n\n    // Note the use of `!=` which checks for null or undefined.\n    var lastChildren = lastContent != null ? null : lastProps.children;\n    var nextChildren = nextContent != null ? null : nextProps.children;\n\n    // If we're switching from children to content/html or vice versa, remove\n    // the old content\n    var lastHasContentOrHtml = lastContent != null || lastHtml != null;\n    var nextHasContentOrHtml = nextContent != null || nextHtml != null;\n    if (lastChildren != null && nextChildren == null) {\n      this.updateChildren(null, transaction, context);\n    } else if (lastHasContentOrHtml && !nextHasContentOrHtml) {\n      this.updateTextContent('');\n      if (true) {\n        ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);\n      }\n    }\n\n    if (nextContent != null) {\n      if (lastContent !== nextContent) {\n        this.updateTextContent('' + nextContent);\n        if (true) {\n          setAndValidateContentChildDev.call(this, nextContent);\n        }\n      }\n    } else if (nextHtml != null) {\n      if (lastHtml !== nextHtml) {\n        this.updateMarkup('' + nextHtml);\n      }\n      if (true) {\n        ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);\n      }\n    } else if (nextChildren != null) {\n      if (true) {\n        setAndValidateContentChildDev.call(this, null);\n      }\n\n      this.updateChildren(nextChildren, transaction, context);\n    }\n  },\n\n  getHostNode: function () {\n    return getNode(this);\n  },\n\n  /**\n   * Destroys all event registrations for this instance. Does not remove from\n   * the DOM. That must be done by the parent.\n   *\n   * @internal\n   */\n  unmountComponent: function (safely) {\n    switch (this._tag) {\n      case 'audio':\n      case 'form':\n      case 'iframe':\n      case 'img':\n      case 'link':\n      case 'object':\n      case 'source':\n      case 'video':\n        var listeners = this._wrapperState.listeners;\n        if (listeners) {\n          for (var i = 0; i < listeners.length; i++) {\n            listeners[i].remove();\n          }\n        }\n        break;\n      case 'html':\n      case 'head':\n      case 'body':\n        /**\n         * Components like <html> <head> and <body> can't be removed or added\n         * easily in a cross-browser way, however it's valuable to be able to\n         * take advantage of React's reconciliation for styling and <title>\n         * management. So we just document it and throw in dangerous cases.\n         */\n         true ?  true ? invariant(false, '<%s> tried to unmount. Because of cross-browser quirks it is impossible to unmount some top-level components (eg <html>, <head>, and <body>) reliably and efficiently. To fix this, have a single top-level component that never unmounts render these elements.', this._tag) : _prodInvariant('66', this._tag) : void 0;\n        break;\n    }\n\n    this.unmountChildren(safely);\n    ReactDOMComponentTree.uncacheNode(this);\n    EventPluginHub.deleteAllListeners(this);\n    this._rootNodeID = 0;\n    this._domID = 0;\n    this._wrapperState = null;\n\n    if (true) {\n      setAndValidateContentChildDev.call(this, null);\n    }\n  },\n\n  getPublicInstance: function () {\n    return getNode(this);\n  }\n\n};\n\n_assign(ReactDOMComponent.prototype, ReactDOMComponent.Mixin, ReactMultiChild.Mixin);\n\nmodule.exports = ReactDOMComponent;//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMjY1LmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9yZWFjdC1kb20vbGliL1JlYWN0RE9NQ29tcG9uZW50LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29weXJpZ2h0IDIwMTMtcHJlc2VudCwgRmFjZWJvb2ssIEluYy5cbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKlxuICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgQlNELXN0eWxlIGxpY2Vuc2UgZm91bmQgaW4gdGhlXG4gKiBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuIEFuIGFkZGl0aW9uYWwgZ3JhbnRcbiAqIG9mIHBhdGVudCByaWdodHMgY2FuIGJlIGZvdW5kIGluIHRoZSBQQVRFTlRTIGZpbGUgaW4gdGhlIHNhbWUgZGlyZWN0b3J5LlxuICpcbiAqL1xuXG4vKiBnbG9iYWwgaGFzT3duUHJvcGVydHk6dHJ1ZSAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBfcHJvZEludmFyaWFudCA9IHJlcXVpcmUoJy4vcmVhY3RQcm9kSW52YXJpYW50JyksXG4gICAgX2Fzc2lnbiA9IHJlcXVpcmUoJ29iamVjdC1hc3NpZ24nKTtcblxudmFyIEF1dG9Gb2N1c1V0aWxzID0gcmVxdWlyZSgnLi9BdXRvRm9jdXNVdGlscycpO1xudmFyIENTU1Byb3BlcnR5T3BlcmF0aW9ucyA9IHJlcXVpcmUoJy4vQ1NTUHJvcGVydHlPcGVyYXRpb25zJyk7XG52YXIgRE9NTGF6eVRyZWUgPSByZXF1aXJlKCcuL0RPTUxhenlUcmVlJyk7XG52YXIgRE9NTmFtZXNwYWNlcyA9IHJlcXVpcmUoJy4vRE9NTmFtZXNwYWNlcycpO1xudmFyIERPTVByb3BlcnR5ID0gcmVxdWlyZSgnLi9ET01Qcm9wZXJ0eScpO1xudmFyIERPTVByb3BlcnR5T3BlcmF0aW9ucyA9IHJlcXVpcmUoJy4vRE9NUHJvcGVydHlPcGVyYXRpb25zJyk7XG52YXIgRXZlbnRQbHVnaW5IdWIgPSByZXF1aXJlKCcuL0V2ZW50UGx1Z2luSHViJyk7XG52YXIgRXZlbnRQbHVnaW5SZWdpc3RyeSA9IHJlcXVpcmUoJy4vRXZlbnRQbHVnaW5SZWdpc3RyeScpO1xudmFyIFJlYWN0QnJvd3NlckV2ZW50RW1pdHRlciA9IHJlcXVpcmUoJy4vUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyJyk7XG52YXIgUmVhY3RET01Db21wb25lbnRGbGFncyA9IHJlcXVpcmUoJy4vUmVhY3RET01Db21wb25lbnRGbGFncycpO1xudmFyIFJlYWN0RE9NQ29tcG9uZW50VHJlZSA9IHJlcXVpcmUoJy4vUmVhY3RET01Db21wb25lbnRUcmVlJyk7XG52YXIgUmVhY3RET01JbnB1dCA9IHJlcXVpcmUoJy4vUmVhY3RET01JbnB1dCcpO1xudmFyIFJlYWN0RE9NT3B0aW9uID0gcmVxdWlyZSgnLi9SZWFjdERPTU9wdGlvbicpO1xudmFyIFJlYWN0RE9NU2VsZWN0ID0gcmVxdWlyZSgnLi9SZWFjdERPTVNlbGVjdCcpO1xudmFyIFJlYWN0RE9NVGV4dGFyZWEgPSByZXF1aXJlKCcuL1JlYWN0RE9NVGV4dGFyZWEnKTtcbnZhciBSZWFjdEluc3RydW1lbnRhdGlvbiA9IHJlcXVpcmUoJy4vUmVhY3RJbnN0cnVtZW50YXRpb24nKTtcbnZhciBSZWFjdE11bHRpQ2hpbGQgPSByZXF1aXJlKCcuL1JlYWN0TXVsdGlDaGlsZCcpO1xudmFyIFJlYWN0U2VydmVyUmVuZGVyaW5nVHJhbnNhY3Rpb24gPSByZXF1aXJlKCcuL1JlYWN0U2VydmVyUmVuZGVyaW5nVHJhbnNhY3Rpb24nKTtcblxudmFyIGVtcHR5RnVuY3Rpb24gPSByZXF1aXJlKCdmYmpzL2xpYi9lbXB0eUZ1bmN0aW9uJyk7XG52YXIgZXNjYXBlVGV4dENvbnRlbnRGb3JCcm93c2VyID0gcmVxdWlyZSgnLi9lc2NhcGVUZXh0Q29udGVudEZvckJyb3dzZXInKTtcbnZhciBpbnZhcmlhbnQgPSByZXF1aXJlKCdmYmpzL2xpYi9pbnZhcmlhbnQnKTtcbnZhciBpc0V2ZW50U3VwcG9ydGVkID0gcmVxdWlyZSgnLi9pc0V2ZW50U3VwcG9ydGVkJyk7XG52YXIgc2hhbGxvd0VxdWFsID0gcmVxdWlyZSgnZmJqcy9saWIvc2hhbGxvd0VxdWFsJyk7XG52YXIgdmFsaWRhdGVET01OZXN0aW5nID0gcmVxdWlyZSgnLi92YWxpZGF0ZURPTU5lc3RpbmcnKTtcbnZhciB3YXJuaW5nID0gcmVxdWlyZSgnZmJqcy9saWIvd2FybmluZycpO1xuXG52YXIgRmxhZ3MgPSBSZWFjdERPTUNvbXBvbmVudEZsYWdzO1xudmFyIGRlbGV0ZUxpc3RlbmVyID0gRXZlbnRQbHVnaW5IdWIuZGVsZXRlTGlzdGVuZXI7XG52YXIgZ2V0Tm9kZSA9IFJlYWN0RE9NQ29tcG9uZW50VHJlZS5nZXROb2RlRnJvbUluc3RhbmNlO1xudmFyIGxpc3RlblRvID0gUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyLmxpc3RlblRvO1xudmFyIHJlZ2lzdHJhdGlvbk5hbWVNb2R1bGVzID0gRXZlbnRQbHVnaW5SZWdpc3RyeS5yZWdpc3RyYXRpb25OYW1lTW9kdWxlcztcblxuLy8gRm9yIHF1aWNrbHkgbWF0Y2hpbmcgY2hpbGRyZW4gdHlwZSwgdG8gdGVzdCBpZiBjYW4gYmUgdHJlYXRlZCBhcyBjb250ZW50LlxudmFyIENPTlRFTlRfVFlQRVMgPSB7ICdzdHJpbmcnOiB0cnVlLCAnbnVtYmVyJzogdHJ1ZSB9O1xuXG52YXIgU1RZTEUgPSAnc3R5bGUnO1xudmFyIEhUTUwgPSAnX19odG1sJztcbnZhciBSRVNFUlZFRF9QUk9QUyA9IHtcbiAgY2hpbGRyZW46IG51bGwsXG4gIGRhbmdlcm91c2x5U2V0SW5uZXJIVE1MOiBudWxsLFxuICBzdXBwcmVzc0NvbnRlbnRFZGl0YWJsZVdhcm5pbmc6IG51bGxcbn07XG5cbi8vIE5vZGUgdHlwZSBmb3IgZG9jdW1lbnQgZnJhZ21lbnRzIChOb2RlLkRPQ1VNRU5UX0ZSQUdNRU5UX05PREUpLlxudmFyIERPQ19GUkFHTUVOVF9UWVBFID0gMTE7XG5cbmZ1bmN0aW9uIGdldERlY2xhcmF0aW9uRXJyb3JBZGRlbmR1bShpbnRlcm5hbEluc3RhbmNlKSB7XG4gIGlmIChpbnRlcm5hbEluc3RhbmNlKSB7XG4gICAgdmFyIG93bmVyID0gaW50ZXJuYWxJbnN0YW5jZS5fY3VycmVudEVsZW1lbnQuX293bmVyIHx8IG51bGw7XG4gICAgaWYgKG93bmVyKSB7XG4gICAgICB2YXIgbmFtZSA9IG93bmVyLmdldE5hbWUoKTtcbiAgICAgIGlmIChuYW1lKSB7XG4gICAgICAgIHJldHVybiAnIFRoaXMgRE9NIG5vZGUgd2FzIHJlbmRlcmVkIGJ5IGAnICsgbmFtZSArICdgLic7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiAnJztcbn1cblxuZnVuY3Rpb24gZnJpZW5kbHlTdHJpbmdpZnkob2JqKSB7XG4gIGlmICh0eXBlb2Ygb2JqID09PSAnb2JqZWN0Jykge1xuICAgIGlmIChBcnJheS5pc0FycmF5KG9iaikpIHtcbiAgICAgIHJldHVybiAnWycgKyBvYmoubWFwKGZyaWVuZGx5U3RyaW5naWZ5KS5qb2luKCcsICcpICsgJ10nO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgcGFpcnMgPSBbXTtcbiAgICAgIGZvciAodmFyIGtleSBpbiBvYmopIHtcbiAgICAgICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIGtleSkpIHtcbiAgICAgICAgICB2YXIga2V5RXNjYXBlZCA9IC9eW2EteiRfXVtcXHckX10qJC9pLnRlc3Qoa2V5KSA/IGtleSA6IEpTT04uc3RyaW5naWZ5KGtleSk7XG4gICAgICAgICAgcGFpcnMucHVzaChrZXlFc2NhcGVkICsgJzogJyArIGZyaWVuZGx5U3RyaW5naWZ5KG9ialtrZXldKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiAneycgKyBwYWlycy5qb2luKCcsICcpICsgJ30nO1xuICAgIH1cbiAgfSBlbHNlIGlmICh0eXBlb2Ygb2JqID09PSAnc3RyaW5nJykge1xuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShvYmopO1xuICB9IGVsc2UgaWYgKHR5cGVvZiBvYmogPT09ICdmdW5jdGlvbicpIHtcbiAgICByZXR1cm4gJ1tmdW5jdGlvbiBvYmplY3RdJztcbiAgfVxuICAvLyBEaWZmZXJzIGZyb20gSlNPTi5zdHJpbmdpZnkgaW4gdGhhdCB1bmRlZmluZWQgYmVjYXVzZSB1bmRlZmluZWQgYW5kIHRoYXRcbiAgLy8gaW5mIGFuZCBuYW4gZG9uJ3QgYmVjb21lIG51bGxcbiAgcmV0dXJuIFN0cmluZyhvYmopO1xufVxuXG52YXIgc3R5bGVNdXRhdGlvbldhcm5pbmcgPSB7fTtcblxuZnVuY3Rpb24gY2hlY2tBbmRXYXJuRm9yTXV0YXRlZFN0eWxlKHN0eWxlMSwgc3R5bGUyLCBjb21wb25lbnQpIHtcbiAgaWYgKHN0eWxlMSA9PSBudWxsIHx8IHN0eWxlMiA9PSBudWxsKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGlmIChzaGFsbG93RXF1YWwoc3R5bGUxLCBzdHlsZTIpKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdmFyIGNvbXBvbmVudE5hbWUgPSBjb21wb25lbnQuX3RhZztcbiAgdmFyIG93bmVyID0gY29tcG9uZW50Ll9jdXJyZW50RWxlbWVudC5fb3duZXI7XG4gIHZhciBvd25lck5hbWU7XG4gIGlmIChvd25lcikge1xuICAgIG93bmVyTmFtZSA9IG93bmVyLmdldE5hbWUoKTtcbiAgfVxuXG4gIHZhciBoYXNoID0gb3duZXJOYW1lICsgJ3wnICsgY29tcG9uZW50TmFtZTtcblxuICBpZiAoc3R5bGVNdXRhdGlvbldhcm5pbmcuaGFzT3duUHJvcGVydHkoaGFzaCkpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBzdHlsZU11dGF0aW9uV2FybmluZ1toYXNoXSA9IHRydWU7XG5cbiAgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IHdhcm5pbmcoZmFsc2UsICdgJXNgIHdhcyBwYXNzZWQgYSBzdHlsZSBvYmplY3QgdGhhdCBoYXMgcHJldmlvdXNseSBiZWVuIG11dGF0ZWQuICcgKyAnTXV0YXRpbmcgYHN0eWxlYCBpcyBkZXByZWNhdGVkLiBDb25zaWRlciBjbG9uaW5nIGl0IGJlZm9yZWhhbmQuIENoZWNrICcgKyAndGhlIGByZW5kZXJgICVzLiBQcmV2aW91cyBzdHlsZTogJXMuIE11dGF0ZWQgc3R5bGU6ICVzLicsIGNvbXBvbmVudE5hbWUsIG93bmVyID8gJ29mIGAnICsgb3duZXJOYW1lICsgJ2AnIDogJ3VzaW5nIDwnICsgY29tcG9uZW50TmFtZSArICc+JywgZnJpZW5kbHlTdHJpbmdpZnkoc3R5bGUxKSwgZnJpZW5kbHlTdHJpbmdpZnkoc3R5bGUyKSkgOiB2b2lkIDA7XG59XG5cbi8qKlxuICogQHBhcmFtIHtvYmplY3R9IGNvbXBvbmVudFxuICogQHBhcmFtIHs/b2JqZWN0fSBwcm9wc1xuICovXG5mdW5jdGlvbiBhc3NlcnRWYWxpZFByb3BzKGNvbXBvbmVudCwgcHJvcHMpIHtcbiAgaWYgKCFwcm9wcykge1xuICAgIHJldHVybjtcbiAgfVxuICAvLyBOb3RlIHRoZSB1c2Ugb2YgYD09YCB3aGljaCBjaGVja3MgZm9yIG51bGwgb3IgdW5kZWZpbmVkLlxuICBpZiAodm9pZEVsZW1lbnRUYWdzW2NvbXBvbmVudC5fdGFnXSkge1xuICAgICEocHJvcHMuY2hpbGRyZW4gPT0gbnVsbCAmJiBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCA9PSBudWxsKSA/IHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicgPyBpbnZhcmlhbnQoZmFsc2UsICclcyBpcyBhIHZvaWQgZWxlbWVudCB0YWcgYW5kIG11c3QgbmVpdGhlciBoYXZlIGBjaGlsZHJlbmAgbm9yIHVzZSBgZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUxgLiVzJywgY29tcG9uZW50Ll90YWcsIGNvbXBvbmVudC5fY3VycmVudEVsZW1lbnQuX293bmVyID8gJyBDaGVjayB0aGUgcmVuZGVyIG1ldGhvZCBvZiAnICsgY29tcG9uZW50Ll9jdXJyZW50RWxlbWVudC5fb3duZXIuZ2V0TmFtZSgpICsgJy4nIDogJycpIDogX3Byb2RJbnZhcmlhbnQoJzEzNycsIGNvbXBvbmVudC5fdGFnLCBjb21wb25lbnQuX2N1cnJlbnRFbGVtZW50Ll9vd25lciA/ICcgQ2hlY2sgdGhlIHJlbmRlciBtZXRob2Qgb2YgJyArIGNvbXBvbmVudC5fY3VycmVudEVsZW1lbnQuX293bmVyLmdldE5hbWUoKSArICcuJyA6ICcnKSA6IHZvaWQgMDtcbiAgfVxuICBpZiAocHJvcHMuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwgIT0gbnVsbCkge1xuICAgICEocHJvcHMuY2hpbGRyZW4gPT0gbnVsbCkgPyBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gaW52YXJpYW50KGZhbHNlLCAnQ2FuIG9ubHkgc2V0IG9uZSBvZiBgY2hpbGRyZW5gIG9yIGBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTGAuJykgOiBfcHJvZEludmFyaWFudCgnNjAnKSA6IHZvaWQgMDtcbiAgICAhKHR5cGVvZiBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCA9PT0gJ29iamVjdCcgJiYgSFRNTCBpbiBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCkgPyBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gaW52YXJpYW50KGZhbHNlLCAnYHByb3BzLmRhbmdlcm91c2x5U2V0SW5uZXJIVE1MYCBtdXN0IGJlIGluIHRoZSBmb3JtIGB7X19odG1sOiAuLi59YC4gUGxlYXNlIHZpc2l0IGh0dHBzOi8vZmIubWUvcmVhY3QtaW52YXJpYW50LWRhbmdlcm91c2x5LXNldC1pbm5lci1odG1sIGZvciBtb3JlIGluZm9ybWF0aW9uLicpIDogX3Byb2RJbnZhcmlhbnQoJzYxJykgOiB2b2lkIDA7XG4gIH1cbiAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gd2FybmluZyhwcm9wcy5pbm5lckhUTUwgPT0gbnVsbCwgJ0RpcmVjdGx5IHNldHRpbmcgcHJvcGVydHkgYGlubmVySFRNTGAgaXMgbm90IHBlcm1pdHRlZC4gJyArICdGb3IgbW9yZSBpbmZvcm1hdGlvbiwgbG9va3VwIGRvY3VtZW50YXRpb24gb24gYGRhbmdlcm91c2x5U2V0SW5uZXJIVE1MYC4nKSA6IHZvaWQgMDtcbiAgICBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gd2FybmluZyhwcm9wcy5zdXBwcmVzc0NvbnRlbnRFZGl0YWJsZVdhcm5pbmcgfHwgIXByb3BzLmNvbnRlbnRFZGl0YWJsZSB8fCBwcm9wcy5jaGlsZHJlbiA9PSBudWxsLCAnQSBjb21wb25lbnQgaXMgYGNvbnRlbnRFZGl0YWJsZWAgYW5kIGNvbnRhaW5zIGBjaGlsZHJlbmAgbWFuYWdlZCBieSAnICsgJ1JlYWN0LiBJdCBpcyBub3cgeW91ciByZXNwb25zaWJpbGl0eSB0byBndWFyYW50ZWUgdGhhdCBub25lIG9mICcgKyAndGhvc2Ugbm9kZXMgYXJlIHVuZXhwZWN0ZWRseSBtb2RpZmllZCBvciBkdXBsaWNhdGVkLiBUaGlzIGlzICcgKyAncHJvYmFibHkgbm90IGludGVudGlvbmFsLicpIDogdm9pZCAwO1xuICAgIHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicgPyB3YXJuaW5nKHByb3BzLm9uRm9jdXNJbiA9PSBudWxsICYmIHByb3BzLm9uRm9jdXNPdXQgPT0gbnVsbCwgJ1JlYWN0IHVzZXMgb25Gb2N1cyBhbmQgb25CbHVyIGluc3RlYWQgb2Ygb25Gb2N1c0luIGFuZCBvbkZvY3VzT3V0LiAnICsgJ0FsbCBSZWFjdCBldmVudHMgYXJlIG5vcm1hbGl6ZWQgdG8gYnViYmxlLCBzbyBvbkZvY3VzSW4gYW5kIG9uRm9jdXNPdXQgJyArICdhcmUgbm90IG5lZWRlZC9zdXBwb3J0ZWQgYnkgUmVhY3QuJykgOiB2b2lkIDA7XG4gIH1cbiAgIShwcm9wcy5zdHlsZSA9PSBudWxsIHx8IHR5cGVvZiBwcm9wcy5zdHlsZSA9PT0gJ29iamVjdCcpID8gcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IGludmFyaWFudChmYWxzZSwgJ1RoZSBgc3R5bGVgIHByb3AgZXhwZWN0cyBhIG1hcHBpbmcgZnJvbSBzdHlsZSBwcm9wZXJ0aWVzIHRvIHZhbHVlcywgbm90IGEgc3RyaW5nLiBGb3IgZXhhbXBsZSwgc3R5bGU9e3ttYXJnaW5SaWdodDogc3BhY2luZyArIFxcJ2VtXFwnfX0gd2hlbiB1c2luZyBKU1guJXMnLCBnZXREZWNsYXJhdGlvbkVycm9yQWRkZW5kdW0oY29tcG9uZW50KSkgOiBfcHJvZEludmFyaWFudCgnNjInLCBnZXREZWNsYXJhdGlvbkVycm9yQWRkZW5kdW0oY29tcG9uZW50KSkgOiB2b2lkIDA7XG59XG5cbmZ1bmN0aW9uIGVucXVldWVQdXRMaXN0ZW5lcihpbnN0LCByZWdpc3RyYXRpb25OYW1lLCBsaXN0ZW5lciwgdHJhbnNhY3Rpb24pIHtcbiAgaWYgKHRyYW5zYWN0aW9uIGluc3RhbmNlb2YgUmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbikge1xuICAgIHJldHVybjtcbiAgfVxuICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJykge1xuICAgIC8vIElFOCBoYXMgbm8gQVBJIGZvciBldmVudCBjYXB0dXJpbmcgYW5kIHRoZSBgb25TY3JvbGxgIGV2ZW50IGRvZXNuJ3RcbiAgICAvLyBidWJibGUuXG4gICAgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IHdhcm5pbmcocmVnaXN0cmF0aW9uTmFtZSAhPT0gJ29uU2Nyb2xsJyB8fCBpc0V2ZW50U3VwcG9ydGVkKCdzY3JvbGwnLCB0cnVlKSwgJ1RoaXMgYnJvd3NlciBkb2VzblxcJ3Qgc3VwcG9ydCB0aGUgYG9uU2Nyb2xsYCBldmVudCcpIDogdm9pZCAwO1xuICB9XG4gIHZhciBjb250YWluZXJJbmZvID0gaW5zdC5faG9zdENvbnRhaW5lckluZm87XG4gIHZhciBpc0RvY3VtZW50RnJhZ21lbnQgPSBjb250YWluZXJJbmZvLl9ub2RlICYmIGNvbnRhaW5lckluZm8uX25vZGUubm9kZVR5cGUgPT09IERPQ19GUkFHTUVOVF9UWVBFO1xuICB2YXIgZG9jID0gaXNEb2N1bWVudEZyYWdtZW50ID8gY29udGFpbmVySW5mby5fbm9kZSA6IGNvbnRhaW5lckluZm8uX293bmVyRG9jdW1lbnQ7XG4gIGxpc3RlblRvKHJlZ2lzdHJhdGlvbk5hbWUsIGRvYyk7XG4gIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUocHV0TGlzdGVuZXIsIHtcbiAgICBpbnN0OiBpbnN0LFxuICAgIHJlZ2lzdHJhdGlvbk5hbWU6IHJlZ2lzdHJhdGlvbk5hbWUsXG4gICAgbGlzdGVuZXI6IGxpc3RlbmVyXG4gIH0pO1xufVxuXG5mdW5jdGlvbiBwdXRMaXN0ZW5lcigpIHtcbiAgdmFyIGxpc3RlbmVyVG9QdXQgPSB0aGlzO1xuICBFdmVudFBsdWdpbkh1Yi5wdXRMaXN0ZW5lcihsaXN0ZW5lclRvUHV0Lmluc3QsIGxpc3RlbmVyVG9QdXQucmVnaXN0cmF0aW9uTmFtZSwgbGlzdGVuZXJUb1B1dC5saXN0ZW5lcik7XG59XG5cbmZ1bmN0aW9uIGlucHV0UG9zdE1vdW50KCkge1xuICB2YXIgaW5zdCA9IHRoaXM7XG4gIFJlYWN0RE9NSW5wdXQucG9zdE1vdW50V3JhcHBlcihpbnN0KTtcbn1cblxuZnVuY3Rpb24gdGV4dGFyZWFQb3N0TW91bnQoKSB7XG4gIHZhciBpbnN0ID0gdGhpcztcbiAgUmVhY3RET01UZXh0YXJlYS5wb3N0TW91bnRXcmFwcGVyKGluc3QpO1xufVxuXG5mdW5jdGlvbiBvcHRpb25Qb3N0TW91bnQoKSB7XG4gIHZhciBpbnN0ID0gdGhpcztcbiAgUmVhY3RET01PcHRpb24ucG9zdE1vdW50V3JhcHBlcihpbnN0KTtcbn1cblxudmFyIHNldEFuZFZhbGlkYXRlQ29udGVudENoaWxkRGV2ID0gZW1wdHlGdW5jdGlvbjtcbmlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gIHNldEFuZFZhbGlkYXRlQ29udGVudENoaWxkRGV2ID0gZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICB2YXIgaGFzRXhpc3RpbmdDb250ZW50ID0gdGhpcy5fY29udGVudERlYnVnSUQgIT0gbnVsbDtcbiAgICB2YXIgZGVidWdJRCA9IHRoaXMuX2RlYnVnSUQ7XG4gICAgLy8gVGhpcyBJRCByZXByZXNlbnRzIHRoZSBpbmxpbmVkIGNoaWxkIHRoYXQgaGFzIG5vIGJhY2tpbmcgaW5zdGFuY2U6XG4gICAgdmFyIGNvbnRlbnREZWJ1Z0lEID0gLWRlYnVnSUQ7XG5cbiAgICBpZiAoY29udGVudCA9PSBudWxsKSB7XG4gICAgICBpZiAoaGFzRXhpc3RpbmdDb250ZW50KSB7XG4gICAgICAgIFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vblVubW91bnRDb21wb25lbnQodGhpcy5fY29udGVudERlYnVnSUQpO1xuICAgICAgfVxuICAgICAgdGhpcy5fY29udGVudERlYnVnSUQgPSBudWxsO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhbGlkYXRlRE9NTmVzdGluZyhudWxsLCBTdHJpbmcoY29udGVudCksIHRoaXMsIHRoaXMuX2FuY2VzdG9ySW5mbyk7XG4gICAgdGhpcy5fY29udGVudERlYnVnSUQgPSBjb250ZW50RGVidWdJRDtcbiAgICBpZiAoaGFzRXhpc3RpbmdDb250ZW50KSB7XG4gICAgICBSZWFjdEluc3RydW1lbnRhdGlvbi5kZWJ1Z1Rvb2wub25CZWZvcmVVcGRhdGVDb21wb25lbnQoY29udGVudERlYnVnSUQsIGNvbnRlbnQpO1xuICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uVXBkYXRlQ29tcG9uZW50KGNvbnRlbnREZWJ1Z0lEKTtcbiAgICB9IGVsc2Uge1xuICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uQmVmb3JlTW91bnRDb21wb25lbnQoY29udGVudERlYnVnSUQsIGNvbnRlbnQsIGRlYnVnSUQpO1xuICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uTW91bnRDb21wb25lbnQoY29udGVudERlYnVnSUQpO1xuICAgICAgUmVhY3RJbnN0cnVtZW50YXRpb24uZGVidWdUb29sLm9uU2V0Q2hpbGRyZW4oZGVidWdJRCwgW2NvbnRlbnREZWJ1Z0lEXSk7XG4gICAgfVxuICB9O1xufVxuXG4vLyBUaGVyZSBhcmUgc28gbWFueSBtZWRpYSBldmVudHMsIGl0IG1ha2VzIHNlbnNlIHRvIGp1c3Rcbi8vIG1haW50YWluIGEgbGlzdCByYXRoZXIgdGhhbiBjcmVhdGUgYSBgdHJhcEJ1YmJsZWRFdmVudGAgZm9yIGVhY2hcbnZhciBtZWRpYUV2ZW50cyA9IHtcbiAgdG9wQWJvcnQ6ICdhYm9ydCcsXG4gIHRvcENhblBsYXk6ICdjYW5wbGF5JyxcbiAgdG9wQ2FuUGxheVRocm91Z2g6ICdjYW5wbGF5dGhyb3VnaCcsXG4gIHRvcER1cmF0aW9uQ2hhbmdlOiAnZHVyYXRpb25jaGFuZ2UnLFxuICB0b3BFbXB0aWVkOiAnZW1wdGllZCcsXG4gIHRvcEVuY3J5cHRlZDogJ2VuY3J5cHRlZCcsXG4gIHRvcEVuZGVkOiAnZW5kZWQnLFxuICB0b3BFcnJvcjogJ2Vycm9yJyxcbiAgdG9wTG9hZGVkRGF0YTogJ2xvYWRlZGRhdGEnLFxuICB0b3BMb2FkZWRNZXRhZGF0YTogJ2xvYWRlZG1ldGFkYXRhJyxcbiAgdG9wTG9hZFN0YXJ0OiAnbG9hZHN0YXJ0JyxcbiAgdG9wUGF1c2U6ICdwYXVzZScsXG4gIHRvcFBsYXk6ICdwbGF5JyxcbiAgdG9wUGxheWluZzogJ3BsYXlpbmcnLFxuICB0b3BQcm9ncmVzczogJ3Byb2dyZXNzJyxcbiAgdG9wUmF0ZUNoYW5nZTogJ3JhdGVjaGFuZ2UnLFxuICB0b3BTZWVrZWQ6ICdzZWVrZWQnLFxuICB0b3BTZWVraW5nOiAnc2Vla2luZycsXG4gIHRvcFN0YWxsZWQ6ICdzdGFsbGVkJyxcbiAgdG9wU3VzcGVuZDogJ3N1c3BlbmQnLFxuICB0b3BUaW1lVXBkYXRlOiAndGltZXVwZGF0ZScsXG4gIHRvcFZvbHVtZUNoYW5nZTogJ3ZvbHVtZWNoYW5nZScsXG4gIHRvcFdhaXRpbmc6ICd3YWl0aW5nJ1xufTtcblxuZnVuY3Rpb24gdHJhcEJ1YmJsZWRFdmVudHNMb2NhbCgpIHtcbiAgdmFyIGluc3QgPSB0aGlzO1xuICAvLyBJZiBhIGNvbXBvbmVudCByZW5kZXJzIHRvIG51bGwgb3IgaWYgYW5vdGhlciBjb21wb25lbnQgZmF0YWxzIGFuZCBjYXVzZXNcbiAgLy8gdGhlIHN0YXRlIG9mIHRoZSB0cmVlIHRvIGJlIGNvcnJ1cHRlZCwgYG5vZGVgIGhlcmUgY2FuIGJlIG51bGwuXG4gICFpbnN0Ll9yb290Tm9kZUlEID8gcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJyA/IGludmFyaWFudChmYWxzZSwgJ011c3QgYmUgbW91bnRlZCB0byB0cmFwIGV2ZW50cycpIDogX3Byb2RJbnZhcmlhbnQoJzYzJykgOiB2b2lkIDA7XG4gIHZhciBub2RlID0gZ2V0Tm9kZShpbnN0KTtcbiAgIW5vZGUgPyBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gaW52YXJpYW50KGZhbHNlLCAndHJhcEJ1YmJsZWRFdmVudCguLi4pOiBSZXF1aXJlcyBub2RlIHRvIGJlIHJlbmRlcmVkLicpIDogX3Byb2RJbnZhcmlhbnQoJzY0JykgOiB2b2lkIDA7XG5cbiAgc3dpdGNoIChpbnN0Ll90YWcpIHtcbiAgICBjYXNlICdpZnJhbWUnOlxuICAgIGNhc2UgJ29iamVjdCc6XG4gICAgICBpbnN0Ll93cmFwcGVyU3RhdGUubGlzdGVuZXJzID0gW1JlYWN0QnJvd3NlckV2ZW50RW1pdHRlci50cmFwQnViYmxlZEV2ZW50KCd0b3BMb2FkJywgJ2xvYWQnLCBub2RlKV07XG4gICAgICBicmVhaztcbiAgICBjYXNlICd2aWRlbyc6XG4gICAgY2FzZSAnYXVkaW8nOlxuXG4gICAgICBpbnN0Ll93cmFwcGVyU3RhdGUubGlzdGVuZXJzID0gW107XG4gICAgICAvLyBDcmVhdGUgbGlzdGVuZXIgZm9yIGVhY2ggbWVkaWEgZXZlbnRcbiAgICAgIGZvciAodmFyIGV2ZW50IGluIG1lZGlhRXZlbnRzKSB7XG4gICAgICAgIGlmIChtZWRpYUV2ZW50cy5oYXNPd25Qcm9wZXJ0eShldmVudCkpIHtcbiAgICAgICAgICBpbnN0Ll93cmFwcGVyU3RhdGUubGlzdGVuZXJzLnB1c2goUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyLnRyYXBCdWJibGVkRXZlbnQoZXZlbnQsIG1lZGlhRXZlbnRzW2V2ZW50XSwgbm9kZSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBicmVhaztcbiAgICBjYXNlICdzb3VyY2UnOlxuICAgICAgaW5zdC5fd3JhcHBlclN0YXRlLmxpc3RlbmVycyA9IFtSZWFjdEJyb3dzZXJFdmVudEVtaXR0ZXIudHJhcEJ1YmJsZWRFdmVudCgndG9wRXJyb3InLCAnZXJyb3InLCBub2RlKV07XG4gICAgICBicmVhaztcbiAgICBjYXNlICdpbWcnOlxuICAgICAgaW5zdC5fd3JhcHBlclN0YXRlLmxpc3RlbmVycyA9IFtSZWFjdEJyb3dzZXJFdmVudEVtaXR0ZXIudHJhcEJ1YmJsZWRFdmVudCgndG9wRXJyb3InLCAnZXJyb3InLCBub2RlKSwgUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyLnRyYXBCdWJibGVkRXZlbnQoJ3RvcExvYWQnLCAnbG9hZCcsIG5vZGUpXTtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgJ2Zvcm0nOlxuICAgICAgaW5zdC5fd3JhcHBlclN0YXRlLmxpc3RlbmVycyA9IFtSZWFjdEJyb3dzZXJFdmVudEVtaXR0ZXIudHJhcEJ1YmJsZWRFdmVudCgndG9wUmVzZXQnLCAncmVzZXQnLCBub2RlKSwgUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyLnRyYXBCdWJibGVkRXZlbnQoJ3RvcFN1Ym1pdCcsICdzdWJtaXQnLCBub2RlKV07XG4gICAgICBicmVhaztcbiAgICBjYXNlICdpbnB1dCc6XG4gICAgY2FzZSAnc2VsZWN0JzpcbiAgICBjYXNlICd0ZXh0YXJlYSc6XG4gICAgICBpbnN0Ll93cmFwcGVyU3RhdGUubGlzdGVuZXJzID0gW1JlYWN0QnJvd3NlckV2ZW50RW1pdHRlci50cmFwQnViYmxlZEV2ZW50KCd0b3BJbnZhbGlkJywgJ2ludmFsaWQnLCBub2RlKV07XG4gICAgICBicmVhaztcbiAgfVxufVxuXG5mdW5jdGlvbiBwb3N0VXBkYXRlU2VsZWN0V3JhcHBlcigpIHtcbiAgUmVhY3RET01TZWxlY3QucG9zdFVwZGF0ZVdyYXBwZXIodGhpcyk7XG59XG5cbi8vIEZvciBIVE1MLCBjZXJ0YWluIHRhZ3Mgc2hvdWxkIG9taXQgdGhlaXIgY2xvc2UgdGFnLiBXZSBrZWVwIGEgd2hpdGVsaXN0IGZvclxuLy8gdGhvc2Ugc3BlY2lhbC1jYXNlIHRhZ3MuXG5cbnZhciBvbWl0dGVkQ2xvc2VUYWdzID0ge1xuICAnYXJlYSc6IHRydWUsXG4gICdiYXNlJzogdHJ1ZSxcbiAgJ2JyJzogdHJ1ZSxcbiAgJ2NvbCc6IHRydWUsXG4gICdlbWJlZCc6IHRydWUsXG4gICdocic6IHRydWUsXG4gICdpbWcnOiB0cnVlLFxuICAnaW5wdXQnOiB0cnVlLFxuICAna2V5Z2VuJzogdHJ1ZSxcbiAgJ2xpbmsnOiB0cnVlLFxuICAnbWV0YSc6IHRydWUsXG4gICdwYXJhbSc6IHRydWUsXG4gICdzb3VyY2UnOiB0cnVlLFxuICAndHJhY2snOiB0cnVlLFxuICAnd2JyJzogdHJ1ZVxufTtcblxudmFyIG5ld2xpbmVFYXRpbmdUYWdzID0ge1xuICAnbGlzdGluZyc6IHRydWUsXG4gICdwcmUnOiB0cnVlLFxuICAndGV4dGFyZWEnOiB0cnVlXG59O1xuXG4vLyBGb3IgSFRNTCwgY2VydGFpbiB0YWdzIGNhbm5vdCBoYXZlIGNoaWxkcmVuLiBUaGlzIGhhcyB0aGUgc2FtZSBwdXJwb3NlIGFzXG4vLyBgb21pdHRlZENsb3NlVGFnc2AgZXhjZXB0IHRoYXQgYG1lbnVpdGVtYCBzaG91bGQgc3RpbGwgaGF2ZSBpdHMgY2xvc2luZyB0YWcuXG5cbnZhciB2b2lkRWxlbWVudFRhZ3MgPSBfYXNzaWduKHtcbiAgJ21lbnVpdGVtJzogdHJ1ZVxufSwgb21pdHRlZENsb3NlVGFncyk7XG5cbi8vIFdlIGFjY2VwdCBhbnkgdGFnIHRvIGJlIHJlbmRlcmVkIGJ1dCBzaW5jZSB0aGlzIGdldHMgaW5qZWN0ZWQgaW50byBhcmJpdHJhcnlcbi8vIEhUTUwsIHdlIHdhbnQgdG8gbWFrZSBzdXJlIHRoYXQgaXQncyBhIHNhZmUgdGFnLlxuLy8gaHR0cDovL3d3dy53My5vcmcvVFIvUkVDLXhtbC8jTlQtTmFtZVxuXG52YXIgVkFMSURfVEFHX1JFR0VYID0gL15bYS16QS1aXVthLXpBLVo6X1xcLlxcLVxcZF0qJC87IC8vIFNpbXBsaWZpZWQgc3Vic2V0XG52YXIgdmFsaWRhdGVkVGFnQ2FjaGUgPSB7fTtcbnZhciBoYXNPd25Qcm9wZXJ0eSA9IHt9Lmhhc093blByb3BlcnR5O1xuXG5mdW5jdGlvbiB2YWxpZGF0ZURhbmdlcm91c1RhZyh0YWcpIHtcbiAgaWYgKCFoYXNPd25Qcm9wZXJ0eS5jYWxsKHZhbGlkYXRlZFRhZ0NhY2hlLCB0YWcpKSB7XG4gICAgIVZBTElEX1RBR19SRUdFWC50ZXN0KHRhZykgPyBwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nID8gaW52YXJpYW50KGZhbHNlLCAnSW52YWxpZCB0YWc6ICVzJywgdGFnKSA6IF9wcm9kSW52YXJpYW50KCc2NScsIHRhZykgOiB2b2lkIDA7XG4gICAgdmFsaWRhdGVkVGFnQ2FjaGVbdGFnXSA9IHRydWU7XG4gIH1cbn1cblxuZnVuY3Rpb24gaXNDdXN0b21Db21wb25lbnQodGFnTmFtZSwgcHJvcHMpIHtcbiAgcmV0dXJuIHRhZ05hbWUuaW5kZXhPZignLScpID49IDAgfHwgcHJvcHMuaXMgIT0gbnVsbDtcbn1cblxudmFyIGdsb2JhbElkQ291bnRlciA9IDE7XG5cbi8qKlxuICogQ3JlYXRlcyBhIG5ldyBSZWFjdCBjbGFzcyB0aGF0IGlzIGlkZW1wb3RlbnQgYW5kIGNhcGFibGUgb2YgY29udGFpbmluZyBvdGhlclxuICogUmVhY3QgY29tcG9uZW50cy4gSXQgYWNjZXB0cyBldmVudCBsaXN0ZW5lcnMgYW5kIERPTSBwcm9wZXJ0aWVzIHRoYXQgYXJlXG4gKiB2YWxpZCBhY2NvcmRpbmcgdG8gYERPTVByb3BlcnR5YC5cbiAqXG4gKiAgLSBFdmVudCBsaXN0ZW5lcnM6IGBvbkNsaWNrYCwgYG9uTW91c2VEb3duYCwgZXRjLlxuICogIC0gRE9NIHByb3BlcnRpZXM6IGBjbGFzc05hbWVgLCBgbmFtZWAsIGB0aXRsZWAsIGV0Yy5cbiAqXG4gKiBUaGUgYHN0eWxlYCBwcm9wZXJ0eSBmdW5jdGlvbnMgZGlmZmVyZW50bHkgZnJvbSB0aGUgRE9NIEFQSS4gSXQgYWNjZXB0cyBhblxuICogb2JqZWN0IG1hcHBpbmcgb2Ygc3R5bGUgcHJvcGVydGllcyB0byB2YWx1ZXMuXG4gKlxuICogQGNvbnN0cnVjdG9yIFJlYWN0RE9NQ29tcG9uZW50XG4gKiBAZXh0ZW5kcyBSZWFjdE11bHRpQ2hpbGRcbiAqL1xuZnVuY3Rpb24gUmVhY3RET01Db21wb25lbnQoZWxlbWVudCkge1xuICB2YXIgdGFnID0gZWxlbWVudC50eXBlO1xuICB2YWxpZGF0ZURhbmdlcm91c1RhZyh0YWcpO1xuICB0aGlzLl9jdXJyZW50RWxlbWVudCA9IGVsZW1lbnQ7XG4gIHRoaXMuX3RhZyA9IHRhZy50b0xvd2VyQ2FzZSgpO1xuICB0aGlzLl9uYW1lc3BhY2VVUkkgPSBudWxsO1xuICB0aGlzLl9yZW5kZXJlZENoaWxkcmVuID0gbnVsbDtcbiAgdGhpcy5fcHJldmlvdXNTdHlsZSA9IG51bGw7XG4gIHRoaXMuX3ByZXZpb3VzU3R5bGVDb3B5ID0gbnVsbDtcbiAgdGhpcy5faG9zdE5vZGUgPSBudWxsO1xuICB0aGlzLl9ob3N0UGFyZW50ID0gbnVsbDtcbiAgdGhpcy5fcm9vdE5vZGVJRCA9IDA7XG4gIHRoaXMuX2RvbUlEID0gMDtcbiAgdGhpcy5faG9zdENvbnRhaW5lckluZm8gPSBudWxsO1xuICB0aGlzLl93cmFwcGVyU3RhdGUgPSBudWxsO1xuICB0aGlzLl90b3BMZXZlbFdyYXBwZXIgPSBudWxsO1xuICB0aGlzLl9mbGFncyA9IDA7XG4gIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgdGhpcy5fYW5jZXN0b3JJbmZvID0gbnVsbDtcbiAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIG51bGwpO1xuICB9XG59XG5cblJlYWN0RE9NQ29tcG9uZW50LmRpc3BsYXlOYW1lID0gJ1JlYWN0RE9NQ29tcG9uZW50JztcblxuUmVhY3RET01Db21wb25lbnQuTWl4aW4gPSB7XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlcyByb290IHRhZyBtYXJrdXAgdGhlbiByZWN1cnNlcy4gVGhpcyBtZXRob2QgaGFzIHNpZGUgZWZmZWN0cyBhbmRcbiAgICogaXMgbm90IGlkZW1wb3RlbnQuXG4gICAqXG4gICAqIEBpbnRlcm5hbFxuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb258UmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHs/UmVhY3RET01Db21wb25lbnR9IHRoZSBwYXJlbnQgY29tcG9uZW50IGluc3RhbmNlXG4gICAqIEBwYXJhbSB7P29iamVjdH0gaW5mbyBhYm91dCB0aGUgaG9zdCBjb250YWluZXJcbiAgICogQHBhcmFtIHtvYmplY3R9IGNvbnRleHRcbiAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgY29tcHV0ZWQgbWFya3VwLlxuICAgKi9cbiAgbW91bnRDb21wb25lbnQ6IGZ1bmN0aW9uICh0cmFuc2FjdGlvbiwgaG9zdFBhcmVudCwgaG9zdENvbnRhaW5lckluZm8sIGNvbnRleHQpIHtcbiAgICB0aGlzLl9yb290Tm9kZUlEID0gZ2xvYmFsSWRDb3VudGVyKys7XG4gICAgdGhpcy5fZG9tSUQgPSBob3N0Q29udGFpbmVySW5mby5faWRDb3VudGVyKys7XG4gICAgdGhpcy5faG9zdFBhcmVudCA9IGhvc3RQYXJlbnQ7XG4gICAgdGhpcy5faG9zdENvbnRhaW5lckluZm8gPSBob3N0Q29udGFpbmVySW5mbztcblxuICAgIHZhciBwcm9wcyA9IHRoaXMuX2N1cnJlbnRFbGVtZW50LnByb3BzO1xuXG4gICAgc3dpdGNoICh0aGlzLl90YWcpIHtcbiAgICAgIGNhc2UgJ2F1ZGlvJzpcbiAgICAgIGNhc2UgJ2Zvcm0nOlxuICAgICAgY2FzZSAnaWZyYW1lJzpcbiAgICAgIGNhc2UgJ2ltZyc6XG4gICAgICBjYXNlICdsaW5rJzpcbiAgICAgIGNhc2UgJ29iamVjdCc6XG4gICAgICBjYXNlICdzb3VyY2UnOlxuICAgICAgY2FzZSAndmlkZW8nOlxuICAgICAgICB0aGlzLl93cmFwcGVyU3RhdGUgPSB7XG4gICAgICAgICAgbGlzdGVuZXJzOiBudWxsXG4gICAgICAgIH07XG4gICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUodHJhcEJ1YmJsZWRFdmVudHNMb2NhbCwgdGhpcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnaW5wdXQnOlxuICAgICAgICBSZWFjdERPTUlucHV0Lm1vdW50V3JhcHBlcih0aGlzLCBwcm9wcywgaG9zdFBhcmVudCk7XG4gICAgICAgIHByb3BzID0gUmVhY3RET01JbnB1dC5nZXRIb3N0UHJvcHModGhpcywgcHJvcHMpO1xuICAgICAgICB0cmFuc2FjdGlvbi5nZXRSZWFjdE1vdW50UmVhZHkoKS5lbnF1ZXVlKHRyYXBCdWJibGVkRXZlbnRzTG9jYWwsIHRoaXMpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ29wdGlvbic6XG4gICAgICAgIFJlYWN0RE9NT3B0aW9uLm1vdW50V3JhcHBlcih0aGlzLCBwcm9wcywgaG9zdFBhcmVudCk7XG4gICAgICAgIHByb3BzID0gUmVhY3RET01PcHRpb24uZ2V0SG9zdFByb3BzKHRoaXMsIHByb3BzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdzZWxlY3QnOlxuICAgICAgICBSZWFjdERPTVNlbGVjdC5tb3VudFdyYXBwZXIodGhpcywgcHJvcHMsIGhvc3RQYXJlbnQpO1xuICAgICAgICBwcm9wcyA9IFJlYWN0RE9NU2VsZWN0LmdldEhvc3RQcm9wcyh0aGlzLCBwcm9wcyk7XG4gICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUodHJhcEJ1YmJsZWRFdmVudHNMb2NhbCwgdGhpcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGV4dGFyZWEnOlxuICAgICAgICBSZWFjdERPTVRleHRhcmVhLm1vdW50V3JhcHBlcih0aGlzLCBwcm9wcywgaG9zdFBhcmVudCk7XG4gICAgICAgIHByb3BzID0gUmVhY3RET01UZXh0YXJlYS5nZXRIb3N0UHJvcHModGhpcywgcHJvcHMpO1xuICAgICAgICB0cmFuc2FjdGlvbi5nZXRSZWFjdE1vdW50UmVhZHkoKS5lbnF1ZXVlKHRyYXBCdWJibGVkRXZlbnRzTG9jYWwsIHRoaXMpO1xuICAgICAgICBicmVhaztcbiAgICB9XG5cbiAgICBhc3NlcnRWYWxpZFByb3BzKHRoaXMsIHByb3BzKTtcblxuICAgIC8vIFdlIGNyZWF0ZSB0YWdzIGluIHRoZSBuYW1lc3BhY2Ugb2YgdGhlaXIgcGFyZW50IGNvbnRhaW5lciwgZXhjZXB0IEhUTUxcbiAgICAvLyB0YWdzIGdldCBubyBuYW1lc3BhY2UuXG4gICAgdmFyIG5hbWVzcGFjZVVSSTtcbiAgICB2YXIgcGFyZW50VGFnO1xuICAgIGlmIChob3N0UGFyZW50ICE9IG51bGwpIHtcbiAgICAgIG5hbWVzcGFjZVVSSSA9IGhvc3RQYXJlbnQuX25hbWVzcGFjZVVSSTtcbiAgICAgIHBhcmVudFRhZyA9IGhvc3RQYXJlbnQuX3RhZztcbiAgICB9IGVsc2UgaWYgKGhvc3RDb250YWluZXJJbmZvLl90YWcpIHtcbiAgICAgIG5hbWVzcGFjZVVSSSA9IGhvc3RDb250YWluZXJJbmZvLl9uYW1lc3BhY2VVUkk7XG4gICAgICBwYXJlbnRUYWcgPSBob3N0Q29udGFpbmVySW5mby5fdGFnO1xuICAgIH1cbiAgICBpZiAobmFtZXNwYWNlVVJJID09IG51bGwgfHwgbmFtZXNwYWNlVVJJID09PSBET01OYW1lc3BhY2VzLnN2ZyAmJiBwYXJlbnRUYWcgPT09ICdmb3JlaWdub2JqZWN0Jykge1xuICAgICAgbmFtZXNwYWNlVVJJID0gRE9NTmFtZXNwYWNlcy5odG1sO1xuICAgIH1cbiAgICBpZiAobmFtZXNwYWNlVVJJID09PSBET01OYW1lc3BhY2VzLmh0bWwpIHtcbiAgICAgIGlmICh0aGlzLl90YWcgPT09ICdzdmcnKSB7XG4gICAgICAgIG5hbWVzcGFjZVVSSSA9IERPTU5hbWVzcGFjZXMuc3ZnO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLl90YWcgPT09ICdtYXRoJykge1xuICAgICAgICBuYW1lc3BhY2VVUkkgPSBET01OYW1lc3BhY2VzLm1hdGhtbDtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5fbmFtZXNwYWNlVVJJID0gbmFtZXNwYWNlVVJJO1xuXG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgIHZhciBwYXJlbnRJbmZvO1xuICAgICAgaWYgKGhvc3RQYXJlbnQgIT0gbnVsbCkge1xuICAgICAgICBwYXJlbnRJbmZvID0gaG9zdFBhcmVudC5fYW5jZXN0b3JJbmZvO1xuICAgICAgfSBlbHNlIGlmIChob3N0Q29udGFpbmVySW5mby5fdGFnKSB7XG4gICAgICAgIHBhcmVudEluZm8gPSBob3N0Q29udGFpbmVySW5mby5fYW5jZXN0b3JJbmZvO1xuICAgICAgfVxuICAgICAgaWYgKHBhcmVudEluZm8pIHtcbiAgICAgICAgLy8gcGFyZW50SW5mbyBzaG91bGQgYWx3YXlzIGJlIHByZXNlbnQgZXhjZXB0IGZvciB0aGUgdG9wLWxldmVsXG4gICAgICAgIC8vIGNvbXBvbmVudCB3aGVuIHNlcnZlciByZW5kZXJpbmdcbiAgICAgICAgdmFsaWRhdGVET01OZXN0aW5nKHRoaXMuX3RhZywgbnVsbCwgdGhpcywgcGFyZW50SW5mbyk7XG4gICAgICB9XG4gICAgICB0aGlzLl9hbmNlc3RvckluZm8gPSB2YWxpZGF0ZURPTU5lc3RpbmcudXBkYXRlZEFuY2VzdG9ySW5mbyhwYXJlbnRJbmZvLCB0aGlzLl90YWcsIHRoaXMpO1xuICAgIH1cblxuICAgIHZhciBtb3VudEltYWdlO1xuICAgIGlmICh0cmFuc2FjdGlvbi51c2VDcmVhdGVFbGVtZW50KSB7XG4gICAgICB2YXIgb3duZXJEb2N1bWVudCA9IGhvc3RDb250YWluZXJJbmZvLl9vd25lckRvY3VtZW50O1xuICAgICAgdmFyIGVsO1xuICAgICAgaWYgKG5hbWVzcGFjZVVSSSA9PT0gRE9NTmFtZXNwYWNlcy5odG1sKSB7XG4gICAgICAgIGlmICh0aGlzLl90YWcgPT09ICdzY3JpcHQnKSB7XG4gICAgICAgICAgLy8gQ3JlYXRlIHRoZSBzY3JpcHQgdmlhIC5pbm5lckhUTUwgc28gaXRzIFwicGFyc2VyLWluc2VydGVkXCIgZmxhZyBpc1xuICAgICAgICAgIC8vIHNldCB0byB0cnVlIGFuZCBpdCBkb2VzIG5vdCBleGVjdXRlXG4gICAgICAgICAgdmFyIGRpdiA9IG93bmVyRG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgICAgdmFyIHR5cGUgPSB0aGlzLl9jdXJyZW50RWxlbWVudC50eXBlO1xuICAgICAgICAgIGRpdi5pbm5lckhUTUwgPSAnPCcgKyB0eXBlICsgJz48LycgKyB0eXBlICsgJz4nO1xuICAgICAgICAgIGVsID0gZGl2LnJlbW92ZUNoaWxkKGRpdi5maXJzdENoaWxkKTtcbiAgICAgICAgfSBlbHNlIGlmIChwcm9wcy5pcykge1xuICAgICAgICAgIGVsID0gb3duZXJEb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuX2N1cnJlbnRFbGVtZW50LnR5cGUsIHByb3BzLmlzKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBTZXBhcmF0ZSBlbHNlIGJyYW5jaCBpbnN0ZWFkIG9mIHVzaW5nIGBwcm9wcy5pcyB8fCB1bmRlZmluZWRgIGFib3ZlIGJlY3Vhc2Ugb2YgYSBGaXJlZm94IGJ1Zy5cbiAgICAgICAgICAvLyBTZWUgZGlzY3Vzc2lvbiBpbiBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2svcmVhY3QvcHVsbC82ODk2XG4gICAgICAgICAgLy8gYW5kIGRpc2N1c3Npb24gaW4gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTI3NjI0MFxuICAgICAgICAgIGVsID0gb3duZXJEb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuX2N1cnJlbnRFbGVtZW50LnR5cGUpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBlbCA9IG93bmVyRG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKG5hbWVzcGFjZVVSSSwgdGhpcy5fY3VycmVudEVsZW1lbnQudHlwZSk7XG4gICAgICB9XG4gICAgICBSZWFjdERPTUNvbXBvbmVudFRyZWUucHJlY2FjaGVOb2RlKHRoaXMsIGVsKTtcbiAgICAgIHRoaXMuX2ZsYWdzIHw9IEZsYWdzLmhhc0NhY2hlZENoaWxkTm9kZXM7XG4gICAgICBpZiAoIXRoaXMuX2hvc3RQYXJlbnQpIHtcbiAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLnNldEF0dHJpYnV0ZUZvclJvb3QoZWwpO1xuICAgICAgfVxuICAgICAgdGhpcy5fdXBkYXRlRE9NUHJvcGVydGllcyhudWxsLCBwcm9wcywgdHJhbnNhY3Rpb24pO1xuICAgICAgdmFyIGxhenlUcmVlID0gRE9NTGF6eVRyZWUoZWwpO1xuICAgICAgdGhpcy5fY3JlYXRlSW5pdGlhbENoaWxkcmVuKHRyYW5zYWN0aW9uLCBwcm9wcywgY29udGV4dCwgbGF6eVRyZWUpO1xuICAgICAgbW91bnRJbWFnZSA9IGxhenlUcmVlO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgdGFnT3BlbiA9IHRoaXMuX2NyZWF0ZU9wZW5UYWdNYXJrdXBBbmRQdXRMaXN0ZW5lcnModHJhbnNhY3Rpb24sIHByb3BzKTtcbiAgICAgIHZhciB0YWdDb250ZW50ID0gdGhpcy5fY3JlYXRlQ29udGVudE1hcmt1cCh0cmFuc2FjdGlvbiwgcHJvcHMsIGNvbnRleHQpO1xuICAgICAgaWYgKCF0YWdDb250ZW50ICYmIG9taXR0ZWRDbG9zZVRhZ3NbdGhpcy5fdGFnXSkge1xuICAgICAgICBtb3VudEltYWdlID0gdGFnT3BlbiArICcvPic7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBtb3VudEltYWdlID0gdGFnT3BlbiArICc+JyArIHRhZ0NvbnRlbnQgKyAnPC8nICsgdGhpcy5fY3VycmVudEVsZW1lbnQudHlwZSArICc+JztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBzd2l0Y2ggKHRoaXMuX3RhZykge1xuICAgICAgY2FzZSAnaW5wdXQnOlxuICAgICAgICB0cmFuc2FjdGlvbi5nZXRSZWFjdE1vdW50UmVhZHkoKS5lbnF1ZXVlKGlucHV0UG9zdE1vdW50LCB0aGlzKTtcbiAgICAgICAgaWYgKHByb3BzLmF1dG9Gb2N1cykge1xuICAgICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoQXV0b0ZvY3VzVXRpbHMuZm9jdXNET01Db21wb25lbnQsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGV4dGFyZWEnOlxuICAgICAgICB0cmFuc2FjdGlvbi5nZXRSZWFjdE1vdW50UmVhZHkoKS5lbnF1ZXVlKHRleHRhcmVhUG9zdE1vdW50LCB0aGlzKTtcbiAgICAgICAgaWYgKHByb3BzLmF1dG9Gb2N1cykge1xuICAgICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoQXV0b0ZvY3VzVXRpbHMuZm9jdXNET01Db21wb25lbnQsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnc2VsZWN0JzpcbiAgICAgICAgaWYgKHByb3BzLmF1dG9Gb2N1cykge1xuICAgICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoQXV0b0ZvY3VzVXRpbHMuZm9jdXNET01Db21wb25lbnQsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnYnV0dG9uJzpcbiAgICAgICAgaWYgKHByb3BzLmF1dG9Gb2N1cykge1xuICAgICAgICAgIHRyYW5zYWN0aW9uLmdldFJlYWN0TW91bnRSZWFkeSgpLmVucXVldWUoQXV0b0ZvY3VzVXRpbHMuZm9jdXNET01Db21wb25lbnQsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnb3B0aW9uJzpcbiAgICAgICAgdHJhbnNhY3Rpb24uZ2V0UmVhY3RNb3VudFJlYWR5KCkuZW5xdWV1ZShvcHRpb25Qb3N0TW91bnQsIHRoaXMpO1xuICAgICAgICBicmVhaztcbiAgICB9XG5cbiAgICByZXR1cm4gbW91bnRJbWFnZTtcbiAgfSxcblxuICAvKipcbiAgICogQ3JlYXRlcyBtYXJrdXAgZm9yIHRoZSBvcGVuIHRhZyBhbmQgYWxsIGF0dHJpYnV0ZXMuXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGhhcyBzaWRlIGVmZmVjdHMgYmVjYXVzZSBldmVudHMgZ2V0IHJlZ2lzdGVyZWQuXG4gICAqXG4gICAqIEl0ZXJhdGluZyBvdmVyIG9iamVjdCBwcm9wZXJ0aWVzIGlzIGZhc3RlciB0aGFuIGl0ZXJhdGluZyBvdmVyIGFycmF5cy5cbiAgICogQHNlZSBodHRwOi8vanNwZXJmLmNvbS9vYmotdnMtYXJyLWl0ZXJhdGlvblxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb258UmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtvYmplY3R9IHByb3BzXG4gICAqIEByZXR1cm4ge3N0cmluZ30gTWFya3VwIG9mIG9wZW5pbmcgdGFnLlxuICAgKi9cbiAgX2NyZWF0ZU9wZW5UYWdNYXJrdXBBbmRQdXRMaXN0ZW5lcnM6IGZ1bmN0aW9uICh0cmFuc2FjdGlvbiwgcHJvcHMpIHtcbiAgICB2YXIgcmV0ID0gJzwnICsgdGhpcy5fY3VycmVudEVsZW1lbnQudHlwZTtcblxuICAgIGZvciAodmFyIHByb3BLZXkgaW4gcHJvcHMpIHtcbiAgICAgIGlmICghcHJvcHMuaGFzT3duUHJvcGVydHkocHJvcEtleSkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICB2YXIgcHJvcFZhbHVlID0gcHJvcHNbcHJvcEtleV07XG4gICAgICBpZiAocHJvcFZhbHVlID09IG51bGwpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAocmVnaXN0cmF0aW9uTmFtZU1vZHVsZXMuaGFzT3duUHJvcGVydHkocHJvcEtleSkpIHtcbiAgICAgICAgaWYgKHByb3BWYWx1ZSkge1xuICAgICAgICAgIGVucXVldWVQdXRMaXN0ZW5lcih0aGlzLCBwcm9wS2V5LCBwcm9wVmFsdWUsIHRyYW5zYWN0aW9uKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHByb3BLZXkgPT09IFNUWUxFKSB7XG4gICAgICAgICAgaWYgKHByb3BWYWx1ZSkge1xuICAgICAgICAgICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgICAgICAgICAgLy8gU2VlIGBfdXBkYXRlRE9NUHJvcGVydGllc2AuIHN0eWxlIGJsb2NrXG4gICAgICAgICAgICAgIHRoaXMuX3ByZXZpb3VzU3R5bGUgPSBwcm9wVmFsdWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBwcm9wVmFsdWUgPSB0aGlzLl9wcmV2aW91c1N0eWxlQ29weSA9IF9hc3NpZ24oe30sIHByb3BzLnN0eWxlKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcHJvcFZhbHVlID0gQ1NTUHJvcGVydHlPcGVyYXRpb25zLmNyZWF0ZU1hcmt1cEZvclN0eWxlcyhwcm9wVmFsdWUsIHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBtYXJrdXAgPSBudWxsO1xuICAgICAgICBpZiAodGhpcy5fdGFnICE9IG51bGwgJiYgaXNDdXN0b21Db21wb25lbnQodGhpcy5fdGFnLCBwcm9wcykpIHtcbiAgICAgICAgICBpZiAoIVJFU0VSVkVEX1BST1BTLmhhc093blByb3BlcnR5KHByb3BLZXkpKSB7XG4gICAgICAgICAgICBtYXJrdXAgPSBET01Qcm9wZXJ0eU9wZXJhdGlvbnMuY3JlYXRlTWFya3VwRm9yQ3VzdG9tQXR0cmlidXRlKHByb3BLZXksIHByb3BWYWx1ZSk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG1hcmt1cCA9IERPTVByb3BlcnR5T3BlcmF0aW9ucy5jcmVhdGVNYXJrdXBGb3JQcm9wZXJ0eShwcm9wS2V5LCBwcm9wVmFsdWUpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChtYXJrdXApIHtcbiAgICAgICAgICByZXQgKz0gJyAnICsgbWFya3VwO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRm9yIHN0YXRpYyBwYWdlcywgbm8gbmVlZCB0byBwdXQgUmVhY3QgSUQgYW5kIGNoZWNrc3VtLiBTYXZlcyBsb3RzIG9mXG4gICAgLy8gYnl0ZXMuXG4gICAgaWYgKHRyYW5zYWN0aW9uLnJlbmRlclRvU3RhdGljTWFya3VwKSB7XG4gICAgICByZXR1cm4gcmV0O1xuICAgIH1cblxuICAgIGlmICghdGhpcy5faG9zdFBhcmVudCkge1xuICAgICAgcmV0ICs9ICcgJyArIERPTVByb3BlcnR5T3BlcmF0aW9ucy5jcmVhdGVNYXJrdXBGb3JSb290KCk7XG4gICAgfVxuICAgIHJldCArPSAnICcgKyBET01Qcm9wZXJ0eU9wZXJhdGlvbnMuY3JlYXRlTWFya3VwRm9ySUQodGhpcy5fZG9tSUQpO1xuICAgIHJldHVybiByZXQ7XG4gIH0sXG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgbWFya3VwIGZvciB0aGUgY29udGVudCBiZXR3ZWVuIHRoZSB0YWdzLlxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb258UmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtvYmplY3R9IHByb3BzXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBjb250ZXh0XG4gICAqIEByZXR1cm4ge3N0cmluZ30gQ29udGVudCBtYXJrdXAuXG4gICAqL1xuICBfY3JlYXRlQ29udGVudE1hcmt1cDogZnVuY3Rpb24gKHRyYW5zYWN0aW9uLCBwcm9wcywgY29udGV4dCkge1xuICAgIHZhciByZXQgPSAnJztcblxuICAgIC8vIEludGVudGlvbmFsIHVzZSBvZiAhPSB0byBhdm9pZCBjYXRjaGluZyB6ZXJvL2ZhbHNlLlxuICAgIHZhciBpbm5lckhUTUwgPSBwcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTDtcbiAgICBpZiAoaW5uZXJIVE1MICE9IG51bGwpIHtcbiAgICAgIGlmIChpbm5lckhUTUwuX19odG1sICE9IG51bGwpIHtcbiAgICAgICAgcmV0ID0gaW5uZXJIVE1MLl9faHRtbDtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIGNvbnRlbnRUb1VzZSA9IENPTlRFTlRfVFlQRVNbdHlwZW9mIHByb3BzLmNoaWxkcmVuXSA/IHByb3BzLmNoaWxkcmVuIDogbnVsbDtcbiAgICAgIHZhciBjaGlsZHJlblRvVXNlID0gY29udGVudFRvVXNlICE9IG51bGwgPyBudWxsIDogcHJvcHMuY2hpbGRyZW47XG4gICAgICBpZiAoY29udGVudFRvVXNlICE9IG51bGwpIHtcbiAgICAgICAgLy8gVE9ETzogVmFsaWRhdGUgdGhhdCB0ZXh0IGlzIGFsbG93ZWQgYXMgYSBjaGlsZCBvZiB0aGlzIG5vZGVcbiAgICAgICAgcmV0ID0gZXNjYXBlVGV4dENvbnRlbnRGb3JCcm93c2VyKGNvbnRlbnRUb1VzZSk7XG4gICAgICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICAgICAgc2V0QW5kVmFsaWRhdGVDb250ZW50Q2hpbGREZXYuY2FsbCh0aGlzLCBjb250ZW50VG9Vc2UpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGNoaWxkcmVuVG9Vc2UgIT0gbnVsbCkge1xuICAgICAgICB2YXIgbW91bnRJbWFnZXMgPSB0aGlzLm1vdW50Q2hpbGRyZW4oY2hpbGRyZW5Ub1VzZSwgdHJhbnNhY3Rpb24sIGNvbnRleHQpO1xuICAgICAgICByZXQgPSBtb3VudEltYWdlcy5qb2luKCcnKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKG5ld2xpbmVFYXRpbmdUYWdzW3RoaXMuX3RhZ10gJiYgcmV0LmNoYXJBdCgwKSA9PT0gJ1xcbicpIHtcbiAgICAgIC8vIHRleHQvaHRtbCBpZ25vcmVzIHRoZSBmaXJzdCBjaGFyYWN0ZXIgaW4gdGhlc2UgdGFncyBpZiBpdCdzIGEgbmV3bGluZVxuICAgICAgLy8gUHJlZmVyIHRvIGJyZWFrIGFwcGxpY2F0aW9uL3htbCBvdmVyIHRleHQvaHRtbCAoZm9yIG5vdykgYnkgYWRkaW5nXG4gICAgICAvLyBhIG5ld2xpbmUgc3BlY2lmaWNhbGx5IHRvIGdldCBlYXRlbiBieSB0aGUgcGFyc2VyLiAoQWx0ZXJuYXRlbHkgZm9yXG4gICAgICAvLyB0ZXh0YXJlYXMsIHJlcGxhY2luZyBcIl5cXG5cIiB3aXRoIFwiXFxyXFxuXCIgZG9lc24ndCBnZXQgZWF0ZW4sIGFuZCB0aGUgZmlyc3RcbiAgICAgIC8vIFxcciBpcyBub3JtYWxpemVkIG91dCBieSBIVE1MVGV4dEFyZWFFbGVtZW50I3ZhbHVlLilcbiAgICAgIC8vIFNlZTogPGh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWwtcG9seWdsb3QvI25ld2xpbmVzLWluLXRleHRhcmVhLWFuZC1wcmU+XG4gICAgICAvLyBTZWU6IDxodHRwOi8vd3d3LnczLm9yZy9UUi9odG1sNS9zeW50YXguaHRtbCNlbGVtZW50LXJlc3RyaWN0aW9ucz5cbiAgICAgIC8vIFNlZTogPGh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWw1L3N5bnRheC5odG1sI25ld2xpbmVzPlxuICAgICAgLy8gU2VlOiBQYXJzaW5nIG9mIFwidGV4dGFyZWFcIiBcImxpc3RpbmdcIiBhbmQgXCJwcmVcIiBlbGVtZW50c1xuICAgICAgLy8gIGZyb20gPGh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWw1L3N5bnRheC5odG1sI3BhcnNpbmctbWFpbi1pbmJvZHk+XG4gICAgICByZXR1cm4gJ1xcbicgKyByZXQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiByZXQ7XG4gICAgfVxuICB9LFxuXG4gIF9jcmVhdGVJbml0aWFsQ2hpbGRyZW46IGZ1bmN0aW9uICh0cmFuc2FjdGlvbiwgcHJvcHMsIGNvbnRleHQsIGxhenlUcmVlKSB7XG4gICAgLy8gSW50ZW50aW9uYWwgdXNlIG9mICE9IHRvIGF2b2lkIGNhdGNoaW5nIHplcm8vZmFsc2UuXG4gICAgdmFyIGlubmVySFRNTCA9IHByb3BzLmRhbmdlcm91c2x5U2V0SW5uZXJIVE1MO1xuICAgIGlmIChpbm5lckhUTUwgIT0gbnVsbCkge1xuICAgICAgaWYgKGlubmVySFRNTC5fX2h0bWwgIT0gbnVsbCkge1xuICAgICAgICBET01MYXp5VHJlZS5xdWV1ZUhUTUwobGF6eVRyZWUsIGlubmVySFRNTC5fX2h0bWwpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgY29udGVudFRvVXNlID0gQ09OVEVOVF9UWVBFU1t0eXBlb2YgcHJvcHMuY2hpbGRyZW5dID8gcHJvcHMuY2hpbGRyZW4gOiBudWxsO1xuICAgICAgdmFyIGNoaWxkcmVuVG9Vc2UgPSBjb250ZW50VG9Vc2UgIT0gbnVsbCA/IG51bGwgOiBwcm9wcy5jaGlsZHJlbjtcbiAgICAgIC8vIFRPRE86IFZhbGlkYXRlIHRoYXQgdGV4dCBpcyBhbGxvd2VkIGFzIGEgY2hpbGQgb2YgdGhpcyBub2RlXG4gICAgICBpZiAoY29udGVudFRvVXNlICE9IG51bGwpIHtcbiAgICAgICAgLy8gQXZvaWQgc2V0dGluZyB0ZXh0Q29udGVudCB3aGVuIHRoZSB0ZXh0IGlzIGVtcHR5LiBJbiBJRTExIHNldHRpbmdcbiAgICAgICAgLy8gdGV4dENvbnRlbnQgb24gYSB0ZXh0IGFyZWEgd2lsbCBjYXVzZSB0aGUgcGxhY2Vob2xkZXIgdG8gbm90XG4gICAgICAgIC8vIHNob3cgd2l0aGluIHRoZSB0ZXh0YXJlYSB1bnRpbCBpdCBoYXMgYmVlbiBmb2N1c2VkIGFuZCBibHVycmVkIGFnYWluLlxuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2svcmVhY3QvaXNzdWVzLzY3MzEjaXNzdWVjb21tZW50LTI1NDg3NDU1M1xuICAgICAgICBpZiAoY29udGVudFRvVXNlICE9PSAnJykge1xuICAgICAgICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICAgICAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIGNvbnRlbnRUb1VzZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIERPTUxhenlUcmVlLnF1ZXVlVGV4dChsYXp5VHJlZSwgY29udGVudFRvVXNlKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChjaGlsZHJlblRvVXNlICE9IG51bGwpIHtcbiAgICAgICAgdmFyIG1vdW50SW1hZ2VzID0gdGhpcy5tb3VudENoaWxkcmVuKGNoaWxkcmVuVG9Vc2UsIHRyYW5zYWN0aW9uLCBjb250ZXh0KTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBtb3VudEltYWdlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIERPTUxhenlUcmVlLnF1ZXVlQ2hpbGQobGF6eVRyZWUsIG1vdW50SW1hZ2VzW2ldKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICAvKipcbiAgICogUmVjZWl2ZXMgYSBuZXh0IGVsZW1lbnQgYW5kIHVwZGF0ZXMgdGhlIGNvbXBvbmVudC5cbiAgICpcbiAgICogQGludGVybmFsXG4gICAqIEBwYXJhbSB7UmVhY3RFbGVtZW50fSBuZXh0RWxlbWVudFxuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb258UmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbn0gdHJhbnNhY3Rpb25cbiAgICogQHBhcmFtIHtvYmplY3R9IGNvbnRleHRcbiAgICovXG4gIHJlY2VpdmVDb21wb25lbnQ6IGZ1bmN0aW9uIChuZXh0RWxlbWVudCwgdHJhbnNhY3Rpb24sIGNvbnRleHQpIHtcbiAgICB2YXIgcHJldkVsZW1lbnQgPSB0aGlzLl9jdXJyZW50RWxlbWVudDtcbiAgICB0aGlzLl9jdXJyZW50RWxlbWVudCA9IG5leHRFbGVtZW50O1xuICAgIHRoaXMudXBkYXRlQ29tcG9uZW50KHRyYW5zYWN0aW9uLCBwcmV2RWxlbWVudCwgbmV4dEVsZW1lbnQsIGNvbnRleHQpO1xuICB9LFxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIGEgRE9NIGNvbXBvbmVudCBhZnRlciBpdCBoYXMgYWxyZWFkeSBiZWVuIGFsbG9jYXRlZCBhbmRcbiAgICogYXR0YWNoZWQgdG8gdGhlIERPTS4gUmVjb25jaWxlcyB0aGUgcm9vdCBET00gbm9kZSwgdGhlbiByZWN1cnNlcy5cbiAgICpcbiAgICogQHBhcmFtIHtSZWFjdFJlY29uY2lsZVRyYW5zYWN0aW9ufSB0cmFuc2FjdGlvblxuICAgKiBAcGFyYW0ge1JlYWN0RWxlbWVudH0gcHJldkVsZW1lbnRcbiAgICogQHBhcmFtIHtSZWFjdEVsZW1lbnR9IG5leHRFbGVtZW50XG4gICAqIEBpbnRlcm5hbFxuICAgKiBAb3ZlcnJpZGFibGVcbiAgICovXG4gIHVwZGF0ZUNvbXBvbmVudDogZnVuY3Rpb24gKHRyYW5zYWN0aW9uLCBwcmV2RWxlbWVudCwgbmV4dEVsZW1lbnQsIGNvbnRleHQpIHtcbiAgICB2YXIgbGFzdFByb3BzID0gcHJldkVsZW1lbnQucHJvcHM7XG4gICAgdmFyIG5leHRQcm9wcyA9IHRoaXMuX2N1cnJlbnRFbGVtZW50LnByb3BzO1xuXG4gICAgc3dpdGNoICh0aGlzLl90YWcpIHtcbiAgICAgIGNhc2UgJ2lucHV0JzpcbiAgICAgICAgbGFzdFByb3BzID0gUmVhY3RET01JbnB1dC5nZXRIb3N0UHJvcHModGhpcywgbGFzdFByb3BzKTtcbiAgICAgICAgbmV4dFByb3BzID0gUmVhY3RET01JbnB1dC5nZXRIb3N0UHJvcHModGhpcywgbmV4dFByb3BzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdvcHRpb24nOlxuICAgICAgICBsYXN0UHJvcHMgPSBSZWFjdERPTU9wdGlvbi5nZXRIb3N0UHJvcHModGhpcywgbGFzdFByb3BzKTtcbiAgICAgICAgbmV4dFByb3BzID0gUmVhY3RET01PcHRpb24uZ2V0SG9zdFByb3BzKHRoaXMsIG5leHRQcm9wcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnc2VsZWN0JzpcbiAgICAgICAgbGFzdFByb3BzID0gUmVhY3RET01TZWxlY3QuZ2V0SG9zdFByb3BzKHRoaXMsIGxhc3RQcm9wcyk7XG4gICAgICAgIG5leHRQcm9wcyA9IFJlYWN0RE9NU2VsZWN0LmdldEhvc3RQcm9wcyh0aGlzLCBuZXh0UHJvcHMpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3RleHRhcmVhJzpcbiAgICAgICAgbGFzdFByb3BzID0gUmVhY3RET01UZXh0YXJlYS5nZXRIb3N0UHJvcHModGhpcywgbGFzdFByb3BzKTtcbiAgICAgICAgbmV4dFByb3BzID0gUmVhY3RET01UZXh0YXJlYS5nZXRIb3N0UHJvcHModGhpcywgbmV4dFByb3BzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgYXNzZXJ0VmFsaWRQcm9wcyh0aGlzLCBuZXh0UHJvcHMpO1xuICAgIHRoaXMuX3VwZGF0ZURPTVByb3BlcnRpZXMobGFzdFByb3BzLCBuZXh0UHJvcHMsIHRyYW5zYWN0aW9uKTtcbiAgICB0aGlzLl91cGRhdGVET01DaGlsZHJlbihsYXN0UHJvcHMsIG5leHRQcm9wcywgdHJhbnNhY3Rpb24sIGNvbnRleHQpO1xuXG4gICAgc3dpdGNoICh0aGlzLl90YWcpIHtcbiAgICAgIGNhc2UgJ2lucHV0JzpcbiAgICAgICAgLy8gVXBkYXRlIHRoZSB3cmFwcGVyIGFyb3VuZCBpbnB1dHMgKmFmdGVyKiB1cGRhdGluZyBwcm9wcy4gVGhpcyBoYXMgdG9cbiAgICAgICAgLy8gaGFwcGVuIGFmdGVyIGBfdXBkYXRlRE9NUHJvcGVydGllc2AuIE90aGVyd2lzZSBIVE1MNSBpbnB1dCB2YWxpZGF0aW9uc1xuICAgICAgICAvLyByYWlzZSB3YXJuaW5ncyBhbmQgcHJldmVudCB0aGUgbmV3IHZhbHVlIGZyb20gYmVpbmcgYXNzaWduZWQuXG4gICAgICAgIFJlYWN0RE9NSW5wdXQudXBkYXRlV3JhcHBlcih0aGlzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd0ZXh0YXJlYSc6XG4gICAgICAgIFJlYWN0RE9NVGV4dGFyZWEudXBkYXRlV3JhcHBlcih0aGlzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdzZWxlY3QnOlxuICAgICAgICAvLyA8c2VsZWN0PiB2YWx1ZSB1cGRhdGUgbmVlZHMgdG8gb2NjdXIgYWZ0ZXIgPG9wdGlvbj4gY2hpbGRyZW5cbiAgICAgICAgLy8gcmVjb25jaWxpYXRpb25cbiAgICAgICAgdHJhbnNhY3Rpb24uZ2V0UmVhY3RNb3VudFJlYWR5KCkuZW5xdWV1ZShwb3N0VXBkYXRlU2VsZWN0V3JhcHBlciwgdGhpcyk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfSxcblxuICAvKipcbiAgICogUmVjb25jaWxlcyB0aGUgcHJvcGVydGllcyBieSBkZXRlY3RpbmcgZGlmZmVyZW5jZXMgaW4gcHJvcGVydHkgdmFsdWVzIGFuZFxuICAgKiB1cGRhdGluZyB0aGUgRE9NIGFzIG5lY2Vzc2FyeS4gVGhpcyBmdW5jdGlvbiBpcyBwcm9iYWJseSB0aGUgc2luZ2xlIG1vc3RcbiAgICogY3JpdGljYWwgcGF0aCBmb3IgcGVyZm9ybWFuY2Ugb3B0aW1pemF0aW9uLlxuICAgKlxuICAgKiBUT0RPOiBCZW5jaG1hcmsgd2hldGhlciBjaGVja2luZyBmb3IgY2hhbmdlZCB2YWx1ZXMgaW4gbWVtb3J5IGFjdHVhbGx5XG4gICAqICAgICAgIGltcHJvdmVzIHBlcmZvcm1hbmNlIChlc3BlY2lhbGx5IHN0YXRpY2FsbHkgcG9zaXRpb25lZCBlbGVtZW50cykuXG4gICAqIFRPRE86IEJlbmNobWFyayB0aGUgZWZmZWN0cyBvZiBwdXR0aW5nIHRoaXMgYXQgdGhlIHRvcCBzaW5jZSA5OSUgb2YgcHJvcHNcbiAgICogICAgICAgZG8gbm90IGNoYW5nZSBmb3IgYSBnaXZlbiByZWNvbmNpbGlhdGlvbi5cbiAgICogVE9ETzogQmVuY2htYXJrIGFyZWFzIHRoYXQgY2FuIGJlIGltcHJvdmVkIHdpdGggY2FjaGluZy5cbiAgICpcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtvYmplY3R9IGxhc3RQcm9wc1xuICAgKiBAcGFyYW0ge29iamVjdH0gbmV4dFByb3BzXG4gICAqIEBwYXJhbSB7P0RPTUVsZW1lbnR9IG5vZGVcbiAgICovXG4gIF91cGRhdGVET01Qcm9wZXJ0aWVzOiBmdW5jdGlvbiAobGFzdFByb3BzLCBuZXh0UHJvcHMsIHRyYW5zYWN0aW9uKSB7XG4gICAgdmFyIHByb3BLZXk7XG4gICAgdmFyIHN0eWxlTmFtZTtcbiAgICB2YXIgc3R5bGVVcGRhdGVzO1xuICAgIGZvciAocHJvcEtleSBpbiBsYXN0UHJvcHMpIHtcbiAgICAgIGlmIChuZXh0UHJvcHMuaGFzT3duUHJvcGVydHkocHJvcEtleSkgfHwgIWxhc3RQcm9wcy5oYXNPd25Qcm9wZXJ0eShwcm9wS2V5KSB8fCBsYXN0UHJvcHNbcHJvcEtleV0gPT0gbnVsbCkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmIChwcm9wS2V5ID09PSBTVFlMRSkge1xuICAgICAgICB2YXIgbGFzdFN0eWxlID0gdGhpcy5fcHJldmlvdXNTdHlsZUNvcHk7XG4gICAgICAgIGZvciAoc3R5bGVOYW1lIGluIGxhc3RTdHlsZSkge1xuICAgICAgICAgIGlmIChsYXN0U3R5bGUuaGFzT3duUHJvcGVydHkoc3R5bGVOYW1lKSkge1xuICAgICAgICAgICAgc3R5bGVVcGRhdGVzID0gc3R5bGVVcGRhdGVzIHx8IHt9O1xuICAgICAgICAgICAgc3R5bGVVcGRhdGVzW3N0eWxlTmFtZV0gPSAnJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fcHJldmlvdXNTdHlsZUNvcHkgPSBudWxsO1xuICAgICAgfSBlbHNlIGlmIChyZWdpc3RyYXRpb25OYW1lTW9kdWxlcy5oYXNPd25Qcm9wZXJ0eShwcm9wS2V5KSkge1xuICAgICAgICBpZiAobGFzdFByb3BzW3Byb3BLZXldKSB7XG4gICAgICAgICAgLy8gT25seSBjYWxsIGRlbGV0ZUxpc3RlbmVyIGlmIHRoZXJlIHdhcyBhIGxpc3RlbmVyIHByZXZpb3VzbHkgb3JcbiAgICAgICAgICAvLyBlbHNlIHdpbGxEZWxldGVMaXN0ZW5lciBnZXRzIGNhbGxlZCB3aGVuIHRoZXJlIHdhc24ndCBhY3R1YWxseSBhXG4gICAgICAgICAgLy8gbGlzdGVuZXIgKGUuZy4sIG9uQ2xpY2s9e251bGx9KVxuICAgICAgICAgIGRlbGV0ZUxpc3RlbmVyKHRoaXMsIHByb3BLZXkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGlzQ3VzdG9tQ29tcG9uZW50KHRoaXMuX3RhZywgbGFzdFByb3BzKSkge1xuICAgICAgICBpZiAoIVJFU0VSVkVEX1BST1BTLmhhc093blByb3BlcnR5KHByb3BLZXkpKSB7XG4gICAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLmRlbGV0ZVZhbHVlRm9yQXR0cmlidXRlKGdldE5vZGUodGhpcyksIHByb3BLZXkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKERPTVByb3BlcnR5LnByb3BlcnRpZXNbcHJvcEtleV0gfHwgRE9NUHJvcGVydHkuaXNDdXN0b21BdHRyaWJ1dGUocHJvcEtleSkpIHtcbiAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLmRlbGV0ZVZhbHVlRm9yUHJvcGVydHkoZ2V0Tm9kZSh0aGlzKSwgcHJvcEtleSk7XG4gICAgICB9XG4gICAgfVxuICAgIGZvciAocHJvcEtleSBpbiBuZXh0UHJvcHMpIHtcbiAgICAgIHZhciBuZXh0UHJvcCA9IG5leHRQcm9wc1twcm9wS2V5XTtcbiAgICAgIHZhciBsYXN0UHJvcCA9IHByb3BLZXkgPT09IFNUWUxFID8gdGhpcy5fcHJldmlvdXNTdHlsZUNvcHkgOiBsYXN0UHJvcHMgIT0gbnVsbCA/IGxhc3RQcm9wc1twcm9wS2V5XSA6IHVuZGVmaW5lZDtcbiAgICAgIGlmICghbmV4dFByb3BzLmhhc093blByb3BlcnR5KHByb3BLZXkpIHx8IG5leHRQcm9wID09PSBsYXN0UHJvcCB8fCBuZXh0UHJvcCA9PSBudWxsICYmIGxhc3RQcm9wID09IG51bGwpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAocHJvcEtleSA9PT0gU1RZTEUpIHtcbiAgICAgICAgaWYgKG5leHRQcm9wKSB7XG4gICAgICAgICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgICAgICAgIGNoZWNrQW5kV2FybkZvck11dGF0ZWRTdHlsZSh0aGlzLl9wcmV2aW91c1N0eWxlQ29weSwgdGhpcy5fcHJldmlvdXNTdHlsZSwgdGhpcyk7XG4gICAgICAgICAgICB0aGlzLl9wcmV2aW91c1N0eWxlID0gbmV4dFByb3A7XG4gICAgICAgICAgfVxuICAgICAgICAgIG5leHRQcm9wID0gdGhpcy5fcHJldmlvdXNTdHlsZUNvcHkgPSBfYXNzaWduKHt9LCBuZXh0UHJvcCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5fcHJldmlvdXNTdHlsZUNvcHkgPSBudWxsO1xuICAgICAgICB9XG4gICAgICAgIGlmIChsYXN0UHJvcCkge1xuICAgICAgICAgIC8vIFVuc2V0IHN0eWxlcyBvbiBgbGFzdFByb3BgIGJ1dCBub3Qgb24gYG5leHRQcm9wYC5cbiAgICAgICAgICBmb3IgKHN0eWxlTmFtZSBpbiBsYXN0UHJvcCkge1xuICAgICAgICAgICAgaWYgKGxhc3RQcm9wLmhhc093blByb3BlcnR5KHN0eWxlTmFtZSkgJiYgKCFuZXh0UHJvcCB8fCAhbmV4dFByb3AuaGFzT3duUHJvcGVydHkoc3R5bGVOYW1lKSkpIHtcbiAgICAgICAgICAgICAgc3R5bGVVcGRhdGVzID0gc3R5bGVVcGRhdGVzIHx8IHt9O1xuICAgICAgICAgICAgICBzdHlsZVVwZGF0ZXNbc3R5bGVOYW1lXSA9ICcnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBVcGRhdGUgc3R5bGVzIHRoYXQgY2hhbmdlZCBzaW5jZSBgbGFzdFByb3BgLlxuICAgICAgICAgIGZvciAoc3R5bGVOYW1lIGluIG5leHRQcm9wKSB7XG4gICAgICAgICAgICBpZiAobmV4dFByb3AuaGFzT3duUHJvcGVydHkoc3R5bGVOYW1lKSAmJiBsYXN0UHJvcFtzdHlsZU5hbWVdICE9PSBuZXh0UHJvcFtzdHlsZU5hbWVdKSB7XG4gICAgICAgICAgICAgIHN0eWxlVXBkYXRlcyA9IHN0eWxlVXBkYXRlcyB8fCB7fTtcbiAgICAgICAgICAgICAgc3R5bGVVcGRhdGVzW3N0eWxlTmFtZV0gPSBuZXh0UHJvcFtzdHlsZU5hbWVdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBSZWxpZXMgb24gYHVwZGF0ZVN0eWxlc0J5SURgIG5vdCBtdXRhdGluZyBgc3R5bGVVcGRhdGVzYC5cbiAgICAgICAgICBzdHlsZVVwZGF0ZXMgPSBuZXh0UHJvcDtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChyZWdpc3RyYXRpb25OYW1lTW9kdWxlcy5oYXNPd25Qcm9wZXJ0eShwcm9wS2V5KSkge1xuICAgICAgICBpZiAobmV4dFByb3ApIHtcbiAgICAgICAgICBlbnF1ZXVlUHV0TGlzdGVuZXIodGhpcywgcHJvcEtleSwgbmV4dFByb3AsIHRyYW5zYWN0aW9uKTtcbiAgICAgICAgfSBlbHNlIGlmIChsYXN0UHJvcCkge1xuICAgICAgICAgIGRlbGV0ZUxpc3RlbmVyKHRoaXMsIHByb3BLZXkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGlzQ3VzdG9tQ29tcG9uZW50KHRoaXMuX3RhZywgbmV4dFByb3BzKSkge1xuICAgICAgICBpZiAoIVJFU0VSVkVEX1BST1BTLmhhc093blByb3BlcnR5KHByb3BLZXkpKSB7XG4gICAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLnNldFZhbHVlRm9yQXR0cmlidXRlKGdldE5vZGUodGhpcyksIHByb3BLZXksIG5leHRQcm9wKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChET01Qcm9wZXJ0eS5wcm9wZXJ0aWVzW3Byb3BLZXldIHx8IERPTVByb3BlcnR5LmlzQ3VzdG9tQXR0cmlidXRlKHByb3BLZXkpKSB7XG4gICAgICAgIHZhciBub2RlID0gZ2V0Tm9kZSh0aGlzKTtcbiAgICAgICAgLy8gSWYgd2UncmUgdXBkYXRpbmcgdG8gbnVsbCBvciB1bmRlZmluZWQsIHdlIHNob3VsZCByZW1vdmUgdGhlIHByb3BlcnR5XG4gICAgICAgIC8vIGZyb20gdGhlIERPTSBub2RlIGluc3RlYWQgb2YgaW5hZHZlcnRlbnRseSBzZXR0aW5nIHRvIGEgc3RyaW5nLiBUaGlzXG4gICAgICAgIC8vIGJyaW5ncyB1cyBpbiBsaW5lIHdpdGggdGhlIHNhbWUgYmVoYXZpb3Igd2UgaGF2ZSBvbiBpbml0aWFsIHJlbmRlci5cbiAgICAgICAgaWYgKG5leHRQcm9wICE9IG51bGwpIHtcbiAgICAgICAgICBET01Qcm9wZXJ0eU9wZXJhdGlvbnMuc2V0VmFsdWVGb3JQcm9wZXJ0eShub2RlLCBwcm9wS2V5LCBuZXh0UHJvcCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgRE9NUHJvcGVydHlPcGVyYXRpb25zLmRlbGV0ZVZhbHVlRm9yUHJvcGVydHkobm9kZSwgcHJvcEtleSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHN0eWxlVXBkYXRlcykge1xuICAgICAgQ1NTUHJvcGVydHlPcGVyYXRpb25zLnNldFZhbHVlRm9yU3R5bGVzKGdldE5vZGUodGhpcyksIHN0eWxlVXBkYXRlcywgdGhpcyk7XG4gICAgfVxuICB9LFxuXG4gIC8qKlxuICAgKiBSZWNvbmNpbGVzIHRoZSBjaGlsZHJlbiB3aXRoIHRoZSB2YXJpb3VzIHByb3BlcnRpZXMgdGhhdCBhZmZlY3QgdGhlXG4gICAqIGNoaWxkcmVuIGNvbnRlbnQuXG4gICAqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBsYXN0UHJvcHNcbiAgICogQHBhcmFtIHtvYmplY3R9IG5leHRQcm9wc1xuICAgKiBAcGFyYW0ge1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb259IHRyYW5zYWN0aW9uXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBjb250ZXh0XG4gICAqL1xuICBfdXBkYXRlRE9NQ2hpbGRyZW46IGZ1bmN0aW9uIChsYXN0UHJvcHMsIG5leHRQcm9wcywgdHJhbnNhY3Rpb24sIGNvbnRleHQpIHtcbiAgICB2YXIgbGFzdENvbnRlbnQgPSBDT05URU5UX1RZUEVTW3R5cGVvZiBsYXN0UHJvcHMuY2hpbGRyZW5dID8gbGFzdFByb3BzLmNoaWxkcmVuIDogbnVsbDtcbiAgICB2YXIgbmV4dENvbnRlbnQgPSBDT05URU5UX1RZUEVTW3R5cGVvZiBuZXh0UHJvcHMuY2hpbGRyZW5dID8gbmV4dFByb3BzLmNoaWxkcmVuIDogbnVsbDtcblxuICAgIHZhciBsYXN0SHRtbCA9IGxhc3RQcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCAmJiBsYXN0UHJvcHMuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwuX19odG1sO1xuICAgIHZhciBuZXh0SHRtbCA9IG5leHRQcm9wcy5kYW5nZXJvdXNseVNldElubmVySFRNTCAmJiBuZXh0UHJvcHMuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwuX19odG1sO1xuXG4gICAgLy8gTm90ZSB0aGUgdXNlIG9mIGAhPWAgd2hpY2ggY2hlY2tzIGZvciBudWxsIG9yIHVuZGVmaW5lZC5cbiAgICB2YXIgbGFzdENoaWxkcmVuID0gbGFzdENvbnRlbnQgIT0gbnVsbCA/IG51bGwgOiBsYXN0UHJvcHMuY2hpbGRyZW47XG4gICAgdmFyIG5leHRDaGlsZHJlbiA9IG5leHRDb250ZW50ICE9IG51bGwgPyBudWxsIDogbmV4dFByb3BzLmNoaWxkcmVuO1xuXG4gICAgLy8gSWYgd2UncmUgc3dpdGNoaW5nIGZyb20gY2hpbGRyZW4gdG8gY29udGVudC9odG1sIG9yIHZpY2UgdmVyc2EsIHJlbW92ZVxuICAgIC8vIHRoZSBvbGQgY29udGVudFxuICAgIHZhciBsYXN0SGFzQ29udGVudE9ySHRtbCA9IGxhc3RDb250ZW50ICE9IG51bGwgfHwgbGFzdEh0bWwgIT0gbnVsbDtcbiAgICB2YXIgbmV4dEhhc0NvbnRlbnRPckh0bWwgPSBuZXh0Q29udGVudCAhPSBudWxsIHx8IG5leHRIdG1sICE9IG51bGw7XG4gICAgaWYgKGxhc3RDaGlsZHJlbiAhPSBudWxsICYmIG5leHRDaGlsZHJlbiA9PSBudWxsKSB7XG4gICAgICB0aGlzLnVwZGF0ZUNoaWxkcmVuKG51bGwsIHRyYW5zYWN0aW9uLCBjb250ZXh0KTtcbiAgICB9IGVsc2UgaWYgKGxhc3RIYXNDb250ZW50T3JIdG1sICYmICFuZXh0SGFzQ29udGVudE9ySHRtbCkge1xuICAgICAgdGhpcy51cGRhdGVUZXh0Q29udGVudCgnJyk7XG4gICAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJykge1xuICAgICAgICBSZWFjdEluc3RydW1lbnRhdGlvbi5kZWJ1Z1Rvb2wub25TZXRDaGlsZHJlbih0aGlzLl9kZWJ1Z0lELCBbXSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG5leHRDb250ZW50ICE9IG51bGwpIHtcbiAgICAgIGlmIChsYXN0Q29udGVudCAhPT0gbmV4dENvbnRlbnQpIHtcbiAgICAgICAgdGhpcy51cGRhdGVUZXh0Q29udGVudCgnJyArIG5leHRDb250ZW50KTtcbiAgICAgICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicpIHtcbiAgICAgICAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIG5leHRDb250ZW50KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAobmV4dEh0bWwgIT0gbnVsbCkge1xuICAgICAgaWYgKGxhc3RIdG1sICE9PSBuZXh0SHRtbCkge1xuICAgICAgICB0aGlzLnVwZGF0ZU1hcmt1cCgnJyArIG5leHRIdG1sKTtcbiAgICAgIH1cbiAgICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICAgIFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vblNldENoaWxkcmVuKHRoaXMuX2RlYnVnSUQsIFtdKTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKG5leHRDaGlsZHJlbiAhPSBudWxsKSB7XG4gICAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJykge1xuICAgICAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIG51bGwpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLnVwZGF0ZUNoaWxkcmVuKG5leHRDaGlsZHJlbiwgdHJhbnNhY3Rpb24sIGNvbnRleHQpO1xuICAgIH1cbiAgfSxcblxuICBnZXRIb3N0Tm9kZTogZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiBnZXROb2RlKHRoaXMpO1xuICB9LFxuXG4gIC8qKlxuICAgKiBEZXN0cm95cyBhbGwgZXZlbnQgcmVnaXN0cmF0aW9ucyBmb3IgdGhpcyBpbnN0YW5jZS4gRG9lcyBub3QgcmVtb3ZlIGZyb21cbiAgICogdGhlIERPTS4gVGhhdCBtdXN0IGJlIGRvbmUgYnkgdGhlIHBhcmVudC5cbiAgICpcbiAgICogQGludGVybmFsXG4gICAqL1xuICB1bm1vdW50Q29tcG9uZW50OiBmdW5jdGlvbiAoc2FmZWx5KSB7XG4gICAgc3dpdGNoICh0aGlzLl90YWcpIHtcbiAgICAgIGNhc2UgJ2F1ZGlvJzpcbiAgICAgIGNhc2UgJ2Zvcm0nOlxuICAgICAgY2FzZSAnaWZyYW1lJzpcbiAgICAgIGNhc2UgJ2ltZyc6XG4gICAgICBjYXNlICdsaW5rJzpcbiAgICAgIGNhc2UgJ29iamVjdCc6XG4gICAgICBjYXNlICdzb3VyY2UnOlxuICAgICAgY2FzZSAndmlkZW8nOlxuICAgICAgICB2YXIgbGlzdGVuZXJzID0gdGhpcy5fd3JhcHBlclN0YXRlLmxpc3RlbmVycztcbiAgICAgICAgaWYgKGxpc3RlbmVycykge1xuICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGlzdGVuZXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBsaXN0ZW5lcnNbaV0ucmVtb3ZlKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnaHRtbCc6XG4gICAgICBjYXNlICdoZWFkJzpcbiAgICAgIGNhc2UgJ2JvZHknOlxuICAgICAgICAvKipcbiAgICAgICAgICogQ29tcG9uZW50cyBsaWtlIDxodG1sPiA8aGVhZD4gYW5kIDxib2R5PiBjYW4ndCBiZSByZW1vdmVkIG9yIGFkZGVkXG4gICAgICAgICAqIGVhc2lseSBpbiBhIGNyb3NzLWJyb3dzZXIgd2F5LCBob3dldmVyIGl0J3MgdmFsdWFibGUgdG8gYmUgYWJsZSB0b1xuICAgICAgICAgKiB0YWtlIGFkdmFudGFnZSBvZiBSZWFjdCdzIHJlY29uY2lsaWF0aW9uIGZvciBzdHlsaW5nIGFuZCA8dGl0bGU+XG4gICAgICAgICAqIG1hbmFnZW1lbnQuIFNvIHdlIGp1c3QgZG9jdW1lbnQgaXQgYW5kIHRocm93IGluIGRhbmdlcm91cyBjYXNlcy5cbiAgICAgICAgICovXG4gICAgICAgICFmYWxzZSA/IHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAncHJvZHVjdGlvbicgPyBpbnZhcmlhbnQoZmFsc2UsICc8JXM+IHRyaWVkIHRvIHVubW91bnQuIEJlY2F1c2Ugb2YgY3Jvc3MtYnJvd3NlciBxdWlya3MgaXQgaXMgaW1wb3NzaWJsZSB0byB1bm1vdW50IHNvbWUgdG9wLWxldmVsIGNvbXBvbmVudHMgKGVnIDxodG1sPiwgPGhlYWQ+LCBhbmQgPGJvZHk+KSByZWxpYWJseSBhbmQgZWZmaWNpZW50bHkuIFRvIGZpeCB0aGlzLCBoYXZlIGEgc2luZ2xlIHRvcC1sZXZlbCBjb21wb25lbnQgdGhhdCBuZXZlciB1bm1vdW50cyByZW5kZXIgdGhlc2UgZWxlbWVudHMuJywgdGhpcy5fdGFnKSA6IF9wcm9kSW52YXJpYW50KCc2NicsIHRoaXMuX3RhZykgOiB2b2lkIDA7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIHRoaXMudW5tb3VudENoaWxkcmVuKHNhZmVseSk7XG4gICAgUmVhY3RET01Db21wb25lbnRUcmVlLnVuY2FjaGVOb2RlKHRoaXMpO1xuICAgIEV2ZW50UGx1Z2luSHViLmRlbGV0ZUFsbExpc3RlbmVycyh0aGlzKTtcbiAgICB0aGlzLl9yb290Tm9kZUlEID0gMDtcbiAgICB0aGlzLl9kb21JRCA9IDA7XG4gICAgdGhpcy5fd3JhcHBlclN0YXRlID0gbnVsbDtcblxuICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgICBzZXRBbmRWYWxpZGF0ZUNvbnRlbnRDaGlsZERldi5jYWxsKHRoaXMsIG51bGwpO1xuICAgIH1cbiAgfSxcblxuICBnZXRQdWJsaWNJbnN0YW5jZTogZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiBnZXROb2RlKHRoaXMpO1xuICB9XG5cbn07XG5cbl9hc3NpZ24oUmVhY3RET01Db21wb25lbnQucHJvdG90eXBlLCBSZWFjdERPTUNvbXBvbmVudC5NaXhpbiwgUmVhY3RNdWx0aUNoaWxkLk1peGluKTtcblxubW9kdWxlLmV4cG9ydHMgPSBSZWFjdERPTUNvbXBvbmVudDtcblxuXG4vLy8vLy8vLy8vLy8vLy8vLy9cbi8vIFdFQlBBQ0sgRk9PVEVSXG4vLyAuL34vcmVhY3QtZG9tL2xpYi9SZWFjdERPTUNvbXBvbmVudC5qc1xuLy8gbW9kdWxlIGlkID0gMjY1XG4vLyBtb2R1bGUgY2h1bmtzID0gMCJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=");

FIXME found
Open

    eval("/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar SDPUtils = __webpack_require__(452);\nvar logging = __webpack_require__(448).log;\n\nvar edgeShim = {\n  shimPeerConnection: function() {\n    if (window.RTCIceGatherer) {\n      // ORTC defines an RTCIceCandidate object but no constructor.\n      // Not implemented in Edge.\n      if (!window.RTCIceCandidate) {\n        window.RTCIceCandidate = function(args) {\n          return args;\n        };\n      }\n      // ORTC does not have a session description object but\n      // other browsers (i.e. Chrome) that will support both PC and ORTC\n      // in the future might have this defined already.\n      if (!window.RTCSessionDescription) {\n        window.RTCSessionDescription = function(args) {\n          return args;\n        };\n      }\n    }\n\n    window.RTCPeerConnection = function(config) {\n      var self = this;\n\n      var _eventTarget = document.createDocumentFragment();\n      ['addEventListener', 'removeEventListener', 'dispatchEvent']\n          .forEach(function(method) {\n            self[method] = _eventTarget[method].bind(_eventTarget);\n          });\n\n      this.onicecandidate = null;\n      this.onaddstream = null;\n      this.ontrack = null;\n      this.onremovestream = null;\n      this.onsignalingstatechange = null;\n      this.oniceconnectionstatechange = null;\n      this.onnegotiationneeded = null;\n      this.ondatachannel = null;\n\n      this.localStreams = [];\n      this.remoteStreams = [];\n      this.getLocalStreams = function() {\n        return self.localStreams;\n      };\n      this.getRemoteStreams = function() {\n        return self.remoteStreams;\n      };\n\n      this.localDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.remoteDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.signalingState = 'stable';\n      this.iceConnectionState = 'new';\n      this.iceGatheringState = 'new';\n\n      this.iceOptions = {\n        gatherPolicy: 'all',\n        iceServers: []\n      };\n      if (config && config.iceTransportPolicy) {\n        switch (config.iceTransportPolicy) {\n          case 'all':\n          case 'relay':\n            this.iceOptions.gatherPolicy = config.iceTransportPolicy;\n            break;\n          case 'none':\n            // FIXME: remove once implementation and spec have added this.\n            throw new TypeError('iceTransportPolicy \"none\" not supported');\n          default:\n            // don't set iceTransportPolicy.\n            break;\n        }\n      }\n      this.usingBundle = config && config.bundlePolicy === 'max-bundle';\n\n      if (config && config.iceServers) {\n        // Edge does not like\n        // 1) stun:\n        // 2) turn: that does not have all of turn:host:port?transport=udp\n        var iceServers = JSON.parse(JSON.stringify(config.iceServers));\n        this.iceOptions.iceServers = iceServers.filter(function(server) {\n          if (server && server.urls) {\n            var urls = server.urls;\n            if (typeof urls === 'string') {\n              urls = [urls];\n            }\n            urls = urls.filter(function(url) {\n              return url.indexOf('turn:') === 0 &&\n                  url.indexOf('transport=udp') !== -1;\n            })[0];\n            return !!urls;\n          }\n          return false;\n        });\n      }\n\n      // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n      // everything that is needed to describe a SDP m-line.\n      this.transceivers = [];\n\n      // since the iceGatherer is currently created in createOffer but we\n      // must not emit candidates until after setLocalDescription we buffer\n      // them in this array.\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {\n      var self = this;\n      var sections = SDPUtils.splitSections(self.localDescription.sdp);\n      // FIXME: need to apply ice candidates in a way which is async but\n      // in-order\n      this._localIceCandidatesBuffer.forEach(function(event) {\n        var end = !event.candidate || Object.keys(event.candidate).length === 0;\n        if (end) {\n          for (var j = 1; j < sections.length; j++) {\n            if (sections[j].indexOf('\\r\\na=end-of-candidates\\r\\n') === -1) {\n              sections[j] += 'a=end-of-candidates\\r\\n';\n            }\n          }\n        } else if (event.candidate.candidate.indexOf('typ endOfCandidates')\n            === -1) {\n          sections[event.candidate.sdpMLineIndex + 1] +=\n              'a=' + event.candidate.candidate + '\\r\\n';\n        }\n        self.localDescription.sdp = sections.join('');\n        self.dispatchEvent(event);\n        if (self.onicecandidate !== null) {\n          self.onicecandidate(event);\n        }\n        if (!event.candidate && self.iceGatheringState !== 'complete') {\n          var complete = self.transceivers.every(function(transceiver) {\n            return transceiver.iceGatherer &&\n                transceiver.iceGatherer.state === 'completed';\n          });\n          if (complete) {\n            self.iceGatheringState = 'complete';\n          }\n        }\n      });\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype.addStream = function(stream) {\n      // Clone is necessary for local demos mostly, attaching directly\n      // to two different senders does not work (build 10547).\n      this.localStreams.push(stream.clone());\n      this._maybeFireNegotiationNeeded();\n    };\n\n    window.RTCPeerConnection.prototype.removeStream = function(stream) {\n      var idx = this.localStreams.indexOf(stream);\n      if (idx > -1) {\n        this.localStreams.splice(idx, 1);\n        this._maybeFireNegotiationNeeded();\n      }\n    };\n\n    window.RTCPeerConnection.prototype.getSenders = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpSender;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpSender;\n      });\n    };\n\n    window.RTCPeerConnection.prototype.getReceivers = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpReceiver;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpReceiver;\n      });\n    };\n\n    // Determines the intersection of local and remote capabilities.\n    window.RTCPeerConnection.prototype._getCommonCapabilities =\n        function(localCapabilities, remoteCapabilities) {\n          var commonCapabilities = {\n            codecs: [],\n            headerExtensions: [],\n            fecMechanisms: []\n          };\n          localCapabilities.codecs.forEach(function(lCodec) {\n            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n              var rCodec = remoteCapabilities.codecs[i];\n              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n                  lCodec.clockRate === rCodec.clockRate &&\n                  lCodec.numChannels === rCodec.numChannels) {\n                // push rCodec so we reply with offerer payload type\n                commonCapabilities.codecs.push(rCodec);\n\n                // FIXME: also need to determine intersection between\n                // .rtcpFeedback and .parameters\n                break;\n              }\n            }\n          });\n\n          localCapabilities.headerExtensions\n              .forEach(function(lHeaderExtension) {\n                for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n                     i++) {\n                  var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n                  if (lHeaderExtension.uri === rHeaderExtension.uri) {\n                    commonCapabilities.headerExtensions.push(rHeaderExtension);\n                    break;\n                  }\n                }\n              });\n\n          // FIXME: fecMechanisms\n          return commonCapabilities;\n        };\n\n    // Create ICE gatherer, ICE transport and DTLS transport.\n    window.RTCPeerConnection.prototype._createIceAndDtlsTransports =\n        function(mid, sdpMLineIndex) {\n          var self = this;\n          var iceGatherer = new RTCIceGatherer(self.iceOptions);\n          var iceTransport = new RTCIceTransport(iceGatherer);\n          iceGatherer.onlocalcandidate = function(evt) {\n            var event = new Event('icecandidate');\n            event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n            var cand = evt.candidate;\n            var end = !cand || Object.keys(cand).length === 0;\n            // Edge emits an empty object for RTCIceCandidateComplete‥\n            if (end) {\n              // polyfill since RTCIceGatherer.state is not implemented in\n              // Edge 10547 yet.\n              if (iceGatherer.state === undefined) {\n                iceGatherer.state = 'completed';\n              }\n\n              // Emit a candidate with type endOfCandidates to make the samples\n              // work. Edge requires addIceCandidate with this empty candidate\n              // to start checking. The real solution is to signal\n              // end-of-candidates to the other side when getting the null\n              // candidate but some apps (like the samples) don't do that.\n              event.candidate.candidate =\n                  'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';\n            } else {\n              // RTCIceCandidate doesn't have a component, needs to be added\n              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;\n              event.candidate.candidate = SDPUtils.writeCandidate(cand);\n            }\n\n            // update local description.\n            var sections = SDPUtils.splitSections(self.localDescription.sdp);\n            if (event.candidate.candidate.indexOf('typ endOfCandidates')\n                === -1) {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=' + event.candidate.candidate + '\\r\\n';\n            } else {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=end-of-candidates\\r\\n';\n            }\n            self.localDescription.sdp = sections.join('');\n\n            var complete = self.transceivers.every(function(transceiver) {\n              return transceiver.iceGatherer &&\n                  transceiver.iceGatherer.state === 'completed';\n            });\n\n            // Emit candidate if localDescription is set.\n            // Also emits null candidate when all gatherers are complete.\n            switch (self.iceGatheringState) {\n              case 'new':\n                self._localIceCandidatesBuffer.push(event);\n                if (end && complete) {\n                  self._localIceCandidatesBuffer.push(\n                      new Event('icecandidate'));\n                }\n                break;\n              case 'gathering':\n                self._emitBufferedCandidates();\n                self.dispatchEvent(event);\n                if (self.onicecandidate !== null) {\n                  self.onicecandidate(event);\n                }\n                if (complete) {\n                  self.dispatchEvent(new Event('icecandidate'));\n                  if (self.onicecandidate !== null) {\n                    self.onicecandidate(new Event('icecandidate'));\n                  }\n                  self.iceGatheringState = 'complete';\n                }\n                break;\n              case 'complete':\n                // should not happen... currently!\n                break;\n              default: // no-op.\n                break;\n            }\n          };\n          iceTransport.onicestatechange = function() {\n            self._updateConnectionState();\n          };\n\n          var dtlsTransport = new RTCDtlsTransport(iceTransport);\n          dtlsTransport.ondtlsstatechange = function() {\n            self._updateConnectionState();\n          };\n          dtlsTransport.onerror = function() {\n            // onerror does not set state to failed by itself.\n            dtlsTransport.state = 'failed';\n            self._updateConnectionState();\n          };\n\n          return {\n            iceGatherer: iceGatherer,\n            iceTransport: iceTransport,\n            dtlsTransport: dtlsTransport\n          };\n        };\n\n    // Start the RTP Sender and Receiver for a transceiver.\n    window.RTCPeerConnection.prototype._transceive = function(transceiver,\n        send, recv) {\n      var params = this._getCommonCapabilities(transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n      if (send && transceiver.rtpSender) {\n        params.encodings = transceiver.sendEncodingParameters;\n        params.rtcp = {\n          cname: SDPUtils.localCName\n        };\n        if (transceiver.recvEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpSender.send(params);\n      }\n      if (recv && transceiver.rtpReceiver) {\n        params.encodings = transceiver.recvEncodingParameters;\n        params.rtcp = {\n          cname: transceiver.cname\n        };\n        if (transceiver.sendEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpReceiver.receive(params);\n      }\n    };\n\n    window.RTCPeerConnection.prototype.setLocalDescription =\n        function(description) {\n          var self = this;\n          var sections;\n          var sessionpart;\n          if (description.type === 'offer') {\n            // FIXME: What was the purpose of this empty if statement?\n            // if (!this._pendingOffer) {\n            // } else {\n            if (this._pendingOffer) {\n              // VERY limited support for SDP munging. Limited to:\n              // * changing the order of codecs\n              sections = SDPUtils.splitSections(description.sdp);\n              sessionpart = sections.shift();\n              sections.forEach(function(mediaSection, sdpMLineIndex) {\n                var caps = SDPUtils.parseRtpParameters(mediaSection);\n                self._pendingOffer[sdpMLineIndex].localCapabilities = caps;\n              });\n              this.transceivers = this._pendingOffer;\n              delete this._pendingOffer;\n            }\n          } else if (description.type === 'answer') {\n            sections = SDPUtils.splitSections(self.remoteDescription.sdp);\n            sessionpart = sections.shift();\n            var isIceLite = SDPUtils.matchPrefix(sessionpart,\n                'a=ice-lite').length > 0;\n            sections.forEach(function(mediaSection, sdpMLineIndex) {\n              var transceiver = self.transceivers[sdpMLineIndex];\n              var iceGatherer = transceiver.iceGatherer;\n              var iceTransport = transceiver.iceTransport;\n              var dtlsTransport = transceiver.dtlsTransport;\n              var localCapabilities = transceiver.localCapabilities;\n              var remoteCapabilities = transceiver.remoteCapabilities;\n              var rejected = mediaSection.split('\\n', 1)[0]\n                  .split(' ', 2)[1] === '0';\n\n              if (!rejected) {\n                var remoteIceParameters = SDPUtils.getIceParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                  .map(function(cand) {\n                    return SDPUtils.parseCandidate(cand);\n                  })\n                  .filter(function(cand) {\n                    return cand.component === '1';\n                  });\n                  // ice-lite only includes host candidates in the SDP so we can\n                  // use setRemoteCandidates (which implies an\n                  // RTCIceCandidateComplete)\n                  if (cands.length) {\n                    iceTransport.setRemoteCandidates(cands);\n                  }\n                }\n                var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  remoteDtlsParameters.role = 'server';\n                }\n\n                if (!self.usingBundle || sdpMLineIndex === 0) {\n                  iceTransport.start(iceGatherer, remoteIceParameters,\n                      isIceLite ? 'controlling' : 'controlled');\n                  dtlsTransport.start(remoteDtlsParameters);\n                }\n\n                // Calculate intersection of capabilities.\n                var params = self._getCommonCapabilities(localCapabilities,\n                    remoteCapabilities);\n\n                // Start the RTCRtpSender. The RTCRtpReceiver for this\n                // transceiver has already been started in setRemoteDescription.\n                self._transceive(transceiver,\n                    params.codecs.length > 0,\n                    false);\n              }\n            });\n          }\n\n          this.localDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-local-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n\n          // If a success callback was provided, emit ICE candidates after it\n          // has been executed. Otherwise, emit callback after the Promise is\n          // resolved.\n          var hasCallback = arguments.length > 1 &&\n            typeof arguments[1] === 'function';\n          if (hasCallback) {\n            var cb = arguments[1];\n            window.setTimeout(function() {\n              cb();\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              self._emitBufferedCandidates();\n            }, 0);\n          }\n          var p = Promise.resolve();\n          p.then(function() {\n            if (!hasCallback) {\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              // Usually candidates will be emitted earlier.\n              window.setTimeout(self._emitBufferedCandidates.bind(self), 500);\n            }\n          });\n          return p;\n        };\n\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n        function(description) {\n          var self = this;\n          var stream = new MediaStream();\n          var receiverList = [];\n          var sections = SDPUtils.splitSections(description.sdp);\n          var sessionpart = sections.shift();\n          var isIceLite = SDPUtils.matchPrefix(sessionpart,\n              'a=ice-lite').length > 0;\n          this.usingBundle = SDPUtils.matchPrefix(sessionpart,\n              'a=group:BUNDLE ').length > 0;\n          sections.forEach(function(mediaSection, sdpMLineIndex) {\n            var lines = SDPUtils.splitLines(mediaSection);\n            var mline = lines[0].substr(2).split(' ');\n            var kind = mline[0];\n            var rejected = mline[1] === '0';\n            var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n\n            var transceiver;\n            var iceGatherer;\n            var iceTransport;\n            var dtlsTransport;\n            var rtpSender;\n            var rtpReceiver;\n            var sendEncodingParameters;\n            var recvEncodingParameters;\n            var localCapabilities;\n\n            var track;\n            // FIXME: ensure the mediaSection has rtcp-mux set.\n            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n            var remoteIceParameters;\n            var remoteDtlsParameters;\n            if (!rejected) {\n              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters.role = 'client';\n            }\n            recvEncodingParameters =\n                SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');\n            if (mid.length) {\n              mid = mid[0].substr(6);\n            } else {\n              mid = SDPUtils.generateIdentifier();\n            }\n\n            var cname;\n            // Gets the first SSRC. Note that with RTX there might be multiple\n            // SSRCs.\n            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n                .map(function(line) {\n                  return SDPUtils.parseSsrcMedia(line);\n                })\n                .filter(function(obj) {\n                  return obj.attribute === 'cname';\n                })[0];\n            if (remoteSsrc) {\n              cname = remoteSsrc.value;\n            }\n\n            var isComplete = SDPUtils.matchPrefix(mediaSection,\n                'a=end-of-candidates').length > 0;\n            var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                .map(function(cand) {\n                  return SDPUtils.parseCandidate(cand);\n                })\n                .filter(function(cand) {\n                  return cand.component === '1';\n                });\n            if (description.type === 'offer' && !rejected) {\n              var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n                iceGatherer: self.transceivers[0].iceGatherer,\n                iceTransport: self.transceivers[0].iceTransport,\n                dtlsTransport: self.transceivers[0].dtlsTransport\n              } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n              if (isComplete) {\n                transports.iceTransport.setRemoteCandidates(cands);\n              }\n\n              localCapabilities = RTCRtpReceiver.getCapabilities(kind);\n              sendEncodingParameters = [{\n                ssrc: (2 * sdpMLineIndex + 2) * 1001\n              }];\n\n              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n\n              track = rtpReceiver.track;\n              receiverList.push([track, rtpReceiver]);\n              // FIXME: not correct when there are multiple streams but that is\n              // not currently supported in this shim.\n              stream.addTrack(track);\n\n              // FIXME: look at direction.\n              if (self.localStreams.length > 0 &&\n                  self.localStreams[0].getTracks().length >= sdpMLineIndex) {\n                // FIXME: actually more complicated, needs to match types etc\n                var localtrack = self.localStreams[0]\n                    .getTracks()[sdpMLineIndex];\n                rtpSender = new RTCRtpSender(localtrack,\n                    transports.dtlsTransport);\n              }\n\n              self.transceivers[sdpMLineIndex] = {\n                iceGatherer: transports.iceGatherer,\n                iceTransport: transports.iceTransport,\n                dtlsTransport: transports.dtlsTransport,\n                localCapabilities: localCapabilities,\n                remoteCapabilities: remoteCapabilities,\n                rtpSender: rtpSender,\n                rtpReceiver: rtpReceiver,\n                kind: kind,\n                mid: mid,\n                cname: cname,\n                sendEncodingParameters: sendEncodingParameters,\n                recvEncodingParameters: recvEncodingParameters\n              };\n              // Start the RTCRtpReceiver now. The RTPSender is started in\n              // setLocalDescription.\n              self._transceive(self.transceivers[sdpMLineIndex],\n                  false,\n                  direction === 'sendrecv' || direction === 'sendonly');\n            } else if (description.type === 'answer' && !rejected) {\n              transceiver = self.transceivers[sdpMLineIndex];\n              iceGatherer = transceiver.iceGatherer;\n              iceTransport = transceiver.iceTransport;\n              dtlsTransport = transceiver.dtlsTransport;\n              rtpSender = transceiver.rtpSender;\n              rtpReceiver = transceiver.rtpReceiver;\n              sendEncodingParameters = transceiver.sendEncodingParameters;\n              localCapabilities = transceiver.localCapabilities;\n\n              self.transceivers[sdpMLineIndex].recvEncodingParameters =\n                  recvEncodingParameters;\n              self.transceivers[sdpMLineIndex].remoteCapabilities =\n                  remoteCapabilities;\n              self.transceivers[sdpMLineIndex].cname = cname;\n\n              if ((isIceLite || isComplete) && cands.length) {\n                iceTransport.setRemoteCandidates(cands);\n              }\n              if (!self.usingBundle || sdpMLineIndex === 0) {\n                iceTransport.start(iceGatherer, remoteIceParameters,\n                    'controlling');\n                dtlsTransport.start(remoteDtlsParameters);\n              }\n\n              self._transceive(transceiver,\n                  direction === 'sendrecv' || direction === 'recvonly',\n                  direction === 'sendrecv' || direction === 'sendonly');\n\n              if (rtpReceiver &&\n                  (direction === 'sendrecv' || direction === 'sendonly')) {\n                track = rtpReceiver.track;\n                receiverList.push([track, rtpReceiver]);\n                stream.addTrack(track);\n              } else {\n                // FIXME: actually the receiver should be created later.\n                delete transceiver.rtpReceiver;\n              }\n            }\n          });\n\n          this.remoteDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-remote-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n          if (stream.getTracks().length) {\n            self.remoteStreams.push(stream);\n            window.setTimeout(function() {\n              var event = new Event('addstream');\n              event.stream = stream;\n              self.dispatchEvent(event);\n              if (self.onaddstream !== null) {\n                window.setTimeout(function() {\n                  self.onaddstream(event);\n                }, 0);\n              }\n\n              receiverList.forEach(function(item) {\n                var track = item[0];\n                var receiver = item[1];\n                var trackEvent = new Event('track');\n                trackEvent.track = track;\n                trackEvent.receiver = receiver;\n                trackEvent.streams = [stream];\n                self.dispatchEvent(event);\n                if (self.ontrack !== null) {\n                  window.setTimeout(function() {\n                    self.ontrack(trackEvent);\n                  }, 0);\n                }\n              });\n            }, 0);\n          }\n          if (arguments.length > 1 && typeof arguments[1] === 'function') {\n            window.setTimeout(arguments[1], 0);\n          }\n          return Promise.resolve();\n        };\n\n    window.RTCPeerConnection.prototype.close = function() {\n      this.transceivers.forEach(function(transceiver) {\n        /* not yet\n        if (transceiver.iceGatherer) {\n          transceiver.iceGatherer.close();\n        }\n        */\n        if (transceiver.iceTransport) {\n          transceiver.iceTransport.stop();\n        }\n        if (transceiver.dtlsTransport) {\n          transceiver.dtlsTransport.stop();\n        }\n        if (transceiver.rtpSender) {\n          transceiver.rtpSender.stop();\n        }\n        if (transceiver.rtpReceiver) {\n          transceiver.rtpReceiver.stop();\n        }\n      });\n      // FIXME: clean up tracks, local streams, remote streams, etc\n      this._updateSignalingState('closed');\n    };\n\n    // Update the signaling state.\n    window.RTCPeerConnection.prototype._updateSignalingState =\n        function(newState) {\n          this.signalingState = newState;\n          var event = new Event('signalingstatechange');\n          this.dispatchEvent(event);\n          if (this.onsignalingstatechange !== null) {\n            this.onsignalingstatechange(event);\n          }\n        };\n\n    // Determine whether to fire the negotiationneeded event.\n    window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =\n        function() {\n          // Fire away (for now).\n          var event = new Event('negotiationneeded');\n          this.dispatchEvent(event);\n          if (this.onnegotiationneeded !== null) {\n            this.onnegotiationneeded(event);\n          }\n        };\n\n    // Update the connection state.\n    window.RTCPeerConnection.prototype._updateConnectionState = function() {\n      var self = this;\n      var newState;\n      var states = {\n        'new': 0,\n        closed: 0,\n        connecting: 0,\n        checking: 0,\n        connected: 0,\n        completed: 0,\n        failed: 0\n      };\n      this.transceivers.forEach(function(transceiver) {\n        states[transceiver.iceTransport.state]++;\n        states[transceiver.dtlsTransport.state]++;\n      });\n      // ICETransport.completed and connected are the same for this purpose.\n      states.connected += states.completed;\n\n      newState = 'new';\n      if (states.failed > 0) {\n        newState = 'failed';\n      } else if (states.connecting > 0 || states.checking > 0) {\n        newState = 'connecting';\n      } else if (states.disconnected > 0) {\n        newState = 'disconnected';\n      } else if (states.new > 0) {\n        newState = 'new';\n      } else if (states.connected > 0 || states.completed > 0) {\n        newState = 'connected';\n      }\n\n      if (newState !== self.iceConnectionState) {\n        self.iceConnectionState = newState;\n        var event = new Event('iceconnectionstatechange');\n        this.dispatchEvent(event);\n        if (this.oniceconnectionstatechange !== null) {\n          this.oniceconnectionstatechange(event);\n        }\n      }\n    };\n\n    window.RTCPeerConnection.prototype.createOffer = function() {\n      var self = this;\n      if (this._pendingOffer) {\n        throw new Error('createOffer called while there is a pending offer.');\n      }\n      var offerOptions;\n      if (arguments.length === 1 && typeof arguments[0] !== 'function') {\n        offerOptions = arguments[0];\n      } else if (arguments.length === 3) {\n        offerOptions = arguments[2];\n      }\n\n      var tracks = [];\n      var numAudioTracks = 0;\n      var numVideoTracks = 0;\n      // Default to sendrecv.\n      if (this.localStreams.length) {\n        numAudioTracks = this.localStreams[0].getAudioTracks().length;\n        numVideoTracks = this.localStreams[0].getVideoTracks().length;\n      }\n      // Determine number of audio and video tracks we need to send/recv.\n      if (offerOptions) {\n        // Reject Chrome legacy constraints.\n        if (offerOptions.mandatory || offerOptions.optional) {\n          throw new TypeError(\n              'Legacy mandatory/optional constraints not supported.');\n        }\n        if (offerOptions.offerToReceiveAudio !== undefined) {\n          numAudioTracks = offerOptions.offerToReceiveAudio;\n        }\n        if (offerOptions.offerToReceiveVideo !== undefined) {\n          numVideoTracks = offerOptions.offerToReceiveVideo;\n        }\n      }\n      if (this.localStreams.length) {\n        // Push local streams.\n        this.localStreams[0].getTracks().forEach(function(track) {\n          tracks.push({\n            kind: track.kind,\n            track: track,\n            wantReceive: track.kind === 'audio' ?\n                numAudioTracks > 0 : numVideoTracks > 0\n          });\n          if (track.kind === 'audio') {\n            numAudioTracks--;\n          } else if (track.kind === 'video') {\n            numVideoTracks--;\n          }\n        });\n      }\n      // Create M-lines for recvonly streams.\n      while (numAudioTracks > 0 || numVideoTracks > 0) {\n        if (numAudioTracks > 0) {\n          tracks.push({\n            kind: 'audio',\n            wantReceive: true\n          });\n          numAudioTracks--;\n        }\n        if (numVideoTracks > 0) {\n          tracks.push({\n            kind: 'video',\n            wantReceive: true\n          });\n          numVideoTracks--;\n        }\n      }\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      var transceivers = [];\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        // For each track, create an ice gatherer, ice transport,\n        // dtls transport, potentially rtpsender and rtpreceiver.\n        var track = mline.track;\n        var kind = mline.kind;\n        var mid = SDPUtils.generateIdentifier();\n\n        var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n          iceGatherer: transceivers[0].iceGatherer,\n          iceTransport: transceivers[0].iceTransport,\n          dtlsTransport: transceivers[0].dtlsTransport\n        } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n        var localCapabilities = RTCRtpSender.getCapabilities(kind);\n        var rtpSender;\n        var rtpReceiver;\n\n        // generate an ssrc now, to be used later in rtpSender.send\n        var sendEncodingParameters = [{\n          ssrc: (2 * sdpMLineIndex + 1) * 1001\n        }];\n        if (track) {\n          rtpSender = new RTCRtpSender(track, transports.dtlsTransport);\n        }\n\n        if (mline.wantReceive) {\n          rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n        }\n\n        transceivers[sdpMLineIndex] = {\n          iceGatherer: transports.iceGatherer,\n          iceTransport: transports.iceTransport,\n          dtlsTransport: transports.dtlsTransport,\n          localCapabilities: localCapabilities,\n          remoteCapabilities: null,\n          rtpSender: rtpSender,\n          rtpReceiver: rtpReceiver,\n          kind: kind,\n          mid: mid,\n          sendEncodingParameters: sendEncodingParameters,\n          recvEncodingParameters: null\n        };\n      });\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        var transceiver = transceivers[sdpMLineIndex];\n        sdp += SDPUtils.writeMediaSection(transceiver,\n            transceiver.localCapabilities, 'offer', self.localStreams[0]);\n      });\n\n      this._pendingOffer = transceivers;\n      var desc = new RTCSessionDescription({\n        type: 'offer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.createAnswer = function() {\n      var self = this;\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      this.transceivers.forEach(function(transceiver) {\n        // Calculate intersection of capabilities.\n        var commonCapabilities = self._getCommonCapabilities(\n            transceiver.localCapabilities,\n            transceiver.remoteCapabilities);\n\n        sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,\n            'answer', self.localStreams[0]);\n      });\n\n      var desc = new RTCSessionDescription({\n        type: 'answer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n      if (candidate === null) {\n        this.transceivers.forEach(function(transceiver) {\n          transceiver.iceTransport.addRemoteCandidate({});\n        });\n      } else {\n        var mLineIndex = candidate.sdpMLineIndex;\n        if (candidate.sdpMid) {\n          for (var i = 0; i < this.transceivers.length; i++) {\n            if (this.transceivers[i].mid === candidate.sdpMid) {\n              mLineIndex = i;\n              break;\n            }\n          }\n        }\n        var transceiver = this.transceivers[mLineIndex];\n        if (transceiver) {\n          var cand = Object.keys(candidate.candidate).length > 0 ?\n              SDPUtils.parseCandidate(candidate.candidate) : {};\n          // Ignore Chrome's invalid candidates since Edge does not like them.\n          if (cand.protocol === 'tcp' && cand.port === 0) {\n            return;\n          }\n          // Ignore RTCP candidates, we assume RTCP-MUX.\n          if (cand.component !== '1') {\n            return;\n          }\n          // A dirty hack to make samples work.\n          if (cand.type === 'endOfCandidates') {\n            cand = {};\n          }\n          transceiver.iceTransport.addRemoteCandidate(cand);\n\n          // update the remoteDescription.\n          var sections = SDPUtils.splitSections(this.remoteDescription.sdp);\n          sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()\n              : 'a=end-of-candidates') + '\\r\\n';\n          this.remoteDescription.sdp = sections.join('');\n        }\n      }\n      if (arguments.length > 1 && typeof arguments[1] === 'function') {\n        window.setTimeout(arguments[1], 0);\n      }\n      return Promise.resolve();\n    };\n\n    window.RTCPeerConnection.prototype.getStats = function() {\n      var promises = [];\n      this.transceivers.forEach(function(transceiver) {\n        ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n            'dtlsTransport'].forEach(function(method) {\n              if (transceiver[method]) {\n                promises.push(transceiver[method].getStats());\n              }\n            });\n      });\n      var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&\n          arguments[1];\n      return new Promise(function(resolve) {\n        // shim getStats with maplike support\n        var results = new Map();\n        Promise.all(promises).then(function(res) {\n          res.forEach(function(result) {\n            Object.keys(result).forEach(function(id) {\n              results.set(id, result[id]);\n              results[id] = result[id];\n            });\n          });\n          if (cb) {\n            window.setTimeout(cb, 0, results);\n          }\n          resolve(results);\n        });\n      });\n    };\n  },\n\n  // Attach a media stream to an element.\n  attachMediaStream: function(element, stream) {\n    logging('DEPRECATED, attachMediaStream will soon be removed.');\n    element.srcObject = stream;\n  },\n\n  reattachMediaStream: function(to, from) {\n    logging('DEPRECATED, reattachMediaStream will soon be removed.');\n    to.srcObject = from.srcObject;\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimPeerConnection: edgeShim.shimPeerConnection,\n  shimGetUserMedia: __webpack_require__(453),\n  attachMediaStream: edgeShim.attachMediaStream,\n  reattachMediaStream: edgeShim.reattachMediaStream\n};\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDUxLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9sb2NhbG1lZGlhL25vZGVfbW9kdWxlcy93ZWJydGMtYWRhcHRlci9zcmMvanMvZWRnZS9lZGdlX3NoaW0uanMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFdlYlJUQyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG4gLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbnZhciBTRFBVdGlscyA9IHJlcXVpcmUoJ3NkcCcpO1xudmFyIGxvZ2dpbmcgPSByZXF1aXJlKCcuLi91dGlscycpLmxvZztcblxudmFyIGVkZ2VTaGltID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGZ1bmN0aW9uKCkge1xuICAgIGlmICh3aW5kb3cuUlRDSWNlR2F0aGVyZXIpIHtcbiAgICAgIC8vIE9SVEMgZGVmaW5lcyBhbiBSVENJY2VDYW5kaWRhdGUgb2JqZWN0IGJ1dCBubyBjb25zdHJ1Y3Rvci5cbiAgICAgIC8vIE5vdCBpbXBsZW1lbnRlZCBpbiBFZGdlLlxuICAgICAgaWYgKCF3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlKSB7XG4gICAgICAgIHdpbmRvdy5SVENJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbihhcmdzKSB7XG4gICAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICAvLyBPUlRDIGRvZXMgbm90IGhhdmUgYSBzZXNzaW9uIGRlc2NyaXB0aW9uIG9iamVjdCBidXRcbiAgICAgIC8vIG90aGVyIGJyb3dzZXJzIChpLmUuIENocm9tZSkgdGhhdCB3aWxsIHN1cHBvcnQgYm90aCBQQyBhbmQgT1JUQ1xuICAgICAgLy8gaW4gdGhlIGZ1dHVyZSBtaWdodCBoYXZlIHRoaXMgZGVmaW5lZCBhbHJlYWR5LlxuICAgICAgaWYgKCF3aW5kb3cuUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKSB7XG4gICAgICAgIHdpbmRvdy5SVENTZXNzaW9uRGVzY3JpcHRpb24gPSBmdW5jdGlvbihhcmdzKSB7XG4gICAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfVxuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uID0gZnVuY3Rpb24oY29uZmlnKSB7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAgIHZhciBfZXZlbnRUYXJnZXQgPSBkb2N1bWVudC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCk7XG4gICAgICBbJ2FkZEV2ZW50TGlzdGVuZXInLCAncmVtb3ZlRXZlbnRMaXN0ZW5lcicsICdkaXNwYXRjaEV2ZW50J11cbiAgICAgICAgICAuZm9yRWFjaChmdW5jdGlvbihtZXRob2QpIHtcbiAgICAgICAgICAgIHNlbGZbbWV0aG9kXSA9IF9ldmVudFRhcmdldFttZXRob2RdLmJpbmQoX2V2ZW50VGFyZ2V0KTtcbiAgICAgICAgICB9KTtcblxuICAgICAgdGhpcy5vbmljZWNhbmRpZGF0ZSA9IG51bGw7XG4gICAgICB0aGlzLm9uYWRkc3RyZWFtID0gbnVsbDtcbiAgICAgIHRoaXMub250cmFjayA9IG51bGw7XG4gICAgICB0aGlzLm9ucmVtb3Zlc3RyZWFtID0gbnVsbDtcbiAgICAgIHRoaXMub25zaWduYWxpbmdzdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgICB0aGlzLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlID0gbnVsbDtcbiAgICAgIHRoaXMub25uZWdvdGlhdGlvbm5lZWRlZCA9IG51bGw7XG4gICAgICB0aGlzLm9uZGF0YWNoYW5uZWwgPSBudWxsO1xuXG4gICAgICB0aGlzLmxvY2FsU3RyZWFtcyA9IFtdO1xuICAgICAgdGhpcy5yZW1vdGVTdHJlYW1zID0gW107XG4gICAgICB0aGlzLmdldExvY2FsU3RyZWFtcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gc2VsZi5sb2NhbFN0cmVhbXM7XG4gICAgICB9O1xuICAgICAgdGhpcy5nZXRSZW1vdGVTdHJlYW1zID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBzZWxmLnJlbW90ZVN0cmVhbXM7XG4gICAgICB9O1xuXG4gICAgICB0aGlzLmxvY2FsRGVzY3JpcHRpb24gPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJycsXG4gICAgICAgIHNkcDogJydcbiAgICAgIH0pO1xuICAgICAgdGhpcy5yZW1vdGVEZXNjcmlwdGlvbiA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiAnJyxcbiAgICAgICAgc2RwOiAnJ1xuICAgICAgfSk7XG4gICAgICB0aGlzLnNpZ25hbGluZ1N0YXRlID0gJ3N0YWJsZSc7XG4gICAgICB0aGlzLmljZUNvbm5lY3Rpb25TdGF0ZSA9ICduZXcnO1xuICAgICAgdGhpcy5pY2VHYXRoZXJpbmdTdGF0ZSA9ICduZXcnO1xuXG4gICAgICB0aGlzLmljZU9wdGlvbnMgPSB7XG4gICAgICAgIGdhdGhlclBvbGljeTogJ2FsbCcsXG4gICAgICAgIGljZVNlcnZlcnM6IFtdXG4gICAgICB9O1xuICAgICAgaWYgKGNvbmZpZyAmJiBjb25maWcuaWNlVHJhbnNwb3J0UG9saWN5KSB7XG4gICAgICAgIHN3aXRjaCAoY29uZmlnLmljZVRyYW5zcG9ydFBvbGljeSkge1xuICAgICAgICAgIGNhc2UgJ2FsbCc6XG4gICAgICAgICAgY2FzZSAncmVsYXknOlxuICAgICAgICAgICAgdGhpcy5pY2VPcHRpb25zLmdhdGhlclBvbGljeSA9IGNvbmZpZy5pY2VUcmFuc3BvcnRQb2xpY3k7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdub25lJzpcbiAgICAgICAgICAgIC8vIEZJWE1FOiByZW1vdmUgb25jZSBpbXBsZW1lbnRhdGlvbiBhbmQgc3BlYyBoYXZlIGFkZGVkIHRoaXMuXG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdpY2VUcmFuc3BvcnRQb2xpY3kgXCJub25lXCIgbm90IHN1cHBvcnRlZCcpO1xuICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAvLyBkb24ndCBzZXQgaWNlVHJhbnNwb3J0UG9saWN5LlxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRoaXMudXNpbmdCdW5kbGUgPSBjb25maWcgJiYgY29uZmlnLmJ1bmRsZVBvbGljeSA9PT0gJ21heC1idW5kbGUnO1xuXG4gICAgICBpZiAoY29uZmlnICYmIGNvbmZpZy5pY2VTZXJ2ZXJzKSB7XG4gICAgICAgIC8vIEVkZ2UgZG9lcyBub3QgbGlrZVxuICAgICAgICAvLyAxKSBzdHVuOlxuICAgICAgICAvLyAyKSB0dXJuOiB0aGF0IGRvZXMgbm90IGhhdmUgYWxsIG9mIHR1cm46aG9zdDpwb3J0P3RyYW5zcG9ydD11ZHBcbiAgICAgICAgdmFyIGljZVNlcnZlcnMgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGNvbmZpZy5pY2VTZXJ2ZXJzKSk7XG4gICAgICAgIHRoaXMuaWNlT3B0aW9ucy5pY2VTZXJ2ZXJzID0gaWNlU2VydmVycy5maWx0ZXIoZnVuY3Rpb24oc2VydmVyKSB7XG4gICAgICAgICAgaWYgKHNlcnZlciAmJiBzZXJ2ZXIudXJscykge1xuICAgICAgICAgICAgdmFyIHVybHMgPSBzZXJ2ZXIudXJscztcbiAgICAgICAgICAgIGlmICh0eXBlb2YgdXJscyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgdXJscyA9IFt1cmxzXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHVybHMgPSB1cmxzLmZpbHRlcihmdW5jdGlvbih1cmwpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHVybC5pbmRleE9mKCd0dXJuOicpID09PSAwICYmXG4gICAgICAgICAgICAgICAgICB1cmwuaW5kZXhPZigndHJhbnNwb3J0PXVkcCcpICE9PSAtMTtcbiAgICAgICAgICAgIH0pWzBdO1xuICAgICAgICAgICAgcmV0dXJuICEhdXJscztcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgLy8gcGVyLXRyYWNrIGljZUdhdGhlcnMsIGljZVRyYW5zcG9ydHMsIGR0bHNUcmFuc3BvcnRzLCBydHBTZW5kZXJzLCAuLi5cbiAgICAgIC8vIGV2ZXJ5dGhpbmcgdGhhdCBpcyBuZWVkZWQgdG8gZGVzY3JpYmUgYSBTRFAgbS1saW5lLlxuICAgICAgdGhpcy50cmFuc2NlaXZlcnMgPSBbXTtcblxuICAgICAgLy8gc2luY2UgdGhlIGljZUdhdGhlcmVyIGlzIGN1cnJlbnRseSBjcmVhdGVkIGluIGNyZWF0ZU9mZmVyIGJ1dCB3ZVxuICAgICAgLy8gbXVzdCBub3QgZW1pdCBjYW5kaWRhdGVzIHVudGlsIGFmdGVyIHNldExvY2FsRGVzY3JpcHRpb24gd2UgYnVmZmVyXG4gICAgICAvLyB0aGVtIGluIHRoaXMgYXJyYXkuXG4gICAgICB0aGlzLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIgPSBbXTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgIC8vIEZJWE1FOiBuZWVkIHRvIGFwcGx5IGljZSBjYW5kaWRhdGVzIGluIGEgd2F5IHdoaWNoIGlzIGFzeW5jIGJ1dFxuICAgICAgLy8gaW4tb3JkZXJcbiAgICAgIHRoaXMuX2xvY2FsSWNlQ2FuZGlkYXRlc0J1ZmZlci5mb3JFYWNoKGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICAgIHZhciBlbmQgPSAhZXZlbnQuY2FuZGlkYXRlIHx8IE9iamVjdC5rZXlzKGV2ZW50LmNhbmRpZGF0ZSkubGVuZ3RoID09PSAwO1xuICAgICAgICBpZiAoZW5kKSB7XG4gICAgICAgICAgZm9yICh2YXIgaiA9IDE7IGogPCBzZWN0aW9ucy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgaWYgKHNlY3Rpb25zW2pdLmluZGV4T2YoJ1xcclxcbmE9ZW5kLW9mLWNhbmRpZGF0ZXNcXHJcXG4nKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgc2VjdGlvbnNbal0gKz0gJ2E9ZW5kLW9mLWNhbmRpZGF0ZXNcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlLmluZGV4T2YoJ3R5cCBlbmRPZkNhbmRpZGF0ZXMnKVxuICAgICAgICAgICAgPT09IC0xKSB7XG4gICAgICAgICAgc2VjdGlvbnNbZXZlbnQuY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXggKyAxXSArPVxuICAgICAgICAgICAgICAnYT0nICsgZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZSArICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgICAgIHNlbGYubG9jYWxEZXNjcmlwdGlvbi5zZHAgPSBzZWN0aW9ucy5qb2luKCcnKTtcbiAgICAgICAgc2VsZi5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgaWYgKHNlbGYub25pY2VjYW5kaWRhdGUgIT09IG51bGwpIHtcbiAgICAgICAgICBzZWxmLm9uaWNlY2FuZGlkYXRlKGV2ZW50KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWV2ZW50LmNhbmRpZGF0ZSAmJiBzZWxmLmljZUdhdGhlcmluZ1N0YXRlICE9PSAnY29tcGxldGUnKSB7XG4gICAgICAgICAgdmFyIGNvbXBsZXRlID0gc2VsZi50cmFuc2NlaXZlcnMuZXZlcnkoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICAgIHJldHVybiB0cmFuc2NlaXZlci5pY2VHYXRoZXJlciAmJlxuICAgICAgICAgICAgICAgIHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyLnN0YXRlID09PSAnY29tcGxldGVkJztcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpZiAoY29tcGxldGUpIHtcbiAgICAgICAgICAgIHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPSAnY29tcGxldGUnO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICB0aGlzLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIgPSBbXTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgIC8vIENsb25lIGlzIG5lY2Vzc2FyeSBmb3IgbG9jYWwgZGVtb3MgbW9zdGx5LCBhdHRhY2hpbmcgZGlyZWN0bHlcbiAgICAgIC8vIHRvIHR3byBkaWZmZXJlbnQgc2VuZGVycyBkb2VzIG5vdCB3b3JrIChidWlsZCAxMDU0NykuXG4gICAgICB0aGlzLmxvY2FsU3RyZWFtcy5wdXNoKHN0cmVhbS5jbG9uZSgpKTtcbiAgICAgIHRoaXMuX21heWJlRmlyZU5lZ290aWF0aW9uTmVlZGVkKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID0gZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICB2YXIgaWR4ID0gdGhpcy5sb2NhbFN0cmVhbXMuaW5kZXhPZihzdHJlYW0pO1xuICAgICAgaWYgKGlkeCA+IC0xKSB7XG4gICAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnNwbGljZShpZHgsIDEpO1xuICAgICAgICB0aGlzLl9tYXliZUZpcmVOZWdvdGlhdGlvbk5lZWRlZCgpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFNlbmRlcnMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiB0aGlzLnRyYW5zY2VpdmVycy5maWx0ZXIoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuICEhdHJhbnNjZWl2ZXIucnRwU2VuZGVyO1xuICAgICAgfSlcbiAgICAgIC5tYXAoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuIHRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFJlY2VpdmVycyA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMudHJhbnNjZWl2ZXJzLmZpbHRlcihmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICByZXR1cm4gISF0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgIH0pXG4gICAgICAubWFwKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIHJldHVybiB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvLyBEZXRlcm1pbmVzIHRoZSBpbnRlcnNlY3Rpb24gb2YgbG9jYWwgYW5kIHJlbW90ZSBjYXBhYmlsaXRpZXMuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzID1cbiAgICAgICAgZnVuY3Rpb24obG9jYWxDYXBhYmlsaXRpZXMsIHJlbW90ZUNhcGFiaWxpdGllcykge1xuICAgICAgICAgIHZhciBjb21tb25DYXBhYmlsaXRpZXMgPSB7XG4gICAgICAgICAgICBjb2RlY3M6IFtdLFxuICAgICAgICAgICAgaGVhZGVyRXh0ZW5zaW9uczogW10sXG4gICAgICAgICAgICBmZWNNZWNoYW5pc21zOiBbXVxuICAgICAgICAgIH07XG4gICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzLmZvckVhY2goZnVuY3Rpb24obENvZGVjKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJlbW90ZUNhcGFiaWxpdGllcy5jb2RlY3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgdmFyIHJDb2RlYyA9IHJlbW90ZUNhcGFiaWxpdGllcy5jb2RlY3NbaV07XG4gICAgICAgICAgICAgIGlmIChsQ29kZWMubmFtZS50b0xvd2VyQ2FzZSgpID09PSByQ29kZWMubmFtZS50b0xvd2VyQ2FzZSgpICYmXG4gICAgICAgICAgICAgICAgICBsQ29kZWMuY2xvY2tSYXRlID09PSByQ29kZWMuY2xvY2tSYXRlICYmXG4gICAgICAgICAgICAgICAgICBsQ29kZWMubnVtQ2hhbm5lbHMgPT09IHJDb2RlYy5udW1DaGFubmVscykge1xuICAgICAgICAgICAgICAgIC8vIHB1c2ggckNvZGVjIHNvIHdlIHJlcGx5IHdpdGggb2ZmZXJlciBwYXlsb2FkIHR5cGVcbiAgICAgICAgICAgICAgICBjb21tb25DYXBhYmlsaXRpZXMuY29kZWNzLnB1c2gockNvZGVjKTtcblxuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBhbHNvIG5lZWQgdG8gZGV0ZXJtaW5lIGludGVyc2VjdGlvbiBiZXR3ZWVuXG4gICAgICAgICAgICAgICAgLy8gLnJ0Y3BGZWVkYmFjayBhbmQgLnBhcmFtZXRlcnNcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9uc1xuICAgICAgICAgICAgICAuZm9yRWFjaChmdW5jdGlvbihsSGVhZGVyRXh0ZW5zaW9uKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZW1vdGVDYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9ucy5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICBpKyspIHtcbiAgICAgICAgICAgICAgICAgIHZhciBySGVhZGVyRXh0ZW5zaW9uID0gcmVtb3RlQ2FwYWJpbGl0aWVzLmhlYWRlckV4dGVuc2lvbnNbaV07XG4gICAgICAgICAgICAgICAgICBpZiAobEhlYWRlckV4dGVuc2lvbi51cmkgPT09IHJIZWFkZXJFeHRlbnNpb24udXJpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbW1vbkNhcGFiaWxpdGllcy5oZWFkZXJFeHRlbnNpb25zLnB1c2gockhlYWRlckV4dGVuc2lvbik7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAvLyBGSVhNRTogZmVjTWVjaGFuaXNtc1xuICAgICAgICAgIHJldHVybiBjb21tb25DYXBhYmlsaXRpZXM7XG4gICAgICAgIH07XG5cbiAgICAvLyBDcmVhdGUgSUNFIGdhdGhlcmVyLCBJQ0UgdHJhbnNwb3J0IGFuZCBEVExTIHRyYW5zcG9ydC5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyA9XG4gICAgICAgIGZ1bmN0aW9uKG1pZCwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICB2YXIgaWNlR2F0aGVyZXIgPSBuZXcgUlRDSWNlR2F0aGVyZXIoc2VsZi5pY2VPcHRpb25zKTtcbiAgICAgICAgICB2YXIgaWNlVHJhbnNwb3J0ID0gbmV3IFJUQ0ljZVRyYW5zcG9ydChpY2VHYXRoZXJlcik7XG4gICAgICAgICAgaWNlR2F0aGVyZXIub25sb2NhbGNhbmRpZGF0ZSA9IGZ1bmN0aW9uKGV2dCkge1xuICAgICAgICAgICAgdmFyIGV2ZW50ID0gbmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKTtcbiAgICAgICAgICAgIGV2ZW50LmNhbmRpZGF0ZSA9IHtzZHBNaWQ6IG1pZCwgc2RwTUxpbmVJbmRleDogc2RwTUxpbmVJbmRleH07XG5cbiAgICAgICAgICAgIHZhciBjYW5kID0gZXZ0LmNhbmRpZGF0ZTtcbiAgICAgICAgICAgIHZhciBlbmQgPSAhY2FuZCB8fCBPYmplY3Qua2V5cyhjYW5kKS5sZW5ndGggPT09IDA7XG4gICAgICAgICAgICAvLyBFZGdlIGVtaXRzIGFuIGVtcHR5IG9iamVjdCBmb3IgUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGXigKVcbiAgICAgICAgICAgIGlmIChlbmQpIHtcbiAgICAgICAgICAgICAgLy8gcG9seWZpbGwgc2luY2UgUlRDSWNlR2F0aGVyZXIuc3RhdGUgaXMgbm90IGltcGxlbWVudGVkIGluXG4gICAgICAgICAgICAgIC8vIEVkZ2UgMTA1NDcgeWV0LlxuICAgICAgICAgICAgICBpZiAoaWNlR2F0aGVyZXIuc3RhdGUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyLnN0YXRlID0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAvLyBFbWl0IGEgY2FuZGlkYXRlIHdpdGggdHlwZSBlbmRPZkNhbmRpZGF0ZXMgdG8gbWFrZSB0aGUgc2FtcGxlc1xuICAgICAgICAgICAgICAvLyB3b3JrLiBFZGdlIHJlcXVpcmVzIGFkZEljZUNhbmRpZGF0ZSB3aXRoIHRoaXMgZW1wdHkgY2FuZGlkYXRlXG4gICAgICAgICAgICAgIC8vIHRvIHN0YXJ0IGNoZWNraW5nLiBUaGUgcmVhbCBzb2x1dGlvbiBpcyB0byBzaWduYWxcbiAgICAgICAgICAgICAgLy8gZW5kLW9mLWNhbmRpZGF0ZXMgdG8gdGhlIG90aGVyIHNpZGUgd2hlbiBnZXR0aW5nIHRoZSBudWxsXG4gICAgICAgICAgICAgIC8vIGNhbmRpZGF0ZSBidXQgc29tZSBhcHBzIChsaWtlIHRoZSBzYW1wbGVzKSBkb24ndCBkbyB0aGF0LlxuICAgICAgICAgICAgICBldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlID1cbiAgICAgICAgICAgICAgICAgICdjYW5kaWRhdGU6MSAxIHVkcCAxIDAuMC4wLjAgOSB0eXAgZW5kT2ZDYW5kaWRhdGVzJztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIFJUQ0ljZUNhbmRpZGF0ZSBkb2Vzbid0IGhhdmUgYSBjb21wb25lbnQsIG5lZWRzIHRvIGJlIGFkZGVkXG4gICAgICAgICAgICAgIGNhbmQuY29tcG9uZW50ID0gaWNlVHJhbnNwb3J0LmNvbXBvbmVudCA9PT0gJ1JUQ1AnID8gMiA6IDE7XG4gICAgICAgICAgICAgIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgPSBTRFBVdGlscy53cml0ZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gdXBkYXRlIGxvY2FsIGRlc2NyaXB0aW9uLlxuICAgICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICAgIGlmIChldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlLmluZGV4T2YoJ3R5cCBlbmRPZkNhbmRpZGF0ZXMnKVxuICAgICAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgICAgICBzZWN0aW9uc1tldmVudC5jYW5kaWRhdGUuc2RwTUxpbmVJbmRleCArIDFdICs9XG4gICAgICAgICAgICAgICAgICAnYT0nICsgZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc2VjdGlvbnNbZXZlbnQuY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXggKyAxXSArPVxuICAgICAgICAgICAgICAgICAgJ2E9ZW5kLW9mLWNhbmRpZGF0ZXNcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCA9IHNlY3Rpb25zLmpvaW4oJycpO1xuXG4gICAgICAgICAgICB2YXIgY29tcGxldGUgPSBzZWxmLnRyYW5zY2VpdmVycy5ldmVyeShmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICAgIHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyLnN0YXRlID09PSAnY29tcGxldGVkJztcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBFbWl0IGNhbmRpZGF0ZSBpZiBsb2NhbERlc2NyaXB0aW9uIGlzIHNldC5cbiAgICAgICAgICAgIC8vIEFsc28gZW1pdHMgbnVsbCBjYW5kaWRhdGUgd2hlbiBhbGwgZ2F0aGVyZXJzIGFyZSBjb21wbGV0ZS5cbiAgICAgICAgICAgIHN3aXRjaCAoc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSkge1xuICAgICAgICAgICAgICBjYXNlICduZXcnOlxuICAgICAgICAgICAgICAgIHNlbGYuX2xvY2FsSWNlQ2FuZGlkYXRlc0J1ZmZlci5wdXNoKGV2ZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZW5kICYmIGNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIucHVzaChcbiAgICAgICAgICAgICAgICAgICAgICBuZXcgRXZlbnQoJ2ljZWNhbmRpZGF0ZScpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgJ2dhdGhlcmluZyc6XG4gICAgICAgICAgICAgICAgc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcygpO1xuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYub25pY2VjYW5kaWRhdGUgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUoZXZlbnQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoY29tcGxldGUpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2ljZWNhbmRpZGF0ZScpKTtcbiAgICAgICAgICAgICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUobmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgJ2NvbXBsZXRlJzpcbiAgICAgICAgICAgICAgICAvLyBzaG91bGQgbm90IGhhcHBlbi4uLiBjdXJyZW50bHkhXG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGRlZmF1bHQ6IC8vIG5vLW9wLlxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH07XG4gICAgICAgICAgaWNlVHJhbnNwb3J0Lm9uaWNlc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHNlbGYuX3VwZGF0ZUNvbm5lY3Rpb25TdGF0ZSgpO1xuICAgICAgICAgIH07XG5cbiAgICAgICAgICB2YXIgZHRsc1RyYW5zcG9ydCA9IG5ldyBSVENEdGxzVHJhbnNwb3J0KGljZVRyYW5zcG9ydCk7XG4gICAgICAgICAgZHRsc1RyYW5zcG9ydC5vbmR0bHNzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgc2VsZi5fdXBkYXRlQ29ubmVjdGlvblN0YXRlKCk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICBkdGxzVHJhbnNwb3J0Lm9uZXJyb3IgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIC8vIG9uZXJyb3IgZG9lcyBub3Qgc2V0IHN0YXRlIHRvIGZhaWxlZCBieSBpdHNlbGYuXG4gICAgICAgICAgICBkdGxzVHJhbnNwb3J0LnN0YXRlID0gJ2ZhaWxlZCc7XG4gICAgICAgICAgICBzZWxmLl91cGRhdGVDb25uZWN0aW9uU3RhdGUoKTtcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGljZUdhdGhlcmVyOiBpY2VHYXRoZXJlcixcbiAgICAgICAgICAgIGljZVRyYW5zcG9ydDogaWNlVHJhbnNwb3J0LFxuICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogZHRsc1RyYW5zcG9ydFxuICAgICAgICAgIH07XG4gICAgICAgIH07XG5cbiAgICAvLyBTdGFydCB0aGUgUlRQIFNlbmRlciBhbmQgUmVjZWl2ZXIgZm9yIGEgdHJhbnNjZWl2ZXIuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdHJhbnNjZWl2ZSA9IGZ1bmN0aW9uKHRyYW5zY2VpdmVyLFxuICAgICAgICBzZW5kLCByZWN2KSB7XG4gICAgICB2YXIgcGFyYW1zID0gdGhpcy5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgIHRyYW5zY2VpdmVyLnJlbW90ZUNhcGFiaWxpdGllcyk7XG4gICAgICBpZiAoc2VuZCAmJiB0cmFuc2NlaXZlci5ydHBTZW5kZXIpIHtcbiAgICAgICAgcGFyYW1zLmVuY29kaW5ncyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgIHBhcmFtcy5ydGNwID0ge1xuICAgICAgICAgIGNuYW1lOiBTRFBVdGlscy5sb2NhbENOYW1lXG4gICAgICAgIH07XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzLmxlbmd0aCkge1xuICAgICAgICAgIHBhcmFtcy5ydGNwLnNzcmMgPSB0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmM7XG4gICAgICAgIH1cbiAgICAgICAgdHJhbnNjZWl2ZXIucnRwU2VuZGVyLnNlbmQocGFyYW1zKTtcbiAgICAgIH1cbiAgICAgIGlmIChyZWN2ICYmIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyKSB7XG4gICAgICAgIHBhcmFtcy5lbmNvZGluZ3MgPSB0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICBwYXJhbXMucnRjcCA9IHtcbiAgICAgICAgICBjbmFtZTogdHJhbnNjZWl2ZXIuY25hbWVcbiAgICAgICAgfTtcbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnMubGVuZ3RoKSB7XG4gICAgICAgICAgcGFyYW1zLnJ0Y3Auc3NyYyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnNbMF0uc3NyYztcbiAgICAgICAgfVxuICAgICAgICB0cmFuc2NlaXZlci5ydHBSZWNlaXZlci5yZWNlaXZlKHBhcmFtcyk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0TG9jYWxEZXNjcmlwdGlvbiA9XG4gICAgICAgIGZ1bmN0aW9uKGRlc2NyaXB0aW9uKSB7XG4gICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgIHZhciBzZWN0aW9ucztcbiAgICAgICAgICB2YXIgc2Vzc2lvbnBhcnQ7XG4gICAgICAgICAgaWYgKGRlc2NyaXB0aW9uLnR5cGUgPT09ICdvZmZlcicpIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiBXaGF0IHdhcyB0aGUgcHVycG9zZSBvZiB0aGlzIGVtcHR5IGlmIHN0YXRlbWVudD9cbiAgICAgICAgICAgIC8vIGlmICghdGhpcy5fcGVuZGluZ09mZmVyKSB7XG4gICAgICAgICAgICAvLyB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKHRoaXMuX3BlbmRpbmdPZmZlcikge1xuICAgICAgICAgICAgICAvLyBWRVJZIGxpbWl0ZWQgc3VwcG9ydCBmb3IgU0RQIG11bmdpbmcuIExpbWl0ZWQgdG86XG4gICAgICAgICAgICAgIC8vICogY2hhbmdpbmcgdGhlIG9yZGVyIG9mIGNvZGVjc1xuICAgICAgICAgICAgICBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoZGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICAgICAgc2Vzc2lvbnBhcnQgPSBzZWN0aW9ucy5zaGlmdCgpO1xuICAgICAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgICAgIHZhciBjYXBzID0gU0RQVXRpbHMucGFyc2VSdHBQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbik7XG4gICAgICAgICAgICAgICAgc2VsZi5fcGVuZGluZ09mZmVyW3NkcE1MaW5lSW5kZXhdLmxvY2FsQ2FwYWJpbGl0aWVzID0gY2FwcztcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIHRoaXMudHJhbnNjZWl2ZXJzID0gdGhpcy5fcGVuZGluZ09mZmVyO1xuICAgICAgICAgICAgICBkZWxldGUgdGhpcy5fcGVuZGluZ09mZmVyO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ2Fuc3dlcicpIHtcbiAgICAgICAgICAgIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhzZWxmLnJlbW90ZURlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgICAgICBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgICB2YXIgaXNJY2VMaXRlID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgoc2Vzc2lvbnBhcnQsXG4gICAgICAgICAgICAgICAgJ2E9aWNlLWxpdGUnKS5sZW5ndGggPiAwO1xuICAgICAgICAgICAgc2VjdGlvbnMuZm9yRWFjaChmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgICAgICAgdmFyIHRyYW5zY2VpdmVyID0gc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF07XG4gICAgICAgICAgICAgIHZhciBpY2VHYXRoZXJlciA9IHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyO1xuICAgICAgICAgICAgICB2YXIgaWNlVHJhbnNwb3J0ID0gdHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0O1xuICAgICAgICAgICAgICB2YXIgZHRsc1RyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIHZhciBsb2NhbENhcGFiaWxpdGllcyA9IHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzO1xuICAgICAgICAgICAgICB2YXIgcmVtb3RlQ2FwYWJpbGl0aWVzID0gdHJhbnNjZWl2ZXIucmVtb3RlQ2FwYWJpbGl0aWVzO1xuICAgICAgICAgICAgICB2YXIgcmVqZWN0ZWQgPSBtZWRpYVNlY3Rpb24uc3BsaXQoJ1xcbicsIDEpWzBdXG4gICAgICAgICAgICAgICAgICAuc3BsaXQoJyAnLCAyKVsxXSA9PT0gJzAnO1xuXG4gICAgICAgICAgICAgIGlmICghcmVqZWN0ZWQpIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVtb3RlSWNlUGFyYW1ldGVycyA9IFNEUFV0aWxzLmdldEljZVBhcmFtZXRlcnMoXG4gICAgICAgICAgICAgICAgICAgIG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpO1xuICAgICAgICAgICAgICAgIGlmIChpc0ljZUxpdGUpIHtcbiAgICAgICAgICAgICAgICAgIHZhciBjYW5kcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9Y2FuZGlkYXRlOicpXG4gICAgICAgICAgICAgICAgICAubWFwKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlQ2FuZGlkYXRlKGNhbmQpO1xuICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgIC5maWx0ZXIoZnVuY3Rpb24oY2FuZCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gY2FuZC5jb21wb25lbnQgPT09ICcxJztcbiAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgLy8gaWNlLWxpdGUgb25seSBpbmNsdWRlcyBob3N0IGNhbmRpZGF0ZXMgaW4gdGhlIFNEUCBzbyB3ZSBjYW5cbiAgICAgICAgICAgICAgICAgIC8vIHVzZSBzZXRSZW1vdGVDYW5kaWRhdGVzICh3aGljaCBpbXBsaWVzIGFuXG4gICAgICAgICAgICAgICAgICAvLyBSVENJY2VDYW5kaWRhdGVDb21wbGV0ZSlcbiAgICAgICAgICAgICAgICAgIGlmIChjYW5kcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgaWNlVHJhbnNwb3J0LnNldFJlbW90ZUNhbmRpZGF0ZXMoY2FuZHMpO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2YXIgcmVtb3RlRHRsc1BhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXREdGxzUGFyYW1ldGVycyhcbiAgICAgICAgICAgICAgICAgICAgbWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCk7XG4gICAgICAgICAgICAgICAgaWYgKGlzSWNlTGl0ZSkge1xuICAgICAgICAgICAgICAgICAgcmVtb3RlRHRsc1BhcmFtZXRlcnMucm9sZSA9ICdzZXJ2ZXInO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICghc2VsZi51c2luZ0J1bmRsZSB8fCBzZHBNTGluZUluZGV4ID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQuc3RhcnQoaWNlR2F0aGVyZXIsIHJlbW90ZUljZVBhcmFtZXRlcnMsXG4gICAgICAgICAgICAgICAgICAgICAgaXNJY2VMaXRlID8gJ2NvbnRyb2xsaW5nJyA6ICdjb250cm9sbGVkJyk7XG4gICAgICAgICAgICAgICAgICBkdGxzVHJhbnNwb3J0LnN0YXJ0KHJlbW90ZUR0bHNQYXJhbWV0ZXJzKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBDYWxjdWxhdGUgaW50ZXJzZWN0aW9uIG9mIGNhcGFiaWxpdGllcy5cbiAgICAgICAgICAgICAgICB2YXIgcGFyYW1zID0gc2VsZi5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKGxvY2FsQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgICAgICAgICAgICByZW1vdGVDYXBhYmlsaXRpZXMpO1xuXG4gICAgICAgICAgICAgICAgLy8gU3RhcnQgdGhlIFJUQ1J0cFNlbmRlci4gVGhlIFJUQ1J0cFJlY2VpdmVyIGZvciB0aGlzXG4gICAgICAgICAgICAgICAgLy8gdHJhbnNjZWl2ZXIgaGFzIGFscmVhZHkgYmVlbiBzdGFydGVkIGluIHNldFJlbW90ZURlc2NyaXB0aW9uLlxuICAgICAgICAgICAgICAgIHNlbGYuX3RyYW5zY2VpdmUodHJhbnNjZWl2ZXIsXG4gICAgICAgICAgICAgICAgICAgIHBhcmFtcy5jb2RlY3MubGVuZ3RoID4gMCxcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB0aGlzLmxvY2FsRGVzY3JpcHRpb24gPSB7XG4gICAgICAgICAgICB0eXBlOiBkZXNjcmlwdGlvbi50eXBlLFxuICAgICAgICAgICAgc2RwOiBkZXNjcmlwdGlvbi5zZHBcbiAgICAgICAgICB9O1xuICAgICAgICAgIHN3aXRjaCAoZGVzY3JpcHRpb24udHlwZSkge1xuICAgICAgICAgICAgY2FzZSAnb2ZmZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnaGF2ZS1sb2NhbC1vZmZlcicpO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2Fuc3dlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdzdGFibGUnKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCd1bnN1cHBvcnRlZCB0eXBlIFwiJyArIGRlc2NyaXB0aW9uLnR5cGUgK1xuICAgICAgICAgICAgICAgICAgJ1wiJyk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gSWYgYSBzdWNjZXNzIGNhbGxiYWNrIHdhcyBwcm92aWRlZCwgZW1pdCBJQ0UgY2FuZGlkYXRlcyBhZnRlciBpdFxuICAgICAgICAgIC8vIGhhcyBiZWVuIGV4ZWN1dGVkLiBPdGhlcndpc2UsIGVtaXQgY2FsbGJhY2sgYWZ0ZXIgdGhlIFByb21pc2UgaXNcbiAgICAgICAgICAvLyByZXNvbHZlZC5cbiAgICAgICAgICB2YXIgaGFzQ2FsbGJhY2sgPSBhcmd1bWVudHMubGVuZ3RoID4gMSAmJlxuICAgICAgICAgICAgdHlwZW9mIGFyZ3VtZW50c1sxXSA9PT0gJ2Z1bmN0aW9uJztcbiAgICAgICAgICBpZiAoaGFzQ2FsbGJhY2spIHtcbiAgICAgICAgICAgIHZhciBjYiA9IGFyZ3VtZW50c1sxXTtcbiAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICBjYigpO1xuICAgICAgICAgICAgICBpZiAoc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9PT0gJ25ldycpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2dhdGhlcmluZyc7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcygpO1xuICAgICAgICAgICAgfSwgMCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHZhciBwID0gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgICAgICAgcC50aGVuKGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgaWYgKCFoYXNDYWxsYmFjaykge1xuICAgICAgICAgICAgICBpZiAoc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9PT0gJ25ldycpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2dhdGhlcmluZyc7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgLy8gVXN1YWxseSBjYW5kaWRhdGVzIHdpbGwgYmUgZW1pdHRlZCBlYXJsaWVyLlxuICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChzZWxmLl9lbWl0QnVmZmVyZWRDYW5kaWRhdGVzLmJpbmQoc2VsZiksIDUwMCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgcmV0dXJuIHA7XG4gICAgICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnNldFJlbW90ZURlc2NyaXB0aW9uID1cbiAgICAgICAgZnVuY3Rpb24oZGVzY3JpcHRpb24pIHtcbiAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgdmFyIHN0cmVhbSA9IG5ldyBNZWRpYVN0cmVhbSgpO1xuICAgICAgICAgIHZhciByZWNlaXZlckxpc3QgPSBbXTtcbiAgICAgICAgICB2YXIgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKGRlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgICAgdmFyIHNlc3Npb25wYXJ0ID0gc2VjdGlvbnMuc2hpZnQoKTtcbiAgICAgICAgICB2YXIgaXNJY2VMaXRlID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgoc2Vzc2lvbnBhcnQsXG4gICAgICAgICAgICAgICdhPWljZS1saXRlJykubGVuZ3RoID4gMDtcbiAgICAgICAgICB0aGlzLnVzaW5nQnVuZGxlID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgoc2Vzc2lvbnBhcnQsXG4gICAgICAgICAgICAgICdhPWdyb3VwOkJVTkRMRSAnKS5sZW5ndGggPiAwO1xuICAgICAgICAgIHNlY3Rpb25zLmZvckVhY2goZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZHBNTGluZUluZGV4KSB7XG4gICAgICAgICAgICB2YXIgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKG1lZGlhU2VjdGlvbik7XG4gICAgICAgICAgICB2YXIgbWxpbmUgPSBsaW5lc1swXS5zdWJzdHIoMikuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgIHZhciBraW5kID0gbWxpbmVbMF07XG4gICAgICAgICAgICB2YXIgcmVqZWN0ZWQgPSBtbGluZVsxXSA9PT0gJzAnO1xuICAgICAgICAgICAgdmFyIGRpcmVjdGlvbiA9IFNEUFV0aWxzLmdldERpcmVjdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KTtcblxuICAgICAgICAgICAgdmFyIHRyYW5zY2VpdmVyO1xuICAgICAgICAgICAgdmFyIGljZUdhdGhlcmVyO1xuICAgICAgICAgICAgdmFyIGljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgIHZhciBkdGxzVHJhbnNwb3J0O1xuICAgICAgICAgICAgdmFyIHJ0cFNlbmRlcjtcbiAgICAgICAgICAgIHZhciBydHBSZWNlaXZlcjtcbiAgICAgICAgICAgIHZhciBzZW5kRW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgdmFyIHJlY3ZFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgICAgICB2YXIgbG9jYWxDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgIHZhciB0cmFjaztcbiAgICAgICAgICAgIC8vIEZJWE1FOiBlbnN1cmUgdGhlIG1lZGlhU2VjdGlvbiBoYXMgcnRjcC1tdXggc2V0LlxuICAgICAgICAgICAgdmFyIHJlbW90ZUNhcGFiaWxpdGllcyA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnM7XG4gICAgICAgICAgICB2YXIgcmVtb3RlRHRsc1BhcmFtZXRlcnM7XG4gICAgICAgICAgICBpZiAoIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAgIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgcmVtb3RlRHRsc1BhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXREdGxzUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24sXG4gICAgICAgICAgICAgICAgICBzZXNzaW9ucGFydCk7XG4gICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnY2xpZW50JztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMgPVxuICAgICAgICAgICAgICAgIFNEUFV0aWxzLnBhcnNlUnRwRW5jb2RpbmdQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbik7XG5cbiAgICAgICAgICAgIHZhciBtaWQgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPW1pZDonKTtcbiAgICAgICAgICAgIGlmIChtaWQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgIG1pZCA9IG1pZFswXS5zdWJzdHIoNik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBtaWQgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIGNuYW1lO1xuICAgICAgICAgICAgLy8gR2V0cyB0aGUgZmlyc3QgU1NSQy4gTm90ZSB0aGF0IHdpdGggUlRYIHRoZXJlIG1pZ2h0IGJlIG11bHRpcGxlXG4gICAgICAgICAgICAvLyBTU1JDcy5cbiAgICAgICAgICAgIHZhciByZW1vdGVTc3JjID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjOicpXG4gICAgICAgICAgICAgICAgLm1hcChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICAgICAgICByZXR1cm4gU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEobGluZSk7XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAuZmlsdGVyKGZ1bmN0aW9uKG9iaikge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIG9iai5hdHRyaWJ1dGUgPT09ICdjbmFtZSc7XG4gICAgICAgICAgICAgICAgfSlbMF07XG4gICAgICAgICAgICBpZiAocmVtb3RlU3NyYykge1xuICAgICAgICAgICAgICBjbmFtZSA9IHJlbW90ZVNzcmMudmFsdWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBpc0NvbXBsZXRlID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLFxuICAgICAgICAgICAgICAgICdhPWVuZC1vZi1jYW5kaWRhdGVzJykubGVuZ3RoID4gMDtcbiAgICAgICAgICAgIHZhciBjYW5kcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9Y2FuZGlkYXRlOicpXG4gICAgICAgICAgICAgICAgLm1hcChmdW5jdGlvbihjYW5kKSB7XG4gICAgICAgICAgICAgICAgICByZXR1cm4gU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUoY2FuZCk7XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAuZmlsdGVyKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBjYW5kLmNvbXBvbmVudCA9PT0gJzEnO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgaWYgKGRlc2NyaXB0aW9uLnR5cGUgPT09ICdvZmZlcicgJiYgIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHZhciB0cmFuc3BvcnRzID0gc2VsZi51c2luZ0J1bmRsZSAmJiBzZHBNTGluZUluZGV4ID4gMCA/IHtcbiAgICAgICAgICAgICAgICBpY2VHYXRoZXJlcjogc2VsZi50cmFuc2NlaXZlcnNbMF0uaWNlR2F0aGVyZXIsXG4gICAgICAgICAgICAgICAgaWNlVHJhbnNwb3J0OiBzZWxmLnRyYW5zY2VpdmVyc1swXS5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogc2VsZi50cmFuc2NlaXZlcnNbMF0uZHRsc1RyYW5zcG9ydFxuICAgICAgICAgICAgICB9IDogc2VsZi5fY3JlYXRlSWNlQW5kRHRsc1RyYW5zcG9ydHMobWlkLCBzZHBNTGluZUluZGV4KTtcblxuICAgICAgICAgICAgICBpZiAoaXNDb21wbGV0ZSkge1xuICAgICAgICAgICAgICAgIHRyYW5zcG9ydHMuaWNlVHJhbnNwb3J0LnNldFJlbW90ZUNhbmRpZGF0ZXMoY2FuZHMpO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMgPSBSVENSdHBSZWNlaXZlci5nZXRDYXBhYmlsaXRpZXMoa2luZCk7XG4gICAgICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMgPSBbe1xuICAgICAgICAgICAgICAgIHNzcmM6ICgyICogc2RwTUxpbmVJbmRleCArIDIpICogMTAwMVxuICAgICAgICAgICAgICB9XTtcblxuICAgICAgICAgICAgICBydHBSZWNlaXZlciA9IG5ldyBSVENSdHBSZWNlaXZlcih0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQsIGtpbmQpO1xuXG4gICAgICAgICAgICAgIHRyYWNrID0gcnRwUmVjZWl2ZXIudHJhY2s7XG4gICAgICAgICAgICAgIHJlY2VpdmVyTGlzdC5wdXNoKFt0cmFjaywgcnRwUmVjZWl2ZXJdKTtcbiAgICAgICAgICAgICAgLy8gRklYTUU6IG5vdCBjb3JyZWN0IHdoZW4gdGhlcmUgYXJlIG11bHRpcGxlIHN0cmVhbXMgYnV0IHRoYXQgaXNcbiAgICAgICAgICAgICAgLy8gbm90IGN1cnJlbnRseSBzdXBwb3J0ZWQgaW4gdGhpcyBzaGltLlxuICAgICAgICAgICAgICBzdHJlYW0uYWRkVHJhY2sodHJhY2spO1xuXG4gICAgICAgICAgICAgIC8vIEZJWE1FOiBsb29rIGF0IGRpcmVjdGlvbi5cbiAgICAgICAgICAgICAgaWYgKHNlbGYubG9jYWxTdHJlYW1zLmxlbmd0aCA+IDAgJiZcbiAgICAgICAgICAgICAgICAgIHNlbGYubG9jYWxTdHJlYW1zWzBdLmdldFRyYWNrcygpLmxlbmd0aCA+PSBzZHBNTGluZUluZGV4KSB7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGFjdHVhbGx5IG1vcmUgY29tcGxpY2F0ZWQsIG5lZWRzIHRvIG1hdGNoIHR5cGVzIGV0Y1xuICAgICAgICAgICAgICAgIHZhciBsb2NhbHRyYWNrID0gc2VsZi5sb2NhbFN0cmVhbXNbMF1cbiAgICAgICAgICAgICAgICAgICAgLmdldFRyYWNrcygpW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICAgICAgICAgIHJ0cFNlbmRlciA9IG5ldyBSVENSdHBTZW5kZXIobG9jYWx0cmFjayxcbiAgICAgICAgICAgICAgICAgICAgdHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0KTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdID0ge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyOiB0cmFuc3BvcnRzLmljZUdhdGhlcmVyLFxuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydDogdHJhbnNwb3J0cy5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LFxuICAgICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzOiBsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICByZW1vdGVDYXBhYmlsaXRpZXM6IHJlbW90ZUNhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICBydHBTZW5kZXI6IHJ0cFNlbmRlcixcbiAgICAgICAgICAgICAgICBydHBSZWNlaXZlcjogcnRwUmVjZWl2ZXIsXG4gICAgICAgICAgICAgICAga2luZDoga2luZCxcbiAgICAgICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgICAgICBjbmFtZTogY25hbWUsXG4gICAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVyczogc2VuZEVuY29kaW5nUGFyYW1ldGVycyxcbiAgICAgICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzOiByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBSZWNlaXZlciBub3cuIFRoZSBSVFBTZW5kZXIgaXMgc3RhcnRlZCBpblxuICAgICAgICAgICAgICAvLyBzZXRMb2NhbERlc2NyaXB0aW9uLlxuICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdLFxuICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPT09ICdzZW5kcmVjdicgfHwgZGlyZWN0aW9uID09PSAnc2VuZG9ubHknKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ2Fuc3dlcicgJiYgIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHRyYW5zY2VpdmVyID0gc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF07XG4gICAgICAgICAgICAgIGljZUdhdGhlcmVyID0gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXI7XG4gICAgICAgICAgICAgIGljZVRyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIHJ0cFNlbmRlciA9IHRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgICAgICAgICAgcnRwUmVjZWl2ZXIgPSB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzID0gdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgICAgc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF0ucmVjdkVuY29kaW5nUGFyYW1ldGVycyA9XG4gICAgICAgICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XS5yZW1vdGVDYXBhYmlsaXRpZXMgPVxuICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzO1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XS5jbmFtZSA9IGNuYW1lO1xuXG4gICAgICAgICAgICAgIGlmICgoaXNJY2VMaXRlIHx8IGlzQ29tcGxldGUpICYmIGNhbmRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zdGFydChpY2VHYXRoZXJlciwgcmVtb3RlSWNlUGFyYW1ldGVycyxcbiAgICAgICAgICAgICAgICAgICAgJ2NvbnRyb2xsaW5nJyk7XG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID09PSAnc2VuZHJlY3YnIHx8IGRpcmVjdGlvbiA9PT0gJ3JlY3Zvbmx5JyxcbiAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdzZW5kb25seScpO1xuXG4gICAgICAgICAgICAgIGlmIChydHBSZWNlaXZlciAmJlxuICAgICAgICAgICAgICAgICAgKGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdzZW5kb25seScpKSB7XG4gICAgICAgICAgICAgICAgdHJhY2sgPSBydHBSZWNlaXZlci50cmFjaztcbiAgICAgICAgICAgICAgICByZWNlaXZlckxpc3QucHVzaChbdHJhY2ssIHJ0cFJlY2VpdmVyXSk7XG4gICAgICAgICAgICAgICAgc3RyZWFtLmFkZFRyYWNrKHRyYWNrKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBGSVhNRTogYWN0dWFsbHkgdGhlIHJlY2VpdmVyIHNob3VsZCBiZSBjcmVhdGVkIGxhdGVyLlxuICAgICAgICAgICAgICAgIGRlbGV0ZSB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgdGhpcy5yZW1vdGVEZXNjcmlwdGlvbiA9IHtcbiAgICAgICAgICAgIHR5cGU6IGRlc2NyaXB0aW9uLnR5cGUsXG4gICAgICAgICAgICBzZHA6IGRlc2NyaXB0aW9uLnNkcFxuICAgICAgICAgIH07XG4gICAgICAgICAgc3dpdGNoIChkZXNjcmlwdGlvbi50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdvZmZlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdoYXZlLXJlbW90ZS1vZmZlcicpO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2Fuc3dlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdzdGFibGUnKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCd1bnN1cHBvcnRlZCB0eXBlIFwiJyArIGRlc2NyaXB0aW9uLnR5cGUgK1xuICAgICAgICAgICAgICAgICAgJ1wiJyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChzdHJlYW0uZ2V0VHJhY2tzKCkubGVuZ3RoKSB7XG4gICAgICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbXMucHVzaChzdHJlYW0pO1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnYWRkc3RyZWFtJyk7XG4gICAgICAgICAgICAgIGV2ZW50LnN0cmVhbSA9IHN0cmVhbTtcbiAgICAgICAgICAgICAgc2VsZi5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYub25hZGRzdHJlYW0gIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYub25hZGRzdHJlYW0oZXZlbnQpO1xuICAgICAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LmZvckVhY2goZnVuY3Rpb24oaXRlbSkge1xuICAgICAgICAgICAgICAgIHZhciB0cmFjayA9IGl0ZW1bMF07XG4gICAgICAgICAgICAgICAgdmFyIHJlY2VpdmVyID0gaXRlbVsxXTtcbiAgICAgICAgICAgICAgICB2YXIgdHJhY2tFdmVudCA9IG5ldyBFdmVudCgndHJhY2snKTtcbiAgICAgICAgICAgICAgICB0cmFja0V2ZW50LnRyYWNrID0gdHJhY2s7XG4gICAgICAgICAgICAgICAgdHJhY2tFdmVudC5yZWNlaXZlciA9IHJlY2VpdmVyO1xuICAgICAgICAgICAgICAgIHRyYWNrRXZlbnQuc3RyZWFtcyA9IFtzdHJlYW1dO1xuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYub250cmFjayAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYub250cmFjayh0cmFja0V2ZW50KTtcbiAgICAgICAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICAgICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY2xvc2UgPSBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgLyogbm90IHlldFxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgICovXG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5kdGxzVHJhbnNwb3J0KSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnJ0cFNlbmRlcikge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJ0cFNlbmRlci5zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyKSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIC8vIEZJWE1FOiBjbGVhbiB1cCB0cmFja3MsIGxvY2FsIHN0cmVhbXMsIHJlbW90ZSBzdHJlYW1zLCBldGNcbiAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdjbG9zZWQnKTtcbiAgICB9O1xuXG4gICAgLy8gVXBkYXRlIHRoZSBzaWduYWxpbmcgc3RhdGUuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdXBkYXRlU2lnbmFsaW5nU3RhdGUgPVxuICAgICAgICBmdW5jdGlvbihuZXdTdGF0ZSkge1xuICAgICAgICAgIHRoaXMuc2lnbmFsaW5nU3RhdGUgPSBuZXdTdGF0ZTtcbiAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ3NpZ25hbGluZ3N0YXRlY2hhbmdlJyk7XG4gICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICBpZiAodGhpcy5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UoZXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIC8vIERldGVybWluZSB3aGV0aGVyIHRvIGZpcmUgdGhlIG5lZ290aWF0aW9ubmVlZGVkIGV2ZW50LlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX21heWJlRmlyZU5lZ290aWF0aW9uTmVlZGVkID1cbiAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgLy8gRmlyZSBhd2F5IChmb3Igbm93KS5cbiAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ25lZ290aWF0aW9ubmVlZGVkJyk7XG4gICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICBpZiAodGhpcy5vbm5lZ290aWF0aW9ubmVlZGVkICE9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQoZXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIC8vIFVwZGF0ZSB0aGUgY29ubmVjdGlvbiBzdGF0ZS5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl91cGRhdGVDb25uZWN0aW9uU3RhdGUgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBuZXdTdGF0ZTtcbiAgICAgIHZhciBzdGF0ZXMgPSB7XG4gICAgICAgICduZXcnOiAwLFxuICAgICAgICBjbG9zZWQ6IDAsXG4gICAgICAgIGNvbm5lY3Rpbmc6IDAsXG4gICAgICAgIGNoZWNraW5nOiAwLFxuICAgICAgICBjb25uZWN0ZWQ6IDAsXG4gICAgICAgIGNvbXBsZXRlZDogMCxcbiAgICAgICAgZmFpbGVkOiAwXG4gICAgICB9O1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBzdGF0ZXNbdHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0LnN0YXRlXSsrO1xuICAgICAgICBzdGF0ZXNbdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5zdGF0ZV0rKztcbiAgICAgIH0pO1xuICAgICAgLy8gSUNFVHJhbnNwb3J0LmNvbXBsZXRlZCBhbmQgY29ubmVjdGVkIGFyZSB0aGUgc2FtZSBmb3IgdGhpcyBwdXJwb3NlLlxuICAgICAgc3RhdGVzLmNvbm5lY3RlZCArPSBzdGF0ZXMuY29tcGxldGVkO1xuXG4gICAgICBuZXdTdGF0ZSA9ICduZXcnO1xuICAgICAgaWYgKHN0YXRlcy5mYWlsZWQgPiAwKSB7XG4gICAgICAgIG5ld1N0YXRlID0gJ2ZhaWxlZCc7XG4gICAgICB9IGVsc2UgaWYgKHN0YXRlcy5jb25uZWN0aW5nID4gMCB8fCBzdGF0ZXMuY2hlY2tpbmcgPiAwKSB7XG4gICAgICAgIG5ld1N0YXRlID0gJ2Nvbm5lY3RpbmcnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuZGlzY29ubmVjdGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdkaXNjb25uZWN0ZWQnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMubmV3ID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICduZXcnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuY29ubmVjdGVkID4gMCB8fCBzdGF0ZXMuY29tcGxldGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdjb25uZWN0ZWQnO1xuICAgICAgfVxuXG4gICAgICBpZiAobmV3U3RhdGUgIT09IHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgIHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlID0gbmV3U3RhdGU7XG4gICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlJyk7XG4gICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmICh0aGlzLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVPZmZlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgaWYgKHRoaXMuX3BlbmRpbmdPZmZlcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2NyZWF0ZU9mZmVyIGNhbGxlZCB3aGlsZSB0aGVyZSBpcyBhIHBlbmRpbmcgb2ZmZXIuJyk7XG4gICAgICB9XG4gICAgICB2YXIgb2ZmZXJPcHRpb25zO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDEgJiYgdHlwZW9mIGFyZ3VtZW50c1swXSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBvZmZlck9wdGlvbnMgPSBhcmd1bWVudHNbMF07XG4gICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDMpIHtcbiAgICAgICAgb2ZmZXJPcHRpb25zID0gYXJndW1lbnRzWzJdO1xuICAgICAgfVxuXG4gICAgICB2YXIgdHJhY2tzID0gW107XG4gICAgICB2YXIgbnVtQXVkaW9UcmFja3MgPSAwO1xuICAgICAgdmFyIG51bVZpZGVvVHJhY2tzID0gMDtcbiAgICAgIC8vIERlZmF1bHQgdG8gc2VuZHJlY3YuXG4gICAgICBpZiAodGhpcy5sb2NhbFN0cmVhbXMubGVuZ3RoKSB7XG4gICAgICAgIG51bUF1ZGlvVHJhY2tzID0gdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0QXVkaW9UcmFja3MoKS5sZW5ndGg7XG4gICAgICAgIG51bVZpZGVvVHJhY2tzID0gdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGg7XG4gICAgICB9XG4gICAgICAvLyBEZXRlcm1pbmUgbnVtYmVyIG9mIGF1ZGlvIGFuZCB2aWRlbyB0cmFja3Mgd2UgbmVlZCB0byBzZW5kL3JlY3YuXG4gICAgICBpZiAob2ZmZXJPcHRpb25zKSB7XG4gICAgICAgIC8vIFJlamVjdCBDaHJvbWUgbGVnYWN5IGNvbnN0cmFpbnRzLlxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm1hbmRhdG9yeSB8fCBvZmZlck9wdGlvbnMub3B0aW9uYWwpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFxuICAgICAgICAgICAgICAnTGVnYWN5IG1hbmRhdG9yeS9vcHRpb25hbCBjb25zdHJhaW50cyBub3Qgc3VwcG9ydGVkLicpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVBdWRpbyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgbnVtQXVkaW9UcmFja3MgPSBvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVBdWRpbztcbiAgICAgICAgfVxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlVmlkZW8gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIG51bVZpZGVvVHJhY2tzID0gb2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlVmlkZW87XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLmxvY2FsU3RyZWFtcy5sZW5ndGgpIHtcbiAgICAgICAgLy8gUHVzaCBsb2NhbCBzdHJlYW1zLlxuICAgICAgICB0aGlzLmxvY2FsU3RyZWFtc1swXS5nZXRUcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uKHRyYWNrKSB7XG4gICAgICAgICAgdHJhY2tzLnB1c2goe1xuICAgICAgICAgICAga2luZDogdHJhY2sua2luZCxcbiAgICAgICAgICAgIHRyYWNrOiB0cmFjayxcbiAgICAgICAgICAgIHdhbnRSZWNlaXZlOiB0cmFjay5raW5kID09PSAnYXVkaW8nID9cbiAgICAgICAgICAgICAgICBudW1BdWRpb1RyYWNrcyA+IDAgOiBudW1WaWRlb1RyYWNrcyA+IDBcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpZiAodHJhY2sua2luZCA9PT0gJ2F1ZGlvJykge1xuICAgICAgICAgICAgbnVtQXVkaW9UcmFja3MtLTtcbiAgICAgICAgICB9IGVsc2UgaWYgKHRyYWNrLmtpbmQgPT09ICd2aWRlbycpIHtcbiAgICAgICAgICAgIG51bVZpZGVvVHJhY2tzLS07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIC8vIENyZWF0ZSBNLWxpbmVzIGZvciByZWN2b25seSBzdHJlYW1zLlxuICAgICAgd2hpbGUgKG51bUF1ZGlvVHJhY2tzID4gMCB8fCBudW1WaWRlb1RyYWNrcyA+IDApIHtcbiAgICAgICAgaWYgKG51bUF1ZGlvVHJhY2tzID4gMCkge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6ICdhdWRpbycsXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG51bUF1ZGlvVHJhY2tzLS07XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG51bVZpZGVvVHJhY2tzID4gMCkge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6ICd2aWRlbycsXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG51bVZpZGVvVHJhY2tzLS07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdmFyIHNkcCA9IFNEUFV0aWxzLndyaXRlU2Vzc2lvbkJvaWxlcnBsYXRlKCk7XG4gICAgICB2YXIgdHJhbnNjZWl2ZXJzID0gW107XG4gICAgICB0cmFja3MuZm9yRWFjaChmdW5jdGlvbihtbGluZSwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAvLyBGb3IgZWFjaCB0cmFjaywgY3JlYXRlIGFuIGljZSBnYXRoZXJlciwgaWNlIHRyYW5zcG9ydCxcbiAgICAgICAgLy8gZHRscyB0cmFuc3BvcnQsIHBvdGVudGlhbGx5IHJ0cHNlbmRlciBhbmQgcnRwcmVjZWl2ZXIuXG4gICAgICAgIHZhciB0cmFjayA9IG1saW5lLnRyYWNrO1xuICAgICAgICB2YXIga2luZCA9IG1saW5lLmtpbmQ7XG4gICAgICAgIHZhciBtaWQgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcblxuICAgICAgICB2YXIgdHJhbnNwb3J0cyA9IHNlbGYudXNpbmdCdW5kbGUgJiYgc2RwTUxpbmVJbmRleCA+IDAgPyB7XG4gICAgICAgICAgaWNlR2F0aGVyZXI6IHRyYW5zY2VpdmVyc1swXS5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zY2VpdmVyc1swXS5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNjZWl2ZXJzWzBdLmR0bHNUcmFuc3BvcnRcbiAgICAgICAgfSA6IHNlbGYuX2NyZWF0ZUljZUFuZER0bHNUcmFuc3BvcnRzKG1pZCwgc2RwTUxpbmVJbmRleCk7XG5cbiAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzID0gUlRDUnRwU2VuZGVyLmdldENhcGFiaWxpdGllcyhraW5kKTtcbiAgICAgICAgdmFyIHJ0cFNlbmRlcjtcbiAgICAgICAgdmFyIHJ0cFJlY2VpdmVyO1xuXG4gICAgICAgIC8vIGdlbmVyYXRlIGFuIHNzcmMgbm93LCB0byBiZSB1c2VkIGxhdGVyIGluIHJ0cFNlbmRlci5zZW5kXG4gICAgICAgIHZhciBzZW5kRW5jb2RpbmdQYXJhbWV0ZXJzID0gW3tcbiAgICAgICAgICBzc3JjOiAoMiAqIHNkcE1MaW5lSW5kZXggKyAxKSAqIDEwMDFcbiAgICAgICAgfV07XG4gICAgICAgIGlmICh0cmFjaykge1xuICAgICAgICAgIHJ0cFNlbmRlciA9IG5ldyBSVENSdHBTZW5kZXIodHJhY2ssIHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAobWxpbmUud2FudFJlY2VpdmUpIHtcbiAgICAgICAgICBydHBSZWNlaXZlciA9IG5ldyBSVENSdHBSZWNlaXZlcih0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQsIGtpbmQpO1xuICAgICAgICB9XG5cbiAgICAgICAgdHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdID0ge1xuICAgICAgICAgIGljZUdhdGhlcmVyOiB0cmFuc3BvcnRzLmljZUdhdGhlcmVyLFxuICAgICAgICAgIGljZVRyYW5zcG9ydDogdHJhbnNwb3J0cy5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LFxuICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzOiBsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICByZW1vdGVDYXBhYmlsaXRpZXM6IG51bGwsXG4gICAgICAgICAgcnRwU2VuZGVyOiBydHBTZW5kZXIsXG4gICAgICAgICAgcnRwUmVjZWl2ZXI6IHJ0cFJlY2VpdmVyLFxuICAgICAgICAgIGtpbmQ6IGtpbmQsXG4gICAgICAgICAgbWlkOiBtaWQsXG4gICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVyczogc2VuZEVuY29kaW5nUGFyYW1ldGVycyxcbiAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzOiBudWxsXG4gICAgICAgIH07XG4gICAgICB9KTtcbiAgICAgIGlmICh0aGlzLnVzaW5nQnVuZGxlKSB7XG4gICAgICAgIHNkcCArPSAnYT1ncm91cDpCVU5ETEUgJyArIHRyYW5zY2VpdmVycy5tYXAoZnVuY3Rpb24odCkge1xuICAgICAgICAgIHJldHVybiB0Lm1pZDtcbiAgICAgICAgfSkuam9pbignICcpICsgJ1xcclxcbic7XG4gICAgICB9XG4gICAgICB0cmFja3MuZm9yRWFjaChmdW5jdGlvbihtbGluZSwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSB0cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF07XG4gICAgICAgIHNkcCArPSBTRFBVdGlscy53cml0ZU1lZGlhU2VjdGlvbih0cmFuc2NlaXZlcixcbiAgICAgICAgICAgIHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzLCAnb2ZmZXInLCBzZWxmLmxvY2FsU3RyZWFtc1swXSk7XG4gICAgICB9KTtcblxuICAgICAgdGhpcy5fcGVuZGluZ09mZmVyID0gdHJhbnNjZWl2ZXJzO1xuICAgICAgdmFyIGRlc2MgPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJ29mZmVyJyxcbiAgICAgICAgc2RwOiBzZHBcbiAgICAgIH0pO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggJiYgdHlwZW9mIGFyZ3VtZW50c1swXSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChhcmd1bWVudHNbMF0sIDAsIGRlc2MpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShkZXNjKTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVBbnN3ZXIgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgICAgdmFyIHNkcCA9IFNEUFV0aWxzLndyaXRlU2Vzc2lvbkJvaWxlcnBsYXRlKCk7XG4gICAgICBpZiAodGhpcy51c2luZ0J1bmRsZSkge1xuICAgICAgICBzZHAgKz0gJ2E9Z3JvdXA6QlVORExFICcgKyB0aGlzLnRyYW5zY2VpdmVycy5tYXAoZnVuY3Rpb24odCkge1xuICAgICAgICAgIHJldHVybiB0Lm1pZDtcbiAgICAgICAgfSkuam9pbignICcpICsgJ1xcclxcbic7XG4gICAgICB9XG4gICAgICB0aGlzLnRyYW5zY2VpdmVycy5mb3JFYWNoKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIC8vIENhbGN1bGF0ZSBpbnRlcnNlY3Rpb24gb2YgY2FwYWJpbGl0aWVzLlxuICAgICAgICB2YXIgY29tbW9uQ2FwYWJpbGl0aWVzID0gc2VsZi5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKFxuICAgICAgICAgICAgdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXMpO1xuXG4gICAgICAgIHNkcCArPSBTRFBVdGlscy53cml0ZU1lZGlhU2VjdGlvbih0cmFuc2NlaXZlciwgY29tbW9uQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgICAgJ2Fuc3dlcicsIHNlbGYubG9jYWxTdHJlYW1zWzBdKTtcbiAgICAgIH0pO1xuXG4gICAgICB2YXIgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiAnYW5zd2VyJyxcbiAgICAgICAgc2RwOiBzZHBcbiAgICAgIH0pO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggJiYgdHlwZW9mIGFyZ3VtZW50c1swXSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChhcmd1bWVudHNbMF0sIDAsIGRlc2MpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShkZXNjKTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgICAgIGlmIChjYW5kaWRhdGUgPT09IG51bGwpIHtcbiAgICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLmljZVRyYW5zcG9ydC5hZGRSZW1vdGVDYW5kaWRhdGUoe30pO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBtTGluZUluZGV4ID0gY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXg7XG4gICAgICAgIGlmIChjYW5kaWRhdGUuc2RwTWlkKSB7XG4gICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLnRyYW5zY2VpdmVycy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKHRoaXMudHJhbnNjZWl2ZXJzW2ldLm1pZCA9PT0gY2FuZGlkYXRlLnNkcE1pZCkge1xuICAgICAgICAgICAgICBtTGluZUluZGV4ID0gaTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHZhciB0cmFuc2NlaXZlciA9IHRoaXMudHJhbnNjZWl2ZXJzW21MaW5lSW5kZXhdO1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICB2YXIgY2FuZCA9IE9iamVjdC5rZXlzKGNhbmRpZGF0ZS5jYW5kaWRhdGUpLmxlbmd0aCA+IDAgP1xuICAgICAgICAgICAgICBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kaWRhdGUuY2FuZGlkYXRlKSA6IHt9O1xuICAgICAgICAgIC8vIElnbm9yZSBDaHJvbWUncyBpbnZhbGlkIGNhbmRpZGF0ZXMgc2luY2UgRWRnZSBkb2VzIG5vdCBsaWtlIHRoZW0uXG4gICAgICAgICAgaWYgKGNhbmQucHJvdG9jb2wgPT09ICd0Y3AnICYmIGNhbmQucG9ydCA9PT0gMCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBJZ25vcmUgUlRDUCBjYW5kaWRhdGVzLCB3ZSBhc3N1bWUgUlRDUC1NVVguXG4gICAgICAgICAgaWYgKGNhbmQuY29tcG9uZW50ICE9PSAnMScpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gQSBkaXJ0eSBoYWNrIHRvIG1ha2Ugc2FtcGxlcyB3b3JrLlxuICAgICAgICAgIGlmIChjYW5kLnR5cGUgPT09ICdlbmRPZkNhbmRpZGF0ZXMnKSB7XG4gICAgICAgICAgICBjYW5kID0ge307XG4gICAgICAgICAgfVxuICAgICAgICAgIHRyYW5zY2VpdmVyLmljZVRyYW5zcG9ydC5hZGRSZW1vdGVDYW5kaWRhdGUoY2FuZCk7XG5cbiAgICAgICAgICAvLyB1cGRhdGUgdGhlIHJlbW90ZURlc2NyaXB0aW9uLlxuICAgICAgICAgIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnModGhpcy5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgIHNlY3Rpb25zW21MaW5lSW5kZXggKyAxXSArPSAoY2FuZC50eXBlID8gY2FuZGlkYXRlLmNhbmRpZGF0ZS50cmltKClcbiAgICAgICAgICAgICAgOiAnYT1lbmQtb2YtY2FuZGlkYXRlcycpICsgJ1xcclxcbic7XG4gICAgICAgICAgdGhpcy5yZW1vdGVEZXNjcmlwdGlvbi5zZHAgPSBzZWN0aW9ucy5qb2luKCcnKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoYXJndW1lbnRzWzFdLCAwKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTdGF0cyA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHByb21pc2VzID0gW107XG4gICAgICB0aGlzLnRyYW5zY2VpdmVycy5mb3JFYWNoKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIFsncnRwU2VuZGVyJywgJ3J0cFJlY2VpdmVyJywgJ2ljZUdhdGhlcmVyJywgJ2ljZVRyYW5zcG9ydCcsXG4gICAgICAgICAgICAnZHRsc1RyYW5zcG9ydCddLmZvckVhY2goZnVuY3Rpb24obWV0aG9kKSB7XG4gICAgICAgICAgICAgIGlmICh0cmFuc2NlaXZlclttZXRob2RdKSB7XG4gICAgICAgICAgICAgICAgcHJvbWlzZXMucHVzaCh0cmFuc2NlaXZlclttZXRob2RdLmdldFN0YXRzKCkpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgICAgdmFyIGNiID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiYgdHlwZW9mIGFyZ3VtZW50c1sxXSA9PT0gJ2Z1bmN0aW9uJyAmJlxuICAgICAgICAgIGFyZ3VtZW50c1sxXTtcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlKSB7XG4gICAgICAgIC8vIHNoaW0gZ2V0U3RhdHMgd2l0aCBtYXBsaWtlIHN1cHBvcnRcbiAgICAgICAgdmFyIHJlc3VsdHMgPSBuZXcgTWFwKCk7XG4gICAgICAgIFByb21pc2UuYWxsKHByb21pc2VzKS50aGVuKGZ1bmN0aW9uKHJlcykge1xuICAgICAgICAgIHJlcy5mb3JFYWNoKGZ1bmN0aW9uKHJlc3VsdCkge1xuICAgICAgICAgICAgT2JqZWN0LmtleXMocmVzdWx0KS5mb3JFYWNoKGZ1bmN0aW9uKGlkKSB7XG4gICAgICAgICAgICAgIHJlc3VsdHMuc2V0KGlkLCByZXN1bHRbaWRdKTtcbiAgICAgICAgICAgICAgcmVzdWx0c1tpZF0gPSByZXN1bHRbaWRdO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKGNiKSB7XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChjYiwgMCwgcmVzdWx0cyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJlc29sdmUocmVzdWx0cyk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfTtcbiAgfSxcblxuICAvLyBBdHRhY2ggYSBtZWRpYSBzdHJlYW0gdG8gYW4gZWxlbWVudC5cbiAgYXR0YWNoTWVkaWFTdHJlYW06IGZ1bmN0aW9uKGVsZW1lbnQsIHN0cmVhbSkge1xuICAgIGxvZ2dpbmcoJ0RFUFJFQ0FURUQsIGF0dGFjaE1lZGlhU3RyZWFtIHdpbGwgc29vbiBiZSByZW1vdmVkLicpO1xuICAgIGVsZW1lbnQuc3JjT2JqZWN0ID0gc3RyZWFtO1xuICB9LFxuXG4gIHJlYXR0YWNoTWVkaWFTdHJlYW06IGZ1bmN0aW9uKHRvLCBmcm9tKSB7XG4gICAgbG9nZ2luZygnREVQUkVDQVRFRCwgcmVhdHRhY2hNZWRpYVN0cmVhbSB3aWxsIHNvb24gYmUgcmVtb3ZlZC4nKTtcbiAgICB0by5zcmNPYmplY3QgPSBmcm9tLnNyY09iamVjdDtcbiAgfVxufTtcblxuLy8gRXhwb3NlIHB1YmxpYyBtZXRob2RzLlxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHNoaW1QZWVyQ29ubmVjdGlvbjogZWRnZVNoaW0uc2hpbVBlZXJDb25uZWN0aW9uLFxuICBzaGltR2V0VXNlck1lZGlhOiByZXF1aXJlKCcuL2dldHVzZXJtZWRpYScpLFxuICBhdHRhY2hNZWRpYVN0cmVhbTogZWRnZVNoaW0uYXR0YWNoTWVkaWFTdHJlYW0sXG4gIHJlYXR0YWNoTWVkaWFTdHJlYW06IGVkZ2VTaGltLnJlYXR0YWNoTWVkaWFTdHJlYW1cbn07XG5cblxuXG4vLy8vLy8vLy8vLy8vLy8vLy9cbi8vIFdFQlBBQ0sgRk9PVEVSXG4vLyAuL34vbG9jYWxtZWRpYS9+L3dlYnJ0Yy1hZGFwdGVyL3NyYy9qcy9lZGdlL2VkZ2Vfc2hpbS5qc1xuLy8gbW9kdWxlIGlkID0gNDUxXG4vLyBtb2R1bGUgY2h1bmtzID0gMCJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=");

FIXME found
Open

    eval("/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar SDPUtils = __webpack_require__(452);\nvar browserDetails = __webpack_require__(460).browserDetails;\n\nvar edgeShim = {\n  shimPeerConnection: function() {\n    if (window.RTCIceGatherer) {\n      // ORTC defines an RTCIceCandidate object but no constructor.\n      // Not implemented in Edge.\n      if (!window.RTCIceCandidate) {\n        window.RTCIceCandidate = function(args) {\n          return args;\n        };\n      }\n      // ORTC does not have a session description object but\n      // other browsers (i.e. Chrome) that will support both PC and ORTC\n      // in the future might have this defined already.\n      if (!window.RTCSessionDescription) {\n        window.RTCSessionDescription = function(args) {\n          return args;\n        };\n      }\n      // this adds an additional event listener to MediaStrackTrack that signals\n      // when a tracks enabled property was changed.\n      var origMSTEnabled = Object.getOwnPropertyDescriptor(\n          MediaStreamTrack.prototype, 'enabled');\n      Object.defineProperty(MediaStreamTrack.prototype, 'enabled', {\n        set: function(value) {\n          origMSTEnabled.set.call(this, value);\n          var ev = new Event('enabled');\n          ev.enabled = value;\n          this.dispatchEvent(ev);\n        }\n      });\n    }\n\n    window.RTCPeerConnection = function(config) {\n      var self = this;\n\n      var _eventTarget = document.createDocumentFragment();\n      ['addEventListener', 'removeEventListener', 'dispatchEvent']\n          .forEach(function(method) {\n            self[method] = _eventTarget[method].bind(_eventTarget);\n          });\n\n      this.onicecandidate = null;\n      this.onaddstream = null;\n      this.ontrack = null;\n      this.onremovestream = null;\n      this.onsignalingstatechange = null;\n      this.oniceconnectionstatechange = null;\n      this.onnegotiationneeded = null;\n      this.ondatachannel = null;\n\n      this.localStreams = [];\n      this.remoteStreams = [];\n      this.getLocalStreams = function() {\n        return self.localStreams;\n      };\n      this.getRemoteStreams = function() {\n        return self.remoteStreams;\n      };\n\n      this.localDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.remoteDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.signalingState = 'stable';\n      this.iceConnectionState = 'new';\n      this.iceGatheringState = 'new';\n\n      this.iceOptions = {\n        gatherPolicy: 'all',\n        iceServers: []\n      };\n      if (config && config.iceTransportPolicy) {\n        switch (config.iceTransportPolicy) {\n          case 'all':\n          case 'relay':\n            this.iceOptions.gatherPolicy = config.iceTransportPolicy;\n            break;\n          case 'none':\n            // FIXME: remove once implementation and spec have added this.\n            throw new TypeError('iceTransportPolicy \"none\" not supported');\n          default:\n            // don't set iceTransportPolicy.\n            break;\n        }\n      }\n      this.usingBundle = config && config.bundlePolicy === 'max-bundle';\n\n      if (config && config.iceServers) {\n        // Edge does not like\n        // 1) stun:\n        // 2) turn: that does not have all of turn:host:port?transport=udp\n        // 3) turn: with ipv6 addresses\n        var iceServers = JSON.parse(JSON.stringify(config.iceServers));\n        this.iceOptions.iceServers = iceServers.filter(function(server) {\n          if (server && server.urls) {\n            var urls = server.urls;\n            if (typeof urls === 'string') {\n              urls = [urls];\n            }\n            urls = urls.filter(function(url) {\n              return (url.indexOf('turn:') === 0 &&\n                  url.indexOf('transport=udp') !== -1 &&\n                  url.indexOf('turn:[') === -1) ||\n                  (url.indexOf('stun:') === 0 &&\n                    browserDetails.version >= 14393);\n            })[0];\n            return !!urls;\n          }\n          return false;\n        });\n      }\n      this._config = config;\n\n      // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n      // everything that is needed to describe a SDP m-line.\n      this.transceivers = [];\n\n      // since the iceGatherer is currently created in createOffer but we\n      // must not emit candidates until after setLocalDescription we buffer\n      // them in this array.\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {\n      var self = this;\n      var sections = SDPUtils.splitSections(self.localDescription.sdp);\n      // FIXME: need to apply ice candidates in a way which is async but\n      // in-order\n      this._localIceCandidatesBuffer.forEach(function(event) {\n        var end = !event.candidate || Object.keys(event.candidate).length === 0;\n        if (end) {\n          for (var j = 1; j < sections.length; j++) {\n            if (sections[j].indexOf('\\r\\na=end-of-candidates\\r\\n') === -1) {\n              sections[j] += 'a=end-of-candidates\\r\\n';\n            }\n          }\n        } else if (event.candidate.candidate.indexOf('typ endOfCandidates')\n            === -1) {\n          sections[event.candidate.sdpMLineIndex + 1] +=\n              'a=' + event.candidate.candidate + '\\r\\n';\n        }\n        self.localDescription.sdp = sections.join('');\n        self.dispatchEvent(event);\n        if (self.onicecandidate !== null) {\n          self.onicecandidate(event);\n        }\n        if (!event.candidate && self.iceGatheringState !== 'complete') {\n          var complete = self.transceivers.every(function(transceiver) {\n            return transceiver.iceGatherer &&\n                transceiver.iceGatherer.state === 'completed';\n          });\n          if (complete) {\n            self.iceGatheringState = 'complete';\n          }\n        }\n      });\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype.getConfiguration = function() {\n      return this._config;\n    };\n\n    window.RTCPeerConnection.prototype.addStream = function(stream) {\n      // Clone is necessary for local demos mostly, attaching directly\n      // to two different senders does not work (build 10547).\n      var clonedStream = stream.clone();\n      stream.getTracks().forEach(function(track, idx) {\n        var clonedTrack = clonedStream.getTracks()[idx];\n        track.addEventListener('enabled', function(event) {\n          clonedTrack.enabled = event.enabled;\n        });\n      });\n      this.localStreams.push(clonedStream);\n      this._maybeFireNegotiationNeeded();\n    };\n\n    window.RTCPeerConnection.prototype.removeStream = function(stream) {\n      var idx = this.localStreams.indexOf(stream);\n      if (idx > -1) {\n        this.localStreams.splice(idx, 1);\n        this._maybeFireNegotiationNeeded();\n      }\n    };\n\n    window.RTCPeerConnection.prototype.getSenders = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpSender;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpSender;\n      });\n    };\n\n    window.RTCPeerConnection.prototype.getReceivers = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpReceiver;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpReceiver;\n      });\n    };\n\n    // Determines the intersection of local and remote capabilities.\n    window.RTCPeerConnection.prototype._getCommonCapabilities =\n        function(localCapabilities, remoteCapabilities) {\n          var commonCapabilities = {\n            codecs: [],\n            headerExtensions: [],\n            fecMechanisms: []\n          };\n          localCapabilities.codecs.forEach(function(lCodec) {\n            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n              var rCodec = remoteCapabilities.codecs[i];\n              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n                  lCodec.clockRate === rCodec.clockRate) {\n                // number of channels is the highest common number of channels\n                rCodec.numChannels = Math.min(lCodec.numChannels,\n                    rCodec.numChannels);\n                // push rCodec so we reply with offerer payload type\n                commonCapabilities.codecs.push(rCodec);\n\n                // determine common feedback mechanisms\n                rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {\n                  for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {\n                    if (lCodec.rtcpFeedback[j].type === fb.type &&\n                        lCodec.rtcpFeedback[j].parameter === fb.parameter) {\n                      return true;\n                    }\n                  }\n                  return false;\n                });\n                // FIXME: also need to determine .parameters\n                //  see https://github.com/openpeer/ortc/issues/569\n                break;\n              }\n            }\n          });\n\n          localCapabilities.headerExtensions\n              .forEach(function(lHeaderExtension) {\n                for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n                     i++) {\n                  var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n                  if (lHeaderExtension.uri === rHeaderExtension.uri) {\n                    commonCapabilities.headerExtensions.push(rHeaderExtension);\n                    break;\n                  }\n                }\n              });\n\n          // FIXME: fecMechanisms\n          return commonCapabilities;\n        };\n\n    // Create ICE gatherer, ICE transport and DTLS transport.\n    window.RTCPeerConnection.prototype._createIceAndDtlsTransports =\n        function(mid, sdpMLineIndex) {\n          var self = this;\n          var iceGatherer = new RTCIceGatherer(self.iceOptions);\n          var iceTransport = new RTCIceTransport(iceGatherer);\n          iceGatherer.onlocalcandidate = function(evt) {\n            var event = new Event('icecandidate');\n            event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n            var cand = evt.candidate;\n            var end = !cand || Object.keys(cand).length === 0;\n            // Edge emits an empty object for RTCIceCandidateComplete‥\n            if (end) {\n              // polyfill since RTCIceGatherer.state is not implemented in\n              // Edge 10547 yet.\n              if (iceGatherer.state === undefined) {\n                iceGatherer.state = 'completed';\n              }\n\n              // Emit a candidate with type endOfCandidates to make the samples\n              // work. Edge requires addIceCandidate with this empty candidate\n              // to start checking. The real solution is to signal\n              // end-of-candidates to the other side when getting the null\n              // candidate but some apps (like the samples) don't do that.\n              event.candidate.candidate =\n                  'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';\n            } else {\n              // RTCIceCandidate doesn't have a component, needs to be added\n              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;\n              event.candidate.candidate = SDPUtils.writeCandidate(cand);\n            }\n\n            // update local description.\n            var sections = SDPUtils.splitSections(self.localDescription.sdp);\n            if (event.candidate.candidate.indexOf('typ endOfCandidates')\n                === -1) {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=' + event.candidate.candidate + '\\r\\n';\n            } else {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=end-of-candidates\\r\\n';\n            }\n            self.localDescription.sdp = sections.join('');\n\n            var complete = self.transceivers.every(function(transceiver) {\n              return transceiver.iceGatherer &&\n                  transceiver.iceGatherer.state === 'completed';\n            });\n\n            // Emit candidate if localDescription is set.\n            // Also emits null candidate when all gatherers are complete.\n            switch (self.iceGatheringState) {\n              case 'new':\n                self._localIceCandidatesBuffer.push(event);\n                if (end && complete) {\n                  self._localIceCandidatesBuffer.push(\n                      new Event('icecandidate'));\n                }\n                break;\n              case 'gathering':\n                self._emitBufferedCandidates();\n                self.dispatchEvent(event);\n                if (self.onicecandidate !== null) {\n                  self.onicecandidate(event);\n                }\n                if (complete) {\n                  self.dispatchEvent(new Event('icecandidate'));\n                  if (self.onicecandidate !== null) {\n                    self.onicecandidate(new Event('icecandidate'));\n                  }\n                  self.iceGatheringState = 'complete';\n                }\n                break;\n              case 'complete':\n                // should not happen... currently!\n                break;\n              default: // no-op.\n                break;\n            }\n          };\n          iceTransport.onicestatechange = function() {\n            self._updateConnectionState();\n          };\n\n          var dtlsTransport = new RTCDtlsTransport(iceTransport);\n          dtlsTransport.ondtlsstatechange = function() {\n            self._updateConnectionState();\n          };\n          dtlsTransport.onerror = function() {\n            // onerror does not set state to failed by itself.\n            dtlsTransport.state = 'failed';\n            self._updateConnectionState();\n          };\n\n          return {\n            iceGatherer: iceGatherer,\n            iceTransport: iceTransport,\n            dtlsTransport: dtlsTransport\n          };\n        };\n\n    // Start the RTP Sender and Receiver for a transceiver.\n    window.RTCPeerConnection.prototype._transceive = function(transceiver,\n        send, recv) {\n      var params = this._getCommonCapabilities(transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n      if (send && transceiver.rtpSender) {\n        params.encodings = transceiver.sendEncodingParameters;\n        params.rtcp = {\n          cname: SDPUtils.localCName\n        };\n        if (transceiver.recvEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpSender.send(params);\n      }\n      if (recv && transceiver.rtpReceiver) {\n        // remove RTX field in Edge 14942\n        if (transceiver.kind === 'video'\n            && transceiver.recvEncodingParameters) {\n          transceiver.recvEncodingParameters.forEach(function(p) {\n            delete p.rtx;\n          });\n        }\n        params.encodings = transceiver.recvEncodingParameters;\n        params.rtcp = {\n          cname: transceiver.cname\n        };\n        if (transceiver.sendEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpReceiver.receive(params);\n      }\n    };\n\n    window.RTCPeerConnection.prototype.setLocalDescription =\n        function(description) {\n          var self = this;\n          var sections;\n          var sessionpart;\n          if (description.type === 'offer') {\n            // FIXME: What was the purpose of this empty if statement?\n            // if (!this._pendingOffer) {\n            // } else {\n            if (this._pendingOffer) {\n              // VERY limited support for SDP munging. Limited to:\n              // * changing the order of codecs\n              sections = SDPUtils.splitSections(description.sdp);\n              sessionpart = sections.shift();\n              sections.forEach(function(mediaSection, sdpMLineIndex) {\n                var caps = SDPUtils.parseRtpParameters(mediaSection);\n                self._pendingOffer[sdpMLineIndex].localCapabilities = caps;\n              });\n              this.transceivers = this._pendingOffer;\n              delete this._pendingOffer;\n            }\n          } else if (description.type === 'answer') {\n            sections = SDPUtils.splitSections(self.remoteDescription.sdp);\n            sessionpart = sections.shift();\n            var isIceLite = SDPUtils.matchPrefix(sessionpart,\n                'a=ice-lite').length > 0;\n            sections.forEach(function(mediaSection, sdpMLineIndex) {\n              var transceiver = self.transceivers[sdpMLineIndex];\n              var iceGatherer = transceiver.iceGatherer;\n              var iceTransport = transceiver.iceTransport;\n              var dtlsTransport = transceiver.dtlsTransport;\n              var localCapabilities = transceiver.localCapabilities;\n              var remoteCapabilities = transceiver.remoteCapabilities;\n\n              var rejected = mediaSection.split('\\n', 1)[0]\n                  .split(' ', 2)[1] === '0';\n\n              if (!rejected && !transceiver.isDatachannel) {\n                var remoteIceParameters = SDPUtils.getIceParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                  .map(function(cand) {\n                    return SDPUtils.parseCandidate(cand);\n                  })\n                  .filter(function(cand) {\n                    return cand.component === '1';\n                  });\n                  // ice-lite only includes host candidates in the SDP so we can\n                  // use setRemoteCandidates (which implies an\n                  // RTCIceCandidateComplete)\n                  if (cands.length) {\n                    iceTransport.setRemoteCandidates(cands);\n                  }\n                }\n                var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  remoteDtlsParameters.role = 'server';\n                }\n\n                if (!self.usingBundle || sdpMLineIndex === 0) {\n                  iceTransport.start(iceGatherer, remoteIceParameters,\n                      isIceLite ? 'controlling' : 'controlled');\n                  dtlsTransport.start(remoteDtlsParameters);\n                }\n\n                // Calculate intersection of capabilities.\n                var params = self._getCommonCapabilities(localCapabilities,\n                    remoteCapabilities);\n\n                // Start the RTCRtpSender. The RTCRtpReceiver for this\n                // transceiver has already been started in setRemoteDescription.\n                self._transceive(transceiver,\n                    params.codecs.length > 0,\n                    false);\n              }\n            });\n          }\n\n          this.localDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-local-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n\n          // If a success callback was provided, emit ICE candidates after it\n          // has been executed. Otherwise, emit callback after the Promise is\n          // resolved.\n          var hasCallback = arguments.length > 1 &&\n            typeof arguments[1] === 'function';\n          if (hasCallback) {\n            var cb = arguments[1];\n            window.setTimeout(function() {\n              cb();\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              self._emitBufferedCandidates();\n            }, 0);\n          }\n          var p = Promise.resolve();\n          p.then(function() {\n            if (!hasCallback) {\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              // Usually candidates will be emitted earlier.\n              window.setTimeout(self._emitBufferedCandidates.bind(self), 500);\n            }\n          });\n          return p;\n        };\n\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n        function(description) {\n          var self = this;\n          var stream = new MediaStream();\n          var receiverList = [];\n          var sections = SDPUtils.splitSections(description.sdp);\n          var sessionpart = sections.shift();\n          var isIceLite = SDPUtils.matchPrefix(sessionpart,\n              'a=ice-lite').length > 0;\n          this.usingBundle = SDPUtils.matchPrefix(sessionpart,\n              'a=group:BUNDLE ').length > 0;\n          sections.forEach(function(mediaSection, sdpMLineIndex) {\n            var lines = SDPUtils.splitLines(mediaSection);\n            var mline = lines[0].substr(2).split(' ');\n            var kind = mline[0];\n            var rejected = mline[1] === '0';\n            var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n\n            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');\n            if (mid.length) {\n              mid = mid[0].substr(6);\n            } else {\n              mid = SDPUtils.generateIdentifier();\n            }\n\n            // Reject datachannels which are not implemented yet.\n            if (kind === 'application' && mline[2] === 'DTLS/SCTP') {\n              self.transceivers[sdpMLineIndex] = {\n                mid: mid,\n                isDatachannel: true\n              };\n              return;\n            }\n\n            var transceiver;\n            var iceGatherer;\n            var iceTransport;\n            var dtlsTransport;\n            var rtpSender;\n            var rtpReceiver;\n            var sendEncodingParameters;\n            var recvEncodingParameters;\n            var localCapabilities;\n\n            var track;\n            // FIXME: ensure the mediaSection has rtcp-mux set.\n            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n            var remoteIceParameters;\n            var remoteDtlsParameters;\n            if (!rejected) {\n              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters.role = 'client';\n            }\n            recvEncodingParameters =\n                SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n            var cname;\n            // Gets the first SSRC. Note that with RTX there might be multiple\n            // SSRCs.\n            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n                .map(function(line) {\n                  return SDPUtils.parseSsrcMedia(line);\n                })\n                .filter(function(obj) {\n                  return obj.attribute === 'cname';\n                })[0];\n            if (remoteSsrc) {\n              cname = remoteSsrc.value;\n            }\n\n            var isComplete = SDPUtils.matchPrefix(mediaSection,\n                'a=end-of-candidates', sessionpart).length > 0;\n            var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                .map(function(cand) {\n                  return SDPUtils.parseCandidate(cand);\n                })\n                .filter(function(cand) {\n                  return cand.component === '1';\n                });\n            if (description.type === 'offer' && !rejected) {\n              var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n                iceGatherer: self.transceivers[0].iceGatherer,\n                iceTransport: self.transceivers[0].iceTransport,\n                dtlsTransport: self.transceivers[0].dtlsTransport\n              } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n              if (isComplete) {\n                transports.iceTransport.setRemoteCandidates(cands);\n              }\n\n              localCapabilities = RTCRtpReceiver.getCapabilities(kind);\n\n              // filter RTX until additional stuff needed for RTX is implemented\n              // in adapter.js\n              localCapabilities.codecs = localCapabilities.codecs.filter(\n                  function(codec) {\n                    return codec.name !== 'rtx';\n                  });\n\n              sendEncodingParameters = [{\n                ssrc: (2 * sdpMLineIndex + 2) * 1001\n              }];\n\n              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n\n              track = rtpReceiver.track;\n              receiverList.push([track, rtpReceiver]);\n              // FIXME: not correct when there are multiple streams but that is\n              // not currently supported in this shim.\n              stream.addTrack(track);\n\n              // FIXME: look at direction.\n              if (self.localStreams.length > 0 &&\n                  self.localStreams[0].getTracks().length >= sdpMLineIndex) {\n                var localTrack;\n                if (kind === 'audio') {\n                  localTrack = self.localStreams[0].getAudioTracks()[0];\n                } else if (kind === 'video') {\n                  localTrack = self.localStreams[0].getVideoTracks()[0];\n                }\n                if (localTrack) {\n                  rtpSender = new RTCRtpSender(localTrack,\n                      transports.dtlsTransport);\n                }\n              }\n\n              self.transceivers[sdpMLineIndex] = {\n                iceGatherer: transports.iceGatherer,\n                iceTransport: transports.iceTransport,\n                dtlsTransport: transports.dtlsTransport,\n                localCapabilities: localCapabilities,\n                remoteCapabilities: remoteCapabilities,\n                rtpSender: rtpSender,\n                rtpReceiver: rtpReceiver,\n                kind: kind,\n                mid: mid,\n                cname: cname,\n                sendEncodingParameters: sendEncodingParameters,\n                recvEncodingParameters: recvEncodingParameters\n              };\n              // Start the RTCRtpReceiver now. The RTPSender is started in\n              // setLocalDescription.\n              self._transceive(self.transceivers[sdpMLineIndex],\n                  false,\n                  direction === 'sendrecv' || direction === 'sendonly');\n            } else if (description.type === 'answer' && !rejected) {\n              transceiver = self.transceivers[sdpMLineIndex];\n              iceGatherer = transceiver.iceGatherer;\n              iceTransport = transceiver.iceTransport;\n              dtlsTransport = transceiver.dtlsTransport;\n              rtpSender = transceiver.rtpSender;\n              rtpReceiver = transceiver.rtpReceiver;\n              sendEncodingParameters = transceiver.sendEncodingParameters;\n              localCapabilities = transceiver.localCapabilities;\n\n              self.transceivers[sdpMLineIndex].recvEncodingParameters =\n                  recvEncodingParameters;\n              self.transceivers[sdpMLineIndex].remoteCapabilities =\n                  remoteCapabilities;\n              self.transceivers[sdpMLineIndex].cname = cname;\n\n              if ((isIceLite || isComplete) && cands.length) {\n                iceTransport.setRemoteCandidates(cands);\n              }\n              if (!self.usingBundle || sdpMLineIndex === 0) {\n                iceTransport.start(iceGatherer, remoteIceParameters,\n                    'controlling');\n                dtlsTransport.start(remoteDtlsParameters);\n              }\n\n              self._transceive(transceiver,\n                  direction === 'sendrecv' || direction === 'recvonly',\n                  direction === 'sendrecv' || direction === 'sendonly');\n\n              if (rtpReceiver &&\n                  (direction === 'sendrecv' || direction === 'sendonly')) {\n                track = rtpReceiver.track;\n                receiverList.push([track, rtpReceiver]);\n                stream.addTrack(track);\n              } else {\n                // FIXME: actually the receiver should be created later.\n                delete transceiver.rtpReceiver;\n              }\n            }\n          });\n\n          this.remoteDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-remote-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n          if (stream.getTracks().length) {\n            self.remoteStreams.push(stream);\n            window.setTimeout(function() {\n              var event = new Event('addstream');\n              event.stream = stream;\n              self.dispatchEvent(event);\n              if (self.onaddstream !== null) {\n                window.setTimeout(function() {\n                  self.onaddstream(event);\n                }, 0);\n              }\n\n              receiverList.forEach(function(item) {\n                var track = item[0];\n                var receiver = item[1];\n                var trackEvent = new Event('track');\n                trackEvent.track = track;\n                trackEvent.receiver = receiver;\n                trackEvent.streams = [stream];\n                self.dispatchEvent(event);\n                if (self.ontrack !== null) {\n                  window.setTimeout(function() {\n                    self.ontrack(trackEvent);\n                  }, 0);\n                }\n              });\n            }, 0);\n          }\n          if (arguments.length > 1 && typeof arguments[1] === 'function') {\n            window.setTimeout(arguments[1], 0);\n          }\n          return Promise.resolve();\n        };\n\n    window.RTCPeerConnection.prototype.close = function() {\n      this.transceivers.forEach(function(transceiver) {\n        /* not yet\n        if (transceiver.iceGatherer) {\n          transceiver.iceGatherer.close();\n        }\n        */\n        if (transceiver.iceTransport) {\n          transceiver.iceTransport.stop();\n        }\n        if (transceiver.dtlsTransport) {\n          transceiver.dtlsTransport.stop();\n        }\n        if (transceiver.rtpSender) {\n          transceiver.rtpSender.stop();\n        }\n        if (transceiver.rtpReceiver) {\n          transceiver.rtpReceiver.stop();\n        }\n      });\n      // FIXME: clean up tracks, local streams, remote streams, etc\n      this._updateSignalingState('closed');\n    };\n\n    // Update the signaling state.\n    window.RTCPeerConnection.prototype._updateSignalingState =\n        function(newState) {\n          this.signalingState = newState;\n          var event = new Event('signalingstatechange');\n          this.dispatchEvent(event);\n          if (this.onsignalingstatechange !== null) {\n            this.onsignalingstatechange(event);\n          }\n        };\n\n    // Determine whether to fire the negotiationneeded event.\n    window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =\n        function() {\n          // Fire away (for now).\n          var event = new Event('negotiationneeded');\n          this.dispatchEvent(event);\n          if (this.onnegotiationneeded !== null) {\n            this.onnegotiationneeded(event);\n          }\n        };\n\n    // Update the connection state.\n    window.RTCPeerConnection.prototype._updateConnectionState = function() {\n      var self = this;\n      var newState;\n      var states = {\n        'new': 0,\n        closed: 0,\n        connecting: 0,\n        checking: 0,\n        connected: 0,\n        completed: 0,\n        failed: 0\n      };\n      this.transceivers.forEach(function(transceiver) {\n        states[transceiver.iceTransport.state]++;\n        states[transceiver.dtlsTransport.state]++;\n      });\n      // ICETransport.completed and connected are the same for this purpose.\n      states.connected += states.completed;\n\n      newState = 'new';\n      if (states.failed > 0) {\n        newState = 'failed';\n      } else if (states.connecting > 0 || states.checking > 0) {\n        newState = 'connecting';\n      } else if (states.disconnected > 0) {\n        newState = 'disconnected';\n      } else if (states.new > 0) {\n        newState = 'new';\n      } else if (states.connected > 0 || states.completed > 0) {\n        newState = 'connected';\n      }\n\n      if (newState !== self.iceConnectionState) {\n        self.iceConnectionState = newState;\n        var event = new Event('iceconnectionstatechange');\n        this.dispatchEvent(event);\n        if (this.oniceconnectionstatechange !== null) {\n          this.oniceconnectionstatechange(event);\n        }\n      }\n    };\n\n    window.RTCPeerConnection.prototype.createOffer = function() {\n      var self = this;\n      if (this._pendingOffer) {\n        throw new Error('createOffer called while there is a pending offer.');\n      }\n      var offerOptions;\n      if (arguments.length === 1 && typeof arguments[0] !== 'function') {\n        offerOptions = arguments[0];\n      } else if (arguments.length === 3) {\n        offerOptions = arguments[2];\n      }\n\n      var tracks = [];\n      var numAudioTracks = 0;\n      var numVideoTracks = 0;\n      // Default to sendrecv.\n      if (this.localStreams.length) {\n        numAudioTracks = this.localStreams[0].getAudioTracks().length;\n        numVideoTracks = this.localStreams[0].getVideoTracks().length;\n      }\n      // Determine number of audio and video tracks we need to send/recv.\n      if (offerOptions) {\n        // Reject Chrome legacy constraints.\n        if (offerOptions.mandatory || offerOptions.optional) {\n          throw new TypeError(\n              'Legacy mandatory/optional constraints not supported.');\n        }\n        if (offerOptions.offerToReceiveAudio !== undefined) {\n          numAudioTracks = offerOptions.offerToReceiveAudio;\n        }\n        if (offerOptions.offerToReceiveVideo !== undefined) {\n          numVideoTracks = offerOptions.offerToReceiveVideo;\n        }\n      }\n      if (this.localStreams.length) {\n        // Push local streams.\n        this.localStreams[0].getTracks().forEach(function(track) {\n          tracks.push({\n            kind: track.kind,\n            track: track,\n            wantReceive: track.kind === 'audio' ?\n                numAudioTracks > 0 : numVideoTracks > 0\n          });\n          if (track.kind === 'audio') {\n            numAudioTracks--;\n          } else if (track.kind === 'video') {\n            numVideoTracks--;\n          }\n        });\n      }\n      // Create M-lines for recvonly streams.\n      while (numAudioTracks > 0 || numVideoTracks > 0) {\n        if (numAudioTracks > 0) {\n          tracks.push({\n            kind: 'audio',\n            wantReceive: true\n          });\n          numAudioTracks--;\n        }\n        if (numVideoTracks > 0) {\n          tracks.push({\n            kind: 'video',\n            wantReceive: true\n          });\n          numVideoTracks--;\n        }\n      }\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      var transceivers = [];\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        // For each track, create an ice gatherer, ice transport,\n        // dtls transport, potentially rtpsender and rtpreceiver.\n        var track = mline.track;\n        var kind = mline.kind;\n        var mid = SDPUtils.generateIdentifier();\n\n        var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n          iceGatherer: transceivers[0].iceGatherer,\n          iceTransport: transceivers[0].iceTransport,\n          dtlsTransport: transceivers[0].dtlsTransport\n        } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n        var localCapabilities = RTCRtpSender.getCapabilities(kind);\n        // filter RTX until additional stuff needed for RTX is implemented\n        // in adapter.js\n        localCapabilities.codecs = localCapabilities.codecs.filter(\n            function(codec) {\n              return codec.name !== 'rtx';\n            });\n        localCapabilities.codecs.forEach(function(codec) {\n          // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552\n          // by adding level-asymmetry-allowed=1\n          if (codec.name === 'H264' &&\n              codec.parameters['level-asymmetry-allowed'] === undefined) {\n            codec.parameters['level-asymmetry-allowed'] = '1';\n          }\n        });\n\n        var rtpSender;\n        var rtpReceiver;\n\n        // generate an ssrc now, to be used later in rtpSender.send\n        var sendEncodingParameters = [{\n          ssrc: (2 * sdpMLineIndex + 1) * 1001\n        }];\n        if (track) {\n          rtpSender = new RTCRtpSender(track, transports.dtlsTransport);\n        }\n\n        if (mline.wantReceive) {\n          rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n        }\n\n        transceivers[sdpMLineIndex] = {\n          iceGatherer: transports.iceGatherer,\n          iceTransport: transports.iceTransport,\n          dtlsTransport: transports.dtlsTransport,\n          localCapabilities: localCapabilities,\n          remoteCapabilities: null,\n          rtpSender: rtpSender,\n          rtpReceiver: rtpReceiver,\n          kind: kind,\n          mid: mid,\n          sendEncodingParameters: sendEncodingParameters,\n          recvEncodingParameters: null\n        };\n      });\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        var transceiver = transceivers[sdpMLineIndex];\n        sdp += SDPUtils.writeMediaSection(transceiver,\n            transceiver.localCapabilities, 'offer', self.localStreams[0]);\n      });\n\n      this._pendingOffer = transceivers;\n      var desc = new RTCSessionDescription({\n        type: 'offer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.createAnswer = function() {\n      var self = this;\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      this.transceivers.forEach(function(transceiver) {\n        if (transceiver.isDatachannel) {\n          sdp += 'm=application 0 DTLS/SCTP 5000\\r\\n' +\n              'c=IN IP4 0.0.0.0\\r\\n' +\n              'a=mid:' + transceiver.mid + '\\r\\n';\n          return;\n        }\n        // Calculate intersection of capabilities.\n        var commonCapabilities = self._getCommonCapabilities(\n            transceiver.localCapabilities,\n            transceiver.remoteCapabilities);\n\n        sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,\n            'answer', self.localStreams[0]);\n      });\n\n      var desc = new RTCSessionDescription({\n        type: 'answer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n      if (!candidate) {\n        this.transceivers.forEach(function(transceiver) {\n          transceiver.iceTransport.addRemoteCandidate({});\n        });\n      } else {\n        var mLineIndex = candidate.sdpMLineIndex;\n        if (candidate.sdpMid) {\n          for (var i = 0; i < this.transceivers.length; i++) {\n            if (this.transceivers[i].mid === candidate.sdpMid) {\n              mLineIndex = i;\n              break;\n            }\n          }\n        }\n        var transceiver = this.transceivers[mLineIndex];\n        if (transceiver) {\n          var cand = Object.keys(candidate.candidate).length > 0 ?\n              SDPUtils.parseCandidate(candidate.candidate) : {};\n          // Ignore Chrome's invalid candidates since Edge does not like them.\n          if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {\n            return;\n          }\n          // Ignore RTCP candidates, we assume RTCP-MUX.\n          if (cand.component !== '1') {\n            return;\n          }\n          // A dirty hack to make samples work.\n          if (cand.type === 'endOfCandidates') {\n            cand = {};\n          }\n          transceiver.iceTransport.addRemoteCandidate(cand);\n\n          // update the remoteDescription.\n          var sections = SDPUtils.splitSections(this.remoteDescription.sdp);\n          sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()\n              : 'a=end-of-candidates') + '\\r\\n';\n          this.remoteDescription.sdp = sections.join('');\n        }\n      }\n      if (arguments.length > 1 && typeof arguments[1] === 'function') {\n        window.setTimeout(arguments[1], 0);\n      }\n      return Promise.resolve();\n    };\n\n    window.RTCPeerConnection.prototype.getStats = function() {\n      var promises = [];\n      this.transceivers.forEach(function(transceiver) {\n        ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n            'dtlsTransport'].forEach(function(method) {\n              if (transceiver[method]) {\n                promises.push(transceiver[method].getStats());\n              }\n            });\n      });\n      var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&\n          arguments[1];\n      return new Promise(function(resolve) {\n        // shim getStats with maplike support\n        var results = new Map();\n        Promise.all(promises).then(function(res) {\n          res.forEach(function(result) {\n            Object.keys(result).forEach(function(id) {\n              results.set(id, result[id]);\n              results[id] = result[id];\n            });\n          });\n          if (cb) {\n            window.setTimeout(cb, 0, results);\n          }\n          resolve(results);\n        });\n      });\n    };\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimPeerConnection: edgeShim.shimPeerConnection,\n  shimGetUserMedia: __webpack_require__(464)\n};\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDYzLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy93ZWJydGMtYWRhcHRlci9zcmMvanMvZWRnZS9lZGdlX3NoaW0uanMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFdlYlJUQyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG4gLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbnZhciBTRFBVdGlscyA9IHJlcXVpcmUoJ3NkcCcpO1xudmFyIGJyb3dzZXJEZXRhaWxzID0gcmVxdWlyZSgnLi4vdXRpbHMnKS5icm93c2VyRGV0YWlscztcblxudmFyIGVkZ2VTaGltID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGZ1bmN0aW9uKCkge1xuICAgIGlmICh3aW5kb3cuUlRDSWNlR2F0aGVyZXIpIHtcbiAgICAgIC8vIE9SVEMgZGVmaW5lcyBhbiBSVENJY2VDYW5kaWRhdGUgb2JqZWN0IGJ1dCBubyBjb25zdHJ1Y3Rvci5cbiAgICAgIC8vIE5vdCBpbXBsZW1lbnRlZCBpbiBFZGdlLlxuICAgICAgaWYgKCF3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlKSB7XG4gICAgICAgIHdpbmRvdy5SVENJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbihhcmdzKSB7XG4gICAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICAvLyBPUlRDIGRvZXMgbm90IGhhdmUgYSBzZXNzaW9uIGRlc2NyaXB0aW9uIG9iamVjdCBidXRcbiAgICAgIC8vIG90aGVyIGJyb3dzZXJzIChpLmUuIENocm9tZSkgdGhhdCB3aWxsIHN1cHBvcnQgYm90aCBQQyBhbmQgT1JUQ1xuICAgICAgLy8gaW4gdGhlIGZ1dHVyZSBtaWdodCBoYXZlIHRoaXMgZGVmaW5lZCBhbHJlYWR5LlxuICAgICAgaWYgKCF3aW5kb3cuUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKSB7XG4gICAgICAgIHdpbmRvdy5SVENTZXNzaW9uRGVzY3JpcHRpb24gPSBmdW5jdGlvbihhcmdzKSB7XG4gICAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICAvLyB0aGlzIGFkZHMgYW4gYWRkaXRpb25hbCBldmVudCBsaXN0ZW5lciB0byBNZWRpYVN0cmFja1RyYWNrIHRoYXQgc2lnbmFsc1xuICAgICAgLy8gd2hlbiBhIHRyYWNrcyBlbmFibGVkIHByb3BlcnR5IHdhcyBjaGFuZ2VkLlxuICAgICAgdmFyIG9yaWdNU1RFbmFibGVkID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihcbiAgICAgICAgICBNZWRpYVN0cmVhbVRyYWNrLnByb3RvdHlwZSwgJ2VuYWJsZWQnKTtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShNZWRpYVN0cmVhbVRyYWNrLnByb3RvdHlwZSwgJ2VuYWJsZWQnLCB7XG4gICAgICAgIHNldDogZnVuY3Rpb24odmFsdWUpIHtcbiAgICAgICAgICBvcmlnTVNURW5hYmxlZC5zZXQuY2FsbCh0aGlzLCB2YWx1ZSk7XG4gICAgICAgICAgdmFyIGV2ID0gbmV3IEV2ZW50KCdlbmFibGVkJyk7XG4gICAgICAgICAgZXYuZW5hYmxlZCA9IHZhbHVlO1xuICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiA9IGZ1bmN0aW9uKGNvbmZpZykge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgICB2YXIgX2V2ZW50VGFyZ2V0ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xuICAgICAgWydhZGRFdmVudExpc3RlbmVyJywgJ3JlbW92ZUV2ZW50TGlzdGVuZXInLCAnZGlzcGF0Y2hFdmVudCddXG4gICAgICAgICAgLmZvckVhY2goZnVuY3Rpb24obWV0aG9kKSB7XG4gICAgICAgICAgICBzZWxmW21ldGhvZF0gPSBfZXZlbnRUYXJnZXRbbWV0aG9kXS5iaW5kKF9ldmVudFRhcmdldCk7XG4gICAgICAgICAgfSk7XG5cbiAgICAgIHRoaXMub25pY2VjYW5kaWRhdGUgPSBudWxsO1xuICAgICAgdGhpcy5vbmFkZHN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9udHJhY2sgPSBudWxsO1xuICAgICAgdGhpcy5vbnJlbW92ZXN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBudWxsO1xuICAgICAgdGhpcy5vbmRhdGFjaGFubmVsID0gbnVsbDtcblxuICAgICAgdGhpcy5sb2NhbFN0cmVhbXMgPSBbXTtcbiAgICAgIHRoaXMucmVtb3RlU3RyZWFtcyA9IFtdO1xuICAgICAgdGhpcy5nZXRMb2NhbFN0cmVhbXMgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYubG9jYWxTdHJlYW1zO1xuICAgICAgfTtcbiAgICAgIHRoaXMuZ2V0UmVtb3RlU3RyZWFtcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gc2VsZi5yZW1vdGVTdHJlYW1zO1xuICAgICAgfTtcblxuICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICcnLFxuICAgICAgICBzZHA6ICcnXG4gICAgICB9KTtcbiAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24gPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJycsXG4gICAgICAgIHNkcDogJydcbiAgICAgIH0pO1xuICAgICAgdGhpcy5zaWduYWxpbmdTdGF0ZSA9ICdzdGFibGUnO1xuICAgICAgdGhpcy5pY2VDb25uZWN0aW9uU3RhdGUgPSAnbmV3JztcbiAgICAgIHRoaXMuaWNlR2F0aGVyaW5nU3RhdGUgPSAnbmV3JztcblxuICAgICAgdGhpcy5pY2VPcHRpb25zID0ge1xuICAgICAgICBnYXRoZXJQb2xpY3k6ICdhbGwnLFxuICAgICAgICBpY2VTZXJ2ZXJzOiBbXVxuICAgICAgfTtcbiAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmljZVRyYW5zcG9ydFBvbGljeSkge1xuICAgICAgICBzd2l0Y2ggKGNvbmZpZy5pY2VUcmFuc3BvcnRQb2xpY3kpIHtcbiAgICAgICAgICBjYXNlICdhbGwnOlxuICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgIHRoaXMuaWNlT3B0aW9ucy5nYXRoZXJQb2xpY3kgPSBjb25maWcuaWNlVHJhbnNwb3J0UG9saWN5O1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnbm9uZSc6XG4gICAgICAgICAgICAvLyBGSVhNRTogcmVtb3ZlIG9uY2UgaW1wbGVtZW50YXRpb24gYW5kIHNwZWMgaGF2ZSBhZGRlZCB0aGlzLlxuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignaWNlVHJhbnNwb3J0UG9saWN5IFwibm9uZVwiIG5vdCBzdXBwb3J0ZWQnKTtcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgLy8gZG9uJ3Qgc2V0IGljZVRyYW5zcG9ydFBvbGljeS5cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLnVzaW5nQnVuZGxlID0gY29uZmlnICYmIGNvbmZpZy5idW5kbGVQb2xpY3kgPT09ICdtYXgtYnVuZGxlJztcblxuICAgICAgaWYgKGNvbmZpZyAmJiBjb25maWcuaWNlU2VydmVycykge1xuICAgICAgICAvLyBFZGdlIGRvZXMgbm90IGxpa2VcbiAgICAgICAgLy8gMSkgc3R1bjpcbiAgICAgICAgLy8gMikgdHVybjogdGhhdCBkb2VzIG5vdCBoYXZlIGFsbCBvZiB0dXJuOmhvc3Q6cG9ydD90cmFuc3BvcnQ9dWRwXG4gICAgICAgIC8vIDMpIHR1cm46IHdpdGggaXB2NiBhZGRyZXNzZXNcbiAgICAgICAgdmFyIGljZVNlcnZlcnMgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGNvbmZpZy5pY2VTZXJ2ZXJzKSk7XG4gICAgICAgIHRoaXMuaWNlT3B0aW9ucy5pY2VTZXJ2ZXJzID0gaWNlU2VydmVycy5maWx0ZXIoZnVuY3Rpb24oc2VydmVyKSB7XG4gICAgICAgICAgaWYgKHNlcnZlciAmJiBzZXJ2ZXIudXJscykge1xuICAgICAgICAgICAgdmFyIHVybHMgPSBzZXJ2ZXIudXJscztcbiAgICAgICAgICAgIGlmICh0eXBlb2YgdXJscyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgdXJscyA9IFt1cmxzXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHVybHMgPSB1cmxzLmZpbHRlcihmdW5jdGlvbih1cmwpIHtcbiAgICAgICAgICAgICAgcmV0dXJuICh1cmwuaW5kZXhPZigndHVybjonKSA9PT0gMCAmJlxuICAgICAgICAgICAgICAgICAgdXJsLmluZGV4T2YoJ3RyYW5zcG9ydD11ZHAnKSAhPT0gLTEgJiZcbiAgICAgICAgICAgICAgICAgIHVybC5pbmRleE9mKCd0dXJuOlsnKSA9PT0gLTEpIHx8XG4gICAgICAgICAgICAgICAgICAodXJsLmluZGV4T2YoJ3N0dW46JykgPT09IDAgJiZcbiAgICAgICAgICAgICAgICAgICAgYnJvd3NlckRldGFpbHMudmVyc2lvbiA+PSAxNDM5Myk7XG4gICAgICAgICAgICB9KVswXTtcbiAgICAgICAgICAgIHJldHVybiAhIXVybHM7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aGlzLl9jb25maWcgPSBjb25maWc7XG5cbiAgICAgIC8vIHBlci10cmFjayBpY2VHYXRoZXJzLCBpY2VUcmFuc3BvcnRzLCBkdGxzVHJhbnNwb3J0cywgcnRwU2VuZGVycywgLi4uXG4gICAgICAvLyBldmVyeXRoaW5nIHRoYXQgaXMgbmVlZGVkIHRvIGRlc2NyaWJlIGEgU0RQIG0tbGluZS5cbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzID0gW107XG5cbiAgICAgIC8vIHNpbmNlIHRoZSBpY2VHYXRoZXJlciBpcyBjdXJyZW50bHkgY3JlYXRlZCBpbiBjcmVhdGVPZmZlciBidXQgd2VcbiAgICAgIC8vIG11c3Qgbm90IGVtaXQgY2FuZGlkYXRlcyB1bnRpbCBhZnRlciBzZXRMb2NhbERlc2NyaXB0aW9uIHdlIGJ1ZmZlclxuICAgICAgLy8gdGhlbSBpbiB0aGlzIGFycmF5LlxuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAvLyBGSVhNRTogbmVlZCB0byBhcHBseSBpY2UgY2FuZGlkYXRlcyBpbiBhIHdheSB3aGljaCBpcyBhc3luYyBidXRcbiAgICAgIC8vIGluLW9yZGVyXG4gICAgICB0aGlzLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIuZm9yRWFjaChmdW5jdGlvbihldmVudCkge1xuICAgICAgICB2YXIgZW5kID0gIWV2ZW50LmNhbmRpZGF0ZSB8fCBPYmplY3Qua2V5cyhldmVudC5jYW5kaWRhdGUpLmxlbmd0aCA9PT0gMDtcbiAgICAgICAgaWYgKGVuZCkge1xuICAgICAgICAgIGZvciAodmFyIGogPSAxOyBqIDwgc2VjdGlvbnMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChzZWN0aW9uc1tqXS5pbmRleE9mKCdcXHJcXG5hPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJykgPT09IC0xKSB7XG4gICAgICAgICAgICAgIHNlY3Rpb25zW2pdICs9ICdhPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZS5pbmRleE9mKCd0eXAgZW5kT2ZDYW5kaWRhdGVzJylcbiAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgIHNlY3Rpb25zW2V2ZW50LmNhbmRpZGF0ZS5zZHBNTGluZUluZGV4ICsgMV0gKz1cbiAgICAgICAgICAgICAgJ2E9JyArIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgKyAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgICAgICBzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgc2VsZi5vbmljZWNhbmRpZGF0ZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFldmVudC5jYW5kaWRhdGUgJiYgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSAhPT0gJ2NvbXBsZXRlJykge1xuICAgICAgICAgIHZhciBjb21wbGV0ZSA9IHNlbGYudHJhbnNjZWl2ZXJzLmV2ZXJ5KGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5zdGF0ZSA9PT0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKGNvbXBsZXRlKSB7XG4gICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0Q29uZmlndXJhdGlvbiA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMuX2NvbmZpZztcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgIC8vIENsb25lIGlzIG5lY2Vzc2FyeSBmb3IgbG9jYWwgZGVtb3MgbW9zdGx5LCBhdHRhY2hpbmcgZGlyZWN0bHlcbiAgICAgIC8vIHRvIHR3byBkaWZmZXJlbnQgc2VuZGVycyBkb2VzIG5vdCB3b3JrIChidWlsZCAxMDU0NykuXG4gICAgICB2YXIgY2xvbmVkU3RyZWFtID0gc3RyZWFtLmNsb25lKCk7XG4gICAgICBzdHJlYW0uZ2V0VHJhY2tzKCkuZm9yRWFjaChmdW5jdGlvbih0cmFjaywgaWR4KSB7XG4gICAgICAgIHZhciBjbG9uZWRUcmFjayA9IGNsb25lZFN0cmVhbS5nZXRUcmFja3MoKVtpZHhdO1xuICAgICAgICB0cmFjay5hZGRFdmVudExpc3RlbmVyKCdlbmFibGVkJywgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICBjbG9uZWRUcmFjay5lbmFibGVkID0gZXZlbnQuZW5hYmxlZDtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnB1c2goY2xvbmVkU3RyZWFtKTtcbiAgICAgIHRoaXMuX21heWJlRmlyZU5lZ290aWF0aW9uTmVlZGVkKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID0gZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICB2YXIgaWR4ID0gdGhpcy5sb2NhbFN0cmVhbXMuaW5kZXhPZihzdHJlYW0pO1xuICAgICAgaWYgKGlkeCA+IC0xKSB7XG4gICAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnNwbGljZShpZHgsIDEpO1xuICAgICAgICB0aGlzLl9tYXliZUZpcmVOZWdvdGlhdGlvbk5lZWRlZCgpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFNlbmRlcnMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiB0aGlzLnRyYW5zY2VpdmVycy5maWx0ZXIoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuICEhdHJhbnNjZWl2ZXIucnRwU2VuZGVyO1xuICAgICAgfSlcbiAgICAgIC5tYXAoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuIHRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFJlY2VpdmVycyA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMudHJhbnNjZWl2ZXJzLmZpbHRlcihmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICByZXR1cm4gISF0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgIH0pXG4gICAgICAubWFwKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIHJldHVybiB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvLyBEZXRlcm1pbmVzIHRoZSBpbnRlcnNlY3Rpb24gb2YgbG9jYWwgYW5kIHJlbW90ZSBjYXBhYmlsaXRpZXMuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzID1cbiAgICAgICAgZnVuY3Rpb24obG9jYWxDYXBhYmlsaXRpZXMsIHJlbW90ZUNhcGFiaWxpdGllcykge1xuICAgICAgICAgIHZhciBjb21tb25DYXBhYmlsaXRpZXMgPSB7XG4gICAgICAgICAgICBjb2RlY3M6IFtdLFxuICAgICAgICAgICAgaGVhZGVyRXh0ZW5zaW9uczogW10sXG4gICAgICAgICAgICBmZWNNZWNoYW5pc21zOiBbXVxuICAgICAgICAgIH07XG4gICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzLmZvckVhY2goZnVuY3Rpb24obENvZGVjKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJlbW90ZUNhcGFiaWxpdGllcy5jb2RlY3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgdmFyIHJDb2RlYyA9IHJlbW90ZUNhcGFiaWxpdGllcy5jb2RlY3NbaV07XG4gICAgICAgICAgICAgIGlmIChsQ29kZWMubmFtZS50b0xvd2VyQ2FzZSgpID09PSByQ29kZWMubmFtZS50b0xvd2VyQ2FzZSgpICYmXG4gICAgICAgICAgICAgICAgICBsQ29kZWMuY2xvY2tSYXRlID09PSByQ29kZWMuY2xvY2tSYXRlKSB7XG4gICAgICAgICAgICAgICAgLy8gbnVtYmVyIG9mIGNoYW5uZWxzIGlzIHRoZSBoaWdoZXN0IGNvbW1vbiBudW1iZXIgb2YgY2hhbm5lbHNcbiAgICAgICAgICAgICAgICByQ29kZWMubnVtQ2hhbm5lbHMgPSBNYXRoLm1pbihsQ29kZWMubnVtQ2hhbm5lbHMsXG4gICAgICAgICAgICAgICAgICAgIHJDb2RlYy5udW1DaGFubmVscyk7XG4gICAgICAgICAgICAgICAgLy8gcHVzaCByQ29kZWMgc28gd2UgcmVwbHkgd2l0aCBvZmZlcmVyIHBheWxvYWQgdHlwZVxuICAgICAgICAgICAgICAgIGNvbW1vbkNhcGFiaWxpdGllcy5jb2RlY3MucHVzaChyQ29kZWMpO1xuXG4gICAgICAgICAgICAgICAgLy8gZGV0ZXJtaW5lIGNvbW1vbiBmZWVkYmFjayBtZWNoYW5pc21zXG4gICAgICAgICAgICAgICAgckNvZGVjLnJ0Y3BGZWVkYmFjayA9IHJDb2RlYy5ydGNwRmVlZGJhY2suZmlsdGVyKGZ1bmN0aW9uKGZiKSB7XG4gICAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxDb2RlYy5ydGNwRmVlZGJhY2subGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGxDb2RlYy5ydGNwRmVlZGJhY2tbal0udHlwZSA9PT0gZmIudHlwZSAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgbENvZGVjLnJ0Y3BGZWVkYmFja1tqXS5wYXJhbWV0ZXIgPT09IGZiLnBhcmFtZXRlcikge1xuICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGFsc28gbmVlZCB0byBkZXRlcm1pbmUgLnBhcmFtZXRlcnNcbiAgICAgICAgICAgICAgICAvLyAgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVucGVlci9vcnRjL2lzc3Vlcy81NjlcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9uc1xuICAgICAgICAgICAgICAuZm9yRWFjaChmdW5jdGlvbihsSGVhZGVyRXh0ZW5zaW9uKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZW1vdGVDYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9ucy5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICBpKyspIHtcbiAgICAgICAgICAgICAgICAgIHZhciBySGVhZGVyRXh0ZW5zaW9uID0gcmVtb3RlQ2FwYWJpbGl0aWVzLmhlYWRlckV4dGVuc2lvbnNbaV07XG4gICAgICAgICAgICAgICAgICBpZiAobEhlYWRlckV4dGVuc2lvbi51cmkgPT09IHJIZWFkZXJFeHRlbnNpb24udXJpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbW1vbkNhcGFiaWxpdGllcy5oZWFkZXJFeHRlbnNpb25zLnB1c2gockhlYWRlckV4dGVuc2lvbik7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAvLyBGSVhNRTogZmVjTWVjaGFuaXNtc1xuICAgICAgICAgIHJldHVybiBjb21tb25DYXBhYmlsaXRpZXM7XG4gICAgICAgIH07XG5cbiAgICAvLyBDcmVhdGUgSUNFIGdhdGhlcmVyLCBJQ0UgdHJhbnNwb3J0IGFuZCBEVExTIHRyYW5zcG9ydC5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyA9XG4gICAgICAgIGZ1bmN0aW9uKG1pZCwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICB2YXIgaWNlR2F0aGVyZXIgPSBuZXcgUlRDSWNlR2F0aGVyZXIoc2VsZi5pY2VPcHRpb25zKTtcbiAgICAgICAgICB2YXIgaWNlVHJhbnNwb3J0ID0gbmV3IFJUQ0ljZVRyYW5zcG9ydChpY2VHYXRoZXJlcik7XG4gICAgICAgICAgaWNlR2F0aGVyZXIub25sb2NhbGNhbmRpZGF0ZSA9IGZ1bmN0aW9uKGV2dCkge1xuICAgICAgICAgICAgdmFyIGV2ZW50ID0gbmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKTtcbiAgICAgICAgICAgIGV2ZW50LmNhbmRpZGF0ZSA9IHtzZHBNaWQ6IG1pZCwgc2RwTUxpbmVJbmRleDogc2RwTUxpbmVJbmRleH07XG5cbiAgICAgICAgICAgIHZhciBjYW5kID0gZXZ0LmNhbmRpZGF0ZTtcbiAgICAgICAgICAgIHZhciBlbmQgPSAhY2FuZCB8fCBPYmplY3Qua2V5cyhjYW5kKS5sZW5ndGggPT09IDA7XG4gICAgICAgICAgICAvLyBFZGdlIGVtaXRzIGFuIGVtcHR5IG9iamVjdCBmb3IgUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGXigKVcbiAgICAgICAgICAgIGlmIChlbmQpIHtcbiAgICAgICAgICAgICAgLy8gcG9seWZpbGwgc2luY2UgUlRDSWNlR2F0aGVyZXIuc3RhdGUgaXMgbm90IGltcGxlbWVudGVkIGluXG4gICAgICAgICAgICAgIC8vIEVkZ2UgMTA1NDcgeWV0LlxuICAgICAgICAgICAgICBpZiAoaWNlR2F0aGVyZXIuc3RhdGUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyLnN0YXRlID0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAvLyBFbWl0IGEgY2FuZGlkYXRlIHdpdGggdHlwZSBlbmRPZkNhbmRpZGF0ZXMgdG8gbWFrZSB0aGUgc2FtcGxlc1xuICAgICAgICAgICAgICAvLyB3b3JrLiBFZGdlIHJlcXVpcmVzIGFkZEljZUNhbmRpZGF0ZSB3aXRoIHRoaXMgZW1wdHkgY2FuZGlkYXRlXG4gICAgICAgICAgICAgIC8vIHRvIHN0YXJ0IGNoZWNraW5nLiBUaGUgcmVhbCBzb2x1dGlvbiBpcyB0byBzaWduYWxcbiAgICAgICAgICAgICAgLy8gZW5kLW9mLWNhbmRpZGF0ZXMgdG8gdGhlIG90aGVyIHNpZGUgd2hlbiBnZXR0aW5nIHRoZSBudWxsXG4gICAgICAgICAgICAgIC8vIGNhbmRpZGF0ZSBidXQgc29tZSBhcHBzIChsaWtlIHRoZSBzYW1wbGVzKSBkb24ndCBkbyB0aGF0LlxuICAgICAgICAgICAgICBldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlID1cbiAgICAgICAgICAgICAgICAgICdjYW5kaWRhdGU6MSAxIHVkcCAxIDAuMC4wLjAgOSB0eXAgZW5kT2ZDYW5kaWRhdGVzJztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIFJUQ0ljZUNhbmRpZGF0ZSBkb2Vzbid0IGhhdmUgYSBjb21wb25lbnQsIG5lZWRzIHRvIGJlIGFkZGVkXG4gICAgICAgICAgICAgIGNhbmQuY29tcG9uZW50ID0gaWNlVHJhbnNwb3J0LmNvbXBvbmVudCA9PT0gJ1JUQ1AnID8gMiA6IDE7XG4gICAgICAgICAgICAgIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgPSBTRFBVdGlscy53cml0ZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gdXBkYXRlIGxvY2FsIGRlc2NyaXB0aW9uLlxuICAgICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICAgIGlmIChldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlLmluZGV4T2YoJ3R5cCBlbmRPZkNhbmRpZGF0ZXMnKVxuICAgICAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgICAgICBzZWN0aW9uc1tldmVudC5jYW5kaWRhdGUuc2RwTUxpbmVJbmRleCArIDFdICs9XG4gICAgICAgICAgICAgICAgICAnYT0nICsgZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc2VjdGlvbnNbZXZlbnQuY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXggKyAxXSArPVxuICAgICAgICAgICAgICAgICAgJ2E9ZW5kLW9mLWNhbmRpZGF0ZXNcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCA9IHNlY3Rpb25zLmpvaW4oJycpO1xuXG4gICAgICAgICAgICB2YXIgY29tcGxldGUgPSBzZWxmLnRyYW5zY2VpdmVycy5ldmVyeShmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICAgIHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyLnN0YXRlID09PSAnY29tcGxldGVkJztcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBFbWl0IGNhbmRpZGF0ZSBpZiBsb2NhbERlc2NyaXB0aW9uIGlzIHNldC5cbiAgICAgICAgICAgIC8vIEFsc28gZW1pdHMgbnVsbCBjYW5kaWRhdGUgd2hlbiBhbGwgZ2F0aGVyZXJzIGFyZSBjb21wbGV0ZS5cbiAgICAgICAgICAgIHN3aXRjaCAoc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSkge1xuICAgICAgICAgICAgICBjYXNlICduZXcnOlxuICAgICAgICAgICAgICAgIHNlbGYuX2xvY2FsSWNlQ2FuZGlkYXRlc0J1ZmZlci5wdXNoKGV2ZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZW5kICYmIGNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIucHVzaChcbiAgICAgICAgICAgICAgICAgICAgICBuZXcgRXZlbnQoJ2ljZWNhbmRpZGF0ZScpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgJ2dhdGhlcmluZyc6XG4gICAgICAgICAgICAgICAgc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcygpO1xuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYub25pY2VjYW5kaWRhdGUgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUoZXZlbnQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoY29tcGxldGUpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2ljZWNhbmRpZGF0ZScpKTtcbiAgICAgICAgICAgICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUobmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgJ2NvbXBsZXRlJzpcbiAgICAgICAgICAgICAgICAvLyBzaG91bGQgbm90IGhhcHBlbi4uLiBjdXJyZW50bHkhXG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGRlZmF1bHQ6IC8vIG5vLW9wLlxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH07XG4gICAgICAgICAgaWNlVHJhbnNwb3J0Lm9uaWNlc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHNlbGYuX3VwZGF0ZUNvbm5lY3Rpb25TdGF0ZSgpO1xuICAgICAgICAgIH07XG5cbiAgICAgICAgICB2YXIgZHRsc1RyYW5zcG9ydCA9IG5ldyBSVENEdGxzVHJhbnNwb3J0KGljZVRyYW5zcG9ydCk7XG4gICAgICAgICAgZHRsc1RyYW5zcG9ydC5vbmR0bHNzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgc2VsZi5fdXBkYXRlQ29ubmVjdGlvblN0YXRlKCk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICBkdGxzVHJhbnNwb3J0Lm9uZXJyb3IgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIC8vIG9uZXJyb3IgZG9lcyBub3Qgc2V0IHN0YXRlIHRvIGZhaWxlZCBieSBpdHNlbGYuXG4gICAgICAgICAgICBkdGxzVHJhbnNwb3J0LnN0YXRlID0gJ2ZhaWxlZCc7XG4gICAgICAgICAgICBzZWxmLl91cGRhdGVDb25uZWN0aW9uU3RhdGUoKTtcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGljZUdhdGhlcmVyOiBpY2VHYXRoZXJlcixcbiAgICAgICAgICAgIGljZVRyYW5zcG9ydDogaWNlVHJhbnNwb3J0LFxuICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogZHRsc1RyYW5zcG9ydFxuICAgICAgICAgIH07XG4gICAgICAgIH07XG5cbiAgICAvLyBTdGFydCB0aGUgUlRQIFNlbmRlciBhbmQgUmVjZWl2ZXIgZm9yIGEgdHJhbnNjZWl2ZXIuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdHJhbnNjZWl2ZSA9IGZ1bmN0aW9uKHRyYW5zY2VpdmVyLFxuICAgICAgICBzZW5kLCByZWN2KSB7XG4gICAgICB2YXIgcGFyYW1zID0gdGhpcy5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgIHRyYW5zY2VpdmVyLnJlbW90ZUNhcGFiaWxpdGllcyk7XG4gICAgICBpZiAoc2VuZCAmJiB0cmFuc2NlaXZlci5ydHBTZW5kZXIpIHtcbiAgICAgICAgcGFyYW1zLmVuY29kaW5ncyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgIHBhcmFtcy5ydGNwID0ge1xuICAgICAgICAgIGNuYW1lOiBTRFBVdGlscy5sb2NhbENOYW1lXG4gICAgICAgIH07XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzLmxlbmd0aCkge1xuICAgICAgICAgIHBhcmFtcy5ydGNwLnNzcmMgPSB0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmM7XG4gICAgICAgIH1cbiAgICAgICAgdHJhbnNjZWl2ZXIucnRwU2VuZGVyLnNlbmQocGFyYW1zKTtcbiAgICAgIH1cbiAgICAgIGlmIChyZWN2ICYmIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyKSB7XG4gICAgICAgIC8vIHJlbW92ZSBSVFggZmllbGQgaW4gRWRnZSAxNDk0MlxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIua2luZCA9PT0gJ3ZpZGVvJ1xuICAgICAgICAgICAgJiYgdHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVycykge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMuZm9yRWFjaChmdW5jdGlvbihwKSB7XG4gICAgICAgICAgICBkZWxldGUgcC5ydHg7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcGFyYW1zLmVuY29kaW5ncyA9IHRyYW5zY2VpdmVyLnJlY3ZFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgIHBhcmFtcy5ydGNwID0ge1xuICAgICAgICAgIGNuYW1lOiB0cmFuc2NlaXZlci5jbmFtZVxuICAgICAgICB9O1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuc2VuZEVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGgpIHtcbiAgICAgICAgICBwYXJhbXMucnRjcC5zc3JjID0gdHJhbnNjZWl2ZXIuc2VuZEVuY29kaW5nUGFyYW1ldGVyc1swXS5zc3JjO1xuICAgICAgICB9XG4gICAgICAgIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyLnJlY2VpdmUocGFyYW1zKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRMb2NhbERlc2NyaXB0aW9uID1cbiAgICAgICAgZnVuY3Rpb24oZGVzY3JpcHRpb24pIHtcbiAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgdmFyIHNlY3Rpb25zO1xuICAgICAgICAgIHZhciBzZXNzaW9ucGFydDtcbiAgICAgICAgICBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ29mZmVyJykge1xuICAgICAgICAgICAgLy8gRklYTUU6IFdoYXQgd2FzIHRoZSBwdXJwb3NlIG9mIHRoaXMgZW1wdHkgaWYgc3RhdGVtZW50P1xuICAgICAgICAgICAgLy8gaWYgKCF0aGlzLl9wZW5kaW5nT2ZmZXIpIHtcbiAgICAgICAgICAgIC8vIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAodGhpcy5fcGVuZGluZ09mZmVyKSB7XG4gICAgICAgICAgICAgIC8vIFZFUlkgbGltaXRlZCBzdXBwb3J0IGZvciBTRFAgbXVuZ2luZy4gTGltaXRlZCB0bzpcbiAgICAgICAgICAgICAgLy8gKiBjaGFuZ2luZyB0aGUgb3JkZXIgb2YgY29kZWNzXG4gICAgICAgICAgICAgIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhkZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgICBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgICAgIHNlY3Rpb25zLmZvckVhY2goZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZHBNTGluZUluZGV4KSB7XG4gICAgICAgICAgICAgICAgdmFyIGNhcHMgPSBTRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMobWVkaWFTZWN0aW9uKTtcbiAgICAgICAgICAgICAgICBzZWxmLl9wZW5kaW5nT2ZmZXJbc2RwTUxpbmVJbmRleF0ubG9jYWxDYXBhYmlsaXRpZXMgPSBjYXBzO1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgdGhpcy50cmFuc2NlaXZlcnMgPSB0aGlzLl9wZW5kaW5nT2ZmZXI7XG4gICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9wZW5kaW5nT2ZmZXI7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmIChkZXNjcmlwdGlvbi50eXBlID09PSAnYW5zd2VyJykge1xuICAgICAgICAgICAgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKHNlbGYucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICAgIHNlc3Npb25wYXJ0ID0gc2VjdGlvbnMuc2hpZnQoKTtcbiAgICAgICAgICAgIHZhciBpc0ljZUxpdGUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChzZXNzaW9ucGFydCxcbiAgICAgICAgICAgICAgICAnYT1pY2UtbGl0ZScpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XTtcbiAgICAgICAgICAgICAgdmFyIGljZUdhdGhlcmVyID0gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXI7XG4gICAgICAgICAgICAgIHZhciBpY2VUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIHZhciBkdGxzVHJhbnNwb3J0ID0gdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydDtcbiAgICAgICAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzID0gdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXM7XG4gICAgICAgICAgICAgIHZhciByZW1vdGVDYXBhYmlsaXRpZXMgPSB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWVkaWFTZWN0aW9uLnNwbGl0KCdcXG4nLCAxKVswXVxuICAgICAgICAgICAgICAgICAgLnNwbGl0KCcgJywgMilbMV0gPT09ICcwJztcblxuICAgICAgICAgICAgICBpZiAoIXJlamVjdGVkICYmICF0cmFuc2NlaXZlci5pc0RhdGFjaGFubmVsKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKFxuICAgICAgICAgICAgICAgICAgICBtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgICBpZiAoaXNJY2VMaXRlKSB7XG4gICAgICAgICAgICAgICAgICB2YXIgY2FuZHMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWNhbmRpZGF0ZTonKVxuICAgICAgICAgICAgICAgICAgLm1hcChmdW5jdGlvbihjYW5kKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAuZmlsdGVyKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhbmQuY29tcG9uZW50ID09PSAnMSc7XG4gICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgIC8vIGljZS1saXRlIG9ubHkgaW5jbHVkZXMgaG9zdCBjYW5kaWRhdGVzIGluIHRoZSBTRFAgc28gd2UgY2FuXG4gICAgICAgICAgICAgICAgICAvLyB1c2Ugc2V0UmVtb3RlQ2FuZGlkYXRlcyAod2hpY2ggaW1wbGllcyBhblxuICAgICAgICAgICAgICAgICAgLy8gUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGUpXG4gICAgICAgICAgICAgICAgICBpZiAoY2FuZHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUR0bHNQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMoXG4gICAgICAgICAgICAgICAgICAgIG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpO1xuICAgICAgICAgICAgICAgIGlmIChpc0ljZUxpdGUpIHtcbiAgICAgICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnc2VydmVyJztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgaWNlVHJhbnNwb3J0LnN0YXJ0KGljZUdhdGhlcmVyLCByZW1vdGVJY2VQYXJhbWV0ZXJzLFxuICAgICAgICAgICAgICAgICAgICAgIGlzSWNlTGl0ZSA/ICdjb250cm9sbGluZycgOiAnY29udHJvbGxlZCcpO1xuICAgICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIGludGVyc2VjdGlvbiBvZiBjYXBhYmlsaXRpZXMuXG4gICAgICAgICAgICAgICAgdmFyIHBhcmFtcyA9IHNlbGYuX2dldENvbW1vbkNhcGFiaWxpdGllcyhsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzKTtcblxuICAgICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBTZW5kZXIuIFRoZSBSVENSdHBSZWNlaXZlciBmb3IgdGhpc1xuICAgICAgICAgICAgICAgIC8vIHRyYW5zY2VpdmVyIGhhcyBhbHJlYWR5IGJlZW4gc3RhcnRlZCBpbiBzZXRSZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgICBwYXJhbXMuY29kZWNzLmxlbmd0aCA+IDAsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0ge1xuICAgICAgICAgICAgdHlwZTogZGVzY3JpcHRpb24udHlwZSxcbiAgICAgICAgICAgIHNkcDogZGVzY3JpcHRpb24uc2RwXG4gICAgICAgICAgfTtcbiAgICAgICAgICBzd2l0Y2ggKGRlc2NyaXB0aW9uLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ29mZmVyJzpcbiAgICAgICAgICAgICAgdGhpcy5fdXBkYXRlU2lnbmFsaW5nU3RhdGUoJ2hhdmUtbG9jYWwtb2ZmZXInKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbnN3ZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnc3RhYmxlJyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigndW5zdXBwb3J0ZWQgdHlwZSBcIicgKyBkZXNjcmlwdGlvbi50eXBlICtcbiAgICAgICAgICAgICAgICAgICdcIicpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIElmIGEgc3VjY2VzcyBjYWxsYmFjayB3YXMgcHJvdmlkZWQsIGVtaXQgSUNFIGNhbmRpZGF0ZXMgYWZ0ZXIgaXRcbiAgICAgICAgICAvLyBoYXMgYmVlbiBleGVjdXRlZC4gT3RoZXJ3aXNlLCBlbWl0IGNhbGxiYWNrIGFmdGVyIHRoZSBQcm9taXNlIGlzXG4gICAgICAgICAgLy8gcmVzb2x2ZWQuXG4gICAgICAgICAgdmFyIGhhc0NhbGxiYWNrID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiZcbiAgICAgICAgICAgIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbic7XG4gICAgICAgICAgaWYgKGhhc0NhbGxiYWNrKSB7XG4gICAgICAgICAgICB2YXIgY2IgPSBhcmd1bWVudHNbMV07XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgY2IoKTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHNlbGYuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMoKTtcbiAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB2YXIgcCA9IFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgICAgIHAudGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmICghaGFzQ2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFVzdWFsbHkgY2FuZGlkYXRlcyB3aWxsIGJlIGVtaXR0ZWQgZWFybGllci5cbiAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcy5iaW5kKHNlbGYpLCA1MDApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybiBwO1xuICAgICAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9XG4gICAgICAgIGZ1bmN0aW9uKGRlc2NyaXB0aW9uKSB7XG4gICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgIHZhciBzdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICAgICAgICB2YXIgcmVjZWl2ZXJMaXN0ID0gW107XG4gICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhkZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgIHZhciBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgdmFyIGlzSWNlTGl0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1pY2UtbGl0ZScpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgdGhpcy51c2luZ0J1bmRsZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1ncm91cDpCVU5ETEUgJykubGVuZ3RoID4gMDtcbiAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIG1saW5lID0gbGluZXNbMF0uc3Vic3RyKDIpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICB2YXIga2luZCA9IG1saW5lWzBdO1xuICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWxpbmVbMV0gPT09ICcwJztcbiAgICAgICAgICAgIHZhciBkaXJlY3Rpb24gPSBTRFBVdGlscy5nZXREaXJlY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCk7XG5cbiAgICAgICAgICAgIHZhciBtaWQgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPW1pZDonKTtcbiAgICAgICAgICAgIGlmIChtaWQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgIG1pZCA9IG1pZFswXS5zdWJzdHIoNik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBtaWQgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUmVqZWN0IGRhdGFjaGFubmVscyB3aGljaCBhcmUgbm90IGltcGxlbWVudGVkIHlldC5cbiAgICAgICAgICAgIGlmIChraW5kID09PSAnYXBwbGljYXRpb24nICYmIG1saW5lWzJdID09PSAnRFRMUy9TQ1RQJykge1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgICAgICBpc0RhdGFjaGFubmVsOiB0cnVlXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHRyYW5zY2VpdmVyO1xuICAgICAgICAgICAgdmFyIGljZUdhdGhlcmVyO1xuICAgICAgICAgICAgdmFyIGljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgIHZhciBkdGxzVHJhbnNwb3J0O1xuICAgICAgICAgICAgdmFyIHJ0cFNlbmRlcjtcbiAgICAgICAgICAgIHZhciBydHBSZWNlaXZlcjtcbiAgICAgICAgICAgIHZhciBzZW5kRW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgdmFyIHJlY3ZFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgICAgICB2YXIgbG9jYWxDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgIHZhciB0cmFjaztcbiAgICAgICAgICAgIC8vIEZJWE1FOiBlbnN1cmUgdGhlIG1lZGlhU2VjdGlvbiBoYXMgcnRjcC1tdXggc2V0LlxuICAgICAgICAgICAgdmFyIHJlbW90ZUNhcGFiaWxpdGllcyA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnM7XG4gICAgICAgICAgICB2YXIgcmVtb3RlRHRsc1BhcmFtZXRlcnM7XG4gICAgICAgICAgICBpZiAoIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAgIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgcmVtb3RlRHRsc1BhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXREdGxzUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24sXG4gICAgICAgICAgICAgICAgICBzZXNzaW9ucGFydCk7XG4gICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnY2xpZW50JztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMgPVxuICAgICAgICAgICAgICAgIFNEUFV0aWxzLnBhcnNlUnRwRW5jb2RpbmdQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbik7XG5cbiAgICAgICAgICAgIHZhciBjbmFtZTtcbiAgICAgICAgICAgIC8vIEdldHMgdGhlIGZpcnN0IFNTUkMuIE5vdGUgdGhhdCB3aXRoIFJUWCB0aGVyZSBtaWdodCBiZSBtdWx0aXBsZVxuICAgICAgICAgICAgLy8gU1NSQ3MuXG4gICAgICAgICAgICB2YXIgcmVtb3RlU3NyYyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgICAgICAgICAgICAgIC5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbHRlcihmdW5jdGlvbihvYmopIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBvYmouYXR0cmlidXRlID09PSAnY25hbWUnO1xuICAgICAgICAgICAgICAgIH0pWzBdO1xuICAgICAgICAgICAgaWYgKHJlbW90ZVNzcmMpIHtcbiAgICAgICAgICAgICAgY25hbWUgPSByZW1vdGVTc3JjLnZhbHVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgaXNDb21wbGV0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAnYT1lbmQtb2YtY2FuZGlkYXRlcycsIHNlc3Npb25wYXJ0KS5sZW5ndGggPiAwO1xuICAgICAgICAgICAgdmFyIGNhbmRzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1jYW5kaWRhdGU6JylcbiAgICAgICAgICAgICAgICAubWFwKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC5maWx0ZXIoZnVuY3Rpb24oY2FuZCkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhbmQuY29tcG9uZW50ID09PSAnMSc7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ29mZmVyJyAmJiAhcmVqZWN0ZWQpIHtcbiAgICAgICAgICAgICAgdmFyIHRyYW5zcG9ydHMgPSBzZWxmLnVzaW5nQnVuZGxlICYmIHNkcE1MaW5lSW5kZXggPiAwID8ge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyOiBzZWxmLnRyYW5zY2VpdmVyc1swXS5pY2VHYXRoZXJlcixcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHNlbGYudHJhbnNjZWl2ZXJzWzBdLmljZVRyYW5zcG9ydCxcbiAgICAgICAgICAgICAgICBkdGxzVHJhbnNwb3J0OiBzZWxmLnRyYW5zY2VpdmVyc1swXS5kdGxzVHJhbnNwb3J0XG4gICAgICAgICAgICAgIH0gOiBzZWxmLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyhtaWQsIHNkcE1MaW5lSW5kZXgpO1xuXG4gICAgICAgICAgICAgIGlmIChpc0NvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgdHJhbnNwb3J0cy5pY2VUcmFuc3BvcnQuc2V0UmVtb3RlQ2FuZGlkYXRlcyhjYW5kcyk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllcyA9IFJUQ1J0cFJlY2VpdmVyLmdldENhcGFiaWxpdGllcyhraW5kKTtcblxuICAgICAgICAgICAgICAvLyBmaWx0ZXIgUlRYIHVudGlsIGFkZGl0aW9uYWwgc3R1ZmYgbmVlZGVkIGZvciBSVFggaXMgaW1wbGVtZW50ZWRcbiAgICAgICAgICAgICAgLy8gaW4gYWRhcHRlci5qc1xuICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MgPSBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MuZmlsdGVyKFxuICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oY29kZWMpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNvZGVjLm5hbWUgIT09ICdydHgnO1xuICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IFt7XG4gICAgICAgICAgICAgICAgc3NyYzogKDIgKiBzZHBNTGluZUluZGV4ICsgMikgKiAxMDAxXG4gICAgICAgICAgICAgIH1dO1xuXG4gICAgICAgICAgICAgIHJ0cFJlY2VpdmVyID0gbmV3IFJUQ1J0cFJlY2VpdmVyKHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCwga2luZCk7XG5cbiAgICAgICAgICAgICAgdHJhY2sgPSBydHBSZWNlaXZlci50cmFjaztcbiAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LnB1c2goW3RyYWNrLCBydHBSZWNlaXZlcl0pO1xuICAgICAgICAgICAgICAvLyBGSVhNRTogbm90IGNvcnJlY3Qgd2hlbiB0aGVyZSBhcmUgbXVsdGlwbGUgc3RyZWFtcyBidXQgdGhhdCBpc1xuICAgICAgICAgICAgICAvLyBub3QgY3VycmVudGx5IHN1cHBvcnRlZCBpbiB0aGlzIHNoaW0uXG4gICAgICAgICAgICAgIHN0cmVhbS5hZGRUcmFjayh0cmFjayk7XG5cbiAgICAgICAgICAgICAgLy8gRklYTUU6IGxvb2sgYXQgZGlyZWN0aW9uLlxuICAgICAgICAgICAgICBpZiAoc2VsZi5sb2NhbFN0cmVhbXMubGVuZ3RoID4gMCAmJlxuICAgICAgICAgICAgICAgICAgc2VsZi5sb2NhbFN0cmVhbXNbMF0uZ2V0VHJhY2tzKCkubGVuZ3RoID49IHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgICAgICAgICB2YXIgbG9jYWxUcmFjaztcbiAgICAgICAgICAgICAgICBpZiAoa2luZCA9PT0gJ2F1ZGlvJykge1xuICAgICAgICAgICAgICAgICAgbG9jYWxUcmFjayA9IHNlbGYubG9jYWxTdHJlYW1zWzBdLmdldEF1ZGlvVHJhY2tzKClbMF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChraW5kID09PSAndmlkZW8nKSB7XG4gICAgICAgICAgICAgICAgICBsb2NhbFRyYWNrID0gc2VsZi5sb2NhbFN0cmVhbXNbMF0uZ2V0VmlkZW9UcmFja3MoKVswXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGxvY2FsVHJhY2spIHtcbiAgICAgICAgICAgICAgICAgIHJ0cFNlbmRlciA9IG5ldyBSVENSdHBTZW5kZXIobG9jYWxUcmFjayxcbiAgICAgICAgICAgICAgICAgICAgICB0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdID0ge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyOiB0cmFuc3BvcnRzLmljZUdhdGhlcmVyLFxuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydDogdHJhbnNwb3J0cy5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LFxuICAgICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzOiBsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICByZW1vdGVDYXBhYmlsaXRpZXM6IHJlbW90ZUNhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICBydHBTZW5kZXI6IHJ0cFNlbmRlcixcbiAgICAgICAgICAgICAgICBydHBSZWNlaXZlcjogcnRwUmVjZWl2ZXIsXG4gICAgICAgICAgICAgICAga2luZDoga2luZCxcbiAgICAgICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgICAgICBjbmFtZTogY25hbWUsXG4gICAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVyczogc2VuZEVuY29kaW5nUGFyYW1ldGVycyxcbiAgICAgICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzOiByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBSZWNlaXZlciBub3cuIFRoZSBSVFBTZW5kZXIgaXMgc3RhcnRlZCBpblxuICAgICAgICAgICAgICAvLyBzZXRMb2NhbERlc2NyaXB0aW9uLlxuICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdLFxuICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPT09ICdzZW5kcmVjdicgfHwgZGlyZWN0aW9uID09PSAnc2VuZG9ubHknKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ2Fuc3dlcicgJiYgIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHRyYW5zY2VpdmVyID0gc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF07XG4gICAgICAgICAgICAgIGljZUdhdGhlcmVyID0gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXI7XG4gICAgICAgICAgICAgIGljZVRyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIHJ0cFNlbmRlciA9IHRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgICAgICAgICAgcnRwUmVjZWl2ZXIgPSB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzID0gdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgICAgc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF0ucmVjdkVuY29kaW5nUGFyYW1ldGVycyA9XG4gICAgICAgICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XS5yZW1vdGVDYXBhYmlsaXRpZXMgPVxuICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzO1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XS5jbmFtZSA9IGNuYW1lO1xuXG4gICAgICAgICAgICAgIGlmICgoaXNJY2VMaXRlIHx8IGlzQ29tcGxldGUpICYmIGNhbmRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zdGFydChpY2VHYXRoZXJlciwgcmVtb3RlSWNlUGFyYW1ldGVycyxcbiAgICAgICAgICAgICAgICAgICAgJ2NvbnRyb2xsaW5nJyk7XG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID09PSAnc2VuZHJlY3YnIHx8IGRpcmVjdGlvbiA9PT0gJ3JlY3Zvbmx5JyxcbiAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdzZW5kb25seScpO1xuXG4gICAgICAgICAgICAgIGlmIChydHBSZWNlaXZlciAmJlxuICAgICAgICAgICAgICAgICAgKGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdzZW5kb25seScpKSB7XG4gICAgICAgICAgICAgICAgdHJhY2sgPSBydHBSZWNlaXZlci50cmFjaztcbiAgICAgICAgICAgICAgICByZWNlaXZlckxpc3QucHVzaChbdHJhY2ssIHJ0cFJlY2VpdmVyXSk7XG4gICAgICAgICAgICAgICAgc3RyZWFtLmFkZFRyYWNrKHRyYWNrKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBGSVhNRTogYWN0dWFsbHkgdGhlIHJlY2VpdmVyIHNob3VsZCBiZSBjcmVhdGVkIGxhdGVyLlxuICAgICAgICAgICAgICAgIGRlbGV0ZSB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgdGhpcy5yZW1vdGVEZXNjcmlwdGlvbiA9IHtcbiAgICAgICAgICAgIHR5cGU6IGRlc2NyaXB0aW9uLnR5cGUsXG4gICAgICAgICAgICBzZHA6IGRlc2NyaXB0aW9uLnNkcFxuICAgICAgICAgIH07XG4gICAgICAgICAgc3dpdGNoIChkZXNjcmlwdGlvbi50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdvZmZlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdoYXZlLXJlbW90ZS1vZmZlcicpO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2Fuc3dlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdzdGFibGUnKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCd1bnN1cHBvcnRlZCB0eXBlIFwiJyArIGRlc2NyaXB0aW9uLnR5cGUgK1xuICAgICAgICAgICAgICAgICAgJ1wiJyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChzdHJlYW0uZ2V0VHJhY2tzKCkubGVuZ3RoKSB7XG4gICAgICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbXMucHVzaChzdHJlYW0pO1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnYWRkc3RyZWFtJyk7XG4gICAgICAgICAgICAgIGV2ZW50LnN0cmVhbSA9IHN0cmVhbTtcbiAgICAgICAgICAgICAgc2VsZi5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYub25hZGRzdHJlYW0gIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYub25hZGRzdHJlYW0oZXZlbnQpO1xuICAgICAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LmZvckVhY2goZnVuY3Rpb24oaXRlbSkge1xuICAgICAgICAgICAgICAgIHZhciB0cmFjayA9IGl0ZW1bMF07XG4gICAgICAgICAgICAgICAgdmFyIHJlY2VpdmVyID0gaXRlbVsxXTtcbiAgICAgICAgICAgICAgICB2YXIgdHJhY2tFdmVudCA9IG5ldyBFdmVudCgndHJhY2snKTtcbiAgICAgICAgICAgICAgICB0cmFja0V2ZW50LnRyYWNrID0gdHJhY2s7XG4gICAgICAgICAgICAgICAgdHJhY2tFdmVudC5yZWNlaXZlciA9IHJlY2VpdmVyO1xuICAgICAgICAgICAgICAgIHRyYWNrRXZlbnQuc3RyZWFtcyA9IFtzdHJlYW1dO1xuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYub250cmFjayAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYub250cmFjayh0cmFja0V2ZW50KTtcbiAgICAgICAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICAgICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY2xvc2UgPSBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgLyogbm90IHlldFxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgICovXG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5kdGxzVHJhbnNwb3J0KSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnJ0cFNlbmRlcikge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJ0cFNlbmRlci5zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyKSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIC8vIEZJWE1FOiBjbGVhbiB1cCB0cmFja3MsIGxvY2FsIHN0cmVhbXMsIHJlbW90ZSBzdHJlYW1zLCBldGNcbiAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdjbG9zZWQnKTtcbiAgICB9O1xuXG4gICAgLy8gVXBkYXRlIHRoZSBzaWduYWxpbmcgc3RhdGUuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdXBkYXRlU2lnbmFsaW5nU3RhdGUgPVxuICAgICAgICBmdW5jdGlvbihuZXdTdGF0ZSkge1xuICAgICAgICAgIHRoaXMuc2lnbmFsaW5nU3RhdGUgPSBuZXdTdGF0ZTtcbiAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ3NpZ25hbGluZ3N0YXRlY2hhbmdlJyk7XG4gICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICBpZiAodGhpcy5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UoZXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIC8vIERldGVybWluZSB3aGV0aGVyIHRvIGZpcmUgdGhlIG5lZ290aWF0aW9ubmVlZGVkIGV2ZW50LlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX21heWJlRmlyZU5lZ290aWF0aW9uTmVlZGVkID1cbiAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgLy8gRmlyZSBhd2F5IChmb3Igbm93KS5cbiAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ25lZ290aWF0aW9ubmVlZGVkJyk7XG4gICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICBpZiAodGhpcy5vbm5lZ290aWF0aW9ubmVlZGVkICE9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQoZXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIC8vIFVwZGF0ZSB0aGUgY29ubmVjdGlvbiBzdGF0ZS5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl91cGRhdGVDb25uZWN0aW9uU3RhdGUgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBuZXdTdGF0ZTtcbiAgICAgIHZhciBzdGF0ZXMgPSB7XG4gICAgICAgICduZXcnOiAwLFxuICAgICAgICBjbG9zZWQ6IDAsXG4gICAgICAgIGNvbm5lY3Rpbmc6IDAsXG4gICAgICAgIGNoZWNraW5nOiAwLFxuICAgICAgICBjb25uZWN0ZWQ6IDAsXG4gICAgICAgIGNvbXBsZXRlZDogMCxcbiAgICAgICAgZmFpbGVkOiAwXG4gICAgICB9O1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBzdGF0ZXNbdHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0LnN0YXRlXSsrO1xuICAgICAgICBzdGF0ZXNbdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5zdGF0ZV0rKztcbiAgICAgIH0pO1xuICAgICAgLy8gSUNFVHJhbnNwb3J0LmNvbXBsZXRlZCBhbmQgY29ubmVjdGVkIGFyZSB0aGUgc2FtZSBmb3IgdGhpcyBwdXJwb3NlLlxuICAgICAgc3RhdGVzLmNvbm5lY3RlZCArPSBzdGF0ZXMuY29tcGxldGVkO1xuXG4gICAgICBuZXdTdGF0ZSA9ICduZXcnO1xuICAgICAgaWYgKHN0YXRlcy5mYWlsZWQgPiAwKSB7XG4gICAgICAgIG5ld1N0YXRlID0gJ2ZhaWxlZCc7XG4gICAgICB9IGVsc2UgaWYgKHN0YXRlcy5jb25uZWN0aW5nID4gMCB8fCBzdGF0ZXMuY2hlY2tpbmcgPiAwKSB7XG4gICAgICAgIG5ld1N0YXRlID0gJ2Nvbm5lY3RpbmcnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuZGlzY29ubmVjdGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdkaXNjb25uZWN0ZWQnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMubmV3ID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICduZXcnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuY29ubmVjdGVkID4gMCB8fCBzdGF0ZXMuY29tcGxldGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdjb25uZWN0ZWQnO1xuICAgICAgfVxuXG4gICAgICBpZiAobmV3U3RhdGUgIT09IHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgIHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlID0gbmV3U3RhdGU7XG4gICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlJyk7XG4gICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmICh0aGlzLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVPZmZlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgaWYgKHRoaXMuX3BlbmRpbmdPZmZlcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2NyZWF0ZU9mZmVyIGNhbGxlZCB3aGlsZSB0aGVyZSBpcyBhIHBlbmRpbmcgb2ZmZXIuJyk7XG4gICAgICB9XG4gICAgICB2YXIgb2ZmZXJPcHRpb25zO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDEgJiYgdHlwZW9mIGFyZ3VtZW50c1swXSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBvZmZlck9wdGlvbnMgPSBhcmd1bWVudHNbMF07XG4gICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDMpIHtcbiAgICAgICAgb2ZmZXJPcHRpb25zID0gYXJndW1lbnRzWzJdO1xuICAgICAgfVxuXG4gICAgICB2YXIgdHJhY2tzID0gW107XG4gICAgICB2YXIgbnVtQXVkaW9UcmFja3MgPSAwO1xuICAgICAgdmFyIG51bVZpZGVvVHJhY2tzID0gMDtcbiAgICAgIC8vIERlZmF1bHQgdG8gc2VuZHJlY3YuXG4gICAgICBpZiAodGhpcy5sb2NhbFN0cmVhbXMubGVuZ3RoKSB7XG4gICAgICAgIG51bUF1ZGlvVHJhY2tzID0gdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0QXVkaW9UcmFja3MoKS5sZW5ndGg7XG4gICAgICAgIG51bVZpZGVvVHJhY2tzID0gdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGg7XG4gICAgICB9XG4gICAgICAvLyBEZXRlcm1pbmUgbnVtYmVyIG9mIGF1ZGlvIGFuZCB2aWRlbyB0cmFja3Mgd2UgbmVlZCB0byBzZW5kL3JlY3YuXG4gICAgICBpZiAob2ZmZXJPcHRpb25zKSB7XG4gICAgICAgIC8vIFJlamVjdCBDaHJvbWUgbGVnYWN5IGNvbnN0cmFpbnRzLlxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm1hbmRhdG9yeSB8fCBvZmZlck9wdGlvbnMub3B0aW9uYWwpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFxuICAgICAgICAgICAgICAnTGVnYWN5IG1hbmRhdG9yeS9vcHRpb25hbCBjb25zdHJhaW50cyBub3Qgc3VwcG9ydGVkLicpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVBdWRpbyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgbnVtQXVkaW9UcmFja3MgPSBvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVBdWRpbztcbiAgICAgICAgfVxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlVmlkZW8gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIG51bVZpZGVvVHJhY2tzID0gb2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlVmlkZW87XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLmxvY2FsU3RyZWFtcy5sZW5ndGgpIHtcbiAgICAgICAgLy8gUHVzaCBsb2NhbCBzdHJlYW1zLlxuICAgICAgICB0aGlzLmxvY2FsU3RyZWFtc1swXS5nZXRUcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uKHRyYWNrKSB7XG4gICAgICAgICAgdHJhY2tzLnB1c2goe1xuICAgICAgICAgICAga2luZDogdHJhY2sua2luZCxcbiAgICAgICAgICAgIHRyYWNrOiB0cmFjayxcbiAgICAgICAgICAgIHdhbnRSZWNlaXZlOiB0cmFjay5raW5kID09PSAnYXVkaW8nID9cbiAgICAgICAgICAgICAgICBudW1BdWRpb1RyYWNrcyA+IDAgOiBudW1WaWRlb1RyYWNrcyA+IDBcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpZiAodHJhY2sua2luZCA9PT0gJ2F1ZGlvJykge1xuICAgICAgICAgICAgbnVtQXVkaW9UcmFja3MtLTtcbiAgICAgICAgICB9IGVsc2UgaWYgKHRyYWNrLmtpbmQgPT09ICd2aWRlbycpIHtcbiAgICAgICAgICAgIG51bVZpZGVvVHJhY2tzLS07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIC8vIENyZWF0ZSBNLWxpbmVzIGZvciByZWN2b25seSBzdHJlYW1zLlxuICAgICAgd2hpbGUgKG51bUF1ZGlvVHJhY2tzID4gMCB8fCBudW1WaWRlb1RyYWNrcyA+IDApIHtcbiAgICAgICAgaWYgKG51bUF1ZGlvVHJhY2tzID4gMCkge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6ICdhdWRpbycsXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG51bUF1ZGlvVHJhY2tzLS07XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG51bVZpZGVvVHJhY2tzID4gMCkge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6ICd2aWRlbycsXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG51bVZpZGVvVHJhY2tzLS07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdmFyIHNkcCA9IFNEUFV0aWxzLndyaXRlU2Vzc2lvbkJvaWxlcnBsYXRlKCk7XG4gICAgICB2YXIgdHJhbnNjZWl2ZXJzID0gW107XG4gICAgICB0cmFja3MuZm9yRWFjaChmdW5jdGlvbihtbGluZSwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAvLyBGb3IgZWFjaCB0cmFjaywgY3JlYXRlIGFuIGljZSBnYXRoZXJlciwgaWNlIHRyYW5zcG9ydCxcbiAgICAgICAgLy8gZHRscyB0cmFuc3BvcnQsIHBvdGVudGlhbGx5IHJ0cHNlbmRlciBhbmQgcnRwcmVjZWl2ZXIuXG4gICAgICAgIHZhciB0cmFjayA9IG1saW5lLnRyYWNrO1xuICAgICAgICB2YXIga2luZCA9IG1saW5lLmtpbmQ7XG4gICAgICAgIHZhciBtaWQgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcblxuICAgICAgICB2YXIgdHJhbnNwb3J0cyA9IHNlbGYudXNpbmdCdW5kbGUgJiYgc2RwTUxpbmVJbmRleCA+IDAgPyB7XG4gICAgICAgICAgaWNlR2F0aGVyZXI6IHRyYW5zY2VpdmVyc1swXS5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zY2VpdmVyc1swXS5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNjZWl2ZXJzWzBdLmR0bHNUcmFuc3BvcnRcbiAgICAgICAgfSA6IHNlbGYuX2NyZWF0ZUljZUFuZER0bHNUcmFuc3BvcnRzKG1pZCwgc2RwTUxpbmVJbmRleCk7XG5cbiAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzID0gUlRDUnRwU2VuZGVyLmdldENhcGFiaWxpdGllcyhraW5kKTtcbiAgICAgICAgLy8gZmlsdGVyIFJUWCB1bnRpbCBhZGRpdGlvbmFsIHN0dWZmIG5lZWRlZCBmb3IgUlRYIGlzIGltcGxlbWVudGVkXG4gICAgICAgIC8vIGluIGFkYXB0ZXIuanNcbiAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzID0gbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzLmZpbHRlcihcbiAgICAgICAgICAgIGZ1bmN0aW9uKGNvZGVjKSB7XG4gICAgICAgICAgICAgIHJldHVybiBjb2RlYy5uYW1lICE9PSAncnR4JztcbiAgICAgICAgICAgIH0pO1xuICAgICAgICBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MuZm9yRWFjaChmdW5jdGlvbihjb2RlYykge1xuICAgICAgICAgIC8vIHdvcmsgYXJvdW5kIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD02NTUyXG4gICAgICAgICAgLy8gYnkgYWRkaW5nIGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTFcbiAgICAgICAgICBpZiAoY29kZWMubmFtZSA9PT0gJ0gyNjQnICYmXG4gICAgICAgICAgICAgIGNvZGVjLnBhcmFtZXRlcnNbJ2xldmVsLWFzeW1tZXRyeS1hbGxvd2VkJ10gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgY29kZWMucGFyYW1ldGVyc1snbGV2ZWwtYXN5bW1ldHJ5LWFsbG93ZWQnXSA9ICcxJztcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHZhciBydHBTZW5kZXI7XG4gICAgICAgIHZhciBydHBSZWNlaXZlcjtcblxuICAgICAgICAvLyBnZW5lcmF0ZSBhbiBzc3JjIG5vdywgdG8gYmUgdXNlZCBsYXRlciBpbiBydHBTZW5kZXIuc2VuZFxuICAgICAgICB2YXIgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IFt7XG4gICAgICAgICAgc3NyYzogKDIgKiBzZHBNTGluZUluZGV4ICsgMSkgKiAxMDAxXG4gICAgICAgIH1dO1xuICAgICAgICBpZiAodHJhY2spIHtcbiAgICAgICAgICBydHBTZW5kZXIgPSBuZXcgUlRDUnRwU2VuZGVyKHRyYWNrLCB0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG1saW5lLndhbnRSZWNlaXZlKSB7XG4gICAgICAgICAgcnRwUmVjZWl2ZXIgPSBuZXcgUlRDUnRwUmVjZWl2ZXIodHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LCBraW5kKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICBpY2VHYXRoZXJlcjogdHJhbnNwb3J0cy5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuaWNlVHJhbnNwb3J0LFxuICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCxcbiAgICAgICAgICBsb2NhbENhcGFiaWxpdGllczogbG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzOiBudWxsLFxuICAgICAgICAgIHJ0cFNlbmRlcjogcnRwU2VuZGVyLFxuICAgICAgICAgIHJ0cFJlY2VpdmVyOiBydHBSZWNlaXZlcixcbiAgICAgICAgICBraW5kOiBraW5kLFxuICAgICAgICAgIG1pZDogbWlkLFxuICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnM6IHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMsXG4gICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVyczogbnVsbFxuICAgICAgICB9O1xuICAgICAgfSk7XG4gICAgICBpZiAodGhpcy51c2luZ0J1bmRsZSkge1xuICAgICAgICBzZHAgKz0gJ2E9Z3JvdXA6QlVORExFICcgKyB0cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdHJhY2tzLmZvckVhY2goZnVuY3Rpb24obWxpbmUsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgdmFyIHRyYW5zY2VpdmVyID0gdHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24odHJhbnNjZWl2ZXIsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcywgJ29mZmVyJywgc2VsZi5sb2NhbFN0cmVhbXNbMF0pO1xuICAgICAgfSk7XG5cbiAgICAgIHRoaXMuX3BlbmRpbmdPZmZlciA9IHRyYW5zY2VpdmVycztcbiAgICAgIHZhciBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICdvZmZlcicsXG4gICAgICAgIHNkcDogc2RwXG4gICAgICB9KTtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoYXJndW1lbnRzWzBdLCAwLCBkZXNjKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGVzYyk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlQW5zd2VyID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAgIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSgpO1xuICAgICAgaWYgKHRoaXMudXNpbmdCdW5kbGUpIHtcbiAgICAgICAgc2RwICs9ICdhPWdyb3VwOkJVTkRMRSAnICsgdGhpcy50cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaXNEYXRhY2hhbm5lbCkge1xuICAgICAgICAgIHNkcCArPSAnbT1hcHBsaWNhdGlvbiAwIERUTFMvU0NUUCA1MDAwXFxyXFxuJyArXG4gICAgICAgICAgICAgICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJyArXG4gICAgICAgICAgICAgICdhPW1pZDonICsgdHJhbnNjZWl2ZXIubWlkICsgJ1xcclxcbic7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIENhbGN1bGF0ZSBpbnRlcnNlY3Rpb24gb2YgY2FwYWJpbGl0aWVzLlxuICAgICAgICB2YXIgY29tbW9uQ2FwYWJpbGl0aWVzID0gc2VsZi5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKFxuICAgICAgICAgICAgdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXMpO1xuXG4gICAgICAgIHNkcCArPSBTRFBVdGlscy53cml0ZU1lZGlhU2VjdGlvbih0cmFuc2NlaXZlciwgY29tbW9uQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgICAgJ2Fuc3dlcicsIHNlbGYubG9jYWxTdHJlYW1zWzBdKTtcbiAgICAgIH0pO1xuXG4gICAgICB2YXIgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiAnYW5zd2VyJyxcbiAgICAgICAgc2RwOiBzZHBcbiAgICAgIH0pO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggJiYgdHlwZW9mIGFyZ3VtZW50c1swXSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChhcmd1bWVudHNbMF0sIDAsIGRlc2MpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShkZXNjKTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgICAgIGlmICghY2FuZGlkYXRlKSB7XG4gICAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKHt9KTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgbUxpbmVJbmRleCA9IGNhbmRpZGF0ZS5zZHBNTGluZUluZGV4O1xuICAgICAgICBpZiAoY2FuZGlkYXRlLnNkcE1pZCkge1xuICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy50cmFuc2NlaXZlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnRyYW5zY2VpdmVyc1tpXS5taWQgPT09IGNhbmRpZGF0ZS5zZHBNaWQpIHtcbiAgICAgICAgICAgICAgbUxpbmVJbmRleCA9IGk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSB0aGlzLnRyYW5zY2VpdmVyc1ttTGluZUluZGV4XTtcbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgdmFyIGNhbmQgPSBPYmplY3Qua2V5cyhjYW5kaWRhdGUuY2FuZGlkYXRlKS5sZW5ndGggPiAwID9cbiAgICAgICAgICAgICAgU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUoY2FuZGlkYXRlLmNhbmRpZGF0ZSkgOiB7fTtcbiAgICAgICAgICAvLyBJZ25vcmUgQ2hyb21lJ3MgaW52YWxpZCBjYW5kaWRhdGVzIHNpbmNlIEVkZ2UgZG9lcyBub3QgbGlrZSB0aGVtLlxuICAgICAgICAgIGlmIChjYW5kLnByb3RvY29sID09PSAndGNwJyAmJiAoY2FuZC5wb3J0ID09PSAwIHx8IGNhbmQucG9ydCA9PT0gOSkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gSWdub3JlIFJUQ1AgY2FuZGlkYXRlcywgd2UgYXNzdW1lIFJUQ1AtTVVYLlxuICAgICAgICAgIGlmIChjYW5kLmNvbXBvbmVudCAhPT0gJzEnKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIEEgZGlydHkgaGFjayB0byBtYWtlIHNhbXBsZXMgd29yay5cbiAgICAgICAgICBpZiAoY2FuZC50eXBlID09PSAnZW5kT2ZDYW5kaWRhdGVzJykge1xuICAgICAgICAgICAgY2FuZCA9IHt9O1xuICAgICAgICAgIH1cbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKGNhbmQpO1xuXG4gICAgICAgICAgLy8gdXBkYXRlIHRoZSByZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICB2YXIgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICBzZWN0aW9uc1ttTGluZUluZGV4ICsgMV0gKz0gKGNhbmQudHlwZSA/IGNhbmRpZGF0ZS5jYW5kaWRhdGUudHJpbSgpXG4gICAgICAgICAgICAgIDogJ2E9ZW5kLW9mLWNhbmRpZGF0ZXMnKSArICdcXHJcXG4nO1xuICAgICAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSAmJiB0eXBlb2YgYXJndW1lbnRzWzFdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0U3RhdHMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBwcm9taXNlcyA9IFtdO1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBbJ3J0cFNlbmRlcicsICdydHBSZWNlaXZlcicsICdpY2VHYXRoZXJlcicsICdpY2VUcmFuc3BvcnQnLFxuICAgICAgICAgICAgJ2R0bHNUcmFuc3BvcnQnXS5mb3JFYWNoKGZ1bmN0aW9uKG1ldGhvZCkge1xuICAgICAgICAgICAgICBpZiAodHJhbnNjZWl2ZXJbbWV0aG9kXSkge1xuICAgICAgICAgICAgICAgIHByb21pc2VzLnB1c2godHJhbnNjZWl2ZXJbbWV0aG9kXS5nZXRTdGF0cygpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICAgIHZhciBjYiA9IGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicgJiZcbiAgICAgICAgICBhcmd1bWVudHNbMV07XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSkge1xuICAgICAgICAvLyBzaGltIGdldFN0YXRzIHdpdGggbWFwbGlrZSBzdXBwb3J0XG4gICAgICAgIHZhciByZXN1bHRzID0gbmV3IE1hcCgpO1xuICAgICAgICBQcm9taXNlLmFsbChwcm9taXNlcykudGhlbihmdW5jdGlvbihyZXMpIHtcbiAgICAgICAgICByZXMuZm9yRWFjaChmdW5jdGlvbihyZXN1bHQpIHtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHJlc3VsdCkuZm9yRWFjaChmdW5jdGlvbihpZCkge1xuICAgICAgICAgICAgICByZXN1bHRzLnNldChpZCwgcmVzdWx0W2lkXSk7XG4gICAgICAgICAgICAgIHJlc3VsdHNbaWRdID0gcmVzdWx0W2lkXTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIGlmIChjYikge1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoY2IsIDAsIHJlc3VsdHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXNvbHZlKHJlc3VsdHMpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH07XG4gIH1cbn07XG5cbi8vIEV4cG9zZSBwdWJsaWMgbWV0aG9kcy5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGVkZ2VTaGltLnNoaW1QZWVyQ29ubmVjdGlvbixcbiAgc2hpbUdldFVzZXJNZWRpYTogcmVxdWlyZSgnLi9nZXR1c2VybWVkaWEnKVxufTtcblxuXG5cbi8vLy8vLy8vLy8vLy8vLy8vL1xuLy8gV0VCUEFDSyBGT09URVJcbi8vIC4vfi93ZWJydGMtYWRhcHRlci9zcmMvanMvZWRnZS9lZGdlX3NoaW0uanNcbi8vIG1vZHVsZSBpZCA9IDQ2M1xuLy8gbW9kdWxlIGNodW5rcyA9IDAiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9");

TODO found
Open

    eval("/**\n * Copyright 2013-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n *\n */\n\n'use strict';\n\nvar _assign = __webpack_require__(176);\n\nvar CallbackQueue = __webpack_require__(230);\nvar PooledClass = __webpack_require__(223);\nvar ReactBrowserEventEmitter = __webpack_require__(278);\nvar ReactInputSelection = __webpack_require__(319);\nvar ReactInstrumentation = __webpack_require__(235);\nvar Transaction = __webpack_require__(241);\nvar ReactUpdateQueue = __webpack_require__(308);\n\n/**\n * Ensures that, when possible, the selection range (currently selected text\n * input) is not disturbed by performing the transaction.\n */\nvar SELECTION_RESTORATION = {\n  /**\n   * @return {Selection} Selection information.\n   */\n  initialize: ReactInputSelection.getSelectionInformation,\n  /**\n   * @param {Selection} sel Selection information returned from `initialize`.\n   */\n  close: ReactInputSelection.restoreSelection\n};\n\n/**\n * Suppresses events (blur/focus) that could be inadvertently dispatched due to\n * high level DOM manipulations (like temporarily removing a text input from the\n * DOM).\n */\nvar EVENT_SUPPRESSION = {\n  /**\n   * @return {boolean} The enabled status of `ReactBrowserEventEmitter` before\n   * the reconciliation.\n   */\n  initialize: function () {\n    var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();\n    ReactBrowserEventEmitter.setEnabled(false);\n    return currentlyEnabled;\n  },\n\n  /**\n   * @param {boolean} previouslyEnabled Enabled status of\n   *   `ReactBrowserEventEmitter` before the reconciliation occurred. `close`\n   *   restores the previous value.\n   */\n  close: function (previouslyEnabled) {\n    ReactBrowserEventEmitter.setEnabled(previouslyEnabled);\n  }\n};\n\n/**\n * Provides a queue for collecting `componentDidMount` and\n * `componentDidUpdate` callbacks during the transaction.\n */\nvar ON_DOM_READY_QUEUEING = {\n  /**\n   * Initializes the internal `onDOMReady` queue.\n   */\n  initialize: function () {\n    this.reactMountReady.reset();\n  },\n\n  /**\n   * After DOM is flushed, invoke all registered `onDOMReady` callbacks.\n   */\n  close: function () {\n    this.reactMountReady.notifyAll();\n  }\n};\n\n/**\n * Executed within the scope of the `Transaction` instance. Consider these as\n * being member methods, but with an implied ordering while being isolated from\n * each other.\n */\nvar TRANSACTION_WRAPPERS = [SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING];\n\nif (true) {\n  TRANSACTION_WRAPPERS.push({\n    initialize: ReactInstrumentation.debugTool.onBeginFlush,\n    close: ReactInstrumentation.debugTool.onEndFlush\n  });\n}\n\n/**\n * Currently:\n * - The order that these are listed in the transaction is critical:\n * - Suppresses events.\n * - Restores selection range.\n *\n * Future:\n * - Restore document/overflow scroll positions that were unintentionally\n *   modified via DOM insertions above the top viewport boundary.\n * - Implement/integrate with customized constraint based layout system and keep\n *   track of which dimensions must be remeasured.\n *\n * @class ReactReconcileTransaction\n */\nfunction ReactReconcileTransaction(useCreateElement) {\n  this.reinitializeTransaction();\n  // Only server-side rendering really needs this option (see\n  // `ReactServerRendering`), but server-side uses\n  // `ReactServerRenderingTransaction` instead. This option is here so that it's\n  // accessible and defaults to false when `ReactDOMComponent` and\n  // `ReactDOMTextComponent` checks it in `mountComponent`.`\n  this.renderToStaticMarkup = false;\n  this.reactMountReady = CallbackQueue.getPooled(null);\n  this.useCreateElement = useCreateElement;\n}\n\nvar Mixin = {\n  /**\n   * @see Transaction\n   * @abstract\n   * @final\n   * @return {array<object>} List of operation wrap procedures.\n   *   TODO: convert to array<TransactionWrapper>\n   */\n  getTransactionWrappers: function () {\n    return TRANSACTION_WRAPPERS;\n  },\n\n  /**\n   * @return {object} The queue to collect `onDOMReady` callbacks with.\n   */\n  getReactMountReady: function () {\n    return this.reactMountReady;\n  },\n\n  /**\n   * @return {object} The queue to collect React async events.\n   */\n  getUpdateQueue: function () {\n    return ReactUpdateQueue;\n  },\n\n  /**\n   * Save current transaction state -- if the return value from this method is\n   * passed to `rollback`, the transaction will be reset to that state.\n   */\n  checkpoint: function () {\n    // reactMountReady is the our only stateful wrapper\n    return this.reactMountReady.checkpoint();\n  },\n\n  rollback: function (checkpoint) {\n    this.reactMountReady.rollback(checkpoint);\n  },\n\n  /**\n   * `PooledClass` looks for this, and will invoke this before allowing this\n   * instance to be reused.\n   */\n  destructor: function () {\n    CallbackQueue.release(this.reactMountReady);\n    this.reactMountReady = null;\n  }\n};\n\n_assign(ReactReconcileTransaction.prototype, Transaction, Mixin);\n\nPooledClass.addPoolingTo(ReactReconcileTransaction);\n\nmodule.exports = ReactReconcileTransaction;//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMzE4LmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9yZWFjdC1kb20vbGliL1JlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb24uanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgMjAxMy1wcmVzZW50LCBGYWNlYm9vaywgSW5jLlxuICogQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBCU0Qtc3R5bGUgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS4gQW4gYWRkaXRpb25hbCBncmFudFxuICogb2YgcGF0ZW50IHJpZ2h0cyBjYW4gYmUgZm91bmQgaW4gdGhlIFBBVEVOVFMgZmlsZSBpbiB0aGUgc2FtZSBkaXJlY3RvcnkuXG4gKlxuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIF9hc3NpZ24gPSByZXF1aXJlKCdvYmplY3QtYXNzaWduJyk7XG5cbnZhciBDYWxsYmFja1F1ZXVlID0gcmVxdWlyZSgnLi9DYWxsYmFja1F1ZXVlJyk7XG52YXIgUG9vbGVkQ2xhc3MgPSByZXF1aXJlKCcuL1Bvb2xlZENsYXNzJyk7XG52YXIgUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyID0gcmVxdWlyZSgnLi9SZWFjdEJyb3dzZXJFdmVudEVtaXR0ZXInKTtcbnZhciBSZWFjdElucHV0U2VsZWN0aW9uID0gcmVxdWlyZSgnLi9SZWFjdElucHV0U2VsZWN0aW9uJyk7XG52YXIgUmVhY3RJbnN0cnVtZW50YXRpb24gPSByZXF1aXJlKCcuL1JlYWN0SW5zdHJ1bWVudGF0aW9uJyk7XG52YXIgVHJhbnNhY3Rpb24gPSByZXF1aXJlKCcuL1RyYW5zYWN0aW9uJyk7XG52YXIgUmVhY3RVcGRhdGVRdWV1ZSA9IHJlcXVpcmUoJy4vUmVhY3RVcGRhdGVRdWV1ZScpO1xuXG4vKipcbiAqIEVuc3VyZXMgdGhhdCwgd2hlbiBwb3NzaWJsZSwgdGhlIHNlbGVjdGlvbiByYW5nZSAoY3VycmVudGx5IHNlbGVjdGVkIHRleHRcbiAqIGlucHV0KSBpcyBub3QgZGlzdHVyYmVkIGJ5IHBlcmZvcm1pbmcgdGhlIHRyYW5zYWN0aW9uLlxuICovXG52YXIgU0VMRUNUSU9OX1JFU1RPUkFUSU9OID0ge1xuICAvKipcbiAgICogQHJldHVybiB7U2VsZWN0aW9ufSBTZWxlY3Rpb24gaW5mb3JtYXRpb24uXG4gICAqL1xuICBpbml0aWFsaXplOiBSZWFjdElucHV0U2VsZWN0aW9uLmdldFNlbGVjdGlvbkluZm9ybWF0aW9uLFxuICAvKipcbiAgICogQHBhcmFtIHtTZWxlY3Rpb259IHNlbCBTZWxlY3Rpb24gaW5mb3JtYXRpb24gcmV0dXJuZWQgZnJvbSBgaW5pdGlhbGl6ZWAuXG4gICAqL1xuICBjbG9zZTogUmVhY3RJbnB1dFNlbGVjdGlvbi5yZXN0b3JlU2VsZWN0aW9uXG59O1xuXG4vKipcbiAqIFN1cHByZXNzZXMgZXZlbnRzIChibHVyL2ZvY3VzKSB0aGF0IGNvdWxkIGJlIGluYWR2ZXJ0ZW50bHkgZGlzcGF0Y2hlZCBkdWUgdG9cbiAqIGhpZ2ggbGV2ZWwgRE9NIG1hbmlwdWxhdGlvbnMgKGxpa2UgdGVtcG9yYXJpbHkgcmVtb3ZpbmcgYSB0ZXh0IGlucHV0IGZyb20gdGhlXG4gKiBET00pLlxuICovXG52YXIgRVZFTlRfU1VQUFJFU1NJT04gPSB7XG4gIC8qKlxuICAgKiBAcmV0dXJuIHtib29sZWFufSBUaGUgZW5hYmxlZCBzdGF0dXMgb2YgYFJlYWN0QnJvd3NlckV2ZW50RW1pdHRlcmAgYmVmb3JlXG4gICAqIHRoZSByZWNvbmNpbGlhdGlvbi5cbiAgICovXG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgY3VycmVudGx5RW5hYmxlZCA9IFJlYWN0QnJvd3NlckV2ZW50RW1pdHRlci5pc0VuYWJsZWQoKTtcbiAgICBSZWFjdEJyb3dzZXJFdmVudEVtaXR0ZXIuc2V0RW5hYmxlZChmYWxzZSk7XG4gICAgcmV0dXJuIGN1cnJlbnRseUVuYWJsZWQ7XG4gIH0sXG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gcHJldmlvdXNseUVuYWJsZWQgRW5hYmxlZCBzdGF0dXMgb2ZcbiAgICogICBgUmVhY3RCcm93c2VyRXZlbnRFbWl0dGVyYCBiZWZvcmUgdGhlIHJlY29uY2lsaWF0aW9uIG9jY3VycmVkLiBgY2xvc2VgXG4gICAqICAgcmVzdG9yZXMgdGhlIHByZXZpb3VzIHZhbHVlLlxuICAgKi9cbiAgY2xvc2U6IGZ1bmN0aW9uIChwcmV2aW91c2x5RW5hYmxlZCkge1xuICAgIFJlYWN0QnJvd3NlckV2ZW50RW1pdHRlci5zZXRFbmFibGVkKHByZXZpb3VzbHlFbmFibGVkKTtcbiAgfVxufTtcblxuLyoqXG4gKiBQcm92aWRlcyBhIHF1ZXVlIGZvciBjb2xsZWN0aW5nIGBjb21wb25lbnREaWRNb3VudGAgYW5kXG4gKiBgY29tcG9uZW50RGlkVXBkYXRlYCBjYWxsYmFja3MgZHVyaW5nIHRoZSB0cmFuc2FjdGlvbi5cbiAqL1xudmFyIE9OX0RPTV9SRUFEWV9RVUVVRUlORyA9IHtcbiAgLyoqXG4gICAqIEluaXRpYWxpemVzIHRoZSBpbnRlcm5hbCBgb25ET01SZWFkeWAgcXVldWUuXG4gICAqL1xuICBpbml0aWFsaXplOiBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5yZWFjdE1vdW50UmVhZHkucmVzZXQoKTtcbiAgfSxcblxuICAvKipcbiAgICogQWZ0ZXIgRE9NIGlzIGZsdXNoZWQsIGludm9rZSBhbGwgcmVnaXN0ZXJlZCBgb25ET01SZWFkeWAgY2FsbGJhY2tzLlxuICAgKi9cbiAgY2xvc2U6IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnJlYWN0TW91bnRSZWFkeS5ub3RpZnlBbGwoKTtcbiAgfVxufTtcblxuLyoqXG4gKiBFeGVjdXRlZCB3aXRoaW4gdGhlIHNjb3BlIG9mIHRoZSBgVHJhbnNhY3Rpb25gIGluc3RhbmNlLiBDb25zaWRlciB0aGVzZSBhc1xuICogYmVpbmcgbWVtYmVyIG1ldGhvZHMsIGJ1dCB3aXRoIGFuIGltcGxpZWQgb3JkZXJpbmcgd2hpbGUgYmVpbmcgaXNvbGF0ZWQgZnJvbVxuICogZWFjaCBvdGhlci5cbiAqL1xudmFyIFRSQU5TQUNUSU9OX1dSQVBQRVJTID0gW1NFTEVDVElPTl9SRVNUT1JBVElPTiwgRVZFTlRfU1VQUFJFU1NJT04sIE9OX0RPTV9SRUFEWV9RVUVVRUlOR107XG5cbmlmIChwcm9jZXNzLmVudi5OT0RFX0VOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4gIFRSQU5TQUNUSU9OX1dSQVBQRVJTLnB1c2goe1xuICAgIGluaXRpYWxpemU6IFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vbkJlZ2luRmx1c2gsXG4gICAgY2xvc2U6IFJlYWN0SW5zdHJ1bWVudGF0aW9uLmRlYnVnVG9vbC5vbkVuZEZsdXNoXG4gIH0pO1xufVxuXG4vKipcbiAqIEN1cnJlbnRseTpcbiAqIC0gVGhlIG9yZGVyIHRoYXQgdGhlc2UgYXJlIGxpc3RlZCBpbiB0aGUgdHJhbnNhY3Rpb24gaXMgY3JpdGljYWw6XG4gKiAtIFN1cHByZXNzZXMgZXZlbnRzLlxuICogLSBSZXN0b3JlcyBzZWxlY3Rpb24gcmFuZ2UuXG4gKlxuICogRnV0dXJlOlxuICogLSBSZXN0b3JlIGRvY3VtZW50L292ZXJmbG93IHNjcm9sbCBwb3NpdGlvbnMgdGhhdCB3ZXJlIHVuaW50ZW50aW9uYWxseVxuICogICBtb2RpZmllZCB2aWEgRE9NIGluc2VydGlvbnMgYWJvdmUgdGhlIHRvcCB2aWV3cG9ydCBib3VuZGFyeS5cbiAqIC0gSW1wbGVtZW50L2ludGVncmF0ZSB3aXRoIGN1c3RvbWl6ZWQgY29uc3RyYWludCBiYXNlZCBsYXlvdXQgc3lzdGVtIGFuZCBrZWVwXG4gKiAgIHRyYWNrIG9mIHdoaWNoIGRpbWVuc2lvbnMgbXVzdCBiZSByZW1lYXN1cmVkLlxuICpcbiAqIEBjbGFzcyBSZWFjdFJlY29uY2lsZVRyYW5zYWN0aW9uXG4gKi9cbmZ1bmN0aW9uIFJlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb24odXNlQ3JlYXRlRWxlbWVudCkge1xuICB0aGlzLnJlaW5pdGlhbGl6ZVRyYW5zYWN0aW9uKCk7XG4gIC8vIE9ubHkgc2VydmVyLXNpZGUgcmVuZGVyaW5nIHJlYWxseSBuZWVkcyB0aGlzIG9wdGlvbiAoc2VlXG4gIC8vIGBSZWFjdFNlcnZlclJlbmRlcmluZ2ApLCBidXQgc2VydmVyLXNpZGUgdXNlc1xuICAvLyBgUmVhY3RTZXJ2ZXJSZW5kZXJpbmdUcmFuc2FjdGlvbmAgaW5zdGVhZC4gVGhpcyBvcHRpb24gaXMgaGVyZSBzbyB0aGF0IGl0J3NcbiAgLy8gYWNjZXNzaWJsZSBhbmQgZGVmYXVsdHMgdG8gZmFsc2Ugd2hlbiBgUmVhY3RET01Db21wb25lbnRgIGFuZFxuICAvLyBgUmVhY3RET01UZXh0Q29tcG9uZW50YCBjaGVja3MgaXQgaW4gYG1vdW50Q29tcG9uZW50YC5gXG4gIHRoaXMucmVuZGVyVG9TdGF0aWNNYXJrdXAgPSBmYWxzZTtcbiAgdGhpcy5yZWFjdE1vdW50UmVhZHkgPSBDYWxsYmFja1F1ZXVlLmdldFBvb2xlZChudWxsKTtcbiAgdGhpcy51c2VDcmVhdGVFbGVtZW50ID0gdXNlQ3JlYXRlRWxlbWVudDtcbn1cblxudmFyIE1peGluID0ge1xuICAvKipcbiAgICogQHNlZSBUcmFuc2FjdGlvblxuICAgKiBAYWJzdHJhY3RcbiAgICogQGZpbmFsXG4gICAqIEByZXR1cm4ge2FycmF5PG9iamVjdD59IExpc3Qgb2Ygb3BlcmF0aW9uIHdyYXAgcHJvY2VkdXJlcy5cbiAgICogICBUT0RPOiBjb252ZXJ0IHRvIGFycmF5PFRyYW5zYWN0aW9uV3JhcHBlcj5cbiAgICovXG4gIGdldFRyYW5zYWN0aW9uV3JhcHBlcnM6IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gVFJBTlNBQ1RJT05fV1JBUFBFUlM7XG4gIH0sXG5cbiAgLyoqXG4gICAqIEByZXR1cm4ge29iamVjdH0gVGhlIHF1ZXVlIHRvIGNvbGxlY3QgYG9uRE9NUmVhZHlgIGNhbGxiYWNrcyB3aXRoLlxuICAgKi9cbiAgZ2V0UmVhY3RNb3VudFJlYWR5OiBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhY3RNb3VudFJlYWR5O1xuICB9LFxuXG4gIC8qKlxuICAgKiBAcmV0dXJuIHtvYmplY3R9IFRoZSBxdWV1ZSB0byBjb2xsZWN0IFJlYWN0IGFzeW5jIGV2ZW50cy5cbiAgICovXG4gIGdldFVwZGF0ZVF1ZXVlOiBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIFJlYWN0VXBkYXRlUXVldWU7XG4gIH0sXG5cbiAgLyoqXG4gICAqIFNhdmUgY3VycmVudCB0cmFuc2FjdGlvbiBzdGF0ZSAtLSBpZiB0aGUgcmV0dXJuIHZhbHVlIGZyb20gdGhpcyBtZXRob2QgaXNcbiAgICogcGFzc2VkIHRvIGByb2xsYmFja2AsIHRoZSB0cmFuc2FjdGlvbiB3aWxsIGJlIHJlc2V0IHRvIHRoYXQgc3RhdGUuXG4gICAqL1xuICBjaGVja3BvaW50OiBmdW5jdGlvbiAoKSB7XG4gICAgLy8gcmVhY3RNb3VudFJlYWR5IGlzIHRoZSBvdXIgb25seSBzdGF0ZWZ1bCB3cmFwcGVyXG4gICAgcmV0dXJuIHRoaXMucmVhY3RNb3VudFJlYWR5LmNoZWNrcG9pbnQoKTtcbiAgfSxcblxuICByb2xsYmFjazogZnVuY3Rpb24gKGNoZWNrcG9pbnQpIHtcbiAgICB0aGlzLnJlYWN0TW91bnRSZWFkeS5yb2xsYmFjayhjaGVja3BvaW50KTtcbiAgfSxcblxuICAvKipcbiAgICogYFBvb2xlZENsYXNzYCBsb29rcyBmb3IgdGhpcywgYW5kIHdpbGwgaW52b2tlIHRoaXMgYmVmb3JlIGFsbG93aW5nIHRoaXNcbiAgICogaW5zdGFuY2UgdG8gYmUgcmV1c2VkLlxuICAgKi9cbiAgZGVzdHJ1Y3RvcjogZnVuY3Rpb24gKCkge1xuICAgIENhbGxiYWNrUXVldWUucmVsZWFzZSh0aGlzLnJlYWN0TW91bnRSZWFkeSk7XG4gICAgdGhpcy5yZWFjdE1vdW50UmVhZHkgPSBudWxsO1xuICB9XG59O1xuXG5fYXNzaWduKFJlYWN0UmVjb25jaWxlVHJhbnNhY3Rpb24ucHJvdG90eXBlLCBUcmFuc2FjdGlvbiwgTWl4aW4pO1xuXG5Qb29sZWRDbGFzcy5hZGRQb29saW5nVG8oUmVhY3RSZWNvbmNpbGVUcmFuc2FjdGlvbik7XG5cbm1vZHVsZS5leHBvcnRzID0gUmVhY3RSZWNvbmNpbGVUcmFuc2FjdGlvbjtcblxuXG4vLy8vLy8vLy8vLy8vLy8vLy9cbi8vIFdFQlBBQ0sgRk9PVEVSXG4vLyAuL34vcmVhY3QtZG9tL2xpYi9SZWFjdFJlY29uY2lsZVRyYW5zYWN0aW9uLmpzXG4vLyBtb2R1bGUgaWQgPSAzMThcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=");

FIXME found
Open

    eval("/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar SDPUtils = __webpack_require__(452);\nvar browserDetails = __webpack_require__(460).browserDetails;\n\nvar edgeShim = {\n  shimPeerConnection: function() {\n    if (window.RTCIceGatherer) {\n      // ORTC defines an RTCIceCandidate object but no constructor.\n      // Not implemented in Edge.\n      if (!window.RTCIceCandidate) {\n        window.RTCIceCandidate = function(args) {\n          return args;\n        };\n      }\n      // ORTC does not have a session description object but\n      // other browsers (i.e. Chrome) that will support both PC and ORTC\n      // in the future might have this defined already.\n      if (!window.RTCSessionDescription) {\n        window.RTCSessionDescription = function(args) {\n          return args;\n        };\n      }\n      // this adds an additional event listener to MediaStrackTrack that signals\n      // when a tracks enabled property was changed.\n      var origMSTEnabled = Object.getOwnPropertyDescriptor(\n          MediaStreamTrack.prototype, 'enabled');\n      Object.defineProperty(MediaStreamTrack.prototype, 'enabled', {\n        set: function(value) {\n          origMSTEnabled.set.call(this, value);\n          var ev = new Event('enabled');\n          ev.enabled = value;\n          this.dispatchEvent(ev);\n        }\n      });\n    }\n\n    window.RTCPeerConnection = function(config) {\n      var self = this;\n\n      var _eventTarget = document.createDocumentFragment();\n      ['addEventListener', 'removeEventListener', 'dispatchEvent']\n          .forEach(function(method) {\n            self[method] = _eventTarget[method].bind(_eventTarget);\n          });\n\n      this.onicecandidate = null;\n      this.onaddstream = null;\n      this.ontrack = null;\n      this.onremovestream = null;\n      this.onsignalingstatechange = null;\n      this.oniceconnectionstatechange = null;\n      this.onnegotiationneeded = null;\n      this.ondatachannel = null;\n\n      this.localStreams = [];\n      this.remoteStreams = [];\n      this.getLocalStreams = function() {\n        return self.localStreams;\n      };\n      this.getRemoteStreams = function() {\n        return self.remoteStreams;\n      };\n\n      this.localDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.remoteDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.signalingState = 'stable';\n      this.iceConnectionState = 'new';\n      this.iceGatheringState = 'new';\n\n      this.iceOptions = {\n        gatherPolicy: 'all',\n        iceServers: []\n      };\n      if (config && config.iceTransportPolicy) {\n        switch (config.iceTransportPolicy) {\n          case 'all':\n          case 'relay':\n            this.iceOptions.gatherPolicy = config.iceTransportPolicy;\n            break;\n          case 'none':\n            // FIXME: remove once implementation and spec have added this.\n            throw new TypeError('iceTransportPolicy \"none\" not supported');\n          default:\n            // don't set iceTransportPolicy.\n            break;\n        }\n      }\n      this.usingBundle = config && config.bundlePolicy === 'max-bundle';\n\n      if (config && config.iceServers) {\n        // Edge does not like\n        // 1) stun:\n        // 2) turn: that does not have all of turn:host:port?transport=udp\n        // 3) turn: with ipv6 addresses\n        var iceServers = JSON.parse(JSON.stringify(config.iceServers));\n        this.iceOptions.iceServers = iceServers.filter(function(server) {\n          if (server && server.urls) {\n            var urls = server.urls;\n            if (typeof urls === 'string') {\n              urls = [urls];\n            }\n            urls = urls.filter(function(url) {\n              return (url.indexOf('turn:') === 0 &&\n                  url.indexOf('transport=udp') !== -1 &&\n                  url.indexOf('turn:[') === -1) ||\n                  (url.indexOf('stun:') === 0 &&\n                    browserDetails.version >= 14393);\n            })[0];\n            return !!urls;\n          }\n          return false;\n        });\n      }\n      this._config = config;\n\n      // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n      // everything that is needed to describe a SDP m-line.\n      this.transceivers = [];\n\n      // since the iceGatherer is currently created in createOffer but we\n      // must not emit candidates until after setLocalDescription we buffer\n      // them in this array.\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {\n      var self = this;\n      var sections = SDPUtils.splitSections(self.localDescription.sdp);\n      // FIXME: need to apply ice candidates in a way which is async but\n      // in-order\n      this._localIceCandidatesBuffer.forEach(function(event) {\n        var end = !event.candidate || Object.keys(event.candidate).length === 0;\n        if (end) {\n          for (var j = 1; j < sections.length; j++) {\n            if (sections[j].indexOf('\\r\\na=end-of-candidates\\r\\n') === -1) {\n              sections[j] += 'a=end-of-candidates\\r\\n';\n            }\n          }\n        } else if (event.candidate.candidate.indexOf('typ endOfCandidates')\n            === -1) {\n          sections[event.candidate.sdpMLineIndex + 1] +=\n              'a=' + event.candidate.candidate + '\\r\\n';\n        }\n        self.localDescription.sdp = sections.join('');\n        self.dispatchEvent(event);\n        if (self.onicecandidate !== null) {\n          self.onicecandidate(event);\n        }\n        if (!event.candidate && self.iceGatheringState !== 'complete') {\n          var complete = self.transceivers.every(function(transceiver) {\n            return transceiver.iceGatherer &&\n                transceiver.iceGatherer.state === 'completed';\n          });\n          if (complete) {\n            self.iceGatheringState = 'complete';\n          }\n        }\n      });\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype.getConfiguration = function() {\n      return this._config;\n    };\n\n    window.RTCPeerConnection.prototype.addStream = function(stream) {\n      // Clone is necessary for local demos mostly, attaching directly\n      // to two different senders does not work (build 10547).\n      var clonedStream = stream.clone();\n      stream.getTracks().forEach(function(track, idx) {\n        var clonedTrack = clonedStream.getTracks()[idx];\n        track.addEventListener('enabled', function(event) {\n          clonedTrack.enabled = event.enabled;\n        });\n      });\n      this.localStreams.push(clonedStream);\n      this._maybeFireNegotiationNeeded();\n    };\n\n    window.RTCPeerConnection.prototype.removeStream = function(stream) {\n      var idx = this.localStreams.indexOf(stream);\n      if (idx > -1) {\n        this.localStreams.splice(idx, 1);\n        this._maybeFireNegotiationNeeded();\n      }\n    };\n\n    window.RTCPeerConnection.prototype.getSenders = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpSender;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpSender;\n      });\n    };\n\n    window.RTCPeerConnection.prototype.getReceivers = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpReceiver;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpReceiver;\n      });\n    };\n\n    // Determines the intersection of local and remote capabilities.\n    window.RTCPeerConnection.prototype._getCommonCapabilities =\n        function(localCapabilities, remoteCapabilities) {\n          var commonCapabilities = {\n            codecs: [],\n            headerExtensions: [],\n            fecMechanisms: []\n          };\n          localCapabilities.codecs.forEach(function(lCodec) {\n            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n              var rCodec = remoteCapabilities.codecs[i];\n              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n                  lCodec.clockRate === rCodec.clockRate) {\n                // number of channels is the highest common number of channels\n                rCodec.numChannels = Math.min(lCodec.numChannels,\n                    rCodec.numChannels);\n                // push rCodec so we reply with offerer payload type\n                commonCapabilities.codecs.push(rCodec);\n\n                // determine common feedback mechanisms\n                rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {\n                  for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {\n                    if (lCodec.rtcpFeedback[j].type === fb.type &&\n                        lCodec.rtcpFeedback[j].parameter === fb.parameter) {\n                      return true;\n                    }\n                  }\n                  return false;\n                });\n                // FIXME: also need to determine .parameters\n                //  see https://github.com/openpeer/ortc/issues/569\n                break;\n              }\n            }\n          });\n\n          localCapabilities.headerExtensions\n              .forEach(function(lHeaderExtension) {\n                for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n                     i++) {\n                  var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n                  if (lHeaderExtension.uri === rHeaderExtension.uri) {\n                    commonCapabilities.headerExtensions.push(rHeaderExtension);\n                    break;\n                  }\n                }\n              });\n\n          // FIXME: fecMechanisms\n          return commonCapabilities;\n        };\n\n    // Create ICE gatherer, ICE transport and DTLS transport.\n    window.RTCPeerConnection.prototype._createIceAndDtlsTransports =\n        function(mid, sdpMLineIndex) {\n          var self = this;\n          var iceGatherer = new RTCIceGatherer(self.iceOptions);\n          var iceTransport = new RTCIceTransport(iceGatherer);\n          iceGatherer.onlocalcandidate = function(evt) {\n            var event = new Event('icecandidate');\n            event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n            var cand = evt.candidate;\n            var end = !cand || Object.keys(cand).length === 0;\n            // Edge emits an empty object for RTCIceCandidateComplete‥\n            if (end) {\n              // polyfill since RTCIceGatherer.state is not implemented in\n              // Edge 10547 yet.\n              if (iceGatherer.state === undefined) {\n                iceGatherer.state = 'completed';\n              }\n\n              // Emit a candidate with type endOfCandidates to make the samples\n              // work. Edge requires addIceCandidate with this empty candidate\n              // to start checking. The real solution is to signal\n              // end-of-candidates to the other side when getting the null\n              // candidate but some apps (like the samples) don't do that.\n              event.candidate.candidate =\n                  'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';\n            } else {\n              // RTCIceCandidate doesn't have a component, needs to be added\n              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;\n              event.candidate.candidate = SDPUtils.writeCandidate(cand);\n            }\n\n            // update local description.\n            var sections = SDPUtils.splitSections(self.localDescription.sdp);\n            if (event.candidate.candidate.indexOf('typ endOfCandidates')\n                === -1) {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=' + event.candidate.candidate + '\\r\\n';\n            } else {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=end-of-candidates\\r\\n';\n            }\n            self.localDescription.sdp = sections.join('');\n\n            var complete = self.transceivers.every(function(transceiver) {\n              return transceiver.iceGatherer &&\n                  transceiver.iceGatherer.state === 'completed';\n            });\n\n            // Emit candidate if localDescription is set.\n            // Also emits null candidate when all gatherers are complete.\n            switch (self.iceGatheringState) {\n              case 'new':\n                self._localIceCandidatesBuffer.push(event);\n                if (end && complete) {\n                  self._localIceCandidatesBuffer.push(\n                      new Event('icecandidate'));\n                }\n                break;\n              case 'gathering':\n                self._emitBufferedCandidates();\n                self.dispatchEvent(event);\n                if (self.onicecandidate !== null) {\n                  self.onicecandidate(event);\n                }\n                if (complete) {\n                  self.dispatchEvent(new Event('icecandidate'));\n                  if (self.onicecandidate !== null) {\n                    self.onicecandidate(new Event('icecandidate'));\n                  }\n                  self.iceGatheringState = 'complete';\n                }\n                break;\n              case 'complete':\n                // should not happen... currently!\n                break;\n              default: // no-op.\n                break;\n            }\n          };\n          iceTransport.onicestatechange = function() {\n            self._updateConnectionState();\n          };\n\n          var dtlsTransport = new RTCDtlsTransport(iceTransport);\n          dtlsTransport.ondtlsstatechange = function() {\n            self._updateConnectionState();\n          };\n          dtlsTransport.onerror = function() {\n            // onerror does not set state to failed by itself.\n            dtlsTransport.state = 'failed';\n            self._updateConnectionState();\n          };\n\n          return {\n            iceGatherer: iceGatherer,\n            iceTransport: iceTransport,\n            dtlsTransport: dtlsTransport\n          };\n        };\n\n    // Start the RTP Sender and Receiver for a transceiver.\n    window.RTCPeerConnection.prototype._transceive = function(transceiver,\n        send, recv) {\n      var params = this._getCommonCapabilities(transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n      if (send && transceiver.rtpSender) {\n        params.encodings = transceiver.sendEncodingParameters;\n        params.rtcp = {\n          cname: SDPUtils.localCName\n        };\n        if (transceiver.recvEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpSender.send(params);\n      }\n      if (recv && transceiver.rtpReceiver) {\n        // remove RTX field in Edge 14942\n        if (transceiver.kind === 'video'\n            && transceiver.recvEncodingParameters) {\n          transceiver.recvEncodingParameters.forEach(function(p) {\n            delete p.rtx;\n          });\n        }\n        params.encodings = transceiver.recvEncodingParameters;\n        params.rtcp = {\n          cname: transceiver.cname\n        };\n        if (transceiver.sendEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpReceiver.receive(params);\n      }\n    };\n\n    window.RTCPeerConnection.prototype.setLocalDescription =\n        function(description) {\n          var self = this;\n          var sections;\n          var sessionpart;\n          if (description.type === 'offer') {\n            // FIXME: What was the purpose of this empty if statement?\n            // if (!this._pendingOffer) {\n            // } else {\n            if (this._pendingOffer) {\n              // VERY limited support for SDP munging. Limited to:\n              // * changing the order of codecs\n              sections = SDPUtils.splitSections(description.sdp);\n              sessionpart = sections.shift();\n              sections.forEach(function(mediaSection, sdpMLineIndex) {\n                var caps = SDPUtils.parseRtpParameters(mediaSection);\n                self._pendingOffer[sdpMLineIndex].localCapabilities = caps;\n              });\n              this.transceivers = this._pendingOffer;\n              delete this._pendingOffer;\n            }\n          } else if (description.type === 'answer') {\n            sections = SDPUtils.splitSections(self.remoteDescription.sdp);\n            sessionpart = sections.shift();\n            var isIceLite = SDPUtils.matchPrefix(sessionpart,\n                'a=ice-lite').length > 0;\n            sections.forEach(function(mediaSection, sdpMLineIndex) {\n              var transceiver = self.transceivers[sdpMLineIndex];\n              var iceGatherer = transceiver.iceGatherer;\n              var iceTransport = transceiver.iceTransport;\n              var dtlsTransport = transceiver.dtlsTransport;\n              var localCapabilities = transceiver.localCapabilities;\n              var remoteCapabilities = transceiver.remoteCapabilities;\n\n              var rejected = mediaSection.split('\\n', 1)[0]\n                  .split(' ', 2)[1] === '0';\n\n              if (!rejected && !transceiver.isDatachannel) {\n                var remoteIceParameters = SDPUtils.getIceParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                  .map(function(cand) {\n                    return SDPUtils.parseCandidate(cand);\n                  })\n                  .filter(function(cand) {\n                    return cand.component === '1';\n                  });\n                  // ice-lite only includes host candidates in the SDP so we can\n                  // use setRemoteCandidates (which implies an\n                  // RTCIceCandidateComplete)\n                  if (cands.length) {\n                    iceTransport.setRemoteCandidates(cands);\n                  }\n                }\n                var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  remoteDtlsParameters.role = 'server';\n                }\n\n                if (!self.usingBundle || sdpMLineIndex === 0) {\n                  iceTransport.start(iceGatherer, remoteIceParameters,\n                      isIceLite ? 'controlling' : 'controlled');\n                  dtlsTransport.start(remoteDtlsParameters);\n                }\n\n                // Calculate intersection of capabilities.\n                var params = self._getCommonCapabilities(localCapabilities,\n                    remoteCapabilities);\n\n                // Start the RTCRtpSender. The RTCRtpReceiver for this\n                // transceiver has already been started in setRemoteDescription.\n                self._transceive(transceiver,\n                    params.codecs.length > 0,\n                    false);\n              }\n            });\n          }\n\n          this.localDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-local-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n\n          // If a success callback was provided, emit ICE candidates after it\n          // has been executed. Otherwise, emit callback after the Promise is\n          // resolved.\n          var hasCallback = arguments.length > 1 &&\n            typeof arguments[1] === 'function';\n          if (hasCallback) {\n            var cb = arguments[1];\n            window.setTimeout(function() {\n              cb();\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              self._emitBufferedCandidates();\n            }, 0);\n          }\n          var p = Promise.resolve();\n          p.then(function() {\n            if (!hasCallback) {\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              // Usually candidates will be emitted earlier.\n              window.setTimeout(self._emitBufferedCandidates.bind(self), 500);\n            }\n          });\n          return p;\n        };\n\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n        function(description) {\n          var self = this;\n          var stream = new MediaStream();\n          var receiverList = [];\n          var sections = SDPUtils.splitSections(description.sdp);\n          var sessionpart = sections.shift();\n          var isIceLite = SDPUtils.matchPrefix(sessionpart,\n              'a=ice-lite').length > 0;\n          this.usingBundle = SDPUtils.matchPrefix(sessionpart,\n              'a=group:BUNDLE ').length > 0;\n          sections.forEach(function(mediaSection, sdpMLineIndex) {\n            var lines = SDPUtils.splitLines(mediaSection);\n            var mline = lines[0].substr(2).split(' ');\n            var kind = mline[0];\n            var rejected = mline[1] === '0';\n            var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n\n            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');\n            if (mid.length) {\n              mid = mid[0].substr(6);\n            } else {\n              mid = SDPUtils.generateIdentifier();\n            }\n\n            // Reject datachannels which are not implemented yet.\n            if (kind === 'application' && mline[2] === 'DTLS/SCTP') {\n              self.transceivers[sdpMLineIndex] = {\n                mid: mid,\n                isDatachannel: true\n              };\n              return;\n            }\n\n            var transceiver;\n            var iceGatherer;\n            var iceTransport;\n            var dtlsTransport;\n            var rtpSender;\n            var rtpReceiver;\n            var sendEncodingParameters;\n            var recvEncodingParameters;\n            var localCapabilities;\n\n            var track;\n            // FIXME: ensure the mediaSection has rtcp-mux set.\n            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n            var remoteIceParameters;\n            var remoteDtlsParameters;\n            if (!rejected) {\n              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters.role = 'client';\n            }\n            recvEncodingParameters =\n                SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n            var cname;\n            // Gets the first SSRC. Note that with RTX there might be multiple\n            // SSRCs.\n            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n                .map(function(line) {\n                  return SDPUtils.parseSsrcMedia(line);\n                })\n                .filter(function(obj) {\n                  return obj.attribute === 'cname';\n                })[0];\n            if (remoteSsrc) {\n              cname = remoteSsrc.value;\n            }\n\n            var isComplete = SDPUtils.matchPrefix(mediaSection,\n                'a=end-of-candidates', sessionpart).length > 0;\n            var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                .map(function(cand) {\n                  return SDPUtils.parseCandidate(cand);\n                })\n                .filter(function(cand) {\n                  return cand.component === '1';\n                });\n            if (description.type === 'offer' && !rejected) {\n              var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n                iceGatherer: self.transceivers[0].iceGatherer,\n                iceTransport: self.transceivers[0].iceTransport,\n                dtlsTransport: self.transceivers[0].dtlsTransport\n              } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n              if (isComplete) {\n                transports.iceTransport.setRemoteCandidates(cands);\n              }\n\n              localCapabilities = RTCRtpReceiver.getCapabilities(kind);\n\n              // filter RTX until additional stuff needed for RTX is implemented\n              // in adapter.js\n              localCapabilities.codecs = localCapabilities.codecs.filter(\n                  function(codec) {\n                    return codec.name !== 'rtx';\n                  });\n\n              sendEncodingParameters = [{\n                ssrc: (2 * sdpMLineIndex + 2) * 1001\n              }];\n\n              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n\n              track = rtpReceiver.track;\n              receiverList.push([track, rtpReceiver]);\n              // FIXME: not correct when there are multiple streams but that is\n              // not currently supported in this shim.\n              stream.addTrack(track);\n\n              // FIXME: look at direction.\n              if (self.localStreams.length > 0 &&\n                  self.localStreams[0].getTracks().length >= sdpMLineIndex) {\n                var localTrack;\n                if (kind === 'audio') {\n                  localTrack = self.localStreams[0].getAudioTracks()[0];\n                } else if (kind === 'video') {\n                  localTrack = self.localStreams[0].getVideoTracks()[0];\n                }\n                if (localTrack) {\n                  rtpSender = new RTCRtpSender(localTrack,\n                      transports.dtlsTransport);\n                }\n              }\n\n              self.transceivers[sdpMLineIndex] = {\n                iceGatherer: transports.iceGatherer,\n                iceTransport: transports.iceTransport,\n                dtlsTransport: transports.dtlsTransport,\n                localCapabilities: localCapabilities,\n                remoteCapabilities: remoteCapabilities,\n                rtpSender: rtpSender,\n                rtpReceiver: rtpReceiver,\n                kind: kind,\n                mid: mid,\n                cname: cname,\n                sendEncodingParameters: sendEncodingParameters,\n                recvEncodingParameters: recvEncodingParameters\n              };\n              // Start the RTCRtpReceiver now. The RTPSender is started in\n              // setLocalDescription.\n              self._transceive(self.transceivers[sdpMLineIndex],\n                  false,\n                  direction === 'sendrecv' || direction === 'sendonly');\n            } else if (description.type === 'answer' && !rejected) {\n              transceiver = self.transceivers[sdpMLineIndex];\n              iceGatherer = transceiver.iceGatherer;\n              iceTransport = transceiver.iceTransport;\n              dtlsTransport = transceiver.dtlsTransport;\n              rtpSender = transceiver.rtpSender;\n              rtpReceiver = transceiver.rtpReceiver;\n              sendEncodingParameters = transceiver.sendEncodingParameters;\n              localCapabilities = transceiver.localCapabilities;\n\n              self.transceivers[sdpMLineIndex].recvEncodingParameters =\n                  recvEncodingParameters;\n              self.transceivers[sdpMLineIndex].remoteCapabilities =\n                  remoteCapabilities;\n              self.transceivers[sdpMLineIndex].cname = cname;\n\n              if ((isIceLite || isComplete) && cands.length) {\n                iceTransport.setRemoteCandidates(cands);\n              }\n              if (!self.usingBundle || sdpMLineIndex === 0) {\n                iceTransport.start(iceGatherer, remoteIceParameters,\n                    'controlling');\n                dtlsTransport.start(remoteDtlsParameters);\n              }\n\n              self._transceive(transceiver,\n                  direction === 'sendrecv' || direction === 'recvonly',\n                  direction === 'sendrecv' || direction === 'sendonly');\n\n              if (rtpReceiver &&\n                  (direction === 'sendrecv' || direction === 'sendonly')) {\n                track = rtpReceiver.track;\n                receiverList.push([track, rtpReceiver]);\n                stream.addTrack(track);\n              } else {\n                // FIXME: actually the receiver should be created later.\n                delete transceiver.rtpReceiver;\n              }\n            }\n          });\n\n          this.remoteDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-remote-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n          if (stream.getTracks().length) {\n            self.remoteStreams.push(stream);\n            window.setTimeout(function() {\n              var event = new Event('addstream');\n              event.stream = stream;\n              self.dispatchEvent(event);\n              if (self.onaddstream !== null) {\n                window.setTimeout(function() {\n                  self.onaddstream(event);\n                }, 0);\n              }\n\n              receiverList.forEach(function(item) {\n                var track = item[0];\n                var receiver = item[1];\n                var trackEvent = new Event('track');\n                trackEvent.track = track;\n                trackEvent.receiver = receiver;\n                trackEvent.streams = [stream];\n                self.dispatchEvent(event);\n                if (self.ontrack !== null) {\n                  window.setTimeout(function() {\n                    self.ontrack(trackEvent);\n                  }, 0);\n                }\n              });\n            }, 0);\n          }\n          if (arguments.length > 1 && typeof arguments[1] === 'function') {\n            window.setTimeout(arguments[1], 0);\n          }\n          return Promise.resolve();\n        };\n\n    window.RTCPeerConnection.prototype.close = function() {\n      this.transceivers.forEach(function(transceiver) {\n        /* not yet\n        if (transceiver.iceGatherer) {\n          transceiver.iceGatherer.close();\n        }\n        */\n        if (transceiver.iceTransport) {\n          transceiver.iceTransport.stop();\n        }\n        if (transceiver.dtlsTransport) {\n          transceiver.dtlsTransport.stop();\n        }\n        if (transceiver.rtpSender) {\n          transceiver.rtpSender.stop();\n        }\n        if (transceiver.rtpReceiver) {\n          transceiver.rtpReceiver.stop();\n        }\n      });\n      // FIXME: clean up tracks, local streams, remote streams, etc\n      this._updateSignalingState('closed');\n    };\n\n    // Update the signaling state.\n    window.RTCPeerConnection.prototype._updateSignalingState =\n        function(newState) {\n          this.signalingState = newState;\n          var event = new Event('signalingstatechange');\n          this.dispatchEvent(event);\n          if (this.onsignalingstatechange !== null) {\n            this.onsignalingstatechange(event);\n          }\n        };\n\n    // Determine whether to fire the negotiationneeded event.\n    window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =\n        function() {\n          // Fire away (for now).\n          var event = new Event('negotiationneeded');\n          this.dispatchEvent(event);\n          if (this.onnegotiationneeded !== null) {\n            this.onnegotiationneeded(event);\n          }\n        };\n\n    // Update the connection state.\n    window.RTCPeerConnection.prototype._updateConnectionState = function() {\n      var self = this;\n      var newState;\n      var states = {\n        'new': 0,\n        closed: 0,\n        connecting: 0,\n        checking: 0,\n        connected: 0,\n        completed: 0,\n        failed: 0\n      };\n      this.transceivers.forEach(function(transceiver) {\n        states[transceiver.iceTransport.state]++;\n        states[transceiver.dtlsTransport.state]++;\n      });\n      // ICETransport.completed and connected are the same for this purpose.\n      states.connected += states.completed;\n\n      newState = 'new';\n      if (states.failed > 0) {\n        newState = 'failed';\n      } else if (states.connecting > 0 || states.checking > 0) {\n        newState = 'connecting';\n      } else if (states.disconnected > 0) {\n        newState = 'disconnected';\n      } else if (states.new > 0) {\n        newState = 'new';\n      } else if (states.connected > 0 || states.completed > 0) {\n        newState = 'connected';\n      }\n\n      if (newState !== self.iceConnectionState) {\n        self.iceConnectionState = newState;\n        var event = new Event('iceconnectionstatechange');\n        this.dispatchEvent(event);\n        if (this.oniceconnectionstatechange !== null) {\n          this.oniceconnectionstatechange(event);\n        }\n      }\n    };\n\n    window.RTCPeerConnection.prototype.createOffer = function() {\n      var self = this;\n      if (this._pendingOffer) {\n        throw new Error('createOffer called while there is a pending offer.');\n      }\n      var offerOptions;\n      if (arguments.length === 1 && typeof arguments[0] !== 'function') {\n        offerOptions = arguments[0];\n      } else if (arguments.length === 3) {\n        offerOptions = arguments[2];\n      }\n\n      var tracks = [];\n      var numAudioTracks = 0;\n      var numVideoTracks = 0;\n      // Default to sendrecv.\n      if (this.localStreams.length) {\n        numAudioTracks = this.localStreams[0].getAudioTracks().length;\n        numVideoTracks = this.localStreams[0].getVideoTracks().length;\n      }\n      // Determine number of audio and video tracks we need to send/recv.\n      if (offerOptions) {\n        // Reject Chrome legacy constraints.\n        if (offerOptions.mandatory || offerOptions.optional) {\n          throw new TypeError(\n              'Legacy mandatory/optional constraints not supported.');\n        }\n        if (offerOptions.offerToReceiveAudio !== undefined) {\n          numAudioTracks = offerOptions.offerToReceiveAudio;\n        }\n        if (offerOptions.offerToReceiveVideo !== undefined) {\n          numVideoTracks = offerOptions.offerToReceiveVideo;\n        }\n      }\n      if (this.localStreams.length) {\n        // Push local streams.\n        this.localStreams[0].getTracks().forEach(function(track) {\n          tracks.push({\n            kind: track.kind,\n            track: track,\n            wantReceive: track.kind === 'audio' ?\n                numAudioTracks > 0 : numVideoTracks > 0\n          });\n          if (track.kind === 'audio') {\n            numAudioTracks--;\n          } else if (track.kind === 'video') {\n            numVideoTracks--;\n          }\n        });\n      }\n      // Create M-lines for recvonly streams.\n      while (numAudioTracks > 0 || numVideoTracks > 0) {\n        if (numAudioTracks > 0) {\n          tracks.push({\n            kind: 'audio',\n            wantReceive: true\n          });\n          numAudioTracks--;\n        }\n        if (numVideoTracks > 0) {\n          tracks.push({\n            kind: 'video',\n            wantReceive: true\n          });\n          numVideoTracks--;\n        }\n      }\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      var transceivers = [];\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        // For each track, create an ice gatherer, ice transport,\n        // dtls transport, potentially rtpsender and rtpreceiver.\n        var track = mline.track;\n        var kind = mline.kind;\n        var mid = SDPUtils.generateIdentifier();\n\n        var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n          iceGatherer: transceivers[0].iceGatherer,\n          iceTransport: transceivers[0].iceTransport,\n          dtlsTransport: transceivers[0].dtlsTransport\n        } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n        var localCapabilities = RTCRtpSender.getCapabilities(kind);\n        // filter RTX until additional stuff needed for RTX is implemented\n        // in adapter.js\n        localCapabilities.codecs = localCapabilities.codecs.filter(\n            function(codec) {\n              return codec.name !== 'rtx';\n            });\n        localCapabilities.codecs.forEach(function(codec) {\n          // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552\n          // by adding level-asymmetry-allowed=1\n          if (codec.name === 'H264' &&\n              codec.parameters['level-asymmetry-allowed'] === undefined) {\n            codec.parameters['level-asymmetry-allowed'] = '1';\n          }\n        });\n\n        var rtpSender;\n        var rtpReceiver;\n\n        // generate an ssrc now, to be used later in rtpSender.send\n        var sendEncodingParameters = [{\n          ssrc: (2 * sdpMLineIndex + 1) * 1001\n        }];\n        if (track) {\n          rtpSender = new RTCRtpSender(track, transports.dtlsTransport);\n        }\n\n        if (mline.wantReceive) {\n          rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n        }\n\n        transceivers[sdpMLineIndex] = {\n          iceGatherer: transports.iceGatherer,\n          iceTransport: transports.iceTransport,\n          dtlsTransport: transports.dtlsTransport,\n          localCapabilities: localCapabilities,\n          remoteCapabilities: null,\n          rtpSender: rtpSender,\n          rtpReceiver: rtpReceiver,\n          kind: kind,\n          mid: mid,\n          sendEncodingParameters: sendEncodingParameters,\n          recvEncodingParameters: null\n        };\n      });\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        var transceiver = transceivers[sdpMLineIndex];\n        sdp += SDPUtils.writeMediaSection(transceiver,\n            transceiver.localCapabilities, 'offer', self.localStreams[0]);\n      });\n\n      this._pendingOffer = transceivers;\n      var desc = new RTCSessionDescription({\n        type: 'offer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.createAnswer = function() {\n      var self = this;\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      this.transceivers.forEach(function(transceiver) {\n        if (transceiver.isDatachannel) {\n          sdp += 'm=application 0 DTLS/SCTP 5000\\r\\n' +\n              'c=IN IP4 0.0.0.0\\r\\n' +\n              'a=mid:' + transceiver.mid + '\\r\\n';\n          return;\n        }\n        // Calculate intersection of capabilities.\n        var commonCapabilities = self._getCommonCapabilities(\n            transceiver.localCapabilities,\n            transceiver.remoteCapabilities);\n\n        sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,\n            'answer', self.localStreams[0]);\n      });\n\n      var desc = new RTCSessionDescription({\n        type: 'answer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n      if (!candidate) {\n        this.transceivers.forEach(function(transceiver) {\n          transceiver.iceTransport.addRemoteCandidate({});\n        });\n      } else {\n        var mLineIndex = candidate.sdpMLineIndex;\n        if (candidate.sdpMid) {\n          for (var i = 0; i < this.transceivers.length; i++) {\n            if (this.transceivers[i].mid === candidate.sdpMid) {\n              mLineIndex = i;\n              break;\n            }\n          }\n        }\n        var transceiver = this.transceivers[mLineIndex];\n        if (transceiver) {\n          var cand = Object.keys(candidate.candidate).length > 0 ?\n              SDPUtils.parseCandidate(candidate.candidate) : {};\n          // Ignore Chrome's invalid candidates since Edge does not like them.\n          if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {\n            return;\n          }\n          // Ignore RTCP candidates, we assume RTCP-MUX.\n          if (cand.component !== '1') {\n            return;\n          }\n          // A dirty hack to make samples work.\n          if (cand.type === 'endOfCandidates') {\n            cand = {};\n          }\n          transceiver.iceTransport.addRemoteCandidate(cand);\n\n          // update the remoteDescription.\n          var sections = SDPUtils.splitSections(this.remoteDescription.sdp);\n          sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()\n              : 'a=end-of-candidates') + '\\r\\n';\n          this.remoteDescription.sdp = sections.join('');\n        }\n      }\n      if (arguments.length > 1 && typeof arguments[1] === 'function') {\n        window.setTimeout(arguments[1], 0);\n      }\n      return Promise.resolve();\n    };\n\n    window.RTCPeerConnection.prototype.getStats = function() {\n      var promises = [];\n      this.transceivers.forEach(function(transceiver) {\n        ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n            'dtlsTransport'].forEach(function(method) {\n              if (transceiver[method]) {\n                promises.push(transceiver[method].getStats());\n              }\n            });\n      });\n      var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&\n          arguments[1];\n      return new Promise(function(resolve) {\n        // shim getStats with maplike support\n        var results = new Map();\n        Promise.all(promises).then(function(res) {\n          res.forEach(function(result) {\n            Object.keys(result).forEach(function(id) {\n              results.set(id, result[id]);\n              results[id] = result[id];\n            });\n          });\n          if (cb) {\n            window.setTimeout(cb, 0, results);\n          }\n          resolve(results);\n        });\n      });\n    };\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimPeerConnection: edgeShim.shimPeerConnection,\n  shimGetUserMedia: __webpack_require__(464)\n};\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDYzLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy93ZWJydGMtYWRhcHRlci9zcmMvanMvZWRnZS9lZGdlX3NoaW0uanMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFdlYlJUQyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG4gLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbnZhciBTRFBVdGlscyA9IHJlcXVpcmUoJ3NkcCcpO1xudmFyIGJyb3dzZXJEZXRhaWxzID0gcmVxdWlyZSgnLi4vdXRpbHMnKS5icm93c2VyRGV0YWlscztcblxudmFyIGVkZ2VTaGltID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGZ1bmN0aW9uKCkge1xuICAgIGlmICh3aW5kb3cuUlRDSWNlR2F0aGVyZXIpIHtcbiAgICAgIC8vIE9SVEMgZGVmaW5lcyBhbiBSVENJY2VDYW5kaWRhdGUgb2JqZWN0IGJ1dCBubyBjb25zdHJ1Y3Rvci5cbiAgICAgIC8vIE5vdCBpbXBsZW1lbnRlZCBpbiBFZGdlLlxuICAgICAgaWYgKCF3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlKSB7XG4gICAgICAgIHdpbmRvdy5SVENJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbihhcmdzKSB7XG4gICAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICAvLyBPUlRDIGRvZXMgbm90IGhhdmUgYSBzZXNzaW9uIGRlc2NyaXB0aW9uIG9iamVjdCBidXRcbiAgICAgIC8vIG90aGVyIGJyb3dzZXJzIChpLmUuIENocm9tZSkgdGhhdCB3aWxsIHN1cHBvcnQgYm90aCBQQyBhbmQgT1JUQ1xuICAgICAgLy8gaW4gdGhlIGZ1dHVyZSBtaWdodCBoYXZlIHRoaXMgZGVmaW5lZCBhbHJlYWR5LlxuICAgICAgaWYgKCF3aW5kb3cuUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKSB7XG4gICAgICAgIHdpbmRvdy5SVENTZXNzaW9uRGVzY3JpcHRpb24gPSBmdW5jdGlvbihhcmdzKSB7XG4gICAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICAvLyB0aGlzIGFkZHMgYW4gYWRkaXRpb25hbCBldmVudCBsaXN0ZW5lciB0byBNZWRpYVN0cmFja1RyYWNrIHRoYXQgc2lnbmFsc1xuICAgICAgLy8gd2hlbiBhIHRyYWNrcyBlbmFibGVkIHByb3BlcnR5IHdhcyBjaGFuZ2VkLlxuICAgICAgdmFyIG9yaWdNU1RFbmFibGVkID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihcbiAgICAgICAgICBNZWRpYVN0cmVhbVRyYWNrLnByb3RvdHlwZSwgJ2VuYWJsZWQnKTtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShNZWRpYVN0cmVhbVRyYWNrLnByb3RvdHlwZSwgJ2VuYWJsZWQnLCB7XG4gICAgICAgIHNldDogZnVuY3Rpb24odmFsdWUpIHtcbiAgICAgICAgICBvcmlnTVNURW5hYmxlZC5zZXQuY2FsbCh0aGlzLCB2YWx1ZSk7XG4gICAgICAgICAgdmFyIGV2ID0gbmV3IEV2ZW50KCdlbmFibGVkJyk7XG4gICAgICAgICAgZXYuZW5hYmxlZCA9IHZhbHVlO1xuICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiA9IGZ1bmN0aW9uKGNvbmZpZykge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgICB2YXIgX2V2ZW50VGFyZ2V0ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xuICAgICAgWydhZGRFdmVudExpc3RlbmVyJywgJ3JlbW92ZUV2ZW50TGlzdGVuZXInLCAnZGlzcGF0Y2hFdmVudCddXG4gICAgICAgICAgLmZvckVhY2goZnVuY3Rpb24obWV0aG9kKSB7XG4gICAgICAgICAgICBzZWxmW21ldGhvZF0gPSBfZXZlbnRUYXJnZXRbbWV0aG9kXS5iaW5kKF9ldmVudFRhcmdldCk7XG4gICAgICAgICAgfSk7XG5cbiAgICAgIHRoaXMub25pY2VjYW5kaWRhdGUgPSBudWxsO1xuICAgICAgdGhpcy5vbmFkZHN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9udHJhY2sgPSBudWxsO1xuICAgICAgdGhpcy5vbnJlbW92ZXN0cmVhbSA9IG51bGw7XG4gICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBudWxsO1xuICAgICAgdGhpcy5vbmRhdGFjaGFubmVsID0gbnVsbDtcblxuICAgICAgdGhpcy5sb2NhbFN0cmVhbXMgPSBbXTtcbiAgICAgIHRoaXMucmVtb3RlU3RyZWFtcyA9IFtdO1xuICAgICAgdGhpcy5nZXRMb2NhbFN0cmVhbXMgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYubG9jYWxTdHJlYW1zO1xuICAgICAgfTtcbiAgICAgIHRoaXMuZ2V0UmVtb3RlU3RyZWFtcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gc2VsZi5yZW1vdGVTdHJlYW1zO1xuICAgICAgfTtcblxuICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICcnLFxuICAgICAgICBzZHA6ICcnXG4gICAgICB9KTtcbiAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24gPSBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgICAgdHlwZTogJycsXG4gICAgICAgIHNkcDogJydcbiAgICAgIH0pO1xuICAgICAgdGhpcy5zaWduYWxpbmdTdGF0ZSA9ICdzdGFibGUnO1xuICAgICAgdGhpcy5pY2VDb25uZWN0aW9uU3RhdGUgPSAnbmV3JztcbiAgICAgIHRoaXMuaWNlR2F0aGVyaW5nU3RhdGUgPSAnbmV3JztcblxuICAgICAgdGhpcy5pY2VPcHRpb25zID0ge1xuICAgICAgICBnYXRoZXJQb2xpY3k6ICdhbGwnLFxuICAgICAgICBpY2VTZXJ2ZXJzOiBbXVxuICAgICAgfTtcbiAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmljZVRyYW5zcG9ydFBvbGljeSkge1xuICAgICAgICBzd2l0Y2ggKGNvbmZpZy5pY2VUcmFuc3BvcnRQb2xpY3kpIHtcbiAgICAgICAgICBjYXNlICdhbGwnOlxuICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgIHRoaXMuaWNlT3B0aW9ucy5nYXRoZXJQb2xpY3kgPSBjb25maWcuaWNlVHJhbnNwb3J0UG9saWN5O1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnbm9uZSc6XG4gICAgICAgICAgICAvLyBGSVhNRTogcmVtb3ZlIG9uY2UgaW1wbGVtZW50YXRpb24gYW5kIHNwZWMgaGF2ZSBhZGRlZCB0aGlzLlxuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignaWNlVHJhbnNwb3J0UG9saWN5IFwibm9uZVwiIG5vdCBzdXBwb3J0ZWQnKTtcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgLy8gZG9uJ3Qgc2V0IGljZVRyYW5zcG9ydFBvbGljeS5cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLnVzaW5nQnVuZGxlID0gY29uZmlnICYmIGNvbmZpZy5idW5kbGVQb2xpY3kgPT09ICdtYXgtYnVuZGxlJztcblxuICAgICAgaWYgKGNvbmZpZyAmJiBjb25maWcuaWNlU2VydmVycykge1xuICAgICAgICAvLyBFZGdlIGRvZXMgbm90IGxpa2VcbiAgICAgICAgLy8gMSkgc3R1bjpcbiAgICAgICAgLy8gMikgdHVybjogdGhhdCBkb2VzIG5vdCBoYXZlIGFsbCBvZiB0dXJuOmhvc3Q6cG9ydD90cmFuc3BvcnQ9dWRwXG4gICAgICAgIC8vIDMpIHR1cm46IHdpdGggaXB2NiBhZGRyZXNzZXNcbiAgICAgICAgdmFyIGljZVNlcnZlcnMgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGNvbmZpZy5pY2VTZXJ2ZXJzKSk7XG4gICAgICAgIHRoaXMuaWNlT3B0aW9ucy5pY2VTZXJ2ZXJzID0gaWNlU2VydmVycy5maWx0ZXIoZnVuY3Rpb24oc2VydmVyKSB7XG4gICAgICAgICAgaWYgKHNlcnZlciAmJiBzZXJ2ZXIudXJscykge1xuICAgICAgICAgICAgdmFyIHVybHMgPSBzZXJ2ZXIudXJscztcbiAgICAgICAgICAgIGlmICh0eXBlb2YgdXJscyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgdXJscyA9IFt1cmxzXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHVybHMgPSB1cmxzLmZpbHRlcihmdW5jdGlvbih1cmwpIHtcbiAgICAgICAgICAgICAgcmV0dXJuICh1cmwuaW5kZXhPZigndHVybjonKSA9PT0gMCAmJlxuICAgICAgICAgICAgICAgICAgdXJsLmluZGV4T2YoJ3RyYW5zcG9ydD11ZHAnKSAhPT0gLTEgJiZcbiAgICAgICAgICAgICAgICAgIHVybC5pbmRleE9mKCd0dXJuOlsnKSA9PT0gLTEpIHx8XG4gICAgICAgICAgICAgICAgICAodXJsLmluZGV4T2YoJ3N0dW46JykgPT09IDAgJiZcbiAgICAgICAgICAgICAgICAgICAgYnJvd3NlckRldGFpbHMudmVyc2lvbiA+PSAxNDM5Myk7XG4gICAgICAgICAgICB9KVswXTtcbiAgICAgICAgICAgIHJldHVybiAhIXVybHM7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aGlzLl9jb25maWcgPSBjb25maWc7XG5cbiAgICAgIC8vIHBlci10cmFjayBpY2VHYXRoZXJzLCBpY2VUcmFuc3BvcnRzLCBkdGxzVHJhbnNwb3J0cywgcnRwU2VuZGVycywgLi4uXG4gICAgICAvLyBldmVyeXRoaW5nIHRoYXQgaXMgbmVlZGVkIHRvIGRlc2NyaWJlIGEgU0RQIG0tbGluZS5cbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzID0gW107XG5cbiAgICAgIC8vIHNpbmNlIHRoZSBpY2VHYXRoZXJlciBpcyBjdXJyZW50bHkgY3JlYXRlZCBpbiBjcmVhdGVPZmZlciBidXQgd2VcbiAgICAgIC8vIG11c3Qgbm90IGVtaXQgY2FuZGlkYXRlcyB1bnRpbCBhZnRlciBzZXRMb2NhbERlc2NyaXB0aW9uIHdlIGJ1ZmZlclxuICAgICAgLy8gdGhlbSBpbiB0aGlzIGFycmF5LlxuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAvLyBGSVhNRTogbmVlZCB0byBhcHBseSBpY2UgY2FuZGlkYXRlcyBpbiBhIHdheSB3aGljaCBpcyBhc3luYyBidXRcbiAgICAgIC8vIGluLW9yZGVyXG4gICAgICB0aGlzLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIuZm9yRWFjaChmdW5jdGlvbihldmVudCkge1xuICAgICAgICB2YXIgZW5kID0gIWV2ZW50LmNhbmRpZGF0ZSB8fCBPYmplY3Qua2V5cyhldmVudC5jYW5kaWRhdGUpLmxlbmd0aCA9PT0gMDtcbiAgICAgICAgaWYgKGVuZCkge1xuICAgICAgICAgIGZvciAodmFyIGogPSAxOyBqIDwgc2VjdGlvbnMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChzZWN0aW9uc1tqXS5pbmRleE9mKCdcXHJcXG5hPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJykgPT09IC0xKSB7XG4gICAgICAgICAgICAgIHNlY3Rpb25zW2pdICs9ICdhPWVuZC1vZi1jYW5kaWRhdGVzXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZS5pbmRleE9mKCd0eXAgZW5kT2ZDYW5kaWRhdGVzJylcbiAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgIHNlY3Rpb25zW2V2ZW50LmNhbmRpZGF0ZS5zZHBNTGluZUluZGV4ICsgMV0gKz1cbiAgICAgICAgICAgICAgJ2E9JyArIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgKyAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgICAgICBzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgc2VsZi5vbmljZWNhbmRpZGF0ZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFldmVudC5jYW5kaWRhdGUgJiYgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSAhPT0gJ2NvbXBsZXRlJykge1xuICAgICAgICAgIHZhciBjb21wbGV0ZSA9IHNlbGYudHJhbnNjZWl2ZXJzLmV2ZXJ5KGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5zdGF0ZSA9PT0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKGNvbXBsZXRlKSB7XG4gICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgdGhpcy5fbG9jYWxJY2VDYW5kaWRhdGVzQnVmZmVyID0gW107XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0Q29uZmlndXJhdGlvbiA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMuX2NvbmZpZztcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbihzdHJlYW0pIHtcbiAgICAgIC8vIENsb25lIGlzIG5lY2Vzc2FyeSBmb3IgbG9jYWwgZGVtb3MgbW9zdGx5LCBhdHRhY2hpbmcgZGlyZWN0bHlcbiAgICAgIC8vIHRvIHR3byBkaWZmZXJlbnQgc2VuZGVycyBkb2VzIG5vdCB3b3JrIChidWlsZCAxMDU0NykuXG4gICAgICB2YXIgY2xvbmVkU3RyZWFtID0gc3RyZWFtLmNsb25lKCk7XG4gICAgICBzdHJlYW0uZ2V0VHJhY2tzKCkuZm9yRWFjaChmdW5jdGlvbih0cmFjaywgaWR4KSB7XG4gICAgICAgIHZhciBjbG9uZWRUcmFjayA9IGNsb25lZFN0cmVhbS5nZXRUcmFja3MoKVtpZHhdO1xuICAgICAgICB0cmFjay5hZGRFdmVudExpc3RlbmVyKCdlbmFibGVkJywgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICBjbG9uZWRUcmFjay5lbmFibGVkID0gZXZlbnQuZW5hYmxlZDtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnB1c2goY2xvbmVkU3RyZWFtKTtcbiAgICAgIHRoaXMuX21heWJlRmlyZU5lZ290aWF0aW9uTmVlZGVkKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID0gZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICB2YXIgaWR4ID0gdGhpcy5sb2NhbFN0cmVhbXMuaW5kZXhPZihzdHJlYW0pO1xuICAgICAgaWYgKGlkeCA+IC0xKSB7XG4gICAgICAgIHRoaXMubG9jYWxTdHJlYW1zLnNwbGljZShpZHgsIDEpO1xuICAgICAgICB0aGlzLl9tYXliZUZpcmVOZWdvdGlhdGlvbk5lZWRlZCgpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFNlbmRlcnMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiB0aGlzLnRyYW5zY2VpdmVycy5maWx0ZXIoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuICEhdHJhbnNjZWl2ZXIucnRwU2VuZGVyO1xuICAgICAgfSlcbiAgICAgIC5tYXAoZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuIHRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFJlY2VpdmVycyA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMudHJhbnNjZWl2ZXJzLmZpbHRlcihmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICByZXR1cm4gISF0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgIH0pXG4gICAgICAubWFwKGZ1bmN0aW9uKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgIHJldHVybiB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvLyBEZXRlcm1pbmVzIHRoZSBpbnRlcnNlY3Rpb24gb2YgbG9jYWwgYW5kIHJlbW90ZSBjYXBhYmlsaXRpZXMuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzID1cbiAgICAgICAgZnVuY3Rpb24obG9jYWxDYXBhYmlsaXRpZXMsIHJlbW90ZUNhcGFiaWxpdGllcykge1xuICAgICAgICAgIHZhciBjb21tb25DYXBhYmlsaXRpZXMgPSB7XG4gICAgICAgICAgICBjb2RlY3M6IFtdLFxuICAgICAgICAgICAgaGVhZGVyRXh0ZW5zaW9uczogW10sXG4gICAgICAgICAgICBmZWNNZWNoYW5pc21zOiBbXVxuICAgICAgICAgIH07XG4gICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzLmZvckVhY2goZnVuY3Rpb24obENvZGVjKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJlbW90ZUNhcGFiaWxpdGllcy5jb2RlY3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgdmFyIHJDb2RlYyA9IHJlbW90ZUNhcGFiaWxpdGllcy5jb2RlY3NbaV07XG4gICAgICAgICAgICAgIGlmIChsQ29kZWMubmFtZS50b0xvd2VyQ2FzZSgpID09PSByQ29kZWMubmFtZS50b0xvd2VyQ2FzZSgpICYmXG4gICAgICAgICAgICAgICAgICBsQ29kZWMuY2xvY2tSYXRlID09PSByQ29kZWMuY2xvY2tSYXRlKSB7XG4gICAgICAgICAgICAgICAgLy8gbnVtYmVyIG9mIGNoYW5uZWxzIGlzIHRoZSBoaWdoZXN0IGNvbW1vbiBudW1iZXIgb2YgY2hhbm5lbHNcbiAgICAgICAgICAgICAgICByQ29kZWMubnVtQ2hhbm5lbHMgPSBNYXRoLm1pbihsQ29kZWMubnVtQ2hhbm5lbHMsXG4gICAgICAgICAgICAgICAgICAgIHJDb2RlYy5udW1DaGFubmVscyk7XG4gICAgICAgICAgICAgICAgLy8gcHVzaCByQ29kZWMgc28gd2UgcmVwbHkgd2l0aCBvZmZlcmVyIHBheWxvYWQgdHlwZVxuICAgICAgICAgICAgICAgIGNvbW1vbkNhcGFiaWxpdGllcy5jb2RlY3MucHVzaChyQ29kZWMpO1xuXG4gICAgICAgICAgICAgICAgLy8gZGV0ZXJtaW5lIGNvbW1vbiBmZWVkYmFjayBtZWNoYW5pc21zXG4gICAgICAgICAgICAgICAgckNvZGVjLnJ0Y3BGZWVkYmFjayA9IHJDb2RlYy5ydGNwRmVlZGJhY2suZmlsdGVyKGZ1bmN0aW9uKGZiKSB7XG4gICAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxDb2RlYy5ydGNwRmVlZGJhY2subGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGxDb2RlYy5ydGNwRmVlZGJhY2tbal0udHlwZSA9PT0gZmIudHlwZSAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgbENvZGVjLnJ0Y3BGZWVkYmFja1tqXS5wYXJhbWV0ZXIgPT09IGZiLnBhcmFtZXRlcikge1xuICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGFsc28gbmVlZCB0byBkZXRlcm1pbmUgLnBhcmFtZXRlcnNcbiAgICAgICAgICAgICAgICAvLyAgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVucGVlci9vcnRjL2lzc3Vlcy81NjlcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9uc1xuICAgICAgICAgICAgICAuZm9yRWFjaChmdW5jdGlvbihsSGVhZGVyRXh0ZW5zaW9uKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZW1vdGVDYXBhYmlsaXRpZXMuaGVhZGVyRXh0ZW5zaW9ucy5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICBpKyspIHtcbiAgICAgICAgICAgICAgICAgIHZhciBySGVhZGVyRXh0ZW5zaW9uID0gcmVtb3RlQ2FwYWJpbGl0aWVzLmhlYWRlckV4dGVuc2lvbnNbaV07XG4gICAgICAgICAgICAgICAgICBpZiAobEhlYWRlckV4dGVuc2lvbi51cmkgPT09IHJIZWFkZXJFeHRlbnNpb24udXJpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbW1vbkNhcGFiaWxpdGllcy5oZWFkZXJFeHRlbnNpb25zLnB1c2gockhlYWRlckV4dGVuc2lvbik7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAvLyBGSVhNRTogZmVjTWVjaGFuaXNtc1xuICAgICAgICAgIHJldHVybiBjb21tb25DYXBhYmlsaXRpZXM7XG4gICAgICAgIH07XG5cbiAgICAvLyBDcmVhdGUgSUNFIGdhdGhlcmVyLCBJQ0UgdHJhbnNwb3J0IGFuZCBEVExTIHRyYW5zcG9ydC5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyA9XG4gICAgICAgIGZ1bmN0aW9uKG1pZCwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICB2YXIgaWNlR2F0aGVyZXIgPSBuZXcgUlRDSWNlR2F0aGVyZXIoc2VsZi5pY2VPcHRpb25zKTtcbiAgICAgICAgICB2YXIgaWNlVHJhbnNwb3J0ID0gbmV3IFJUQ0ljZVRyYW5zcG9ydChpY2VHYXRoZXJlcik7XG4gICAgICAgICAgaWNlR2F0aGVyZXIub25sb2NhbGNhbmRpZGF0ZSA9IGZ1bmN0aW9uKGV2dCkge1xuICAgICAgICAgICAgdmFyIGV2ZW50ID0gbmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKTtcbiAgICAgICAgICAgIGV2ZW50LmNhbmRpZGF0ZSA9IHtzZHBNaWQ6IG1pZCwgc2RwTUxpbmVJbmRleDogc2RwTUxpbmVJbmRleH07XG5cbiAgICAgICAgICAgIHZhciBjYW5kID0gZXZ0LmNhbmRpZGF0ZTtcbiAgICAgICAgICAgIHZhciBlbmQgPSAhY2FuZCB8fCBPYmplY3Qua2V5cyhjYW5kKS5sZW5ndGggPT09IDA7XG4gICAgICAgICAgICAvLyBFZGdlIGVtaXRzIGFuIGVtcHR5IG9iamVjdCBmb3IgUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGXigKVcbiAgICAgICAgICAgIGlmIChlbmQpIHtcbiAgICAgICAgICAgICAgLy8gcG9seWZpbGwgc2luY2UgUlRDSWNlR2F0aGVyZXIuc3RhdGUgaXMgbm90IGltcGxlbWVudGVkIGluXG4gICAgICAgICAgICAgIC8vIEVkZ2UgMTA1NDcgeWV0LlxuICAgICAgICAgICAgICBpZiAoaWNlR2F0aGVyZXIuc3RhdGUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyLnN0YXRlID0gJ2NvbXBsZXRlZCc7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAvLyBFbWl0IGEgY2FuZGlkYXRlIHdpdGggdHlwZSBlbmRPZkNhbmRpZGF0ZXMgdG8gbWFrZSB0aGUgc2FtcGxlc1xuICAgICAgICAgICAgICAvLyB3b3JrLiBFZGdlIHJlcXVpcmVzIGFkZEljZUNhbmRpZGF0ZSB3aXRoIHRoaXMgZW1wdHkgY2FuZGlkYXRlXG4gICAgICAgICAgICAgIC8vIHRvIHN0YXJ0IGNoZWNraW5nLiBUaGUgcmVhbCBzb2x1dGlvbiBpcyB0byBzaWduYWxcbiAgICAgICAgICAgICAgLy8gZW5kLW9mLWNhbmRpZGF0ZXMgdG8gdGhlIG90aGVyIHNpZGUgd2hlbiBnZXR0aW5nIHRoZSBudWxsXG4gICAgICAgICAgICAgIC8vIGNhbmRpZGF0ZSBidXQgc29tZSBhcHBzIChsaWtlIHRoZSBzYW1wbGVzKSBkb24ndCBkbyB0aGF0LlxuICAgICAgICAgICAgICBldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlID1cbiAgICAgICAgICAgICAgICAgICdjYW5kaWRhdGU6MSAxIHVkcCAxIDAuMC4wLjAgOSB0eXAgZW5kT2ZDYW5kaWRhdGVzJztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIFJUQ0ljZUNhbmRpZGF0ZSBkb2Vzbid0IGhhdmUgYSBjb21wb25lbnQsIG5lZWRzIHRvIGJlIGFkZGVkXG4gICAgICAgICAgICAgIGNhbmQuY29tcG9uZW50ID0gaWNlVHJhbnNwb3J0LmNvbXBvbmVudCA9PT0gJ1JUQ1AnID8gMiA6IDE7XG4gICAgICAgICAgICAgIGV2ZW50LmNhbmRpZGF0ZS5jYW5kaWRhdGUgPSBTRFBVdGlscy53cml0ZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gdXBkYXRlIGxvY2FsIGRlc2NyaXB0aW9uLlxuICAgICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhzZWxmLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICAgIGlmIChldmVudC5jYW5kaWRhdGUuY2FuZGlkYXRlLmluZGV4T2YoJ3R5cCBlbmRPZkNhbmRpZGF0ZXMnKVxuICAgICAgICAgICAgICAgID09PSAtMSkge1xuICAgICAgICAgICAgICBzZWN0aW9uc1tldmVudC5jYW5kaWRhdGUuc2RwTUxpbmVJbmRleCArIDFdICs9XG4gICAgICAgICAgICAgICAgICAnYT0nICsgZXZlbnQuY2FuZGlkYXRlLmNhbmRpZGF0ZSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc2VjdGlvbnNbZXZlbnQuY2FuZGlkYXRlLnNkcE1MaW5lSW5kZXggKyAxXSArPVxuICAgICAgICAgICAgICAgICAgJ2E9ZW5kLW9mLWNhbmRpZGF0ZXNcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5sb2NhbERlc2NyaXB0aW9uLnNkcCA9IHNlY3Rpb25zLmpvaW4oJycpO1xuXG4gICAgICAgICAgICB2YXIgY29tcGxldGUgPSBzZWxmLnRyYW5zY2VpdmVycy5ldmVyeShmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICAgICAgICByZXR1cm4gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIgJiZcbiAgICAgICAgICAgICAgICAgIHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyLnN0YXRlID09PSAnY29tcGxldGVkJztcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBFbWl0IGNhbmRpZGF0ZSBpZiBsb2NhbERlc2NyaXB0aW9uIGlzIHNldC5cbiAgICAgICAgICAgIC8vIEFsc28gZW1pdHMgbnVsbCBjYW5kaWRhdGUgd2hlbiBhbGwgZ2F0aGVyZXJzIGFyZSBjb21wbGV0ZS5cbiAgICAgICAgICAgIHN3aXRjaCAoc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSkge1xuICAgICAgICAgICAgICBjYXNlICduZXcnOlxuICAgICAgICAgICAgICAgIHNlbGYuX2xvY2FsSWNlQ2FuZGlkYXRlc0J1ZmZlci5wdXNoKGV2ZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZW5kICYmIGNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgICBzZWxmLl9sb2NhbEljZUNhbmRpZGF0ZXNCdWZmZXIucHVzaChcbiAgICAgICAgICAgICAgICAgICAgICBuZXcgRXZlbnQoJ2ljZWNhbmRpZGF0ZScpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgJ2dhdGhlcmluZyc6XG4gICAgICAgICAgICAgICAgc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcygpO1xuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYub25pY2VjYW5kaWRhdGUgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUoZXZlbnQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoY29tcGxldGUpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2ljZWNhbmRpZGF0ZScpKTtcbiAgICAgICAgICAgICAgICAgIGlmIChzZWxmLm9uaWNlY2FuZGlkYXRlICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUobmV3IEV2ZW50KCdpY2VjYW5kaWRhdGUnKSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBzZWxmLmljZUdhdGhlcmluZ1N0YXRlID0gJ2NvbXBsZXRlJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgJ2NvbXBsZXRlJzpcbiAgICAgICAgICAgICAgICAvLyBzaG91bGQgbm90IGhhcHBlbi4uLiBjdXJyZW50bHkhXG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGRlZmF1bHQ6IC8vIG5vLW9wLlxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH07XG4gICAgICAgICAgaWNlVHJhbnNwb3J0Lm9uaWNlc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHNlbGYuX3VwZGF0ZUNvbm5lY3Rpb25TdGF0ZSgpO1xuICAgICAgICAgIH07XG5cbiAgICAgICAgICB2YXIgZHRsc1RyYW5zcG9ydCA9IG5ldyBSVENEdGxzVHJhbnNwb3J0KGljZVRyYW5zcG9ydCk7XG4gICAgICAgICAgZHRsc1RyYW5zcG9ydC5vbmR0bHNzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgc2VsZi5fdXBkYXRlQ29ubmVjdGlvblN0YXRlKCk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICBkdGxzVHJhbnNwb3J0Lm9uZXJyb3IgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIC8vIG9uZXJyb3IgZG9lcyBub3Qgc2V0IHN0YXRlIHRvIGZhaWxlZCBieSBpdHNlbGYuXG4gICAgICAgICAgICBkdGxzVHJhbnNwb3J0LnN0YXRlID0gJ2ZhaWxlZCc7XG4gICAgICAgICAgICBzZWxmLl91cGRhdGVDb25uZWN0aW9uU3RhdGUoKTtcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGljZUdhdGhlcmVyOiBpY2VHYXRoZXJlcixcbiAgICAgICAgICAgIGljZVRyYW5zcG9ydDogaWNlVHJhbnNwb3J0LFxuICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogZHRsc1RyYW5zcG9ydFxuICAgICAgICAgIH07XG4gICAgICAgIH07XG5cbiAgICAvLyBTdGFydCB0aGUgUlRQIFNlbmRlciBhbmQgUmVjZWl2ZXIgZm9yIGEgdHJhbnNjZWl2ZXIuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdHJhbnNjZWl2ZSA9IGZ1bmN0aW9uKHRyYW5zY2VpdmVyLFxuICAgICAgICBzZW5kLCByZWN2KSB7XG4gICAgICB2YXIgcGFyYW1zID0gdGhpcy5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKHRyYW5zY2VpdmVyLmxvY2FsQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgIHRyYW5zY2VpdmVyLnJlbW90ZUNhcGFiaWxpdGllcyk7XG4gICAgICBpZiAoc2VuZCAmJiB0cmFuc2NlaXZlci5ydHBTZW5kZXIpIHtcbiAgICAgICAgcGFyYW1zLmVuY29kaW5ncyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgIHBhcmFtcy5ydGNwID0ge1xuICAgICAgICAgIGNuYW1lOiBTRFBVdGlscy5sb2NhbENOYW1lXG4gICAgICAgIH07XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzLmxlbmd0aCkge1xuICAgICAgICAgIHBhcmFtcy5ydGNwLnNzcmMgPSB0cmFuc2NlaXZlci5yZWN2RW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmM7XG4gICAgICAgIH1cbiAgICAgICAgdHJhbnNjZWl2ZXIucnRwU2VuZGVyLnNlbmQocGFyYW1zKTtcbiAgICAgIH1cbiAgICAgIGlmIChyZWN2ICYmIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyKSB7XG4gICAgICAgIC8vIHJlbW92ZSBSVFggZmllbGQgaW4gRWRnZSAxNDk0MlxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIua2luZCA9PT0gJ3ZpZGVvJ1xuICAgICAgICAgICAgJiYgdHJhbnNjZWl2ZXIucmVjdkVuY29kaW5nUGFyYW1ldGVycykge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMuZm9yRWFjaChmdW5jdGlvbihwKSB7XG4gICAgICAgICAgICBkZWxldGUgcC5ydHg7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcGFyYW1zLmVuY29kaW5ncyA9IHRyYW5zY2VpdmVyLnJlY3ZFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgIHBhcmFtcy5ydGNwID0ge1xuICAgICAgICAgIGNuYW1lOiB0cmFuc2NlaXZlci5jbmFtZVxuICAgICAgICB9O1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuc2VuZEVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGgpIHtcbiAgICAgICAgICBwYXJhbXMucnRjcC5zc3JjID0gdHJhbnNjZWl2ZXIuc2VuZEVuY29kaW5nUGFyYW1ldGVyc1swXS5zc3JjO1xuICAgICAgICB9XG4gICAgICAgIHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyLnJlY2VpdmUocGFyYW1zKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRMb2NhbERlc2NyaXB0aW9uID1cbiAgICAgICAgZnVuY3Rpb24oZGVzY3JpcHRpb24pIHtcbiAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgdmFyIHNlY3Rpb25zO1xuICAgICAgICAgIHZhciBzZXNzaW9ucGFydDtcbiAgICAgICAgICBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ29mZmVyJykge1xuICAgICAgICAgICAgLy8gRklYTUU6IFdoYXQgd2FzIHRoZSBwdXJwb3NlIG9mIHRoaXMgZW1wdHkgaWYgc3RhdGVtZW50P1xuICAgICAgICAgICAgLy8gaWYgKCF0aGlzLl9wZW5kaW5nT2ZmZXIpIHtcbiAgICAgICAgICAgIC8vIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAodGhpcy5fcGVuZGluZ09mZmVyKSB7XG4gICAgICAgICAgICAgIC8vIFZFUlkgbGltaXRlZCBzdXBwb3J0IGZvciBTRFAgbXVuZ2luZy4gTGltaXRlZCB0bzpcbiAgICAgICAgICAgICAgLy8gKiBjaGFuZ2luZyB0aGUgb3JkZXIgb2YgY29kZWNzXG4gICAgICAgICAgICAgIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhkZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgICBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgICAgIHNlY3Rpb25zLmZvckVhY2goZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZHBNTGluZUluZGV4KSB7XG4gICAgICAgICAgICAgICAgdmFyIGNhcHMgPSBTRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMobWVkaWFTZWN0aW9uKTtcbiAgICAgICAgICAgICAgICBzZWxmLl9wZW5kaW5nT2ZmZXJbc2RwTUxpbmVJbmRleF0ubG9jYWxDYXBhYmlsaXRpZXMgPSBjYXBzO1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgdGhpcy50cmFuc2NlaXZlcnMgPSB0aGlzLl9wZW5kaW5nT2ZmZXI7XG4gICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9wZW5kaW5nT2ZmZXI7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmIChkZXNjcmlwdGlvbi50eXBlID09PSAnYW5zd2VyJykge1xuICAgICAgICAgICAgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKHNlbGYucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICAgIHNlc3Npb25wYXJ0ID0gc2VjdGlvbnMuc2hpZnQoKTtcbiAgICAgICAgICAgIHZhciBpc0ljZUxpdGUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChzZXNzaW9ucGFydCxcbiAgICAgICAgICAgICAgICAnYT1pY2UtbGl0ZScpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XTtcbiAgICAgICAgICAgICAgdmFyIGljZUdhdGhlcmVyID0gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXI7XG4gICAgICAgICAgICAgIHZhciBpY2VUcmFuc3BvcnQgPSB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIHZhciBkdGxzVHJhbnNwb3J0ID0gdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydDtcbiAgICAgICAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzID0gdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXM7XG4gICAgICAgICAgICAgIHZhciByZW1vdGVDYXBhYmlsaXRpZXMgPSB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWVkaWFTZWN0aW9uLnNwbGl0KCdcXG4nLCAxKVswXVxuICAgICAgICAgICAgICAgICAgLnNwbGl0KCcgJywgMilbMV0gPT09ICcwJztcblxuICAgICAgICAgICAgICBpZiAoIXJlamVjdGVkICYmICF0cmFuc2NlaXZlci5pc0RhdGFjaGFubmVsKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKFxuICAgICAgICAgICAgICAgICAgICBtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgICBpZiAoaXNJY2VMaXRlKSB7XG4gICAgICAgICAgICAgICAgICB2YXIgY2FuZHMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWNhbmRpZGF0ZTonKVxuICAgICAgICAgICAgICAgICAgLm1hcChmdW5jdGlvbihjYW5kKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAuZmlsdGVyKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhbmQuY29tcG9uZW50ID09PSAnMSc7XG4gICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgIC8vIGljZS1saXRlIG9ubHkgaW5jbHVkZXMgaG9zdCBjYW5kaWRhdGVzIGluIHRoZSBTRFAgc28gd2UgY2FuXG4gICAgICAgICAgICAgICAgICAvLyB1c2Ugc2V0UmVtb3RlQ2FuZGlkYXRlcyAod2hpY2ggaW1wbGllcyBhblxuICAgICAgICAgICAgICAgICAgLy8gUlRDSWNlQ2FuZGlkYXRlQ29tcGxldGUpXG4gICAgICAgICAgICAgICAgICBpZiAoY2FuZHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdmFyIHJlbW90ZUR0bHNQYXJhbWV0ZXJzID0gU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMoXG4gICAgICAgICAgICAgICAgICAgIG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpO1xuICAgICAgICAgICAgICAgIGlmIChpc0ljZUxpdGUpIHtcbiAgICAgICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnc2VydmVyJztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgaWNlVHJhbnNwb3J0LnN0YXJ0KGljZUdhdGhlcmVyLCByZW1vdGVJY2VQYXJhbWV0ZXJzLFxuICAgICAgICAgICAgICAgICAgICAgIGlzSWNlTGl0ZSA/ICdjb250cm9sbGluZycgOiAnY29udHJvbGxlZCcpO1xuICAgICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIGludGVyc2VjdGlvbiBvZiBjYXBhYmlsaXRpZXMuXG4gICAgICAgICAgICAgICAgdmFyIHBhcmFtcyA9IHNlbGYuX2dldENvbW1vbkNhcGFiaWxpdGllcyhsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzKTtcblxuICAgICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBTZW5kZXIuIFRoZSBSVENSdHBSZWNlaXZlciBmb3IgdGhpc1xuICAgICAgICAgICAgICAgIC8vIHRyYW5zY2VpdmVyIGhhcyBhbHJlYWR5IGJlZW4gc3RhcnRlZCBpbiBzZXRSZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgICBwYXJhbXMuY29kZWNzLmxlbmd0aCA+IDAsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy5sb2NhbERlc2NyaXB0aW9uID0ge1xuICAgICAgICAgICAgdHlwZTogZGVzY3JpcHRpb24udHlwZSxcbiAgICAgICAgICAgIHNkcDogZGVzY3JpcHRpb24uc2RwXG4gICAgICAgICAgfTtcbiAgICAgICAgICBzd2l0Y2ggKGRlc2NyaXB0aW9uLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ29mZmVyJzpcbiAgICAgICAgICAgICAgdGhpcy5fdXBkYXRlU2lnbmFsaW5nU3RhdGUoJ2hhdmUtbG9jYWwtb2ZmZXInKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdhbnN3ZXInOlxuICAgICAgICAgICAgICB0aGlzLl91cGRhdGVTaWduYWxpbmdTdGF0ZSgnc3RhYmxlJyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigndW5zdXBwb3J0ZWQgdHlwZSBcIicgKyBkZXNjcmlwdGlvbi50eXBlICtcbiAgICAgICAgICAgICAgICAgICdcIicpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIElmIGEgc3VjY2VzcyBjYWxsYmFjayB3YXMgcHJvdmlkZWQsIGVtaXQgSUNFIGNhbmRpZGF0ZXMgYWZ0ZXIgaXRcbiAgICAgICAgICAvLyBoYXMgYmVlbiBleGVjdXRlZC4gT3RoZXJ3aXNlLCBlbWl0IGNhbGxiYWNrIGFmdGVyIHRoZSBQcm9taXNlIGlzXG4gICAgICAgICAgLy8gcmVzb2x2ZWQuXG4gICAgICAgICAgdmFyIGhhc0NhbGxiYWNrID0gYXJndW1lbnRzLmxlbmd0aCA+IDEgJiZcbiAgICAgICAgICAgIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbic7XG4gICAgICAgICAgaWYgKGhhc0NhbGxiYWNrKSB7XG4gICAgICAgICAgICB2YXIgY2IgPSBhcmd1bWVudHNbMV07XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgY2IoKTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHNlbGYuX2VtaXRCdWZmZXJlZENhbmRpZGF0ZXMoKTtcbiAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB2YXIgcCA9IFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgICAgIHAudGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlmICghaGFzQ2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgaWYgKHNlbGYuaWNlR2F0aGVyaW5nU3RhdGUgPT09ICduZXcnKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5pY2VHYXRoZXJpbmdTdGF0ZSA9ICdnYXRoZXJpbmcnO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFVzdWFsbHkgY2FuZGlkYXRlcyB3aWxsIGJlIGVtaXR0ZWQgZWFybGllci5cbiAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoc2VsZi5fZW1pdEJ1ZmZlcmVkQ2FuZGlkYXRlcy5iaW5kKHNlbGYpLCA1MDApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybiBwO1xuICAgICAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9XG4gICAgICAgIGZ1bmN0aW9uKGRlc2NyaXB0aW9uKSB7XG4gICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgIHZhciBzdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICAgICAgICB2YXIgcmVjZWl2ZXJMaXN0ID0gW107XG4gICAgICAgICAgdmFyIHNlY3Rpb25zID0gU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyhkZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgIHZhciBzZXNzaW9ucGFydCA9IHNlY3Rpb25zLnNoaWZ0KCk7XG4gICAgICAgICAgdmFyIGlzSWNlTGl0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1pY2UtbGl0ZScpLmxlbmd0aCA+IDA7XG4gICAgICAgICAgdGhpcy51c2luZ0J1bmRsZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KHNlc3Npb25wYXJ0LFxuICAgICAgICAgICAgICAnYT1ncm91cDpCVU5ETEUgJykubGVuZ3RoID4gMDtcbiAgICAgICAgICBzZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIG1saW5lID0gbGluZXNbMF0uc3Vic3RyKDIpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICB2YXIga2luZCA9IG1saW5lWzBdO1xuICAgICAgICAgICAgdmFyIHJlamVjdGVkID0gbWxpbmVbMV0gPT09ICcwJztcbiAgICAgICAgICAgIHZhciBkaXJlY3Rpb24gPSBTRFBVdGlscy5nZXREaXJlY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCk7XG5cbiAgICAgICAgICAgIHZhciBtaWQgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPW1pZDonKTtcbiAgICAgICAgICAgIGlmIChtaWQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgIG1pZCA9IG1pZFswXS5zdWJzdHIoNik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBtaWQgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUmVqZWN0IGRhdGFjaGFubmVscyB3aGljaCBhcmUgbm90IGltcGxlbWVudGVkIHlldC5cbiAgICAgICAgICAgIGlmIChraW5kID09PSAnYXBwbGljYXRpb24nICYmIG1saW5lWzJdID09PSAnRFRMUy9TQ1RQJykge1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgICAgICBpc0RhdGFjaGFubmVsOiB0cnVlXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHRyYW5zY2VpdmVyO1xuICAgICAgICAgICAgdmFyIGljZUdhdGhlcmVyO1xuICAgICAgICAgICAgdmFyIGljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgIHZhciBkdGxzVHJhbnNwb3J0O1xuICAgICAgICAgICAgdmFyIHJ0cFNlbmRlcjtcbiAgICAgICAgICAgIHZhciBydHBSZWNlaXZlcjtcbiAgICAgICAgICAgIHZhciBzZW5kRW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgdmFyIHJlY3ZFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgICAgICB2YXIgbG9jYWxDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgIHZhciB0cmFjaztcbiAgICAgICAgICAgIC8vIEZJWE1FOiBlbnN1cmUgdGhlIG1lZGlhU2VjdGlvbiBoYXMgcnRjcC1tdXggc2V0LlxuICAgICAgICAgICAgdmFyIHJlbW90ZUNhcGFiaWxpdGllcyA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICAgICAgICAgICAgdmFyIHJlbW90ZUljZVBhcmFtZXRlcnM7XG4gICAgICAgICAgICB2YXIgcmVtb3RlRHRsc1BhcmFtZXRlcnM7XG4gICAgICAgICAgICBpZiAoIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHJlbW90ZUljZVBhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAgIHNlc3Npb25wYXJ0KTtcbiAgICAgICAgICAgICAgcmVtb3RlRHRsc1BhcmFtZXRlcnMgPSBTRFBVdGlscy5nZXREdGxzUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24sXG4gICAgICAgICAgICAgICAgICBzZXNzaW9ucGFydCk7XG4gICAgICAgICAgICAgIHJlbW90ZUR0bHNQYXJhbWV0ZXJzLnJvbGUgPSAnY2xpZW50JztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJlY3ZFbmNvZGluZ1BhcmFtZXRlcnMgPVxuICAgICAgICAgICAgICAgIFNEUFV0aWxzLnBhcnNlUnRwRW5jb2RpbmdQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbik7XG5cbiAgICAgICAgICAgIHZhciBjbmFtZTtcbiAgICAgICAgICAgIC8vIEdldHMgdGhlIGZpcnN0IFNTUkMuIE5vdGUgdGhhdCB3aXRoIFJUWCB0aGVyZSBtaWdodCBiZSBtdWx0aXBsZVxuICAgICAgICAgICAgLy8gU1NSQ3MuXG4gICAgICAgICAgICB2YXIgcmVtb3RlU3NyYyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgICAgICAgICAgICAgIC5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbHRlcihmdW5jdGlvbihvYmopIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBvYmouYXR0cmlidXRlID09PSAnY25hbWUnO1xuICAgICAgICAgICAgICAgIH0pWzBdO1xuICAgICAgICAgICAgaWYgKHJlbW90ZVNzcmMpIHtcbiAgICAgICAgICAgICAgY25hbWUgPSByZW1vdGVTc3JjLnZhbHVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgaXNDb21wbGV0ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbixcbiAgICAgICAgICAgICAgICAnYT1lbmQtb2YtY2FuZGlkYXRlcycsIHNlc3Npb25wYXJ0KS5sZW5ndGggPiAwO1xuICAgICAgICAgICAgdmFyIGNhbmRzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1jYW5kaWRhdGU6JylcbiAgICAgICAgICAgICAgICAubWFwKGZ1bmN0aW9uKGNhbmQpIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShjYW5kKTtcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC5maWx0ZXIoZnVuY3Rpb24oY2FuZCkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhbmQuY29tcG9uZW50ID09PSAnMSc7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ29mZmVyJyAmJiAhcmVqZWN0ZWQpIHtcbiAgICAgICAgICAgICAgdmFyIHRyYW5zcG9ydHMgPSBzZWxmLnVzaW5nQnVuZGxlICYmIHNkcE1MaW5lSW5kZXggPiAwID8ge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyOiBzZWxmLnRyYW5zY2VpdmVyc1swXS5pY2VHYXRoZXJlcixcbiAgICAgICAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHNlbGYudHJhbnNjZWl2ZXJzWzBdLmljZVRyYW5zcG9ydCxcbiAgICAgICAgICAgICAgICBkdGxzVHJhbnNwb3J0OiBzZWxmLnRyYW5zY2VpdmVyc1swXS5kdGxzVHJhbnNwb3J0XG4gICAgICAgICAgICAgIH0gOiBzZWxmLl9jcmVhdGVJY2VBbmREdGxzVHJhbnNwb3J0cyhtaWQsIHNkcE1MaW5lSW5kZXgpO1xuXG4gICAgICAgICAgICAgIGlmIChpc0NvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgdHJhbnNwb3J0cy5pY2VUcmFuc3BvcnQuc2V0UmVtb3RlQ2FuZGlkYXRlcyhjYW5kcyk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllcyA9IFJUQ1J0cFJlY2VpdmVyLmdldENhcGFiaWxpdGllcyhraW5kKTtcblxuICAgICAgICAgICAgICAvLyBmaWx0ZXIgUlRYIHVudGlsIGFkZGl0aW9uYWwgc3R1ZmYgbmVlZGVkIGZvciBSVFggaXMgaW1wbGVtZW50ZWRcbiAgICAgICAgICAgICAgLy8gaW4gYWRhcHRlci5qc1xuICAgICAgICAgICAgICBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MgPSBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MuZmlsdGVyKFxuICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oY29kZWMpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNvZGVjLm5hbWUgIT09ICdydHgnO1xuICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IFt7XG4gICAgICAgICAgICAgICAgc3NyYzogKDIgKiBzZHBNTGluZUluZGV4ICsgMikgKiAxMDAxXG4gICAgICAgICAgICAgIH1dO1xuXG4gICAgICAgICAgICAgIHJ0cFJlY2VpdmVyID0gbmV3IFJUQ1J0cFJlY2VpdmVyKHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCwga2luZCk7XG5cbiAgICAgICAgICAgICAgdHJhY2sgPSBydHBSZWNlaXZlci50cmFjaztcbiAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LnB1c2goW3RyYWNrLCBydHBSZWNlaXZlcl0pO1xuICAgICAgICAgICAgICAvLyBGSVhNRTogbm90IGNvcnJlY3Qgd2hlbiB0aGVyZSBhcmUgbXVsdGlwbGUgc3RyZWFtcyBidXQgdGhhdCBpc1xuICAgICAgICAgICAgICAvLyBub3QgY3VycmVudGx5IHN1cHBvcnRlZCBpbiB0aGlzIHNoaW0uXG4gICAgICAgICAgICAgIHN0cmVhbS5hZGRUcmFjayh0cmFjayk7XG5cbiAgICAgICAgICAgICAgLy8gRklYTUU6IGxvb2sgYXQgZGlyZWN0aW9uLlxuICAgICAgICAgICAgICBpZiAoc2VsZi5sb2NhbFN0cmVhbXMubGVuZ3RoID4gMCAmJlxuICAgICAgICAgICAgICAgICAgc2VsZi5sb2NhbFN0cmVhbXNbMF0uZ2V0VHJhY2tzKCkubGVuZ3RoID49IHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgICAgICAgICB2YXIgbG9jYWxUcmFjaztcbiAgICAgICAgICAgICAgICBpZiAoa2luZCA9PT0gJ2F1ZGlvJykge1xuICAgICAgICAgICAgICAgICAgbG9jYWxUcmFjayA9IHNlbGYubG9jYWxTdHJlYW1zWzBdLmdldEF1ZGlvVHJhY2tzKClbMF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChraW5kID09PSAndmlkZW8nKSB7XG4gICAgICAgICAgICAgICAgICBsb2NhbFRyYWNrID0gc2VsZi5sb2NhbFN0cmVhbXNbMF0uZ2V0VmlkZW9UcmFja3MoKVswXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGxvY2FsVHJhY2spIHtcbiAgICAgICAgICAgICAgICAgIHJ0cFNlbmRlciA9IG5ldyBSVENSdHBTZW5kZXIobG9jYWxUcmFjayxcbiAgICAgICAgICAgICAgICAgICAgICB0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdID0ge1xuICAgICAgICAgICAgICAgIGljZUdhdGhlcmVyOiB0cmFuc3BvcnRzLmljZUdhdGhlcmVyLFxuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydDogdHJhbnNwb3J0cy5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LFxuICAgICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzOiBsb2NhbENhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICByZW1vdGVDYXBhYmlsaXRpZXM6IHJlbW90ZUNhcGFiaWxpdGllcyxcbiAgICAgICAgICAgICAgICBydHBTZW5kZXI6IHJ0cFNlbmRlcixcbiAgICAgICAgICAgICAgICBydHBSZWNlaXZlcjogcnRwUmVjZWl2ZXIsXG4gICAgICAgICAgICAgICAga2luZDoga2luZCxcbiAgICAgICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgICAgICBjbmFtZTogY25hbWUsXG4gICAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVyczogc2VuZEVuY29kaW5nUGFyYW1ldGVycyxcbiAgICAgICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzOiByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIC8vIFN0YXJ0IHRoZSBSVENSdHBSZWNlaXZlciBub3cuIFRoZSBSVFBTZW5kZXIgaXMgc3RhcnRlZCBpblxuICAgICAgICAgICAgICAvLyBzZXRMb2NhbERlc2NyaXB0aW9uLlxuICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHNlbGYudHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdLFxuICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPT09ICdzZW5kcmVjdicgfHwgZGlyZWN0aW9uID09PSAnc2VuZG9ubHknKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoZGVzY3JpcHRpb24udHlwZSA9PT0gJ2Fuc3dlcicgJiYgIXJlamVjdGVkKSB7XG4gICAgICAgICAgICAgIHRyYW5zY2VpdmVyID0gc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF07XG4gICAgICAgICAgICAgIGljZUdhdGhlcmVyID0gdHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXI7XG4gICAgICAgICAgICAgIGljZVRyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmljZVRyYW5zcG9ydDtcbiAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydCA9IHRyYW5zY2VpdmVyLmR0bHNUcmFuc3BvcnQ7XG4gICAgICAgICAgICAgIHJ0cFNlbmRlciA9IHRyYW5zY2VpdmVyLnJ0cFNlbmRlcjtcbiAgICAgICAgICAgICAgcnRwUmVjZWl2ZXIgPSB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgICAgICAgICAgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnM7XG4gICAgICAgICAgICAgIGxvY2FsQ2FwYWJpbGl0aWVzID0gdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXM7XG5cbiAgICAgICAgICAgICAgc2VsZi50cmFuc2NlaXZlcnNbc2RwTUxpbmVJbmRleF0ucmVjdkVuY29kaW5nUGFyYW1ldGVycyA9XG4gICAgICAgICAgICAgICAgICByZWN2RW5jb2RpbmdQYXJhbWV0ZXJzO1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XS5yZW1vdGVDYXBhYmlsaXRpZXMgPVxuICAgICAgICAgICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzO1xuICAgICAgICAgICAgICBzZWxmLnRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XS5jbmFtZSA9IGNuYW1lO1xuXG4gICAgICAgICAgICAgIGlmICgoaXNJY2VMaXRlIHx8IGlzQ29tcGxldGUpICYmIGNhbmRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zZXRSZW1vdGVDYW5kaWRhdGVzKGNhbmRzKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoIXNlbGYudXNpbmdCdW5kbGUgfHwgc2RwTUxpbmVJbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgIGljZVRyYW5zcG9ydC5zdGFydChpY2VHYXRoZXJlciwgcmVtb3RlSWNlUGFyYW1ldGVycyxcbiAgICAgICAgICAgICAgICAgICAgJ2NvbnRyb2xsaW5nJyk7XG4gICAgICAgICAgICAgICAgZHRsc1RyYW5zcG9ydC5zdGFydChyZW1vdGVEdGxzUGFyYW1ldGVycyk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBzZWxmLl90cmFuc2NlaXZlKHRyYW5zY2VpdmVyLFxuICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID09PSAnc2VuZHJlY3YnIHx8IGRpcmVjdGlvbiA9PT0gJ3JlY3Zvbmx5JyxcbiAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdzZW5kb25seScpO1xuXG4gICAgICAgICAgICAgIGlmIChydHBSZWNlaXZlciAmJlxuICAgICAgICAgICAgICAgICAgKGRpcmVjdGlvbiA9PT0gJ3NlbmRyZWN2JyB8fCBkaXJlY3Rpb24gPT09ICdzZW5kb25seScpKSB7XG4gICAgICAgICAgICAgICAgdHJhY2sgPSBydHBSZWNlaXZlci50cmFjaztcbiAgICAgICAgICAgICAgICByZWNlaXZlckxpc3QucHVzaChbdHJhY2ssIHJ0cFJlY2VpdmVyXSk7XG4gICAgICAgICAgICAgICAgc3RyZWFtLmFkZFRyYWNrKHRyYWNrKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBGSVhNRTogYWN0dWFsbHkgdGhlIHJlY2VpdmVyIHNob3VsZCBiZSBjcmVhdGVkIGxhdGVyLlxuICAgICAgICAgICAgICAgIGRlbGV0ZSB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgdGhpcy5yZW1vdGVEZXNjcmlwdGlvbiA9IHtcbiAgICAgICAgICAgIHR5cGU6IGRlc2NyaXB0aW9uLnR5cGUsXG4gICAgICAgICAgICBzZHA6IGRlc2NyaXB0aW9uLnNkcFxuICAgICAgICAgIH07XG4gICAgICAgICAgc3dpdGNoIChkZXNjcmlwdGlvbi50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdvZmZlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdoYXZlLXJlbW90ZS1vZmZlcicpO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2Fuc3dlcic6XG4gICAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdzdGFibGUnKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCd1bnN1cHBvcnRlZCB0eXBlIFwiJyArIGRlc2NyaXB0aW9uLnR5cGUgK1xuICAgICAgICAgICAgICAgICAgJ1wiJyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChzdHJlYW0uZ2V0VHJhY2tzKCkubGVuZ3RoKSB7XG4gICAgICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbXMucHVzaChzdHJlYW0pO1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnYWRkc3RyZWFtJyk7XG4gICAgICAgICAgICAgIGV2ZW50LnN0cmVhbSA9IHN0cmVhbTtcbiAgICAgICAgICAgICAgc2VsZi5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICAgICAgaWYgKHNlbGYub25hZGRzdHJlYW0gIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgIHNlbGYub25hZGRzdHJlYW0oZXZlbnQpO1xuICAgICAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgcmVjZWl2ZXJMaXN0LmZvckVhY2goZnVuY3Rpb24oaXRlbSkge1xuICAgICAgICAgICAgICAgIHZhciB0cmFjayA9IGl0ZW1bMF07XG4gICAgICAgICAgICAgICAgdmFyIHJlY2VpdmVyID0gaXRlbVsxXTtcbiAgICAgICAgICAgICAgICB2YXIgdHJhY2tFdmVudCA9IG5ldyBFdmVudCgndHJhY2snKTtcbiAgICAgICAgICAgICAgICB0cmFja0V2ZW50LnRyYWNrID0gdHJhY2s7XG4gICAgICAgICAgICAgICAgdHJhY2tFdmVudC5yZWNlaXZlciA9IHJlY2VpdmVyO1xuICAgICAgICAgICAgICAgIHRyYWNrRXZlbnQuc3RyZWFtcyA9IFtzdHJlYW1dO1xuICAgICAgICAgICAgICAgIHNlbGYuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgaWYgKHNlbGYub250cmFjayAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYub250cmFjayh0cmFja0V2ZW50KTtcbiAgICAgICAgICAgICAgICAgIH0sIDApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICAgICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY2xvc2UgPSBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgLyogbm90IHlldFxuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaWNlR2F0aGVyZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VHYXRoZXJlci5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgICovXG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2NlaXZlci5kdGxzVHJhbnNwb3J0KSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnJ0cFNlbmRlcikge1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnJ0cFNlbmRlci5zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyLnJ0cFJlY2VpdmVyKSB7XG4gICAgICAgICAgdHJhbnNjZWl2ZXIucnRwUmVjZWl2ZXIuc3RvcCgpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIC8vIEZJWE1FOiBjbGVhbiB1cCB0cmFja3MsIGxvY2FsIHN0cmVhbXMsIHJlbW90ZSBzdHJlYW1zLCBldGNcbiAgICAgIHRoaXMuX3VwZGF0ZVNpZ25hbGluZ1N0YXRlKCdjbG9zZWQnKTtcbiAgICB9O1xuXG4gICAgLy8gVXBkYXRlIHRoZSBzaWduYWxpbmcgc3RhdGUuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fdXBkYXRlU2lnbmFsaW5nU3RhdGUgPVxuICAgICAgICBmdW5jdGlvbihuZXdTdGF0ZSkge1xuICAgICAgICAgIHRoaXMuc2lnbmFsaW5nU3RhdGUgPSBuZXdTdGF0ZTtcbiAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ3NpZ25hbGluZ3N0YXRlY2hhbmdlJyk7XG4gICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICBpZiAodGhpcy5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UoZXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIC8vIERldGVybWluZSB3aGV0aGVyIHRvIGZpcmUgdGhlIG5lZ290aWF0aW9ubmVlZGVkIGV2ZW50LlxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX21heWJlRmlyZU5lZ290aWF0aW9uTmVlZGVkID1cbiAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgLy8gRmlyZSBhd2F5IChmb3Igbm93KS5cbiAgICAgICAgICB2YXIgZXZlbnQgPSBuZXcgRXZlbnQoJ25lZ290aWF0aW9ubmVlZGVkJyk7XG4gICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICBpZiAodGhpcy5vbm5lZ290aWF0aW9ubmVlZGVkICE9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9ubmVnb3RpYXRpb25uZWVkZWQoZXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIC8vIFVwZGF0ZSB0aGUgY29ubmVjdGlvbiBzdGF0ZS5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLl91cGRhdGVDb25uZWN0aW9uU3RhdGUgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgIHZhciBuZXdTdGF0ZTtcbiAgICAgIHZhciBzdGF0ZXMgPSB7XG4gICAgICAgICduZXcnOiAwLFxuICAgICAgICBjbG9zZWQ6IDAsXG4gICAgICAgIGNvbm5lY3Rpbmc6IDAsXG4gICAgICAgIGNoZWNraW5nOiAwLFxuICAgICAgICBjb25uZWN0ZWQ6IDAsXG4gICAgICAgIGNvbXBsZXRlZDogMCxcbiAgICAgICAgZmFpbGVkOiAwXG4gICAgICB9O1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBzdGF0ZXNbdHJhbnNjZWl2ZXIuaWNlVHJhbnNwb3J0LnN0YXRlXSsrO1xuICAgICAgICBzdGF0ZXNbdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5zdGF0ZV0rKztcbiAgICAgIH0pO1xuICAgICAgLy8gSUNFVHJhbnNwb3J0LmNvbXBsZXRlZCBhbmQgY29ubmVjdGVkIGFyZSB0aGUgc2FtZSBmb3IgdGhpcyBwdXJwb3NlLlxuICAgICAgc3RhdGVzLmNvbm5lY3RlZCArPSBzdGF0ZXMuY29tcGxldGVkO1xuXG4gICAgICBuZXdTdGF0ZSA9ICduZXcnO1xuICAgICAgaWYgKHN0YXRlcy5mYWlsZWQgPiAwKSB7XG4gICAgICAgIG5ld1N0YXRlID0gJ2ZhaWxlZCc7XG4gICAgICB9IGVsc2UgaWYgKHN0YXRlcy5jb25uZWN0aW5nID4gMCB8fCBzdGF0ZXMuY2hlY2tpbmcgPiAwKSB7XG4gICAgICAgIG5ld1N0YXRlID0gJ2Nvbm5lY3RpbmcnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuZGlzY29ubmVjdGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdkaXNjb25uZWN0ZWQnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMubmV3ID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICduZXcnO1xuICAgICAgfSBlbHNlIGlmIChzdGF0ZXMuY29ubmVjdGVkID4gMCB8fCBzdGF0ZXMuY29tcGxldGVkID4gMCkge1xuICAgICAgICBuZXdTdGF0ZSA9ICdjb25uZWN0ZWQnO1xuICAgICAgfVxuXG4gICAgICBpZiAobmV3U3RhdGUgIT09IHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgIHNlbGYuaWNlQ29ubmVjdGlvblN0YXRlID0gbmV3U3RhdGU7XG4gICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgnaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlJyk7XG4gICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIGlmICh0aGlzLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgdGhpcy5vbmljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZShldmVudCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVPZmZlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgaWYgKHRoaXMuX3BlbmRpbmdPZmZlcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2NyZWF0ZU9mZmVyIGNhbGxlZCB3aGlsZSB0aGVyZSBpcyBhIHBlbmRpbmcgb2ZmZXIuJyk7XG4gICAgICB9XG4gICAgICB2YXIgb2ZmZXJPcHRpb25zO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDEgJiYgdHlwZW9mIGFyZ3VtZW50c1swXSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBvZmZlck9wdGlvbnMgPSBhcmd1bWVudHNbMF07XG4gICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDMpIHtcbiAgICAgICAgb2ZmZXJPcHRpb25zID0gYXJndW1lbnRzWzJdO1xuICAgICAgfVxuXG4gICAgICB2YXIgdHJhY2tzID0gW107XG4gICAgICB2YXIgbnVtQXVkaW9UcmFja3MgPSAwO1xuICAgICAgdmFyIG51bVZpZGVvVHJhY2tzID0gMDtcbiAgICAgIC8vIERlZmF1bHQgdG8gc2VuZHJlY3YuXG4gICAgICBpZiAodGhpcy5sb2NhbFN0cmVhbXMubGVuZ3RoKSB7XG4gICAgICAgIG51bUF1ZGlvVHJhY2tzID0gdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0QXVkaW9UcmFja3MoKS5sZW5ndGg7XG4gICAgICAgIG51bVZpZGVvVHJhY2tzID0gdGhpcy5sb2NhbFN0cmVhbXNbMF0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGg7XG4gICAgICB9XG4gICAgICAvLyBEZXRlcm1pbmUgbnVtYmVyIG9mIGF1ZGlvIGFuZCB2aWRlbyB0cmFja3Mgd2UgbmVlZCB0byBzZW5kL3JlY3YuXG4gICAgICBpZiAob2ZmZXJPcHRpb25zKSB7XG4gICAgICAgIC8vIFJlamVjdCBDaHJvbWUgbGVnYWN5IGNvbnN0cmFpbnRzLlxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm1hbmRhdG9yeSB8fCBvZmZlck9wdGlvbnMub3B0aW9uYWwpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFxuICAgICAgICAgICAgICAnTGVnYWN5IG1hbmRhdG9yeS9vcHRpb25hbCBjb25zdHJhaW50cyBub3Qgc3VwcG9ydGVkLicpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVBdWRpbyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgbnVtQXVkaW9UcmFja3MgPSBvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVBdWRpbztcbiAgICAgICAgfVxuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlVmlkZW8gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIG51bVZpZGVvVHJhY2tzID0gb2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlVmlkZW87XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLmxvY2FsU3RyZWFtcy5sZW5ndGgpIHtcbiAgICAgICAgLy8gUHVzaCBsb2NhbCBzdHJlYW1zLlxuICAgICAgICB0aGlzLmxvY2FsU3RyZWFtc1swXS5nZXRUcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uKHRyYWNrKSB7XG4gICAgICAgICAgdHJhY2tzLnB1c2goe1xuICAgICAgICAgICAga2luZDogdHJhY2sua2luZCxcbiAgICAgICAgICAgIHRyYWNrOiB0cmFjayxcbiAgICAgICAgICAgIHdhbnRSZWNlaXZlOiB0cmFjay5raW5kID09PSAnYXVkaW8nID9cbiAgICAgICAgICAgICAgICBudW1BdWRpb1RyYWNrcyA+IDAgOiBudW1WaWRlb1RyYWNrcyA+IDBcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpZiAodHJhY2sua2luZCA9PT0gJ2F1ZGlvJykge1xuICAgICAgICAgICAgbnVtQXVkaW9UcmFja3MtLTtcbiAgICAgICAgICB9IGVsc2UgaWYgKHRyYWNrLmtpbmQgPT09ICd2aWRlbycpIHtcbiAgICAgICAgICAgIG51bVZpZGVvVHJhY2tzLS07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIC8vIENyZWF0ZSBNLWxpbmVzIGZvciByZWN2b25seSBzdHJlYW1zLlxuICAgICAgd2hpbGUgKG51bUF1ZGlvVHJhY2tzID4gMCB8fCBudW1WaWRlb1RyYWNrcyA+IDApIHtcbiAgICAgICAgaWYgKG51bUF1ZGlvVHJhY2tzID4gMCkge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6ICdhdWRpbycsXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG51bUF1ZGlvVHJhY2tzLS07XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG51bVZpZGVvVHJhY2tzID4gMCkge1xuICAgICAgICAgIHRyYWNrcy5wdXNoKHtcbiAgICAgICAgICAgIGtpbmQ6ICd2aWRlbycsXG4gICAgICAgICAgICB3YW50UmVjZWl2ZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG51bVZpZGVvVHJhY2tzLS07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdmFyIHNkcCA9IFNEUFV0aWxzLndyaXRlU2Vzc2lvbkJvaWxlcnBsYXRlKCk7XG4gICAgICB2YXIgdHJhbnNjZWl2ZXJzID0gW107XG4gICAgICB0cmFja3MuZm9yRWFjaChmdW5jdGlvbihtbGluZSwgc2RwTUxpbmVJbmRleCkge1xuICAgICAgICAvLyBGb3IgZWFjaCB0cmFjaywgY3JlYXRlIGFuIGljZSBnYXRoZXJlciwgaWNlIHRyYW5zcG9ydCxcbiAgICAgICAgLy8gZHRscyB0cmFuc3BvcnQsIHBvdGVudGlhbGx5IHJ0cHNlbmRlciBhbmQgcnRwcmVjZWl2ZXIuXG4gICAgICAgIHZhciB0cmFjayA9IG1saW5lLnRyYWNrO1xuICAgICAgICB2YXIga2luZCA9IG1saW5lLmtpbmQ7XG4gICAgICAgIHZhciBtaWQgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcblxuICAgICAgICB2YXIgdHJhbnNwb3J0cyA9IHNlbGYudXNpbmdCdW5kbGUgJiYgc2RwTUxpbmVJbmRleCA+IDAgPyB7XG4gICAgICAgICAgaWNlR2F0aGVyZXI6IHRyYW5zY2VpdmVyc1swXS5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zY2VpdmVyc1swXS5pY2VUcmFuc3BvcnQsXG4gICAgICAgICAgZHRsc1RyYW5zcG9ydDogdHJhbnNjZWl2ZXJzWzBdLmR0bHNUcmFuc3BvcnRcbiAgICAgICAgfSA6IHNlbGYuX2NyZWF0ZUljZUFuZER0bHNUcmFuc3BvcnRzKG1pZCwgc2RwTUxpbmVJbmRleCk7XG5cbiAgICAgICAgdmFyIGxvY2FsQ2FwYWJpbGl0aWVzID0gUlRDUnRwU2VuZGVyLmdldENhcGFiaWxpdGllcyhraW5kKTtcbiAgICAgICAgLy8gZmlsdGVyIFJUWCB1bnRpbCBhZGRpdGlvbmFsIHN0dWZmIG5lZWRlZCBmb3IgUlRYIGlzIGltcGxlbWVudGVkXG4gICAgICAgIC8vIGluIGFkYXB0ZXIuanNcbiAgICAgICAgbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzID0gbG9jYWxDYXBhYmlsaXRpZXMuY29kZWNzLmZpbHRlcihcbiAgICAgICAgICAgIGZ1bmN0aW9uKGNvZGVjKSB7XG4gICAgICAgICAgICAgIHJldHVybiBjb2RlYy5uYW1lICE9PSAncnR4JztcbiAgICAgICAgICAgIH0pO1xuICAgICAgICBsb2NhbENhcGFiaWxpdGllcy5jb2RlY3MuZm9yRWFjaChmdW5jdGlvbihjb2RlYykge1xuICAgICAgICAgIC8vIHdvcmsgYXJvdW5kIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD02NTUyXG4gICAgICAgICAgLy8gYnkgYWRkaW5nIGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTFcbiAgICAgICAgICBpZiAoY29kZWMubmFtZSA9PT0gJ0gyNjQnICYmXG4gICAgICAgICAgICAgIGNvZGVjLnBhcmFtZXRlcnNbJ2xldmVsLWFzeW1tZXRyeS1hbGxvd2VkJ10gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgY29kZWMucGFyYW1ldGVyc1snbGV2ZWwtYXN5bW1ldHJ5LWFsbG93ZWQnXSA9ICcxJztcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHZhciBydHBTZW5kZXI7XG4gICAgICAgIHZhciBydHBSZWNlaXZlcjtcblxuICAgICAgICAvLyBnZW5lcmF0ZSBhbiBzc3JjIG5vdywgdG8gYmUgdXNlZCBsYXRlciBpbiBydHBTZW5kZXIuc2VuZFxuICAgICAgICB2YXIgc2VuZEVuY29kaW5nUGFyYW1ldGVycyA9IFt7XG4gICAgICAgICAgc3NyYzogKDIgKiBzZHBNTGluZUluZGV4ICsgMSkgKiAxMDAxXG4gICAgICAgIH1dO1xuICAgICAgICBpZiAodHJhY2spIHtcbiAgICAgICAgICBydHBTZW5kZXIgPSBuZXcgUlRDUnRwU2VuZGVyKHRyYWNrLCB0cmFuc3BvcnRzLmR0bHNUcmFuc3BvcnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG1saW5lLndhbnRSZWNlaXZlKSB7XG4gICAgICAgICAgcnRwUmVjZWl2ZXIgPSBuZXcgUlRDUnRwUmVjZWl2ZXIodHJhbnNwb3J0cy5kdGxzVHJhbnNwb3J0LCBraW5kKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyYW5zY2VpdmVyc1tzZHBNTGluZUluZGV4XSA9IHtcbiAgICAgICAgICBpY2VHYXRoZXJlcjogdHJhbnNwb3J0cy5pY2VHYXRoZXJlcixcbiAgICAgICAgICBpY2VUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuaWNlVHJhbnNwb3J0LFxuICAgICAgICAgIGR0bHNUcmFuc3BvcnQ6IHRyYW5zcG9ydHMuZHRsc1RyYW5zcG9ydCxcbiAgICAgICAgICBsb2NhbENhcGFiaWxpdGllczogbG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgcmVtb3RlQ2FwYWJpbGl0aWVzOiBudWxsLFxuICAgICAgICAgIHJ0cFNlbmRlcjogcnRwU2VuZGVyLFxuICAgICAgICAgIHJ0cFJlY2VpdmVyOiBydHBSZWNlaXZlcixcbiAgICAgICAgICBraW5kOiBraW5kLFxuICAgICAgICAgIG1pZDogbWlkLFxuICAgICAgICAgIHNlbmRFbmNvZGluZ1BhcmFtZXRlcnM6IHNlbmRFbmNvZGluZ1BhcmFtZXRlcnMsXG4gICAgICAgICAgcmVjdkVuY29kaW5nUGFyYW1ldGVyczogbnVsbFxuICAgICAgICB9O1xuICAgICAgfSk7XG4gICAgICBpZiAodGhpcy51c2luZ0J1bmRsZSkge1xuICAgICAgICBzZHAgKz0gJ2E9Z3JvdXA6QlVORExFICcgKyB0cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdHJhY2tzLmZvckVhY2goZnVuY3Rpb24obWxpbmUsIHNkcE1MaW5lSW5kZXgpIHtcbiAgICAgICAgdmFyIHRyYW5zY2VpdmVyID0gdHJhbnNjZWl2ZXJzW3NkcE1MaW5lSW5kZXhdO1xuICAgICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24odHJhbnNjZWl2ZXIsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5sb2NhbENhcGFiaWxpdGllcywgJ29mZmVyJywgc2VsZi5sb2NhbFN0cmVhbXNbMF0pO1xuICAgICAgfSk7XG5cbiAgICAgIHRoaXMuX3BlbmRpbmdPZmZlciA9IHRyYW5zY2VpdmVycztcbiAgICAgIHZhciBkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7XG4gICAgICAgIHR5cGU6ICdvZmZlcicsXG4gICAgICAgIHNkcDogc2RwXG4gICAgICB9KTtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoICYmIHR5cGVvZiBhcmd1bWVudHNbMF0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoYXJndW1lbnRzWzBdLCAwLCBkZXNjKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGVzYyk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlQW5zd2VyID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAgIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSgpO1xuICAgICAgaWYgKHRoaXMudXNpbmdCdW5kbGUpIHtcbiAgICAgICAgc2RwICs9ICdhPWdyb3VwOkJVTkRMRSAnICsgdGhpcy50cmFuc2NlaXZlcnMubWFwKGZ1bmN0aW9uKHQpIHtcbiAgICAgICAgICByZXR1cm4gdC5taWQ7XG4gICAgICAgIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgfVxuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBpZiAodHJhbnNjZWl2ZXIuaXNEYXRhY2hhbm5lbCkge1xuICAgICAgICAgIHNkcCArPSAnbT1hcHBsaWNhdGlvbiAwIERUTFMvU0NUUCA1MDAwXFxyXFxuJyArXG4gICAgICAgICAgICAgICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJyArXG4gICAgICAgICAgICAgICdhPW1pZDonICsgdHJhbnNjZWl2ZXIubWlkICsgJ1xcclxcbic7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIENhbGN1bGF0ZSBpbnRlcnNlY3Rpb24gb2YgY2FwYWJpbGl0aWVzLlxuICAgICAgICB2YXIgY29tbW9uQ2FwYWJpbGl0aWVzID0gc2VsZi5fZ2V0Q29tbW9uQ2FwYWJpbGl0aWVzKFxuICAgICAgICAgICAgdHJhbnNjZWl2ZXIubG9jYWxDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgICB0cmFuc2NlaXZlci5yZW1vdGVDYXBhYmlsaXRpZXMpO1xuXG4gICAgICAgIHNkcCArPSBTRFBVdGlscy53cml0ZU1lZGlhU2VjdGlvbih0cmFuc2NlaXZlciwgY29tbW9uQ2FwYWJpbGl0aWVzLFxuICAgICAgICAgICAgJ2Fuc3dlcicsIHNlbGYubG9jYWxTdHJlYW1zWzBdKTtcbiAgICAgIH0pO1xuXG4gICAgICB2YXIgZGVzYyA9IG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICB0eXBlOiAnYW5zd2VyJyxcbiAgICAgICAgc2RwOiBzZHBcbiAgICAgIH0pO1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggJiYgdHlwZW9mIGFyZ3VtZW50c1swXSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChhcmd1bWVudHNbMF0sIDAsIGRlc2MpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShkZXNjKTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgICAgIGlmICghY2FuZGlkYXRlKSB7XG4gICAgICAgIHRoaXMudHJhbnNjZWl2ZXJzLmZvckVhY2goZnVuY3Rpb24odHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKHt9KTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgbUxpbmVJbmRleCA9IGNhbmRpZGF0ZS5zZHBNTGluZUluZGV4O1xuICAgICAgICBpZiAoY2FuZGlkYXRlLnNkcE1pZCkge1xuICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy50cmFuc2NlaXZlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnRyYW5zY2VpdmVyc1tpXS5taWQgPT09IGNhbmRpZGF0ZS5zZHBNaWQpIHtcbiAgICAgICAgICAgICAgbUxpbmVJbmRleCA9IGk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB2YXIgdHJhbnNjZWl2ZXIgPSB0aGlzLnRyYW5zY2VpdmVyc1ttTGluZUluZGV4XTtcbiAgICAgICAgaWYgKHRyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgdmFyIGNhbmQgPSBPYmplY3Qua2V5cyhjYW5kaWRhdGUuY2FuZGlkYXRlKS5sZW5ndGggPiAwID9cbiAgICAgICAgICAgICAgU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUoY2FuZGlkYXRlLmNhbmRpZGF0ZSkgOiB7fTtcbiAgICAgICAgICAvLyBJZ25vcmUgQ2hyb21lJ3MgaW52YWxpZCBjYW5kaWRhdGVzIHNpbmNlIEVkZ2UgZG9lcyBub3QgbGlrZSB0aGVtLlxuICAgICAgICAgIGlmIChjYW5kLnByb3RvY29sID09PSAndGNwJyAmJiAoY2FuZC5wb3J0ID09PSAwIHx8IGNhbmQucG9ydCA9PT0gOSkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gSWdub3JlIFJUQ1AgY2FuZGlkYXRlcywgd2UgYXNzdW1lIFJUQ1AtTVVYLlxuICAgICAgICAgIGlmIChjYW5kLmNvbXBvbmVudCAhPT0gJzEnKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIEEgZGlydHkgaGFjayB0byBtYWtlIHNhbXBsZXMgd29yay5cbiAgICAgICAgICBpZiAoY2FuZC50eXBlID09PSAnZW5kT2ZDYW5kaWRhdGVzJykge1xuICAgICAgICAgICAgY2FuZCA9IHt9O1xuICAgICAgICAgIH1cbiAgICAgICAgICB0cmFuc2NlaXZlci5pY2VUcmFuc3BvcnQuYWRkUmVtb3RlQ2FuZGlkYXRlKGNhbmQpO1xuXG4gICAgICAgICAgLy8gdXBkYXRlIHRoZSByZW1vdGVEZXNjcmlwdGlvbi5cbiAgICAgICAgICB2YXIgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcbiAgICAgICAgICBzZWN0aW9uc1ttTGluZUluZGV4ICsgMV0gKz0gKGNhbmQudHlwZSA/IGNhbmRpZGF0ZS5jYW5kaWRhdGUudHJpbSgpXG4gICAgICAgICAgICAgIDogJ2E9ZW5kLW9mLWNhbmRpZGF0ZXMnKSArICdcXHJcXG4nO1xuICAgICAgICAgIHRoaXMucmVtb3RlRGVzY3JpcHRpb24uc2RwID0gc2VjdGlvbnMuam9pbignJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSAmJiB0eXBlb2YgYXJndW1lbnRzWzFdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGFyZ3VtZW50c1sxXSwgMCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfTtcblxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0U3RhdHMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBwcm9taXNlcyA9IFtdO1xuICAgICAgdGhpcy50cmFuc2NlaXZlcnMuZm9yRWFjaChmdW5jdGlvbih0cmFuc2NlaXZlcikge1xuICAgICAgICBbJ3J0cFNlbmRlcicsICdydHBSZWNlaXZlcicsICdpY2VHYXRoZXJlcicsICdpY2VUcmFuc3BvcnQnLFxuICAgICAgICAgICAgJ2R0bHNUcmFuc3BvcnQnXS5mb3JFYWNoKGZ1bmN0aW9uKG1ldGhvZCkge1xuICAgICAgICAgICAgICBpZiAodHJhbnNjZWl2ZXJbbWV0aG9kXSkge1xuICAgICAgICAgICAgICAgIHByb21pc2VzLnB1c2godHJhbnNjZWl2ZXJbbWV0aG9kXS5nZXRTdGF0cygpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICAgIHZhciBjYiA9IGFyZ3VtZW50cy5sZW5ndGggPiAxICYmIHR5cGVvZiBhcmd1bWVudHNbMV0gPT09ICdmdW5jdGlvbicgJiZcbiAgICAgICAgICBhcmd1bWVudHNbMV07XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSkge1xuICAgICAgICAvLyBzaGltIGdldFN0YXRzIHdpdGggbWFwbGlrZSBzdXBwb3J0XG4gICAgICAgIHZhciByZXN1bHRzID0gbmV3IE1hcCgpO1xuICAgICAgICBQcm9taXNlLmFsbChwcm9taXNlcykudGhlbihmdW5jdGlvbihyZXMpIHtcbiAgICAgICAgICByZXMuZm9yRWFjaChmdW5jdGlvbihyZXN1bHQpIHtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHJlc3VsdCkuZm9yRWFjaChmdW5jdGlvbihpZCkge1xuICAgICAgICAgICAgICByZXN1bHRzLnNldChpZCwgcmVzdWx0W2lkXSk7XG4gICAgICAgICAgICAgIHJlc3VsdHNbaWRdID0gcmVzdWx0W2lkXTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIGlmIChjYikge1xuICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoY2IsIDAsIHJlc3VsdHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXNvbHZlKHJlc3VsdHMpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH07XG4gIH1cbn07XG5cbi8vIEV4cG9zZSBwdWJsaWMgbWV0aG9kcy5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBzaGltUGVlckNvbm5lY3Rpb246IGVkZ2VTaGltLnNoaW1QZWVyQ29ubmVjdGlvbixcbiAgc2hpbUdldFVzZXJNZWRpYTogcmVxdWlyZSgnLi9nZXR1c2VybWVkaWEnKVxufTtcblxuXG5cbi8vLy8vLy8vLy8vLy8vLy8vL1xuLy8gV0VCUEFDSyBGT09URVJcbi8vIC4vfi93ZWJydGMtYWRhcHRlci9zcmMvanMvZWRnZS9lZGdlX3NoaW0uanNcbi8vIG1vZHVsZSBpZCA9IDQ2M1xuLy8gbW9kdWxlIGNodW5rcyA9IDAiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9");

FIXME found
Open

    eval("var util = __webpack_require__(438);\nvar SJJ = __webpack_require__(472);\nvar WildEmitter = __webpack_require__(445);\nvar Peerconn = __webpack_require__(477);\nvar adapter = __webpack_require__(459);\nvar cloneDeep = __webpack_require__(487);\n\nfunction PeerConnection(config, constraints) {\n    var self = this;\n    var item;\n    WildEmitter.call(this);\n\n    config = config || {};\n    config.iceServers = config.iceServers || [];\n\n    var detectedBrowser = adapter.browserDetails.browser;\n\n    // make sure this only gets enabled in Google Chrome\n    // EXPERIMENTAL FLAG, might get removed without notice\n    this.enableChromeNativeSimulcast = false;\n    if (constraints && constraints.optional &&\n            detectedBrowser === 'chrome' &&\n            navigator.appVersion.match(/Chromium\\//) === null) {\n        constraints.optional.forEach(function (constraint) {\n            if (constraint.enableChromeNativeSimulcast) {\n                self.enableChromeNativeSimulcast = true;\n            }\n        });\n    }\n\n    // EXPERIMENTAL FLAG, might get removed without notice\n    this.enableMultiStreamHacks = false;\n    if (constraints && constraints.optional &&\n            detectedBrowser === 'chrome') {\n        constraints.optional.forEach(function (constraint) {\n            if (constraint.enableMultiStreamHacks) {\n                self.enableMultiStreamHacks = true;\n            }\n        });\n    }\n    // EXPERIMENTAL FLAG, might get removed without notice\n    this.restrictBandwidth = 0;\n    if (constraints && constraints.optional) {\n        constraints.optional.forEach(function (constraint) {\n            if (constraint.andyetRestrictBandwidth) {\n                self.restrictBandwidth = constraint.andyetRestrictBandwidth;\n            }\n        });\n    }\n\n    // EXPERIMENTAL FLAG, might get removed without notice\n    // bundle up ice candidates, only works for jingle mode\n    // number > 0 is the delay to wait for additional candidates\n    // ~20ms seems good\n    this.batchIceCandidates = 0;\n    if (constraints && constraints.optional) {\n        constraints.optional.forEach(function (constraint) {\n            if (constraint.andyetBatchIce) {\n                self.batchIceCandidates = constraint.andyetBatchIce;\n            }\n        });\n    }\n    this.batchedIceCandidates = [];\n\n    // EXPERIMENTAL FLAG, might get removed without notice\n    // this attemps to strip out candidates with an already known foundation\n    // and type -- i.e. those which are gathered via the same TURN server\n    // but different transports (TURN udp, tcp and tls respectively)\n    if (constraints && constraints.optional && detectedBrowser === 'chrome') {\n        constraints.optional.forEach(function (constraint) {\n            if (constraint.andyetFasterICE) {\n                self.eliminateDuplicateCandidates = constraint.andyetFasterICE;\n            }\n        });\n    }\n    // EXPERIMENTAL FLAG, might get removed without notice\n    // when using a server such as the jitsi videobridge we don't need to signal\n    // our candidates\n    if (constraints && constraints.optional) {\n        constraints.optional.forEach(function (constraint) {\n            if (constraint.andyetDontSignalCandidates) {\n                self.dontSignalCandidates = constraint.andyetDontSignalCandidates;\n            }\n        });\n    }\n\n\n    // EXPERIMENTAL FLAG, might get removed without notice\n    this.assumeSetLocalSuccess = false;\n    if (constraints && constraints.optional) {\n        constraints.optional.forEach(function (constraint) {\n            if (constraint.andyetAssumeSetLocalSuccess) {\n                self.assumeSetLocalSuccess = constraint.andyetAssumeSetLocalSuccess;\n            }\n        });\n    }\n\n    // EXPERIMENTAL FLAG, might get removed without notice\n    // working around https://bugzilla.mozilla.org/show_bug.cgi?id=1087551\n    // pass in a timeout for this\n    if (detectedBrowser === 'firefox') {\n        if (constraints && constraints.optional) {\n            this.wtFirefox = 0;\n            constraints.optional.forEach(function (constraint) {\n                if (constraint.andyetFirefoxMakesMeSad) {\n                    self.wtFirefox = constraint.andyetFirefoxMakesMeSad;\n                    if (self.wtFirefox > 0) {\n                        self.firefoxcandidatebuffer = [];\n                    }\n                }\n            });\n        }\n    }\n\n\n    this.pc = new Peerconn(config, constraints);\n\n    this.getLocalStreams = this.pc.getLocalStreams.bind(this.pc);\n    this.getRemoteStreams = this.pc.getRemoteStreams.bind(this.pc);\n    this.addStream = this.pc.addStream.bind(this.pc);\n    this.removeStream = this.pc.removeStream.bind(this.pc);\n\n    // proxy events\n    this.pc.on('*', function () {\n        self.emit.apply(self, arguments);\n    });\n\n    // proxy some events directly\n    this.pc.onremovestream = this.emit.bind(this, 'removeStream');\n    this.pc.onaddstream = this.emit.bind(this, 'addStream');\n    this.pc.onnegotiationneeded = this.emit.bind(this, 'negotiationNeeded');\n    this.pc.oniceconnectionstatechange = this.emit.bind(this, 'iceConnectionStateChange');\n    this.pc.onsignalingstatechange = this.emit.bind(this, 'signalingStateChange');\n\n    // handle ice candidate and data channel events\n    this.pc.onicecandidate = this._onIce.bind(this);\n    this.pc.ondatachannel = this._onDataChannel.bind(this);\n\n    this.localDescription = {\n        contents: []\n    };\n    this.remoteDescription = {\n        contents: []\n    };\n\n    this.config = {\n        debug: false,\n        sid: '',\n        isInitiator: true,\n        sdpSessionID: Date.now(),\n        useJingle: false\n    };\n\n    this.iceCredentials = {\n        local: {},\n        remote: {}\n    };\n\n    // apply our config\n    for (item in config) {\n        this.config[item] = config[item];\n    }\n\n    if (this.config.debug) {\n        this.on('*', function () {\n            var logger = config.logger || console;\n            logger.log('PeerConnection event:', arguments);\n        });\n    }\n    this.hadLocalStunCandidate = false;\n    this.hadRemoteStunCandidate = false;\n    this.hadLocalRelayCandidate = false;\n    this.hadRemoteRelayCandidate = false;\n\n    this.hadLocalIPv6Candidate = false;\n    this.hadRemoteIPv6Candidate = false;\n\n    // keeping references for all our data channels\n    // so they dont get garbage collected\n    // can be removed once the following bugs have been fixed\n    // https://crbug.com/405545\n    // https://bugzilla.mozilla.org/show_bug.cgi?id=964092\n    // to be filed for opera\n    this._remoteDataChannels = [];\n    this._localDataChannels = [];\n\n    this._candidateBuffer = [];\n}\n\nutil.inherits(PeerConnection, WildEmitter);\n\nObject.defineProperty(PeerConnection.prototype, 'signalingState', {\n    get: function () {\n        return this.pc.signalingState;\n    }\n});\nObject.defineProperty(PeerConnection.prototype, 'iceConnectionState', {\n    get: function () {\n        return this.pc.iceConnectionState;\n    }\n});\n\nPeerConnection.prototype._role = function () {\n    return this.isInitiator ? 'initiator' : 'responder';\n};\n\n// Add a stream to the peer connection object\nPeerConnection.prototype.addStream = function (stream) {\n    this.localStream = stream;\n    this.pc.addStream(stream);\n};\n\n// helper function to check if a remote candidate is a stun/relay\n// candidate or an ipv6 candidate\nPeerConnection.prototype._checkLocalCandidate = function (candidate) {\n    var cand = SJJ.toCandidateJSON(candidate);\n    if (cand.type == 'srflx') {\n        this.hadLocalStunCandidate = true;\n    } else if (cand.type == 'relay') {\n        this.hadLocalRelayCandidate = true;\n    }\n    if (cand.ip.indexOf(':') != -1) {\n        this.hadLocalIPv6Candidate = true;\n    }\n};\n\n// helper function to check if a remote candidate is a stun/relay\n// candidate or an ipv6 candidate\nPeerConnection.prototype._checkRemoteCandidate = function (candidate) {\n    var cand = SJJ.toCandidateJSON(candidate);\n    if (cand.type == 'srflx') {\n        this.hadRemoteStunCandidate = true;\n    } else if (cand.type == 'relay') {\n        this.hadRemoteRelayCandidate = true;\n    }\n    if (cand.ip.indexOf(':') != -1) {\n        this.hadRemoteIPv6Candidate = true;\n    }\n};\n\n\n// Init and add ice candidate object with correct constructor\nPeerConnection.prototype.processIce = function (update, cb) {\n    cb = cb || function () {};\n    var self = this;\n\n    // ignore any added ice candidates to avoid errors. why does the\n    // spec not do this?\n    if (this.pc.signalingState === 'closed') return cb();\n\n    if (update.contents || (update.jingle && update.jingle.contents)) {\n        var contentNames = this.remoteDescription.contents.map(function (c) { return c.name; });\n        var contents = update.contents || update.jingle.contents;\n\n        contents.forEach(function (content) {\n            var transport = content.transport || {};\n            var candidates = transport.candidates || [];\n            var mline = contentNames.indexOf(content.name);\n            var mid = content.name;\n            var remoteContent = self.remoteDescription.contents.find(function (c) {\n                return c.name === content.name;\n            });\n\n            // process candidates as a callback, in case we need to\n            // update ufrag and pwd with offer/answer\n            var processCandidates = function () {\n                candidates.forEach(\n                    function (candidate) {\n                    var iceCandidate = SJJ.toCandidateSDP(candidate) + '\\r\\n';\n                    self.pc.addIceCandidate(\n                        new RTCIceCandidate({\n                            candidate: iceCandidate,\n                            sdpMLineIndex: mline,\n                            sdpMid: mid\n                        }), function () {\n                            // well, this success callback is pretty meaningless\n                        },\n                        function (err) {\n                            self.emit('error', err);\n                        }\n                    );\n                    self._checkRemoteCandidate(iceCandidate);\n                });\n                cb();\n            };\n\n            if (self.iceCredentials.remote[content.name] && transport.ufrag &&\n                self.iceCredentials.remote[content.name].ufrag !== transport.ufrag) {\n                if (remoteContent) {\n                    remoteContent.transport.ufrag = transport.ufrag;\n                    remoteContent.transport.pwd = transport.pwd;\n                    var offer = {\n                        type: 'offer',\n                        jingle: self.remoteDescription\n                    };\n                    offer.sdp = SJJ.toSessionSDP(offer.jingle, {\n                        sid: self.config.sdpSessionID,\n                        role: self._role(),\n                        direction: 'incoming'\n                    });\n                    self.pc.setRemoteDescription(new RTCSessionDescription(offer),\n                        function () {\n                            processCandidates();\n                        },\n                        function (err) {\n                            self.emit('error', err);\n                        }\n                    );\n                } else {\n                    self.emit('error', 'ice restart failed to find matching content');\n                }\n            } else {\n                processCandidates();\n            }\n        });\n    } else {\n        // working around https://code.google.com/p/webrtc/issues/detail?id=3669\n        if (update.candidate && update.candidate.candidate.indexOf('a=') !== 0) {\n            update.candidate.candidate = 'a=' + update.candidate.candidate;\n        }\n\n        if (this.wtFirefox && this.firefoxcandidatebuffer !== null) {\n            // we cant add this yet due to https://bugzilla.mozilla.org/show_bug.cgi?id=1087551\n            if (this.pc.localDescription && this.pc.localDescription.type === 'offer') {\n                this.firefoxcandidatebuffer.push(update.candidate);\n                return cb();\n            }\n        }\n\n        self.pc.addIceCandidate(\n            new RTCIceCandidate(update.candidate),\n            function () { },\n            function (err) {\n                self.emit('error', err);\n            }\n        );\n        self._checkRemoteCandidate(update.candidate.candidate);\n        cb();\n    }\n};\n\n// Generate and emit an offer with the given constraints\nPeerConnection.prototype.offer = function (constraints, cb) {\n    var self = this;\n    var hasConstraints = arguments.length === 2;\n    var mediaConstraints = hasConstraints && constraints ? constraints : {\n            offerToReceiveAudio: 1,\n            offerToReceiveVideo: 1\n        };\n    cb = hasConstraints ? cb : constraints;\n    cb = cb || function () {};\n\n    if (this.pc.signalingState === 'closed') return cb('Already closed');\n\n    // Actually generate the offer\n    this.pc.createOffer(\n        function (offer) {\n            // does not work for jingle, but jingle.js doesn't need\n            // this hack...\n            var expandedOffer = {\n                type: 'offer',\n                sdp: offer.sdp\n            };\n            if (self.assumeSetLocalSuccess) {\n                self.emit('offer', expandedOffer);\n                cb(null, expandedOffer);\n            }\n            self._candidateBuffer = [];\n            self.pc.setLocalDescription(offer,\n                function () {\n                    var jingle;\n                    if (self.config.useJingle) {\n                        jingle = SJJ.toSessionJSON(offer.sdp, {\n                            role: self._role(),\n                            direction: 'outgoing'\n                        });\n                        jingle.sid = self.config.sid;\n                        self.localDescription = jingle;\n\n                        // Save ICE credentials\n                        jingle.contents.forEach(function (content) {\n                            var transport = content.transport || {};\n                            if (transport.ufrag) {\n                                self.iceCredentials.local[content.name] = {\n                                    ufrag: transport.ufrag,\n                                    pwd: transport.pwd\n                                };\n                            }\n                        });\n\n                        expandedOffer.jingle = jingle;\n                    }\n                    expandedOffer.sdp.split('\\r\\n').forEach(function (line) {\n                        if (line.indexOf('a=candidate:') === 0) {\n                            self._checkLocalCandidate(line);\n                        }\n                    });\n\n                    if (!self.assumeSetLocalSuccess) {\n                        self.emit('offer', expandedOffer);\n                        cb(null, expandedOffer);\n                    }\n                },\n                function (err) {\n                    self.emit('error', err);\n                    cb(err);\n                }\n            );\n        },\n        function (err) {\n            self.emit('error', err);\n            cb(err);\n        },\n        mediaConstraints\n    );\n};\n\n\n// Process an incoming offer so that ICE may proceed before deciding\n// to answer the request.\nPeerConnection.prototype.handleOffer = function (offer, cb) {\n    cb = cb || function () {};\n    var self = this;\n    offer.type = 'offer';\n    if (offer.jingle) {\n        if (this.enableChromeNativeSimulcast) {\n            offer.jingle.contents.forEach(function (content) {\n                if (content.name === 'video') {\n                    content.application.googConferenceFlag = true;\n                }\n\n            });\n        }\n        if (this.enableMultiStreamHacks) {\n            // add a mixed video stream as first stream\n            offer.jingle.contents.forEach(function (content) {\n                if (content.name === 'video') {\n                    var sources = content.application.sources || [];\n                    if (sources.length === 0 || sources[0].ssrc !== \"3735928559\") {\n                        sources.unshift({\n                            ssrc: \"3735928559\", // 0xdeadbeef\n                            parameters: [\n                                {\n                                    key: \"cname\",\n                                    value: \"deadbeef\"\n                                },\n                                {\n                                    key: \"msid\",\n                                    value: \"mixyourfecintothis please\"\n                                }\n                            ]\n                        });\n                        content.application.sources = sources;\n                    }\n                }\n            });\n        }\n        if (self.restrictBandwidth > 0) {\n            if (offer.jingle.contents.length >= 2 && offer.jingle.contents[1].name === 'video') {\n                var content = offer.jingle.contents[1];\n                var hasBw = content.application && content.application.bandwidth && content.application.bandwidth.bandwidth;\n                if (!hasBw) {\n                    offer.jingle.contents[1].application.bandwidth = { type: 'AS', bandwidth: self.restrictBandwidth.toString() };\n                    offer.sdp = SJJ.toSessionSDP(offer.jingle, {\n                        sid: self.config.sdpSessionID,\n                        role: self._role(),\n                        direction: 'outgoing'\n                    });\n                }\n            }\n        }\n        // Save ICE credentials\n        offer.jingle.contents.forEach(function (content) {\n            var transport = content.transport || {};\n            if (transport.ufrag) {\n                self.iceCredentials.remote[content.name] = {\n                    ufrag: transport.ufrag,\n                    pwd: transport.pwd\n                };\n            }\n        });\n        offer.sdp = SJJ.toSessionSDP(offer.jingle, {\n            sid: self.config.sdpSessionID,\n            role: self._role(),\n            direction: 'incoming'\n        });\n        self.remoteDescription = offer.jingle;\n    }\n    offer.sdp.split('\\r\\n').forEach(function (line) {\n        if (line.indexOf('a=candidate:') === 0) {\n            self._checkRemoteCandidate(line);\n        }\n    });\n    self.pc.setRemoteDescription(new RTCSessionDescription(offer),\n        function () {\n            cb();\n        },\n        cb\n    );\n};\n\n// Answer an offer with audio only\nPeerConnection.prototype.answerAudioOnly = function (cb) {\n    var mediaConstraints = {\n            mandatory: {\n                OfferToReceiveAudio: true,\n                OfferToReceiveVideo: false\n            }\n        };\n    this._answer(mediaConstraints, cb);\n};\n\n// Answer an offer without offering to recieve\nPeerConnection.prototype.answerBroadcastOnly = function (cb) {\n    var mediaConstraints = {\n            mandatory: {\n                OfferToReceiveAudio: false,\n                OfferToReceiveVideo: false\n            }\n        };\n    this._answer(mediaConstraints, cb);\n};\n\n// Answer an offer with given constraints default is audio/video\nPeerConnection.prototype.answer = function (constraints, cb) {\n    var hasConstraints = arguments.length === 2;\n    var callback = hasConstraints ? cb : constraints;\n    var mediaConstraints = hasConstraints && constraints ? constraints : {\n            mandatory: {\n                OfferToReceiveAudio: true,\n                OfferToReceiveVideo: true\n            }\n        };\n\n    this._answer(mediaConstraints, callback);\n};\n\n// Process an answer\nPeerConnection.prototype.handleAnswer = function (answer, cb) {\n    cb = cb || function () {};\n    var self = this;\n    if (answer.jingle) {\n        answer.sdp = SJJ.toSessionSDP(answer.jingle, {\n            sid: self.config.sdpSessionID,\n            role: self._role(),\n            direction: 'incoming'\n        });\n        self.remoteDescription = answer.jingle;\n\n        // Save ICE credentials\n        answer.jingle.contents.forEach(function (content) {\n            var transport = content.transport || {};\n            if (transport.ufrag) {\n                self.iceCredentials.remote[content.name] = {\n                    ufrag: transport.ufrag,\n                    pwd: transport.pwd\n                };\n            }\n        });\n    }\n    answer.sdp.split('\\r\\n').forEach(function (line) {\n        if (line.indexOf('a=candidate:') === 0) {\n            self._checkRemoteCandidate(line);\n        }\n    });\n    self.pc.setRemoteDescription(\n        new RTCSessionDescription(answer),\n        function () {\n            if (self.wtFirefox) {\n                window.setTimeout(function () {\n                    self.firefoxcandidatebuffer.forEach(function (candidate) {\n                        // add candidates later\n                        self.pc.addIceCandidate(\n                            new RTCIceCandidate(candidate),\n                            function () { },\n                            function (err) {\n                                self.emit('error', err);\n                            }\n                        );\n                        self._checkRemoteCandidate(candidate.candidate);\n                    });\n                    self.firefoxcandidatebuffer = null;\n                }, self.wtFirefox);\n            }\n            cb(null);\n        },\n        cb\n    );\n};\n\n// Close the peer connection\nPeerConnection.prototype.close = function () {\n    this.pc.close();\n\n    this._localDataChannels = [];\n    this._remoteDataChannels = [];\n\n    this.emit('close');\n};\n\n// Internal code sharing for various types of answer methods\nPeerConnection.prototype._answer = function (constraints, cb) {\n    cb = cb || function () {};\n    var self = this;\n    if (!this.pc.remoteDescription) {\n        // the old API is used, call handleOffer\n        throw new Error('remoteDescription not set');\n    }\n\n    if (this.pc.signalingState === 'closed') return cb('Already closed');\n\n    self.pc.createAnswer(\n        function (answer) {\n            var sim = [];\n            if (self.enableChromeNativeSimulcast) {\n                // native simulcast part 1: add another SSRC\n                answer.jingle = SJJ.toSessionJSON(answer.sdp, {\n                    role: self._role(),\n                    direction: 'outgoing'\n                });\n                if (answer.jingle.contents.length >= 2 && answer.jingle.contents[1].name === 'video') {\n                    var groups = answer.jingle.contents[1].application.sourceGroups || [];\n                    var hasSim = false;\n                    groups.forEach(function (group) {\n                        if (group.semantics == 'SIM') hasSim = true;\n                    });\n                    if (!hasSim &&\n                        answer.jingle.contents[1].application.sources.length) {\n                        var newssrc = JSON.parse(JSON.stringify(answer.jingle.contents[1].application.sources[0]));\n                        newssrc.ssrc = '' + Math.floor(Math.random() * 0xffffffff); // FIXME: look for conflicts\n                        answer.jingle.contents[1].application.sources.push(newssrc);\n\n                        sim.push(answer.jingle.contents[1].application.sources[0].ssrc);\n                        sim.push(newssrc.ssrc);\n                        groups.push({\n                            semantics: 'SIM',\n                            sources: sim\n                        });\n\n                        // also create an RTX one for the SIM one\n                        var rtxssrc = JSON.parse(JSON.stringify(newssrc));\n                        rtxssrc.ssrc = '' + Math.floor(Math.random() * 0xffffffff); // FIXME: look for conflicts\n                        answer.jingle.contents[1].application.sources.push(rtxssrc);\n                        groups.push({\n                            semantics: 'FID',\n                            sources: [newssrc.ssrc, rtxssrc.ssrc]\n                        });\n\n                        answer.jingle.contents[1].application.sourceGroups = groups;\n                        answer.sdp = SJJ.toSessionSDP(answer.jingle, {\n                            sid: self.config.sdpSessionID,\n                            role: self._role(),\n                            direction: 'outgoing'\n                        });\n                    }\n                }\n            }\n            var expandedAnswer = {\n                type: 'answer',\n                sdp: answer.sdp\n            };\n            if (self.assumeSetLocalSuccess) {\n                // not safe to do when doing simulcast mangling\n                var copy = cloneDeep(expandedAnswer);\n                self.emit('answer', copy);\n                cb(null, copy);\n            }\n            self._candidateBuffer = [];\n            self.pc.setLocalDescription(answer,\n                function () {\n                    if (self.config.useJingle) {\n                        var jingle = SJJ.toSessionJSON(answer.sdp, {\n                            role: self._role(),\n                            direction: 'outgoing'\n                        });\n                        jingle.sid = self.config.sid;\n                        self.localDescription = jingle;\n                        expandedAnswer.jingle = jingle;\n                    }\n                    if (self.enableChromeNativeSimulcast) {\n                        // native simulcast part 2:\n                        // signal multiple tracks to the receiver\n                        // for anything in the SIM group\n                        if (!expandedAnswer.jingle) {\n                            expandedAnswer.jingle = SJJ.toSessionJSON(answer.sdp, {\n                                role: self._role(),\n                                direction: 'outgoing'\n                            });\n                        }\n                        expandedAnswer.jingle.contents[1].application.sources.forEach(function (source, idx) {\n                            // the floor idx/2 is a hack that relies on a particular order\n                            // of groups, alternating between sim and rtx\n                            source.parameters = source.parameters.map(function (parameter) {\n                                if (parameter.key === 'msid') {\n                                    parameter.value += '-' + Math.floor(idx / 2);\n                                }\n                                return parameter;\n                            });\n                        });\n                        expandedAnswer.sdp = SJJ.toSessionSDP(expandedAnswer.jingle, {\n                            sid: self.sdpSessionID,\n                            role: self._role(),\n                            direction: 'outgoing'\n                        });\n                    }\n                    expandedAnswer.sdp.split('\\r\\n').forEach(function (line) {\n                        if (line.indexOf('a=candidate:') === 0) {\n                            self._checkLocalCandidate(line);\n                        }\n                    });\n                    if (!self.assumeSetLocalSuccess) {\n                        var copy = cloneDeep(expandedAnswer);\n                        self.emit('answer', copy);\n                        cb(null, copy);\n                    }\n                },\n                function (err) {\n                    self.emit('error', err);\n                    cb(err);\n                }\n            );\n        },\n        function (err) {\n            self.emit('error', err);\n            cb(err);\n        },\n        constraints\n    );\n};\n\n// Internal method for emitting ice candidates on our peer object\nPeerConnection.prototype._onIce = function (event) {\n    var self = this;\n    if (event.candidate) {\n        if (this.dontSignalCandidates) return;\n        var ice = event.candidate;\n\n        var expandedCandidate = {\n            candidate: {\n                candidate: ice.candidate,\n                sdpMid: ice.sdpMid,\n                sdpMLineIndex: ice.sdpMLineIndex\n            }\n        };\n        this._checkLocalCandidate(ice.candidate);\n\n        var cand = SJJ.toCandidateJSON(ice.candidate);\n\n        var already;\n        var idx;\n        if (this.eliminateDuplicateCandidates && cand.type === 'relay') {\n            // drop candidates with same foundation, component\n            // take local type pref into account so we don't ignore udp\n            // ones when we know about a TCP one. unlikely but...\n            already = this._candidateBuffer.filter(\n                function (c) {\n                    return c.type === 'relay';\n                }).map(function (c) {\n                    return c.foundation + ':' + c.component;\n                }\n            );\n            idx = already.indexOf(cand.foundation + ':' + cand.component);\n            // remember: local type pref of udp is 0, tcp 1, tls 2\n            if (idx > -1 && ((cand.priority >> 24) >= (already[idx].priority >> 24))) {\n                // drop it, same foundation with higher (worse) type pref\n                return;\n            }\n        }\n        if (this.config.bundlePolicy === 'max-bundle') {\n            // drop candidates which are duplicate for audio/video/data\n            // duplicate means same host/port but different sdpMid\n            already = this._candidateBuffer.filter(\n                function (c) {\n                    return cand.type === c.type;\n                }).map(function (cand) {\n                    return cand.address + ':' + cand.port;\n                }\n            );\n            idx = already.indexOf(cand.address + ':' + cand.port);\n            if (idx > -1) return;\n        }\n        // also drop rtcp candidates since we know the peer supports RTCP-MUX\n        // this is a workaround until browsers implement this natively\n        if (this.config.rtcpMuxPolicy === 'require' && cand.component === '2') {\n            return;\n        }\n        this._candidateBuffer.push(cand);\n\n        if (self.config.useJingle) {\n            if (!ice.sdpMid) { // firefox doesn't set this\n                if (self.pc.remoteDescription && self.pc.remoteDescription.type === 'offer') {\n                    // preserve name from remote\n                    ice.sdpMid = self.remoteDescription.contents[ice.sdpMLineIndex].name;\n                } else {\n                    ice.sdpMid = self.localDescription.contents[ice.sdpMLineIndex].name;\n                }\n            }\n            if (!self.iceCredentials.local[ice.sdpMid]) {\n                var jingle = SJJ.toSessionJSON(self.pc.localDescription.sdp, {\n                    role: self._role(),\n                    direction: 'outgoing'\n                });\n                jingle.contents.forEach(function (content) {\n                    var transport = content.transport || {};\n                    if (transport.ufrag) {\n                        self.iceCredentials.local[content.name] = {\n                            ufrag: transport.ufrag,\n                            pwd: transport.pwd\n                        };\n                    }\n                });\n            }\n            expandedCandidate.jingle = {\n                contents: [{\n                    name: ice.sdpMid,\n                    creator: self._role(),\n                    transport: {\n                        transportType: 'iceUdp',\n                        ufrag: self.iceCredentials.local[ice.sdpMid].ufrag,\n                        pwd: self.iceCredentials.local[ice.sdpMid].pwd,\n                        candidates: [\n                            cand\n                        ]\n                    }\n                }]\n            };\n            if (self.batchIceCandidates > 0) {\n                if (self.batchedIceCandidates.length === 0) {\n                    window.setTimeout(function () {\n                        var contents = {};\n                        self.batchedIceCandidates.forEach(function (content) {\n                            content = content.contents[0];\n                            if (!contents[content.name]) contents[content.name] = content;\n                            contents[content.name].transport.candidates.push(content.transport.candidates[0]);\n                        });\n                        var newCand = {\n                            jingle: {\n                                contents: []\n                            }\n                        };\n                        Object.keys(contents).forEach(function (name) {\n                            newCand.jingle.contents.push(contents[name]);\n                        });\n                        self.batchedIceCandidates = [];\n                        self.emit('ice', newCand);\n                    }, self.batchIceCandidates);\n                }\n                self.batchedIceCandidates.push(expandedCandidate.jingle);\n                return;\n            }\n\n        }\n        this.emit('ice', expandedCandidate);\n    } else {\n        this.emit('endOfCandidates');\n    }\n};\n\n// Internal method for processing a new data channel being added by the\n// other peer.\nPeerConnection.prototype._onDataChannel = function (event) {\n    // make sure we keep a reference so this doesn't get garbage collected\n    var channel = event.channel;\n    this._remoteDataChannels.push(channel);\n\n    this.emit('addChannel', channel);\n};\n\n// Create a data channel spec reference:\n// http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCDataChannelInit\nPeerConnection.prototype.createDataChannel = function (name, opts) {\n    var channel = this.pc.createDataChannel(name, opts);\n\n    // make sure we keep a reference so this doesn't get garbage collected\n    this._localDataChannels.push(channel);\n\n    return channel;\n};\n\nPeerConnection.prototype.getStats = function (cb) {\n    this.pc.getStats(null,\n        function (res) {\n            cb(null, res);\n        },\n        function (err) {\n            cb(err);\n        }\n    );\n};\n\nmodule.exports = PeerConnection;\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNDcxLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9ydGNwZWVyY29ubmVjdGlvbi9ydGNwZWVyY29ubmVjdGlvbi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgdXRpbCA9IHJlcXVpcmUoJ3V0aWwnKTtcbnZhciBTSkogPSByZXF1aXJlKCdzZHAtamluZ2xlLWpzb24nKTtcbnZhciBXaWxkRW1pdHRlciA9IHJlcXVpcmUoJ3dpbGRlbWl0dGVyJyk7XG52YXIgUGVlcmNvbm4gPSByZXF1aXJlKCd0cmFjZWFibGVwZWVyY29ubmVjdGlvbicpO1xudmFyIGFkYXB0ZXIgPSByZXF1aXJlKCd3ZWJydGMtYWRhcHRlcicpO1xudmFyIGNsb25lRGVlcCA9IHJlcXVpcmUoJ2xvZGFzaC5jbG9uZWRlZXAnKTtcblxuZnVuY3Rpb24gUGVlckNvbm5lY3Rpb24oY29uZmlnLCBjb25zdHJhaW50cykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgaXRlbTtcbiAgICBXaWxkRW1pdHRlci5jYWxsKHRoaXMpO1xuXG4gICAgY29uZmlnID0gY29uZmlnIHx8IHt9O1xuICAgIGNvbmZpZy5pY2VTZXJ2ZXJzID0gY29uZmlnLmljZVNlcnZlcnMgfHwgW107XG5cbiAgICB2YXIgZGV0ZWN0ZWRCcm93c2VyID0gYWRhcHRlci5icm93c2VyRGV0YWlscy5icm93c2VyO1xuXG4gICAgLy8gbWFrZSBzdXJlIHRoaXMgb25seSBnZXRzIGVuYWJsZWQgaW4gR29vZ2xlIENocm9tZVxuICAgIC8vIEVYUEVSSU1FTlRBTCBGTEFHLCBtaWdodCBnZXQgcmVtb3ZlZCB3aXRob3V0IG5vdGljZVxuICAgIHRoaXMuZW5hYmxlQ2hyb21lTmF0aXZlU2ltdWxjYXN0ID0gZmFsc2U7XG4gICAgaWYgKGNvbnN0cmFpbnRzICYmIGNvbnN0cmFpbnRzLm9wdGlvbmFsICYmXG4gICAgICAgICAgICBkZXRlY3RlZEJyb3dzZXIgPT09ICdjaHJvbWUnICYmXG4gICAgICAgICAgICBuYXZpZ2F0b3IuYXBwVmVyc2lvbi5tYXRjaCgvQ2hyb21pdW1cXC8vKSA9PT0gbnVsbCkge1xuICAgICAgICBjb25zdHJhaW50cy5vcHRpb25hbC5mb3JFYWNoKGZ1bmN0aW9uIChjb25zdHJhaW50KSB7XG4gICAgICAgICAgICBpZiAoY29uc3RyYWludC5lbmFibGVDaHJvbWVOYXRpdmVTaW11bGNhc3QpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmVuYWJsZUNocm9tZU5hdGl2ZVNpbXVsY2FzdCA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEVYUEVSSU1FTlRBTCBGTEFHLCBtaWdodCBnZXQgcmVtb3ZlZCB3aXRob3V0IG5vdGljZVxuICAgIHRoaXMuZW5hYmxlTXVsdGlTdHJlYW1IYWNrcyA9IGZhbHNlO1xuICAgIGlmIChjb25zdHJhaW50cyAmJiBjb25zdHJhaW50cy5vcHRpb25hbCAmJlxuICAgICAgICAgICAgZGV0ZWN0ZWRCcm93c2VyID09PSAnY2hyb21lJykge1xuICAgICAgICBjb25zdHJhaW50cy5vcHRpb25hbC5mb3JFYWNoKGZ1bmN0aW9uIChjb25zdHJhaW50KSB7XG4gICAgICAgICAgICBpZiAoY29uc3RyYWludC5lbmFibGVNdWx0aVN0cmVhbUhhY2tzKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5lbmFibGVNdWx0aVN0cmVhbUhhY2tzID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8vIEVYUEVSSU1FTlRBTCBGTEFHLCBtaWdodCBnZXQgcmVtb3ZlZCB3aXRob3V0IG5vdGljZVxuICAgIHRoaXMucmVzdHJpY3RCYW5kd2lkdGggPSAwO1xuICAgIGlmIChjb25zdHJhaW50cyAmJiBjb25zdHJhaW50cy5vcHRpb25hbCkge1xuICAgICAgICBjb25zdHJhaW50cy5vcHRpb25hbC5mb3JFYWNoKGZ1bmN0aW9uIChjb25zdHJhaW50KSB7XG4gICAgICAgICAgICBpZiAoY29uc3RyYWludC5hbmR5ZXRSZXN0cmljdEJhbmR3aWR0aCkge1xuICAgICAgICAgICAgICAgIHNlbGYucmVzdHJpY3RCYW5kd2lkdGggPSBjb25zdHJhaW50LmFuZHlldFJlc3RyaWN0QmFuZHdpZHRoO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBFWFBFUklNRU5UQUwgRkxBRywgbWlnaHQgZ2V0IHJlbW92ZWQgd2l0aG91dCBub3RpY2VcbiAgICAvLyBidW5kbGUgdXAgaWNlIGNhbmRpZGF0ZXMsIG9ubHkgd29ya3MgZm9yIGppbmdsZSBtb2RlXG4gICAgLy8gbnVtYmVyID4gMCBpcyB0aGUgZGVsYXkgdG8gd2FpdCBmb3IgYWRkaXRpb25hbCBjYW5kaWRhdGVzXG4gICAgLy8gfjIwbXMgc2VlbXMgZ29vZFxuICAgIHRoaXMuYmF0Y2hJY2VDYW5kaWRhdGVzID0gMDtcbiAgICBpZiAoY29uc3RyYWludHMgJiYgY29uc3RyYWludHMub3B0aW9uYWwpIHtcbiAgICAgICAgY29uc3RyYWludHMub3B0aW9uYWwuZm9yRWFjaChmdW5jdGlvbiAoY29uc3RyYWludCkge1xuICAgICAgICAgICAgaWYgKGNvbnN0cmFpbnQuYW5keWV0QmF0Y2hJY2UpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmJhdGNoSWNlQ2FuZGlkYXRlcyA9IGNvbnN0cmFpbnQuYW5keWV0QmF0Y2hJY2U7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLmJhdGNoZWRJY2VDYW5kaWRhdGVzID0gW107XG5cbiAgICAvLyBFWFBFUklNRU5UQUwgRkxBRywgbWlnaHQgZ2V0IHJlbW92ZWQgd2l0aG91dCBub3RpY2VcbiAgICAvLyB0aGlzIGF0dGVtcHMgdG8gc3RyaXAgb3V0IGNhbmRpZGF0ZXMgd2l0aCBhbiBhbHJlYWR5IGtub3duIGZvdW5kYXRpb25cbiAgICAvLyBhbmQgdHlwZSAtLSBpLmUuIHRob3NlIHdoaWNoIGFyZSBnYXRoZXJlZCB2aWEgdGhlIHNhbWUgVFVSTiBzZXJ2ZXJcbiAgICAvLyBidXQgZGlmZmVyZW50IHRyYW5zcG9ydHMgKFRVUk4gdWRwLCB0Y3AgYW5kIHRscyByZXNwZWN0aXZlbHkpXG4gICAgaWYgKGNvbnN0cmFpbnRzICYmIGNvbnN0cmFpbnRzLm9wdGlvbmFsICYmIGRldGVjdGVkQnJvd3NlciA9PT0gJ2Nocm9tZScpIHtcbiAgICAgICAgY29uc3RyYWludHMub3B0aW9uYWwuZm9yRWFjaChmdW5jdGlvbiAoY29uc3RyYWludCkge1xuICAgICAgICAgICAgaWYgKGNvbnN0cmFpbnQuYW5keWV0RmFzdGVySUNFKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5lbGltaW5hdGVEdXBsaWNhdGVDYW5kaWRhdGVzID0gY29uc3RyYWludC5hbmR5ZXRGYXN0ZXJJQ0U7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvLyBFWFBFUklNRU5UQUwgRkxBRywgbWlnaHQgZ2V0IHJlbW92ZWQgd2l0aG91dCBub3RpY2VcbiAgICAvLyB3aGVuIHVzaW5nIGEgc2VydmVyIHN1Y2ggYXMgdGhlIGppdHNpIHZpZGVvYnJpZGdlIHdlIGRvbid0IG5lZWQgdG8gc2lnbmFsXG4gICAgLy8gb3VyIGNhbmRpZGF0ZXNcbiAgICBpZiAoY29uc3RyYWludHMgJiYgY29uc3RyYWludHMub3B0aW9uYWwpIHtcbiAgICAgICAgY29uc3RyYWludHMub3B0aW9uYWwuZm9yRWFjaChmdW5jdGlvbiAoY29uc3RyYWludCkge1xuICAgICAgICAgICAgaWYgKGNvbnN0cmFpbnQuYW5keWV0RG9udFNpZ25hbENhbmRpZGF0ZXMpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmRvbnRTaWduYWxDYW5kaWRhdGVzID0gY29uc3RyYWludC5hbmR5ZXREb250U2lnbmFsQ2FuZGlkYXRlcztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG5cbiAgICAvLyBFWFBFUklNRU5UQUwgRkxBRywgbWlnaHQgZ2V0IHJlbW92ZWQgd2l0aG91dCBub3RpY2VcbiAgICB0aGlzLmFzc3VtZVNldExvY2FsU3VjY2VzcyA9IGZhbHNlO1xuICAgIGlmIChjb25zdHJhaW50cyAmJiBjb25zdHJhaW50cy5vcHRpb25hbCkge1xuICAgICAgICBjb25zdHJhaW50cy5vcHRpb25hbC5mb3JFYWNoKGZ1bmN0aW9uIChjb25zdHJhaW50KSB7XG4gICAgICAgICAgICBpZiAoY29uc3RyYWludC5hbmR5ZXRBc3N1bWVTZXRMb2NhbFN1Y2Nlc3MpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmFzc3VtZVNldExvY2FsU3VjY2VzcyA9IGNvbnN0cmFpbnQuYW5keWV0QXNzdW1lU2V0TG9jYWxTdWNjZXNzO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBFWFBFUklNRU5UQUwgRkxBRywgbWlnaHQgZ2V0IHJlbW92ZWQgd2l0aG91dCBub3RpY2VcbiAgICAvLyB3b3JraW5nIGFyb3VuZCBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xMDg3NTUxXG4gICAgLy8gcGFzcyBpbiBhIHRpbWVvdXQgZm9yIHRoaXNcbiAgICBpZiAoZGV0ZWN0ZWRCcm93c2VyID09PSAnZmlyZWZveCcpIHtcbiAgICAgICAgaWYgKGNvbnN0cmFpbnRzICYmIGNvbnN0cmFpbnRzLm9wdGlvbmFsKSB7XG4gICAgICAgICAgICB0aGlzLnd0RmlyZWZveCA9IDA7XG4gICAgICAgICAgICBjb25zdHJhaW50cy5vcHRpb25hbC5mb3JFYWNoKGZ1bmN0aW9uIChjb25zdHJhaW50KSB7XG4gICAgICAgICAgICAgICAgaWYgKGNvbnN0cmFpbnQuYW5keWV0RmlyZWZveE1ha2VzTWVTYWQpIHtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi53dEZpcmVmb3ggPSBjb25zdHJhaW50LmFuZHlldEZpcmVmb3hNYWtlc01lU2FkO1xuICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi53dEZpcmVmb3ggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmZpcmVmb3hjYW5kaWRhdGVidWZmZXIgPSBbXTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxuXG5cbiAgICB0aGlzLnBjID0gbmV3IFBlZXJjb25uKGNvbmZpZywgY29uc3RyYWludHMpO1xuXG4gICAgdGhpcy5nZXRMb2NhbFN0cmVhbXMgPSB0aGlzLnBjLmdldExvY2FsU3RyZWFtcy5iaW5kKHRoaXMucGMpO1xuICAgIHRoaXMuZ2V0UmVtb3RlU3RyZWFtcyA9IHRoaXMucGMuZ2V0UmVtb3RlU3RyZWFtcy5iaW5kKHRoaXMucGMpO1xuICAgIHRoaXMuYWRkU3RyZWFtID0gdGhpcy5wYy5hZGRTdHJlYW0uYmluZCh0aGlzLnBjKTtcbiAgICB0aGlzLnJlbW92ZVN0cmVhbSA9IHRoaXMucGMucmVtb3ZlU3RyZWFtLmJpbmQodGhpcy5wYyk7XG5cbiAgICAvLyBwcm94eSBldmVudHNcbiAgICB0aGlzLnBjLm9uKCcqJywgZnVuY3Rpb24gKCkge1xuICAgICAgICBzZWxmLmVtaXQuYXBwbHkoc2VsZiwgYXJndW1lbnRzKTtcbiAgICB9KTtcblxuICAgIC8vIHByb3h5IHNvbWUgZXZlbnRzIGRpcmVjdGx5XG4gICAgdGhpcy5wYy5vbnJlbW92ZXN0cmVhbSA9IHRoaXMuZW1pdC5iaW5kKHRoaXMsICdyZW1vdmVTdHJlYW0nKTtcbiAgICB0aGlzLnBjLm9uYWRkc3RyZWFtID0gdGhpcy5lbWl0LmJpbmQodGhpcywgJ2FkZFN0cmVhbScpO1xuICAgIHRoaXMucGMub25uZWdvdGlhdGlvbm5lZWRlZCA9IHRoaXMuZW1pdC5iaW5kKHRoaXMsICduZWdvdGlhdGlvbk5lZWRlZCcpO1xuICAgIHRoaXMucGMub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgPSB0aGlzLmVtaXQuYmluZCh0aGlzLCAnaWNlQ29ubmVjdGlvblN0YXRlQ2hhbmdlJyk7XG4gICAgdGhpcy5wYy5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlID0gdGhpcy5lbWl0LmJpbmQodGhpcywgJ3NpZ25hbGluZ1N0YXRlQ2hhbmdlJyk7XG5cbiAgICAvLyBoYW5kbGUgaWNlIGNhbmRpZGF0ZSBhbmQgZGF0YSBjaGFubmVsIGV2ZW50c1xuICAgIHRoaXMucGMub25pY2VjYW5kaWRhdGUgPSB0aGlzLl9vbkljZS5iaW5kKHRoaXMpO1xuICAgIHRoaXMucGMub25kYXRhY2hhbm5lbCA9IHRoaXMuX29uRGF0YUNoYW5uZWwuYmluZCh0aGlzKTtcblxuICAgIHRoaXMubG9jYWxEZXNjcmlwdGlvbiA9IHtcbiAgICAgICAgY29udGVudHM6IFtdXG4gICAgfTtcbiAgICB0aGlzLnJlbW90ZURlc2NyaXB0aW9uID0ge1xuICAgICAgICBjb250ZW50czogW11cbiAgICB9O1xuXG4gICAgdGhpcy5jb25maWcgPSB7XG4gICAgICAgIGRlYnVnOiBmYWxzZSxcbiAgICAgICAgc2lkOiAnJyxcbiAgICAgICAgaXNJbml0aWF0b3I6IHRydWUsXG4gICAgICAgIHNkcFNlc3Npb25JRDogRGF0ZS5ub3coKSxcbiAgICAgICAgdXNlSmluZ2xlOiBmYWxzZVxuICAgIH07XG5cbiAgICB0aGlzLmljZUNyZWRlbnRpYWxzID0ge1xuICAgICAgICBsb2NhbDoge30sXG4gICAgICAgIHJlbW90ZToge31cbiAgICB9O1xuXG4gICAgLy8gYXBwbHkgb3VyIGNvbmZpZ1xuICAgIGZvciAoaXRlbSBpbiBjb25maWcpIHtcbiAgICAgICAgdGhpcy5jb25maWdbaXRlbV0gPSBjb25maWdbaXRlbV07XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuY29uZmlnLmRlYnVnKSB7XG4gICAgICAgIHRoaXMub24oJyonLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgbG9nZ2VyID0gY29uZmlnLmxvZ2dlciB8fCBjb25zb2xlO1xuICAgICAgICAgICAgbG9nZ2VyLmxvZygnUGVlckNvbm5lY3Rpb24gZXZlbnQ6JywgYXJndW1lbnRzKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHRoaXMuaGFkTG9jYWxTdHVuQ2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5oYWRSZW1vdGVTdHVuQ2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5oYWRMb2NhbFJlbGF5Q2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5oYWRSZW1vdGVSZWxheUNhbmRpZGF0ZSA9IGZhbHNlO1xuXG4gICAgdGhpcy5oYWRMb2NhbElQdjZDYW5kaWRhdGUgPSBmYWxzZTtcbiAgICB0aGlzLmhhZFJlbW90ZUlQdjZDYW5kaWRhdGUgPSBmYWxzZTtcblxuICAgIC8vIGtlZXBpbmcgcmVmZXJlbmNlcyBmb3IgYWxsIG91ciBkYXRhIGNoYW5uZWxzXG4gICAgLy8gc28gdGhleSBkb250IGdldCBnYXJiYWdlIGNvbGxlY3RlZFxuICAgIC8vIGNhbiBiZSByZW1vdmVkIG9uY2UgdGhlIGZvbGxvd2luZyBidWdzIGhhdmUgYmVlbiBmaXhlZFxuICAgIC8vIGh0dHBzOi8vY3JidWcuY29tLzQwNTU0NVxuICAgIC8vIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTk2NDA5MlxuICAgIC8vIHRvIGJlIGZpbGVkIGZvciBvcGVyYVxuICAgIHRoaXMuX3JlbW90ZURhdGFDaGFubmVscyA9IFtdO1xuICAgIHRoaXMuX2xvY2FsRGF0YUNoYW5uZWxzID0gW107XG5cbiAgICB0aGlzLl9jYW5kaWRhdGVCdWZmZXIgPSBbXTtcbn1cblxudXRpbC5pbmhlcml0cyhQZWVyQ29ubmVjdGlvbiwgV2lsZEVtaXR0ZXIpO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLCAnc2lnbmFsaW5nU3RhdGUnLCB7XG4gICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBjLnNpZ25hbGluZ1N0YXRlO1xuICAgIH1cbn0pO1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KFBlZXJDb25uZWN0aW9uLnByb3RvdHlwZSwgJ2ljZUNvbm5lY3Rpb25TdGF0ZScsIHtcbiAgICBnZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGMuaWNlQ29ubmVjdGlvblN0YXRlO1xuICAgIH1cbn0pO1xuXG5QZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX3JvbGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNJbml0aWF0b3IgPyAnaW5pdGlhdG9yJyA6ICdyZXNwb25kZXInO1xufTtcblxuLy8gQWRkIGEgc3RyZWFtIHRvIHRoZSBwZWVyIGNvbm5lY3Rpb24gb2JqZWN0XG5QZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgIHRoaXMubG9jYWxTdHJlYW0gPSBzdHJlYW07XG4gICAgdGhpcy5wYy5hZGRTdHJlYW0oc3RyZWFtKTtcbn07XG5cbi8vIGhlbHBlciBmdW5jdGlvbiB0byBjaGVjayBpZiBhIHJlbW90ZSBjYW5kaWRhdGUgaXMgYSBzdHVuL3JlbGF5XG4vLyBjYW5kaWRhdGUgb3IgYW4gaXB2NiBjYW5kaWRhdGVcblBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fY2hlY2tMb2NhbENhbmRpZGF0ZSA9IGZ1bmN0aW9uIChjYW5kaWRhdGUpIHtcbiAgICB2YXIgY2FuZCA9IFNKSi50b0NhbmRpZGF0ZUpTT04oY2FuZGlkYXRlKTtcbiAgICBpZiAoY2FuZC50eXBlID09ICdzcmZseCcpIHtcbiAgICAgICAgdGhpcy5oYWRMb2NhbFN0dW5DYW5kaWRhdGUgPSB0cnVlO1xuICAgIH0gZWxzZSBpZiAoY2FuZC50eXBlID09ICdyZWxheScpIHtcbiAgICAgICAgdGhpcy5oYWRMb2NhbFJlbGF5Q2FuZGlkYXRlID0gdHJ1ZTtcbiAgICB9XG4gICAgaWYgKGNhbmQuaXAuaW5kZXhPZignOicpICE9IC0xKSB7XG4gICAgICAgIHRoaXMuaGFkTG9jYWxJUHY2Q2FuZGlkYXRlID0gdHJ1ZTtcbiAgICB9XG59O1xuXG4vLyBoZWxwZXIgZnVuY3Rpb24gdG8gY2hlY2sgaWYgYSByZW1vdGUgY2FuZGlkYXRlIGlzIGEgc3R1bi9yZWxheVxuLy8gY2FuZGlkYXRlIG9yIGFuIGlwdjYgY2FuZGlkYXRlXG5QZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2NoZWNrUmVtb3RlQ2FuZGlkYXRlID0gZnVuY3Rpb24gKGNhbmRpZGF0ZSkge1xuICAgIHZhciBjYW5kID0gU0pKLnRvQ2FuZGlkYXRlSlNPTihjYW5kaWRhdGUpO1xuICAgIGlmIChjYW5kLnR5cGUgPT0gJ3NyZmx4Jykge1xuICAgICAgICB0aGlzLmhhZFJlbW90ZVN0dW5DYW5kaWRhdGUgPSB0cnVlO1xuICAgIH0gZWxzZSBpZiAoY2FuZC50eXBlID09ICdyZWxheScpIHtcbiAgICAgICAgdGhpcy5oYWRSZW1vdGVSZWxheUNhbmRpZGF0ZSA9IHRydWU7XG4gICAgfVxuICAgIGlmIChjYW5kLmlwLmluZGV4T2YoJzonKSAhPSAtMSkge1xuICAgICAgICB0aGlzLmhhZFJlbW90ZUlQdjZDYW5kaWRhdGUgPSB0cnVlO1xuICAgIH1cbn07XG5cblxuLy8gSW5pdCBhbmQgYWRkIGljZSBjYW5kaWRhdGUgb2JqZWN0IHdpdGggY29ycmVjdCBjb25zdHJ1Y3RvclxuUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnByb2Nlc3NJY2UgPSBmdW5jdGlvbiAodXBkYXRlLCBjYikge1xuICAgIGNiID0gY2IgfHwgZnVuY3Rpb24gKCkge307XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgLy8gaWdub3JlIGFueSBhZGRlZCBpY2UgY2FuZGlkYXRlcyB0byBhdm9pZCBlcnJvcnMuIHdoeSBkb2VzIHRoZVxuICAgIC8vIHNwZWMgbm90IGRvIHRoaXM/XG4gICAgaWYgKHRoaXMucGMuc2lnbmFsaW5nU3RhdGUgPT09ICdjbG9zZWQnKSByZXR1cm4gY2IoKTtcblxuICAgIGlmICh1cGRhdGUuY29udGVudHMgfHwgKHVwZGF0ZS5qaW5nbGUgJiYgdXBkYXRlLmppbmdsZS5jb250ZW50cykpIHtcbiAgICAgICAgdmFyIGNvbnRlbnROYW1lcyA9IHRoaXMucmVtb3RlRGVzY3JpcHRpb24uY29udGVudHMubWFwKGZ1bmN0aW9uIChjKSB7IHJldHVybiBjLm5hbWU7IH0pO1xuICAgICAgICB2YXIgY29udGVudHMgPSB1cGRhdGUuY29udGVudHMgfHwgdXBkYXRlLmppbmdsZS5jb250ZW50cztcblxuICAgICAgICBjb250ZW50cy5mb3JFYWNoKGZ1bmN0aW9uIChjb250ZW50KSB7XG4gICAgICAgICAgICB2YXIgdHJhbnNwb3J0ID0gY29udGVudC50cmFuc3BvcnQgfHwge307XG4gICAgICAgICAgICB2YXIgY2FuZGlkYXRlcyA9IHRyYW5zcG9ydC5jYW5kaWRhdGVzIHx8IFtdO1xuICAgICAgICAgICAgdmFyIG1saW5lID0gY29udGVudE5hbWVzLmluZGV4T2YoY29udGVudC5uYW1lKTtcbiAgICAgICAgICAgIHZhciBtaWQgPSBjb250ZW50Lm5hbWU7XG4gICAgICAgICAgICB2YXIgcmVtb3RlQ29udGVudCA9IHNlbGYucmVtb3RlRGVzY3JpcHRpb24uY29udGVudHMuZmluZChmdW5jdGlvbiAoYykge1xuICAgICAgICAgICAgICAgIHJldHVybiBjLm5hbWUgPT09IGNvbnRlbnQubmFtZTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBwcm9jZXNzIGNhbmRpZGF0ZXMgYXMgYSBjYWxsYmFjaywgaW4gY2FzZSB3ZSBuZWVkIHRvXG4gICAgICAgICAgICAvLyB1cGRhdGUgdWZyYWcgYW5kIHB3ZCB3aXRoIG9mZmVyL2Fuc3dlclxuICAgICAgICAgICAgdmFyIHByb2Nlc3NDYW5kaWRhdGVzID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGNhbmRpZGF0ZXMuZm9yRWFjaChcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGNhbmRpZGF0ZSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgaWNlQ2FuZGlkYXRlID0gU0pKLnRvQ2FuZGlkYXRlU0RQKGNhbmRpZGF0ZSkgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5wYy5hZGRJY2VDYW5kaWRhdGUoXG4gICAgICAgICAgICAgICAgICAgICAgICBuZXcgUlRDSWNlQ2FuZGlkYXRlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGU6IGljZUNhbmRpZGF0ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHBNTGluZUluZGV4OiBtbGluZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHBNaWQ6IG1pZFxuICAgICAgICAgICAgICAgICAgICAgICAgfSksIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB3ZWxsLCB0aGlzIHN1Y2Nlc3MgY2FsbGJhY2sgaXMgcHJldHR5IG1lYW5pbmdsZXNzXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuZW1pdCgnZXJyb3InLCBlcnIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICBzZWxmLl9jaGVja1JlbW90ZUNhbmRpZGF0ZShpY2VDYW5kaWRhdGUpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGNiKCk7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBpZiAoc2VsZi5pY2VDcmVkZW50aWFscy5yZW1vdGVbY29udGVudC5uYW1lXSAmJiB0cmFuc3BvcnQudWZyYWcgJiZcbiAgICAgICAgICAgICAgICBzZWxmLmljZUNyZWRlbnRpYWxzLnJlbW90ZVtjb250ZW50Lm5hbWVdLnVmcmFnICE9PSB0cmFuc3BvcnQudWZyYWcpIHtcbiAgICAgICAgICAgICAgICBpZiAocmVtb3RlQ29udGVudCkge1xuICAgICAgICAgICAgICAgICAgICByZW1vdGVDb250ZW50LnRyYW5zcG9ydC51ZnJhZyA9IHRyYW5zcG9ydC51ZnJhZztcbiAgICAgICAgICAgICAgICAgICAgcmVtb3RlQ29udGVudC50cmFuc3BvcnQucHdkID0gdHJhbnNwb3J0LnB3ZDtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG9mZmVyID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29mZmVyJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGppbmdsZTogc2VsZi5yZW1vdGVEZXNjcmlwdGlvblxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICBvZmZlci5zZHAgPSBTSkoudG9TZXNzaW9uU0RQKG9mZmVyLmppbmdsZSwge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2lkOiBzZWxmLmNvbmZpZy5zZHBTZXNzaW9uSUQsXG4gICAgICAgICAgICAgICAgICAgICAgICByb2xlOiBzZWxmLl9yb2xlKCksXG4gICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb246ICdpbmNvbWluZydcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYucGMuc2V0UmVtb3RlRGVzY3JpcHRpb24obmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbihvZmZlciksXG4gICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvY2Vzc0NhbmRpZGF0ZXMoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5lbWl0KCdlcnJvcicsICdpY2UgcmVzdGFydCBmYWlsZWQgdG8gZmluZCBtYXRjaGluZyBjb250ZW50Jyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBwcm9jZXNzQ2FuZGlkYXRlcygpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgICAvLyB3b3JraW5nIGFyb3VuZCBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3dlYnJ0Yy9pc3N1ZXMvZGV0YWlsP2lkPTM2NjlcbiAgICAgICAgaWYgKHVwZGF0ZS5jYW5kaWRhdGUgJiYgdXBkYXRlLmNhbmRpZGF0ZS5jYW5kaWRhdGUuaW5kZXhPZignYT0nKSAhPT0gMCkge1xuICAgICAgICAgICAgdXBkYXRlLmNhbmRpZGF0ZS5jYW5kaWRhdGUgPSAnYT0nICsgdXBkYXRlLmNhbmRpZGF0ZS5jYW5kaWRhdGU7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy53dEZpcmVmb3ggJiYgdGhpcy5maXJlZm94Y2FuZGlkYXRlYnVmZmVyICE9PSBudWxsKSB7XG4gICAgICAgICAgICAvLyB3ZSBjYW50IGFkZCB0aGlzIHlldCBkdWUgdG8gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTA4NzU1MVxuICAgICAgICAgICAgaWYgKHRoaXMucGMubG9jYWxEZXNjcmlwdGlvbiAmJiB0aGlzLnBjLmxvY2FsRGVzY3JpcHRpb24udHlwZSA9PT0gJ29mZmVyJykge1xuICAgICAgICAgICAgICAgIHRoaXMuZmlyZWZveGNhbmRpZGF0ZWJ1ZmZlci5wdXNoKHVwZGF0ZS5jYW5kaWRhdGUpO1xuICAgICAgICAgICAgICAgIHJldHVybiBjYigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgc2VsZi5wYy5hZGRJY2VDYW5kaWRhdGUoXG4gICAgICAgICAgICBuZXcgUlRDSWNlQ2FuZGlkYXRlKHVwZGF0ZS5jYW5kaWRhdGUpLFxuICAgICAgICAgICAgZnVuY3Rpb24gKCkgeyB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgICAgIHNlbGYuZW1pdCgnZXJyb3InLCBlcnIpO1xuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgICAgICBzZWxmLl9jaGVja1JlbW90ZUNhbmRpZGF0ZSh1cGRhdGUuY2FuZGlkYXRlLmNhbmRpZGF0ZSk7XG4gICAgICAgIGNiKCk7XG4gICAgfVxufTtcblxuLy8gR2VuZXJhdGUgYW5kIGVtaXQgYW4gb2ZmZXIgd2l0aCB0aGUgZ2l2ZW4gY29uc3RyYWludHNcblBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5vZmZlciA9IGZ1bmN0aW9uIChjb25zdHJhaW50cywgY2IpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIGhhc0NvbnN0cmFpbnRzID0gYXJndW1lbnRzLmxlbmd0aCA9PT0gMjtcbiAgICB2YXIgbWVkaWFDb25zdHJhaW50cyA9IGhhc0NvbnN0cmFpbnRzICYmIGNvbnN0cmFpbnRzID8gY29uc3RyYWludHMgOiB7XG4gICAgICAgICAgICBvZmZlclRvUmVjZWl2ZUF1ZGlvOiAxLFxuICAgICAgICAgICAgb2ZmZXJUb1JlY2VpdmVWaWRlbzogMVxuICAgICAgICB9O1xuICAgIGNiID0gaGFzQ29uc3RyYWludHMgPyBjYiA6IGNvbnN0cmFpbnRzO1xuICAgIGNiID0gY2IgfHwgZnVuY3Rpb24gKCkge307XG5cbiAgICBpZiAodGhpcy5wYy5zaWduYWxpbmdTdGF0ZSA9PT0gJ2Nsb3NlZCcpIHJldHVybiBjYignQWxyZWFkeSBjbG9zZWQnKTtcblxuICAgIC8vIEFjdHVhbGx5IGdlbmVyYXRlIHRoZSBvZmZlclxuICAgIHRoaXMucGMuY3JlYXRlT2ZmZXIoXG4gICAgICAgIGZ1bmN0aW9uIChvZmZlcikge1xuICAgICAgICAgICAgLy8gZG9lcyBub3Qgd29yayBmb3IgamluZ2xlLCBidXQgamluZ2xlLmpzIGRvZXNuJ3QgbmVlZFxuICAgICAgICAgICAgLy8gdGhpcyBoYWNrLi4uXG4gICAgICAgICAgICB2YXIgZXhwYW5kZWRPZmZlciA9IHtcbiAgICAgICAgICAgICAgICB0eXBlOiAnb2ZmZXInLFxuICAgICAgICAgICAgICAgIHNkcDogb2ZmZXIuc2RwXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgaWYgKHNlbGYuYXNzdW1lU2V0TG9jYWxTdWNjZXNzKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5lbWl0KCdvZmZlcicsIGV4cGFuZGVkT2ZmZXIpO1xuICAgICAgICAgICAgICAgIGNiKG51bGwsIGV4cGFuZGVkT2ZmZXIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5fY2FuZGlkYXRlQnVmZmVyID0gW107XG4gICAgICAgICAgICBzZWxmLnBjLnNldExvY2FsRGVzY3JpcHRpb24ob2ZmZXIsXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgamluZ2xlO1xuICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5jb25maWcudXNlSmluZ2xlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBqaW5nbGUgPSBTSkoudG9TZXNzaW9uSlNPTihvZmZlci5zZHAsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb2xlOiBzZWxmLl9yb2xlKCksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uOiAnb3V0Z29pbmcnXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGppbmdsZS5zaWQgPSBzZWxmLmNvbmZpZy5zaWQ7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmxvY2FsRGVzY3JpcHRpb24gPSBqaW5nbGU7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNhdmUgSUNFIGNyZWRlbnRpYWxzXG4gICAgICAgICAgICAgICAgICAgICAgICBqaW5nbGUuY29udGVudHMuZm9yRWFjaChmdW5jdGlvbiAoY29udGVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0cmFuc3BvcnQgPSBjb250ZW50LnRyYW5zcG9ydCB8fCB7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodHJhbnNwb3J0LnVmcmFnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuaWNlQ3JlZGVudGlhbHMubG9jYWxbY29udGVudC5uYW1lXSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVmcmFnOiB0cmFuc3BvcnQudWZyYWcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwd2Q6IHRyYW5zcG9ydC5wd2RcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgZXhwYW5kZWRPZmZlci5qaW5nbGUgPSBqaW5nbGU7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZXhwYW5kZWRPZmZlci5zZHAuc3BsaXQoJ1xcclxcbicpLmZvckVhY2goZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChsaW5lLmluZGV4T2YoJ2E9Y2FuZGlkYXRlOicpID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5fY2hlY2tMb2NhbENhbmRpZGF0ZShsaW5lKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCFzZWxmLmFzc3VtZVNldExvY2FsU3VjY2Vzcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5lbWl0KCdvZmZlcicsIGV4cGFuZGVkT2ZmZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgY2IobnVsbCwgZXhwYW5kZWRPZmZlcik7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICAgICAgICAgICAgICAgIGNiKGVycik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgc2VsZi5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICAgICAgICBjYihlcnIpO1xuICAgICAgICB9LFxuICAgICAgICBtZWRpYUNvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cblxuLy8gUHJvY2VzcyBhbiBpbmNvbWluZyBvZmZlciBzbyB0aGF0IElDRSBtYXkgcHJvY2VlZCBiZWZvcmUgZGVjaWRpbmdcbi8vIHRvIGFuc3dlciB0aGUgcmVxdWVzdC5cblBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5oYW5kbGVPZmZlciA9IGZ1bmN0aW9uIChvZmZlciwgY2IpIHtcbiAgICBjYiA9IGNiIHx8IGZ1bmN0aW9uICgpIHt9O1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBvZmZlci50eXBlID0gJ29mZmVyJztcbiAgICBpZiAob2ZmZXIuamluZ2xlKSB7XG4gICAgICAgIGlmICh0aGlzLmVuYWJsZUNocm9tZU5hdGl2ZVNpbXVsY2FzdCkge1xuICAgICAgICAgICAgb2ZmZXIuamluZ2xlLmNvbnRlbnRzLmZvckVhY2goZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICAgICAgICAgICAgICBpZiAoY29udGVudC5uYW1lID09PSAndmlkZW8nKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnRlbnQuYXBwbGljYXRpb24uZ29vZ0NvbmZlcmVuY2VGbGFnID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLmVuYWJsZU11bHRpU3RyZWFtSGFja3MpIHtcbiAgICAgICAgICAgIC8vIGFkZCBhIG1peGVkIHZpZGVvIHN0cmVhbSBhcyBmaXJzdCBzdHJlYW1cbiAgICAgICAgICAgIG9mZmVyLmppbmdsZS5jb250ZW50cy5mb3JFYWNoKGZ1bmN0aW9uIChjb250ZW50KSB7XG4gICAgICAgICAgICAgICAgaWYgKGNvbnRlbnQubmFtZSA9PT0gJ3ZpZGVvJykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgc291cmNlcyA9IGNvbnRlbnQuYXBwbGljYXRpb24uc291cmNlcyB8fCBbXTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNvdXJjZXMubGVuZ3RoID09PSAwIHx8IHNvdXJjZXNbMF0uc3NyYyAhPT0gXCIzNzM1OTI4NTU5XCIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZXMudW5zaGlmdCh7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3NyYzogXCIzNzM1OTI4NTU5XCIsIC8vIDB4ZGVhZGJlZWZcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbWV0ZXJzOiBbXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleTogXCJjbmFtZVwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IFwiZGVhZGJlZWZcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXk6IFwibXNpZFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IFwibWl4eW91cmZlY2ludG90aGlzIHBsZWFzZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBdXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRlbnQuYXBwbGljYXRpb24uc291cmNlcyA9IHNvdXJjZXM7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoc2VsZi5yZXN0cmljdEJhbmR3aWR0aCA+IDApIHtcbiAgICAgICAgICAgIGlmIChvZmZlci5qaW5nbGUuY29udGVudHMubGVuZ3RoID49IDIgJiYgb2ZmZXIuamluZ2xlLmNvbnRlbnRzWzFdLm5hbWUgPT09ICd2aWRlbycpIHtcbiAgICAgICAgICAgICAgICB2YXIgY29udGVudCA9IG9mZmVyLmppbmdsZS5jb250ZW50c1sxXTtcbiAgICAgICAgICAgICAgICB2YXIgaGFzQncgPSBjb250ZW50LmFwcGxpY2F0aW9uICYmIGNvbnRlbnQuYXBwbGljYXRpb24uYmFuZHdpZHRoICYmIGNvbnRlbnQuYXBwbGljYXRpb24uYmFuZHdpZHRoLmJhbmR3aWR0aDtcbiAgICAgICAgICAgICAgICBpZiAoIWhhc0J3KSB7XG4gICAgICAgICAgICAgICAgICAgIG9mZmVyLmppbmdsZS5jb250ZW50c1sxXS5hcHBsaWNhdGlvbi5iYW5kd2lkdGggPSB7IHR5cGU6ICdBUycsIGJhbmR3aWR0aDogc2VsZi5yZXN0cmljdEJhbmR3aWR0aC50b1N0cmluZygpIH07XG4gICAgICAgICAgICAgICAgICAgIG9mZmVyLnNkcCA9IFNKSi50b1Nlc3Npb25TRFAob2ZmZXIuamluZ2xlLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzaWQ6IHNlbGYuY29uZmlnLnNkcFNlc3Npb25JRCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHJvbGU6IHNlbGYuX3JvbGUoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbjogJ291dGdvaW5nJ1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gU2F2ZSBJQ0UgY3JlZGVudGlhbHNcbiAgICAgICAgb2ZmZXIuamluZ2xlLmNvbnRlbnRzLmZvckVhY2goZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICAgICAgICAgIHZhciB0cmFuc3BvcnQgPSBjb250ZW50LnRyYW5zcG9ydCB8fCB7fTtcbiAgICAgICAgICAgIGlmICh0cmFuc3BvcnQudWZyYWcpIHtcbiAgICAgICAgICAgICAgICBzZWxmLmljZUNyZWRlbnRpYWxzLnJlbW90ZVtjb250ZW50Lm5hbWVdID0ge1xuICAgICAgICAgICAgICAgICAgICB1ZnJhZzogdHJhbnNwb3J0LnVmcmFnLFxuICAgICAgICAgICAgICAgICAgICBwd2Q6IHRyYW5zcG9ydC5wd2RcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgb2ZmZXIuc2RwID0gU0pKLnRvU2Vzc2lvblNEUChvZmZlci5qaW5nbGUsIHtcbiAgICAgICAgICAgIHNpZDogc2VsZi5jb25maWcuc2RwU2Vzc2lvbklELFxuICAgICAgICAgICAgcm9sZTogc2VsZi5fcm9sZSgpLFxuICAgICAgICAgICAgZGlyZWN0aW9uOiAnaW5jb21pbmcnXG4gICAgICAgIH0pO1xuICAgICAgICBzZWxmLnJlbW90ZURlc2NyaXB0aW9uID0gb2ZmZXIuamluZ2xlO1xuICAgIH1cbiAgICBvZmZlci5zZHAuc3BsaXQoJ1xcclxcbicpLmZvckVhY2goZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgaWYgKGxpbmUuaW5kZXhPZignYT1jYW5kaWRhdGU6JykgPT09IDApIHtcbiAgICAgICAgICAgIHNlbGYuX2NoZWNrUmVtb3RlQ2FuZGlkYXRlKGxpbmUpO1xuICAgICAgICB9XG4gICAgfSk7XG4gICAgc2VsZi5wYy5zZXRSZW1vdGVEZXNjcmlwdGlvbihuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKG9mZmVyKSxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgY2IoKTtcbiAgICAgICAgfSxcbiAgICAgICAgY2JcbiAgICApO1xufTtcblxuLy8gQW5zd2VyIGFuIG9mZmVyIHdpdGggYXVkaW8gb25seVxuUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFuc3dlckF1ZGlvT25seSA9IGZ1bmN0aW9uIChjYikge1xuICAgIHZhciBtZWRpYUNvbnN0cmFpbnRzID0ge1xuICAgICAgICAgICAgbWFuZGF0b3J5OiB7XG4gICAgICAgICAgICAgICAgT2ZmZXJUb1JlY2VpdmVBdWRpbzogdHJ1ZSxcbiAgICAgICAgICAgICAgICBPZmZlclRvUmVjZWl2ZVZpZGVvOiBmYWxzZVxuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgIHRoaXMuX2Fuc3dlcihtZWRpYUNvbnN0cmFpbnRzLCBjYik7XG59O1xuXG4vLyBBbnN3ZXIgYW4gb2ZmZXIgd2l0aG91dCBvZmZlcmluZyB0byByZWNpZXZlXG5QZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYW5zd2VyQnJvYWRjYXN0T25seSA9IGZ1bmN0aW9uIChjYikge1xuICAgIHZhciBtZWRpYUNvbnN0cmFpbnRzID0ge1xuICAgICAgICAgICAgbWFuZGF0b3J5OiB7XG4gICAgICAgICAgICAgICAgT2ZmZXJUb1JlY2VpdmVBdWRpbzogZmFsc2UsXG4gICAgICAgICAgICAgICAgT2ZmZXJUb1JlY2VpdmVWaWRlbzogZmFsc2VcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICB0aGlzLl9hbnN3ZXIobWVkaWFDb25zdHJhaW50cywgY2IpO1xufTtcblxuLy8gQW5zd2VyIGFuIG9mZmVyIHdpdGggZ2l2ZW4gY29uc3RyYWludHMgZGVmYXVsdCBpcyBhdWRpby92aWRlb1xuUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFuc3dlciA9IGZ1bmN0aW9uIChjb25zdHJhaW50cywgY2IpIHtcbiAgICB2YXIgaGFzQ29uc3RyYWludHMgPSBhcmd1bWVudHMubGVuZ3RoID09PSAyO1xuICAgIHZhciBjYWxsYmFjayA9IGhhc0NvbnN0cmFpbnRzID8gY2IgOiBjb25zdHJhaW50cztcbiAgICB2YXIgbWVkaWFDb25zdHJhaW50cyA9IGhhc0NvbnN0cmFpbnRzICYmIGNvbnN0cmFpbnRzID8gY29uc3RyYWludHMgOiB7XG4gICAgICAgICAgICBtYW5kYXRvcnk6IHtcbiAgICAgICAgICAgICAgICBPZmZlclRvUmVjZWl2ZUF1ZGlvOiB0cnVlLFxuICAgICAgICAgICAgICAgIE9mZmVyVG9SZWNlaXZlVmlkZW86IHRydWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgIHRoaXMuX2Fuc3dlcihtZWRpYUNvbnN0cmFpbnRzLCBjYWxsYmFjayk7XG59O1xuXG4vLyBQcm9jZXNzIGFuIGFuc3dlclxuUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmhhbmRsZUFuc3dlciA9IGZ1bmN0aW9uIChhbnN3ZXIsIGNiKSB7XG4gICAgY2IgPSBjYiB8fCBmdW5jdGlvbiAoKSB7fTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKGFuc3dlci5qaW5nbGUpIHtcbiAgICAgICAgYW5zd2VyLnNkcCA9IFNKSi50b1Nlc3Npb25TRFAoYW5zd2VyLmppbmdsZSwge1xuICAgICAgICAgICAgc2lkOiBzZWxmLmNvbmZpZy5zZHBTZXNzaW9uSUQsXG4gICAgICAgICAgICByb2xlOiBzZWxmLl9yb2xlKCksXG4gICAgICAgICAgICBkaXJlY3Rpb246ICdpbmNvbWluZydcbiAgICAgICAgfSk7XG4gICAgICAgIHNlbGYucmVtb3RlRGVzY3JpcHRpb24gPSBhbnN3ZXIuamluZ2xlO1xuXG4gICAgICAgIC8vIFNhdmUgSUNFIGNyZWRlbnRpYWxzXG4gICAgICAgIGFuc3dlci5qaW5nbGUuY29udGVudHMuZm9yRWFjaChmdW5jdGlvbiAoY29udGVudCkge1xuICAgICAgICAgICAgdmFyIHRyYW5zcG9ydCA9IGNvbnRlbnQudHJhbnNwb3J0IHx8IHt9O1xuICAgICAgICAgICAgaWYgKHRyYW5zcG9ydC51ZnJhZykge1xuICAgICAgICAgICAgICAgIHNlbGYuaWNlQ3JlZGVudGlhbHMucmVtb3RlW2NvbnRlbnQubmFtZV0gPSB7XG4gICAgICAgICAgICAgICAgICAgIHVmcmFnOiB0cmFuc3BvcnQudWZyYWcsXG4gICAgICAgICAgICAgICAgICAgIHB3ZDogdHJhbnNwb3J0LnB3ZFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICBhbnN3ZXIuc2RwLnNwbGl0KCdcXHJcXG4nKS5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIGlmIChsaW5lLmluZGV4T2YoJ2E9Y2FuZGlkYXRlOicpID09PSAwKSB7XG4gICAgICAgICAgICBzZWxmLl9jaGVja1JlbW90ZUNhbmRpZGF0ZShsaW5lKTtcbiAgICAgICAgfVxuICAgIH0pO1xuICAgIHNlbGYucGMuc2V0UmVtb3RlRGVzY3JpcHRpb24oXG4gICAgICAgIG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oYW5zd2VyKSxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgaWYgKHNlbGYud3RGaXJlZm94KSB7XG4gICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLmZpcmVmb3hjYW5kaWRhdGVidWZmZXIuZm9yRWFjaChmdW5jdGlvbiAoY2FuZGlkYXRlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBhZGQgY2FuZGlkYXRlcyBsYXRlclxuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5wYy5hZGRJY2VDYW5kaWRhdGUoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IFJUQ0ljZUNhbmRpZGF0ZShjYW5kaWRhdGUpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHsgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuZW1pdCgnZXJyb3InLCBlcnIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLl9jaGVja1JlbW90ZUNhbmRpZGF0ZShjYW5kaWRhdGUuY2FuZGlkYXRlKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIHNlbGYuZmlyZWZveGNhbmRpZGF0ZWJ1ZmZlciA9IG51bGw7XG4gICAgICAgICAgICAgICAgfSwgc2VsZi53dEZpcmVmb3gpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2IobnVsbCk7XG4gICAgICAgIH0sXG4gICAgICAgIGNiXG4gICAgKTtcbn07XG5cbi8vIENsb3NlIHRoZSBwZWVyIGNvbm5lY3Rpb25cblBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jbG9zZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnBjLmNsb3NlKCk7XG5cbiAgICB0aGlzLl9sb2NhbERhdGFDaGFubmVscyA9IFtdO1xuICAgIHRoaXMuX3JlbW90ZURhdGFDaGFubmVscyA9IFtdO1xuXG4gICAgdGhpcy5lbWl0KCdjbG9zZScpO1xufTtcblxuLy8gSW50ZXJuYWwgY29kZSBzaGFyaW5nIGZvciB2YXJpb3VzIHR5cGVzIG9mIGFuc3dlciBtZXRob2RzXG5QZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX2Fuc3dlciA9IGZ1bmN0aW9uIChjb25zdHJhaW50cywgY2IpIHtcbiAgICBjYiA9IGNiIHx8IGZ1bmN0aW9uICgpIHt9O1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAoIXRoaXMucGMucmVtb3RlRGVzY3JpcHRpb24pIHtcbiAgICAgICAgLy8gdGhlIG9sZCBBUEkgaXMgdXNlZCwgY2FsbCBoYW5kbGVPZmZlclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3JlbW90ZURlc2NyaXB0aW9uIG5vdCBzZXQnKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5wYy5zaWduYWxpbmdTdGF0ZSA9PT0gJ2Nsb3NlZCcpIHJldHVybiBjYignQWxyZWFkeSBjbG9zZWQnKTtcblxuICAgIHNlbGYucGMuY3JlYXRlQW5zd2VyKFxuICAgICAgICBmdW5jdGlvbiAoYW5zd2VyKSB7XG4gICAgICAgICAgICB2YXIgc2ltID0gW107XG4gICAgICAgICAgICBpZiAoc2VsZi5lbmFibGVDaHJvbWVOYXRpdmVTaW11bGNhc3QpIHtcbiAgICAgICAgICAgICAgICAvLyBuYXRpdmUgc2ltdWxjYXN0IHBhcnQgMTogYWRkIGFub3RoZXIgU1NSQ1xuICAgICAgICAgICAgICAgIGFuc3dlci5qaW5nbGUgPSBTSkoudG9TZXNzaW9uSlNPTihhbnN3ZXIuc2RwLCB7XG4gICAgICAgICAgICAgICAgICAgIHJvbGU6IHNlbGYuX3JvbGUoKSxcbiAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uOiAnb3V0Z29pbmcnXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgaWYgKGFuc3dlci5qaW5nbGUuY29udGVudHMubGVuZ3RoID49IDIgJiYgYW5zd2VyLmppbmdsZS5jb250ZW50c1sxXS5uYW1lID09PSAndmlkZW8nKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBncm91cHMgPSBhbnN3ZXIuamluZ2xlLmNvbnRlbnRzWzFdLmFwcGxpY2F0aW9uLnNvdXJjZUdyb3VwcyB8fCBbXTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGhhc1NpbSA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICBncm91cHMuZm9yRWFjaChmdW5jdGlvbiAoZ3JvdXApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChncm91cC5zZW1hbnRpY3MgPT0gJ1NJTScpIGhhc1NpbSA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICBpZiAoIWhhc1NpbSAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgYW5zd2VyLmppbmdsZS5jb250ZW50c1sxXS5hcHBsaWNhdGlvbi5zb3VyY2VzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIG5ld3NzcmMgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGFuc3dlci5qaW5nbGUuY29udGVudHNbMV0uYXBwbGljYXRpb24uc291cmNlc1swXSkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgbmV3c3NyYy5zc3JjID0gJycgKyBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAweGZmZmZmZmZmKTsgLy8gRklYTUU6IGxvb2sgZm9yIGNvbmZsaWN0c1xuICAgICAgICAgICAgICAgICAgICAgICAgYW5zd2VyLmppbmdsZS5jb250ZW50c1sxXS5hcHBsaWNhdGlvbi5zb3VyY2VzLnB1c2gobmV3c3NyYyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIHNpbS5wdXNoKGFuc3dlci5qaW5nbGUuY29udGVudHNbMV0uYXBwbGljYXRpb24uc291cmNlc1swXS5zc3JjKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNpbS5wdXNoKG5ld3NzcmMuc3NyYyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBncm91cHMucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VtYW50aWNzOiAnU0lNJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2VzOiBzaW1cbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBhbHNvIGNyZWF0ZSBhbiBSVFggb25lIGZvciB0aGUgU0lNIG9uZVxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHJ0eHNzcmMgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KG5ld3NzcmMpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJ0eHNzcmMuc3NyYyA9ICcnICsgTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogMHhmZmZmZmZmZik7IC8vIEZJWE1FOiBsb29rIGZvciBjb25mbGljdHNcbiAgICAgICAgICAgICAgICAgICAgICAgIGFuc3dlci5qaW5nbGUuY29udGVudHNbMV0uYXBwbGljYXRpb24uc291cmNlcy5wdXNoKHJ0eHNzcmMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbWFudGljczogJ0ZJRCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlczogW25ld3NzcmMuc3NyYywgcnR4c3NyYy5zc3JjXVxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGFuc3dlci5qaW5nbGUuY29udGVudHNbMV0uYXBwbGljYXRpb24uc291cmNlR3JvdXBzID0gZ3JvdXBzO1xuICAgICAgICAgICAgICAgICAgICAgICAgYW5zd2VyLnNkcCA9IFNKSi50b1Nlc3Npb25TRFAoYW5zd2VyLmppbmdsZSwge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZDogc2VsZi5jb25maWcuc2RwU2Vzc2lvbklELFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvbGU6IHNlbGYuX3JvbGUoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb246ICdvdXRnb2luZydcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIGV4cGFuZGVkQW5zd2VyID0ge1xuICAgICAgICAgICAgICAgIHR5cGU6ICdhbnN3ZXInLFxuICAgICAgICAgICAgICAgIHNkcDogYW5zd2VyLnNkcFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGlmIChzZWxmLmFzc3VtZVNldExvY2FsU3VjY2Vzcykge1xuICAgICAgICAgICAgICAgIC8vIG5vdCBzYWZlIHRvIGRvIHdoZW4gZG9pbmcgc2ltdWxjYXN0IG1hbmdsaW5nXG4gICAgICAgICAgICAgICAgdmFyIGNvcHkgPSBjbG9uZURlZXAoZXhwYW5kZWRBbnN3ZXIpO1xuICAgICAgICAgICAgICAgIHNlbGYuZW1pdCgnYW5zd2VyJywgY29weSk7XG4gICAgICAgICAgICAgICAgY2IobnVsbCwgY29weSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzZWxmLl9jYW5kaWRhdGVCdWZmZXIgPSBbXTtcbiAgICAgICAgICAgIHNlbGYucGMuc2V0TG9jYWxEZXNjcmlwdGlvbihhbnN3ZXIsXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5jb25maWcudXNlSmluZ2xlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgamluZ2xlID0gU0pKLnRvU2Vzc2lvbkpTT04oYW5zd2VyLnNkcCwge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvbGU6IHNlbGYuX3JvbGUoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb246ICdvdXRnb2luZydcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgamluZ2xlLnNpZCA9IHNlbGYuY29uZmlnLnNpZDtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubG9jYWxEZXNjcmlwdGlvbiA9IGppbmdsZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZGVkQW5zd2VyLmppbmdsZSA9IGppbmdsZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5lbmFibGVDaHJvbWVOYXRpdmVTaW11bGNhc3QpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG5hdGl2ZSBzaW11bGNhc3QgcGFydCAyOlxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gc2lnbmFsIG11bHRpcGxlIHRyYWNrcyB0byB0aGUgcmVjZWl2ZXJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGZvciBhbnl0aGluZyBpbiB0aGUgU0lNIGdyb3VwXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIWV4cGFuZGVkQW5zd2VyLmppbmdsZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZGVkQW5zd2VyLmppbmdsZSA9IFNKSi50b1Nlc3Npb25KU09OKGFuc3dlci5zZHAsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9sZTogc2VsZi5fcm9sZSgpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb246ICdvdXRnb2luZydcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZGVkQW5zd2VyLmppbmdsZS5jb250ZW50c1sxXS5hcHBsaWNhdGlvbi5zb3VyY2VzLmZvckVhY2goZnVuY3Rpb24gKHNvdXJjZSwgaWR4KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGhlIGZsb29yIGlkeC8yIGlzIGEgaGFjayB0aGF0IHJlbGllcyBvbiBhIHBhcnRpY3VsYXIgb3JkZXJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBvZiBncm91cHMsIGFsdGVybmF0aW5nIGJldHdlZW4gc2ltIGFuZCBydHhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2UucGFyYW1ldGVycyA9IHNvdXJjZS5wYXJhbWV0ZXJzLm1hcChmdW5jdGlvbiAocGFyYW1ldGVyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwYXJhbWV0ZXIua2V5ID09PSAnbXNpZCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtZXRlci52YWx1ZSArPSAnLScgKyBNYXRoLmZsb29yKGlkeCAvIDIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBwYXJhbWV0ZXI7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZGVkQW5zd2VyLnNkcCA9IFNKSi50b1Nlc3Npb25TRFAoZXhwYW5kZWRBbnN3ZXIuamluZ2xlLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lkOiBzZWxmLnNkcFNlc3Npb25JRCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb2xlOiBzZWxmLl9yb2xlKCksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uOiAnb3V0Z29pbmcnXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBleHBhbmRlZEFuc3dlci5zZHAuc3BsaXQoJ1xcclxcbicpLmZvckVhY2goZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChsaW5lLmluZGV4T2YoJ2E9Y2FuZGlkYXRlOicpID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5fY2hlY2tMb2NhbENhbmRpZGF0ZShsaW5lKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIGlmICghc2VsZi5hc3N1bWVTZXRMb2NhbFN1Y2Nlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBjb3B5ID0gY2xvbmVEZWVwKGV4cGFuZGVkQW5zd2VyKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuZW1pdCgnYW5zd2VyJywgY29weSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYihudWxsLCBjb3B5KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLmVtaXQoJ2Vycm9yJywgZXJyKTtcbiAgICAgICAgICAgICAgICAgICAgY2IoZXJyKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICBzZWxmLmVtaXQoJ2Vycm9yJywgZXJyKTtcbiAgICAgICAgICAgIGNiKGVycik7XG4gICAgICAgIH0sXG4gICAgICAgIGNvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cbi8vIEludGVybmFsIG1ldGhvZCBmb3IgZW1pdHRpbmcgaWNlIGNhbmRpZGF0ZXMgb24gb3VyIHBlZXIgb2JqZWN0XG5QZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX29uSWNlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGlmIChldmVudC5jYW5kaWRhdGUpIHtcbiAgICAgICAgaWYgKHRoaXMuZG9udFNpZ25hbENhbmRpZGF0ZXMpIHJldHVybjtcbiAgICAgICAgdmFyIGljZSA9IGV2ZW50LmNhbmRpZGF0ZTtcblxuICAgICAgICB2YXIgZXhwYW5kZWRDYW5kaWRhdGUgPSB7XG4gICAgICAgICAgICBjYW5kaWRhdGU6IHtcbiAgICAgICAgICAgICAgICBjYW5kaWRhdGU6IGljZS5jYW5kaWRhdGUsXG4gICAgICAgICAgICAgICAgc2RwTWlkOiBpY2Uuc2RwTWlkLFxuICAgICAgICAgICAgICAgIHNkcE1MaW5lSW5kZXg6IGljZS5zZHBNTGluZUluZGV4XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuX2NoZWNrTG9jYWxDYW5kaWRhdGUoaWNlLmNhbmRpZGF0ZSk7XG5cbiAgICAgICAgdmFyIGNhbmQgPSBTSkoudG9DYW5kaWRhdGVKU09OKGljZS5jYW5kaWRhdGUpO1xuXG4gICAgICAgIHZhciBhbHJlYWR5O1xuICAgICAgICB2YXIgaWR4O1xuICAgICAgICBpZiAodGhpcy5lbGltaW5hdGVEdXBsaWNhdGVDYW5kaWRhdGVzICYmIGNhbmQudHlwZSA9PT0gJ3JlbGF5Jykge1xuICAgICAgICAgICAgLy8gZHJvcCBjYW5kaWRhdGVzIHdpdGggc2FtZSBmb3VuZGF0aW9uLCBjb21wb25lbnRcbiAgICAgICAgICAgIC8vIHRha2UgbG9jYWwgdHlwZSBwcmVmIGludG8gYWNjb3VudCBzbyB3ZSBkb24ndCBpZ25vcmUgdWRwXG4gICAgICAgICAgICAvLyBvbmVzIHdoZW4gd2Uga25vdyBhYm91dCBhIFRDUCBvbmUuIHVubGlrZWx5IGJ1dC4uLlxuICAgICAgICAgICAgYWxyZWFkeSA9IHRoaXMuX2NhbmRpZGF0ZUJ1ZmZlci5maWx0ZXIoXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKGMpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGMudHlwZSA9PT0gJ3JlbGF5JztcbiAgICAgICAgICAgICAgICB9KS5tYXAoZnVuY3Rpb24gKGMpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGMuZm91bmRhdGlvbiArICc6JyArIGMuY29tcG9uZW50O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBpZHggPSBhbHJlYWR5LmluZGV4T2YoY2FuZC5mb3VuZGF0aW9uICsgJzonICsgY2FuZC5jb21wb25lbnQpO1xuICAgICAgICAgICAgLy8gcmVtZW1iZXI6IGxvY2FsIHR5cGUgcHJlZiBvZiB1ZHAgaXMgMCwgdGNwIDEsIHRscyAyXG4gICAgICAgICAgICBpZiAoaWR4ID4gLTEgJiYgKChjYW5kLnByaW9yaXR5ID4+IDI0KSA+PSAoYWxyZWFkeVtpZHhdLnByaW9yaXR5ID4+IDI0KSkpIHtcbiAgICAgICAgICAgICAgICAvLyBkcm9wIGl0LCBzYW1lIGZvdW5kYXRpb24gd2l0aCBoaWdoZXIgKHdvcnNlKSB0eXBlIHByZWZcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuY29uZmlnLmJ1bmRsZVBvbGljeSA9PT0gJ21heC1idW5kbGUnKSB7XG4gICAgICAgICAgICAvLyBkcm9wIGNhbmRpZGF0ZXMgd2hpY2ggYXJlIGR1cGxpY2F0ZSBmb3IgYXVkaW8vdmlkZW8vZGF0YVxuICAgICAgICAgICAgLy8gZHVwbGljYXRlIG1lYW5zIHNhbWUgaG9zdC9wb3J0IGJ1dCBkaWZmZXJlbnQgc2RwTWlkXG4gICAgICAgICAgICBhbHJlYWR5ID0gdGhpcy5fY2FuZGlkYXRlQnVmZmVyLmZpbHRlcihcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoYykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gY2FuZC50eXBlID09PSBjLnR5cGU7XG4gICAgICAgICAgICAgICAgfSkubWFwKGZ1bmN0aW9uIChjYW5kKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBjYW5kLmFkZHJlc3MgKyAnOicgKyBjYW5kLnBvcnQ7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGlkeCA9IGFscmVhZHkuaW5kZXhPZihjYW5kLmFkZHJlc3MgKyAnOicgKyBjYW5kLnBvcnQpO1xuICAgICAgICAgICAgaWYgKGlkeCA+IC0xKSByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gYWxzbyBkcm9wIHJ0Y3AgY2FuZGlkYXRlcyBzaW5jZSB3ZSBrbm93IHRoZSBwZWVyIHN1cHBvcnRzIFJUQ1AtTVVYXG4gICAgICAgIC8vIHRoaXMgaXMgYSB3b3JrYXJvdW5kIHVudGlsIGJyb3dzZXJzIGltcGxlbWVudCB0aGlzIG5hdGl2ZWx5XG4gICAgICAgIGlmICh0aGlzLmNvbmZpZy5ydGNwTXV4UG9saWN5ID09PSAncmVxdWlyZScgJiYgY2FuZC5jb21wb25lbnQgPT09ICcyJykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuX2NhbmRpZGF0ZUJ1ZmZlci5wdXNoKGNhbmQpO1xuXG4gICAgICAgIGlmIChzZWxmLmNvbmZpZy51c2VKaW5nbGUpIHtcbiAgICAgICAgICAgIGlmICghaWNlLnNkcE1pZCkgeyAvLyBmaXJlZm94IGRvZXNuJ3Qgc2V0IHRoaXNcbiAgICAgICAgICAgICAgICBpZiAoc2VsZi5wYy5yZW1vdGVEZXNjcmlwdGlvbiAmJiBzZWxmLnBjLnJlbW90ZURlc2NyaXB0aW9uLnR5cGUgPT09ICdvZmZlcicpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gcHJlc2VydmUgbmFtZSBmcm9tIHJlbW90ZVxuICAgICAgICAgICAgICAgICAgICBpY2Uuc2RwTWlkID0gc2VsZi5yZW1vdGVEZXNjcmlwdGlvbi5jb250ZW50c1tpY2Uuc2RwTUxpbmVJbmRleF0ubmFtZTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBpY2Uuc2RwTWlkID0gc2VsZi5sb2NhbERlc2NyaXB0aW9uLmNvbnRlbnRzW2ljZS5zZHBNTGluZUluZGV4XS5uYW1lO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICghc2VsZi5pY2VDcmVkZW50aWFscy5sb2NhbFtpY2Uuc2RwTWlkXSkge1xuICAgICAgICAgICAgICAgIHZhciBqaW5nbGUgPSBTSkoudG9TZXNzaW9uSlNPTihzZWxmLnBjLmxvY2FsRGVzY3JpcHRpb24uc2RwLCB7XG4gICAgICAgICAgICAgICAgICAgIHJvbGU6IHNlbGYuX3JvbGUoKSxcbiAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uOiAnb3V0Z29pbmcnXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgamluZ2xlLmNvbnRlbnRzLmZvckVhY2goZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHRyYW5zcG9ydCA9IGNvbnRlbnQudHJhbnNwb3J0IHx8IHt9O1xuICAgICAgICAgICAgICAgICAgICBpZiAodHJhbnNwb3J0LnVmcmFnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmljZUNyZWRlbnRpYWxzLmxvY2FsW2NvbnRlbnQubmFtZV0gPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdWZyYWc6IHRyYW5zcG9ydC51ZnJhZyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwd2Q6IHRyYW5zcG9ydC5wd2RcbiAgICAgICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGV4cGFuZGVkQ2FuZGlkYXRlLmppbmdsZSA9IHtcbiAgICAgICAgICAgICAgICBjb250ZW50czogW3tcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogaWNlLnNkcE1pZCxcbiAgICAgICAgICAgICAgICAgICAgY3JlYXRvcjogc2VsZi5fcm9sZSgpLFxuICAgICAgICAgICAgICAgICAgICB0cmFuc3BvcnQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRyYW5zcG9ydFR5cGU6ICdpY2VVZHAnLFxuICAgICAgICAgICAgICAgICAgICAgICAgdWZyYWc6IHNlbGYuaWNlQ3JlZGVudGlhbHMubG9jYWxbaWNlLnNkcE1pZF0udWZyYWcsXG4gICAgICAgICAgICAgICAgICAgICAgICBwd2Q6IHNlbGYuaWNlQ3JlZGVudGlhbHMubG9jYWxbaWNlLnNkcE1pZF0ucHdkLFxuICAgICAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlczogW1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbmRcbiAgICAgICAgICAgICAgICAgICAgICAgIF1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1dXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgaWYgKHNlbGYuYmF0Y2hJY2VDYW5kaWRhdGVzID4gMCkge1xuICAgICAgICAgICAgICAgIGlmIChzZWxmLmJhdGNoZWRJY2VDYW5kaWRhdGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgY29udGVudHMgPSB7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuYmF0Y2hlZEljZUNhbmRpZGF0ZXMuZm9yRWFjaChmdW5jdGlvbiAoY29udGVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRlbnQgPSBjb250ZW50LmNvbnRlbnRzWzBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghY29udGVudHNbY29udGVudC5uYW1lXSkgY29udGVudHNbY29udGVudC5uYW1lXSA9IGNvbnRlbnQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGVudHNbY29udGVudC5uYW1lXS50cmFuc3BvcnQuY2FuZGlkYXRlcy5wdXNoKGNvbnRlbnQudHJhbnNwb3J0LmNhbmRpZGF0ZXNbMF0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgbmV3Q2FuZCA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBqaW5nbGU6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGVudHM6IFtdXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgICAgIE9iamVjdC5rZXlzKGNvbnRlbnRzKS5mb3JFYWNoKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3Q2FuZC5qaW5nbGUuY29udGVudHMucHVzaChjb250ZW50c1tuYW1lXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuYmF0Y2hlZEljZUNhbmRpZGF0ZXMgPSBbXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuZW1pdCgnaWNlJywgbmV3Q2FuZCk7XG4gICAgICAgICAgICAgICAgICAgIH0sIHNlbGYuYmF0Y2hJY2VDYW5kaWRhdGVzKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc2VsZi5iYXRjaGVkSWNlQ2FuZGlkYXRlcy5wdXNoKGV4cGFuZGVkQ2FuZGlkYXRlLmppbmdsZSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5lbWl0KCdpY2UnLCBleHBhbmRlZENhbmRpZGF0ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5lbWl0KCdlbmRPZkNhbmRpZGF0ZXMnKTtcbiAgICB9XG59O1xuXG4vLyBJbnRlcm5hbCBtZXRob2QgZm9yIHByb2Nlc3NpbmcgYSBuZXcgZGF0YSBjaGFubmVsIGJlaW5nIGFkZGVkIGJ5IHRoZVxuLy8gb3RoZXIgcGVlci5cblBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fb25EYXRhQ2hhbm5lbCA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgIC8vIG1ha2Ugc3VyZSB3ZSBrZWVwIGEgcmVmZXJlbmNlIHNvIHRoaXMgZG9lc24ndCBnZXQgZ2FyYmFnZSBjb2xsZWN0ZWRcbiAgICB2YXIgY2hhbm5lbCA9IGV2ZW50LmNoYW5uZWw7XG4gICAgdGhpcy5fcmVtb3RlRGF0YUNoYW5uZWxzLnB1c2goY2hhbm5lbCk7XG5cbiAgICB0aGlzLmVtaXQoJ2FkZENoYW5uZWwnLCBjaGFubmVsKTtcbn07XG5cbi8vIENyZWF0ZSBhIGRhdGEgY2hhbm5lbCBzcGVjIHJlZmVyZW5jZTpcbi8vIGh0dHA6Ly9kZXYudzMub3JnLzIwMTEvd2VicnRjL2VkaXRvci93ZWJydGMuaHRtbCNpZGwtZGVmLVJUQ0RhdGFDaGFubmVsSW5pdFxuUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmNyZWF0ZURhdGFDaGFubmVsID0gZnVuY3Rpb24gKG5hbWUsIG9wdHMpIHtcbiAgICB2YXIgY2hhbm5lbCA9IHRoaXMucGMuY3JlYXRlRGF0YUNoYW5uZWwobmFtZSwgb3B0cyk7XG5cbiAgICAvLyBtYWtlIHN1cmUgd2Uga2VlcCBhIHJlZmVyZW5jZSBzbyB0aGlzIGRvZXNuJ3QgZ2V0IGdhcmJhZ2UgY29sbGVjdGVkXG4gICAgdGhpcy5fbG9jYWxEYXRhQ2hhbm5lbHMucHVzaChjaGFubmVsKTtcblxuICAgIHJldHVybiBjaGFubmVsO1xufTtcblxuUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFN0YXRzID0gZnVuY3Rpb24gKGNiKSB7XG4gICAgdGhpcy5wYy5nZXRTdGF0cyhudWxsLFxuICAgICAgICBmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgICAgICBjYihudWxsLCByZXMpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICBjYihlcnIpO1xuICAgICAgICB9XG4gICAgKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gUGVlckNvbm5lY3Rpb247XG5cblxuXG4vLy8vLy8vLy8vLy8vLy8vLy9cbi8vIFdFQlBBQ0sgRk9PVEVSXG4vLyAuL34vcnRjcGVlcmNvbm5lY3Rpb24vcnRjcGVlcmNvbm5lY3Rpb24uanNcbi8vIG1vZHVsZSBpZCA9IDQ3MVxuLy8gbW9kdWxlIGNodW5rcyA9IDAiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=");

TODO found
Open

    eval("'use strict';Object.defineProperty(exports, \"__esModule\", { value: true });var _values2 = __webpack_require__(151);var _values3 = _interopRequireDefault(_values2);var _get2 = __webpack_require__(118);var _get3 = _interopRequireDefault(_get2);var _each2 = __webpack_require__(137);var _each3 = _interopRequireDefault(_each2);var _find2 = __webpack_require__(28);var _find3 = _interopRequireDefault(_find2);var _templateObject = _taggedTemplateLiteral(['\\n        <ul>\\n          <li class=\"changeP toCommons\"><div class=\"rc-perm-icon\"></div>commons</li>\\n          <li class=\"changeP toPublic\"><div class=\"rc-perm-icon\"></div>public</li>\\n          <li class=\"changeP toPrivate\"><div class=\"rc-perm-icon\"></div>private</li>\\n        </ul>'], ['\\n        <ul>\\n          <li class=\"changeP toCommons\"><div class=\"rc-perm-icon\"></div>commons</li>\\n          <li class=\"changeP toPublic\"><div class=\"rc-perm-icon\"></div>public</li>\\n          <li class=\"changeP toPrivate\"><div class=\"rc-perm-icon\"></div>private</li>\\n        </ul>']),_templateObject2 = _taggedTemplateLiteral(['\\n        <li class=\"rc-permission\">\\n          <div class=\"rc-icon\"></div>\\n          Change permissions\\n          ', '\\n          <div class=\"expandLi\"></div>\\n        </li>'], ['\\n        <li class=\"rc-permission\">\\n          <div class=\"rc-icon\"></div>\\n          Change permissions\\n          ', '\\n          <div class=\"expandLi\"></div>\\n        </li>']),_templateObject3 = _taggedTemplateLiteral(['\\n        <ul id=\"fetchSiblingList\">\\n          <li class=\"fetchAll\">All<div class=\"rc-keyboard\">Alt+R</div></li>\\n          <li id=\"loadingSiblings\"></li>\\n        </ul>'], ['\\n        <ul id=\"fetchSiblingList\">\\n          <li class=\"fetchAll\">All<div class=\"rc-keyboard\">Alt+R</div></li>\\n          <li id=\"loadingSiblings\"></li>\\n        </ul>']),_templateObject4 = _taggedTemplateLiteral(['\\n        <ul>\\n          <li class=\"changeP toCommons\"><div class=\"rc-perm-icon\"></div>commons</li>\\n          <li class=\"changeP toPublic\"><div class=\"rc-perm-icon\"></div>public</li>           <li class=\"changeP toPrivate\"><div class=\"rc-perm-icon\"></div>private</li>         </ul>'], ['\\n        <ul>\\n          <li class=\"changeP toCommons\"><div class=\"rc-perm-icon\"></div>commons</li>\\n          <li class=\"changeP toPublic\"><div class=\"rc-perm-icon\"></div>public</li>           <li class=\"changeP toPrivate\"><div class=\"rc-perm-icon\"></div>private</li>         </ul>']);\n\n\nvar _outdent = __webpack_require__(148);var _outdent2 = _interopRequireDefault(_outdent);\n\nvar _JIT = __webpack_require__(170);var _JIT2 = _interopRequireDefault(_JIT);\n\nvar _Active = __webpack_require__(6);var _Active2 = _interopRequireDefault(_Active);\nvar _Control = __webpack_require__(27);var _Control2 = _interopRequireDefault(_Control);\nvar _Create = __webpack_require__(173);var _Create2 = _interopRequireDefault(_Create);\nvar _DataModel = __webpack_require__(149);var _DataModel2 = _interopRequireDefault(_DataModel);\nvar _Engine = __webpack_require__(150);var _Engine2 = _interopRequireDefault(_Engine);\nvar _Filter = __webpack_require__(356);var _Filter2 = _interopRequireDefault(_Filter);\nvar _GlobalUI = __webpack_require__(362);var _GlobalUI2 = _interopRequireDefault(_GlobalUI);\nvar _Map = __webpack_require__(369);var _Map2 = _interopRequireDefault(_Map);\nvar _Mouse = __webpack_require__(352);var _Mouse2 = _interopRequireDefault(_Mouse);\nvar _Selected = __webpack_require__(353);var _Selected2 = _interopRequireDefault(_Selected);\nvar _Settings = __webpack_require__(572);var _Settings2 = _interopRequireDefault(_Settings);\nvar _Synapse = __webpack_require__(354);var _Synapse2 = _interopRequireDefault(_Synapse);\nvar _SynapseCard = __webpack_require__(569);var _SynapseCard2 = _interopRequireDefault(_SynapseCard);\nvar _Topic = __webpack_require__(571);var _Topic2 = _interopRequireDefault(_Topic);\nvar _TopicCard = __webpack_require__(566);var _TopicCard2 = _interopRequireDefault(_TopicCard);\nvar _Util = __webpack_require__(410);var _Util2 = _interopRequireDefault(_Util);\nvar _Visualize = __webpack_require__(561);var _Visualize2 = _interopRequireDefault(_Visualize);\nvar _clipboardJs = __webpack_require__(363);var _clipboardJs2 = _interopRequireDefault(_clipboardJs);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}function _taggedTemplateLiteral(strings, raw) {return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } }));} /* global $, Image, CanvasLoader */\n\nvar panningInt = void 0;\n\nvar JIT = {\n  tempInit: false,\n  tempNode: null,\n  tempNode2: null,\n  mouseDownPix: {},\n  dragFlag: 0,\n  dragTolerance: 0,\n  virtualPointer: {},\n\n  events: {\n    topicDrag: 'Metamaps:JIT:events:topicDrag',\n    pan: 'Metamaps:JIT:events:pan',\n    zoom: 'Metamaps:JIT:events:zoom',\n    animationDone: 'Metamaps:JIT:events:animationDone' },\n\n  vizData: [], // contains the visualization-compatible graph\n  /**\n   * This method will bind the event handlers it is interested and initialize the class.\n   */\n  init: function init(serverData) {\n    var self = JIT;\n\n    $('.zoomIn').click(self.zoomIn);\n    $('.zoomOut').click(self.zoomOut);\n\n    var zoomExtents = function zoomExtents(event) {\n      self.zoomExtents(event, _Visualize2.default.mGraph.canvas);\n    };\n    $('.zoomExtents').click(zoomExtents);\n\n    $('.takeScreenshot').click(_Map2.default.exportImage);\n\n    self.topicDescImage = new Image();\n    self.topicDescImage.src = serverData['topic_description_signifier.png'];\n\n    self.topicLinkImage = new Image();\n    self.topicLinkImage.src = serverData['topic_link_signifier.png'];\n  },\n  /**\n      * convert our topic JSON into something JIT can use\n      */\n  convertModelsToJIT: function convertModelsToJIT(topics, synapses) {\n    var jitReady = [];\n\n    var synapsesToRemove = [];\n    var mapping = void 0;\n    var node = void 0;\n    var nodes = {};\n    var existingEdge = void 0;\n    var edge = void 0;\n    var edges = [];\n\n    topics.each(function (t) {\n      node = t.createNode();\n      nodes[node.id] = node;\n    });\n    synapses.each(function (s) {\n      edge = s.createEdge();\n\n      if (topics.get(s.get('topic1_id')) === undefined || topics.get(s.get('topic2_id')) === undefined) {\n        // this means it's an invalid synapse\n        synapsesToRemove.push(s);\n      } else if (nodes[edge.nodeFrom] && nodes[edge.nodeTo]) {\n        existingEdge = (0, _find3.default)(edges, {\n          nodeFrom: edge.nodeFrom,\n          nodeTo: edge.nodeTo }) ||\n\n        (0, _find3.default)(edges, {\n          nodeFrom: edge.nodeTo,\n          nodeTo: edge.nodeFrom });\n\n\n        if (existingEdge) {\n          // for when you're dealing with multiple relationships between the same two topics\n          if (_Active2.default.Map) {\n            mapping = s.getMapping();\n            existingEdge.data['$mappingIDs'].push(mapping.id);\n          }\n          existingEdge.data['$synapseIDs'].push(s.id);\n        } else {\n          // for when you're dealing with a topic that has relationships to many different nodes\n          nodes[edge.nodeFrom].adjacencies.push(edge);\n          edges.push(edge);\n        }\n      }\n    });\n\n    (0, _each3.default)(nodes, function (node) {\n      jitReady.push(node);\n    });\n\n    return [jitReady, synapsesToRemove];\n  },\n  prepareVizData: function prepareVizData() {\n    var self = JIT;\n    var mapping = void 0;\n\n    // reset/empty vizData\n    self.vizData = [];\n    _Visualize2.default.loadLater = false;\n\n    var results = self.convertModelsToJIT(_DataModel2.default.Topics, _DataModel2.default.Synapses);\n\n    self.vizData = results[0];\n\n    // clean up the synapses array in case of any faulty data\n    (0, _each3.default)(results[1], function (synapse) {\n      mapping = synapse.getMapping();\n      _DataModel2.default.Synapses.remove(synapse);\n      if (_DataModel2.default.Mappings) _DataModel2.default.Mappings.remove(mapping);\n    });\n\n    // set up addTopic instructions in case they delete all the topics\n    // i.e. if there are 0 topics at any time, it should have instructions again\n    $('#instructions div').hide();\n    if (_Active2.default.Map && _Active2.default.Map.authorizeToEdit(_Active2.default.Mapper)) {\n      $('#instructions div.addTopic').show();\n    }\n\n    if (self.vizData.length === 0) {\n      _GlobalUI2.default.showDiv('#instructions');\n      _Visualize2.default.loadLater = true;\n    } else {\n      _GlobalUI2.default.hideDiv('#instructions');\n    }\n\n    _Visualize2.default.render();\n  }, // prepareVizData\n  edgeRender: function edgeRender(adj, canvas) {\n    // get nodes cartesian coordinates\n    var pos = adj.nodeFrom.pos.getc(true);\n    var posChild = adj.nodeTo.pos.getc(true);\n\n    var synapse = void 0;\n    if (adj.getData('displayIndex')) {\n      synapse = adj.getData('synapses')[adj.getData('displayIndex')];\n      if (!synapse) {\n        delete adj.data.$displayIndex;\n        synapse = adj.getData('synapses')[0];\n      }\n    } else {\n      synapse = adj.getData('synapses')[0];\n    }\n\n    if (!synapse) return; // this means there are no corresponding synapses for\n    // this edge, don't render it\n\n    // label placement on edges\n    if (canvas.denySelected) {\n      var color = _Settings2.default.colors.synapses.normal;\n      canvas.getCtx().fillStyle = canvas.getCtx().strokeStyle = color;\n    }\n    JIT.renderEdgeArrows(_JIT2.default.Graph.Plot.edgeHelper, adj, synapse, canvas);\n\n    // check for edge label in data\n    var desc = synapse.get('desc');\n\n    var showDesc = adj.getData('showDesc');\n\n    var drawSynapseCount = function drawSynapseCount(context, x, y, count) {\n      /*\n                                                                            circle size: 16x16px\n                                                                            positioning: overlay and center on top right corner of synapse label - 8px left and 8px down\n                                                                            color: #dab539\n                                                                            border color: #424242\n                                                                            border size: 1.5px\n                                                                            font: DIN medium\n                                                                            font-size: 14pt\n                                                                            font-color: #424242\n                                                                            */\n      context.beginPath();\n      context.arc(x, y, 8, 0, 2 * Math.PI, false);\n      context.fillStyle = '#DAB539';\n      context.strokeStyle = '#424242';\n      context.lineWidth = 1.5;\n      context.closePath();\n      context.fill();\n      context.stroke();\n\n      // add the synapse count\n      context.fillStyle = '#424242';\n      context.textAlign = 'center';\n      context.font = '14px din-medium';\n\n      context.fillText(count, x, y + 5);\n    };\n\n    if (!canvas.denySelected && desc !== '' && showDesc) {\n      // '&amp;' to '&'\n      desc = _Util2.default.decodeEntities(desc);\n\n      // now adjust the label placement\n      var ctx = canvas.getCtx();\n      ctx.font = 'bold 14px arial';\n      ctx.fillStyle = '#FFF';\n      ctx.textBaseline = 'alphabetic';\n\n      var arrayOfLabelLines = _Util2.default.splitLine(desc, 25).split('\\n');\n      var lineWidths = [];\n      for (var index = 0; index < arrayOfLabelLines.length; ++index) {\n        lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width);\n      }\n      var width = Math.max.apply(null, lineWidths) + 16;\n      var height = 16 * arrayOfLabelLines.length + 8;\n\n      var x = (pos.x + posChild.x - width) / 2;\n      var y = (pos.y + posChild.y) / 2 - height / 2;\n\n      var radius = 5;\n\n      // render background\n      ctx.beginPath();\n      ctx.moveTo(x + radius, y);\n      ctx.lineTo(x + width - radius, y);\n      ctx.quadraticCurveTo(x + width, y, x + width, y + radius);\n      ctx.lineTo(x + width, y + height - radius);\n      ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);\n      ctx.lineTo(x + radius, y + height);\n      ctx.quadraticCurveTo(x, y + height, x, y + height - radius);\n      ctx.lineTo(x, y + radius);\n      ctx.quadraticCurveTo(x, y, x + radius, y);\n      ctx.closePath();\n      ctx.fill();\n\n      // get number of synapses\n      var synapseNum = adj.getData('synapses').length;\n\n      // render text\n      ctx.fillStyle = '#424242';\n      ctx.textAlign = 'center';\n      for (var _index = 0; _index < arrayOfLabelLines.length; ++_index) {\n        ctx.fillText(arrayOfLabelLines[_index], x + width / 2, y + 18 + 16 * _index);\n      }\n\n      if (synapseNum > 1) {\n        drawSynapseCount(ctx, x + width, y, synapseNum);\n      }\n    } else if (!canvas.denySelected && showDesc) {\n      // get number of synapses\n      var _synapseNum = adj.getData('synapses').length;\n\n      if (_synapseNum > 1) {\n        var _ctx = canvas.getCtx();\n        var _x = (pos.x + posChild.x) / 2;\n        var _y = (pos.y + posChild.y) / 2;\n        drawSynapseCount(_ctx, _x, _y, _synapseNum);\n      }\n    }\n  }, // edgeRender\n  ForceDirected: {\n    animateSavedLayout: {\n      modes: ['linear'],\n      // TODO fix tests so we don't need _.get\n      transition: (0, _get3.default)(_JIT2.default, 'Trans.Quad.easeInOut'),\n      duration: 800,\n      onComplete: function onComplete() {\n        _Visualize2.default.mGraph.busy = false;\n        $(document).trigger(JIT.events.animationDone);\n      } },\n\n    animateFDLayout: {\n      modes: ['linear'],\n      // TODO fix tests so we don't need _.get\n      transition: (0, _get3.default)(_JIT2.default, 'Trans.Elastic.easeOut'),\n      duration: 800,\n      onComplete: function onComplete() {\n        _Visualize2.default.mGraph.busy = false;\n      } },\n\n    graphSettings: {\n      // id of the visualization container\n      injectInto: 'infovis',\n      // Enable zooming and panning\n      // by scrolling and DnD\n      Navigation: {\n        enable: true,\n        // Enable panning events only if we're dragging the empty\n        // canvas (and not a node).\n        panning: 'avoid nodes',\n        zooming: 28 // zoom speed. higher is more sensible\n      },\n      // Change node and edge styles such as\n      // color and width.\n      // These properties are also set per node\n      // with dollar prefixed data-properties in the\n      // JSON structure.\n      Node: {\n        overridable: true,\n        color: '#2D6A5D',\n        type: 'customNode',\n        dim: 25 },\n\n      Edge: {\n        overridable: true,\n        color: _Settings2.default.colors.synapses.normal,\n        type: 'customEdge',\n        lineWidth: 2,\n        alpha: 1 },\n\n      // Native canvas text styling\n      Label: {\n        type: 'Native', // Native or HTML\n        size: 20,\n        family: 'arial',\n        textBaseline: 'alphabetic',\n        color: _Settings2.default.colors.labels.text },\n\n      // Add Tips\n      Tips: {\n        enable: false,\n        onShow: function onShow(tip, node) {} },\n\n      // Add node events\n      Events: {\n        enable: true,\n        enableForEdges: true,\n        onMouseMove: function onMouseMove(node, eventInfo, e) {\n          JIT.onMouseMoveHandler(node, eventInfo, e);\n          // console.log('called mouse move handler')\n        },\n        // Update node positions when dragged\n        onDragMove: function onDragMove(node, eventInfo, e) {\n          JIT.onDragMoveTopicHandler(node, eventInfo, e);\n          // console.log('called drag move handler')\n        },\n        onDragEnd: function onDragEnd(node, eventInfo, e) {\n          JIT.onDragEndTopicHandler(node, eventInfo, e, false);\n          // console.log('called drag end handler')\n        },\n        onDragCancel: function onDragCancel(node, eventInfo, e) {\n          JIT.onDragCancelHandler(node, eventInfo, e, false);\n        },\n        // Implement the same handler for touchscreens\n        onTouchStart: function onTouchStart(node, eventInfo, e) {},\n        // Implement the same handler for touchscreens\n        onTouchMove: function onTouchMove(node, eventInfo, e) {\n          JIT.onDragMoveTopicHandler(node, eventInfo, e);\n        },\n        // Implement the same handler for touchscreens\n        onTouchEnd: function onTouchEnd(node, eventInfo, e) {},\n        // Implement the same handler for touchscreens\n        onTouchCancel: function onTouchCancel(node, eventInfo, e) {},\n        // Add also a click handler to nodes\n        onClick: function onClick(node, eventInfo, e) {\n          // remove the rightclickmenu\n          $('.rightclickmenu').remove();\n\n          if (_Mouse2.default.boxStartCoordinates) {\n            if (e.ctrlKey) {\n              _Visualize2.default.mGraph.busy = false;\n              _Mouse2.default.boxEndCoordinates = eventInfo.getPos();\n\n              var bS = _Mouse2.default.boxStartCoordinates;\n              var bE = _Mouse2.default.boxEndCoordinates;\n              if (Math.abs(bS.x - bE.x) > 20 && Math.abs(bS.y - bE.y) > 20) {\n                JIT.zoomToBox(e);\n                return;\n              } else {\n                _Mouse2.default.boxStartCoordinates = null;\n                _Mouse2.default.boxEndCoordinates = null;\n              }\n            }\n\n            if (e.shiftKey) {\n              _Visualize2.default.mGraph.busy = false;\n              _Mouse2.default.boxEndCoordinates = eventInfo.getPos();\n              JIT.selectWithBox(e);\n              return;\n            }\n          }\n\n          if (e.target.id !== 'infovis-canvas') return false;\n\n          // clicking on a edge, node, or clicking on blank part of canvas?\n          if (node.nodeFrom) {\n            JIT.selectEdgeOnClickHandler(node, e);\n          } else if (node && !node.nodeFrom) {\n            JIT.selectNodeOnClickHandler(node, e);\n            _Engine2.default.setFocusNode(node);\n          } else {\n            JIT.canvasClickHandler(eventInfo.getPos(), e);\n          } // if\n        },\n        // Add also a click handler to nodes\n        onRightClick: function onRightClick(node, eventInfo, e) {\n          // remove the rightclickmenu\n          $('.rightclickmenu').remove();\n\n          if (_Mouse2.default.boxStartCoordinates) {\n            _Create2.default.newSynapse.hide();\n            _Visualize2.default.mGraph.busy = false;\n            _Mouse2.default.boxEndCoordinates = eventInfo.getPos();\n            JIT.selectWithBox(e);\n            return;\n          }\n\n          if (e.target.id !== 'infovis-canvas') return false;\n\n          // clicking on a edge, node, or clicking on blank part of canvas?\n          if (node.nodeFrom) {\n            JIT.selectEdgeOnRightClickHandler(node, e);\n          } else if (node && !node.nodeFrom) {\n            JIT.selectNodeOnRightClickHandler(node, e);\n          } else {\n            // right click open space\n            _Create2.default.newSynapse.hide();\n          }\n        } },\n\n      // Number of iterations for the FD algorithm\n      iterations: 200,\n      // Edge length\n      levelDistance: 200 },\n\n    nodeSettings: {\n      'customNode': {\n        'render': function render(node, canvas) {\n          var pos = node.pos.getc(true);\n          var dim = node.getData('dim');\n          var topic = node.getData('topic');\n          var metacode = topic ? topic.getMetacode() : false;\n          var ctx = canvas.getCtx();\n\n          // if the topic is selected draw a circle around it\n          if (!canvas.denySelected && node.selected) {\n            ctx.beginPath();\n            ctx.arc(pos.x, pos.y, dim + 3, 0, 2 * Math.PI, false);\n            ctx.strokeStyle = _Settings2.default.colors.topics.selected;\n            ctx.lineWidth = 2;\n            ctx.stroke();\n          }\n\n          if (!metacode ||\n          !metacode.get('image') ||\n          !metacode.get('image').complete ||\n          typeof metacode.get('image').naturalWidth !== 'undefined' &&\n          metacode.get('image').naturalWidth === 0) {\n            ctx.beginPath();\n            ctx.arc(pos.x, pos.y, dim, 0, 2 * Math.PI, false);\n            ctx.fillStyle = '#B6B2FD';\n            ctx.fill();\n          } else {\n            ctx.drawImage(metacode.get('image'), pos.x - dim, pos.y - dim, dim * 2, dim * 2);\n          }\n\n          // if the topic has a link, draw a small image to indicate that\n          var hasLink = topic && topic.get('link') !== '' && topic.get('link') !== null;\n          var linkImage = JIT.topicLinkImage;\n          var linkImageLoaded = linkImage.complete ||\n          typeof linkImage.naturalWidth !== 'undefined' &&\n          linkImage.naturalWidth !== 0;\n          if (hasLink && linkImageLoaded) {\n            ctx.drawImage(linkImage, pos.x - dim - 8, pos.y - dim - 8, 16, 16);\n          }\n\n          // if the topic has a desc, draw a small image to indicate that\n          var hasDesc = topic && topic.get('desc') !== '' && topic.get('desc') !== null;\n          var descImage = JIT.topicDescImage;\n          var descImageLoaded = descImage.complete ||\n          typeof descImage.naturalWidth !== 'undefined' &&\n          descImage.naturalWidth !== 0;\n          if (hasDesc && descImageLoaded) {\n            ctx.drawImage(descImage, pos.x + dim - 8, pos.y - dim - 8, 16, 16);\n          }\n        },\n        'contains': function contains(node, pos) {\n          var npos = node.pos.getc(true);\n          var dim = node.getData('dim');\n          var arrayOfLabelLines = _Util2.default.splitLine(node.name, 25).split('\\n');\n          var ctx = _Visualize2.default.mGraph.canvas.getCtx();\n\n          var height = 25 * arrayOfLabelLines.length;\n\n          var lineWidths = [];\n          for (var index = 0; index < arrayOfLabelLines.length; ++index) {\n            lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width);\n          }\n          var width = Math.max.apply(null, lineWidths) + 8;\n          var labely = npos.y + node.getData('height') + 5 + height / 2;\n\n          var overLabel = this.nodeHelper.rectangle.contains({\n            x: npos.x,\n            y: labely },\n          pos, width, height);\n\n          return this.nodeHelper.circle.contains(npos, pos, dim) || overLabel;\n        } } },\n\n\n    edgeSettings: {\n      'customEdge': {\n        'render': function render(adj, canvas) {\n          JIT.edgeRender(adj, canvas);\n        },\n        'contains': function contains(adj, pos) {\n          var from = adj.nodeFrom.pos.getc();\n          var to = adj.nodeTo.pos.getc();\n\n          // this fixes an issue where when edges are perfectly horizontal or perfectly vertical\n          // it becomes incredibly difficult to hover over them\n          if (-1 < pos.x && pos.x < 1) pos.x = 0;\n          if (-1 < pos.y && pos.y < 1) pos.y = 0;\n\n          return _JIT2.default.Graph.Plot.edgeHelper.line.contains(from, to, pos, adj.Edge.epsilon + 5);\n        } } } },\n\n\n  // ForceDirected\n  ForceDirected3D: {\n    animate: {\n      modes: ['linear'],\n      // TODO fix tests so we don't need _.get\n      transition: (0, _get3.default)(_JIT2.default, 'Trans.Elastic.easeOut'),\n      duration: 2500,\n      onComplete: function onComplete() {\n        _Visualize2.default.mGraph.busy = false;\n      } },\n\n    graphSettings: {\n      // id of the visualization container\n      injectInto: 'infovis',\n      type: '3D',\n      Scene: {\n        Lighting: {\n          enable: false,\n          ambient: [0.5, 0.5, 0.5],\n          directional: {\n            direction: {\n              x: 1,\n              y: 0,\n              z: -1 },\n\n            color: [0.9, 0.9, 0.9] } } },\n\n\n\n      // Enable zooming and panning\n      // by scrolling and DnD\n      Navigation: {\n        enable: false,\n        // Enable panning events only if we're dragging the empty\n        // canvas (and not a node).\n        panning: 'avoid nodes',\n        zooming: 10 // zoom speed. higher is more sensible\n      },\n      // Change node and edge styles such as\n      // color and width.\n      // These properties are also set per node\n      // with dollar prefixed data-properties in the\n      // JSON structure.\n      Node: {\n        overridable: true,\n        type: 'sphere',\n        dim: 15,\n        color: '#ffffff' },\n\n      Edge: {\n        overridable: false,\n        type: 'tube',\n        color: '#111',\n        lineWidth: 3 },\n\n      // Native canvas text styling\n      Label: {\n        type: 'HTML', // Native or HTML\n        size: 10,\n        style: 'bold' },\n\n      // Add node events\n      Events: {\n        enable: true,\n        type: 'Native',\n        i: 0,\n        onMouseMove: function onMouseMove(node, eventInfo, e) {\n          // if(this.i++ % 3) return\n          var pos = eventInfo.getPos();\n          _Visualize2.default.cameraPosition.x += (pos.x - _Visualize2.default.cameraPosition.x) * 0.5;\n          _Visualize2.default.cameraPosition.y += (-pos.y - _Visualize2.default.cameraPosition.y) * 0.5;\n          _Visualize2.default.mGraph.plot();\n        },\n        onMouseWheel: function onMouseWheel(delta) {\n          _Visualize2.default.cameraPosition.z += -delta * 20;\n          _Visualize2.default.mGraph.plot();\n        },\n        onClick: function onClick() {} },\n\n      // Number of iterations for the FD algorithm\n      iterations: 200,\n      // Edge length\n      levelDistance: 100 },\n\n    nodeSettings: {},\n\n\n    edgeSettings: {} },\n\n\n  // ForceDirected3D\n  RGraph: {\n    animate: {\n      modes: ['polar'],\n      duration: 800,\n      onComplete: function onComplete() {\n        _Visualize2.default.mGraph.busy = false;\n      } },\n\n    // this will just be used to patch the ForceDirected graphsettings with the few things which actually differ\n    background: {\n      levelDistance: 200,\n      numberOfCircles: 4,\n      CanvasStyles: {\n        strokeStyle: '#333',\n        lineWidth: 1.5 } },\n\n\n    levelDistance: 200 },\n\n  onMouseEnter: function onMouseEnter(edge) {\n    var filtered = edge.getData('alpha') === 0;\n\n    // don't do anything if the edge is filtered\n    // or if the canvas is animating\n    if (filtered || _Visualize2.default.mGraph.busy) return;\n\n    $('canvas').css('cursor', 'pointer');\n    var edgeIsSelected = _Selected2.default.Edges.indexOf(edge);\n    // following if statement only executes if the edge being hovered over is not selected\n    if (edgeIsSelected === -1) {\n      edge.setData('showDesc', true, 'current');\n    }\n\n    edge.setDataset('end', {\n      lineWidth: 4 });\n\n    _Visualize2.default.mGraph.fx.animate({\n      modes: ['edge-property:lineWidth'],\n      duration: 100 });\n\n    _Visualize2.default.mGraph.plot();\n  }, // onMouseEnter\n  onMouseLeave: function onMouseLeave(edge) {\n    if (edge.getData('alpha') === 0) return; // don't do anything if the edge is filtered\n    $('canvas').css('cursor', 'default');\n    var edgeIsSelected = _Selected2.default.Edges.indexOf(edge);\n    // following if statement only executes if the edge being hovered over is not selected\n    if (edgeIsSelected === -1) {\n      edge.setData('showDesc', false, 'current');\n    }\n\n    edge.setDataset('end', {\n      lineWidth: 2 });\n\n    _Visualize2.default.mGraph.fx.animate({\n      modes: ['edge-property:lineWidth'],\n      duration: 100 });\n\n    _Visualize2.default.mGraph.plot();\n  }, // onMouseLeave\n  onMouseMoveHandler: function onMouseMoveHandler(_node, eventInfo, e) {\n    var self = JIT;\n\n    if (_Visualize2.default.mGraph.busy) return;\n\n    var node = eventInfo.getNode();\n    var edge = eventInfo.getEdge();\n\n    // if we're on top of a node object, act like there aren't edges under it\n    if (node !== false) {\n      if (_Mouse2.default.edgeHoveringOver) {\n        self.onMouseLeave(_Mouse2.default.edgeHoveringOver);\n      }\n      $('canvas').css('cursor', 'pointer');\n      return;\n    }\n\n    if (edge === false && _Mouse2.default.edgeHoveringOver !== false) {\n      // mouse not on an edge, but we were on an edge previously\n      self.onMouseLeave(_Mouse2.default.edgeHoveringOver);\n    } else if (edge !== false && _Mouse2.default.edgeHoveringOver === false) {\n      // mouse is on an edge, but there isn't a stored edge\n      self.onMouseEnter(edge);\n    } else if (edge !== false && _Mouse2.default.edgeHoveringOver !== edge) {\n      // mouse is on an edge, but a different edge is stored\n      self.onMouseLeave(_Mouse2.default.edgeHoveringOver);\n      self.onMouseEnter(edge);\n    }\n\n    // could be false\n    _Mouse2.default.edgeHoveringOver = edge;\n\n    if (!node && !edge) {\n      $('canvas').css('cursor', 'default');\n    }\n  }, // onMouseMoveHandler\n  enterKeyHandler: function enterKeyHandler(e) {\n    var creatingMap = _GlobalUI2.default.lightbox;\n    if (creatingMap === 'newmap' || creatingMap === 'forkmap') {\n      _GlobalUI2.default.CreateMap.submit();\n    } else if (e.target.id === 'topic_name' && !_Create2.default.newTopic.metacodeSelectorOpen) {\n      _Topic2.default.createTopicLocally();\n    } else if (_Create2.default.newSynapse.beingCreated) {\n      _Synapse2.default.createSynapseLocally(_Create2.default.newSynapse.topic1id, _Create2.default.newSynapse.topic2id);\n      _Engine2.default.runLayout();\n      _Create2.default.newSynapse.hide();\n    }\n  }, // enterKeyHandler\n  escKeyHandler: function escKeyHandler() {\n    _Control2.default.deselectAllEdges();\n    _Control2.default.deselectAllNodes();\n  }, // escKeyHandler\n  onDragMoveTopicHandler: function onDragMoveTopicHandler(node, eventInfo, e) {\n    var self = JIT;\n\n    var authorized = _Active2.default.Map && _Active2.default.Map.authorizeToEdit(_Active2.default.Mapper);\n\n    if (node && !node.nodeFrom) {\n\n      var pos = eventInfo.getPos();\n      if ((e.button === 0 || e.buttons === 0) && authorized) {\n        // start synapse creation  ->second option is for firefox\n        if (JIT.tempInit === false) {\n          JIT.tempNode = node;\n          JIT.tempInit = true;\n          _Create2.default.newSynapse.hide();\n          // set the draw synapse start positions\n          _Mouse2.default.synapseStartCoordinates = [];\n          if (_Selected2.default.Nodes.length) {\n            _Selected2.default.Nodes.forEach(function (n) {\n              _Mouse2.default.synapseStartCoordinates.push({\n                x: n.pos.getc().x,\n                y: n.pos.getc().y });\n\n            });\n          } else\n          {\n            _Mouse2.default.synapseStartCoordinates = [{\n              x: node.pos.getc().x,\n              y: node.pos.getc().y }];\n\n          }\n          _Mouse2.default.synapseEndCoordinates = {\n            x: pos.x,\n            y: pos.y };\n\n        }\n        //\n        var temp = eventInfo.getNode();\n        if (temp !== false && temp.id !== node.id && _Selected2.default.Nodes.indexOf(temp) === -1) {// this means a Node has been returned\n          JIT.tempNode2 = temp;\n          _Mouse2.default.synapseEndCoordinates = {\n            x: JIT.tempNode2.pos.getc().x,\n            y: JIT.tempNode2.pos.getc().y };\n\n          // before making the highlighted one bigger, make sure all the others are regular size\n          _Visualize2.default.mGraph.graph.eachNode(function (n) {\n            n.setData('dim', 25, 'current');\n          });\n          temp.setData('dim', 35, 'current');\n        } else if (!temp) {\n          JIT.tempNode2 = null;\n          _Mouse2.default.synapseEndCoordinates = {\n            x: pos.x,\n            y: pos.y };\n\n          _Visualize2.default.mGraph.graph.eachNode(function (n) {\n            n.setData('dim', 25, 'current');\n          });\n        }\n      }\n    }\n    _Visualize2.default.mGraph.plot();\n  }, // onDragMoveTopicHandler\n  onDragCancelHandler: function onDragCancelHandler(node, eventInfo, e) {\n    JIT.tempNode = null;\n    if (JIT.tempNode2) JIT.tempNode2.setData('dim', 25, 'current');\n    JIT.tempNode2 = null;\n    JIT.tempInit = false;\n    // reset the draw synapse positions to false\n    _Mouse2.default.synapseStartCoordinates = [];\n    _Mouse2.default.synapseEndCoordinates = null;\n    _Visualize2.default.mGraph.plot();\n  }, // onDragCancelHandler\n  onDragEndTopicHandler: function onDragEndTopicHandler(node, eventInfo, e) {\n    var self = JIT;\n    var midpoint = {};\n    var pixelPos = void 0;\n    var mapping = void 0;\n\n    if (JIT.tempInit && JIT.tempNode2 === null) {\n      _Mouse2.default.synapseEndCoordinates = null;\n    } else if (JIT.tempInit && JIT.tempNode2 !== null) {\n      // this means you want to create a synapse between two existing topics\n      _Create2.default.newSynapse.topic1id = JIT.tempNode.getData('topic').id;\n      _Create2.default.newSynapse.topic2id = JIT.tempNode2.getData('topic').id;\n      _Create2.default.newSynapse.node1 = JIT.tempNode;\n      _Create2.default.newSynapse.node2 = JIT.tempNode2;\n      JIT.tempNode2.setData('dim', 25, 'current');\n      midpoint.x = JIT.tempNode.pos.getc().x + (JIT.tempNode2.pos.getc().x - JIT.tempNode.pos.getc().x) / 2;\n      midpoint.y = JIT.tempNode.pos.getc().y + (JIT.tempNode2.pos.getc().y - JIT.tempNode.pos.getc().y) / 2;\n      pixelPos = _Util2.default.coordsToPixels(_Visualize2.default.mGraph, midpoint);\n      $('#new_synapse').css('left', pixelPos.x + 'px');\n      $('#new_synapse').css('top', pixelPos.y + 'px');\n      _Create2.default.newSynapse.open();\n      JIT.tempNode = null;\n      JIT.tempNode2 = null;\n      JIT.tempInit = false;\n    }\n    _Visualize2.default.mGraph.plot();\n  }, // onDragEndTopicHandler\n  canvasClickHandler: function canvasClickHandler(canvasLoc, e) {\n    // grab the location and timestamp of the click\n    var storedTime = _Mouse2.default.lastCanvasClick;\n    var now = Date.now(); // not compatible with IE8 FYI\n    _Mouse2.default.lastCanvasClick = now;\n\n    var authorized = _Active2.default.Map && _Active2.default.Map.authorizeToEdit(_Active2.default.Mapper);\n\n    if (now - storedTime < _Mouse2.default.DOUBLE_CLICK_TOLERANCE && !_Mouse2.default.didPan) {\n      // DOUBLE CLICK\n    } else if (!_Mouse2.default.didPan) {\n      // SINGLE CLICK, no pan\n      _Filter2.default.close();\n      _TopicCard2.default.hideCard();\n      _SynapseCard2.default.hideCard();\n      $('.rightclickmenu').remove();\n      // reset the draw synapse positions to false\n      _Mouse2.default.synapseStartCoordinates = [];\n      _Mouse2.default.synapseEndCoordinates = null;\n      JIT.tempInit = false;\n      JIT.tempNode = null;\n      JIT.tempNode2 = null;\n      if (!e.ctrlKey && !e.shiftKey) {\n        _Control2.default.deselectAllEdges();\n        _Control2.default.deselectAllNodes();\n      }\n    } else {\n      // SINGLE CLICK, resulting from pan\n    }\n  }, // canvasClickHandler\n  updateTopicPositions: function updateTopicPositions(node, pos) {\n    var len = _Selected2.default.Nodes.length;\n    // this is used to send nodes that are moving to\n    // other realtime collaborators on the same map\n    var positionsToSend = {};\n\n    // first define offset for each node\n    var xOffset = [];\n    var yOffset = [];\n    for (var i = 0; i < len; i += 1) {\n      var n = _Selected2.default.Nodes[i];\n      xOffset[i] = n.pos.getc().x - node.pos.getc().x;\n      yOffset[i] = n.pos.getc().y - node.pos.getc().y;\n    } // for\n\n    for (var _i = 0; _i < len; _i += 1) {\n      var _n = _Selected2.default.Nodes[_i];\n      var x = pos.x + xOffset[_i];\n      var y = pos.y + yOffset[_i];\n      if (_n.pos.rho || _n.pos.rho === 0) {\n        // this means we're in topic view\n        var rho = Math.sqrt(x * x + y * y);\n        var theta = Math.atan2(y, x);\n        _n.pos.setp(theta, rho);\n      } else {\n        _n.pos.setc(x, y);\n      }\n\n      if (_Active2.default.Map) {\n        var topic = _n.getData('topic');\n        // we use the topic ID not the node id\n        // because we can't depend on the node id\n        // to be the same as on other collaborators\n        // maps\n        positionsToSend[topic.id] = _n.pos;\n      }\n    } // for\n\n    if (_Active2.default.Map) {\n      $(document).trigger(JIT.events.topicDrag, [positionsToSend]);\n    }\n  },\n\n  nodeDoubleClickHandler: function nodeDoubleClickHandler(node, e) {\n    _TopicCard2.default.showCard(node);\n  }, // nodeDoubleClickHandler\n  edgeDoubleClickHandler: function edgeDoubleClickHandler(adj, e) {\n    _SynapseCard2.default.showCard(adj, e);\n  }, // nodeDoubleClickHandler\n  nodeWasDoubleClicked: function nodeWasDoubleClicked() {\n    // grab the timestamp of the click\n    var storedTime = _Mouse2.default.lastNodeClick;\n    var now = Date.now(); // not compatible with IE8 FYI\n    _Mouse2.default.lastNodeClick = now;\n\n    if (now - storedTime < _Mouse2.default.DOUBLE_CLICK_TOLERANCE) {\n      return true;\n    } else {\n      return false;\n    }\n  }, // nodeWasDoubleClicked\n  handleSelectionBeforeDragging: function handleSelectionBeforeDragging(node, e) {\n    if (_Selected2.default.Nodes.length === 0) {\n      _Control2.default.selectNode(node, e);\n    }\n    if (_Selected2.default.Nodes.indexOf(node) === -1) {\n      if (e.shiftKey) {\n        _Control2.default.selectNode(node, e);\n      } else {\n        _Control2.default.deselectAllEdges();\n        _Control2.default.deselectAllNodes();\n        _Control2.default.selectNode(node, e);\n      }\n    }\n  }, //  handleSelectionBeforeDragging\n  getNodeXY: function getNodeXY(node) {\n    if (typeof node.pos.x === 'number' && typeof node.pos.y === 'number') {\n      return node.pos;\n    } else if (typeof node.pos.theta === 'number' && typeof node.pos.rho === 'number') {\n      return new _JIT2.default.Polar(node.pos.theta, node.pos.rho).getc(true);\n    } else {\n      console.error('getNodeXY: unrecognized node pos format');\n      return {};\n    }\n  },\n  selectWithBox: function selectWithBox(e) {\n    var self = this;\n    var sX = _Mouse2.default.boxStartCoordinates.x;\n    var sY = _Mouse2.default.boxStartCoordinates.y;\n    var eX = _Mouse2.default.boxEndCoordinates.x;\n    var eY = _Mouse2.default.boxEndCoordinates.y;\n\n    if (!e.shiftKey) {\n      _Control2.default.deselectAllNodes();\n      _Control2.default.deselectAllEdges();\n    }\n\n    // select all nodes that are within the box\n    _Visualize2.default.mGraph.graph.eachNode(function (n) {\n      var pos = self.getNodeXY(n);\n      var x = pos.x;\n      var y = pos.y;\n\n      // depending on which way the person dragged the box, check that\n      // x and y are between the start and end values of the box\n      if (sX < x && x < eX && sY < y && y < eY ||\n      sX > x && x > eX && sY > y && y > eY ||\n      sX > x && x > eX && sY < y && y < eY ||\n      sX < x && x < eX && sY > y && y > eY) {\n        if (e.shiftKey) {\n          if (n.selected) {\n            _Control2.default.deselectNode(n);\n          } else {\n            _Control2.default.selectNode(n, e);\n          }\n        } else {\n          _Control2.default.selectNode(n, e);\n        }\n      }\n    });\n\n    // Convert selection box coordinates to traditional coordinates (+,+) in upper right\n    sY = -1 * sY;\n    eY = -1 * eY;\n\n    var edgesToToggle = [];\n    _DataModel2.default.Synapses.each(function (synapse) {\n      var e = synapse.get('edge');\n      if (edgesToToggle.indexOf(e) === -1) {\n        edgesToToggle.push(e);\n      }\n    });\n    edgesToToggle.forEach(function (edge) {\n      var fromNodePos = self.getNodeXY(edge.nodeFrom);\n      var fromNodeX = fromNodePos.x;\n      var fromNodeY = -1 * fromNodePos.y;\n      var toNodePos = self.getNodeXY(edge.nodeTo);\n      var toNodeX = toNodePos.x;\n      var toNodeY = -1 * toNodePos.y;\n\n      var maxX = fromNodeX;\n      var maxY = fromNodeY;\n      var minX = fromNodeX;\n      var minY = fromNodeY\n\n      // Correct maxX, MaxY values\n      ;toNodeX > maxX ? maxX = toNodeX : minX = toNodeX;\n      toNodeY > maxY ? maxY = toNodeY : minY = toNodeY;\n\n      var maxBoxX = sX;\n      var maxBoxY = sY;\n      var minBoxX = sX;\n      var minBoxY = sY\n\n      // Correct maxBoxX, maxBoxY values\n      ;eX > maxBoxX ? maxBoxX = eX : minBoxX = eX;\n      eY > maxBoxY ? maxBoxY = eY : minBoxY = eY;\n\n      // Find the slopes from the synapse fromNode to the 4 corners of the selection box\n      var slopes = [];\n      slopes.push((sY - fromNodeY) / (sX - fromNodeX));\n      slopes.push((sY - fromNodeY) / (eX - fromNodeX));\n      slopes.push((eY - fromNodeY) / (eX - fromNodeX));\n      slopes.push((eY - fromNodeY) / (sX - fromNodeX));\n\n      var minSlope = slopes[0];\n      var maxSlope = slopes[0];\n      slopes.forEach(function (entry) {\n        if (entry > maxSlope) maxSlope = entry;\n        if (entry < minSlope) minSlope = entry;\n      });\n\n      // Find synapse-in-question's slope\n      var synSlope = (toNodeY - fromNodeY) / (toNodeX - fromNodeX);\n      var b = fromNodeY - synSlope * fromNodeX;\n\n      // Use the selection box edges as test cases for synapse intersection\n      var testX = sX;\n      var testY = synSlope * testX + b;\n\n      var selectTest = void 0;\n\n      if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testY >= minBoxY && testY <= maxBoxY) {\n        selectTest = true;\n      }\n\n      testX = eX;\n      testY = synSlope * testX + b;\n\n      if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testY >= minBoxY && testY <= maxBoxY) {\n        selectTest = true;\n      }\n\n      testY = sY;\n      testX = (testY - b) / synSlope;\n\n      if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testX >= minBoxX && testX <= maxBoxX) {\n        selectTest = true;\n      }\n\n      testY = eY;\n      testX = (testY - b) / synSlope;\n\n      if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testX >= minBoxX && testX <= maxBoxX) {\n        selectTest = true;\n      }\n\n      // Case where the synapse is wholly enclosed in the seldction box\n      if (fromNodeX >= minBoxX && fromNodeX <= maxBoxX && fromNodeY >= minBoxY && fromNodeY <= maxBoxY && toNodeX >= minBoxX && toNodeX <= maxBoxX && toNodeY >= minBoxY && toNodeY <= maxBoxY) {\n        selectTest = true;\n      }\n\n      // The test synapse was selected!\n\n      if (selectTest) {\n        // shiftKey = toggleSelect, otherwise\n        if (e.shiftKey) {\n          if (_Selected2.default.Edges.indexOf(edge) !== -1) {\n            _Control2.default.deselectEdge(edge);\n          } else {\n            _Control2.default.selectEdge(edge);\n          }\n        } else {\n          _Control2.default.selectEdge(edge);\n        }\n      }\n    });\n    _Mouse2.default.boxStartCoordinates = false;\n    _Mouse2.default.boxEndCoordinates = false;\n    _Visualize2.default.mGraph.plot();\n  }, // selectWithBox\n  selectNodeOnClickHandler: function selectNodeOnClickHandler(node, e) {\n    if (_Visualize2.default.mGraph.busy) return;\n\n    var self = JIT;\n\n    // Copy topic title to clipboard\n    if (e.button === 1 && e.ctrlKey) _clipboardJs2.default.copy(node.name);\n\n    // catch right click on mac, which is often like ctrl+click\n    if (navigator.platform.indexOf('Mac') !== -1 && e.ctrlKey) {\n      self.selectNodeOnRightClickHandler(node, e);\n      return;\n    }\n\n    // if on a topic page, let alt+click center you on a new topic\n    if (_Active2.default.Topic && e.altKey) {\n      JIT.RGraph.centerOn(node.id);\n      return;\n    }\n\n    var check = self.nodeWasDoubleClicked();\n    if (check) {\n      self.nodeDoubleClickHandler(node, e);\n      return;\n    } else {\n      // wait a certain length of time, then check again, then run this code\n      setTimeout(function () {\n        if (!JIT.nodeWasDoubleClicked()) {\n          if (e.button === 1 && !e.ctrlKey) {\n            var len = _Selected2.default.Nodes.length;\n\n            for (var i = 0; i < len; i += 1) {\n              var n = _Selected2.default.Nodes[i];\n              var result = _Util2.default.openLink(_DataModel2.default.Topics.get(n.id).attributes.link);\n\n              if (!result) {// if link failed to open\n                break;\n              }\n            }\n\n            if (!node.selected) _Util2.default.openLink(_DataModel2.default.Topics.get(node.id).attributes.link);\n          }\n        }\n      }, _Mouse2.default.DOUBLE_CLICK_TOLERANCE);\n    }\n  }, // selectNodeOnClickHandler\n  selectNodeOnRightClickHandler: function selectNodeOnRightClickHandler(node, e) {\n    // the 'node' variable is a JIT node, the one that was clicked on\n    // the 'e' variable is the click event\n\n    e.preventDefault();\n    e.stopPropagation();\n\n    if (_Visualize2.default.mGraph.busy) return;\n\n    // select the node\n    _Control2.default.selectNode(node, e);\n\n    // delete old right click menu\n    $('.rightclickmenu').remove();\n    // create new menu for clicked on node\n    var rightclickmenu = document.createElement('div');\n    rightclickmenu.className = 'rightclickmenu';\n    // prevent the custom context menu from immediately opening the default context menu as well\n    rightclickmenu.setAttribute('oncontextmenu', 'return false');\n\n    // add the proper options to the menu\n    var menustring = '<ul>';\n\n    var authorized = _Active2.default.Map && _Active2.default.Map.authorizeToEdit(_Active2.default.Mapper);\n\n    var disabled = authorized ? '' : 'disabled';\n\n    if (_Active2.default.Map) menustring += '<li class=\"rc-hide\"><div class=\"rc-icon\"></div>Hide until refresh<div class=\"rc-keyboard\">Ctrl+H</div></li>';\n    if (_Active2.default.Map && _Active2.default.Mapper) menustring += '<li class=\"rc-remove ' + disabled + '\"><div class=\"rc-icon\"></div>Remove from map<div class=\"rc-keyboard\">Ctrl+M</div></li>';\n    if (_Active2.default.Topic) menustring += '<li class=\"rc-remove\"><div class=\"rc-icon\"></div>Remove from view<div class=\"rc-keyboard\">Ctrl+M</div></li>';\n    if (_Active2.default.Map && _Active2.default.Mapper) menustring += '<li class=\"rc-delete ' + disabled + '\"><div class=\"rc-icon\"></div>Delete<div class=\"rc-keyboard\">Ctrl+D</div></li>';\n\n    if (_Active2.default.Topic) {\n      menustring += '<li class=\"rc-center\"><div class=\"rc-icon\"></div>Center this topic<div class=\"rc-keyboard\">Alt+E</div></li>';\n    }\n\n    menustring += '<li class=\"rc-popout\"><div class=\"rc-icon\"></div>Open in new tab</li>';\n\n    if (_Active2.default.Mapper) {\n      var options = (0, _outdent2.default)(_templateObject);\n\n\n\n\n\n\n      menustring += '<li class=\"rc-spacer\"></li>';\n\n      menustring += (0, _outdent2.default)(_templateObject2,\n\n\n\n      options);\n\n\n\n      var metacodeOptions = $('#metacodeOptions').html();\n\n      menustring += '<li class=\"rc-metacode\"><div class=\"rc-icon\"></div>Change metacode' + metacodeOptions + '<div class=\"expandLi\"></div></li>';\n    }\n    if (_Active2.default.Topic) {\n      if (!_Active2.default.Mapper) {\n        menustring += '<li class=\"rc-spacer\"></li>';\n      }\n\n      // set up the get sibling menu as a \"lazy load\"\n      // only fill in the submenu when they hover over the get siblings list item\n      var siblingMenu = (0, _outdent2.default)(_templateObject3);\n\n\n\n\n      menustring += '<li class=\"rc-siblings\"><div class=\"rc-icon\"></div>Reveal siblings' + siblingMenu + '<div class=\"expandLi\"></div></li>';\n    }\n\n    menustring += '</ul>';\n    rightclickmenu.innerHTML = menustring;\n\n    // position the menu where the click happened\n    var position = {};\n    var RIGHTCLICK_WIDTH = 300;\n    var RIGHTCLICK_HEIGHT = 144; // this does vary somewhat, but we can use static\n    var SUBMENUS_WIDTH = 256;\n    var MAX_SUBMENU_HEIGHT = 270;\n    var windowWidth = $(window).width();\n    var windowHeight = $(window).height();\n\n    if (windowWidth - e.clientX < SUBMENUS_WIDTH) {\n      position.right = windowWidth - e.clientX;\n      $(rightclickmenu).addClass('moveMenusToLeft');\n    } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) {\n      position.right = windowWidth - e.clientX;\n    } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) {\n      position.left = e.clientX;\n      $(rightclickmenu).addClass('moveMenusToLeft');\n    } else {\n      position.left = e.clientX;\n    }\n\n    if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) {\n      position.bottom = windowHeight - e.clientY;\n      $(rightclickmenu).addClass('moveMenusUp');\n    } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) {\n      position.top = e.clientY;\n      $(rightclickmenu).addClass('moveMenusUp');\n    } else {\n      position.top = e.clientY;\n    }\n\n    $(rightclickmenu).css(position);\n    // add the menu to the page\n    $('#wrapper').append(rightclickmenu);\n\n    // attach events to clicks on the list items\n\n    // delete the selected things from the database\n    if (authorized) {\n      $('.rc-delete').click(function () {\n        $('.rightclickmenu').remove();\n        _Control2.default.deleteSelected();\n      });\n    }\n\n    // remove the selected things from the map\n    if (_Active2.default.Topic || authorized) {\n      $('.rc-remove').click(function () {\n        $('.rightclickmenu').remove();\n        _Control2.default.removeSelectedEdges();\n        _Control2.default.removeSelectedNodes();\n      });\n    }\n\n    // hide selected nodes and synapses until refresh\n    $('.rc-hide').click(function () {\n      $('.rightclickmenu').remove();\n      _Control2.default.hideSelectedEdges();\n      _Control2.default.hideSelectedNodes();\n    });\n\n    // when in radial, center on the topic you picked\n    $('.rc-center').click(function () {\n      $('.rightclickmenu').remove();\n      _Topic2.default.centerOn(node.id);\n    });\n\n    // open the entity in a new tab\n    $('.rc-popout').click(function () {\n      $('.rightclickmenu').remove();\n      var win = window.open('/topics/' + node.id, '_blank');\n      win.focus();\n    });\n\n    // change the permission of all the selected nodes and synapses that you were the originator of\n    $('.rc-permission li').click(function () {\n      $('.rightclickmenu').remove();\n      // $(this).text() will be 'commons' 'public' or 'private'\n      _Control2.default.updateSelectedPermissions($(this).text());\n    });\n\n    // change the metacode of all the selected nodes that you have edit permission for\n    $('.rc-metacode li li').click(function () {\n      $('.rightclickmenu').remove();\n      //\n      _Control2.default.updateSelectedMetacodes($(this).attr('data-id'));\n    });\n\n    // fetch relatives\n    var fetchSent = false;\n    $('.rc-siblings').hover(function () {\n      if (!fetchSent) {\n        JIT.populateRightClickSiblings(node);\n        fetchSent = true;\n      }\n    });\n    $('.rc-siblings .fetchAll').click(function () {\n      $('.rightclickmenu').remove();\n      // data-id is a metacode id\n      _Topic2.default.fetchRelatives(node);\n    });\n  }, // selectNodeOnRightClickHandler,\n  populateRightClickSiblings: function populateRightClickSiblings(node) {\n    // depending on how many topics are selected, do different things\n    var topic = node.getData('topic');\n\n    // add a loading icon for now\n    var loader = new CanvasLoader('loadingSiblings');\n    loader.setColor('#4FC059'); // default is '#000000'\n    loader.setDiameter(15); // default is 40\n    loader.setDensity(41); // default is 40\n    loader.setRange(0.9); // default is 1.3\n    loader.show(); // Hidden by default\n\n    var topics = _DataModel2.default.Topics.map(function (t) {return t.id;});\n    var topicsString = topics.join();\n\n    var successCallback = function successCallback(data) {\n      $('#loadingSiblings').remove();\n\n      for (var key in data) {\n        var string = _DataModel2.default.Metacodes.get(key).get('name') + ' (' + data[key] + ')';\n        $('#fetchSiblingList').append('<li class=\"getSiblings\" data-id=\"' + key + '\">' + string + '</li>');\n      }\n\n      $('.rc-siblings .getSiblings').click(function () {\n        $('.rightclickmenu').remove();\n        // data-id is a metacode id\n        _Topic2.default.fetchRelatives(node, $(this).attr('data-id'));\n      });\n    };\n\n    $.ajax({\n      type: 'GET',\n      url: '/topics/' + topic.id + '/relative_numbers.json?network=' + topicsString,\n      success: successCallback,\n      error: function error() {} });\n\n  },\n  selectEdgeOnClickHandler: function selectEdgeOnClickHandler(adj, e) {\n    if (_Visualize2.default.mGraph.busy) return;\n\n    var self = JIT;\n    var synapseText = adj.data.$synapses[0].attributes.desc;\n    // Copy synapse label to clipboard\n    if (e.button === 1 && e.ctrlKey && synapseText !== '') _clipboardJs2.default.copy(synapseText);\n\n    // catch right click on mac, which is often like ctrl+click\n    if (navigator.platform.indexOf('Mac') !== -1 && e.ctrlKey) {\n      self.selectEdgeOnRightClickHandler(adj, e);\n      return;\n    }\n\n    var check = self.nodeWasDoubleClicked();\n    if (check) {\n      self.edgeDoubleClickHandler(adj, e);\n      return;\n    } else {\n      // wait a certain length of time, then check again, then run this code\n      setTimeout(function () {\n        if (!JIT.nodeWasDoubleClicked()) {\n          var edgeAlreadySelected = _Selected2.default.Edges.indexOf(adj) !== -1;\n\n          if (!e.shiftKey) {\n            _Control2.default.deselectAllNodes();\n            _Control2.default.deselectAllEdges();\n          }\n\n          if (edgeAlreadySelected) {\n            _Control2.default.deselectEdge(adj);\n          } else {\n            _Control2.default.selectEdge(adj);\n          }\n\n          _Visualize2.default.mGraph.plot();\n        }\n      }, _Mouse2.default.DOUBLE_CLICK_TOLERANCE);\n    }\n  }, // selectEdgeOnClickHandler\n  selectEdgeOnRightClickHandler: function selectEdgeOnRightClickHandler(adj, e) {\n    // the 'node' variable is a JIT node, the one that was clicked on\n    // the 'e' variable is the click event\n\n    if (adj.getData('alpha') === 0) return; // don't do anything if the edge is filtered\n\n    e.preventDefault();\n    e.stopPropagation();\n\n    if (_Visualize2.default.mGraph.busy) return;\n\n    _Control2.default.selectEdge(adj);\n\n    // delete old right click menu\n    $('.rightclickmenu').remove();\n    // create new menu for clicked on node\n    var rightclickmenu = document.createElement('div');\n    rightclickmenu.className = 'rightclickmenu';\n    // prevent the custom context menu from immediately opening the default context menu as well\n    rightclickmenu.setAttribute('oncontextmenu', 'return false');\n\n    // add the proper options to the menu\n    var menustring = '<ul>';\n\n    var authorized = _Active2.default.Map && _Active2.default.Map.authorizeToEdit(_Active2.default.Mapper);\n\n    var disabled = authorized ? '' : 'disabled';\n\n    if (_Active2.default.Map) menustring += '<li class=\"rc-hide\"><div class=\"rc-icon\"></div>Hide until refresh<div class=\"rc-keyboard\">Ctrl+H</div></li>';\n    if (_Active2.default.Map && _Active2.default.Mapper) menustring += '<li class=\"rc-remove ' + disabled + '\"><div class=\"rc-icon\"></div>Remove from map<div class=\"rc-keyboard\">Ctrl+M</div></li>';\n    if (_Active2.default.Topic) menustring += '<li class=\"rc-remove\"><div class=\"rc-icon\"></div>Remove from view<div class=\"rc-keyboard\">Ctrl+M</div></li>';\n    if (_Active2.default.Map && _Active2.default.Mapper) menustring += '<li class=\"rc-delete ' + disabled + '\"><div class=\"rc-icon\"></div>Delete<div class=\"rc-keyboard\">Ctrl+D</div></li>';\n\n    if (_Active2.default.Map && _Active2.default.Mapper) menustring += '<li class=\"rc-spacer\"></li>';\n\n    if (_Active2.default.Mapper) {\n      var permOptions = (0, _outdent2.default)(_templateObject4);\n\n\n\n\n      menustring += '<li class=\"rc-permission\"><div class=\"rc-icon\"></div>Change permissions' + permOptions + '<div class=\"expandLi\"></div></li>';\n    }\n\n    menustring += '</ul>';\n    rightclickmenu.innerHTML = menustring;\n\n    // position the menu where the click happened\n    var position = {};\n    var RIGHTCLICK_WIDTH = 300;\n    var RIGHTCLICK_HEIGHT = 144; // this does vary somewhat, but we can use static\n    var SUBMENUS_WIDTH = 256;\n    var MAX_SUBMENU_HEIGHT = 270;\n    var windowWidth = $(window).width();\n    var windowHeight = $(window).height();\n\n    if (windowWidth - e.clientX < SUBMENUS_WIDTH) {\n      position.right = windowWidth - e.clientX;\n      $(rightclickmenu).addClass('moveMenusToLeft');\n    } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) {\n      position.right = windowWidth - e.clientX;\n    } else position.left = e.clientX;\n\n    if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) {\n      position.bottom = windowHeight - e.clientY;\n      $(rightclickmenu).addClass('moveMenusUp');\n    } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) {\n      position.top = e.clientY;\n      $(rightclickmenu).addClass('moveMenusUp');\n    } else position.top = e.clientY;\n\n    $(rightclickmenu).css(position);\n\n    // add the menu to the page\n    $('#wrapper').append(rightclickmenu);\n\n    // attach events to clicks on the list items\n\n    // delete the selected things from the database\n    if (authorized) {\n      $('.rc-delete').click(function () {\n        $('.rightclickmenu').remove();\n        _Control2.default.deleteSelected();\n      });\n    }\n\n    // remove the selected things from the map\n    if (authorized) {\n      $('.rc-remove').click(function () {\n        $('.rightclickmenu').remove();\n        _Control2.default.removeSelectedEdges();\n        _Control2.default.removeSelectedNodes();\n      });\n    }\n\n    // hide selected nodes and synapses until refresh\n    $('.rc-hide').click(function () {\n      $('.rightclickmenu').remove();\n      _Control2.default.hideSelectedEdges();\n      _Control2.default.hideSelectedNodes();\n    });\n\n    // change the permission of all the selected nodes and synapses that you were the originator of\n    $('.rc-permission li').click(function () {\n      $('.rightclickmenu').remove();\n      // $(this).text() will be 'commons' 'public' or 'private'\n      _Control2.default.updateSelectedPermissions($(this).text());\n    });\n  }, // selectEdgeOnRightClickHandler\n  SmoothPanning: function SmoothPanning() {\n    var sx = _Visualize2.default.mGraph.canvas.scaleOffsetX;\n    var sy = _Visualize2.default.mGraph.canvas.scaleOffsetY;\n    var yVelocity = _Mouse2.default.changeInY; // initial y velocity\n    var xVelocity = _Mouse2.default.changeInX; // initial x velocity\n    var easing = 1; // frictional value\n\n    window.clearInterval(panningInt);\n    panningInt = setInterval(function () {\n      myTimer();\n    }, 1);\n\n    function myTimer() {\n      _Visualize2.default.mGraph.canvas.translate(xVelocity * easing * 1 / sx, yVelocity * easing * 1 / sy);\n      $(document).trigger(JIT.events.pan);\n      easing = easing * 0.75;\n\n      if (easing < 0.1) window.clearInterval(panningInt);\n    }\n  }, // SmoothPanning\n  renderMidArrow: function renderMidArrow(from, to, dim, swap, canvas, placement, newSynapse) {\n    var ctx = canvas.getCtx();\n    // invert edge direction\n    if (swap) {\n      var tmp = from;\n      from = to;\n      to = tmp;\n    }\n    // vect represents a line from tip to tail of the arrow\n    var vect = new _JIT2.default.Complex(to.x - from.x, to.y - from.y);\n    // scale it\n    vect.$scale(dim / vect.norm());\n    // compute the midpoint of the edge line\n    var newX = (to.x - from.x) * placement + from.x;\n    var newY = (to.y - from.y) * placement + from.y;\n    var midPoint = new _JIT2.default.Complex(newX, newY);\n\n    // move midpoint by half the \"length\" of the arrow so the arrow is centered on the midpoint\n    var arrowPoint = new _JIT2.default.Complex(vect.x / 0.7 + midPoint.x, vect.y / 0.7 + midPoint.y);\n    // compute the tail intersection point with the edge line\n    var intermediatePoint = new _JIT2.default.Complex(arrowPoint.x - vect.x, arrowPoint.y - vect.y);\n    // vector perpendicular to vect\n    var normal = new _JIT2.default.Complex(-vect.y / 2, vect.x / 2);\n    var v1 = intermediatePoint.add(normal);\n    var v2 = intermediatePoint.$add(normal.$scale(-1));\n\n    if (newSynapse) {\n      ctx.strokeStyle = '#4fc059';\n      ctx.lineWidth = 2;\n      ctx.globalAlpha = 1;\n    }\n    ctx.beginPath();\n    ctx.moveTo(from.x, from.y);\n    ctx.lineTo(to.x, to.y);\n    ctx.stroke();\n    ctx.beginPath();\n    ctx.moveTo(v1.x, v1.y);\n    ctx.lineTo(arrowPoint.x, arrowPoint.y);\n    ctx.lineTo(v2.x, v2.y);\n    ctx.stroke();\n  }, // renderMidArrow\n  renderEdgeArrows: function renderEdgeArrows(edgeHelper, adj, synapse, canvas) {\n    var self = JIT;\n\n    var directionCat = synapse.get('category');\n    var direction = synapse.getDirection();\n\n    var pos = adj.nodeFrom.pos.getc(true);\n    var posChild = adj.nodeTo.pos.getc(true);\n\n    // plot arrow edge\n    if (!direction) {\n      // render nothing for this arrow if the direction couldn't be retrieved\n    } else if (directionCat === 'none') {\n      edgeHelper.line.render({\n        x: pos.x,\n        y: pos.y },\n      {\n        x: posChild.x,\n        y: posChild.y },\n      canvas);\n    } else if (directionCat === 'both') {\n      self.renderMidArrow({\n        x: pos.x,\n        y: pos.y },\n      {\n        x: posChild.x,\n        y: posChild.y },\n      13, true, canvas, 0.7);\n      self.renderMidArrow({\n        x: pos.x,\n        y: pos.y },\n      {\n        x: posChild.x,\n        y: posChild.y },\n      13, false, canvas, 0.7);\n    } else if (directionCat === 'from-to') {\n      var inv = direction[0] !== adj.nodeFrom.id;\n      self.renderMidArrow({\n        x: pos.x,\n        y: pos.y },\n      {\n        x: posChild.x,\n        y: posChild.y },\n      13, inv, canvas, 0.7);\n      self.renderMidArrow({\n        x: pos.x,\n        y: pos.y },\n      {\n        x: posChild.x,\n        y: posChild.y },\n      13, inv, canvas, 0.3);\n    }\n  }, // renderEdgeArrows\n  zoomIn: function zoomIn(event) {\n    _Visualize2.default.mGraph.canvas.scale(1.25, 1.25);\n    $(document).trigger(JIT.events.zoom, [event]);\n  },\n  zoomOut: function zoomOut(event) {\n    _Visualize2.default.mGraph.canvas.scale(0.8, 0.8);\n    $(document).trigger(JIT.events.zoom, [event]);\n  },\n  centerMap: function centerMap(canvas) {\n    var offsetScale = canvas.scaleOffsetX;\n\n    canvas.scale(1 / offsetScale, 1 / offsetScale);\n\n    var offsetX = canvas.translateOffsetX;\n    var offsetY = canvas.translateOffsetY;\n\n    canvas.translate(-1 * offsetX, -1 * offsetY);\n  },\n  zoomToBox: function zoomToBox(event) {\n    var sX = _Mouse2.default.boxStartCoordinates.x;\n    var sY = _Mouse2.default.boxStartCoordinates.y;\n    var eX = _Mouse2.default.boxEndCoordinates.x;\n    var eY = _Mouse2.default.boxEndCoordinates.y;\n\n    var canvas = _Visualize2.default.mGraph.canvas;\n    JIT.centerMap(canvas);\n\n    var height = $(document).height();\n    var width = $(document).width();\n\n    var spanX = Math.abs(sX - eX);\n    var spanY = Math.abs(sY - eY);\n    var ratioX = width / spanX;\n    var ratioY = height / spanY;\n\n    var newRatio = Math.min(ratioX, ratioY);\n\n    if (canvas.scaleOffsetX * newRatio <= 5 && canvas.scaleOffsetX * newRatio >= 0.2) {\n      canvas.scale(newRatio, newRatio);\n    } else if (canvas.scaleOffsetX * newRatio > 5) {\n      newRatio = 5 / canvas.scaleOffsetX;\n      canvas.scale(newRatio, newRatio);\n    } else {\n      newRatio = 0.2 / canvas.scaleOffsetX;\n      canvas.scale(newRatio, newRatio);\n    }\n\n    var cogX = (sX + eX) / 2;\n    var cogY = (sY + eY) / 2;\n\n    canvas.translate(-1 * cogX, -1 * cogY);\n    $(document).trigger(JIT.events.zoom, [event]);\n\n    _Mouse2.default.boxStartCoordinates = false;\n    _Mouse2.default.boxEndCoordinates = false;\n    _Visualize2.default.mGraph.plot();\n  },\n  zoomExtents: function zoomExtents(event, canvas, denySelected) {\n    JIT.centerMap(canvas);\n    var height = canvas.getSize().height;\n    var width = canvas.getSize().width;\n    var maxX = void 0;\n    var maxY = void 0;\n    var minX = void 0;\n    var minY = void 0;\n    var counter = 0;\n\n    var nodes = void 0;\n    if (!denySelected && _Selected2.default.Nodes.length > 0) {\n      nodes = _Selected2.default.Nodes;\n    } else {\n      nodes = (0, _values3.default)(_Visualize2.default.mGraph.graph.nodes);\n    }\n\n    if (nodes.length > 1) {\n      nodes.forEach(function (n) {\n        var x = n.pos.x;\n        var y = n.pos.y;\n\n        if (counter === 0 && n.getData('alpha') === 1) {\n          maxX = x;\n          minX = x;\n          maxY = y;\n          minY = y;\n        }\n\n        var arrayOfLabelLines = _Util2.default.splitLine(n.name, 25).split('\\n');\n        var dim = n.getData('dim');\n        var ctx = canvas.getCtx();\n\n        var height = 25 * arrayOfLabelLines.length;\n\n        var lineWidths = [];\n        for (var index = 0; index < arrayOfLabelLines.length; ++index) {\n          lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width);\n        }\n        var width = Math.max.apply(null, lineWidths) + 8;\n\n        // only adjust these values if the node is not filtered\n        if (n.getData('alpha') === 1) {\n          maxX = Math.max(x + width / 2, maxX);\n          maxY = Math.max(y + n.getData('height') + 5 + height, maxY);\n          minX = Math.min(x - width / 2, minX);\n          minY = Math.min(y - dim, minY);\n\n          counter++;\n        }\n      });\n\n      var spanX = maxX - minX;\n      var spanY = maxY - minY;\n      var ratioX = spanX / width;\n      var ratioY = spanY / height;\n\n      var cogX = (maxX + minX) / 2;\n      var cogY = (maxY + minY) / 2;\n\n      canvas.translate(-1 * cogX, -1 * cogY);\n\n      var newRatio = Math.max(ratioX, ratioY);\n      var scaleMultiplier = 1 / newRatio * 0.9;\n\n      if (canvas.scaleOffsetX * scaleMultiplier <= 3 && canvas.scaleOffsetX * scaleMultiplier >= 0.2) {\n        canvas.scale(scaleMultiplier, scaleMultiplier);\n      } else if (canvas.scaleOffsetX * scaleMultiplier > 3) {\n        scaleMultiplier = 3 / canvas.scaleOffsetX;\n        canvas.scale(scaleMultiplier, scaleMultiplier);\n      } else {\n        scaleMultiplier = 0.2 / canvas.scaleOffsetX;\n        canvas.scale(scaleMultiplier, scaleMultiplier);\n      }\n\n      $(document).trigger(JIT.events.zoom, [event]);\n    } else if (nodes.length === 1) {\n      nodes.forEach(function (n) {\n        var x = n.pos.x;\n        var y = n.pos.y;\n\n        canvas.translate(-1 * x, -1 * y);\n        $(document).trigger(JIT.events.zoom, [event]);\n      });\n    }\n  } };exports.default =\n\n\nJIT;//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMzU1LmpzIiwic291cmNlcyI6WyJmcm9udGVuZC9zcmMvTWV0YW1hcHMvSklULmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGdsb2JhbCAkLCBJbWFnZSwgQ2FudmFzTG9hZGVyICovXG5cbmltcG9ydCBfIGZyb20gJ2xvZGFzaCdcbmltcG9ydCBvdXRkZW50IGZyb20gJ291dGRlbnQnXG5cbmltcG9ydCAkaml0IGZyb20gJy4uL3BhdGNoZWQvSklUJ1xuXG5pbXBvcnQgQWN0aXZlIGZyb20gJy4vQWN0aXZlJ1xuaW1wb3J0IENvbnRyb2wgZnJvbSAnLi9Db250cm9sJ1xuaW1wb3J0IENyZWF0ZSBmcm9tICcuL0NyZWF0ZSdcbmltcG9ydCBEYXRhTW9kZWwgZnJvbSAnLi9EYXRhTW9kZWwnXG5pbXBvcnQgRW5naW5lIGZyb20gJy4vRW5naW5lJ1xuaW1wb3J0IEZpbHRlciBmcm9tICcuL0ZpbHRlcidcbmltcG9ydCBHbG9iYWxVSSBmcm9tICcuL0dsb2JhbFVJJ1xuaW1wb3J0IE1hcCBmcm9tICcuL01hcCdcbmltcG9ydCBNb3VzZSBmcm9tICcuL01vdXNlJ1xuaW1wb3J0IFNlbGVjdGVkIGZyb20gJy4vU2VsZWN0ZWQnXG5pbXBvcnQgU2V0dGluZ3MgZnJvbSAnLi9TZXR0aW5ncydcbmltcG9ydCBTeW5hcHNlIGZyb20gJy4vU3luYXBzZSdcbmltcG9ydCBTeW5hcHNlQ2FyZCBmcm9tICcuL1N5bmFwc2VDYXJkJ1xuaW1wb3J0IFRvcGljIGZyb20gJy4vVG9waWMnXG5pbXBvcnQgVG9waWNDYXJkIGZyb20gJy4vVG9waWNDYXJkJ1xuaW1wb3J0IFV0aWwgZnJvbSAnLi9VdGlsJ1xuaW1wb3J0IFZpc3VhbGl6ZSBmcm9tICcuL1Zpc3VhbGl6ZSdcbmltcG9ydCBjbGlwYm9hcmQgZnJvbSAnY2xpcGJvYXJkLWpzJ1xuXG5sZXQgcGFubmluZ0ludFxuXG5jb25zdCBKSVQgPSB7XG4gIHRlbXBJbml0OiBmYWxzZSxcbiAgdGVtcE5vZGU6IG51bGwsXG4gIHRlbXBOb2RlMjogbnVsbCxcbiAgbW91c2VEb3duUGl4OiB7fSxcbiAgZHJhZ0ZsYWc6IDAsXG4gIGRyYWdUb2xlcmFuY2U6IDAsXG4gIHZpcnR1YWxQb2ludGVyOiB7fSxcblxuICBldmVudHM6IHtcbiAgICB0b3BpY0RyYWc6ICdNZXRhbWFwczpKSVQ6ZXZlbnRzOnRvcGljRHJhZycsXG4gICAgcGFuOiAnTWV0YW1hcHM6SklUOmV2ZW50czpwYW4nLFxuICAgIHpvb206ICdNZXRhbWFwczpKSVQ6ZXZlbnRzOnpvb20nLFxuICAgIGFuaW1hdGlvbkRvbmU6ICdNZXRhbWFwczpKSVQ6ZXZlbnRzOmFuaW1hdGlvbkRvbmUnXG4gIH0sXG4gIHZpekRhdGE6IFtdLCAvLyBjb250YWlucyB0aGUgdmlzdWFsaXphdGlvbi1jb21wYXRpYmxlIGdyYXBoXG4gIC8qKlxuICAgKiBUaGlzIG1ldGhvZCB3aWxsIGJpbmQgdGhlIGV2ZW50IGhhbmRsZXJzIGl0IGlzIGludGVyZXN0ZWQgYW5kIGluaXRpYWxpemUgdGhlIGNsYXNzLlxuICAgKi9cbiAgaW5pdDogZnVuY3Rpb24oc2VydmVyRGF0YSkge1xuICAgIGNvbnN0IHNlbGYgPSBKSVRcblxuICAgICQoJy56b29tSW4nKS5jbGljayhzZWxmLnpvb21JbilcbiAgICAkKCcuem9vbU91dCcpLmNsaWNrKHNlbGYuem9vbU91dClcblxuICAgIGNvbnN0IHpvb21FeHRlbnRzID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgIHNlbGYuem9vbUV4dGVudHMoZXZlbnQsIFZpc3VhbGl6ZS5tR3JhcGguY2FudmFzKVxuICAgIH1cbiAgICAkKCcuem9vbUV4dGVudHMnKS5jbGljayh6b29tRXh0ZW50cylcblxuICAgICQoJy50YWtlU2NyZWVuc2hvdCcpLmNsaWNrKE1hcC5leHBvcnRJbWFnZSlcblxuICAgIHNlbGYudG9waWNEZXNjSW1hZ2UgPSBuZXcgSW1hZ2UoKVxuICAgIHNlbGYudG9waWNEZXNjSW1hZ2Uuc3JjID0gc2VydmVyRGF0YVsndG9waWNfZGVzY3JpcHRpb25fc2lnbmlmaWVyLnBuZyddXG5cbiAgICBzZWxmLnRvcGljTGlua0ltYWdlID0gbmV3IEltYWdlKClcbiAgICBzZWxmLnRvcGljTGlua0ltYWdlLnNyYyA9IHNlcnZlckRhdGFbJ3RvcGljX2xpbmtfc2lnbmlmaWVyLnBuZyddXG4gIH0sXG4gIC8qKlxuICAgKiBjb252ZXJ0IG91ciB0b3BpYyBKU09OIGludG8gc29tZXRoaW5nIEpJVCBjYW4gdXNlXG4gICAqL1xuICBjb252ZXJ0TW9kZWxzVG9KSVQ6IGZ1bmN0aW9uKHRvcGljcywgc3luYXBzZXMpIHtcbiAgICBjb25zdCBqaXRSZWFkeSA9IFtdXG5cbiAgICBjb25zdCBzeW5hcHNlc1RvUmVtb3ZlID0gW11cbiAgICBsZXQgbWFwcGluZ1xuICAgIGxldCBub2RlXG4gICAgY29uc3Qgbm9kZXMgPSB7fVxuICAgIGxldCBleGlzdGluZ0VkZ2VcbiAgICBsZXQgZWRnZVxuICAgIGNvbnN0IGVkZ2VzID0gW11cblxuICAgIHRvcGljcy5lYWNoKGZ1bmN0aW9uKHQpIHtcbiAgICAgIG5vZGUgPSB0LmNyZWF0ZU5vZGUoKVxuICAgICAgbm9kZXNbbm9kZS5pZF0gPSBub2RlXG4gICAgfSlcbiAgICBzeW5hcHNlcy5lYWNoKGZ1bmN0aW9uKHMpIHtcbiAgICAgIGVkZ2UgPSBzLmNyZWF0ZUVkZ2UoKVxuXG4gICAgICBpZiAodG9waWNzLmdldChzLmdldCgndG9waWMxX2lkJykpID09PSB1bmRlZmluZWQgfHwgdG9waWNzLmdldChzLmdldCgndG9waWMyX2lkJykpID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgLy8gdGhpcyBtZWFucyBpdCdzIGFuIGludmFsaWQgc3luYXBzZVxuICAgICAgICBzeW5hcHNlc1RvUmVtb3ZlLnB1c2gocylcbiAgICAgIH0gZWxzZSBpZiAobm9kZXNbZWRnZS5ub2RlRnJvbV0gJiYgbm9kZXNbZWRnZS5ub2RlVG9dKSB7XG4gICAgICAgIGV4aXN0aW5nRWRnZSA9IF8uZmluZChlZGdlcywge1xuICAgICAgICAgIG5vZGVGcm9tOiBlZGdlLm5vZGVGcm9tLFxuICAgICAgICAgIG5vZGVUbzogZWRnZS5ub2RlVG9cbiAgICAgICAgfSkgfHxcbiAgICAgICAgXy5maW5kKGVkZ2VzLCB7XG4gICAgICAgICAgbm9kZUZyb206IGVkZ2Uubm9kZVRvLFxuICAgICAgICAgIG5vZGVUbzogZWRnZS5ub2RlRnJvbVxuICAgICAgICB9KVxuXG4gICAgICAgIGlmIChleGlzdGluZ0VkZ2UpIHtcbiAgICAgICAgICAvLyBmb3Igd2hlbiB5b3UncmUgZGVhbGluZyB3aXRoIG11bHRpcGxlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgc2FtZSB0d28gdG9waWNzXG4gICAgICAgICAgaWYgKEFjdGl2ZS5NYXApIHtcbiAgICAgICAgICAgIG1hcHBpbmcgPSBzLmdldE1hcHBpbmcoKVxuICAgICAgICAgICAgZXhpc3RpbmdFZGdlLmRhdGFbJyRtYXBwaW5nSURzJ10ucHVzaChtYXBwaW5nLmlkKVxuICAgICAgICAgIH1cbiAgICAgICAgICBleGlzdGluZ0VkZ2UuZGF0YVsnJHN5bmFwc2VJRHMnXS5wdXNoKHMuaWQpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gZm9yIHdoZW4geW91J3JlIGRlYWxpbmcgd2l0aCBhIHRvcGljIHRoYXQgaGFzIHJlbGF0aW9uc2hpcHMgdG8gbWFueSBkaWZmZXJlbnQgbm9kZXNcbiAgICAgICAgICBub2Rlc1tlZGdlLm5vZGVGcm9tXS5hZGphY2VuY2llcy5wdXNoKGVkZ2UpXG4gICAgICAgICAgZWRnZXMucHVzaChlZGdlKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSlcblxuICAgIF8uZWFjaChub2RlcywgZnVuY3Rpb24obm9kZSkge1xuICAgICAgaml0UmVhZHkucHVzaChub2RlKVxuICAgIH0pXG5cbiAgICByZXR1cm4gW2ppdFJlYWR5LCBzeW5hcHNlc1RvUmVtb3ZlXVxuICB9LFxuICBwcmVwYXJlVml6RGF0YTogZnVuY3Rpb24oKSB7XG4gICAgY29uc3Qgc2VsZiA9IEpJVFxuICAgIGxldCBtYXBwaW5nXG5cbiAgICAvLyByZXNldC9lbXB0eSB2aXpEYXRhXG4gICAgc2VsZi52aXpEYXRhID0gW11cbiAgICBWaXN1YWxpemUubG9hZExhdGVyID0gZmFsc2VcblxuICAgIGNvbnN0IHJlc3VsdHMgPSBzZWxmLmNvbnZlcnRNb2RlbHNUb0pJVChEYXRhTW9kZWwuVG9waWNzLCBEYXRhTW9kZWwuU3luYXBzZXMpXG5cbiAgICBzZWxmLnZpekRhdGEgPSByZXN1bHRzWzBdXG5cbiAgICAvLyBjbGVhbiB1cCB0aGUgc3luYXBzZXMgYXJyYXkgaW4gY2FzZSBvZiBhbnkgZmF1bHR5IGRhdGFcbiAgICBfLmVhY2gocmVzdWx0c1sxXSwgZnVuY3Rpb24oc3luYXBzZSkge1xuICAgICAgbWFwcGluZyA9IHN5bmFwc2UuZ2V0TWFwcGluZygpXG4gICAgICBEYXRhTW9kZWwuU3luYXBzZXMucmVtb3ZlKHN5bmFwc2UpXG4gICAgICBpZiAoRGF0YU1vZGVsLk1hcHBpbmdzKSBEYXRhTW9kZWwuTWFwcGluZ3MucmVtb3ZlKG1hcHBpbmcpXG4gICAgfSlcblxuICAgIC8vIHNldCB1cCBhZGRUb3BpYyBpbnN0cnVjdGlvbnMgaW4gY2FzZSB0aGV5IGRlbGV0ZSBhbGwgdGhlIHRvcGljc1xuICAgIC8vIGkuZS4gaWYgdGhlcmUgYXJlIDAgdG9waWNzIGF0IGFueSB0aW1lLCBpdCBzaG91bGQgaGF2ZSBpbnN0cnVjdGlvbnMgYWdhaW5cbiAgICAkKCcjaW5zdHJ1Y3Rpb25zIGRpdicpLmhpZGUoKVxuICAgIGlmIChBY3RpdmUuTWFwICYmIEFjdGl2ZS5NYXAuYXV0aG9yaXplVG9FZGl0KEFjdGl2ZS5NYXBwZXIpKSB7XG4gICAgICAkKCcjaW5zdHJ1Y3Rpb25zIGRpdi5hZGRUb3BpYycpLnNob3coKVxuICAgIH1cblxuICAgIGlmIChzZWxmLnZpekRhdGEubGVuZ3RoID09PSAwKSB7XG4gICAgICBHbG9iYWxVSS5zaG93RGl2KCcjaW5zdHJ1Y3Rpb25zJylcbiAgICAgIFZpc3VhbGl6ZS5sb2FkTGF0ZXIgPSB0cnVlXG4gICAgfSBlbHNlIHtcbiAgICAgIEdsb2JhbFVJLmhpZGVEaXYoJyNpbnN0cnVjdGlvbnMnKVxuICAgIH1cblxuICAgIFZpc3VhbGl6ZS5yZW5kZXIoKVxuICB9LCAvLyBwcmVwYXJlVml6RGF0YVxuICBlZGdlUmVuZGVyOiBmdW5jdGlvbihhZGosIGNhbnZhcykge1xuICAgIC8vIGdldCBub2RlcyBjYXJ0ZXNpYW4gY29vcmRpbmF0ZXNcbiAgICBjb25zdCBwb3MgPSBhZGoubm9kZUZyb20ucG9zLmdldGModHJ1ZSlcbiAgICBjb25zdCBwb3NDaGlsZCA9IGFkai5ub2RlVG8ucG9zLmdldGModHJ1ZSlcblxuICAgIGxldCBzeW5hcHNlXG4gICAgaWYgKGFkai5nZXREYXRhKCdkaXNwbGF5SW5kZXgnKSkge1xuICAgICAgc3luYXBzZSA9IGFkai5nZXREYXRhKCdzeW5hcHNlcycpW2Fkai5nZXREYXRhKCdkaXNwbGF5SW5kZXgnKV1cbiAgICAgIGlmICghc3luYXBzZSkge1xuICAgICAgICBkZWxldGUgYWRqLmRhdGEuJGRpc3BsYXlJbmRleFxuICAgICAgICBzeW5hcHNlID0gYWRqLmdldERhdGEoJ3N5bmFwc2VzJylbMF1cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgc3luYXBzZSA9IGFkai5nZXREYXRhKCdzeW5hcHNlcycpWzBdXG4gICAgfVxuXG4gICAgaWYgKCFzeW5hcHNlKSByZXR1cm4gLy8gdGhpcyBtZWFucyB0aGVyZSBhcmUgbm8gY29ycmVzcG9uZGluZyBzeW5hcHNlcyBmb3JcbiAgICAvLyB0aGlzIGVkZ2UsIGRvbid0IHJlbmRlciBpdFxuXG4gICAgLy8gbGFiZWwgcGxhY2VtZW50IG9uIGVkZ2VzXG4gICAgaWYgKGNhbnZhcy5kZW55U2VsZWN0ZWQpIHtcbiAgICAgIGNvbnN0IGNvbG9yID0gU2V0dGluZ3MuY29sb3JzLnN5bmFwc2VzLm5vcm1hbFxuICAgICAgY2FudmFzLmdldEN0eCgpLmZpbGxTdHlsZSA9IGNhbnZhcy5nZXRDdHgoKS5zdHJva2VTdHlsZSA9IGNvbG9yXG4gICAgfVxuICAgIEpJVC5yZW5kZXJFZGdlQXJyb3dzKCRqaXQuR3JhcGguUGxvdC5lZGdlSGVscGVyLCBhZGosIHN5bmFwc2UsIGNhbnZhcylcblxuICAgIC8vIGNoZWNrIGZvciBlZGdlIGxhYmVsIGluIGRhdGFcbiAgICBsZXQgZGVzYyA9IHN5bmFwc2UuZ2V0KCdkZXNjJylcblxuICAgIGNvbnN0IHNob3dEZXNjID0gYWRqLmdldERhdGEoJ3Nob3dEZXNjJylcblxuICAgIGNvbnN0IGRyYXdTeW5hcHNlQ291bnQgPSBmdW5jdGlvbihjb250ZXh0LCB4LCB5LCBjb3VudCkge1xuICAgICAgLypcbiAgICAgIGNpcmNsZSBzaXplOiAxNngxNnB4XG4gICAgICBwb3NpdGlvbmluZzogb3ZlcmxheSBhbmQgY2VudGVyIG9uIHRvcCByaWdodCBjb3JuZXIgb2Ygc3luYXBzZSBsYWJlbCAtIDhweCBsZWZ0IGFuZCA4cHggZG93blxuICAgICAgY29sb3I6ICNkYWI1MzlcbiAgICAgIGJvcmRlciBjb2xvcjogIzQyNDI0MlxuICAgICAgYm9yZGVyIHNpemU6IDEuNXB4XG4gICAgICBmb250OiBESU4gbWVkaXVtXG4gICAgICBmb250LXNpemU6IDE0cHRcbiAgICAgIGZvbnQtY29sb3I6ICM0MjQyNDJcbiAgICAgICovXG4gICAgICBjb250ZXh0LmJlZ2luUGF0aCgpXG4gICAgICBjb250ZXh0LmFyYyh4LCB5LCA4LCAwLCAyICogTWF0aC5QSSwgZmFsc2UpXG4gICAgICBjb250ZXh0LmZpbGxTdHlsZSA9ICcjREFCNTM5J1xuICAgICAgY29udGV4dC5zdHJva2VTdHlsZSA9ICcjNDI0MjQyJ1xuICAgICAgY29udGV4dC5saW5lV2lkdGggPSAxLjVcbiAgICAgIGNvbnRleHQuY2xvc2VQYXRoKClcbiAgICAgIGNvbnRleHQuZmlsbCgpXG4gICAgICBjb250ZXh0LnN0cm9rZSgpXG5cbiAgICAgIC8vIGFkZCB0aGUgc3luYXBzZSBjb3VudFxuICAgICAgY29udGV4dC5maWxsU3R5bGUgPSAnIzQyNDI0MidcbiAgICAgIGNvbnRleHQudGV4dEFsaWduID0gJ2NlbnRlcidcbiAgICAgIGNvbnRleHQuZm9udCA9ICcxNHB4IGRpbi1tZWRpdW0nXG5cbiAgICAgIGNvbnRleHQuZmlsbFRleHQoY291bnQsIHgsIHkgKyA1KVxuICAgIH1cblxuICAgIGlmICghY2FudmFzLmRlbnlTZWxlY3RlZCAmJiBkZXNjICE9PSAnJyAmJiBzaG93RGVzYykge1xuICAgICAgLy8gJyZhbXA7JyB0byAnJidcbiAgICAgIGRlc2MgPSBVdGlsLmRlY29kZUVudGl0aWVzKGRlc2MpXG5cbiAgICAgIC8vIG5vdyBhZGp1c3QgdGhlIGxhYmVsIHBsYWNlbWVudFxuICAgICAgY29uc3QgY3R4ID0gY2FudmFzLmdldEN0eCgpXG4gICAgICBjdHguZm9udCA9ICdib2xkIDE0cHggYXJpYWwnXG4gICAgICBjdHguZmlsbFN0eWxlID0gJyNGRkYnXG4gICAgICBjdHgudGV4dEJhc2VsaW5lID0gJ2FscGhhYmV0aWMnXG5cbiAgICAgIGNvbnN0IGFycmF5T2ZMYWJlbExpbmVzID0gVXRpbC5zcGxpdExpbmUoZGVzYywgMjUpLnNwbGl0KCdcXG4nKVxuICAgICAgbGV0IGxpbmVXaWR0aHMgPSBbXVxuICAgICAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IGFycmF5T2ZMYWJlbExpbmVzLmxlbmd0aDsgKytpbmRleCkge1xuICAgICAgICBsaW5lV2lkdGhzLnB1c2goY3R4Lm1lYXN1cmVUZXh0KGFycmF5T2ZMYWJlbExpbmVzW2luZGV4XSkud2lkdGgpXG4gICAgICB9XG4gICAgICBjb25zdCB3aWR0aCA9IE1hdGgubWF4LmFwcGx5KG51bGwsIGxpbmVXaWR0aHMpICsgMTZcbiAgICAgIGNvbnN0IGhlaWdodCA9ICgxNiAqIGFycmF5T2ZMYWJlbExpbmVzLmxlbmd0aCkgKyA4XG5cbiAgICAgIGNvbnN0IHggPSAocG9zLnggKyBwb3NDaGlsZC54IC0gd2lkdGgpIC8gMlxuICAgICAgY29uc3QgeSA9ICgocG9zLnkgKyBwb3NDaGlsZC55KSAvIDIpIC0gaGVpZ2h0IC8gMlxuXG4gICAgICBjb25zdCByYWRpdXMgPSA1XG5cbiAgICAgIC8vIHJlbmRlciBiYWNrZ3JvdW5kXG4gICAgICBjdHguYmVnaW5QYXRoKClcbiAgICAgIGN0eC5tb3ZlVG8oeCArIHJhZGl1cywgeSlcbiAgICAgIGN0eC5saW5lVG8oeCArIHdpZHRoIC0gcmFkaXVzLCB5KVxuICAgICAgY3R4LnF1YWRyYXRpY0N1cnZlVG8oeCArIHdpZHRoLCB5LCB4ICsgd2lkdGgsIHkgKyByYWRpdXMpXG4gICAgICBjdHgubGluZVRvKHggKyB3aWR0aCwgeSArIGhlaWdodCAtIHJhZGl1cylcbiAgICAgIGN0eC5xdWFkcmF0aWNDdXJ2ZVRvKHggKyB3aWR0aCwgeSArIGhlaWdodCwgeCArIHdpZHRoIC0gcmFkaXVzLCB5ICsgaGVpZ2h0KVxuICAgICAgY3R4LmxpbmVUbyh4ICsgcmFkaXVzLCB5ICsgaGVpZ2h0KVxuICAgICAgY3R4LnF1YWRyYXRpY0N1cnZlVG8oeCwgeSArIGhlaWdodCwgeCwgeSArIGhlaWdodCAtIHJhZGl1cylcbiAgICAgIGN0eC5saW5lVG8oeCwgeSArIHJhZGl1cylcbiAgICAgIGN0eC5xdWFkcmF0aWNDdXJ2ZVRvKHgsIHksIHggKyByYWRpdXMsIHkpXG4gICAgICBjdHguY2xvc2VQYXRoKClcbiAgICAgIGN0eC5maWxsKClcblxuICAgICAgLy8gZ2V0IG51bWJlciBvZiBzeW5hcHNlc1xuICAgICAgY29uc3Qgc3luYXBzZU51bSA9IGFkai5nZXREYXRhKCdzeW5hcHNlcycpLmxlbmd0aFxuXG4gICAgICAvLyByZW5kZXIgdGV4dFxuICAgICAgY3R4LmZpbGxTdHlsZSA9ICcjNDI0MjQyJ1xuICAgICAgY3R4LnRleHRBbGlnbiA9ICdjZW50ZXInXG4gICAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgYXJyYXlPZkxhYmVsTGluZXMubGVuZ3RoOyArK2luZGV4KSB7XG4gICAgICAgIGN0eC5maWxsVGV4dChhcnJheU9mTGFiZWxMaW5lc1tpbmRleF0sIHggKyAod2lkdGggLyAyKSwgeSArIDE4ICsgKDE2ICogaW5kZXgpKVxuICAgICAgfVxuXG4gICAgICBpZiAoc3luYXBzZU51bSA+IDEpIHtcbiAgICAgICAgZHJhd1N5bmFwc2VDb3VudChjdHgsIHggKyB3aWR0aCwgeSwgc3luYXBzZU51bSlcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKCFjYW52YXMuZGVueVNlbGVjdGVkICYmIHNob3dEZXNjKSB7XG4gICAgICAvLyBnZXQgbnVtYmVyIG9mIHN5bmFwc2VzXG4gICAgICBjb25zdCBzeW5hcHNlTnVtID0gYWRqLmdldERhdGEoJ3N5bmFwc2VzJykubGVuZ3RoXG5cbiAgICAgIGlmIChzeW5hcHNlTnVtID4gMSkge1xuICAgICAgICBjb25zdCBjdHggPSBjYW52YXMuZ2V0Q3R4KClcbiAgICAgICAgY29uc3QgeCA9IChwb3MueCArIHBvc0NoaWxkLngpIC8gMlxuICAgICAgICBjb25zdCB5ID0gKHBvcy55ICsgcG9zQ2hpbGQueSkgLyAyXG4gICAgICAgIGRyYXdTeW5hcHNlQ291bnQoY3R4LCB4LCB5LCBzeW5hcHNlTnVtKVxuICAgICAgfVxuICAgIH1cbiAgfSwgLy8gZWRnZVJlbmRlclxuICBGb3JjZURpcmVjdGVkOiB7XG4gICAgYW5pbWF0ZVNhdmVkTGF5b3V0OiB7XG4gICAgICBtb2RlczogWydsaW5lYXInXSxcbiAgICAgIC8vIFRPRE8gZml4IHRlc3RzIHNvIHdlIGRvbid0IG5lZWQgXy5nZXRcbiAgICAgIHRyYW5zaXRpb246IF8uZ2V0KCRqaXQsICdUcmFucy5RdWFkLmVhc2VJbk91dCcpLFxuICAgICAgZHVyYXRpb246IDgwMCxcbiAgICAgIG9uQ29tcGxldGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICBWaXN1YWxpemUubUdyYXBoLmJ1c3kgPSBmYWxzZVxuICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKEpJVC5ldmVudHMuYW5pbWF0aW9uRG9uZSlcbiAgICAgIH1cbiAgICB9LFxuICAgIGFuaW1hdGVGRExheW91dDoge1xuICAgICAgbW9kZXM6IFsnbGluZWFyJ10sXG4gICAgICAvLyBUT0RPIGZpeCB0ZXN0cyBzbyB3ZSBkb24ndCBuZWVkIF8uZ2V0XG4gICAgICB0cmFuc2l0aW9uOiBfLmdldCgkaml0LCAnVHJhbnMuRWxhc3RpYy5lYXNlT3V0JyksXG4gICAgICBkdXJhdGlvbjogODAwLFxuICAgICAgb25Db21wbGV0ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIFZpc3VhbGl6ZS5tR3JhcGguYnVzeSA9IGZhbHNlXG4gICAgICB9XG4gICAgfSxcbiAgICBncmFwaFNldHRpbmdzOiB7XG4gICAgICAvLyBpZCBvZiB0aGUgdmlzdWFsaXphdGlvbiBjb250YWluZXJcbiAgICAgIGluamVjdEludG86ICdpbmZvdmlzJyxcbiAgICAgIC8vIEVuYWJsZSB6b29taW5nIGFuZCBwYW5uaW5nXG4gICAgICAvLyBieSBzY3JvbGxpbmcgYW5kIERuRFxuICAgICAgTmF2aWdhdGlvbjoge1xuICAgICAgICBlbmFibGU6IHRydWUsXG4gICAgICAgIC8vIEVuYWJsZSBwYW5uaW5nIGV2ZW50cyBvbmx5IGlmIHdlJ3JlIGRyYWdnaW5nIHRoZSBlbXB0eVxuICAgICAgICAvLyBjYW52YXMgKGFuZCBub3QgYSBub2RlKS5cbiAgICAgICAgcGFubmluZzogJ2F2b2lkIG5vZGVzJyxcbiAgICAgICAgem9vbWluZzogMjggLy8gem9vbSBzcGVlZC4gaGlnaGVyIGlzIG1vcmUgc2Vuc2libGVcbiAgICAgIH0sXG4gICAgICAvLyBDaGFuZ2Ugbm9kZSBhbmQgZWRnZSBzdHlsZXMgc3VjaCBhc1xuICAgICAgLy8gY29sb3IgYW5kIHdpZHRoLlxuICAgICAgLy8gVGhlc2UgcHJvcGVydGllcyBhcmUgYWxzbyBzZXQgcGVyIG5vZGVcbiAgICAgIC8vIHdpdGggZG9sbGFyIHByZWZpeGVkIGRhdGEtcHJvcGVydGllcyBpbiB0aGVcbiAgICAgIC8vIEpTT04gc3RydWN0dXJlLlxuICAgICAgTm9kZToge1xuICAgICAgICBvdmVycmlkYWJsZTogdHJ1ZSxcbiAgICAgICAgY29sb3I6ICcjMkQ2QTVEJyxcbiAgICAgICAgdHlwZTogJ2N1c3RvbU5vZGUnLFxuICAgICAgICBkaW06IDI1XG4gICAgICB9LFxuICAgICAgRWRnZToge1xuICAgICAgICBvdmVycmlkYWJsZTogdHJ1ZSxcbiAgICAgICAgY29sb3I6IFNldHRpbmdzLmNvbG9ycy5zeW5hcHNlcy5ub3JtYWwsXG4gICAgICAgIHR5cGU6ICdjdXN0b21FZGdlJyxcbiAgICAgICAgbGluZVdpZHRoOiAyLFxuICAgICAgICBhbHBoYTogMVxuICAgICAgfSxcbiAgICAgIC8vIE5hdGl2ZSBjYW52YXMgdGV4dCBzdHlsaW5nXG4gICAgICBMYWJlbDoge1xuICAgICAgICB0eXBlOiAnTmF0aXZlJywgLy8gTmF0aXZlIG9yIEhUTUxcbiAgICAgICAgc2l6ZTogMjAsXG4gICAgICAgIGZhbWlseTogJ2FyaWFsJyxcbiAgICAgICAgdGV4dEJhc2VsaW5lOiAnYWxwaGFiZXRpYycsXG4gICAgICAgIGNvbG9yOiBTZXR0aW5ncy5jb2xvcnMubGFiZWxzLnRleHRcbiAgICAgIH0sXG4gICAgICAvLyBBZGQgVGlwc1xuICAgICAgVGlwczoge1xuICAgICAgICBlbmFibGU6IGZhbHNlLFxuICAgICAgICBvblNob3c6IGZ1bmN0aW9uKHRpcCwgbm9kZSkge31cbiAgICAgIH0sXG4gICAgICAvLyBBZGQgbm9kZSBldmVudHNcbiAgICAgIEV2ZW50czoge1xuICAgICAgICBlbmFibGU6IHRydWUsXG4gICAgICAgIGVuYWJsZUZvckVkZ2VzOiB0cnVlLFxuICAgICAgICBvbk1vdXNlTW92ZTogZnVuY3Rpb24obm9kZSwgZXZlbnRJbmZvLCBlKSB7XG4gICAgICAgICAgSklULm9uTW91c2VNb3ZlSGFuZGxlcihub2RlLCBldmVudEluZm8sIGUpXG4gICAgICAgIC8vIGNvbnNvbGUubG9nKCdjYWxsZWQgbW91c2UgbW92ZSBoYW5kbGVyJylcbiAgICAgICAgfSxcbiAgICAgICAgLy8gVXBkYXRlIG5vZGUgcG9zaXRpb25zIHdoZW4gZHJhZ2dlZFxuICAgICAgICBvbkRyYWdNb3ZlOiBmdW5jdGlvbihub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICAgICAgICBKSVQub25EcmFnTW92ZVRvcGljSGFuZGxlcihub2RlLCBldmVudEluZm8sIGUpXG4gICAgICAgIC8vIGNvbnNvbGUubG9nKCdjYWxsZWQgZHJhZyBtb3ZlIGhhbmRsZXInKVxuICAgICAgICB9LFxuICAgICAgICBvbkRyYWdFbmQ6IGZ1bmN0aW9uKG5vZGUsIGV2ZW50SW5mbywgZSkge1xuICAgICAgICAgIEpJVC5vbkRyYWdFbmRUb3BpY0hhbmRsZXIobm9kZSwgZXZlbnRJbmZvLCBlLCBmYWxzZSlcbiAgICAgICAgLy8gY29uc29sZS5sb2coJ2NhbGxlZCBkcmFnIGVuZCBoYW5kbGVyJylcbiAgICAgICAgfSxcbiAgICAgICAgb25EcmFnQ2FuY2VsOiBmdW5jdGlvbihub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICAgICAgICBKSVQub25EcmFnQ2FuY2VsSGFuZGxlcihub2RlLCBldmVudEluZm8sIGUsIGZhbHNlKVxuICAgICAgICB9LFxuICAgICAgICAvLyBJbXBsZW1lbnQgdGhlIHNhbWUgaGFuZGxlciBmb3IgdG91Y2hzY3JlZW5zXG4gICAgICAgIG9uVG91Y2hTdGFydDogZnVuY3Rpb24obm9kZSwgZXZlbnRJbmZvLCBlKSB7fSxcbiAgICAgICAgLy8gSW1wbGVtZW50IHRoZSBzYW1lIGhhbmRsZXIgZm9yIHRvdWNoc2NyZWVuc1xuICAgICAgICBvblRvdWNoTW92ZTogZnVuY3Rpb24obm9kZSwgZXZlbnRJbmZvLCBlKSB7XG4gICAgICAgICAgSklULm9uRHJhZ01vdmVUb3BpY0hhbmRsZXIobm9kZSwgZXZlbnRJbmZvLCBlKVxuICAgICAgICB9LFxuICAgICAgICAvLyBJbXBsZW1lbnQgdGhlIHNhbWUgaGFuZGxlciBmb3IgdG91Y2hzY3JlZW5zXG4gICAgICAgIG9uVG91Y2hFbmQ6IGZ1bmN0aW9uKG5vZGUsIGV2ZW50SW5mbywgZSkge30sXG4gICAgICAgIC8vIEltcGxlbWVudCB0aGUgc2FtZSBoYW5kbGVyIGZvciB0b3VjaHNjcmVlbnNcbiAgICAgICAgb25Ub3VjaENhbmNlbDogZnVuY3Rpb24obm9kZSwgZXZlbnRJbmZvLCBlKSB7fSxcbiAgICAgICAgLy8gQWRkIGFsc28gYSBjbGljayBoYW5kbGVyIHRvIG5vZGVzXG4gICAgICAgIG9uQ2xpY2s6IGZ1bmN0aW9uKG5vZGUsIGV2ZW50SW5mbywgZSkge1xuICAgICAgICAgIC8vIHJlbW92ZSB0aGUgcmlnaHRjbGlja21lbnVcbiAgICAgICAgICAkKCcucmlnaHRjbGlja21lbnUnKS5yZW1vdmUoKVxuXG4gICAgICAgICAgaWYgKE1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMpIHtcbiAgICAgICAgICAgIGlmIChlLmN0cmxLZXkpIHtcbiAgICAgICAgICAgICAgVmlzdWFsaXplLm1HcmFwaC5idXN5ID0gZmFsc2VcbiAgICAgICAgICAgICAgTW91c2UuYm94RW5kQ29vcmRpbmF0ZXMgPSBldmVudEluZm8uZ2V0UG9zKClcblxuICAgICAgICAgICAgICBjb25zdCBiUyA9IE1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXNcbiAgICAgICAgICAgICAgY29uc3QgYkUgPSBNb3VzZS5ib3hFbmRDb29yZGluYXRlc1xuICAgICAgICAgICAgICBpZiAoTWF0aC5hYnMoYlMueCAtIGJFLngpID4gMjAgJiYgTWF0aC5hYnMoYlMueSAtIGJFLnkpID4gMjApIHtcbiAgICAgICAgICAgICAgICBKSVQuem9vbVRvQm94KGUpXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgTW91c2UuYm94U3RhcnRDb29yZGluYXRlcyA9IG51bGxcbiAgICAgICAgICAgICAgICBNb3VzZS5ib3hFbmRDb29yZGluYXRlcyA9IG51bGxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoZS5zaGlmdEtleSkge1xuICAgICAgICAgICAgICBWaXN1YWxpemUubUdyYXBoLmJ1c3kgPSBmYWxzZVxuICAgICAgICAgICAgICBNb3VzZS5ib3hFbmRDb29yZGluYXRlcyA9IGV2ZW50SW5mby5nZXRQb3MoKVxuICAgICAgICAgICAgICBKSVQuc2VsZWN0V2l0aEJveChlKVxuICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoZS50YXJnZXQuaWQgIT09ICdpbmZvdmlzLWNhbnZhcycpIHJldHVybiBmYWxzZVxuXG4gICAgICAgICAgLy8gY2xpY2tpbmcgb24gYSBlZGdlLCBub2RlLCBvciBjbGlja2luZyBvbiBibGFuayBwYXJ0IG9mIGNhbnZhcz9cbiAgICAgICAgICBpZiAobm9kZS5ub2RlRnJvbSkge1xuICAgICAgICAgICAgSklULnNlbGVjdEVkZ2VPbkNsaWNrSGFuZGxlcihub2RlLCBlKVxuICAgICAgICAgIH0gZWxzZSBpZiAobm9kZSAmJiAhbm9kZS5ub2RlRnJvbSkge1xuICAgICAgICAgICAgSklULnNlbGVjdE5vZGVPbkNsaWNrSGFuZGxlcihub2RlLCBlKVxuICAgICAgICAgICAgRW5naW5lLnNldEZvY3VzTm9kZShub2RlKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBKSVQuY2FudmFzQ2xpY2tIYW5kbGVyKGV2ZW50SW5mby5nZXRQb3MoKSwgZSlcbiAgICAgICAgICB9IC8vIGlmXG4gICAgICAgIH0sXG4gICAgICAgIC8vIEFkZCBhbHNvIGEgY2xpY2sgaGFuZGxlciB0byBub2Rlc1xuICAgICAgICBvblJpZ2h0Q2xpY2s6IGZ1bmN0aW9uKG5vZGUsIGV2ZW50SW5mbywgZSkge1xuICAgICAgICAgIC8vIHJlbW92ZSB0aGUgcmlnaHRjbGlja21lbnVcbiAgICAgICAgICAkKCcucmlnaHRjbGlja21lbnUnKS5yZW1vdmUoKVxuXG4gICAgICAgICAgaWYgKE1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMpIHtcbiAgICAgICAgICAgIENyZWF0ZS5uZXdTeW5hcHNlLmhpZGUoKVxuICAgICAgICAgICAgVmlzdWFsaXplLm1HcmFwaC5idXN5ID0gZmFsc2VcbiAgICAgICAgICAgIE1vdXNlLmJveEVuZENvb3JkaW5hdGVzID0gZXZlbnRJbmZvLmdldFBvcygpXG4gICAgICAgICAgICBKSVQuc2VsZWN0V2l0aEJveChlKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKGUudGFyZ2V0LmlkICE9PSAnaW5mb3Zpcy1jYW52YXMnKSByZXR1cm4gZmFsc2VcblxuICAgICAgICAgIC8vIGNsaWNraW5nIG9uIGEgZWRnZSwgbm9kZSwgb3IgY2xpY2tpbmcgb24gYmxhbmsgcGFydCBvZiBjYW52YXM/XG4gICAgICAgICAgaWYgKG5vZGUubm9kZUZyb20pIHtcbiAgICAgICAgICAgIEpJVC5zZWxlY3RFZGdlT25SaWdodENsaWNrSGFuZGxlcihub2RlLCBlKVxuICAgICAgICAgIH0gZWxzZSBpZiAobm9kZSAmJiAhbm9kZS5ub2RlRnJvbSkge1xuICAgICAgICAgICAgSklULnNlbGVjdE5vZGVPblJpZ2h0Q2xpY2tIYW5kbGVyKG5vZGUsIGUpXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIHJpZ2h0IGNsaWNrIG9wZW4gc3BhY2VcbiAgICAgICAgICAgIENyZWF0ZS5uZXdTeW5hcHNlLmhpZGUoKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIC8vIE51bWJlciBvZiBpdGVyYXRpb25zIGZvciB0aGUgRkQgYWxnb3JpdGhtXG4gICAgICBpdGVyYXRpb25zOiAyMDAsXG4gICAgICAvLyBFZGdlIGxlbmd0aFxuICAgICAgbGV2ZWxEaXN0YW5jZTogMjAwXG4gICAgfSxcbiAgICBub2RlU2V0dGluZ3M6IHtcbiAgICAgICdjdXN0b21Ob2RlJzoge1xuICAgICAgICAncmVuZGVyJzogZnVuY3Rpb24obm9kZSwgY2FudmFzKSB7XG4gICAgICAgICAgY29uc3QgcG9zID0gbm9kZS5wb3MuZ2V0Yyh0cnVlKVxuICAgICAgICAgIGNvbnN0IGRpbSA9IG5vZGUuZ2V0RGF0YSgnZGltJylcbiAgICAgICAgICBjb25zdCB0b3BpYyA9IG5vZGUuZ2V0RGF0YSgndG9waWMnKVxuICAgICAgICAgIGNvbnN0IG1ldGFjb2RlID0gdG9waWMgPyB0b3BpYy5nZXRNZXRhY29kZSgpIDogZmFsc2VcbiAgICAgICAgICBjb25zdCBjdHggPSBjYW52YXMuZ2V0Q3R4KClcblxuICAgICAgICAgIC8vIGlmIHRoZSB0b3BpYyBpcyBzZWxlY3RlZCBkcmF3IGEgY2lyY2xlIGFyb3VuZCBpdFxuICAgICAgICAgIGlmICghY2FudmFzLmRlbnlTZWxlY3RlZCAmJiBub2RlLnNlbGVjdGVkKSB7XG4gICAgICAgICAgICBjdHguYmVnaW5QYXRoKClcbiAgICAgICAgICAgIGN0eC5hcmMocG9zLngsIHBvcy55LCBkaW0gKyAzLCAwLCAyICogTWF0aC5QSSwgZmFsc2UpXG4gICAgICAgICAgICBjdHguc3Ryb2tlU3R5bGUgPSBTZXR0aW5ncy5jb2xvcnMudG9waWNzLnNlbGVjdGVkXG4gICAgICAgICAgICBjdHgubGluZVdpZHRoID0gMlxuICAgICAgICAgICAgY3R4LnN0cm9rZSgpXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKCFtZXRhY29kZSB8fFxuICAgICAgICAgICAgIW1ldGFjb2RlLmdldCgnaW1hZ2UnKSB8fFxuICAgICAgICAgICAgIW1ldGFjb2RlLmdldCgnaW1hZ2UnKS5jb21wbGV0ZSB8fFxuICAgICAgICAgICAgKHR5cGVvZiBtZXRhY29kZS5nZXQoJ2ltYWdlJykubmF0dXJhbFdpZHRoICE9PSAndW5kZWZpbmVkJyAmJlxuICAgICAgICAgICAgbWV0YWNvZGUuZ2V0KCdpbWFnZScpLm5hdHVyYWxXaWR0aCA9PT0gMCkpIHtcbiAgICAgICAgICAgIGN0eC5iZWdpblBhdGgoKVxuICAgICAgICAgICAgY3R4LmFyYyhwb3MueCwgcG9zLnksIGRpbSwgMCwgMiAqIE1hdGguUEksIGZhbHNlKVxuICAgICAgICAgICAgY3R4LmZpbGxTdHlsZSA9ICcjQjZCMkZEJ1xuICAgICAgICAgICAgY3R4LmZpbGwoKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjdHguZHJhd0ltYWdlKG1ldGFjb2RlLmdldCgnaW1hZ2UnKSwgcG9zLnggLSBkaW0sIHBvcy55IC0gZGltLCBkaW0gKiAyLCBkaW0gKiAyKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIGlmIHRoZSB0b3BpYyBoYXMgYSBsaW5rLCBkcmF3IGEgc21hbGwgaW1hZ2UgdG8gaW5kaWNhdGUgdGhhdFxuICAgICAgICAgIGNvbnN0IGhhc0xpbmsgPSB0b3BpYyAmJiB0b3BpYy5nZXQoJ2xpbmsnKSAhPT0gJycgJiYgdG9waWMuZ2V0KCdsaW5rJykgIT09IG51bGxcbiAgICAgICAgICBjb25zdCBsaW5rSW1hZ2UgPSBKSVQudG9waWNMaW5rSW1hZ2VcbiAgICAgICAgICBjb25zdCBsaW5rSW1hZ2VMb2FkZWQgPSBsaW5rSW1hZ2UuY29tcGxldGUgfHxcbiAgICAgICAgICAodHlwZW9mIGxpbmtJbWFnZS5uYXR1cmFsV2lkdGggIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgICAgbGlua0ltYWdlLm5hdHVyYWxXaWR0aCAhPT0gMClcbiAgICAgICAgICBpZiAoaGFzTGluayAmJiBsaW5rSW1hZ2VMb2FkZWQpIHtcbiAgICAgICAgICAgIGN0eC5kcmF3SW1hZ2UobGlua0ltYWdlLCBwb3MueCAtIGRpbSAtIDgsIHBvcy55IC0gZGltIC0gOCwgMTYsIDE2KVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIGlmIHRoZSB0b3BpYyBoYXMgYSBkZXNjLCBkcmF3IGEgc21hbGwgaW1hZ2UgdG8gaW5kaWNhdGUgdGhhdFxuICAgICAgICAgIGNvbnN0IGhhc0Rlc2MgPSB0b3BpYyAmJiB0b3BpYy5nZXQoJ2Rlc2MnKSAhPT0gJycgJiYgdG9waWMuZ2V0KCdkZXNjJykgIT09IG51bGxcbiAgICAgICAgICBjb25zdCBkZXNjSW1hZ2UgPSBKSVQudG9waWNEZXNjSW1hZ2VcbiAgICAgICAgICBjb25zdCBkZXNjSW1hZ2VMb2FkZWQgPSBkZXNjSW1hZ2UuY29tcGxldGUgfHxcbiAgICAgICAgICAodHlwZW9mIGRlc2NJbWFnZS5uYXR1cmFsV2lkdGggIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgICAgZGVzY0ltYWdlLm5hdHVyYWxXaWR0aCAhPT0gMClcbiAgICAgICAgICBpZiAoaGFzRGVzYyAmJiBkZXNjSW1hZ2VMb2FkZWQpIHtcbiAgICAgICAgICAgIGN0eC5kcmF3SW1hZ2UoZGVzY0ltYWdlLCBwb3MueCArIGRpbSAtIDgsIHBvcy55IC0gZGltIC0gOCwgMTYsIDE2KVxuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgJ2NvbnRhaW5zJzogZnVuY3Rpb24obm9kZSwgcG9zKSB7XG4gICAgICAgICAgY29uc3QgbnBvcyA9IG5vZGUucG9zLmdldGModHJ1ZSlcbiAgICAgICAgICBjb25zdCBkaW0gPSBub2RlLmdldERhdGEoJ2RpbScpXG4gICAgICAgICAgY29uc3QgYXJyYXlPZkxhYmVsTGluZXMgPSBVdGlsLnNwbGl0TGluZShub2RlLm5hbWUsIDI1KS5zcGxpdCgnXFxuJylcbiAgICAgICAgICBjb25zdCBjdHggPSBWaXN1YWxpemUubUdyYXBoLmNhbnZhcy5nZXRDdHgoKVxuXG4gICAgICAgICAgY29uc3QgaGVpZ2h0ID0gMjUgKiBhcnJheU9mTGFiZWxMaW5lcy5sZW5ndGhcblxuICAgICAgICAgIGxldCBsaW5lV2lkdGhzID0gW11cbiAgICAgICAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgYXJyYXlPZkxhYmVsTGluZXMubGVuZ3RoOyArK2luZGV4KSB7XG4gICAgICAgICAgICBsaW5lV2lkdGhzLnB1c2goY3R4Lm1lYXN1cmVUZXh0KGFycmF5T2ZMYWJlbExpbmVzW2luZGV4XSkud2lkdGgpXG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IHdpZHRoID0gTWF0aC5tYXguYXBwbHkobnVsbCwgbGluZVdpZHRocykgKyA4XG4gICAgICAgICAgY29uc3QgbGFiZWx5ID0gbnBvcy55ICsgbm9kZS5nZXREYXRhKCdoZWlnaHQnKSArIDUgKyBoZWlnaHQgLyAyXG5cbiAgICAgICAgICBjb25zdCBvdmVyTGFiZWwgPSB0aGlzLm5vZGVIZWxwZXIucmVjdGFuZ2xlLmNvbnRhaW5zKHtcbiAgICAgICAgICAgIHg6IG5wb3MueCxcbiAgICAgICAgICAgIHk6IGxhYmVseVxuICAgICAgICAgIH0sIHBvcywgd2lkdGgsIGhlaWdodClcblxuICAgICAgICAgIHJldHVybiB0aGlzLm5vZGVIZWxwZXIuY2lyY2xlLmNvbnRhaW5zKG5wb3MsIHBvcywgZGltKSB8fCBvdmVyTGFiZWxcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0sXG4gICAgZWRnZVNldHRpbmdzOiB7XG4gICAgICAnY3VzdG9tRWRnZSc6IHtcbiAgICAgICAgJ3JlbmRlcic6IGZ1bmN0aW9uKGFkaiwgY2FudmFzKSB7XG4gICAgICAgICAgSklULmVkZ2VSZW5kZXIoYWRqLCBjYW52YXMpXG4gICAgICAgIH0sXG4gICAgICAgICdjb250YWlucyc6IGZ1bmN0aW9uKGFkaiwgcG9zKSB7XG4gICAgICAgICAgY29uc3QgZnJvbSA9IGFkai5ub2RlRnJvbS5wb3MuZ2V0YygpXG4gICAgICAgICAgY29uc3QgdG8gPSBhZGoubm9kZVRvLnBvcy5nZXRjKClcblxuICAgICAgICAgIC8vIHRoaXMgZml4ZXMgYW4gaXNzdWUgd2hlcmUgd2hlbiBlZGdlcyBhcmUgcGVyZmVjdGx5IGhvcml6b250YWwgb3IgcGVyZmVjdGx5IHZlcnRpY2FsXG4gICAgICAgICAgLy8gaXQgYmVjb21lcyBpbmNyZWRpYmx5IGRpZmZpY3VsdCB0byBob3ZlciBvdmVyIHRoZW1cbiAgICAgICAgICBpZiAoLTEgPCBwb3MueCAmJiBwb3MueCA8IDEpIHBvcy54ID0gMFxuICAgICAgICAgIGlmICgtMSA8IHBvcy55ICYmIHBvcy55IDwgMSkgcG9zLnkgPSAwXG5cbiAgICAgICAgICByZXR1cm4gJGppdC5HcmFwaC5QbG90LmVkZ2VIZWxwZXIubGluZS5jb250YWlucyhmcm9tLCB0bywgcG9zLCBhZGouRWRnZS5lcHNpbG9uICsgNSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSwgLy8gRm9yY2VEaXJlY3RlZFxuICBGb3JjZURpcmVjdGVkM0Q6IHtcbiAgICBhbmltYXRlOiB7XG4gICAgICBtb2RlczogWydsaW5lYXInXSxcbiAgICAgIC8vIFRPRE8gZml4IHRlc3RzIHNvIHdlIGRvbid0IG5lZWQgXy5nZXRcbiAgICAgIHRyYW5zaXRpb246IF8uZ2V0KCRqaXQsICdUcmFucy5FbGFzdGljLmVhc2VPdXQnKSxcbiAgICAgIGR1cmF0aW9uOiAyNTAwLFxuICAgICAgb25Db21wbGV0ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIFZpc3VhbGl6ZS5tR3JhcGguYnVzeSA9IGZhbHNlXG4gICAgICB9XG4gICAgfSxcbiAgICBncmFwaFNldHRpbmdzOiB7XG4gICAgICAvLyBpZCBvZiB0aGUgdmlzdWFsaXphdGlvbiBjb250YWluZXJcbiAgICAgIGluamVjdEludG86ICdpbmZvdmlzJyxcbiAgICAgIHR5cGU6ICczRCcsXG4gICAgICBTY2VuZToge1xuICAgICAgICBMaWdodGluZzoge1xuICAgICAgICAgIGVuYWJsZTogZmFsc2UsXG4gICAgICAgICAgYW1iaWVudDogWzAuNSwgMC41LCAwLjVdLFxuICAgICAgICAgIGRpcmVjdGlvbmFsOiB7XG4gICAgICAgICAgICBkaXJlY3Rpb246IHtcbiAgICAgICAgICAgICAgeDogMSxcbiAgICAgICAgICAgICAgeTogMCxcbiAgICAgICAgICAgICAgejogLTFcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBjb2xvcjogWzAuOSwgMC45LCAwLjldXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgLy8gRW5hYmxlIHpvb21pbmcgYW5kIHBhbm5pbmdcbiAgICAgIC8vIGJ5IHNjcm9sbGluZyBhbmQgRG5EXG4gICAgICBOYXZpZ2F0aW9uOiB7XG4gICAgICAgIGVuYWJsZTogZmFsc2UsXG4gICAgICAgIC8vIEVuYWJsZSBwYW5uaW5nIGV2ZW50cyBvbmx5IGlmIHdlJ3JlIGRyYWdnaW5nIHRoZSBlbXB0eVxuICAgICAgICAvLyBjYW52YXMgKGFuZCBub3QgYSBub2RlKS5cbiAgICAgICAgcGFubmluZzogJ2F2b2lkIG5vZGVzJyxcbiAgICAgICAgem9vbWluZzogMTAgLy8gem9vbSBzcGVlZC4gaGlnaGVyIGlzIG1vcmUgc2Vuc2libGVcbiAgICAgIH0sXG4gICAgICAvLyBDaGFuZ2Ugbm9kZSBhbmQgZWRnZSBzdHlsZXMgc3VjaCBhc1xuICAgICAgLy8gY29sb3IgYW5kIHdpZHRoLlxuICAgICAgLy8gVGhlc2UgcHJvcGVydGllcyBhcmUgYWxzbyBzZXQgcGVyIG5vZGVcbiAgICAgIC8vIHdpdGggZG9sbGFyIHByZWZpeGVkIGRhdGEtcHJvcGVydGllcyBpbiB0aGVcbiAgICAgIC8vIEpTT04gc3RydWN0dXJlLlxuICAgICAgTm9kZToge1xuICAgICAgICBvdmVycmlkYWJsZTogdHJ1ZSxcbiAgICAgICAgdHlwZTogJ3NwaGVyZScsXG4gICAgICAgIGRpbTogMTUsXG4gICAgICAgIGNvbG9yOiAnI2ZmZmZmZidcbiAgICAgIH0sXG4gICAgICBFZGdlOiB7XG4gICAgICAgIG92ZXJyaWRhYmxlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ3R1YmUnLFxuICAgICAgICBjb2xvcjogJyMxMTEnLFxuICAgICAgICBsaW5lV2lkdGg6IDNcbiAgICAgIH0sXG4gICAgICAvLyBOYXRpdmUgY2FudmFzIHRleHQgc3R5bGluZ1xuICAgICAgTGFiZWw6IHtcbiAgICAgICAgdHlwZTogJ0hUTUwnLCAvLyBOYXRpdmUgb3IgSFRNTFxuICAgICAgICBzaXplOiAxMCxcbiAgICAgICAgc3R5bGU6ICdib2xkJ1xuICAgICAgfSxcbiAgICAgIC8vIEFkZCBub2RlIGV2ZW50c1xuICAgICAgRXZlbnRzOiB7XG4gICAgICAgIGVuYWJsZTogdHJ1ZSxcbiAgICAgICAgdHlwZTogJ05hdGl2ZScsXG4gICAgICAgIGk6IDAsXG4gICAgICAgIG9uTW91c2VNb3ZlOiBmdW5jdGlvbihub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICAgICAgICAvLyBpZih0aGlzLmkrKyAlIDMpIHJldHVyblxuICAgICAgICAgIGNvbnN0IHBvcyA9IGV2ZW50SW5mby5nZXRQb3MoKVxuICAgICAgICAgIFZpc3VhbGl6ZS5jYW1lcmFQb3NpdGlvbi54ICs9IChwb3MueCAtIFZpc3VhbGl6ZS5jYW1lcmFQb3NpdGlvbi54KSAqIDAuNVxuICAgICAgICAgIFZpc3VhbGl6ZS5jYW1lcmFQb3NpdGlvbi55ICs9ICgtcG9zLnkgLSBWaXN1YWxpemUuY2FtZXJhUG9zaXRpb24ueSkgKiAwLjVcbiAgICAgICAgICBWaXN1YWxpemUubUdyYXBoLnBsb3QoKVxuICAgICAgICB9LFxuICAgICAgICBvbk1vdXNlV2hlZWw6IGZ1bmN0aW9uKGRlbHRhKSB7XG4gICAgICAgICAgVmlzdWFsaXplLmNhbWVyYVBvc2l0aW9uLnogKz0gLWRlbHRhICogMjBcbiAgICAgICAgICBWaXN1YWxpemUubUdyYXBoLnBsb3QoKVxuICAgICAgICB9LFxuICAgICAgICBvbkNsaWNrOiBmdW5jdGlvbigpIHt9XG4gICAgICB9LFxuICAgICAgLy8gTnVtYmVyIG9mIGl0ZXJhdGlvbnMgZm9yIHRoZSBGRCBhbGdvcml0aG1cbiAgICAgIGl0ZXJhdGlvbnM6IDIwMCxcbiAgICAgIC8vIEVkZ2UgbGVuZ3RoXG4gICAgICBsZXZlbERpc3RhbmNlOiAxMDBcbiAgICB9LFxuICAgIG5vZGVTZXR0aW5nczoge1xuXG4gICAgfSxcbiAgICBlZGdlU2V0dGluZ3M6IHtcblxuICAgIH1cbiAgfSwgLy8gRm9yY2VEaXJlY3RlZDNEXG4gIFJHcmFwaDoge1xuICAgIGFuaW1hdGU6IHtcbiAgICAgIG1vZGVzOiBbJ3BvbGFyJ10sXG4gICAgICBkdXJhdGlvbjogODAwLFxuICAgICAgb25Db21wbGV0ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIFZpc3VhbGl6ZS5tR3JhcGguYnVzeSA9IGZhbHNlXG4gICAgICB9XG4gICAgfSxcbiAgICAvLyB0aGlzIHdpbGwganVzdCBiZSB1c2VkIHRvIHBhdGNoIHRoZSBGb3JjZURpcmVjdGVkIGdyYXBoc2V0dGluZ3Mgd2l0aCB0aGUgZmV3IHRoaW5ncyB3aGljaCBhY3R1YWxseSBkaWZmZXJcbiAgICBiYWNrZ3JvdW5kOiB7XG4gICAgICBsZXZlbERpc3RhbmNlOiAyMDAsXG4gICAgICBudW1iZXJPZkNpcmNsZXM6IDQsXG4gICAgICBDYW52YXNTdHlsZXM6IHtcbiAgICAgICAgc3Ryb2tlU3R5bGU6ICcjMzMzJyxcbiAgICAgICAgbGluZVdpZHRoOiAxLjVcbiAgICAgIH1cbiAgICB9LFxuICAgIGxldmVsRGlzdGFuY2U6IDIwMFxuICB9LFxuICBvbk1vdXNlRW50ZXI6IGZ1bmN0aW9uKGVkZ2UpIHtcbiAgICBjb25zdCBmaWx0ZXJlZCA9IGVkZ2UuZ2V0RGF0YSgnYWxwaGEnKSA9PT0gMFxuXG4gICAgLy8gZG9uJ3QgZG8gYW55dGhpbmcgaWYgdGhlIGVkZ2UgaXMgZmlsdGVyZWRcbiAgICAvLyBvciBpZiB0aGUgY2FudmFzIGlzIGFuaW1hdGluZ1xuICAgIGlmIChmaWx0ZXJlZCB8fCBWaXN1YWxpemUubUdyYXBoLmJ1c3kpIHJldHVyblxuXG4gICAgJCgnY2FudmFzJykuY3NzKCdjdXJzb3InLCAncG9pbnRlcicpXG4gICAgY29uc3QgZWRnZUlzU2VsZWN0ZWQgPSBTZWxlY3RlZC5FZGdlcy5pbmRleE9mKGVkZ2UpXG4gICAgLy8gZm9sbG93aW5nIGlmIHN0YXRlbWVudCBvbmx5IGV4ZWN1dGVzIGlmIHRoZSBlZGdlIGJlaW5nIGhvdmVyZWQgb3ZlciBpcyBub3Qgc2VsZWN0ZWRcbiAgICBpZiAoZWRnZUlzU2VsZWN0ZWQgPT09IC0xKSB7XG4gICAgICBlZGdlLnNldERhdGEoJ3Nob3dEZXNjJywgdHJ1ZSwgJ2N1cnJlbnQnKVxuICAgIH1cblxuICAgIGVkZ2Uuc2V0RGF0YXNldCgnZW5kJywge1xuICAgICAgbGluZVdpZHRoOiA0XG4gICAgfSlcbiAgICBWaXN1YWxpemUubUdyYXBoLmZ4LmFuaW1hdGUoe1xuICAgICAgbW9kZXM6IFsnZWRnZS1wcm9wZXJ0eTpsaW5lV2lkdGgnXSxcbiAgICAgIGR1cmF0aW9uOiAxMDBcbiAgICB9KVxuICAgIFZpc3VhbGl6ZS5tR3JhcGgucGxvdCgpXG4gIH0sIC8vIG9uTW91c2VFbnRlclxuICBvbk1vdXNlTGVhdmU6IGZ1bmN0aW9uKGVkZ2UpIHtcbiAgICBpZiAoZWRnZS5nZXREYXRhKCdhbHBoYScpID09PSAwKSByZXR1cm4gLy8gZG9uJ3QgZG8gYW55dGhpbmcgaWYgdGhlIGVkZ2UgaXMgZmlsdGVyZWRcbiAgICAkKCdjYW52YXMnKS5jc3MoJ2N1cnNvcicsICdkZWZhdWx0JylcbiAgICBjb25zdCBlZGdlSXNTZWxlY3RlZCA9IFNlbGVjdGVkLkVkZ2VzLmluZGV4T2YoZWRnZSlcbiAgICAvLyBmb2xsb3dpbmcgaWYgc3RhdGVtZW50IG9ubHkgZXhlY3V0ZXMgaWYgdGhlIGVkZ2UgYmVpbmcgaG92ZXJlZCBvdmVyIGlzIG5vdCBzZWxlY3RlZFxuICAgIGlmIChlZGdlSXNTZWxlY3RlZCA9PT0gLTEpIHtcbiAgICAgIGVkZ2Uuc2V0RGF0YSgnc2hvd0Rlc2MnLCBmYWxzZSwgJ2N1cnJlbnQnKVxuICAgIH1cblxuICAgIGVkZ2Uuc2V0RGF0YXNldCgnZW5kJywge1xuICAgICAgbGluZVdpZHRoOiAyXG4gICAgfSlcbiAgICBWaXN1YWxpemUubUdyYXBoLmZ4LmFuaW1hdGUoe1xuICAgICAgbW9kZXM6IFsnZWRnZS1wcm9wZXJ0eTpsaW5lV2lkdGgnXSxcbiAgICAgIGR1cmF0aW9uOiAxMDBcbiAgICB9KVxuICAgIFZpc3VhbGl6ZS5tR3JhcGgucGxvdCgpXG4gIH0sIC8vIG9uTW91c2VMZWF2ZVxuICBvbk1vdXNlTW92ZUhhbmRsZXI6IGZ1bmN0aW9uKF9ub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICBjb25zdCBzZWxmID0gSklUXG5cbiAgICBpZiAoVmlzdWFsaXplLm1HcmFwaC5idXN5KSByZXR1cm5cblxuICAgIGNvbnN0IG5vZGUgPSBldmVudEluZm8uZ2V0Tm9kZSgpXG4gICAgY29uc3QgZWRnZSA9IGV2ZW50SW5mby5nZXRFZGdlKClcblxuICAgIC8vIGlmIHdlJ3JlIG9uIHRvcCBvZiBhIG5vZGUgb2JqZWN0LCBhY3QgbGlrZSB0aGVyZSBhcmVuJ3QgZWRnZXMgdW5kZXIgaXRcbiAgICBpZiAobm9kZSAhPT0gZmFsc2UpIHtcbiAgICAgIGlmIChNb3VzZS5lZGdlSG92ZXJpbmdPdmVyKSB7XG4gICAgICAgIHNlbGYub25Nb3VzZUxlYXZlKE1vdXNlLmVkZ2VIb3ZlcmluZ092ZXIpXG4gICAgICB9XG4gICAgICAkKCdjYW52YXMnKS5jc3MoJ2N1cnNvcicsICdwb2ludGVyJylcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChlZGdlID09PSBmYWxzZSAmJiBNb3VzZS5lZGdlSG92ZXJpbmdPdmVyICE9PSBmYWxzZSkge1xuICAgICAgLy8gbW91c2Ugbm90IG9uIGFuIGVkZ2UsIGJ1dCB3ZSB3ZXJlIG9uIGFuIGVkZ2UgcHJldmlvdXNseVxuICAgICAgc2VsZi5vbk1vdXNlTGVhdmUoTW91c2UuZWRnZUhvdmVyaW5nT3ZlcilcbiAgICB9IGVsc2UgaWYgKGVkZ2UgIT09IGZhbHNlICYmIE1vdXNlLmVkZ2VIb3ZlcmluZ092ZXIgPT09IGZhbHNlKSB7XG4gICAgICAvLyBtb3VzZSBpcyBvbiBhbiBlZGdlLCBidXQgdGhlcmUgaXNuJ3QgYSBzdG9yZWQgZWRnZVxuICAgICAgc2VsZi5vbk1vdXNlRW50ZXIoZWRnZSlcbiAgICB9IGVsc2UgaWYgKGVkZ2UgIT09IGZhbHNlICYmIE1vdXNlLmVkZ2VIb3ZlcmluZ092ZXIgIT09IGVkZ2UpIHtcbiAgICAgIC8vIG1vdXNlIGlzIG9uIGFuIGVkZ2UsIGJ1dCBhIGRpZmZlcmVudCBlZGdlIGlzIHN0b3JlZFxuICAgICAgc2VsZi5vbk1vdXNlTGVhdmUoTW91c2UuZWRnZUhvdmVyaW5nT3ZlcilcbiAgICAgIHNlbGYub25Nb3VzZUVudGVyKGVkZ2UpXG4gICAgfVxuXG4gICAgLy8gY291bGQgYmUgZmFsc2VcbiAgICBNb3VzZS5lZGdlSG92ZXJpbmdPdmVyID0gZWRnZVxuXG4gICAgaWYgKCFub2RlICYmICFlZGdlKSB7XG4gICAgICAkKCdjYW52YXMnKS5jc3MoJ2N1cnNvcicsICdkZWZhdWx0JylcbiAgICB9XG4gIH0sIC8vIG9uTW91c2VNb3ZlSGFuZGxlclxuICBlbnRlcktleUhhbmRsZXI6IGZ1bmN0aW9uKGUpIHtcbiAgICBjb25zdCBjcmVhdGluZ01hcCA9IEdsb2JhbFVJLmxpZ2h0Ym94XG4gICAgaWYgKGNyZWF0aW5nTWFwID09PSAnbmV3bWFwJyB8fCBjcmVhdGluZ01hcCA9PT0gJ2ZvcmttYXAnKSB7XG4gICAgICBHbG9iYWxVSS5DcmVhdGVNYXAuc3VibWl0KClcbiAgICB9IGVsc2UgaWYgKGUudGFyZ2V0LmlkID09PSAndG9waWNfbmFtZScgJiYgIUNyZWF0ZS5uZXdUb3BpYy5tZXRhY29kZVNlbGVjdG9yT3Blbikge1xuICAgICAgVG9waWMuY3JlYXRlVG9waWNMb2NhbGx5KClcbiAgICB9IGVsc2UgaWYgKENyZWF0ZS5uZXdTeW5hcHNlLmJlaW5nQ3JlYXRlZCkge1xuICAgICAgU3luYXBzZS5jcmVhdGVTeW5hcHNlTG9jYWxseShDcmVhdGUubmV3U3luYXBzZS50b3BpYzFpZCwgQ3JlYXRlLm5ld1N5bmFwc2UudG9waWMyaWQpXG4gICAgICBFbmdpbmUucnVuTGF5b3V0KClcbiAgICAgIENyZWF0ZS5uZXdTeW5hcHNlLmhpZGUoKVxuICAgIH1cbiAgfSwgLy8gZW50ZXJLZXlIYW5kbGVyXG4gIGVzY0tleUhhbmRsZXI6IGZ1bmN0aW9uKCkge1xuICAgIENvbnRyb2wuZGVzZWxlY3RBbGxFZGdlcygpXG4gICAgQ29udHJvbC5kZXNlbGVjdEFsbE5vZGVzKClcbiAgfSwgLy8gZXNjS2V5SGFuZGxlclxuICBvbkRyYWdNb3ZlVG9waWNIYW5kbGVyOiBmdW5jdGlvbihub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICB2YXIgc2VsZiA9IEpJVFxuXG4gICAgdmFyIGF1dGhvcml6ZWQgPSBBY3RpdmUuTWFwICYmIEFjdGl2ZS5NYXAuYXV0aG9yaXplVG9FZGl0KEFjdGl2ZS5NYXBwZXIpXG5cbiAgICBpZiAobm9kZSAmJiAhbm9kZS5ub2RlRnJvbSkge1xuXG4gICAgICBjb25zdCBwb3MgPSBldmVudEluZm8uZ2V0UG9zKClcbiAgICAgIGlmICgoZS5idXR0b24gPT09IDAgfHwgZS5idXR0b25zID09PSAwKSAmJiBhdXRob3JpemVkKSB7XG4gICAgICAgIC8vIHN0YXJ0IHN5bmFwc2UgY3JlYXRpb24gIC0+c2Vjb25kIG9wdGlvbiBpcyBmb3IgZmlyZWZveFxuICAgICAgICBpZiAoSklULnRlbXBJbml0ID09PSBmYWxzZSkge1xuICAgICAgICAgIEpJVC50ZW1wTm9kZSA9IG5vZGVcbiAgICAgICAgICBKSVQudGVtcEluaXQgPSB0cnVlXG4gICAgICAgICAgQ3JlYXRlLm5ld1N5bmFwc2UuaGlkZSgpXG4gICAgICAgICAgLy8gc2V0IHRoZSBkcmF3IHN5bmFwc2Ugc3RhcnQgcG9zaXRpb25zXG4gICAgICAgICAgTW91c2Uuc3luYXBzZVN0YXJ0Q29vcmRpbmF0ZXMgPSBbXVxuICAgICAgICAgIGlmIChTZWxlY3RlZC5Ob2Rlcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIFNlbGVjdGVkLk5vZGVzLmZvckVhY2gobiA9PiB7XG4gICAgICAgICAgICAgIE1vdXNlLnN5bmFwc2VTdGFydENvb3JkaW5hdGVzLnB1c2goe1xuICAgICAgICAgICAgICAgIHg6IG4ucG9zLmdldGMoKS54LFxuICAgICAgICAgICAgICAgIHk6IG4ucG9zLmdldGMoKS55XG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH1cbiAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIE1vdXNlLnN5bmFwc2VTdGFydENvb3JkaW5hdGVzID0gW3tcbiAgICAgICAgICAgICAgeDogbm9kZS5wb3MuZ2V0YygpLngsXG4gICAgICAgICAgICAgIHk6IG5vZGUucG9zLmdldGMoKS55XG4gICAgICAgICAgICB9XVxuICAgICAgICAgIH1cbiAgICAgICAgICBNb3VzZS5zeW5hcHNlRW5kQ29vcmRpbmF0ZXMgPSB7XG4gICAgICAgICAgICB4OiBwb3MueCxcbiAgICAgICAgICAgIHk6IHBvcy55XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vXG4gICAgICAgIGxldCB0ZW1wID0gZXZlbnRJbmZvLmdldE5vZGUoKVxuICAgICAgICBpZiAodGVtcCAhPT0gZmFsc2UgJiYgdGVtcC5pZCAhPT0gbm9kZS5pZCAmJiBTZWxlY3RlZC5Ob2Rlcy5pbmRleE9mKHRlbXApID09PSAtMSkgeyAvLyB0aGlzIG1lYW5zIGEgTm9kZSBoYXMgYmVlbiByZXR1cm5lZFxuICAgICAgICAgIEpJVC50ZW1wTm9kZTIgPSB0ZW1wXG4gICAgICAgICAgTW91c2Uuc3luYXBzZUVuZENvb3JkaW5hdGVzID0ge1xuICAgICAgICAgICAgeDogSklULnRlbXBOb2RlMi5wb3MuZ2V0YygpLngsXG4gICAgICAgICAgICB5OiBKSVQudGVtcE5vZGUyLnBvcy5nZXRjKCkueVxuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBiZWZvcmUgbWFraW5nIHRoZSBoaWdobGlnaHRlZCBvbmUgYmlnZ2VyLCBtYWtlIHN1cmUgYWxsIHRoZSBvdGhlcnMgYXJlIHJlZ3VsYXIgc2l6ZVxuICAgICAgICAgIFZpc3VhbGl6ZS5tR3JhcGguZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obikge1xuICAgICAgICAgICAgbi5zZXREYXRhKCdkaW0nLCAyNSwgJ2N1cnJlbnQnKVxuICAgICAgICAgIH0pXG4gICAgICAgICAgdGVtcC5zZXREYXRhKCdkaW0nLCAzNSwgJ2N1cnJlbnQnKVxuICAgICAgICB9IGVsc2UgaWYgKCF0ZW1wKSB7XG4gICAgICAgICAgSklULnRlbXBOb2RlMiA9IG51bGxcbiAgICAgICAgICBNb3VzZS5zeW5hcHNlRW5kQ29vcmRpbmF0ZXMgPSB7XG4gICAgICAgICAgICB4OiBwb3MueCxcbiAgICAgICAgICAgIHk6IHBvcy55XG4gICAgICAgICAgfVxuICAgICAgICAgIFZpc3VhbGl6ZS5tR3JhcGguZ3JhcGguZWFjaE5vZGUoZnVuY3Rpb24obikge1xuICAgICAgICAgICAgbi5zZXREYXRhKCdkaW0nLCAyNSwgJ2N1cnJlbnQnKVxuICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgVmlzdWFsaXplLm1HcmFwaC5wbG90KClcbiAgfSwgLy8gb25EcmFnTW92ZVRvcGljSGFuZGxlclxuICBvbkRyYWdDYW5jZWxIYW5kbGVyOiBmdW5jdGlvbihub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICBKSVQudGVtcE5vZGUgPSBudWxsXG4gICAgaWYgKEpJVC50ZW1wTm9kZTIpIEpJVC50ZW1wTm9kZTIuc2V0RGF0YSgnZGltJywgMjUsICdjdXJyZW50JylcbiAgICBKSVQudGVtcE5vZGUyID0gbnVsbFxuICAgIEpJVC50ZW1wSW5pdCA9IGZhbHNlXG4gICAgLy8gcmVzZXQgdGhlIGRyYXcgc3luYXBzZSBwb3NpdGlvbnMgdG8gZmFsc2VcbiAgICBNb3VzZS5zeW5hcHNlU3RhcnRDb29yZGluYXRlcyA9IFtdXG4gICAgTW91c2Uuc3luYXBzZUVuZENvb3JkaW5hdGVzID0gbnVsbFxuICAgIFZpc3VhbGl6ZS5tR3JhcGgucGxvdCgpXG4gIH0sIC8vIG9uRHJhZ0NhbmNlbEhhbmRsZXJcbiAgb25EcmFnRW5kVG9waWNIYW5kbGVyOiBmdW5jdGlvbihub2RlLCBldmVudEluZm8sIGUpIHtcbiAgICBjb25zdCBzZWxmID0gSklUXG4gICAgY29uc3QgbWlkcG9pbnQgPSB7fVxuICAgIGxldCBwaXhlbFBvc1xuICAgIGxldCBtYXBwaW5nXG5cbiAgICBpZiAoSklULnRlbXBJbml0ICYmIEpJVC50ZW1wTm9kZTIgPT09IG51bGwpIHtcbiAgICAgIE1vdXNlLnN5bmFwc2VFbmRDb29yZGluYXRlcyA9IG51bGxcbiAgICB9IGVsc2UgaWYgKEpJVC50ZW1wSW5pdCAmJiBKSVQudGVtcE5vZGUyICE9PSBudWxsKSB7XG4gICAgICAvLyB0aGlzIG1lYW5zIHlvdSB3YW50IHRvIGNyZWF0ZSBhIHN5bmFwc2UgYmV0d2VlbiB0d28gZXhpc3RpbmcgdG9waWNzXG4gICAgICBDcmVhdGUubmV3U3luYXBzZS50b3BpYzFpZCA9IEpJVC50ZW1wTm9kZS5nZXREYXRhKCd0b3BpYycpLmlkXG4gICAgICBDcmVhdGUubmV3U3luYXBzZS50b3BpYzJpZCA9IEpJVC50ZW1wTm9kZTIuZ2V0RGF0YSgndG9waWMnKS5pZFxuICAgICAgQ3JlYXRlLm5ld1N5bmFwc2Uubm9kZTEgPSBKSVQudGVtcE5vZGVcbiAgICAgIENyZWF0ZS5uZXdTeW5hcHNlLm5vZGUyID0gSklULnRlbXBOb2RlMlxuICAgICAgSklULnRlbXBOb2RlMi5zZXREYXRhKCdkaW0nLCAyNSwgJ2N1cnJlbnQnKVxuICAgICAgbWlkcG9pbnQueCA9IEpJVC50ZW1wTm9kZS5wb3MuZ2V0YygpLnggKyAoSklULnRlbXBOb2RlMi5wb3MuZ2V0YygpLnggLSBKSVQudGVtcE5vZGUucG9zLmdldGMoKS54KSAvIDJcbiAgICAgIG1pZHBvaW50LnkgPSBKSVQudGVtcE5vZGUucG9zLmdldGMoKS55ICsgKEpJVC50ZW1wTm9kZTIucG9zLmdldGMoKS55IC0gSklULnRlbXBOb2RlLnBvcy5nZXRjKCkueSkgLyAyXG4gICAgICBwaXhlbFBvcyA9IFV0aWwuY29vcmRzVG9QaXhlbHMoVmlzdWFsaXplLm1HcmFwaCwgbWlkcG9pbnQpXG4gICAgICAkKCcjbmV3X3N5bmFwc2UnKS5jc3MoJ2xlZnQnLCBwaXhlbFBvcy54ICsgJ3B4JylcbiAgICAgICQoJyNuZXdfc3luYXBzZScpLmNzcygndG9wJywgcGl4ZWxQb3MueSArICdweCcpXG4gICAgICBDcmVhdGUubmV3U3luYXBzZS5vcGVuKClcbiAgICAgIEpJVC50ZW1wTm9kZSA9IG51bGxcbiAgICAgIEpJVC50ZW1wTm9kZTIgPSBudWxsXG4gICAgICBKSVQudGVtcEluaXQgPSBmYWxzZVxuICAgIH1cbiAgICBWaXN1YWxpemUubUdyYXBoLnBsb3QoKVxuICB9LCAvLyBvbkRyYWdFbmRUb3BpY0hhbmRsZXJcbiAgY2FudmFzQ2xpY2tIYW5kbGVyOiBmdW5jdGlvbihjYW52YXNMb2MsIGUpIHtcbiAgICAvLyBncmFiIHRoZSBsb2NhdGlvbiBhbmQgdGltZXN0YW1wIG9mIHRoZSBjbGlja1xuICAgIGNvbnN0IHN0b3JlZFRpbWUgPSBNb3VzZS5sYXN0Q2FudmFzQ2xpY2tcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpIC8vIG5vdCBjb21wYXRpYmxlIHdpdGggSUU4IEZZSVxuICAgIE1vdXNlLmxhc3RDYW52YXNDbGljayA9IG5vd1xuXG4gICAgY29uc3QgYXV0aG9yaXplZCA9IEFjdGl2ZS5NYXAgJiYgQWN0aXZlLk1hcC5hdXRob3JpemVUb0VkaXQoQWN0aXZlLk1hcHBlcilcblxuICAgIGlmIChub3cgLSBzdG9yZWRUaW1lIDwgTW91c2UuRE9VQkxFX0NMSUNLX1RPTEVSQU5DRSAmJiAhTW91c2UuZGlkUGFuKSB7XG4gICAgICAvLyBET1VCTEUgQ0xJQ0tcbiAgICB9IGVsc2UgaWYgKCFNb3VzZS5kaWRQYW4pIHtcbiAgICAgIC8vIFNJTkdMRSBDTElDSywgbm8gcGFuXG4gICAgICBGaWx0ZXIuY2xvc2UoKVxuICAgICAgVG9waWNDYXJkLmhpZGVDYXJkKClcbiAgICAgIFN5bmFwc2VDYXJkLmhpZGVDYXJkKClcbiAgICAgICQoJy5yaWdodGNsaWNrbWVudScpLnJlbW92ZSgpXG4gICAgICAvLyByZXNldCB0aGUgZHJhdyBzeW5hcHNlIHBvc2l0aW9ucyB0byBmYWxzZVxuICAgICAgTW91c2Uuc3luYXBzZVN0YXJ0Q29vcmRpbmF0ZXMgPSBbXVxuICAgICAgTW91c2Uuc3luYXBzZUVuZENvb3JkaW5hdGVzID0gbnVsbFxuICAgICAgSklULnRlbXBJbml0ID0gZmFsc2VcbiAgICAgIEpJVC50ZW1wTm9kZSA9IG51bGxcbiAgICAgIEpJVC50ZW1wTm9kZTIgPSBudWxsXG4gICAgICBpZiAoIWUuY3RybEtleSAmJiAhZS5zaGlmdEtleSkge1xuICAgICAgICBDb250cm9sLmRlc2VsZWN0QWxsRWRnZXMoKVxuICAgICAgICBDb250cm9sLmRlc2VsZWN0QWxsTm9kZXMoKVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBTSU5HTEUgQ0xJQ0ssIHJlc3VsdGluZyBmcm9tIHBhblxuICAgIH1cbiAgfSwgLy8gY2FudmFzQ2xpY2tIYW5kbGVyXG4gIHVwZGF0ZVRvcGljUG9zaXRpb25zOiBmdW5jdGlvbihub2RlLCBwb3MpIHtcbiAgICBjb25zdCBsZW4gPSBTZWxlY3RlZC5Ob2Rlcy5sZW5ndGhcbiAgICAvLyB0aGlzIGlzIHVzZWQgdG8gc2VuZCBub2RlcyB0aGF0IGFyZSBtb3ZpbmcgdG9cbiAgICAvLyBvdGhlciByZWFsdGltZSBjb2xsYWJvcmF0b3JzIG9uIHRoZSBzYW1lIG1hcFxuICAgIGNvbnN0IHBvc2l0aW9uc1RvU2VuZCA9IHt9XG5cbiAgICAvLyBmaXJzdCBkZWZpbmUgb2Zmc2V0IGZvciBlYWNoIG5vZGVcbiAgICB2YXIgeE9mZnNldCA9IFtdXG4gICAgdmFyIHlPZmZzZXQgPSBbXVxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuOyBpICs9IDEpIHtcbiAgICAgIGNvbnN0IG4gPSBTZWxlY3RlZC5Ob2Rlc1tpXVxuICAgICAgeE9mZnNldFtpXSA9IG4ucG9zLmdldGMoKS54IC0gbm9kZS5wb3MuZ2V0YygpLnhcbiAgICAgIHlPZmZzZXRbaV0gPSBuLnBvcy5nZXRjKCkueSAtIG5vZGUucG9zLmdldGMoKS55XG4gICAgfSAvLyBmb3JcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuOyBpICs9IDEpIHtcbiAgICAgIGNvbnN0IG4gPSBTZWxlY3RlZC5Ob2Rlc1tpXVxuICAgICAgY29uc3QgeCA9IHBvcy54ICsgeE9mZnNldFtpXVxuICAgICAgY29uc3QgeSA9IHBvcy55ICsgeU9mZnNldFtpXVxuICAgICAgaWYgKG4ucG9zLnJobyB8fCBuLnBvcy5yaG8gPT09IDApIHtcbiAgICAgICAgLy8gdGhpcyBtZWFucyB3ZSdyZSBpbiB0b3BpYyB2aWV3XG4gICAgICAgIGNvbnN0IHJobyA9IE1hdGguc3FydCh4ICogeCArIHkgKiB5KVxuICAgICAgICBjb25zdCB0aGV0YSA9IE1hdGguYXRhbjIoeSwgeClcbiAgICAgICAgbi5wb3Muc2V0cCh0aGV0YSwgcmhvKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbi5wb3Muc2V0Yyh4LCB5KVxuICAgICAgfVxuXG4gICAgICBpZiAoQWN0aXZlLk1hcCkge1xuICAgICAgICBjb25zdCB0b3BpYyA9IG4uZ2V0RGF0YSgndG9waWMnKVxuICAgICAgICAvLyB3ZSB1c2UgdGhlIHRvcGljIElEIG5vdCB0aGUgbm9kZSBpZFxuICAgICAgICAvLyBiZWNhdXNlIHdlIGNhbid0IGRlcGVuZCBvbiB0aGUgbm9kZSBpZFxuICAgICAgICAvLyB0byBiZSB0aGUgc2FtZSBhcyBvbiBvdGhlciBjb2xsYWJvcmF0b3JzXG4gICAgICAgIC8vIG1hcHNcbiAgICAgICAgcG9zaXRpb25zVG9TZW5kW3RvcGljLmlkXSA9IG4ucG9zXG4gICAgICB9XG4gICAgfSAvLyBmb3JcblxuICAgIGlmIChBY3RpdmUuTWFwKSB7XG4gICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKEpJVC5ldmVudHMudG9waWNEcmFnLCBbcG9zaXRpb25zVG9TZW5kXSlcbiAgICB9XG4gIH0sXG5cbiAgbm9kZURvdWJsZUNsaWNrSGFuZGxlcjogZnVuY3Rpb24obm9kZSwgZSkge1xuICAgIFRvcGljQ2FyZC5zaG93Q2FyZChub2RlKVxuICB9LCAvLyBub2RlRG91YmxlQ2xpY2tIYW5kbGVyXG4gIGVkZ2VEb3VibGVDbGlja0hhbmRsZXI6IGZ1bmN0aW9uKGFkaiwgZSkge1xuICAgIFN5bmFwc2VDYXJkLnNob3dDYXJkKGFkaiwgZSlcbiAgfSwgLy8gbm9kZURvdWJsZUNsaWNrSGFuZGxlclxuICBub2RlV2FzRG91YmxlQ2xpY2tlZDogZnVuY3Rpb24oKSB7XG4gICAgLy8gZ3JhYiB0aGUgdGltZXN0YW1wIG9mIHRoZSBjbGlja1xuICAgIGNvbnN0IHN0b3JlZFRpbWUgPSBNb3VzZS5sYXN0Tm9kZUNsaWNrXG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKSAvLyBub3QgY29tcGF0aWJsZSB3aXRoIElFOCBGWUlcbiAgICBNb3VzZS5sYXN0Tm9kZUNsaWNrID0gbm93XG5cbiAgICBpZiAobm93IC0gc3RvcmVkVGltZSA8IE1vdXNlLkRPVUJMRV9DTElDS19UT0xFUkFOQ0UpIHtcbiAgICAgIHJldHVybiB0cnVlXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cbiAgfSwgLy8gbm9kZVdhc0RvdWJsZUNsaWNrZWRcbiAgaGFuZGxlU2VsZWN0aW9uQmVmb3JlRHJhZ2dpbmc6IGZ1bmN0aW9uKG5vZGUsIGUpIHtcbiAgICBpZiAoU2VsZWN0ZWQuTm9kZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBDb250cm9sLnNlbGVjdE5vZGUobm9kZSwgZSlcbiAgICB9XG4gICAgaWYgKFNlbGVjdGVkLk5vZGVzLmluZGV4T2Yobm9kZSkgPT09IC0xKSB7XG4gICAgICBpZiAoZS5zaGlmdEtleSkge1xuICAgICAgICBDb250cm9sLnNlbGVjdE5vZGUobm9kZSwgZSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIENvbnRyb2wuZGVzZWxlY3RBbGxFZGdlcygpXG4gICAgICAgIENvbnRyb2wuZGVzZWxlY3RBbGxOb2RlcygpXG4gICAgICAgIENvbnRyb2wuc2VsZWN0Tm9kZShub2RlLCBlKVxuICAgICAgfVxuICAgIH1cbiAgfSwgLy8gIGhhbmRsZVNlbGVjdGlvbkJlZm9yZURyYWdnaW5nXG4gIGdldE5vZGVYWTogZnVuY3Rpb24obm9kZSkge1xuICAgIGlmICh0eXBlb2Ygbm9kZS5wb3MueCA9PT0gJ251bWJlcicgJiYgdHlwZW9mIG5vZGUucG9zLnkgPT09ICdudW1iZXInKSB7XG4gICAgICByZXR1cm4gbm9kZS5wb3NcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBub2RlLnBvcy50aGV0YSA9PT0gJ251bWJlcicgJiYgdHlwZW9mIG5vZGUucG9zLnJobyA9PT0gJ251bWJlcicpIHtcbiAgICAgIHJldHVybiBuZXcgJGppdC5Qb2xhcihub2RlLnBvcy50aGV0YSwgbm9kZS5wb3MucmhvKS5nZXRjKHRydWUpXG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ2dldE5vZGVYWTogdW5yZWNvZ25pemVkIG5vZGUgcG9zIGZvcm1hdCcpXG4gICAgICByZXR1cm4ge31cbiAgICB9XG4gIH0sXG4gIHNlbGVjdFdpdGhCb3g6IGZ1bmN0aW9uKGUpIHtcbiAgICBjb25zdCBzZWxmID0gdGhpc1xuICAgIGxldCBzWCA9IE1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMueFxuICAgIGxldCBzWSA9IE1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMueVxuICAgIGxldCBlWCA9IE1vdXNlLmJveEVuZENvb3JkaW5hdGVzLnhcbiAgICBsZXQgZVkgPSBNb3VzZS5ib3hFbmRDb29yZGluYXRlcy55XG5cbiAgICBpZiAoIWUuc2hpZnRLZXkpIHtcbiAgICAgIENvbnRyb2wuZGVzZWxlY3RBbGxOb2RlcygpXG4gICAgICBDb250cm9sLmRlc2VsZWN0QWxsRWRnZXMoKVxuICAgIH1cblxuICAgIC8vIHNlbGVjdCBhbGwgbm9kZXMgdGhhdCBhcmUgd2l0aGluIHRoZSBib3hcbiAgICBWaXN1YWxpemUubUdyYXBoLmdyYXBoLmVhY2hOb2RlKGZ1bmN0aW9uKG4pIHtcbiAgICAgIGNvbnN0IHBvcyA9IHNlbGYuZ2V0Tm9kZVhZKG4pXG4gICAgICBjb25zdCB4ID0gcG9zLnhcbiAgICAgIGNvbnN0IHkgPSBwb3MueVxuXG4gICAgICAvLyBkZXBlbmRpbmcgb24gd2hpY2ggd2F5IHRoZSBwZXJzb24gZHJhZ2dlZCB0aGUgYm94LCBjaGVjayB0aGF0XG4gICAgICAvLyB4IGFuZCB5IGFyZSBiZXR3ZWVuIHRoZSBzdGFydCBhbmQgZW5kIHZhbHVlcyBvZiB0aGUgYm94XG4gICAgICBpZiAoKHNYIDwgeCAmJiB4IDwgZVggJiYgc1kgPCB5ICYmIHkgPCBlWSkgfHxcbiAgICAgICAgKHNYID4geCAmJiB4ID4gZVggJiYgc1kgPiB5ICYmIHkgPiBlWSkgfHxcbiAgICAgICAgKHNYID4geCAmJiB4ID4gZVggJiYgc1kgPCB5ICYmIHkgPCBlWSkgfHxcbiAgICAgICAgKHNYIDwgeCAmJiB4IDwgZVggJiYgc1kgPiB5ICYmIHkgPiBlWSkpIHtcbiAgICAgICAgaWYgKGUuc2hpZnRLZXkpIHtcbiAgICAgICAgICBpZiAobi5zZWxlY3RlZCkge1xuICAgICAgICAgICAgQ29udHJvbC5kZXNlbGVjdE5vZGUobilcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgQ29udHJvbC5zZWxlY3ROb2RlKG4sIGUpXG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIENvbnRyb2wuc2VsZWN0Tm9kZShuLCBlKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIENvbnZlcnQgc2VsZWN0aW9uIGJveCBjb29yZGluYXRlcyB0byB0cmFkaXRpb25hbCBjb29yZGluYXRlcyAoKywrKSBpbiB1cHBlciByaWdodFxuICAgIHNZID0gLTEgKiBzWVxuICAgIGVZID0gLTEgKiBlWVxuXG4gICAgY29uc3QgZWRnZXNUb1RvZ2dsZSA9IFtdXG4gICAgRGF0YU1vZGVsLlN5bmFwc2VzLmVhY2goZnVuY3Rpb24oc3luYXBzZSkge1xuICAgICAgY29uc3QgZSA9IHN5bmFwc2UuZ2V0KCdlZGdlJylcbiAgICAgIGlmIChlZGdlc1RvVG9nZ2xlLmluZGV4T2YoZSkgPT09IC0xKSB7XG4gICAgICAgIGVkZ2VzVG9Ub2dnbGUucHVzaChlKVxuICAgICAgfVxuICAgIH0pXG4gICAgZWRnZXNUb1RvZ2dsZS5mb3JFYWNoKGZ1bmN0aW9uKGVkZ2UpIHtcbiAgICAgIGNvbnN0IGZyb21Ob2RlUG9zID0gc2VsZi5nZXROb2RlWFkoZWRnZS5ub2RlRnJvbSlcbiAgICAgIGNvbnN0IGZyb21Ob2RlWCA9IGZyb21Ob2RlUG9zLnhcbiAgICAgIGNvbnN0IGZyb21Ob2RlWSA9IC0xICogZnJvbU5vZGVQb3MueVxuICAgICAgY29uc3QgdG9Ob2RlUG9zID0gc2VsZi5nZXROb2RlWFkoZWRnZS5ub2RlVG8pXG4gICAgICBjb25zdCB0b05vZGVYID0gdG9Ob2RlUG9zLnhcbiAgICAgIGNvbnN0IHRvTm9kZVkgPSAtMSAqIHRvTm9kZVBvcy55XG5cbiAgICAgIGxldCBtYXhYID0gZnJvbU5vZGVYXG4gICAgICBsZXQgbWF4WSA9IGZyb21Ob2RlWVxuICAgICAgbGV0IG1pblggPSBmcm9tTm9kZVhcbiAgICAgIGxldCBtaW5ZID0gZnJvbU5vZGVZXG5cbiAgICAgIC8vIENvcnJlY3QgbWF4WCwgTWF4WSB2YWx1ZXNcbiAgICAgIDsodG9Ob2RlWCA+IG1heFgpID8gKG1heFggPSB0b05vZGVYKSA6IChtaW5YID0gdG9Ob2RlWClcbiAgICAgIDsodG9Ob2RlWSA+IG1heFkpID8gKG1heFkgPSB0b05vZGVZKSA6IChtaW5ZID0gdG9Ob2RlWSlcblxuICAgICAgbGV0IG1heEJveFggPSBzWFxuICAgICAgbGV0IG1heEJveFkgPSBzWVxuICAgICAgbGV0IG1pbkJveFggPSBzWFxuICAgICAgbGV0IG1pbkJveFkgPSBzWVxuXG4gICAgICAvLyBDb3JyZWN0IG1heEJveFgsIG1heEJveFkgdmFsdWVzXG4gICAgICA7KGVYID4gbWF4Qm94WCkgPyAobWF4Qm94WCA9IGVYKSA6IChtaW5Cb3hYID0gZVgpXG4gICAgICA7KGVZID4gbWF4Qm94WSkgPyAobWF4Qm94WSA9IGVZKSA6IChtaW5Cb3hZID0gZVkpXG5cbiAgICAgIC8vIEZpbmQgdGhlIHNsb3BlcyBmcm9tIHRoZSBzeW5hcHNlIGZyb21Ob2RlIHRvIHRoZSA0IGNvcm5lcnMgb2YgdGhlIHNlbGVjdGlvbiBib3hcbiAgICAgIGNvbnN0IHNsb3BlcyA9IFtdXG4gICAgICBzbG9wZXMucHVzaCgoc1kgLSBmcm9tTm9kZVkpIC8gKHNYIC0gZnJvbU5vZGVYKSlcbiAgICAgIHNsb3Blcy5wdXNoKChzWSAtIGZyb21Ob2RlWSkgLyAoZVggLSBmcm9tTm9kZVgpKVxuICAgICAgc2xvcGVzLnB1c2goKGVZIC0gZnJvbU5vZGVZKSAvIChlWCAtIGZyb21Ob2RlWCkpXG4gICAgICBzbG9wZXMucHVzaCgoZVkgLSBmcm9tTm9kZVkpIC8gKHNYIC0gZnJvbU5vZGVYKSlcblxuICAgICAgbGV0IG1pblNsb3BlID0gc2xvcGVzWzBdXG4gICAgICBsZXQgbWF4U2xvcGUgPSBzbG9wZXNbMF1cbiAgICAgIHNsb3Blcy5mb3JFYWNoKGZ1bmN0aW9uKGVudHJ5KSB7XG4gICAgICAgIGlmIChlbnRyeSA+IG1heFNsb3BlKSBtYXhTbG9wZSA9IGVudHJ5XG4gICAgICAgIGlmIChlbnRyeSA8IG1pblNsb3BlKSBtaW5TbG9wZSA9IGVudHJ5XG4gICAgICB9KVxuXG4gICAgICAvLyBGaW5kIHN5bmFwc2UtaW4tcXVlc3Rpb24ncyBzbG9wZVxuICAgICAgY29uc3Qgc3luU2xvcGUgPSAodG9Ob2RlWSAtIGZyb21Ob2RlWSkgLyAodG9Ob2RlWCAtIGZyb21Ob2RlWClcbiAgICAgIGNvbnN0IGIgPSBmcm9tTm9kZVkgLSBzeW5TbG9wZSAqIGZyb21Ob2RlWFxuXG4gICAgICAvLyBVc2UgdGhlIHNlbGVjdGlvbiBib3ggZWRnZXMgYXMgdGVzdCBjYXNlcyBmb3Igc3luYXBzZSBpbnRlcnNlY3Rpb25cbiAgICAgIGxldCB0ZXN0WCA9IHNYXG4gICAgICBsZXQgdGVzdFkgPSBzeW5TbG9wZSAqIHRlc3RYICsgYlxuXG4gICAgICBsZXQgc2VsZWN0VGVzdFxuXG4gICAgICBpZiAodGVzdFggPj0gbWluWCAmJiB0ZXN0WCA8PSBtYXhYICYmIHRlc3RZID49IG1pblkgJiYgdGVzdFkgPD0gbWF4WSAmJiB0ZXN0WSA+PSBtaW5Cb3hZICYmIHRlc3RZIDw9IG1heEJveFkpIHtcbiAgICAgICAgc2VsZWN0VGVzdCA9IHRydWVcbiAgICAgIH1cblxuICAgICAgdGVzdFggPSBlWFxuICAgICAgdGVzdFkgPSBzeW5TbG9wZSAqIHRlc3RYICsgYlxuXG4gICAgICBpZiAodGVzdFggPj0gbWluWCAmJiB0ZXN0WCA8PSBtYXhYICYmIHRlc3RZID49IG1pblkgJiYgdGVzdFkgPD0gbWF4WSAmJiB0ZXN0WSA+PSBtaW5Cb3hZICYmIHRlc3RZIDw9IG1heEJveFkpIHtcbiAgICAgICAgc2VsZWN0VGVzdCA9IHRydWVcbiAgICAgIH1cblxuICAgICAgdGVzdFkgPSBzWVxuICAgICAgdGVzdFggPSAodGVzdFkgLSBiKSAvIHN5blNsb3BlXG5cbiAgICAgIGlmICh0ZXN0WCA+PSBtaW5YICYmIHRlc3RYIDw9IG1heFggJiYgdGVzdFkgPj0gbWluWSAmJiB0ZXN0WSA8PSBtYXhZICYmIHRlc3RYID49IG1pbkJveFggJiYgdGVzdFggPD0gbWF4Qm94WCkge1xuICAgICAgICBzZWxlY3RUZXN0ID0gdHJ1ZVxuICAgICAgfVxuXG4gICAgICB0ZXN0WSA9IGVZXG4gICAgICB0ZXN0WCA9ICh0ZXN0WSAtIGIpIC8gc3luU2xvcGVcblxuICAgICAgaWYgKHRlc3RYID49IG1pblggJiYgdGVzdFggPD0gbWF4WCAmJiB0ZXN0WSA+PSBtaW5ZICYmIHRlc3RZIDw9IG1heFkgJiYgdGVzdFggPj0gbWluQm94WCAmJiB0ZXN0WCA8PSBtYXhCb3hYKSB7XG4gICAgICAgIHNlbGVjdFRlc3QgPSB0cnVlXG4gICAgICB9XG5cbiAgICAgIC8vIENhc2Ugd2hlcmUgdGhlIHN5bmFwc2UgaXMgd2hvbGx5IGVuY2xvc2VkIGluIHRoZSBzZWxkY3Rpb24gYm94XG4gICAgICBpZiAoZnJvbU5vZGVYID49IG1pbkJveFggJiYgZnJvbU5vZGVYIDw9IG1heEJveFggJiYgZnJvbU5vZGVZID49IG1pbkJveFkgJiYgZnJvbU5vZGVZIDw9IG1heEJveFkgJiYgdG9Ob2RlWCA+PSBtaW5Cb3hYICYmIHRvTm9kZVggPD0gbWF4Qm94WCAmJiB0b05vZGVZID49IG1pbkJveFkgJiYgdG9Ob2RlWSA8PSBtYXhCb3hZKSB7XG4gICAgICAgIHNlbGVjdFRlc3QgPSB0cnVlXG4gICAgICB9XG5cbiAgICAgIC8vIFRoZSB0ZXN0IHN5bmFwc2Ugd2FzIHNlbGVjdGVkIVxuXG4gICAgICBpZiAoc2VsZWN0VGVzdCkge1xuICAgICAgICAvLyBzaGlmdEtleSA9IHRvZ2dsZVNlbGVjdCwgb3RoZXJ3aXNlXG4gICAgICAgIGlmIChlLnNoaWZ0S2V5KSB7XG4gICAgICAgICAgaWYgKFNlbGVjdGVkLkVkZ2VzLmluZGV4T2YoZWRnZSkgIT09IC0xKSB7XG4gICAgICAgICAgICBDb250cm9sLmRlc2VsZWN0RWRnZShlZGdlKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBDb250cm9sLnNlbGVjdEVkZ2UoZWRnZSlcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgQ29udHJvbC5zZWxlY3RFZGdlKGVkZ2UpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KVxuICAgIE1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMgPSBmYWxzZVxuICAgIE1vdXNlLmJveEVuZENvb3JkaW5hdGVzID0gZmFsc2VcbiAgICBWaXN1YWxpemUubUdyYXBoLnBsb3QoKVxuICB9LCAvLyBzZWxlY3RXaXRoQm94XG4gIHNlbGVjdE5vZGVPbkNsaWNrSGFuZGxlcjogZnVuY3Rpb24obm9kZSwgZSkge1xuICAgIGlmIChWaXN1YWxpemUubUdyYXBoLmJ1c3kpIHJldHVyblxuXG4gICAgY29uc3Qgc2VsZiA9IEpJVFxuXG4gICAgLy8gQ29weSB0b3BpYyB0aXRsZSB0byBjbGlwYm9hcmRcbiAgICBpZiAoZS5idXR0b24gPT09IDEgJiYgZS5jdHJsS2V5KSBjbGlwYm9hcmQuY29weShub2RlLm5hbWUpXG5cbiAgICAvLyBjYXRjaCByaWdodCBjbGljayBvbiBtYWMsIHdoaWNoIGlzIG9mdGVuIGxpa2UgY3RybCtjbGlja1xuICAgIGlmIChuYXZpZ2F0b3IucGxhdGZvcm0uaW5kZXhPZignTWFjJykgIT09IC0xICYmIGUuY3RybEtleSkge1xuICAgICAgc2VsZi5zZWxlY3ROb2RlT25SaWdodENsaWNrSGFuZGxlcihub2RlLCBlKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gaWYgb24gYSB0b3BpYyBwYWdlLCBsZXQgYWx0K2NsaWNrIGNlbnRlciB5b3Ugb24gYSBuZXcgdG9waWNcbiAgICBpZiAoQWN0aXZlLlRvcGljICYmIGUuYWx0S2V5KSB7XG4gICAgICBKSVQuUkdyYXBoLmNlbnRlck9uKG5vZGUuaWQpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjb25zdCBjaGVjayA9IHNlbGYubm9kZVdhc0RvdWJsZUNsaWNrZWQoKVxuICAgIGlmIChjaGVjaykge1xuICAgICAgc2VsZi5ub2RlRG91YmxlQ2xpY2tIYW5kbGVyKG5vZGUsIGUpXG4gICAgICByZXR1cm5cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gd2FpdCBhIGNlcnRhaW4gbGVuZ3RoIG9mIHRpbWUsIHRoZW4gY2hlY2sgYWdhaW4sIHRoZW4gcnVuIHRoaXMgY29kZVxuICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKCFKSVQubm9kZVdhc0RvdWJsZUNsaWNrZWQoKSkge1xuICAgICAgICAgIGlmIChlLmJ1dHRvbiA9PT0gMSAmJiAhZS5jdHJsS2V5KSB7XG4gICAgICAgICAgICB2YXIgbGVuID0gU2VsZWN0ZWQuTm9kZXMubGVuZ3RoXG5cbiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuOyBpICs9IDEpIHtcbiAgICAgICAgICAgICAgbGV0IG4gPSBTZWxlY3RlZC5Ob2Rlc1tpXVxuICAgICAgICAgICAgICBsZXQgcmVzdWx0ID0gVXRpbC5vcGVuTGluayhEYXRhTW9kZWwuVG9waWNzLmdldChuLmlkKS5hdHRyaWJ1dGVzLmxpbmspXG5cbiAgICAgICAgICAgICAgaWYgKCFyZXN1bHQpIHsgLy8gaWYgbGluayBmYWlsZWQgdG8gb3BlblxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCFub2RlLnNlbGVjdGVkKSBVdGlsLm9wZW5MaW5rKERhdGFNb2RlbC5Ub3BpY3MuZ2V0KG5vZGUuaWQpLmF0dHJpYnV0ZXMubGluaylcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0sIE1vdXNlLkRPVUJMRV9DTElDS19UT0xFUkFOQ0UpXG4gICAgfVxuICB9LCAvLyBzZWxlY3ROb2RlT25DbGlja0hhbmRsZXJcbiAgc2VsZWN0Tm9kZU9uUmlnaHRDbGlja0hhbmRsZXI6IGZ1bmN0aW9uKG5vZGUsIGUpIHtcbiAgICAvLyB0aGUgJ25vZGUnIHZhcmlhYmxlIGlzIGEgSklUIG5vZGUsIHRoZSBvbmUgdGhhdCB3YXMgY2xpY2tlZCBvblxuICAgIC8vIHRoZSAnZScgdmFyaWFibGUgaXMgdGhlIGNsaWNrIGV2ZW50XG5cbiAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICBlLnN0b3BQcm9wYWdhdGlvbigpXG5cbiAgICBpZiAoVmlzdWFsaXplLm1HcmFwaC5idXN5KSByZXR1cm5cblxuICAgIC8vIHNlbGVjdCB0aGUgbm9kZVxuICAgIENvbnRyb2wuc2VsZWN0Tm9kZShub2RlLCBlKVxuXG4gICAgLy8gZGVsZXRlIG9sZCByaWdodCBjbGljayBtZW51XG4gICAgJCgnLnJpZ2h0Y2xpY2ttZW51JykucmVtb3ZlKClcbiAgICAvLyBjcmVhdGUgbmV3IG1lbnUgZm9yIGNsaWNrZWQgb24gbm9kZVxuICAgIGNvbnN0IHJpZ2h0Y2xpY2ttZW51ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICByaWdodGNsaWNrbWVudS5jbGFzc05hbWUgPSAncmlnaHRjbGlja21lbnUnXG4gICAgLy8gcHJldmVudCB0aGUgY3VzdG9tIGNvbnRleHQgbWVudSBmcm9tIGltbWVkaWF0ZWx5IG9wZW5pbmcgdGhlIGRlZmF1bHQgY29udGV4dCBtZW51IGFzIHdlbGxcbiAgICByaWdodGNsaWNrbWVudS5zZXRBdHRyaWJ1dGUoJ29uY29udGV4dG1lbnUnLCAncmV0dXJuIGZhbHNlJylcblxuICAgIC8vIGFkZCB0aGUgcHJvcGVyIG9wdGlvbnMgdG8gdGhlIG1lbnVcbiAgICBsZXQgbWVudXN0cmluZyA9ICc8dWw+J1xuXG4gICAgY29uc3QgYXV0aG9yaXplZCA9IEFjdGl2ZS5NYXAgJiYgQWN0aXZlLk1hcC5hdXRob3JpemVUb0VkaXQoQWN0aXZlLk1hcHBlcilcblxuICAgIGNvbnN0IGRpc2FibGVkID0gYXV0aG9yaXplZCA/ICcnIDogJ2Rpc2FibGVkJ1xuXG4gICAgaWYgKEFjdGl2ZS5NYXApIG1lbnVzdHJpbmcgKz0gJzxsaSBjbGFzcz1cInJjLWhpZGVcIj48ZGl2IGNsYXNzPVwicmMtaWNvblwiPjwvZGl2PkhpZGUgdW50aWwgcmVmcmVzaDxkaXYgY2xhc3M9XCJyYy1rZXlib2FyZFwiPkN0cmwrSDwvZGl2PjwvbGk+J1xuICAgIGlmIChBY3RpdmUuTWFwICYmIEFjdGl2ZS5NYXBwZXIpIG1lbnVzdHJpbmcgKz0gJzxsaSBjbGFzcz1cInJjLXJlbW92ZSAnICsgZGlzYWJsZWQgKyAnXCI+PGRpdiBjbGFzcz1cInJjLWljb25cIj48L2Rpdj5SZW1vdmUgZnJvbSBtYXA8ZGl2IGNsYXNzPVwicmMta2V5Ym9hcmRcIj5DdHJsK008L2Rpdj48L2xpPidcbiAgICBpZiAoQWN0aXZlLlRvcGljKSBtZW51c3RyaW5nICs9ICc8bGkgY2xhc3M9XCJyYy1yZW1vdmVcIj48ZGl2IGNsYXNzPVwicmMtaWNvblwiPjwvZGl2PlJlbW92ZSBmcm9tIHZpZXc8ZGl2IGNsYXNzPVwicmMta2V5Ym9hcmRcIj5DdHJsK008L2Rpdj48L2xpPidcbiAgICBpZiAoQWN0aXZlLk1hcCAmJiBBY3RpdmUuTWFwcGVyKSBtZW51c3RyaW5nICs9ICc8bGkgY2xhc3M9XCJyYy1kZWxldGUgJyArIGRpc2FibGVkICsgJ1wiPjxkaXYgY2xhc3M9XCJyYy1pY29uXCI+PC9kaXY+RGVsZXRlPGRpdiBjbGFzcz1cInJjLWtleWJvYXJkXCI+Q3RybCtEPC9kaXY+PC9saT4nXG5cbiAgICBpZiAoQWN0aXZlLlRvcGljKSB7XG4gICAgICBtZW51c3RyaW5nICs9ICc8bGkgY2xhc3M9XCJyYy1jZW50ZXJcIj48ZGl2IGNsYXNzPVwicmMtaWNvblwiPjwvZGl2PkNlbnRlciB0aGlzIHRvcGljPGRpdiBjbGFzcz1cInJjLWtleWJvYXJkXCI+QWx0K0U8L2Rpdj48L2xpPidcbiAgICB9XG5cbiAgICBtZW51c3RyaW5nICs9ICc8bGkgY2xhc3M9XCJyYy1wb3BvdXRcIj48ZGl2IGNsYXNzPVwicmMtaWNvblwiPjwvZGl2Pk9wZW4gaW4gbmV3IHRhYjwvbGk+J1xuXG4gICAgaWYgKEFjdGl2ZS5NYXBwZXIpIHtcbiAgICAgIGNvbnN0IG9wdGlvbnMgPSBvdXRkZW50YFxuICAgICAgICA8dWw+XG4gICAgICAgICAgPGxpIGNsYXNzPVwiY2hhbmdlUCB0b0NvbW1vbnNcIj48ZGl2IGNsYXNzPVwicmMtcGVybS1pY29uXCI+PC9kaXY+Y29tbW9uczwvbGk+XG4gICAgICAgICAgPGxpIGNsYXNzPVwiY2hhbmdlUCB0b1B1YmxpY1wiPjxkaXYgY2xhc3M9XCJyYy1wZXJtLWljb25cIj48L2Rpdj5wdWJsaWM8L2xpPlxuICAgICAgICAgIDxsaSBjbGFzcz1cImNoYW5nZVAgdG9Qcml2YXRlXCI+PGRpdiBjbGFzcz1cInJjLXBlcm0taWNvblwiPjwvZGl2PnByaXZhdGU8L2xpPlxuICAgICAgICA8L3VsPmBcblxuICAgICAgbWVudXN0cmluZyArPSAnPGxpIGNsYXNzPVwicmMtc3BhY2VyXCI+PC9saT4nXG5cbiAgICAgIG1lbnVzdHJpbmcgKz0gb3V0ZGVudGBcbiAgICAgICAgPGxpIGNsYXNzPVwicmMtcGVybWlzc2lvblwiPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJyYy1pY29uXCI+PC9kaXY+XG4gICAgICAgICAgQ2hhbmdlIHBlcm1pc3Npb25zXG4gICAgICAgICAgJHtvcHRpb25zfVxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJleHBhbmRMaVwiPjwvZGl2PlxuICAgICAgICA8L2xpPmBcblxuICAgICAgY29uc3QgbWV0YWNvZGVPcHRpb25zID0gJCgnI21ldGFjb2RlT3B0aW9ucycpLmh0bWwoKVxuXG4gICAgICBtZW51c3RyaW5nICs9ICc8bGkgY2xhc3M9XCJyYy1tZXRhY29kZVwiPjxkaXYgY2xhc3M9XCJyYy1pY29uXCI+PC9kaXY+Q2hhbmdlIG1ldGFjb2RlJyArIG1ldGFjb2RlT3B0aW9ucyArICc8ZGl2IGNsYXNzPVwiZXhwYW5kTGlcIj48L2Rpdj48L2xpPidcbiAgICB9XG4gICAgaWYgKEFjdGl2ZS5Ub3BpYykge1xuICAgICAgaWYgKCFBY3RpdmUuTWFwcGVyKSB7XG4gICAgICAgIG1lbnVzdHJpbmcgKz0gJzxsaSBjbGFzcz1cInJjLXNwYWNlclwiPjwvbGk+J1xuICAgICAgfVxuXG4gICAgICAvLyBzZXQgdXAgdGhlIGdldCBzaWJsaW5nIG1lbnUgYXMgYSBcImxhenkgbG9hZFwiXG4gICAgICAvLyBvbmx5IGZpbGwgaW4gdGhlIHN1Ym1lbnUgd2hlbiB0aGV5IGhvdmVyIG92ZXIgdGhlIGdldCBzaWJsaW5ncyBsaXN0IGl0ZW1cbiAgICAgIGNvbnN0IHNpYmxpbmdNZW51ID0gb3V0ZGVudGBcbiAgICAgICAgPHVsIGlkPVwiZmV0Y2hTaWJsaW5nTGlzdFwiPlxuICAgICAgICAgIDxsaSBjbGFzcz1cImZldGNoQWxsXCI+QWxsPGRpdiBjbGFzcz1cInJjLWtleWJvYXJkXCI+QWx0K1I8L2Rpdj48L2xpPlxuICAgICAgICAgIDxsaSBpZD1cImxvYWRpbmdTaWJsaW5nc1wiPjwvbGk+XG4gICAgICAgIDwvdWw+YFxuICAgICAgbWVudXN0cmluZyArPSAnPGxpIGNsYXNzPVwicmMtc2libGluZ3NcIj48ZGl2IGNsYXNzPVwicmMtaWNvblwiPjwvZGl2PlJldmVhbCBzaWJsaW5ncycgKyBzaWJsaW5nTWVudSArICc8ZGl2IGNsYXNzPVwiZXhwYW5kTGlcIj48L2Rpdj48L2xpPidcbiAgICB9XG5cbiAgICBtZW51c3RyaW5nICs9ICc8L3VsPidcbiAgICByaWdodGNsaWNrbWVudS5pbm5lckhUTUwgPSBtZW51c3RyaW5nXG5cbiAgICAvLyBwb3NpdGlvbiB0aGUgbWVudSB3aGVyZSB0aGUgY2xpY2sgaGFwcGVuZWRcbiAgICBjb25zdCBwb3NpdGlvbiA9IHt9XG4gICAgY29uc3QgUklHSFRDTElDS19XSURUSCA9IDMwMFxuICAgIGNvbnN0IFJJR0hUQ0xJQ0tfSEVJR0hUID0gMTQ0IC8vIHRoaXMgZG9lcyB2YXJ5IHNvbWV3aGF0LCBidXQgd2UgY2FuIHVzZSBzdGF0aWNcbiAgICBjb25zdCBTVUJNRU5VU19XSURUSCA9IDI1NlxuICAgIGNvbnN0IE1BWF9TVUJNRU5VX0hFSUdIVCA9IDI3MFxuICAgIGNvbnN0IHdpbmRvd1dpZHRoID0gJCh3aW5kb3cpLndpZHRoKClcbiAgICBjb25zdCB3aW5kb3dIZWlnaHQgPSAkKHdpbmRvdykuaGVpZ2h0KClcblxuICAgIGlmICh3aW5kb3dXaWR0aCAtIGUuY2xpZW50WCA8IFNVQk1FTlVTX1dJRFRIKSB7XG4gICAgICBwb3NpdGlvbi5yaWdodCA9IHdpbmRvd1dpZHRoIC0gZS5jbGllbnRYXG4gICAgICAkKHJpZ2h0Y2xpY2ttZW51KS5hZGRDbGFzcygnbW92ZU1lbnVzVG9MZWZ0JylcbiAgICB9IGVsc2UgaWYgKHdpbmRvd1dpZHRoIC0gZS5jbGllbnRYIDwgUklHSFRDTElDS19XSURUSCkge1xuICAgICAgcG9zaXRpb24ucmlnaHQgPSB3aW5kb3dXaWR0aCAtIGUuY2xpZW50WFxuICAgIH0gZWxzZSBpZiAod2luZG93V2lkdGggLSBlLmNsaWVudFggPCBSSUdIVENMSUNLX1dJRFRIICsgU1VCTUVOVVNfV0lEVEgpIHtcbiAgICAgIHBvc2l0aW9uLmxlZnQgPSBlLmNsaWVudFhcbiAgICAgICQocmlnaHRjbGlja21lbnUpLmFkZENsYXNzKCdtb3ZlTWVudXNUb0xlZnQnKVxuICAgIH0gZWxzZSB7XG4gICAgICBwb3NpdGlvbi5sZWZ0ID0gZS5jbGllbnRYXG4gICAgfVxuXG4gICAgaWYgKHdpbmRvd0hlaWdodCAtIGUuY2xpZW50WSA8IE1BWF9TVUJNRU5VX0hFSUdIVCkge1xuICAgICAgcG9zaXRpb24uYm90dG9tID0gd2luZG93SGVpZ2h0IC0gZS5jbGllbnRZXG4gICAgICAkKHJpZ2h0Y2xpY2ttZW51KS5hZGRDbGFzcygnbW92ZU1lbnVzVXAnKVxuICAgIH0gZWxzZSBpZiAod2luZG93SGVpZ2h0IC0gZS5jbGllbnRZIDwgUklHSFRDTElDS19IRUlHSFQgKyBNQVhfU1VCTUVOVV9IRUlHSFQpIHtcbiAgICAgIHBvc2l0aW9uLnRvcCA9IGUuY2xpZW50WVxuICAgICAgJChyaWdodGNsaWNrbWVudSkuYWRkQ2xhc3MoJ21vdmVNZW51c1VwJylcbiAgICB9IGVsc2Uge1xuICAgICAgcG9zaXRpb24udG9wID0gZS5jbGllbnRZXG4gICAgfVxuXG4gICAgJChyaWdodGNsaWNrbWVudSkuY3NzKHBvc2l0aW9uKVxuICAgIC8vIGFkZCB0aGUgbWVudSB0byB0aGUgcGFnZVxuICAgICQoJyN3cmFwcGVyJykuYXBwZW5kKHJpZ2h0Y2xpY2ttZW51KVxuXG4gICAgLy8gYXR0YWNoIGV2ZW50cyB0byBjbGlja3Mgb24gdGhlIGxpc3QgaXRlbXNcblxuICAgIC8vIGRlbGV0ZSB0aGUgc2VsZWN0ZWQgdGhpbmdzIGZyb20gdGhlIGRhdGFiYXNlXG4gICAgaWYgKGF1dGhvcml6ZWQpIHtcbiAgICAgICQoJy5yYy1kZWxldGUnKS5jbGljayhmdW5jdGlvbigpIHtcbiAgICAgICAgJCgnLnJpZ2h0Y2xpY2ttZW51JykucmVtb3ZlKClcbiAgICAgICAgQ29udHJvbC5kZWxldGVTZWxlY3RlZCgpXG4gICAgICB9KVxuICAgIH1cblxuICAgIC8vIHJlbW92ZSB0aGUgc2VsZWN0ZWQgdGhpbmdzIGZyb20gdGhlIG1hcFxuICAgIGlmIChBY3RpdmUuVG9waWMgfHwgYXV0aG9yaXplZCkge1xuICAgICAgJCgnLnJjLXJlbW92ZScpLmNsaWNrKGZ1bmN0aW9uKCkge1xuICAgICAgICAkKCcucmlnaHRjbGlja21lbnUnKS5yZW1vdmUoKVxuICAgICAgICBDb250cm9sLnJlbW92ZVNlbGVjdGVkRWRnZXMoKVxuICAgICAgICBDb250cm9sLnJlbW92ZVNlbGVjdGVkTm9kZXMoKVxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBoaWRlIHNlbGVjdGVkIG5vZGVzIGFuZCBzeW5hcHNlcyB1bnRpbCByZWZyZXNoXG4gICAgJCgnLnJjLWhpZGUnKS5jbGljayhmdW5jdGlvbigpIHtcbiAgICAgICQoJy5yaWdodGNsaWNrbWVudScpLnJlbW92ZSgpXG4gICAgICBDb250cm9sLmhpZGVTZWxlY3RlZEVkZ2VzKClcbiAgICAgIENvbnRyb2wuaGlkZVNlbGVjdGVkTm9kZXMoKVxuICAgIH0pXG5cbiAgICAvLyB3aGVuIGluIHJhZGlhbCwgY2VudGVyIG9uIHRoZSB0b3BpYyB5b3UgcGlja2VkXG4gICAgJCgnLnJjLWNlbnRlcicpLmNsaWNrKGZ1bmN0aW9uKCkge1xuICAgICAgJCgnLnJpZ2h0Y2xpY2ttZW51JykucmVtb3ZlKClcbiAgICAgIFRvcGljLmNlbnRlck9uKG5vZGUuaWQpXG4gICAgfSlcblxuICAgIC8vIG9wZW4gdGhlIGVudGl0eSBpbiBhIG5ldyB0YWJcbiAgICAkKCcucmMtcG9wb3V0JykuY2xpY2soZnVuY3Rpb24oKSB7XG4gICAgICAkKCcucmlnaHRjbGlja21lbnUnKS5yZW1vdmUoKVxuICAgICAgY29uc3Qgd2luID0gd2luZG93Lm9wZW4oJy90b3BpY3MvJyArIG5vZGUuaWQsICdfYmxhbmsnKVxuICAgICAgd2luLmZvY3VzKClcbiAgICB9KVxuXG4gICAgLy8gY2hhbmdlIHRoZSBwZXJtaXNzaW9uIG9mIGFsbCB0aGUgc2VsZWN0ZWQgbm9kZXMgYW5kIHN5bmFwc2VzIHRoYXQgeW91IHdlcmUgdGhlIG9yaWdpbmF0b3Igb2ZcbiAgICAkKCcucmMtcGVybWlzc2lvbiBsaScpLmNsaWNrKGZ1bmN0aW9uKCkge1xuICAgICAgJCgnLnJpZ2h0Y2xpY2ttZW51JykucmVtb3ZlKClcbiAgICAgIC8vICQodGhpcykudGV4dCgpIHdpbGwgYmUgJ2NvbW1vbnMnICdwdWJsaWMnIG9yICdwcml2YXRlJ1xuICAgICAgQ29udHJvbC51cGRhdGVTZWxlY3RlZFBlcm1pc3Npb25zKCQodGhpcykudGV4dCgpKVxuICAgIH0pXG5cbiAgICAvLyBjaGFuZ2UgdGhlIG1ldGFjb2RlIG9mIGFsbCB0aGUgc2VsZWN0ZWQgbm9kZXMgdGhhdCB5b3UgaGF2ZSBlZGl0IHBlcm1pc3Npb24gZm9yXG4gICAgJCgnLnJjLW1ldGFjb2RlIGxpIGxpJykuY2xpY2soZnVuY3Rpb24oKSB7XG4gICAgICAkKCcucmlnaHRjbGlja21lbnUnKS5yZW1vdmUoKVxuICAgICAgLy9cbiAgICAgIENvbnRyb2wudXBkYXRlU2VsZWN0ZWRNZXRhY29kZXMoJCh0aGlzKS5hdHRyKCdkYXRhLWlkJykpXG4gICAgfSlcblxuICAgIC8vIGZldGNoIHJlbGF0aXZlc1xuICAgIGxldCBmZXRjaFNlbnQgPSBmYWxzZVxuICAgICQoJy5yYy1zaWJsaW5ncycpLmhvdmVyKGZ1bmN0aW9uKCkge1xuICAgICAgaWYgKCFmZXRjaFNlbnQpIHtcbiAgICAgICAgSklULnBvcHVsYXRlUmlnaHRDbGlja1NpYmxpbmdzKG5vZGUpXG4gICAgICAgIGZldGNoU2VudCA9IHRydWVcbiAgICAgIH1cbiAgICB9KVxuICAgICQoJy5yYy1zaWJsaW5ncyAuZmV0Y2hBbGwnKS5jbGljayhmdW5jdGlvbigpIHtcbiAgICAgICQoJy5yaWdodGNsaWNrbWVudScpLnJlbW92ZSgpXG4gICAgICAvLyBkYXRhLWlkIGlzIGEgbWV0YWNvZGUgaWRcbiAgICAgIFRvcGljLmZldGNoUmVsYXRpdmVzKG5vZGUpXG4gICAgfSlcbiAgfSwgLy8gc2VsZWN0Tm9kZU9uUmlnaHRDbGlja0hhbmRsZXIsXG4gIHBvcHVsYXRlUmlnaHRDbGlja1NpYmxpbmdzOiBmdW5jdGlvbihub2RlKSB7XG4gICAgLy8gZGVwZW5kaW5nIG9uIGhvdyBtYW55IHRvcGljcyBhcmUgc2VsZWN0ZWQsIGRvIGRpZmZlcmVudCB0aGluZ3NcbiAgICBjb25zdCB0b3BpYyA9IG5vZGUuZ2V0RGF0YSgndG9waWMnKVxuXG4gICAgLy8gYWRkIGEgbG9hZGluZyBpY29uIGZvciBub3dcbiAgICBjb25zdCBsb2FkZXIgPSBuZXcgQ2FudmFzTG9hZGVyKCdsb2FkaW5nU2libGluZ3MnKVxuICAgIGxvYWRlci5zZXRDb2xvcignIzRGQzA1OScpIC8vIGRlZmF1bHQgaXMgJyMwMDAwMDAnXG4gICAgbG9hZGVyLnNldERpYW1ldGVyKDE1KSAvLyBkZWZhdWx0IGlzIDQwXG4gICAgbG9hZGVyLnNldERlbnNpdHkoNDEpIC8vIGRlZmF1bHQgaXMgNDBcbiAgICBsb2FkZXIuc2V0UmFuZ2UoMC45KSAvLyBkZWZhdWx0IGlzIDEuM1xuICAgIGxvYWRlci5zaG93KCkgLy8gSGlkZGVuIGJ5IGRlZmF1bHRcblxuICAgIGNvbnN0IHRvcGljcyA9IERhdGFNb2RlbC5Ub3BpY3MubWFwKGZ1bmN0aW9uKHQpIHsgcmV0dXJuIHQuaWQgfSlcbiAgICBjb25zdCB0b3BpY3NTdHJpbmcgPSB0b3BpY3Muam9pbigpXG5cbiAgICBjb25zdCBzdWNjZXNzQ2FsbGJhY2sgPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAkKCcjbG9hZGluZ1NpYmxpbmdzJykucmVtb3ZlKClcblxuICAgICAgZm9yICh2YXIga2V5IGluIGRhdGEpIHtcbiAgICAgICAgY29uc3Qgc3RyaW5nID0gYCR7RGF0YU1vZGVsLk1ldGFjb2Rlcy5nZXQoa2V5KS5nZXQoJ25hbWUnKX0gKCR7ZGF0YVtrZXldfSlgXG4gICAgICAgICQoJyNmZXRjaFNpYmxpbmdMaXN0JykuYXBwZW5kKGA8bGkgY2xhc3M9XCJnZXRTaWJsaW5nc1wiIGRhdGEtaWQ9XCIke2tleX1cIj4ke3N0cmluZ308L2xpPmApXG4gICAgICB9XG5cbiAgICAgICQoJy5yYy1zaWJsaW5ncyAuZ2V0U2libGluZ3MnKS5jbGljayhmdW5jdGlvbigpIHtcbiAgICAgICAgJCgnLnJpZ2h0Y2xpY2ttZW51JykucmVtb3ZlKClcbiAgICAgICAgLy8gZGF0YS1pZCBpcyBhIG1ldGFjb2RlIGlkXG4gICAgICAgIFRvcGljLmZldGNoUmVsYXRpdmVzKG5vZGUsICQodGhpcykuYXR0cignZGF0YS1pZCcpKVxuICAgICAgfSlcbiAgICB9XG5cbiAgICAkLmFqYXgoe1xuICAgICAgdHlwZTogJ0dFVCcsXG4gICAgICB1cmw6ICcvdG9waWNzLycgKyB0b3BpYy5pZCArICcvcmVsYXRpdmVfbnVtYmVycy5qc29uP25ldHdvcms9JyArIHRvcGljc1N0cmluZyxcbiAgICAgIHN1Y2Nlc3M6IHN1Y2Nlc3NDYWxsYmFjayxcbiAgICAgIGVycm9yOiBmdW5jdGlvbigpIHt9XG4gICAgfSlcbiAgfSxcbiAgc2VsZWN0RWRnZU9uQ2xpY2tIYW5kbGVyOiBmdW5jdGlvbihhZGosIGUpIHtcbiAgICBpZiAoVmlzdWFsaXplLm1HcmFwaC5idXN5KSByZXR1cm5cblxuICAgIGNvbnN0IHNlbGYgPSBKSVRcbiAgICB2YXIgc3luYXBzZVRleHQgPSBhZGouZGF0YS4kc3luYXBzZXNbMF0uYXR0cmlidXRlcy5kZXNjXG4gICAgLy8gQ29weSBzeW5hcHNlIGxhYmVsIHRvIGNsaXBib2FyZFxuICAgIGlmIChlLmJ1dHRvbiA9PT0gMSAmJiBlLmN0cmxLZXkgJiYgc3luYXBzZVRleHQgIT09ICcnKSBjbGlwYm9hcmQuY29weShzeW5hcHNlVGV4dClcblxuICAgIC8vIGNhdGNoIHJpZ2h0IGNsaWNrIG9uIG1hYywgd2hpY2ggaXMgb2Z0ZW4gbGlrZSBjdHJsK2NsaWNrXG4gICAgaWYgKG5hdmlnYXRvci5wbGF0Zm9ybS5pbmRleE9mKCdNYWMnKSAhPT0gLTEgJiYgZS5jdHJsS2V5KSB7XG4gICAgICBzZWxmLnNlbGVjdEVkZ2VPblJpZ2h0Q2xpY2tIYW5kbGVyKGFkaiwgZSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IGNoZWNrID0gc2VsZi5ub2RlV2FzRG91YmxlQ2xpY2tlZCgpXG4gICAgaWYgKGNoZWNrKSB7XG4gICAgICBzZWxmLmVkZ2VEb3VibGVDbGlja0hhbmRsZXIoYWRqLCBlKVxuICAgICAgcmV0dXJuXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIHdhaXQgYSBjZXJ0YWluIGxlbmd0aCBvZiB0aW1lLCB0aGVuIGNoZWNrIGFnYWluLCB0aGVuIHJ1biB0aGlzIGNvZGVcbiAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmICghSklULm5vZGVXYXNEb3VibGVDbGlja2VkKCkpIHtcbiAgICAgICAgICBjb25zdCBlZGdlQWxyZWFkeVNlbGVjdGVkID0gU2VsZWN0ZWQuRWRnZXMuaW5kZXhPZihhZGopICE9PSAtMVxuXG4gICAgICAgICAgaWYgKCFlLnNoaWZ0S2V5KSB7XG4gICAgICAgICAgICBDb250cm9sLmRlc2VsZWN0QWxsTm9kZXMoKVxuICAgICAgICAgICAgQ29udHJvbC5kZXNlbGVjdEFsbEVkZ2VzKClcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoZWRnZUFscmVhZHlTZWxlY3RlZCkge1xuICAgICAgICAgICAgQ29udHJvbC5kZXNlbGVjdEVkZ2UoYWRqKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBDb250cm9sLnNlbGVjdEVkZ2UoYWRqKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIFZpc3VhbGl6ZS5tR3JhcGgucGxvdCgpXG4gICAgICAgIH1cbiAgICAgIH0sIE1vdXNlLkRPVUJMRV9DTElDS19UT0xFUkFOQ0UpXG4gICAgfVxuICB9LCAvLyBzZWxlY3RFZGdlT25DbGlja0hhbmRsZXJcbiAgc2VsZWN0RWRnZU9uUmlnaHRDbGlja0hhbmRsZXI6IGZ1bmN0aW9uKGFkaiwgZSkge1xuICAgIC8vIHRoZSAnbm9kZScgdmFyaWFibGUgaXMgYSBKSVQgbm9kZSwgdGhlIG9uZSB0aGF0IHdhcyBjbGlja2VkIG9uXG4gICAgLy8gdGhlICdlJyB2YXJpYWJsZSBpcyB0aGUgY2xpY2sgZXZlbnRcblxuICAgIGlmIChhZGouZ2V0RGF0YSgnYWxwaGEnKSA9PT0gMCkgcmV0dXJuIC8vIGRvbid0IGRvIGFueXRoaW5nIGlmIHRoZSBlZGdlIGlzIGZpbHRlcmVkXG5cbiAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICBlLnN0b3BQcm9wYWdhdGlvbigpXG5cbiAgICBpZiAoVmlzdWFsaXplLm1HcmFwaC5idXN5KSByZXR1cm5cblxuICAgIENvbnRyb2wuc2VsZWN0RWRnZShhZGopXG5cbiAgICAvLyBkZWxldGUgb2xkIHJpZ2h0IGNsaWNrIG1lbnVcbiAgICAkKCcucmlnaHRjbGlja21lbnUnKS5yZW1vdmUoKVxuICAgIC8vIGNyZWF0ZSBuZXcgbWVudSBmb3IgY2xpY2tlZCBvbiBub2RlXG4gICAgY29uc3QgcmlnaHRjbGlja21lbnUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKVxuICAgIHJpZ2h0Y2xpY2ttZW51LmNsYXNzTmFtZSA9ICdyaWdodGNsaWNrbWVudSdcbiAgICAvLyBwcmV2ZW50IHRoZSBjdXN0b20gY29udGV4dCBtZW51IGZyb20gaW1tZWRpYXRlbHkgb3BlbmluZyB0aGUgZGVmYXVsdCBjb250ZXh0IG1lbnUgYXMgd2VsbFxuICAgIHJpZ2h0Y2xpY2ttZW51LnNldEF0dHJpYnV0ZSgnb25jb250ZXh0bWVudScsICdyZXR1cm4gZmFsc2UnKVxuXG4gICAgLy8gYWRkIHRoZSBwcm9wZXIgb3B0aW9ucyB0byB0aGUgbWVudVxuICAgIGxldCBtZW51c3RyaW5nID0gJzx1bD4nXG5cbiAgICBjb25zdCBhdXRob3JpemVkID0gQWN0aXZlLk1hcCAmJiBBY3RpdmUuTWFwLmF1dGhvcml6ZVRvRWRpdChBY3RpdmUuTWFwcGVyKVxuXG4gICAgY29uc3QgZGlzYWJsZWQgPSBhdXRob3JpemVkID8gJycgOiAnZGlzYWJsZWQnXG5cbiAgICBpZiAoQWN0aXZlLk1hcCkgbWVudXN0cmluZyArPSAnPGxpIGNsYXNzPVwicmMtaGlkZVwiPjxkaXYgY2xhc3M9XCJyYy1pY29uXCI+PC9kaXY+SGlkZSB1bnRpbCByZWZyZXNoPGRpdiBjbGFzcz1cInJjLWtleWJvYXJkXCI+Q3RybCtIPC9kaXY+PC9saT4nXG4gICAgaWYgKEFjdGl2ZS5NYXAgJiYgQWN0aXZlLk1hcHBlcikgbWVudXN0cmluZyArPSAnPGxpIGNsYXNzPVwicmMtcmVtb3ZlICcgKyBkaXNhYmxlZCArICdcIj48ZGl2IGNsYXNzPVwicmMtaWNvblwiPjwvZGl2PlJlbW92ZSBmcm9tIG1hcDxkaXYgY2xhc3M9XCJyYy1rZXlib2FyZFwiPkN0cmwrTTwvZGl2PjwvbGk+J1xuICAgIGlmIChBY3RpdmUuVG9waWMpIG1lbnVzdHJpbmcgKz0gJzxsaSBjbGFzcz1cInJjLXJlbW92ZVwiPjxkaXYgY2xhc3M9XCJyYy1pY29uXCI+PC9kaXY+UmVtb3ZlIGZyb20gdmlldzxkaXYgY2xhc3M9XCJyYy1rZXlib2FyZFwiPkN0cmwrTTwvZGl2PjwvbGk+J1xuICAgIGlmIChBY3RpdmUuTWFwICYmIEFjdGl2ZS5NYXBwZXIpIG1lbnVzdHJpbmcgKz0gJzxsaSBjbGFzcz1cInJjLWRlbGV0ZSAnICsgZGlzYWJsZWQgKyAnXCI+PGRpdiBjbGFzcz1cInJjLWljb25cIj48L2Rpdj5EZWxldGU8ZGl2IGNsYXNzPVwicmMta2V5Ym9hcmRcIj5DdHJsK0Q8L2Rpdj48L2xpPidcblxuICAgIGlmIChBY3RpdmUuTWFwICYmIEFjdGl2ZS5NYXBwZXIpIG1lbnVzdHJpbmcgKz0gJzxsaSBjbGFzcz1cInJjLXNwYWNlclwiPjwvbGk+J1xuXG4gICAgaWYgKEFjdGl2ZS5NYXBwZXIpIHtcbiAgICAgIGNvbnN0IHBlcm1PcHRpb25zID0gb3V0ZGVudGBcbiAgICAgICAgPHVsPlxuICAgICAgICAgIDxsaSBjbGFzcz1cImNoYW5nZVAgdG9Db21tb25zXCI+PGRpdiBjbGFzcz1cInJjLXBlcm0taWNvblwiPjwvZGl2PmNvbW1vbnM8L2xpPlxuICAgICAgICAgIDxsaSBjbGFzcz1cImNoYW5nZVAgdG9QdWJsaWNcIj48ZGl2IGNsYXNzPVwicmMtcGVybS1pY29uXCI+PC9kaXY+cHVibGljPC9saT4gICAgICAgICAgIDxsaSBjbGFzcz1cImNoYW5nZVAgdG9Qcml2YXRlXCI+PGRpdiBjbGFzcz1cInJjLXBlcm0taWNvblwiPjwvZGl2PnByaXZhdGU8L2xpPiAgICAgICAgIDwvdWw+YFxuXG4gICAgICBtZW51c3RyaW5nICs9ICc8bGkgY2xhc3M9XCJyYy1wZXJtaXNzaW9uXCI+PGRpdiBjbGFzcz1cInJjLWljb25cIj48L2Rpdj5DaGFuZ2UgcGVybWlzc2lvbnMnICsgcGVybU9wdGlvbnMgKyAnPGRpdiBjbGFzcz1cImV4cGFuZExpXCI+PC9kaXY+PC9saT4nXG4gICAgfVxuXG4gICAgbWVudXN0cmluZyArPSAnPC91bD4nXG4gICAgcmlnaHRjbGlja21lbnUuaW5uZXJIVE1MID0gbWVudXN0cmluZ1xuXG4gICAgLy8gcG9zaXRpb24gdGhlIG1lbnUgd2hlcmUgdGhlIGNsaWNrIGhhcHBlbmVkXG4gICAgY29uc3QgcG9zaXRpb24gPSB7fVxuICAgIGNvbnN0IFJJR0hUQ0xJQ0tfV0lEVEggPSAzMDBcbiAgICBjb25zdCBSSUdIVENMSUNLX0hFSUdIVCA9IDE0NCAvLyB0aGlzIGRvZXMgdmFyeSBzb21ld2hhdCwgYnV0IHdlIGNhbiB1c2Ugc3RhdGljXG4gICAgY29uc3QgU1VCTUVOVVNfV0lEVEggPSAyNTZcbiAgICBjb25zdCBNQVhfU1VCTUVOVV9IRUlHSFQgPSAyNzBcbiAgICBjb25zdCB3aW5kb3dXaWR0aCA9ICQod2luZG93KS53aWR0aCgpXG4gICAgY29uc3Qgd2luZG93SGVpZ2h0ID0gJCh3aW5kb3cpLmhlaWdodCgpXG5cbiAgICBpZiAod2luZG93V2lkdGggLSBlLmNsaWVudFggPCBTVUJNRU5VU19XSURUSCkge1xuICAgICAgcG9zaXRpb24ucmlnaHQgPSB3aW5kb3dXaWR0aCAtIGUuY2xpZW50WFxuICAgICAgJChyaWdodGNsaWNrbWVudSkuYWRkQ2xhc3MoJ21vdmVNZW51c1RvTGVmdCcpXG4gICAgfSBlbHNlIGlmICh3aW5kb3dXaWR0aCAtIGUuY2xpZW50WCA8IFJJR0hUQ0xJQ0tfV0lEVEgpIHtcbiAgICAgIHBvc2l0aW9uLnJpZ2h0ID0gd2luZG93V2lkdGggLSBlLmNsaWVudFhcbiAgICB9IGVsc2UgcG9zaXRpb24ubGVmdCA9IGUuY2xpZW50WFxuXG4gICAgaWYgKHdpbmRvd0hlaWdodCAtIGUuY2xpZW50WSA8IE1BWF9TVUJNRU5VX0hFSUdIVCkge1xuICAgICAgcG9zaXRpb24uYm90dG9tID0gd2luZG93SGVpZ2h0IC0gZS5jbGllbnRZXG4gICAgICAkKHJpZ2h0Y2xpY2ttZW51KS5hZGRDbGFzcygnbW92ZU1lbnVzVXAnKVxuICAgIH0gZWxzZSBpZiAod2luZG93SGVpZ2h0IC0gZS5jbGllbnRZIDwgUklHSFRDTElDS19IRUlHSFQgKyBNQVhfU1VCTUVOVV9IRUlHSFQpIHtcbiAgICAgIHBvc2l0aW9uLnRvcCA9IGUuY2xpZW50WVxuICAgICAgJChyaWdodGNsaWNrbWVudSkuYWRkQ2xhc3MoJ21vdmVNZW51c1VwJylcbiAgICB9IGVsc2UgcG9zaXRpb24udG9wID0gZS5jbGllbnRZXG5cbiAgICAkKHJpZ2h0Y2xpY2ttZW51KS5jc3MocG9zaXRpb24pXG5cbiAgICAvLyBhZGQgdGhlIG1lbnUgdG8gdGhlIHBhZ2VcbiAgICAkKCcjd3JhcHBlcicpLmFwcGVuZChyaWdodGNsaWNrbWVudSlcblxuICAgIC8vIGF0dGFjaCBldmVudHMgdG8gY2xpY2tzIG9uIHRoZSBsaXN0IGl0ZW1zXG5cbiAgICAvLyBkZWxldGUgdGhlIHNlbGVjdGVkIHRoaW5ncyBmcm9tIHRoZSBkYXRhYmFzZVxuICAgIGlmIChhdXRob3JpemVkKSB7XG4gICAgICAkKCcucmMtZGVsZXRlJykuY2xpY2soZnVuY3Rpb24oKSB7XG4gICAgICAgICQoJy5yaWdodGNsaWNrbWVudScpLnJlbW92ZSgpXG4gICAgICAgIENvbnRyb2wuZGVsZXRlU2VsZWN0ZWQoKVxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyByZW1vdmUgdGhlIHNlbGVjdGVkIHRoaW5ncyBmcm9tIHRoZSBtYXBcbiAgICBpZiAoYXV0aG9yaXplZCkge1xuICAgICAgJCgnLnJjLXJlbW92ZScpLmNsaWNrKGZ1bmN0aW9uKCkge1xuICAgICAgICAkKCcucmlnaHRjbGlja21lbnUnKS5yZW1vdmUoKVxuICAgICAgICBDb250cm9sLnJlbW92ZVNlbGVjdGVkRWRnZXMoKVxuICAgICAgICBDb250cm9sLnJlbW92ZVNlbGVjdGVkTm9kZXMoKVxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBoaWRlIHNlbGVjdGVkIG5vZGVzIGFuZCBzeW5hcHNlcyB1bnRpbCByZWZyZXNoXG4gICAgJCgnLnJjLWhpZGUnKS5jbGljayhmdW5jdGlvbigpIHtcbiAgICAgICQoJy5yaWdodGNsaWNrbWVudScpLnJlbW92ZSgpXG4gICAgICBDb250cm9sLmhpZGVTZWxlY3RlZEVkZ2VzKClcbiAgICAgIENvbnRyb2wuaGlkZVNlbGVjdGVkTm9kZXMoKVxuICAgIH0pXG5cbiAgICAvLyBjaGFuZ2UgdGhlIHBlcm1pc3Npb24gb2YgYWxsIHRoZSBzZWxlY3RlZCBub2RlcyBhbmQgc3luYXBzZXMgdGhhdCB5b3Ugd2VyZSB0aGUgb3JpZ2luYXRvciBvZlxuICAgICQoJy5yYy1wZXJtaXNzaW9uIGxpJykuY2xpY2soZnVuY3Rpb24oKSB7XG4gICAgICAkKCcucmlnaHRjbGlja21lbnUnKS5yZW1vdmUoKVxuICAgICAgLy8gJCh0aGlzKS50ZXh0KCkgd2lsbCBiZSAnY29tbW9ucycgJ3B1YmxpYycgb3IgJ3ByaXZhdGUnXG4gICAgICBDb250cm9sLnVwZGF0ZVNlbGVjdGVkUGVybWlzc2lvbnMoJCh0aGlzKS50ZXh0KCkpXG4gICAgfSlcbiAgfSwgLy8gc2VsZWN0RWRnZU9uUmlnaHRDbGlja0hhbmRsZXJcbiAgU21vb3RoUGFubmluZzogZnVuY3Rpb24oKSB7XG4gICAgY29uc3Qgc3ggPSBWaXN1YWxpemUubUdyYXBoLmNhbnZhcy5zY2FsZU9mZnNldFhcbiAgICBjb25zdCBzeSA9IFZpc3VhbGl6ZS5tR3JhcGguY2FudmFzLnNjYWxlT2Zmc2V0WVxuICAgIGNvbnN0IHlWZWxvY2l0eSA9IE1vdXNlLmNoYW5nZUluWSAvLyBpbml0aWFsIHkgdmVsb2NpdHlcbiAgICBjb25zdCB4VmVsb2NpdHkgPSBNb3VzZS5jaGFuZ2VJblggLy8gaW5pdGlhbCB4IHZlbG9jaXR5XG4gICAgbGV0IGVhc2luZyA9IDEgLy8gZnJpY3Rpb25hbCB2YWx1ZVxuXG4gICAgd2luZG93LmNsZWFySW50ZXJ2YWwocGFubmluZ0ludClcbiAgICBwYW5uaW5nSW50ID0gc2V0SW50ZXJ2YWwoZnVuY3Rpb24oKSB7XG4gICAgICBteVRpbWVyKClcbiAgICB9LCAxKVxuXG4gICAgZnVuY3Rpb24gbXlUaW1lcigpIHtcbiAgICAgIFZpc3VhbGl6ZS5tR3JhcGguY2FudmFzLnRyYW5zbGF0ZSh4VmVsb2NpdHkgKiBlYXNpbmcgKiAxIC8gc3gsIHlWZWxvY2l0eSAqIGVhc2luZyAqIDEgLyBzeSlcbiAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoSklULmV2ZW50cy5wYW4pXG4gICAgICBlYXNpbmcgPSBlYXNpbmcgKiAwLjc1XG5cbiAgICAgIGlmIChlYXNpbmcgPCAwLjEpIHdpbmRvdy5jbGVhckludGVydmFsKHBhbm5pbmdJbnQpXG4gICAgfVxuICB9LCAvLyBTbW9vdGhQYW5uaW5nXG4gIHJlbmRlck1pZEFycm93OiBmdW5jdGlvbihmcm9tLCB0bywgZGltLCBzd2FwLCBjYW52YXMsIHBsYWNlbWVudCwgbmV3U3luYXBzZSkge1xuICAgIGNvbnN0IGN0eCA9IGNhbnZhcy5nZXRDdHgoKVxuICAgIC8vIGludmVydCBlZGdlIGRpcmVjdGlvblxuICAgIGlmIChzd2FwKSB7XG4gICAgICBjb25zdCB0bXAgPSBmcm9tXG4gICAgICBmcm9tID0gdG9cbiAgICAgIHRvID0gdG1wXG4gICAgfVxuICAgIC8vIHZlY3QgcmVwcmVzZW50cyBhIGxpbmUgZnJvbSB0aXAgdG8gdGFpbCBvZiB0aGUgYXJyb3dcbiAgICBjb25zdCB2ZWN0ID0gbmV3ICRqaXQuQ29tcGxleCh0by54IC0gZnJvbS54LCB0by55IC0gZnJvbS55KVxuICAgIC8vIHNjYWxlIGl0XG4gICAgdmVjdC4kc2NhbGUoZGltIC8gdmVjdC5ub3JtKCkpXG4gICAgLy8gY29tcHV0ZSB0aGUgbWlkcG9pbnQgb2YgdGhlIGVkZ2UgbGluZVxuICAgIGNvbnN0IG5ld1ggPSAodG8ueCAtIGZyb20ueCkgKiBwbGFjZW1lbnQgKyBmcm9tLnhcbiAgICBjb25zdCBuZXdZID0gKHRvLnkgLSBmcm9tLnkpICogcGxhY2VtZW50ICsgZnJvbS55XG4gICAgY29uc3QgbWlkUG9pbnQgPSBuZXcgJGppdC5Db21wbGV4KG5ld1gsIG5ld1kpXG5cbiAgICAvLyBtb3ZlIG1pZHBvaW50IGJ5IGhhbGYgdGhlIFwibGVuZ3RoXCIgb2YgdGhlIGFycm93IHNvIHRoZSBhcnJvdyBpcyBjZW50ZXJlZCBvbiB0aGUgbWlkcG9pbnRcbiAgICBjb25zdCBhcnJvd1BvaW50ID0gbmV3ICRqaXQuQ29tcGxleCgodmVjdC54IC8gMC43KSArIG1pZFBvaW50LngsICh2ZWN0LnkgLyAwLjcpICsgbWlkUG9pbnQueSlcbiAgICAvLyBjb21wdXRlIHRoZSB0YWlsIGludGVyc2VjdGlvbiBwb2ludCB3aXRoIHRoZSBlZGdlIGxpbmVcbiAgICBjb25zdCBpbnRlcm1lZGlhdGVQb2ludCA9IG5ldyAkaml0LkNvbXBsZXgoYXJyb3dQb2ludC54IC0gdmVjdC54LCBhcnJvd1BvaW50LnkgLSB2ZWN0LnkpXG4gICAgLy8gdmVjdG9yIHBlcnBlbmRpY3VsYXIgdG8gdmVjdFxuICAgIGNvbnN0IG5vcm1hbCA9IG5ldyAkaml0LkNvbXBsZXgoLXZlY3QueSAvIDIsIHZlY3QueCAvIDIpXG4gICAgY29uc3QgdjEgPSBpbnRlcm1lZGlhdGVQb2ludC5hZGQobm9ybWFsKVxuICAgIGNvbnN0IHYyID0gaW50ZXJtZWRpYXRlUG9pbnQuJGFkZChub3JtYWwuJHNjYWxlKC0xKSlcblxuICAgIGlmIChuZXdTeW5hcHNlKSB7XG4gICAgICBjdHguc3Ryb2tlU3R5bGUgPSAnIzRmYzA1OSdcbiAgICAgIGN0eC5saW5lV2lkdGggPSAyXG4gICAgICBjdHguZ2xvYmFsQWxwaGEgPSAxXG4gICAgfVxuICAgIGN0eC5iZWdpblBhdGgoKVxuICAgIGN0eC5tb3ZlVG8oZnJvbS54LCBmcm9tLnkpXG4gICAgY3R4LmxpbmVUbyh0by54LCB0by55KVxuICAgIGN0eC5zdHJva2UoKVxuICAgIGN0eC5iZWdpblBhdGgoKVxuICAgIGN0eC5tb3ZlVG8odjEueCwgdjEueSlcbiAgICBjdHgubGluZVRvKGFycm93UG9pbnQueCwgYXJyb3dQb2ludC55KVxuICAgIGN0eC5saW5lVG8odjIueCwgdjIueSlcbiAgICBjdHguc3Ryb2tlKClcbiAgfSwgLy8gcmVuZGVyTWlkQXJyb3dcbiAgcmVuZGVyRWRnZUFycm93czogZnVuY3Rpb24oZWRnZUhlbHBlciwgYWRqLCBzeW5hcHNlLCBjYW52YXMpIHtcbiAgICBjb25zdCBzZWxmID0gSklUXG5cbiAgICBjb25zdCBkaXJlY3Rpb25DYXQgPSBzeW5hcHNlLmdldCgnY2F0ZWdvcnknKVxuICAgIGNvbnN0IGRpcmVjdGlvbiA9IHN5bmFwc2UuZ2V0RGlyZWN0aW9uKClcblxuICAgIGNvbnN0IHBvcyA9IGFkai5ub2RlRnJvbS5wb3MuZ2V0Yyh0cnVlKVxuICAgIGNvbnN0IHBvc0NoaWxkID0gYWRqLm5vZGVUby5wb3MuZ2V0Yyh0cnVlKVxuXG4gICAgLy8gcGxvdCBhcnJvdyBlZGdlXG4gICAgaWYgKCFkaXJlY3Rpb24pIHtcbiAgICAgIC8vIHJlbmRlciBub3RoaW5nIGZvciB0aGlzIGFycm93IGlmIHRoZSBkaXJlY3Rpb24gY291bGRuJ3QgYmUgcmV0cmlldmVkXG4gICAgfSBlbHNlIGlmIChkaXJlY3Rpb25DYXQgPT09ICdub25lJykge1xuICAgICAgZWRnZUhlbHBlci5saW5lLnJlbmRlcih7XG4gICAgICAgIHg6IHBvcy54LFxuICAgICAgICB5OiBwb3MueVxuICAgICAgfSwge1xuICAgICAgICB4OiBwb3NDaGlsZC54LFxuICAgICAgICB5OiBwb3NDaGlsZC55XG4gICAgICB9LCBjYW52YXMpXG4gICAgfSBlbHNlIGlmIChkaXJlY3Rpb25DYXQgPT09ICdib3RoJykge1xuICAgICAgc2VsZi5yZW5kZXJNaWRBcnJvdyh7XG4gICAgICAgIHg6IHBvcy54LFxuICAgICAgICB5OiBwb3MueVxuICAgICAgfSwge1xuICAgICAgICB4OiBwb3NDaGlsZC54LFxuICAgICAgICB5OiBwb3NDaGlsZC55XG4gICAgICB9LCAxMywgdHJ1ZSwgY2FudmFzLCAwLjcpXG4gICAgICBzZWxmLnJlbmRlck1pZEFycm93KHtcbiAgICAgICAgeDogcG9zLngsXG4gICAgICAgIHk6IHBvcy55XG4gICAgICB9LCB7XG4gICAgICAgIHg6IHBvc0NoaWxkLngsXG4gICAgICAgIHk6IHBvc0NoaWxkLnlcbiAgICAgIH0sIDEzLCBmYWxzZSwgY2FudmFzLCAwLjcpXG4gICAgfSBlbHNlIGlmIChkaXJlY3Rpb25DYXQgPT09ICdmcm9tLXRvJykge1xuICAgICAgY29uc3QgaW52ID0gKGRpcmVjdGlvblswXSAhPT0gYWRqLm5vZGVGcm9tLmlkKVxuICAgICAgc2VsZi5yZW5kZXJNaWRBcnJvdyh7XG4gICAgICAgIHg6IHBvcy54LFxuICAgICAgICB5OiBwb3MueVxuICAgICAgfSwge1xuICAgICAgICB4OiBwb3NDaGlsZC54LFxuICAgICAgICB5OiBwb3NDaGlsZC55XG4gICAgICB9LCAxMywgaW52LCBjYW52YXMsIDAuNylcbiAgICAgIHNlbGYucmVuZGVyTWlkQXJyb3coe1xuICAgICAgICB4OiBwb3MueCxcbiAgICAgICAgeTogcG9zLnlcbiAgICAgIH0sIHtcbiAgICAgICAgeDogcG9zQ2hpbGQueCxcbiAgICAgICAgeTogcG9zQ2hpbGQueVxuICAgICAgfSwgMTMsIGludiwgY2FudmFzLCAwLjMpXG4gICAgfVxuICB9LCAvLyByZW5kZXJFZGdlQXJyb3dzXG4gIHpvb21JbjogZnVuY3Rpb24oZXZlbnQpIHtcbiAgICBWaXN1YWxpemUubUdyYXBoLmNhbnZhcy5zY2FsZSgxLjI1LCAxLjI1KVxuICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoSklULmV2ZW50cy56b29tLCBbZXZlbnRdKVxuICB9LFxuICB6b29tT3V0OiBmdW5jdGlvbihldmVudCkge1xuICAgIFZpc3VhbGl6ZS5tR3JhcGguY2FudmFzLnNjYWxlKDAuOCwgMC44KVxuICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoSklULmV2ZW50cy56b29tLCBbZXZlbnRdKVxuICB9LFxuICBjZW50ZXJNYXA6IGZ1bmN0aW9uKGNhbnZhcykge1xuICAgIGNvbnN0IG9mZnNldFNjYWxlID0gY2FudmFzLnNjYWxlT2Zmc2V0WFxuXG4gICAgY2FudmFzLnNjYWxlKDEgLyBvZmZzZXRTY2FsZSwgMSAvIG9mZnNldFNjYWxlKVxuXG4gICAgY29uc3Qgb2Zmc2V0WCA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRYXG4gICAgY29uc3Qgb2Zmc2V0WSA9IGNhbnZhcy50cmFuc2xhdGVPZmZzZXRZXG5cbiAgICBjYW52YXMudHJhbnNsYXRlKC0xICogb2Zmc2V0WCwgLTEgKiBvZmZzZXRZKVxuICB9LFxuICB6b29tVG9Cb3g6IGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgY29uc3Qgc1ggPSBNb3VzZS5ib3hTdGFydENvb3JkaW5hdGVzLnhcbiAgICBjb25zdCBzWSA9IE1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMueVxuICAgIGNvbnN0IGVYID0gTW91c2UuYm94RW5kQ29vcmRpbmF0ZXMueFxuICAgIGNvbnN0IGVZID0gTW91c2UuYm94RW5kQ29vcmRpbmF0ZXMueVxuXG4gICAgbGV0IGNhbnZhcyA9IFZpc3VhbGl6ZS5tR3JhcGguY2FudmFzXG4gICAgSklULmNlbnRlck1hcChjYW52YXMpXG5cbiAgICBsZXQgaGVpZ2h0ID0gJChkb2N1bWVudCkuaGVpZ2h0KClcbiAgICBsZXQgd2lkdGggPSAkKGRvY3VtZW50KS53aWR0aCgpXG5cbiAgICBsZXQgc3BhblggPSBNYXRoLmFicyhzWCAtIGVYKVxuICAgIGxldCBzcGFuWSA9IE1hdGguYWJzKHNZIC0gZVkpXG4gICAgbGV0IHJhdGlvWCA9IHdpZHRoIC8gc3BhblhcbiAgICBsZXQgcmF0aW9ZID0gaGVpZ2h0IC8gc3BhbllcblxuICAgIGxldCBuZXdSYXRpbyA9IE1hdGgubWluKHJhdGlvWCwgcmF0aW9ZKVxuXG4gICAgaWYgKGNhbnZhcy5zY2FsZU9mZnNldFggKiBuZXdSYXRpbyA8PSA1ICYmIGNhbnZhcy5zY2FsZU9mZnNldFggKiBuZXdSYXRpbyA+PSAwLjIpIHtcbiAgICAgIGNhbnZhcy5zY2FsZShuZXdSYXRpbywgbmV3UmF0aW8pXG4gICAgfSBlbHNlIGlmIChjYW52YXMuc2NhbGVPZmZzZXRYICogbmV3UmF0aW8gPiA1KSB7XG4gICAgICBuZXdSYXRpbyA9IDUgLyBjYW52YXMuc2NhbGVPZmZzZXRYXG4gICAgICBjYW52YXMuc2NhbGUobmV3UmF0aW8sIG5ld1JhdGlvKVxuICAgIH0gZWxzZSB7XG4gICAgICBuZXdSYXRpbyA9IDAuMiAvIGNhbnZhcy5zY2FsZU9mZnNldFhcbiAgICAgIGNhbnZhcy5zY2FsZShuZXdSYXRpbywgbmV3UmF0aW8pXG4gICAgfVxuXG4gICAgY29uc3QgY29nWCA9IChzWCArIGVYKSAvIDJcbiAgICBjb25zdCBjb2dZID0gKHNZICsgZVkpIC8gMlxuXG4gICAgY2FudmFzLnRyYW5zbGF0ZSgtMSAqIGNvZ1gsIC0xICogY29nWSlcbiAgICAkKGRvY3VtZW50KS50cmlnZ2VyKEpJVC5ldmVudHMuem9vbSwgW2V2ZW50XSlcblxuICAgIE1vdXNlLmJveFN0YXJ0Q29vcmRpbmF0ZXMgPSBmYWxzZVxuICAgIE1vdXNlLmJveEVuZENvb3JkaW5hdGVzID0gZmFsc2VcbiAgICBWaXN1YWxpemUubUdyYXBoLnBsb3QoKVxuICB9LFxuICB6b29tRXh0ZW50czogZnVuY3Rpb24oZXZlbnQsIGNhbnZhcywgZGVueVNlbGVjdGVkKSB7XG4gICAgSklULmNlbnRlck1hcChjYW52YXMpXG4gICAgbGV0IGhlaWdodCA9IGNhbnZhcy5nZXRTaXplKCkuaGVpZ2h0XG4gICAgbGV0IHdpZHRoID0gY2FudmFzLmdldFNpemUoKS53aWR0aFxuICAgIGxldCBtYXhYXG4gICAgbGV0IG1heFlcbiAgICBsZXQgbWluWFxuICAgIGxldCBtaW5ZXG4gICAgbGV0IGNvdW50ZXIgPSAwXG5cbiAgICBsZXQgbm9kZXNcbiAgICBpZiAoIWRlbnlTZWxlY3RlZCAmJiBTZWxlY3RlZC5Ob2Rlcy5sZW5ndGggPiAwKSB7XG4gICAgICBub2RlcyA9IFNlbGVjdGVkLk5vZGVzXG4gICAgfSBlbHNlIHtcbiAgICAgIG5vZGVzID0gXy52YWx1ZXMoVmlzdWFsaXplLm1HcmFwaC5ncmFwaC5ub2RlcylcbiAgICB9XG5cbiAgICBpZiAobm9kZXMubGVuZ3RoID4gMSkge1xuICAgICAgbm9kZXMuZm9yRWFjaChmdW5jdGlvbihuKSB7XG4gICAgICAgIGxldCB4ID0gbi5wb3MueFxuICAgICAgICBsZXQgeSA9IG4ucG9zLnlcblxuICAgICAgICBpZiAoY291bnRlciA9PT0gMCAmJiBuLmdldERhdGEoJ2FscGhhJykgPT09IDEpIHtcbiAgICAgICAgICBtYXhYID0geFxuICAgICAgICAgIG1pblggPSB4XG4gICAgICAgICAgbWF4WSA9IHlcbiAgICAgICAgICBtaW5ZID0geVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGFycmF5T2ZMYWJlbExpbmVzID0gVXRpbC5zcGxpdExpbmUobi5uYW1lLCAyNSkuc3BsaXQoJ1xcbicpXG4gICAgICAgIGxldCBkaW0gPSBuLmdldERhdGEoJ2RpbScpXG4gICAgICAgIGxldCBjdHggPSBjYW52YXMuZ2V0Q3R4KClcblxuICAgICAgICBsZXQgaGVpZ2h0ID0gMjUgKiBhcnJheU9mTGFiZWxMaW5lcy5sZW5ndGhcblxuICAgICAgICBsZXQgbGluZVdpZHRocyA9IFtdXG4gICAgICAgIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggPCBhcnJheU9mTGFiZWxMaW5lcy5sZW5ndGg7ICsraW5kZXgpIHtcbiAgICAgICAgICBsaW5lV2lkdGhzLnB1c2goY3R4Lm1lYXN1cmVUZXh0KGFycmF5T2ZMYWJlbExpbmVzW2luZGV4XSkud2lkdGgpXG4gICAgICAgIH1cbiAgICAgICAgbGV0IHdpZHRoID0gTWF0aC5tYXguYXBwbHkobnVsbCwgbGluZVdpZHRocykgKyA4XG5cbiAgICAgICAgLy8gb25seSBhZGp1c3QgdGhlc2UgdmFsdWVzIGlmIHRoZSBub2RlIGlzIG5vdCBmaWx0ZXJlZFxuICAgICAgICBpZiAobi5nZXREYXRhKCdhbHBoYScpID09PSAxKSB7XG4gICAgICAgICAgbWF4WCA9IE1hdGgubWF4KHggKyB3aWR0aCAvIDIsIG1heFgpXG4gICAgICAgICAgbWF4WSA9IE1hdGgubWF4KHkgKyBuLmdldERhdGEoJ2hlaWdodCcpICsgNSArIGhlaWdodCwgbWF4WSlcbiAgICAgICAgICBtaW5YID0gTWF0aC5taW4oeCAtIHdpZHRoIC8gMiwgbWluWClcbiAgICAgICAgICBtaW5ZID0gTWF0aC5taW4oeSAtIGRpbSwgbWluWSlcblxuICAgICAgICAgIGNvdW50ZXIrK1xuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICBsZXQgc3BhblggPSBtYXhYIC0gbWluWFxuICAgICAgbGV0IHNwYW5ZID0gbWF4WSAtIG1pbllcbiAgICAgIGxldCByYXRpb1ggPSBzcGFuWCAvIHdpZHRoXG4gICAgICBsZXQgcmF0aW9ZID0gc3BhblkgLyBoZWlnaHRcblxuICAgICAgbGV0IGNvZ1ggPSAobWF4WCArIG1pblgpIC8gMlxuICAgICAgbGV0IGNvZ1kgPSAobWF4WSArIG1pblkpIC8gMlxuXG4gICAgICBjYW52YXMudHJhbnNsYXRlKC0xICogY29nWCwgLTEgKiBjb2dZKVxuXG4gICAgICBsZXQgbmV3UmF0aW8gPSBNYXRoLm1heChyYXRpb1gsIHJhdGlvWSlcbiAgICAgIGxldCBzY2FsZU11bHRpcGxpZXIgPSAxIC8gbmV3UmF0aW8gKiAwLjlcblxuICAgICAgaWYgKGNhbnZhcy5zY2FsZU9mZnNldFggKiBzY2FsZU11bHRpcGxpZXIgPD0gMyAmJiBjYW52YXMuc2NhbGVPZmZzZXRYICogc2NhbGVNdWx0aXBsaWVyID49IDAuMikge1xuICAgICAgICBjYW52YXMuc2NhbGUoc2NhbGVNdWx0aXBsaWVyLCBzY2FsZU11bHRpcGxpZXIpXG4gICAgICB9IGVsc2UgaWYgKGNhbnZhcy5zY2FsZU9mZnNldFggKiBzY2FsZU11bHRpcGxpZXIgPiAzKSB7XG4gICAgICAgIHNjYWxlTXVsdGlwbGllciA9IDMgLyBjYW52YXMuc2NhbGVPZmZzZXRYXG4gICAgICAgIGNhbnZhcy5zY2FsZShzY2FsZU11bHRpcGxpZXIsIHNjYWxlTXVsdGlwbGllcilcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNjYWxlTXVsdGlwbGllciA9IDAuMiAvIGNhbnZhcy5zY2FsZU9mZnNldFhcbiAgICAgICAgY2FudmFzLnNjYWxlKHNjYWxlTXVsdGlwbGllciwgc2NhbGVNdWx0aXBsaWVyKVxuICAgICAgfVxuXG4gICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKEpJVC5ldmVudHMuem9vbSwgW2V2ZW50XSlcbiAgICB9IGVsc2UgaWYgKG5vZGVzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgbm9kZXMuZm9yRWFjaChmdW5jdGlvbihuKSB7XG4gICAgICAgIGNvbnN0IHggPSBuLnBvcy54XG4gICAgICAgIGNvbnN0IHkgPSBuLnBvcy55XG5cbiAgICAgICAgY2FudmFzLnRyYW5zbGF0ZSgtMSAqIHgsIC0xICogeSlcbiAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcihKSVQuZXZlbnRzLnpvb20sIFtldmVudF0pXG4gICAgICB9KVxuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBKSVRcblxuXG5cbi8vIFdFQlBBQ0sgRk9PVEVSIC8vXG4vLyBmcm9udGVuZC9zcmMvTWV0YW1hcHMvSklULmpzIl0sIm1hcHBpbmdzIjoiOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7OztBQVVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFMQTtBQU9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFMQTtBQU9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFGQTtBQUlBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFGQTtBQUlBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9");

TODO found
Open

    eval("// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nvar Buffer = __webpack_require__(591).Buffer;\n\nvar isBufferEncoding = Buffer.isEncoding\n  || function(encoding) {\n       switch (encoding && encoding.toLowerCase()) {\n         case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true;\n         default: return false;\n       }\n     }\n\n\nfunction assertEncoding(encoding) {\n  if (encoding && !isBufferEncoding(encoding)) {\n    throw new Error('Unknown encoding: ' + encoding);\n  }\n}\n\n// StringDecoder provides an interface for efficiently splitting a series of\n// buffers into a series of JS strings without breaking apart multi-byte\n// characters. CESU-8 is handled as part of the UTF-8 encoding.\n//\n// @TODO Handling all encodings inside a single object makes it very difficult\n// to reason about this code, so it should be split up in the future.\n// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code\n// points as used by CESU-8.\nvar StringDecoder = exports.StringDecoder = function(encoding) {\n  this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, '');\n  assertEncoding(encoding);\n  switch (this.encoding) {\n    case 'utf8':\n      // CESU-8 represents each of Surrogate Pair by 3-bytes\n      this.surrogateSize = 3;\n      break;\n    case 'ucs2':\n    case 'utf16le':\n      // UTF-16 represents each of Surrogate Pair by 2-bytes\n      this.surrogateSize = 2;\n      this.detectIncompleteChar = utf16DetectIncompleteChar;\n      break;\n    case 'base64':\n      // Base-64 stores 3 bytes in 4 chars, and pads the remainder.\n      this.surrogateSize = 3;\n      this.detectIncompleteChar = base64DetectIncompleteChar;\n      break;\n    default:\n      this.write = passThroughWrite;\n      return;\n  }\n\n  // Enough space to store all bytes of a single character. UTF-8 needs 4\n  // bytes, but CESU-8 may require up to 6 (3 bytes per surrogate).\n  this.charBuffer = new Buffer(6);\n  // Number of bytes received for the current incomplete multi-byte character.\n  this.charReceived = 0;\n  // Number of bytes expected for the current incomplete multi-byte character.\n  this.charLength = 0;\n};\n\n\n// write decodes the given buffer and returns it as JS string that is\n// guaranteed to not contain any partial multi-byte characters. Any partial\n// character found at the end of the buffer is buffered up, and will be\n// returned when calling write again with the remaining bytes.\n//\n// Note: Converting a Buffer containing an orphan surrogate to a String\n// currently works, but converting a String to a Buffer (via `new Buffer`, or\n// Buffer#write) will replace incomplete surrogates with the unicode\n// replacement character. See https://codereview.chromium.org/121173009/ .\nStringDecoder.prototype.write = function(buffer) {\n  var charStr = '';\n  // if our last write ended with an incomplete multibyte character\n  while (this.charLength) {\n    // determine how many remaining bytes this buffer has to offer for this char\n    var available = (buffer.length >= this.charLength - this.charReceived) ?\n        this.charLength - this.charReceived :\n        buffer.length;\n\n    // add the new bytes to the char buffer\n    buffer.copy(this.charBuffer, this.charReceived, 0, available);\n    this.charReceived += available;\n\n    if (this.charReceived < this.charLength) {\n      // still not enough chars in this buffer? wait for more ...\n      return '';\n    }\n\n    // remove bytes belonging to the current character from the buffer\n    buffer = buffer.slice(available, buffer.length);\n\n    // get the character that was split\n    charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding);\n\n    // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character\n    var charCode = charStr.charCodeAt(charStr.length - 1);\n    if (charCode >= 0xD800 && charCode <= 0xDBFF) {\n      this.charLength += this.surrogateSize;\n      charStr = '';\n      continue;\n    }\n    this.charReceived = this.charLength = 0;\n\n    // if there are no more bytes in this buffer, just emit our char\n    if (buffer.length === 0) {\n      return charStr;\n    }\n    break;\n  }\n\n  // determine and set charLength / charReceived\n  this.detectIncompleteChar(buffer);\n\n  var end = buffer.length;\n  if (this.charLength) {\n    // buffer the incomplete character bytes we got\n    buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end);\n    end -= this.charReceived;\n  }\n\n  charStr += buffer.toString(this.encoding, 0, end);\n\n  var end = charStr.length - 1;\n  var charCode = charStr.charCodeAt(end);\n  // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character\n  if (charCode >= 0xD800 && charCode <= 0xDBFF) {\n    var size = this.surrogateSize;\n    this.charLength += size;\n    this.charReceived += size;\n    this.charBuffer.copy(this.charBuffer, size, 0, size);\n    buffer.copy(this.charBuffer, 0, 0, size);\n    return charStr.substring(0, end);\n  }\n\n  // or just emit the charStr\n  return charStr;\n};\n\n// detectIncompleteChar determines if there is an incomplete UTF-8 character at\n// the end of the given buffer. If so, it sets this.charLength to the byte\n// length that character, and sets this.charReceived to the number of bytes\n// that are available for this character.\nStringDecoder.prototype.detectIncompleteChar = function(buffer) {\n  // determine how many bytes we have to check at the end of this buffer\n  var i = (buffer.length >= 3) ? 3 : buffer.length;\n\n  // Figure out if one of the last i bytes of our buffer announces an\n  // incomplete char.\n  for (; i > 0; i--) {\n    var c = buffer[buffer.length - i];\n\n    // See http://en.wikipedia.org/wiki/UTF-8#Description\n\n    // 110XXXXX\n    if (i == 1 && c >> 5 == 0x06) {\n      this.charLength = 2;\n      break;\n    }\n\n    // 1110XXXX\n    if (i <= 2 && c >> 4 == 0x0E) {\n      this.charLength = 3;\n      break;\n    }\n\n    // 11110XXX\n    if (i <= 3 && c >> 3 == 0x1E) {\n      this.charLength = 4;\n      break;\n    }\n  }\n  this.charReceived = i;\n};\n\nStringDecoder.prototype.end = function(buffer) {\n  var res = '';\n  if (buffer && buffer.length)\n    res = this.write(buffer);\n\n  if (this.charReceived) {\n    var cr = this.charReceived;\n    var buf = this.charBuffer;\n    var enc = this.encoding;\n    res += buf.slice(0, cr).toString(enc);\n  }\n\n  return res;\n};\n\nfunction passThroughWrite(buffer) {\n  return buffer.toString(this.encoding);\n}\n\nfunction utf16DetectIncompleteChar(buffer) {\n  this.charReceived = buffer.length % 2;\n  this.charLength = this.charReceived ? 2 : 0;\n}\n\nfunction base64DetectIncompleteChar(buffer) {\n  this.charReceived = buffer.length % 3;\n  this.charLength = this.charReceived ? 3 : 0;\n}\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNjAwLmpzIiwic291cmNlcyI6WyIvaG9tZS91YnVudHUvd29ya3NwYWNlL25vZGVfbW9kdWxlcy9zdHJpbmdfZGVjb2Rlci9pbmRleC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgSm95ZW50LCBJbmMuIGFuZCBvdGhlciBOb2RlIGNvbnRyaWJ1dG9ycy5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYVxuLy8gY29weSBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZVxuLy8gXCJTb2Z0d2FyZVwiKSwgdG8gZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nXG4vLyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsXG4vLyBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Igc2VsbCBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0XG4vLyBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGVcbi8vIGZvbGxvd2luZyBjb25kaXRpb25zOlxuLy9cbi8vIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkXG4vLyBpbiBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS5cbi8vXG4vLyBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTXG4vLyBPUiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GXG4vLyBNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOXG4vLyBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSxcbi8vIERBTUFHRVMgT1IgT1RIRVIgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUlxuLy8gT1RIRVJXSVNFLCBBUklTSU5HIEZST00sIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRVxuLy8gVVNFIE9SIE9USEVSIERFQUxJTkdTIElOIFRIRSBTT0ZUV0FSRS5cblxudmFyIEJ1ZmZlciA9IHJlcXVpcmUoJ2J1ZmZlcicpLkJ1ZmZlcjtcblxudmFyIGlzQnVmZmVyRW5jb2RpbmcgPSBCdWZmZXIuaXNFbmNvZGluZ1xuICB8fCBmdW5jdGlvbihlbmNvZGluZykge1xuICAgICAgIHN3aXRjaCAoZW5jb2RpbmcgJiYgZW5jb2RpbmcudG9Mb3dlckNhc2UoKSkge1xuICAgICAgICAgY2FzZSAnaGV4JzogY2FzZSAndXRmOCc6IGNhc2UgJ3V0Zi04JzogY2FzZSAnYXNjaWknOiBjYXNlICdiaW5hcnknOiBjYXNlICdiYXNlNjQnOiBjYXNlICd1Y3MyJzogY2FzZSAndWNzLTInOiBjYXNlICd1dGYxNmxlJzogY2FzZSAndXRmLTE2bGUnOiBjYXNlICdyYXcnOiByZXR1cm4gdHJ1ZTtcbiAgICAgICAgIGRlZmF1bHQ6IHJldHVybiBmYWxzZTtcbiAgICAgICB9XG4gICAgIH1cblxuXG5mdW5jdGlvbiBhc3NlcnRFbmNvZGluZyhlbmNvZGluZykge1xuICBpZiAoZW5jb2RpbmcgJiYgIWlzQnVmZmVyRW5jb2RpbmcoZW5jb2RpbmcpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdVbmtub3duIGVuY29kaW5nOiAnICsgZW5jb2RpbmcpO1xuICB9XG59XG5cbi8vIFN0cmluZ0RlY29kZXIgcHJvdmlkZXMgYW4gaW50ZXJmYWNlIGZvciBlZmZpY2llbnRseSBzcGxpdHRpbmcgYSBzZXJpZXMgb2Zcbi8vIGJ1ZmZlcnMgaW50byBhIHNlcmllcyBvZiBKUyBzdHJpbmdzIHdpdGhvdXQgYnJlYWtpbmcgYXBhcnQgbXVsdGktYnl0ZVxuLy8gY2hhcmFjdGVycy4gQ0VTVS04IGlzIGhhbmRsZWQgYXMgcGFydCBvZiB0aGUgVVRGLTggZW5jb2RpbmcuXG4vL1xuLy8gQFRPRE8gSGFuZGxpbmcgYWxsIGVuY29kaW5ncyBpbnNpZGUgYSBzaW5nbGUgb2JqZWN0IG1ha2VzIGl0IHZlcnkgZGlmZmljdWx0XG4vLyB0byByZWFzb24gYWJvdXQgdGhpcyBjb2RlLCBzbyBpdCBzaG91bGQgYmUgc3BsaXQgdXAgaW4gdGhlIGZ1dHVyZS5cbi8vIEBUT0RPIFRoZXJlIHNob3VsZCBiZSBhIHV0Zjgtc3RyaWN0IGVuY29kaW5nIHRoYXQgcmVqZWN0cyBpbnZhbGlkIFVURi04IGNvZGVcbi8vIHBvaW50cyBhcyB1c2VkIGJ5IENFU1UtOC5cbnZhciBTdHJpbmdEZWNvZGVyID0gZXhwb3J0cy5TdHJpbmdEZWNvZGVyID0gZnVuY3Rpb24oZW5jb2RpbmcpIHtcbiAgdGhpcy5lbmNvZGluZyA9IChlbmNvZGluZyB8fCAndXRmOCcpLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgvWy1fXS8sICcnKTtcbiAgYXNzZXJ0RW5jb2RpbmcoZW5jb2RpbmcpO1xuICBzd2l0Y2ggKHRoaXMuZW5jb2RpbmcpIHtcbiAgICBjYXNlICd1dGY4JzpcbiAgICAgIC8vIENFU1UtOCByZXByZXNlbnRzIGVhY2ggb2YgU3Vycm9nYXRlIFBhaXIgYnkgMy1ieXRlc1xuICAgICAgdGhpcy5zdXJyb2dhdGVTaXplID0gMztcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgJ3VjczInOlxuICAgIGNhc2UgJ3V0ZjE2bGUnOlxuICAgICAgLy8gVVRGLTE2IHJlcHJlc2VudHMgZWFjaCBvZiBTdXJyb2dhdGUgUGFpciBieSAyLWJ5dGVzXG4gICAgICB0aGlzLnN1cnJvZ2F0ZVNpemUgPSAyO1xuICAgICAgdGhpcy5kZXRlY3RJbmNvbXBsZXRlQ2hhciA9IHV0ZjE2RGV0ZWN0SW5jb21wbGV0ZUNoYXI7XG4gICAgICBicmVhaztcbiAgICBjYXNlICdiYXNlNjQnOlxuICAgICAgLy8gQmFzZS02NCBzdG9yZXMgMyBieXRlcyBpbiA0IGNoYXJzLCBhbmQgcGFkcyB0aGUgcmVtYWluZGVyLlxuICAgICAgdGhpcy5zdXJyb2dhdGVTaXplID0gMztcbiAgICAgIHRoaXMuZGV0ZWN0SW5jb21wbGV0ZUNoYXIgPSBiYXNlNjREZXRlY3RJbmNvbXBsZXRlQ2hhcjtcbiAgICAgIGJyZWFrO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aGlzLndyaXRlID0gcGFzc1Rocm91Z2hXcml0ZTtcbiAgICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIEVub3VnaCBzcGFjZSB0byBzdG9yZSBhbGwgYnl0ZXMgb2YgYSBzaW5nbGUgY2hhcmFjdGVyLiBVVEYtOCBuZWVkcyA0XG4gIC8vIGJ5dGVzLCBidXQgQ0VTVS04IG1heSByZXF1aXJlIHVwIHRvIDYgKDMgYnl0ZXMgcGVyIHN1cnJvZ2F0ZSkuXG4gIHRoaXMuY2hhckJ1ZmZlciA9IG5ldyBCdWZmZXIoNik7XG4gIC8vIE51bWJlciBvZiBieXRlcyByZWNlaXZlZCBmb3IgdGhlIGN1cnJlbnQgaW5jb21wbGV0ZSBtdWx0aS1ieXRlIGNoYXJhY3Rlci5cbiAgdGhpcy5jaGFyUmVjZWl2ZWQgPSAwO1xuICAvLyBOdW1iZXIgb2YgYnl0ZXMgZXhwZWN0ZWQgZm9yIHRoZSBjdXJyZW50IGluY29tcGxldGUgbXVsdGktYnl0ZSBjaGFyYWN0ZXIuXG4gIHRoaXMuY2hhckxlbmd0aCA9IDA7XG59O1xuXG5cbi8vIHdyaXRlIGRlY29kZXMgdGhlIGdpdmVuIGJ1ZmZlciBhbmQgcmV0dXJucyBpdCBhcyBKUyBzdHJpbmcgdGhhdCBpc1xuLy8gZ3VhcmFudGVlZCB0byBub3QgY29udGFpbiBhbnkgcGFydGlhbCBtdWx0aS1ieXRlIGNoYXJhY3RlcnMuIEFueSBwYXJ0aWFsXG4vLyBjaGFyYWN0ZXIgZm91bmQgYXQgdGhlIGVuZCBvZiB0aGUgYnVmZmVyIGlzIGJ1ZmZlcmVkIHVwLCBhbmQgd2lsbCBiZVxuLy8gcmV0dXJuZWQgd2hlbiBjYWxsaW5nIHdyaXRlIGFnYWluIHdpdGggdGhlIHJlbWFpbmluZyBieXRlcy5cbi8vXG4vLyBOb3RlOiBDb252ZXJ0aW5nIGEgQnVmZmVyIGNvbnRhaW5pbmcgYW4gb3JwaGFuIHN1cnJvZ2F0ZSB0byBhIFN0cmluZ1xuLy8gY3VycmVudGx5IHdvcmtzLCBidXQgY29udmVydGluZyBhIFN0cmluZyB0byBhIEJ1ZmZlciAodmlhIGBuZXcgQnVmZmVyYCwgb3Jcbi8vIEJ1ZmZlciN3cml0ZSkgd2lsbCByZXBsYWNlIGluY29tcGxldGUgc3Vycm9nYXRlcyB3aXRoIHRoZSB1bmljb2RlXG4vLyByZXBsYWNlbWVudCBjaGFyYWN0ZXIuIFNlZSBodHRwczovL2NvZGVyZXZpZXcuY2hyb21pdW0ub3JnLzEyMTE3MzAwOS8gLlxuU3RyaW5nRGVjb2Rlci5wcm90b3R5cGUud3JpdGUgPSBmdW5jdGlvbihidWZmZXIpIHtcbiAgdmFyIGNoYXJTdHIgPSAnJztcbiAgLy8gaWYgb3VyIGxhc3Qgd3JpdGUgZW5kZWQgd2l0aCBhbiBpbmNvbXBsZXRlIG11bHRpYnl0ZSBjaGFyYWN0ZXJcbiAgd2hpbGUgKHRoaXMuY2hhckxlbmd0aCkge1xuICAgIC8vIGRldGVybWluZSBob3cgbWFueSByZW1haW5pbmcgYnl0ZXMgdGhpcyBidWZmZXIgaGFzIHRvIG9mZmVyIGZvciB0aGlzIGNoYXJcbiAgICB2YXIgYXZhaWxhYmxlID0gKGJ1ZmZlci5sZW5ndGggPj0gdGhpcy5jaGFyTGVuZ3RoIC0gdGhpcy5jaGFyUmVjZWl2ZWQpID9cbiAgICAgICAgdGhpcy5jaGFyTGVuZ3RoIC0gdGhpcy5jaGFyUmVjZWl2ZWQgOlxuICAgICAgICBidWZmZXIubGVuZ3RoO1xuXG4gICAgLy8gYWRkIHRoZSBuZXcgYnl0ZXMgdG8gdGhlIGNoYXIgYnVmZmVyXG4gICAgYnVmZmVyLmNvcHkodGhpcy5jaGFyQnVmZmVyLCB0aGlzLmNoYXJSZWNlaXZlZCwgMCwgYXZhaWxhYmxlKTtcbiAgICB0aGlzLmNoYXJSZWNlaXZlZCArPSBhdmFpbGFibGU7XG5cbiAgICBpZiAodGhpcy5jaGFyUmVjZWl2ZWQgPCB0aGlzLmNoYXJMZW5ndGgpIHtcbiAgICAgIC8vIHN0aWxsIG5vdCBlbm91Z2ggY2hhcnMgaW4gdGhpcyBidWZmZXI/IHdhaXQgZm9yIG1vcmUgLi4uXG4gICAgICByZXR1cm4gJyc7XG4gICAgfVxuXG4gICAgLy8gcmVtb3ZlIGJ5dGVzIGJlbG9uZ2luZyB0byB0aGUgY3VycmVudCBjaGFyYWN0ZXIgZnJvbSB0aGUgYnVmZmVyXG4gICAgYnVmZmVyID0gYnVmZmVyLnNsaWNlKGF2YWlsYWJsZSwgYnVmZmVyLmxlbmd0aCk7XG5cbiAgICAvLyBnZXQgdGhlIGNoYXJhY3RlciB0aGF0IHdhcyBzcGxpdFxuICAgIGNoYXJTdHIgPSB0aGlzLmNoYXJCdWZmZXIuc2xpY2UoMCwgdGhpcy5jaGFyTGVuZ3RoKS50b1N0cmluZyh0aGlzLmVuY29kaW5nKTtcblxuICAgIC8vIENFU1UtODogbGVhZCBzdXJyb2dhdGUgKEQ4MDAtREJGRikgaXMgYWxzbyB0aGUgaW5jb21wbGV0ZSBjaGFyYWN0ZXJcbiAgICB2YXIgY2hhckNvZGUgPSBjaGFyU3RyLmNoYXJDb2RlQXQoY2hhclN0ci5sZW5ndGggLSAxKTtcbiAgICBpZiAoY2hhckNvZGUgPj0gMHhEODAwICYmIGNoYXJDb2RlIDw9IDB4REJGRikge1xuICAgICAgdGhpcy5jaGFyTGVuZ3RoICs9IHRoaXMuc3Vycm9nYXRlU2l6ZTtcbiAgICAgIGNoYXJTdHIgPSAnJztcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICB0aGlzLmNoYXJSZWNlaXZlZCA9IHRoaXMuY2hhckxlbmd0aCA9IDA7XG5cbiAgICAvLyBpZiB0aGVyZSBhcmUgbm8gbW9yZSBieXRlcyBpbiB0aGlzIGJ1ZmZlciwganVzdCBlbWl0IG91ciBjaGFyXG4gICAgaWYgKGJ1ZmZlci5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBjaGFyU3RyO1xuICAgIH1cbiAgICBicmVhaztcbiAgfVxuXG4gIC8vIGRldGVybWluZSBhbmQgc2V0IGNoYXJMZW5ndGggLyBjaGFyUmVjZWl2ZWRcbiAgdGhpcy5kZXRlY3RJbmNvbXBsZXRlQ2hhcihidWZmZXIpO1xuXG4gIHZhciBlbmQgPSBidWZmZXIubGVuZ3RoO1xuICBpZiAodGhpcy5jaGFyTGVuZ3RoKSB7XG4gICAgLy8gYnVmZmVyIHRoZSBpbmNvbXBsZXRlIGNoYXJhY3RlciBieXRlcyB3ZSBnb3RcbiAgICBidWZmZXIuY29weSh0aGlzLmNoYXJCdWZmZXIsIDAsIGJ1ZmZlci5sZW5ndGggLSB0aGlzLmNoYXJSZWNlaXZlZCwgZW5kKTtcbiAgICBlbmQgLT0gdGhpcy5jaGFyUmVjZWl2ZWQ7XG4gIH1cblxuICBjaGFyU3RyICs9IGJ1ZmZlci50b1N0cmluZyh0aGlzLmVuY29kaW5nLCAwLCBlbmQpO1xuXG4gIHZhciBlbmQgPSBjaGFyU3RyLmxlbmd0aCAtIDE7XG4gIHZhciBjaGFyQ29kZSA9IGNoYXJTdHIuY2hhckNvZGVBdChlbmQpO1xuICAvLyBDRVNVLTg6IGxlYWQgc3Vycm9nYXRlIChEODAwLURCRkYpIGlzIGFsc28gdGhlIGluY29tcGxldGUgY2hhcmFjdGVyXG4gIGlmIChjaGFyQ29kZSA+PSAweEQ4MDAgJiYgY2hhckNvZGUgPD0gMHhEQkZGKSB7XG4gICAgdmFyIHNpemUgPSB0aGlzLnN1cnJvZ2F0ZVNpemU7XG4gICAgdGhpcy5jaGFyTGVuZ3RoICs9IHNpemU7XG4gICAgdGhpcy5jaGFyUmVjZWl2ZWQgKz0gc2l6ZTtcbiAgICB0aGlzLmNoYXJCdWZmZXIuY29weSh0aGlzLmNoYXJCdWZmZXIsIHNpemUsIDAsIHNpemUpO1xuICAgIGJ1ZmZlci5jb3B5KHRoaXMuY2hhckJ1ZmZlciwgMCwgMCwgc2l6ZSk7XG4gICAgcmV0dXJuIGNoYXJTdHIuc3Vic3RyaW5nKDAsIGVuZCk7XG4gIH1cblxuICAvLyBvciBqdXN0IGVtaXQgdGhlIGNoYXJTdHJcbiAgcmV0dXJuIGNoYXJTdHI7XG59O1xuXG4vLyBkZXRlY3RJbmNvbXBsZXRlQ2hhciBkZXRlcm1pbmVzIGlmIHRoZXJlIGlzIGFuIGluY29tcGxldGUgVVRGLTggY2hhcmFjdGVyIGF0XG4vLyB0aGUgZW5kIG9mIHRoZSBnaXZlbiBidWZmZXIuIElmIHNvLCBpdCBzZXRzIHRoaXMuY2hhckxlbmd0aCB0byB0aGUgYnl0ZVxuLy8gbGVuZ3RoIHRoYXQgY2hhcmFjdGVyLCBhbmQgc2V0cyB0aGlzLmNoYXJSZWNlaXZlZCB0byB0aGUgbnVtYmVyIG9mIGJ5dGVzXG4vLyB0aGF0IGFyZSBhdmFpbGFibGUgZm9yIHRoaXMgY2hhcmFjdGVyLlxuU3RyaW5nRGVjb2Rlci5wcm90b3R5cGUuZGV0ZWN0SW5jb21wbGV0ZUNoYXIgPSBmdW5jdGlvbihidWZmZXIpIHtcbiAgLy8gZGV0ZXJtaW5lIGhvdyBtYW55IGJ5dGVzIHdlIGhhdmUgdG8gY2hlY2sgYXQgdGhlIGVuZCBvZiB0aGlzIGJ1ZmZlclxuICB2YXIgaSA9IChidWZmZXIubGVuZ3RoID49IDMpID8gMyA6IGJ1ZmZlci5sZW5ndGg7XG5cbiAgLy8gRmlndXJlIG91dCBpZiBvbmUgb2YgdGhlIGxhc3QgaSBieXRlcyBvZiBvdXIgYnVmZmVyIGFubm91bmNlcyBhblxuICAvLyBpbmNvbXBsZXRlIGNoYXIuXG4gIGZvciAoOyBpID4gMDsgaS0tKSB7XG4gICAgdmFyIGMgPSBidWZmZXJbYnVmZmVyLmxlbmd0aCAtIGldO1xuXG4gICAgLy8gU2VlIGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVVRGLTgjRGVzY3JpcHRpb25cblxuICAgIC8vIDExMFhYWFhYXG4gICAgaWYgKGkgPT0gMSAmJiBjID4+IDUgPT0gMHgwNikge1xuICAgICAgdGhpcy5jaGFyTGVuZ3RoID0gMjtcbiAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIC8vIDExMTBYWFhYXG4gICAgaWYgKGkgPD0gMiAmJiBjID4+IDQgPT0gMHgwRSkge1xuICAgICAgdGhpcy5jaGFyTGVuZ3RoID0gMztcbiAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIC8vIDExMTEwWFhYXG4gICAgaWYgKGkgPD0gMyAmJiBjID4+IDMgPT0gMHgxRSkge1xuICAgICAgdGhpcy5jaGFyTGVuZ3RoID0gNDtcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICB0aGlzLmNoYXJSZWNlaXZlZCA9IGk7XG59O1xuXG5TdHJpbmdEZWNvZGVyLnByb3RvdHlwZS5lbmQgPSBmdW5jdGlvbihidWZmZXIpIHtcbiAgdmFyIHJlcyA9ICcnO1xuICBpZiAoYnVmZmVyICYmIGJ1ZmZlci5sZW5ndGgpXG4gICAgcmVzID0gdGhpcy53cml0ZShidWZmZXIpO1xuXG4gIGlmICh0aGlzLmNoYXJSZWNlaXZlZCkge1xuICAgIHZhciBjciA9IHRoaXMuY2hhclJlY2VpdmVkO1xuICAgIHZhciBidWYgPSB0aGlzLmNoYXJCdWZmZXI7XG4gICAgdmFyIGVuYyA9IHRoaXMuZW5jb2Rpbmc7XG4gICAgcmVzICs9IGJ1Zi5zbGljZSgwLCBjcikudG9TdHJpbmcoZW5jKTtcbiAgfVxuXG4gIHJldHVybiByZXM7XG59O1xuXG5mdW5jdGlvbiBwYXNzVGhyb3VnaFdyaXRlKGJ1ZmZlcikge1xuICByZXR1cm4gYnVmZmVyLnRvU3RyaW5nKHRoaXMuZW5jb2RpbmcpO1xufVxuXG5mdW5jdGlvbiB1dGYxNkRldGVjdEluY29tcGxldGVDaGFyKGJ1ZmZlcikge1xuICB0aGlzLmNoYXJSZWNlaXZlZCA9IGJ1ZmZlci5sZW5ndGggJSAyO1xuICB0aGlzLmNoYXJMZW5ndGggPSB0aGlzLmNoYXJSZWNlaXZlZCA/IDIgOiAwO1xufVxuXG5mdW5jdGlvbiBiYXNlNjREZXRlY3RJbmNvbXBsZXRlQ2hhcihidWZmZXIpIHtcbiAgdGhpcy5jaGFyUmVjZWl2ZWQgPSBidWZmZXIubGVuZ3RoICUgMztcbiAgdGhpcy5jaGFyTGVuZ3RoID0gdGhpcy5jaGFyUmVjZWl2ZWQgPyAzIDogMDtcbn1cblxuXG5cbi8vLy8vLy8vLy8vLy8vLy8vL1xuLy8gV0VCUEFDSyBGT09URVJcbi8vIC4vfi9zdHJpbmdfZGVjb2Rlci9pbmRleC5qc1xuLy8gbW9kdWxlIGlkID0gNjAwXG4vLyBtb2R1bGUgY2h1bmtzID0gMCJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=");

TODO found
Open

    // TODO: handle case where permission changed
Severity: Minor
Found in frontend/src/Metamaps/Cable.js by fixme
Severity
Category
Status
Source
Language