diff --git a/dist/epicenter.js b/dist/epicenter.js index 02a122a2..e2abf5c9 100644 --- a/dist/epicenter.js +++ b/dist/epicenter.js @@ -5903,6 +5903,8 @@ var StorageFactory = require('./store-factory'); var optionUtils = require('../util/option-utils'); var EPI_SESSION_KEY = keyNames.EPI_SESSION_KEY; +var EPI_MANAGER_KEY = 'epicenter.token'; //can't be under key-names, or logout will clear this too + var defaults = { /** * Where to store user access tokens for temporary access. Defaults to storing in a cookie in the browser. @@ -5976,13 +5978,19 @@ var SessionManager = function (managerOptions) { var baseOptions = getBaseOptions(overrides); var session = this.getSession(overrides); + var token = session.auth_token; + if (!token) { + var factory = new StorageFactory(); + token = factory.get(EPI_MANAGER_KEY); + } + var sessionDefaults = { /** * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)). * @see [Authentication API Service](../auth-api-service/) for getting tokens. * @type {String} */ - token: session.auth_token, + token: token, /** * The account. If left undefined, taken from the cookie session. @@ -6604,4 +6612,4 @@ module.exports = (function () { }()); },{"./query-util":47}]},{},[4]) -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvQmFzZTY0L2Jhc2U2NC5qcyIsIm5vZGVfbW9kdWxlcy9vYmplY3QtYXNzaWduL2luZGV4LmpzIiwic3JjL2FwaS12ZXJzaW9uLmpzb24iLCJzcmMvYXBwLmpzIiwic3JjL2Vudi1sb2FkLmpzIiwic3JjL21hbmFnZXJzL2F1dGgtbWFuYWdlci5qcyIsInNyYy9tYW5hZ2Vycy9jaGFubmVsLW1hbmFnZXIuanMiLCJzcmMvbWFuYWdlcnMvZXBpY2VudGVyLWNoYW5uZWwtbWFuYWdlci5qcyIsInNyYy9tYW5hZ2Vycy9rZXktbmFtZXMuanMiLCJzcmMvbWFuYWdlcnMvcnVuLW1hbmFnZXIuanMiLCJzcmMvbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvYWx3YXlzLW5ldy1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9jb25kaXRpb25hbC1jcmVhdGlvbi1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9tdWx0aXBsYXllci1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9uZXctaWYtaW5pdGlhbGl6ZWQtc3RyYXRlZ3kuanMiLCJzcmMvbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvbmV3LWlmLW1pc3Npbmctc3RyYXRlZ3kuanMiLCJzcmMvbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvbmV3LWlmLXBlcnNpc3RlZC1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9ub25lLXN0cmF0ZWd5LmpzIiwic3JjL21hbmFnZXJzL3J1bi1zdHJhdGVnaWVzL3BlcnNpc3RlbnQtc2luZ2xlLXBsYXllci1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9zdHJhdGVnaWVzLW1hcC5qcyIsInNyYy9tYW5hZ2Vycy9zY2VuYXJpby1tYW5hZ2VyLmpzIiwic3JjL21hbmFnZXJzL3NwZWNpYWwtb3BlcmF0aW9ucy5qcyIsInNyYy9tYW5hZ2Vycy93b3JsZC1tYW5hZ2VyLmpzIiwic3JjL3NlcnZpY2UvYWRtaW4tZmlsZS1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvYXNzZXQtYXBpLWFkYXB0ZXIuanMiLCJzcmMvc2VydmljZS9hdXRoLWFwaS1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvY2hhbm5lbC1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvY29uZmlndXJhdGlvbi1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvZGF0YS1hcGktc2VydmljZS5qcyIsInNyYy9zZXJ2aWNlL2dyb3VwLWFwaS1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvaW50cm9zcGVjdGlvbi1hcGktc2VydmljZS5qcyIsInNyYy9zZXJ2aWNlL21lbWJlci1hcGktYWRhcHRlci5qcyIsInNyYy9zZXJ2aWNlL3J1bi1hcGktc2VydmljZS5qcyIsInNyYy9zZXJ2aWNlL3NlcnZpY2UtdXRpbHMuanMiLCJzcmMvc2VydmljZS9zdGF0ZS1hcGktYWRhcHRlci5qcyIsInNyYy9zZXJ2aWNlL3VybC1jb25maWctc2VydmljZS5qcyIsInNyYy9zZXJ2aWNlL3VzZXItYXBpLWFkYXB0ZXIuanMiLCJzcmMvc2VydmljZS92YXJpYWJsZXMtYXBpLXNlcnZpY2UuanMiLCJzcmMvc2VydmljZS93b3JsZC1hcGktYWRhcHRlci5qcyIsInNyYy9zdG9yZS9jb29raWUtc3RvcmUuanMiLCJzcmMvc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyLmpzIiwic3JjL3N0b3JlL3N0b3JlLWZhY3RvcnkuanMiLCJzcmMvdHJhbnNwb3J0L2FqYXgtaHR0cC10cmFuc3BvcnQuanMiLCJzcmMvdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnkuanMiLCJzcmMvdXRpbC9pbmhlcml0LmpzIiwic3JjL3V0aWwvb2JqZWN0LXV0aWwuanMiLCJzcmMvdXRpbC9vcHRpb24tdXRpbHMuanMiLCJzcmMvdXRpbC9xdWVyeS11dGlsLmpzIiwic3JjL3V0aWwvcnVuLXV0aWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkZBO0FBQ0E7QUFDQTtBQUNBOzs7QUNIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDdEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDallBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN1BBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RZQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdkxBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFYQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4SEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4T0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hTQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcElBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1TEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNWhCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1SEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3J2QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaEpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUhBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDVkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiOyhmdW5jdGlvbiAoKSB7XG5cbiAgdmFyIG9iamVjdCA9IHR5cGVvZiBleHBvcnRzICE9ICd1bmRlZmluZWQnID8gZXhwb3J0cyA6IHNlbGY7IC8vICM4OiB3ZWIgd29ya2Vyc1xuICB2YXIgY2hhcnMgPSAnQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLz0nO1xuXG4gIGZ1bmN0aW9uIEludmFsaWRDaGFyYWN0ZXJFcnJvcihtZXNzYWdlKSB7XG4gICAgdGhpcy5tZXNzYWdlID0gbWVzc2FnZTtcbiAgfVxuICBJbnZhbGlkQ2hhcmFjdGVyRXJyb3IucHJvdG90eXBlID0gbmV3IEVycm9yO1xuICBJbnZhbGlkQ2hhcmFjdGVyRXJyb3IucHJvdG90eXBlLm5hbWUgPSAnSW52YWxpZENoYXJhY3RlckVycm9yJztcblxuICAvLyBlbmNvZGVyXG4gIC8vIFtodHRwczovL2dpc3QuZ2l0aHViLmNvbS85OTkxNjZdIGJ5IFtodHRwczovL2dpdGh1Yi5jb20vbmlnbmFnXVxuICBvYmplY3QuYnRvYSB8fCAoXG4gIG9iamVjdC5idG9hID0gZnVuY3Rpb24gKGlucHV0KSB7XG4gICAgdmFyIHN0ciA9IFN0cmluZyhpbnB1dCk7XG4gICAgZm9yIChcbiAgICAgIC8vIGluaXRpYWxpemUgcmVzdWx0IGFuZCBjb3VudGVyXG4gICAgICB2YXIgYmxvY2ssIGNoYXJDb2RlLCBpZHggPSAwLCBtYXAgPSBjaGFycywgb3V0cHV0ID0gJyc7XG4gICAgICAvLyBpZiB0aGUgbmV4dCBzdHIgaW5kZXggZG9lcyBub3QgZXhpc3Q6XG4gICAgICAvLyAgIGNoYW5nZSB0aGUgbWFwcGluZyB0YWJsZSB0byBcIj1cIlxuICAgICAgLy8gICBjaGVjayBpZiBkIGhhcyBubyBmcmFjdGlvbmFsIGRpZ2l0c1xuICAgICAgc3RyLmNoYXJBdChpZHggfCAwKSB8fCAobWFwID0gJz0nLCBpZHggJSAxKTtcbiAgICAgIC8vIFwiOCAtIGlkeCAlIDEgKiA4XCIgZ2VuZXJhdGVzIHRoZSBzZXF1ZW5jZSAyLCA0LCA2LCA4XG4gICAgICBvdXRwdXQgKz0gbWFwLmNoYXJBdCg2MyAmIGJsb2NrID4+IDggLSBpZHggJSAxICogOClcbiAgICApIHtcbiAgICAgIGNoYXJDb2RlID0gc3RyLmNoYXJDb2RlQXQoaWR4ICs9IDMvNCk7XG4gICAgICBpZiAoY2hhckNvZGUgPiAweEZGKSB7XG4gICAgICAgIHRocm93IG5ldyBJbnZhbGlkQ2hhcmFjdGVyRXJyb3IoXCInYnRvYScgZmFpbGVkOiBUaGUgc3RyaW5nIHRvIGJlIGVuY29kZWQgY29udGFpbnMgY2hhcmFjdGVycyBvdXRzaWRlIG9mIHRoZSBMYXRpbjEgcmFuZ2UuXCIpO1xuICAgICAgfVxuICAgICAgYmxvY2sgPSBibG9jayA8PCA4IHwgY2hhckNvZGU7XG4gICAgfVxuICAgIHJldHVybiBvdXRwdXQ7XG4gIH0pO1xuXG4gIC8vIGRlY29kZXJcbiAgLy8gW2h0dHBzOi8vZ2lzdC5naXRodWIuY29tLzEwMjAzOTZdIGJ5IFtodHRwczovL2dpdGh1Yi5jb20vYXRrXVxuICBvYmplY3QuYXRvYiB8fCAoXG4gIG9iamVjdC5hdG9iID0gZnVuY3Rpb24gKGlucHV0KSB7XG4gICAgdmFyIHN0ciA9IFN0cmluZyhpbnB1dCkucmVwbGFjZSgvPSskLywgJycpO1xuICAgIGlmIChzdHIubGVuZ3RoICUgNCA9PSAxKSB7XG4gICAgICB0aHJvdyBuZXcgSW52YWxpZENoYXJhY3RlckVycm9yKFwiJ2F0b2InIGZhaWxlZDogVGhlIHN0cmluZyB0byBiZSBkZWNvZGVkIGlzIG5vdCBjb3JyZWN0bHkgZW5jb2RlZC5cIik7XG4gICAgfVxuICAgIGZvciAoXG4gICAgICAvLyBpbml0aWFsaXplIHJlc3VsdCBhbmQgY291bnRlcnNcbiAgICAgIHZhciBiYyA9IDAsIGJzLCBidWZmZXIsIGlkeCA9IDAsIG91dHB1dCA9ICcnO1xuICAgICAgLy8gZ2V0IG5leHQgY2hhcmFjdGVyXG4gICAgICBidWZmZXIgPSBzdHIuY2hhckF0KGlkeCsrKTtcbiAgICAgIC8vIGNoYXJhY3RlciBmb3VuZCBpbiB0YWJsZT8gaW5pdGlhbGl6ZSBiaXQgc3RvcmFnZSBhbmQgYWRkIGl0cyBhc2NpaSB2YWx1ZTtcbiAgICAgIH5idWZmZXIgJiYgKGJzID0gYmMgJSA0ID8gYnMgKiA2NCArIGJ1ZmZlciA6IGJ1ZmZlcixcbiAgICAgICAgLy8gYW5kIGlmIG5vdCBmaXJzdCBvZiBlYWNoIDQgY2hhcmFjdGVycyxcbiAgICAgICAgLy8gY29udmVydCB0aGUgZmlyc3QgOCBiaXRzIHRvIG9uZSBhc2NpaSBjaGFyYWN0ZXJcbiAgICAgICAgYmMrKyAlIDQpID8gb3V0cHV0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoMjU1ICYgYnMgPj4gKC0yICogYmMgJiA2KSkgOiAwXG4gICAgKSB7XG4gICAgICAvLyB0cnkgdG8gZmluZCBjaGFyYWN0ZXIgaW4gdGFibGUgKDAtNjMsIG5vdCBmb3VuZCA9PiAtMSlcbiAgICAgIGJ1ZmZlciA9IGNoYXJzLmluZGV4T2YoYnVmZmVyKTtcbiAgICB9XG4gICAgcmV0dXJuIG91dHB1dDtcbiAgfSk7XG5cbn0oKSk7XG4iLCIndXNlIHN0cmljdCc7XG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bnVzZWQtdmFycyAqL1xudmFyIGhhc093blByb3BlcnR5ID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eTtcbnZhciBwcm9wSXNFbnVtZXJhYmxlID0gT2JqZWN0LnByb3RvdHlwZS5wcm9wZXJ0eUlzRW51bWVyYWJsZTtcblxuZnVuY3Rpb24gdG9PYmplY3QodmFsKSB7XG5cdGlmICh2YWwgPT09IG51bGwgfHwgdmFsID09PSB1bmRlZmluZWQpIHtcblx0XHR0aHJvdyBuZXcgVHlwZUVycm9yKCdPYmplY3QuYXNzaWduIGNhbm5vdCBiZSBjYWxsZWQgd2l0aCBudWxsIG9yIHVuZGVmaW5lZCcpO1xuXHR9XG5cblx0cmV0dXJuIE9iamVjdCh2YWwpO1xufVxuXG5mdW5jdGlvbiBzaG91bGRVc2VOYXRpdmUoKSB7XG5cdHRyeSB7XG5cdFx0aWYgKCFPYmplY3QuYXNzaWduKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXG5cdFx0Ly8gRGV0ZWN0IGJ1Z2d5IHByb3BlcnR5IGVudW1lcmF0aW9uIG9yZGVyIGluIG9sZGVyIFY4IHZlcnNpb25zLlxuXG5cdFx0Ly8gaHR0cHM6Ly9idWdzLmNocm9taXVtLm9yZy9wL3Y4L2lzc3Vlcy9kZXRhaWw/aWQ9NDExOFxuXHRcdHZhciB0ZXN0MSA9IG5ldyBTdHJpbmcoJ2FiYycpOyAgLy8gZXNsaW50LWRpc2FibGUtbGluZVxuXHRcdHRlc3QxWzVdID0gJ2RlJztcblx0XHRpZiAoT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXModGVzdDEpWzBdID09PSAnNScpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHQvLyBodHRwczovL2J1Z3MuY2hyb21pdW0ub3JnL3AvdjgvaXNzdWVzL2RldGFpbD9pZD0zMDU2XG5cdFx0dmFyIHRlc3QyID0ge307XG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCAxMDsgaSsrKSB7XG5cdFx0XHR0ZXN0MlsnXycgKyBTdHJpbmcuZnJvbUNoYXJDb2RlKGkpXSA9IGk7XG5cdFx0fVxuXHRcdHZhciBvcmRlcjIgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyh0ZXN0MikubWFwKGZ1bmN0aW9uIChuKSB7XG5cdFx0XHRyZXR1cm4gdGVzdDJbbl07XG5cdFx0fSk7XG5cdFx0aWYgKG9yZGVyMi5qb2luKCcnKSAhPT0gJzAxMjM0NTY3ODknKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXG5cdFx0Ly8gaHR0cHM6Ly9idWdzLmNocm9taXVtLm9yZy9wL3Y4L2lzc3Vlcy9kZXRhaWw/aWQ9MzA1NlxuXHRcdHZhciB0ZXN0MyA9IHt9O1xuXHRcdCdhYmNkZWZnaGlqa2xtbm9wcXJzdCcuc3BsaXQoJycpLmZvckVhY2goZnVuY3Rpb24gKGxldHRlcikge1xuXHRcdFx0dGVzdDNbbGV0dGVyXSA9IGxldHRlcjtcblx0XHR9KTtcblx0XHRpZiAoT2JqZWN0LmtleXMoT2JqZWN0LmFzc2lnbih7fSwgdGVzdDMpKS5qb2luKCcnKSAhPT1cblx0XHRcdFx0J2FiY2RlZmdoaWprbG1ub3BxcnN0Jykge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdHJldHVybiB0cnVlO1xuXHR9IGNhdGNoIChlKSB7XG5cdFx0Ly8gV2UgZG9uJ3QgZXhwZWN0IGFueSBvZiB0aGUgYWJvdmUgdG8gdGhyb3csIGJ1dCBiZXR0ZXIgdG8gYmUgc2FmZS5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBzaG91bGRVc2VOYXRpdmUoKSA/IE9iamVjdC5hc3NpZ24gOiBmdW5jdGlvbiAodGFyZ2V0LCBzb3VyY2UpIHtcblx0dmFyIGZyb207XG5cdHZhciB0byA9IHRvT2JqZWN0KHRhcmdldCk7XG5cdHZhciBzeW1ib2xzO1xuXG5cdGZvciAodmFyIHMgPSAxOyBzIDwgYXJndW1lbnRzLmxlbmd0aDsgcysrKSB7XG5cdFx0ZnJvbSA9IE9iamVjdChhcmd1bWVudHNbc10pO1xuXG5cdFx0Zm9yICh2YXIga2V5IGluIGZyb20pIHtcblx0XHRcdGlmIChoYXNPd25Qcm9wZXJ0eS5jYWxsKGZyb20sIGtleSkpIHtcblx0XHRcdFx0dG9ba2V5XSA9IGZyb21ba2V5XTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scykge1xuXHRcdFx0c3ltYm9scyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMoZnJvbSk7XG5cdFx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHN5bWJvbHMubGVuZ3RoOyBpKyspIHtcblx0XHRcdFx0aWYgKHByb3BJc0VudW1lcmFibGUuY2FsbChmcm9tLCBzeW1ib2xzW2ldKSkge1xuXHRcdFx0XHRcdHRvW3N5bWJvbHNbaV1dID0gZnJvbVtzeW1ib2xzW2ldXTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdHJldHVybiB0bztcbn07XG4iLCJtb2R1bGUuZXhwb3J0cz17XG4gICAgXCJ2ZXJzaW9uXCI6IFwidjJcIlxufVxuIiwiLyoqXG4gKiBFcGljZW50ZXIgSmF2YXNjcmlwdCBsaWJyYXJpZXNcbiAqIHY8JT0gdmVyc2lvbiAlPlxuICogaHR0cHM6Ly9naXRodWIuY29tL2ZvcmlvL2VwaWNlbnRlci1qcy1saWJzXG4gKi9cblxudmFyIEYgPSB7XG4gICAgdXRpbDoge30sXG4gICAgZmFjdG9yeToge30sXG4gICAgdHJhbnNwb3J0OiB7fSxcbiAgICBzdG9yZToge30sXG4gICAgc2VydmljZToge30sXG4gICAgbWFuYWdlcjoge1xuICAgICAgICBzdHJhdGVneToge31cbiAgICB9LFxuXG59O1xuXG5GLmxvYWQgPSByZXF1aXJlKCcuL2Vudi1sb2FkJyk7XG5cbmlmICghZ2xvYmFsLlNLSVBfRU5WX0xPQUQpIHtcbiAgICBGLmxvYWQoKTtcbn1cblxuRi51dGlsLnF1ZXJ5ID0gcmVxdWlyZSgnLi91dGlsL3F1ZXJ5LXV0aWwnKTtcbkYudXRpbC5ydW4gPSByZXF1aXJlKCcuL3V0aWwvcnVuLXV0aWwnKTtcbkYudXRpbC5jbGFzc0Zyb20gPSByZXF1aXJlKCcuL3V0aWwvaW5oZXJpdCcpO1xuXG5GLmZhY3RvcnkuVHJhbnNwb3J0ID0gcmVxdWlyZSgnLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xuRi50cmFuc3BvcnQuQWpheCA9IHJlcXVpcmUoJy4vdHJhbnNwb3J0L2FqYXgtaHR0cC10cmFuc3BvcnQnKTtcblxuRi5zZXJ2aWNlLlVSTCA9IHJlcXVpcmUoJy4vc2VydmljZS91cmwtY29uZmlnLXNlcnZpY2UnKTtcbkYuc2VydmljZS5Db25maWcgPSByZXF1aXJlKCcuL3NlcnZpY2UvY29uZmlndXJhdGlvbi1zZXJ2aWNlJyk7XG5GLnNlcnZpY2UuUnVuID0gcmVxdWlyZSgnLi9zZXJ2aWNlL3J1bi1hcGktc2VydmljZScpO1xuRi5zZXJ2aWNlLkZpbGUgPSByZXF1aXJlKCcuL3NlcnZpY2UvYWRtaW4tZmlsZS1zZXJ2aWNlJyk7XG5GLnNlcnZpY2UuVmFyaWFibGVzID0gcmVxdWlyZSgnLi9zZXJ2aWNlL3ZhcmlhYmxlcy1hcGktc2VydmljZScpO1xuRi5zZXJ2aWNlLkRhdGEgPSByZXF1aXJlKCcuL3NlcnZpY2UvZGF0YS1hcGktc2VydmljZScpO1xuRi5zZXJ2aWNlLkF1dGggPSByZXF1aXJlKCcuL3NlcnZpY2UvYXV0aC1hcGktc2VydmljZScpO1xuRi5zZXJ2aWNlLldvcmxkID0gcmVxdWlyZSgnLi9zZXJ2aWNlL3dvcmxkLWFwaS1hZGFwdGVyJyk7XG5GLnNlcnZpY2UuU3RhdGUgPSByZXF1aXJlKCcuL3NlcnZpY2Uvc3RhdGUtYXBpLWFkYXB0ZXInKTtcbkYuc2VydmljZS5Vc2VyID0gcmVxdWlyZSgnLi9zZXJ2aWNlL3VzZXItYXBpLWFkYXB0ZXInKTtcbkYuc2VydmljZS5NZW1iZXIgPSByZXF1aXJlKCcuL3NlcnZpY2UvbWVtYmVyLWFwaS1hZGFwdGVyJyk7XG5GLnNlcnZpY2UuQXNzZXQgPSByZXF1aXJlKCcuL3NlcnZpY2UvYXNzZXQtYXBpLWFkYXB0ZXInKTtcbkYuc2VydmljZS5Hcm91cCA9IHJlcXVpcmUoJy4vc2VydmljZS9ncm91cC1hcGktc2VydmljZScpO1xuRi5zZXJ2aWNlLkludHJvc3BlY3QgPSByZXF1aXJlKCcuL3NlcnZpY2UvaW50cm9zcGVjdGlvbi1hcGktc2VydmljZScpO1xuXG5GLnN0b3JlLkNvb2tpZSA9IHJlcXVpcmUoJy4vc3RvcmUvY29va2llLXN0b3JlJyk7XG5GLmZhY3RvcnkuU3RvcmUgPSByZXF1aXJlKCcuL3N0b3JlL3N0b3JlLWZhY3RvcnknKTtcblxuRi5tYW5hZ2VyLlNjZW5hcmlvTWFuYWdlciA9IHJlcXVpcmUoJy4vbWFuYWdlcnMvc2NlbmFyaW8tbWFuYWdlcicpO1xuRi5tYW5hZ2VyLlJ1bk1hbmFnZXIgPSByZXF1aXJlKCcuL21hbmFnZXJzL3J1bi1tYW5hZ2VyJyk7XG5GLm1hbmFnZXIuQXV0aE1hbmFnZXIgPSByZXF1aXJlKCcuL21hbmFnZXJzL2F1dGgtbWFuYWdlcicpO1xuRi5tYW5hZ2VyLldvcmxkTWFuYWdlciA9IHJlcXVpcmUoJy4vbWFuYWdlcnMvd29ybGQtbWFuYWdlcicpO1xuXG5GLm1hbmFnZXIuc3RyYXRlZ3lbJ2Fsd2F5cy1uZXcnXSA9IHJlcXVpcmUoJy4vbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvYWx3YXlzLW5ldy1zdHJhdGVneScpO1xuRi5tYW5hZ2VyLnN0cmF0ZWd5Wydjb25kaXRpb25hbC1jcmVhdGlvbiddID0gcmVxdWlyZSgnLi9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9jb25kaXRpb25hbC1jcmVhdGlvbi1zdHJhdGVneScpO1xuRi5tYW5hZ2VyLnN0cmF0ZWd5LmlkZW50aXR5ID0gcmVxdWlyZSgnLi9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9ub25lLXN0cmF0ZWd5Jyk7XG5GLm1hbmFnZXIuc3RyYXRlZ3lbJ25ldy1pZi1taXNzaW5nJ10gPSByZXF1aXJlKCcuL21hbmFnZXJzL3J1bi1zdHJhdGVnaWVzL25ldy1pZi1taXNzaW5nLXN0cmF0ZWd5Jyk7XG5GLm1hbmFnZXIuc3RyYXRlZ3lbJ25ldy1pZi1taXNzaW5nJ10gPSByZXF1aXJlKCcuL21hbmFnZXJzL3J1bi1zdHJhdGVnaWVzL25ldy1pZi1taXNzaW5nLXN0cmF0ZWd5Jyk7XG5GLm1hbmFnZXIuc3RyYXRlZ3lbJ25ldy1pZi1wZXJzaXN0ZWQnXSA9IHJlcXVpcmUoJy4vbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvbmV3LWlmLXBlcnNpc3RlZC1zdHJhdGVneScpO1xuRi5tYW5hZ2VyLnN0cmF0ZWd5WyduZXctaWYtaW5pdGlhbGl6ZWQnXSA9IHJlcXVpcmUoJy4vbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvbmV3LWlmLWluaXRpYWxpemVkLXN0cmF0ZWd5Jyk7XG5cbkYubWFuYWdlci5DaGFubmVsTWFuYWdlciA9IHJlcXVpcmUoJy4vbWFuYWdlcnMvZXBpY2VudGVyLWNoYW5uZWwtbWFuYWdlcicpO1xuRi5zZXJ2aWNlLkNoYW5uZWwgPSByZXF1aXJlKCcuL3NlcnZpY2UvY2hhbm5lbC1zZXJ2aWNlJyk7XG5cbkYudmVyc2lvbiA9ICc8JT0gdmVyc2lvbiAlPic7XG5GLmFwaSA9IHJlcXVpcmUoJy4vYXBpLXZlcnNpb24uanNvbicpO1xuXG5nbG9iYWwuRiA9IEY7XG5tb2R1bGUuZXhwb3J0cyA9IEY7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBVUkxDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9zZXJ2aWNlL3VybC1jb25maWctc2VydmljZScpO1xuXG52YXIgZW52TG9hZCA9IGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgIHZhciB1cmxTZXJ2aWNlID0gbmV3IFVSTENvbmZpZ1NlcnZpY2UoKTtcbiAgICB2YXIgaW5mb1VybCA9IHVybFNlcnZpY2UuZ2V0QVBJUGF0aCgnY29uZmlnJyk7XG4gICAgdmFyIGVudlByb21pc2UgPSAkLmFqYXgoeyB1cmw6IGluZm9VcmwsIGFzeW5jOiBmYWxzZSB9KTtcbiAgICBlbnZQcm9taXNlID0gZW52UHJvbWlzZS50aGVuKGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgdmFyIG92ZXJyaWRlcyA9IHJlcy5hcGk7XG4gICAgICAgIFVSTENvbmZpZ1NlcnZpY2UuZGVmYXVsdHMgPSAkLmV4dGVuZChVUkxDb25maWdTZXJ2aWNlLmRlZmF1bHRzLCBvdmVycmlkZXMpO1xuICAgIH0pO1xuICAgIHJldHVybiBlbnZQcm9taXNlLnRoZW4oY2FsbGJhY2spLmZhaWwoY2FsbGJhY2spO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBlbnZMb2FkO1xuIiwiLyoqXG4qICMjIEF1dGhvcml6YXRpb24gTWFuYWdlclxuKlxuKiBUaGUgQXV0aG9yaXphdGlvbiBNYW5hZ2VyIHByb3ZpZGVzIGFuIGVhc3kgd2F5IHRvIG1hbmFnZSB1c2VyIGF1dGhlbnRpY2F0aW9uIChsb2dnaW5nIGluIGFuZCBvdXQpIGFuZCBhdXRob3JpemF0aW9uIChrZWVwaW5nIHRyYWNrIG9mIHRva2Vucywgc2Vzc2lvbnMsIGFuZCBncm91cHMpIGZvciBwcm9qZWN0cy5cbipcbiogVGhlIEF1dGhvcml6YXRpb24gTWFuYWdlciBpcyBtb3N0IHVzZWZ1bCBmb3IgW3RlYW0gcHJvamVjdHNdKC4uLy4uLy4uL2dsb3NzYXJ5LyN0ZWFtKSB3aXRoIGFuIGFjY2VzcyBsZXZlbCBvZiBbQXV0aGVudGljYXRlZF0oLi4vLi4vLi4vZ2xvc3NhcnkvI2FjY2VzcykuIFRoZXNlIHByb2plY3RzIGFyZSBhY2Nlc3NlZCBieSBbZW5kIHVzZXJzXSguLi8uLi8uLi9nbG9zc2FyeS8jdXNlcnMpIHdobyBhcmUgbWVtYmVycyBvZiBvbmUgb3IgbW9yZSBbZ3JvdXBzXSguLi8uLi8uLi9nbG9zc2FyeS8jZ3JvdXBzKS5cbipcbiogIyMjIyBVc2luZyB0aGUgQXV0aG9yaXphdGlvbiBNYW5hZ2VyXG4qXG4qIFRvIHVzZSB0aGUgQXV0aG9yaXphdGlvbiBNYW5hZ2VyLCBpbnN0YW50aWF0ZSBpdC4gVGhlbiwgbWFrZSBjYWxscyB0byBhbnkgb2YgdGhlIG1ldGhvZHMgeW91IG5lZWQ6XG4qXG4qICAgICAgIHZhciBhdXRoTWdyID0gbmV3IEYubWFuYWdlci5BdXRoTWFuYWdlcih7XG4qICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4qICAgICAgICAgICB1c2VyTmFtZTogJ2VuZHVzZXIxJyxcbiogICAgICAgICAgIHBhc3N3b3JkOiAncGFzc3cwcmQnXG4qICAgICAgIH0pO1xuKiAgICAgICBhdXRoTWdyLmxvZ2luKCkudGhlbihmdW5jdGlvbiAoKSB7XG4qICAgICAgICAgICBhdXRoTWdyLmdldEN1cnJlbnRVc2VyU2Vzc2lvbkluZm8oKTtcbiogICAgICAgfSk7XG4qXG4qXG4qIFRoZSBgb3B0aW9uc2Agb2JqZWN0IHBhc3NlZCB0byB0aGUgYEYubWFuYWdlci5BdXRoTWFuYWdlcigpYCBjYWxsIGNhbiBpbmNsdWRlOlxuKlxuKiAgICogYGFjY291bnRgOiBUaGUgYWNjb3VudCBpZCBmb3IgdGhpcyBgdXNlck5hbWVgLiBJbiB0aGUgRXBpY2VudGVyIFVJLCB0aGlzIGlzIHRoZSAqKlRlYW0gSUQqKiAoZm9yIHRlYW0gcHJvamVjdHMpIG9yIHRoZSAqKlVzZXIgSUQqKiAoZm9yIHBlcnNvbmFsIHByb2plY3RzKS5cbiogICAqIGB1c2VyTmFtZWA6IEVtYWlsIG9yIHVzZXJuYW1lIHRvIHVzZSBmb3IgbG9nZ2luZyBpbi5cbiogICAqIGBwYXNzd29yZGA6IFBhc3N3b3JkIGZvciBzcGVjaWZpZWQgYHVzZXJOYW1lYC5cbiogICAqIGBwcm9qZWN0YDogVGhlICoqUHJvamVjdCBJRCoqIGZvciB0aGUgcHJvamVjdCB0byBsb2cgdGhpcyB1c2VyIGludG8uIE9wdGlvbmFsLlxuKiAgICogYGdyb3VwSWRgOiBJZCBvZiB0aGUgZ3JvdXAgdG8gd2hpY2ggYHVzZXJOYW1lYCBiZWxvbmdzLiBSZXF1aXJlZCBmb3IgZW5kIHVzZXJzIGlmIHRoZSBgcHJvamVjdGAgaXMgc3BlY2lmaWVkLlxuKlxuKiBJZiB5b3UgcHJlZmVyIHN0YXJ0aW5nIGZyb20gYSB0ZW1wbGF0ZSwgdGhlIEVwaWNlbnRlciBKUyBMaWJzIFtMb2dpbiBDb21wb25lbnRdKC4uLy4uLyNjb21wb25lbnRzKSB1c2VzIHRoZSBBdXRob3JpemF0aW9uIE1hbmFnZXIgYXMgd2VsbC4gVGhpcyBzYW1wbGUgSFRNTCBwYWdlIChhbmQgYXNzb2NpYXRlZCBDU1MgYW5kIEpTIGZpbGVzKSBwcm92aWRlcyBhIGxvZ2luIGZvcm0gZm9yIHRlYW0gbWVtYmVycyBhbmQgZW5kIHVzZXJzIG9mIHlvdXIgcHJvamVjdC4gSXQgYWxzbyBpbmNsdWRlcyBhIGdyb3VwIHNlbGVjdG9yIGZvciBlbmQgdXNlcnMgdGhhdCBhcmUgbWVtYmVycyBvZiBtdWx0aXBsZSBncm91cHMuXG4qL1xuXG4ndXNlIHN0cmljdCc7XG52YXIgQXV0aEFkYXB0ZXIgPSByZXF1aXJlKCcuLi9zZXJ2aWNlL2F1dGgtYXBpLXNlcnZpY2UnKTtcbnZhciBNZW1iZXJBZGFwdGVyID0gcmVxdWlyZSgnLi4vc2VydmljZS9tZW1iZXItYXBpLWFkYXB0ZXInKTtcbnZhciBHcm91cFNlcnZpY2UgPSByZXF1aXJlKCcuLi9zZXJ2aWNlL2dyb3VwLWFwaS1zZXJ2aWNlJyk7XG52YXIgU2Vzc2lvbk1hbmFnZXIgPSByZXF1aXJlKCcuLi9zdG9yZS9zZXNzaW9uLW1hbmFnZXInKTtcbnZhciBfcGljayA9IHJlcXVpcmUoJy4uL3V0aWwvb2JqZWN0LXV0aWwnKS5fcGljaztcbnZhciBvYmplY3RBc3NpZ24gPSByZXF1aXJlKCdvYmplY3QtYXNzaWduJyk7XG5cbnZhciBhdG9iID0gd2luZG93LmF0b2IgfHwgcmVxdWlyZSgnQmFzZTY0JykuYXRvYjtcblxudmFyIGRlZmF1bHRzID0ge1xuICAgIHJlcXVpcmVzR3JvdXA6IHRydWVcbn07XG5cbmZ1bmN0aW9uIEF1dGhNYW5hZ2VyKG9wdGlvbnMpIHtcbiAgICBvcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIGRlZmF1bHRzLCBvcHRpb25zKTtcbiAgICB0aGlzLnNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKG9wdGlvbnMpO1xuICAgIHRoaXMub3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucygpO1xuXG4gICAgdGhpcy5hdXRoQWRhcHRlciA9IG5ldyBBdXRoQWRhcHRlcih0aGlzLm9wdGlvbnMpO1xufVxuXG52YXIgX2ZpbmRVc2VySW5Hcm91cCA9IGZ1bmN0aW9uIChtZW1iZXJzLCBpZCkge1xuICAgIGZvciAodmFyIGogPSAwOyBqIDwgbWVtYmVycy5sZW5ndGg7IGorKykge1xuICAgICAgICBpZiAobWVtYmVyc1tqXS51c2VySWQgPT09IGlkKSB7XG4gICAgICAgICAgICByZXR1cm4gbWVtYmVyc1tqXTtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbn07XG5cbkF1dGhNYW5hZ2VyLnByb3RvdHlwZSA9ICQuZXh0ZW5kKEF1dGhNYW5hZ2VyLnByb3RvdHlwZSwge1xuXG4gICAgLyoqXG4gICAgKiBMb2dzIHVzZXIgaW4uXG4gICAgKlxuICAgICogKipFeGFtcGxlKipcbiAgICAqXG4gICAgKiAgICAgICBhdXRoTWdyLmxvZ2luKHtcbiAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAqICAgICAgICAgICB1c2VyTmFtZTogJ2VuZHVzZXIxJyxcbiAgICAqICAgICAgICAgICBwYXNzd29yZDogJ3Bhc3N3MHJkJ1xuICAgICogICAgICAgfSlcbiAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbihzdGF0dXNPYmopIHtcbiAgICAqICAgICAgICAgICAgICAgLy8gaWYgZW5kdXNlcjEgYmVsb25ncyB0byBleGFjdGx5IG9uZSBncm91cFxuICAgICogICAgICAgICAgICAgICAvLyAob3IgaWYgdGhlIGxvZ2luKCkgY2FsbCBpcyBtb2RpZmllZCB0byBpbmNsdWRlIHRoZSBncm91cCBpZClcbiAgICAqICAgICAgICAgICAgICAgLy8gY29udGludWUgaGVyZVxuICAgICogICAgICAgICAgIH0pXG4gICAgKiAgICAgICAgICAgLmZhaWwoZnVuY3Rpb24oc3RhdHVzT2JqKSB7XG4gICAgKiAgICAgICAgICAgICAgIC8vIGlmIGVuZHVzZXIxIGJlbG9uZ3MgdG8gbXVsdGlwbGUgZ3JvdXBzLFxuICAgICogICAgICAgICAgICAgICAvLyB0aGUgbG9naW4oKSBjYWxsIGZhaWxzXG4gICAgKiAgICAgICAgICAgICAgIC8vIGFuZCByZXR1cm5zIGFsbCBncm91cHMgb2Ygd2hpY2ggdGhlIHVzZXIgaXMgYSBtZW1iZXJcbiAgICAqICAgICAgICAgICAgICAgZm9yICh2YXIgaT0wOyBpIDwgc3RhdHVzT2JqLnVzZXJHcm91cHMubGVuZ3RoOyBpKyspIHtcbiAgICAqICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHN0YXR1c09iai51c2VyR3JvdXBzW2ldLm5hbWUsIHN0YXR1c09iai51c2VyR3JvdXBzW2ldLmdyb3VwSWQpO1xuICAgICogICAgICAgICAgICAgICB9XG4gICAgKiAgICAgICAgICAgfSk7XG4gICAgKlxuICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAqXG4gICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLiBJZiBub3QgcGFzc2VkIGluIHdoZW4gY3JlYXRpbmcgYW4gaW5zdGFuY2Ugb2YgdGhlIG1hbmFnZXIgKGBGLm1hbmFnZXIuQXV0aE1hbmFnZXIoKWApLCB0aGVzZSBvcHRpb25zIHNob3VsZCBpbmNsdWRlOlxuICAgICogQHBhcmFtIHtzdHJpbmd9IG9wdGlvbnMuYWNjb3VudCBUaGUgYWNjb3VudCBpZCBmb3IgdGhpcyBgdXNlck5hbWVgLiBJbiB0aGUgRXBpY2VudGVyIFVJLCB0aGlzIGlzIHRoZSAqKlRlYW0gSUQqKiAoZm9yIHRlYW0gcHJvamVjdHMpIG9yIHRoZSAqKlVzZXIgSUQqKiAoZm9yIHBlcnNvbmFsIHByb2plY3RzKS5cbiAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLnVzZXJOYW1lIEVtYWlsIG9yIHVzZXJuYW1lIHRvIHVzZSBmb3IgbG9nZ2luZyBpbi5cbiAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLnBhc3N3b3JkIFBhc3N3b3JkIGZvciBzcGVjaWZpZWQgYHVzZXJOYW1lYC5cbiAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLnByb2plY3QgKE9wdGlvbmFsKSBUaGUgKipQcm9qZWN0IElEKiogZm9yIHRoZSBwcm9qZWN0IHRvIGxvZyB0aGlzIHVzZXIgaW50by5cbiAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLmdyb3VwSWQgVGhlIGlkIG9mIHRoZSBncm91cCB0byB3aGljaCBgdXNlck5hbWVgIGJlbG9uZ3MuIFJlcXVpcmVkIGZvciBbZW5kIHVzZXJzXSguLi8uLi8uLi9nbG9zc2FyeS8jdXNlcnMpIGlmIHRoZSBgcHJvamVjdGAgaXMgc3BlY2lmaWVkIGFuZCBpZiB0aGUgZW5kIHVzZXJzIGFyZSBtZW1iZXJzIG9mIG11bHRpcGxlIFtncm91cHNdKC4uLy4uLy4uL2dsb3NzYXJ5LyNncm91cHMpLCBvdGhlcndpc2Ugb3B0aW9uYWwuXG4gICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICovXG4gICAgbG9naW46IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgIHZhciAkZCA9ICQuRGVmZXJyZWQoKTtcbiAgICAgICAgdmFyIHNlc3Npb25NYW5hZ2VyID0gdGhpcy5zZXNzaW9uTWFuYWdlcjtcbiAgICAgICAgdmFyIGFkYXB0ZXJPcHRpb25zID0gc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyh7IHN1Y2Nlc3M6ICQubm9vcCwgZXJyb3I6ICQubm9vcCB9LCBvcHRpb25zKTtcbiAgICAgICAgdmFyIG91dFN1Y2Nlc3MgPSBhZGFwdGVyT3B0aW9ucy5zdWNjZXNzO1xuICAgICAgICB2YXIgb3V0RXJyb3IgPSBhZGFwdGVyT3B0aW9ucy5lcnJvcjtcbiAgICAgICAgdmFyIGdyb3VwSWQgPSBhZGFwdGVyT3B0aW9ucy5ncm91cElkO1xuXG4gICAgICAgIHZhciBkZWNvZGVUb2tlbiA9IGZ1bmN0aW9uICh0b2tlbikge1xuICAgICAgICAgICAgdmFyIGVuY29kZWQgPSB0b2tlbi5zcGxpdCgnLicpWzFdO1xuICAgICAgICAgICAgd2hpbGUgKGVuY29kZWQubGVuZ3RoICUgNCAhPT0gMCkgeyAvL2VzbGludC1kaXNhYmxlLWxpbmVcbiAgICAgICAgICAgICAgICBlbmNvZGVkICs9ICc9JztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGF0b2IoZW5jb2RlZCkpO1xuICAgICAgICB9O1xuXG4gICAgICAgIHZhciBoYW5kbGVHcm91cEVycm9yID0gZnVuY3Rpb24gKG1lc3NhZ2UsIHN0YXR1c0NvZGUsIGRhdGEpIHtcbiAgICAgICAgICAgIC8vIGxvZ291dCB0aGUgdXNlciBzaW5jZSBpdCdzIGluIGFuIGludmFsaWQgc3RhdGUgd2l0aCBubyBncm91cCBzZWxlY3RlZFxuICAgICAgICAgICAgbWUubG9nb3V0KCkudGhlbihmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgdmFyIGVycm9yID0gJC5leHRlbmQodHJ1ZSwge30sIGRhdGEsIHsgc3RhdHVzVGV4dDogbWVzc2FnZSwgc3RhdHVzOiBzdGF0dXNDb2RlIH0pO1xuICAgICAgICAgICAgICAgICRkLnJlamVjdChlcnJvcik7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfTtcblxuICAgICAgICB2YXIgaGFuZGxlU3VjY2VzcyA9IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgdmFyIHRva2VuID0gcmVzcG9uc2UuYWNjZXNzX3Rva2VuO1xuICAgICAgICAgICAgdmFyIHVzZXJJbmZvID0gZGVjb2RlVG9rZW4odG9rZW4pO1xuICAgICAgICAgICAgdmFyIG9sZEdyb3VwcyA9IHNlc3Npb25NYW5hZ2VyLmdldFNlc3Npb24oYWRhcHRlck9wdGlvbnMpLmdyb3VwcyB8fCB7fTtcbiAgICAgICAgICAgIHZhciB1c2VyR3JvdXBPcHRzID0gJC5leHRlbmQodHJ1ZSwge30sIGFkYXB0ZXJPcHRpb25zLCB7IHN1Y2Nlc3M6ICQubm9vcCB9KTtcbiAgICAgICAgICAgIHZhciBkYXRhID0geyBhdXRoOiByZXNwb25zZSwgdXNlcjogdXNlckluZm8gfTtcbiAgICAgICAgICAgIHZhciBwcm9qZWN0ID0gYWRhcHRlck9wdGlvbnMucHJvamVjdDtcbiAgICAgICAgICAgIHZhciBpc1RlYW1NZW1iZXIgPSB1c2VySW5mby5wYXJlbnRfYWNjb3VudF9pZCA9PT0gbnVsbDtcbiAgICAgICAgICAgIHZhciByZXF1aXJlc0dyb3VwID0gYWRhcHRlck9wdGlvbnMucmVxdWlyZXNHcm91cCAmJiBwcm9qZWN0O1xuXG4gICAgICAgICAgICB2YXIgc2Vzc2lvbkluZm8gPSB7XG4gICAgICAgICAgICAgICAgYXV0aF90b2tlbjogdG9rZW4sXG4gICAgICAgICAgICAgICAgYWNjb3VudDogYWRhcHRlck9wdGlvbnMuYWNjb3VudCxcbiAgICAgICAgICAgICAgICBwcm9qZWN0OiBwcm9qZWN0LFxuICAgICAgICAgICAgICAgIHVzZXJJZDogdXNlckluZm8udXNlcl9pZCxcbiAgICAgICAgICAgICAgICBncm91cHM6IG9sZEdyb3VwcyxcbiAgICAgICAgICAgICAgICBpc1RlYW1NZW1iZXI6IGlzVGVhbU1lbWJlclxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIC8vIFRoZSBncm91cCBpcyBub3QgcmVxdWlyZWQgaWYgdGhlIHVzZXIgaXMgbm90IGxvZ2dpbmcgaW50byBhIHByb2plY3RcbiAgICAgICAgICAgIGlmICghcmVxdWlyZXNHcm91cCkge1xuICAgICAgICAgICAgICAgIHNlc3Npb25NYW5hZ2VyLnNhdmVTZXNzaW9uKHNlc3Npb25JbmZvKTtcbiAgICAgICAgICAgICAgICBvdXRTdWNjZXNzLmFwcGx5KHRoaXMsIFtkYXRhXSk7XG4gICAgICAgICAgICAgICAgJGQucmVzb2x2ZShkYXRhKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBoYW5kbGVHcm91cExpc3QgPSBmdW5jdGlvbiAoZ3JvdXBMaXN0KSB7XG4gICAgICAgICAgICAgICAgZGF0YS51c2VyR3JvdXBzID0gZ3JvdXBMaXN0O1xuXG4gICAgICAgICAgICAgICAgdmFyIGdyb3VwID0gbnVsbDtcbiAgICAgICAgICAgICAgICBpZiAoZ3JvdXBMaXN0Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICBoYW5kbGVHcm91cEVycm9yKCdUaGUgdXNlciBoYXMgbm8gZ3JvdXBzIGFzc29jaWF0ZWQgaW4gdGhpcyBhY2NvdW50JywgNDAxLCBkYXRhKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoZ3JvdXBMaXN0Lmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBTZWxlY3QgdGhlIG9ubHkgZ3JvdXBcbiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cExpc3RbMF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChncm91cExpc3QubGVuZ3RoID4gMSkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoZ3JvdXBJZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGZpbHRlcmVkR3JvdXBzID0gJC5ncmVwKGdyb3VwTGlzdCwgZnVuY3Rpb24gKHJlc0dyb3VwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc0dyb3VwLmdyb3VwSWQgPT09IGdyb3VwSWQ7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZmlsdGVyZWRHcm91cHMubGVuZ3RoID09PSAxID8gZmlsdGVyZWRHcm91cHNbMF0gOiBudWxsO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGdyb3VwKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIEEgdGVhbSBtZW1iZXIgZG9lcyBub3QgZ2V0IHRoZSBncm91cCBtZW1iZXJzIGJlY2F1c2UgaXMgY2FsbGluZyB0aGUgR3JvdXAgQVBJXG4gICAgICAgICAgICAgICAgICAgIC8vIGJ1dCBpdCdzIGF1dG9tYXRpY2FsbHkgYSBmYWMgdXNlclxuICAgICAgICAgICAgICAgICAgICB2YXIgaXNGYWMgPSBpc1RlYW1NZW1iZXIgPyB0cnVlIDogX2ZpbmRVc2VySW5Hcm91cChncm91cC5tZW1iZXJzLCB1c2VySW5mby51c2VyX2lkKS5yb2xlID09PSAnZmFjaWxpdGF0b3InO1xuICAgICAgICAgICAgICAgICAgICB2YXIgZ3JvdXBEYXRhID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBJZDogZ3JvdXAuZ3JvdXBJZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwTmFtZTogZ3JvdXAubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGlzRmFjOiBpc0ZhY1xuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICB2YXIgc2Vzc2lvbkluZm9XaXRoR3JvdXAgPSBvYmplY3RBc3NpZ24oe30sIHNlc3Npb25JbmZvLCBncm91cERhdGEpO1xuICAgICAgICAgICAgICAgICAgICBzZXNzaW9uSW5mby5ncm91cHNbcHJvamVjdF0gPSBncm91cERhdGE7XG4gICAgICAgICAgICAgICAgICAgIG1lLnNlc3Npb25NYW5hZ2VyLnNhdmVTZXNzaW9uKHNlc3Npb25JbmZvV2l0aEdyb3VwLCBhZGFwdGVyT3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgICAgIG91dFN1Y2Nlc3MuYXBwbHkodGhpcywgW2RhdGFdKTtcbiAgICAgICAgICAgICAgICAgICAgJGQucmVzb2x2ZShkYXRhKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBoYW5kbGVHcm91cEVycm9yKCdUaGlzIHVzZXIgaXMgYXNzb2NpYXRlZCB3aXRoIG1vcmUgdGhhbiBvbmUgZ3JvdXAuIFBsZWFzZSBzcGVjaWZ5IGEgZ3JvdXAgaWQgdG8gbG9nIGludG8gYW5kIHRyeSBhZ2FpbicsIDQwMywgZGF0YSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgaWYgKCFpc1RlYW1NZW1iZXIpIHtcbiAgICAgICAgICAgICAgICBtZS5nZXRVc2VyR3JvdXBzKHsgdXNlcklkOiB1c2VySW5mby51c2VyX2lkLCB0b2tlbjogdG9rZW4gfSwgdXNlckdyb3VwT3B0cylcbiAgICAgICAgICAgICAgICAgICAgLnRoZW4oaGFuZGxlR3JvdXBMaXN0LCAkZC5yZWplY3QpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB2YXIgb3B0cyA9IG9iamVjdEFzc2lnbih7fSwgdXNlckdyb3VwT3B0cywgeyB0b2tlbjogdG9rZW4gfSk7XG4gICAgICAgICAgICAgICAgdmFyIGdyb3VwU2VydmljZSA9IG5ldyBHcm91cFNlcnZpY2Uob3B0cyk7XG4gICAgICAgICAgICAgICAgZ3JvdXBTZXJ2aWNlLmdldEdyb3Vwcyh7IGFjY291bnQ6IGFkYXB0ZXJPcHRpb25zLmFjY291bnQsIHByb2plY3Q6IHByb2plY3QgfSlcbiAgICAgICAgICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKGdyb3Vwcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gR3JvdXAgQVBJIHJldHVybnMgaWQgaW5zdGVhZCBvZiBncm91cElkXG4gICAgICAgICAgICAgICAgICAgICAgICBncm91cHMuZm9yRWFjaChmdW5jdGlvbiAoZ3JvdXApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ncm91cElkID0gZ3JvdXAuaWQ7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhbmRsZUdyb3VwTGlzdChncm91cHMpO1xuICAgICAgICAgICAgICAgICAgICB9LCAkZC5yZWplY3QpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIGFkYXB0ZXJPcHRpb25zLnN1Y2Nlc3MgPSBoYW5kbGVTdWNjZXNzO1xuICAgICAgICBhZGFwdGVyT3B0aW9ucy5lcnJvciA9IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgaWYgKGFkYXB0ZXJPcHRpb25zLmFjY291bnQpIHtcbiAgICAgICAgICAgICAgICAvLyBUcnkgdG8gbG9naW4gYXMgYSBzeXN0ZW0gdXNlclxuICAgICAgICAgICAgICAgIGFkYXB0ZXJPcHRpb25zLmFjY291bnQgPSBudWxsO1xuICAgICAgICAgICAgICAgIGFkYXB0ZXJPcHRpb25zLmVycm9yID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBvdXRFcnJvci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgICAgICAgICAkZC5yZWplY3QocmVzcG9uc2UpO1xuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICBtZS5hdXRoQWRhcHRlci5sb2dpbihhZGFwdGVyT3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBvdXRFcnJvci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgJGQucmVqZWN0KHJlc3BvbnNlKTtcbiAgICAgICAgfTtcblxuICAgICAgICB0aGlzLmF1dGhBZGFwdGVyLmxvZ2luKGFkYXB0ZXJPcHRpb25zKTtcbiAgICAgICAgcmV0dXJuICRkLnByb21pc2UoKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgKiBMb2dzIHVzZXIgb3V0IGJ5IGNsZWFyaW5nIGFsbCBzZXNzaW9uIGluZm9ybWF0aW9uLlxuICAgICpcbiAgICAqICoqRXhhbXBsZSoqXG4gICAgKlxuICAgICogICAgICAgYXV0aE1nci5sb2dvdXQoKTtcbiAgICAqXG4gICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICpcbiAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICovXG4gICAgbG9nb3V0OiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICB2YXIgbWUgPSB0aGlzO1xuICAgICAgICB2YXIgYWRhcHRlck9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMob3B0aW9ucyk7XG5cbiAgICAgICAgdmFyIHJlbW92ZUNvb2tpZUZuID0gZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG4gICAgICAgICAgICBtZS5zZXNzaW9uTWFuYWdlci5yZW1vdmVTZXNzaW9uKCk7XG4gICAgICAgIH07XG5cbiAgICAgICAgcmV0dXJuIHRoaXMuYXV0aEFkYXB0ZXIubG9nb3V0KGFkYXB0ZXJPcHRpb25zKS50aGVuKHJlbW92ZUNvb2tpZUZuKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgZXhpc3RpbmcgdXNlciBhY2Nlc3MgdG9rZW4gaWYgdGhlIHVzZXIgaXMgYWxyZWFkeSBsb2dnZWQgaW4uIE90aGVyd2lzZSwgbG9ncyB0aGUgdXNlciBpbiwgY3JlYXRpbmcgYSBuZXcgdXNlciBhY2Nlc3MgdG9rZW4sIGFuZCByZXR1cm5zIHRoZSBuZXcgdG9rZW4uIChTZWUgW21vcmUgYmFja2dyb3VuZCBvbiBhY2Nlc3MgdG9rZW5zXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pKS5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgIGF1dGhNZ3IuZ2V0VG9rZW4oKVxuICAgICAqICAgICAgICAgIC50aGVuKGZ1bmN0aW9uICh0b2tlbikge1xuICAgICAqICAgICAgICAgICAgICBjb25zb2xlLmxvZygnTXkgdG9rZW4gaXMgJywgdG9rZW4pO1xuICAgICAqICAgICAgICAgIH0pO1xuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICovXG4gICAgZ2V0VG9rZW46IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIHZhciBodHRwT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhvcHRpb25zKTtcblxuICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0U2Vzc2lvbihodHRwT3B0aW9ucyk7XG4gICAgICAgIHZhciAkZCA9ICQuRGVmZXJyZWQoKTtcbiAgICAgICAgaWYgKHNlc3Npb24uYXV0aF90b2tlbikge1xuICAgICAgICAgICAgJGQucmVzb2x2ZShzZXNzaW9uLmF1dGhfdG9rZW4pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5sb2dpbihodHRwT3B0aW9ucykudGhlbigkZC5yZXNvbHZlKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gJGQucHJvbWlzZSgpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGFuIGFycmF5IG9mIGdyb3VwIHJlY29yZHMsIG9uZSBmb3IgZWFjaCBncm91cCBvZiB3aGljaCB0aGUgY3VycmVudCB1c2VyIGlzIGEgbWVtYmVyLiBFYWNoIGdyb3VwIHJlY29yZCBpbmNsdWRlcyB0aGUgZ3JvdXAgYG5hbWVgLCBgYWNjb3VudGAsIGBwcm9qZWN0YCwgYW5kIGBncm91cElkYC5cbiAgICAgKlxuICAgICAqIElmIHNvbWUgZW5kIHVzZXJzIGluIHlvdXIgcHJvamVjdCBhcmUgbWVtYmVycyBvZiBtdWx0aXBsZSBncm91cHMsIHRoaXMgaXMgYSB1c2VmdWwgbWV0aG9kIHRvIGNhbGwgb24geW91ciBwcm9qZWN0J3MgbG9naW4gcGFnZS4gV2hlbiB0aGUgdXNlciBhdHRlbXB0cyB0byBsb2cgaW4sIHlvdSBjYW4gdXNlIHRoaXMgdG8gZGlzcGxheSB0aGUgZ3JvdXBzIG9mIHdoaWNoIHRoZSB1c2VyIGlzIG1lbWJlciwgYW5kIGhhdmUgdGhlIHVzZXIgc2VsZWN0IHRoZSBjb3JyZWN0IGdyb3VwIHRvIGxvZyBpbiB0byBmb3IgdGhpcyBzZXNzaW9uLlxuICAgICAqXG4gICAgICogKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICAgLy8gZ2V0IGdyb3VwcyBmb3IgY3VycmVudCB1c2VyXG4gICAgICogICAgICB2YXIgc2Vzc2lvbk9iaiA9IGF1dGhNZ3IuZ2V0Q3VycmVudFVzZXJTZXNzaW9uSW5mbygpO1xuICAgICAqICAgICAgYXV0aE1nci5nZXRVc2VyR3JvdXBzKHsgdXNlcklkOiBzZXNzaW9uT2JqLnVzZXJJZCwgdG9rZW46IHNlc3Npb25PYmouYXV0aF90b2tlbiB9KVxuICAgICAqICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChncm91cHMpIHtcbiAgICAgKiAgICAgICAgICAgICAgZm9yICh2YXIgaT0wOyBpIDwgZ3JvdXBzLmxlbmd0aDsgaSsrKVxuICAgICAqICAgICAgICAgICAgICAgICAgeyBjb25zb2xlLmxvZyhncm91cHNbaV0ubmFtZSk7IH1cbiAgICAgKiAgICAgICAgICB9KTtcbiAgICAgKlxuICAgICAqICAgICAgLy8gZ2V0IGdyb3VwcyBmb3IgcGFydGljdWxhciB1c2VyXG4gICAgICogICAgICBhdXRoTWdyLmdldFVzZXJHcm91cHMoe3VzZXJJZDogJ2IxYzE5ZGRhLTJkMmUtNDc3Ny1hZDVkLTM5MjlmMTdlODZkMycsIHRva2VuOiBzYXZlZFByb2pBY2Nlc3NUb2tlbiB9KTtcbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBhcmFtcyBPYmplY3Qgd2l0aCBhIHVzZXJJZCBhbmQgdG9rZW4gcHJvcGVydGllcy5cbiAgICAgKiBAcGFyYW0ge1N0cmluZ30gcGFyYW1zLnVzZXJJZCBUaGUgdXNlcklkLiBJZiBsb29raW5nIHVwIGdyb3VwcyBmb3IgdGhlIGN1cnJlbnRseSBsb2dnZWQgaW4gdXNlciwgdGhpcyBpcyBpbiB0aGUgc2Vzc2lvbiBpbmZvcm1hdGlvbi4gT3RoZXJ3aXNlLCBwYXNzIGEgc3RyaW5nLlxuICAgICAqIEBwYXJhbSB7U3RyaW5nfSBwYXJhbXMudG9rZW4gVGhlIGF1dGhvcml6YXRpb24gY3JlZGVudGlhbHMgKGFjY2VzcyB0b2tlbikgdG8gdXNlIGZvciBjaGVja2luZyB0aGUgZ3JvdXBzIGZvciB0aGlzIHVzZXIuIElmIGxvb2tpbmcgdXAgZ3JvdXBzIGZvciB0aGUgY3VycmVudGx5IGxvZ2dlZCBpbiB1c2VyLCB0aGlzIGlzIGluIHRoZSBzZXNzaW9uIGluZm9ybWF0aW9uLiBBIHRlYW0gbWVtYmVyJ3MgdG9rZW4gb3IgYSBwcm9qZWN0IGFjY2VzcyB0b2tlbiBjYW4gYWNjZXNzIGFsbCB0aGUgZ3JvdXBzIGZvciBhbGwgZW5kIHVzZXJzIGluIHRoZSB0ZWFtIG9yIHByb2plY3QuXG4gICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAqL1xuICAgIGdldFVzZXJHcm91cHM6IGZ1bmN0aW9uIChwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgdmFyIGFkYXB0ZXJPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKHsgc3VjY2VzczogJC5ub29wIH0sIG9wdGlvbnMpO1xuICAgICAgICB2YXIgJGQgPSAkLkRlZmVycmVkKCk7XG4gICAgICAgIHZhciBvdXRTdWNjZXNzID0gYWRhcHRlck9wdGlvbnMuc3VjY2VzcztcblxuICAgICAgICBhZGFwdGVyT3B0aW9ucy5zdWNjZXNzID0gZnVuY3Rpb24gKG1lbWJlckluZm8pIHtcbiAgICAgICAgICAgIC8vIFRoZSBtZW1iZXIgQVBJIGlzIGF0IHRoZSBhY2NvdW50IHNjb3BlLCB3ZSBmaWx0ZXIgYnkgcHJvamVjdFxuICAgICAgICAgICAgaWYgKGFkYXB0ZXJPcHRpb25zLnByb2plY3QpIHtcbiAgICAgICAgICAgICAgICBtZW1iZXJJbmZvID0gJC5ncmVwKG1lbWJlckluZm8sIGZ1bmN0aW9uIChncm91cCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZ3JvdXAucHJvamVjdCA9PT0gYWRhcHRlck9wdGlvbnMucHJvamVjdDtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgb3V0U3VjY2Vzcy5hcHBseSh0aGlzLCBbbWVtYmVySW5mb10pO1xuICAgICAgICAgICAgJGQucmVzb2x2ZShtZW1iZXJJbmZvKTtcbiAgICAgICAgfTtcblxuICAgICAgICB2YXIgbWVtYmVyQWRhcHRlciA9IG5ldyBNZW1iZXJBZGFwdGVyKHsgdG9rZW46IHBhcmFtcy50b2tlbiwgc2VydmVyOiBhZGFwdGVyT3B0aW9ucy5zZXJ2ZXIgfSk7XG4gICAgICAgIG1lbWJlckFkYXB0ZXIuZ2V0R3JvdXBzRm9yVXNlcihwYXJhbXMsIGFkYXB0ZXJPcHRpb25zKS5mYWlsKCRkLnJlamVjdCk7XG4gICAgICAgIHJldHVybiAkZC5wcm9taXNlKCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgc2Vzc2lvbiBpbmZvcm1hdGlvbiBmb3IgdGhlIGN1cnJlbnQgdXNlciwgaW5jbHVkaW5nIHRoZSBgdXNlcklkYCwgYGFjY291bnRgLCBgcHJvamVjdGAsIGBncm91cElkYCwgYGdyb3VwTmFtZWAsIGBpc0ZhY2AgKHdoZXRoZXIgdGhlIGVuZCB1c2VyIGlzIGEgZmFjaWxpdGF0b3Igb2YgdGhpcyBncm91cCksIGFuZCBgYXV0aF90b2tlbmAgKHVzZXIgYWNjZXNzIHRva2VuKS5cbiAgICAgKlxuICAgICAqICpJbXBvcnRhbnQqOiBUaGlzIG1ldGhvZCBpcyBzeW5jaHJvbm91cy4gVGhlIHNlc3Npb24gaW5mb3JtYXRpb24gaXMgcmV0dXJuZWQgaW1tZWRpYXRlbHkgaW4gYW4gb2JqZWN0OyBubyBjYWxsYmFja3Mgb3IgcHJvbWlzZXMgYXJlIG5lZWRlZC5cbiAgICAgKlxuICAgICAqIFNlc3Npb24gaW5mb3JtYXRpb24gaXMgc3RvcmVkIGluIGEgY29va2llIGluIHRoZSBicm93c2VyLlxuICAgICAqXG4gICAgICogKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICAgdmFyIHNlc3Npb25PYmogPSBhdXRoTWdyLmdldEN1cnJlbnRVc2VyU2Vzc2lvbkluZm8oKTtcbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgKiBAcmV0dXJuIHtPYmplY3R9IHNlc3Npb24gaW5mb3JtYXRpb25cbiAgICAgKi9cbiAgICBnZXRDdXJyZW50VXNlclNlc3Npb25JbmZvOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICB2YXIgYWRhcHRlck9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMoeyBzdWNjZXNzOiAkLm5vb3AgfSwgb3B0aW9ucyk7XG4gICAgICAgIHJldHVybiB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldFNlc3Npb24oYWRhcHRlck9wdGlvbnMpO1xuICAgIH0sXG5cbiAgICAvKlxuICAgICAqIEFkZHMgb25lIG9yIG1vcmUgZ3JvdXBzIHRvIHRoZSBjdXJyZW50IHNlc3Npb24uIFxuICAgICAqXG4gICAgICogVGhpcyBtZXRob2QgYXNzdW1lcyB0aGF0IHRoZSBwcm9qZWN0IGFuZCBncm91cCBleGlzdCBhbmQgdGhlIHVzZXIgc3BlY2lmaWVkIGluIHRoZSBzZXNzaW9uIGlzIHBhcnQgb2YgdGhpcyBwcm9qZWN0IGFuZCBncm91cC5cbiAgICAgKlxuICAgICAqIFJldHVybnMgdGhlIG5ldyBzZXNzaW9uIG9iamVjdC5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgIGF1dGhNZ3IuYWRkR3JvdXBzKHsgcHJvamVjdDogJ2hlbGxvLXdvcmxkJywgZ3JvdXBOYW1lOiAnZ3JvdXBOYW1lJywgZ3JvdXBJZDogJ2dyb3VwSWQnIH0pO1xuICAgICAqICAgICAgYXV0aE1nci5hZGRHcm91cHMoW3sgcHJvamVjdDogJ2hlbGxvLXdvcmxkJywgZ3JvdXBOYW1lOiAnZ3JvdXBOYW1lJywgZ3JvdXBJZDogJ2dyb3VwSWQnIH0sIHsgcHJvamVjdDogJ2hlbGxvLXdvcmxkJywgZ3JvdXBOYW1lOiAnLi4uJyB9XSk7XG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fGFycmF5fSBncm91cHMgKFJlcXVpcmVkKSBUaGUgZ3JvdXAgb2JqZWN0IG11c3QgY29udGFpbiB0aGUgYHByb2plY3RgICgqKlByb2plY3QgSUQqKikgYW5kIGBncm91cE5hbWVgIHByb3BlcnRpZXMuIElmIHBhc3NpbmcgYW4gYXJyYXkgb2Ygc3VjaCBvYmplY3RzLCBhbGwgb2YgdGhlIG9iamVjdHMgbXVzdCBjb250YWluICpkaWZmZXJlbnQqIGBwcm9qZWN0YCAoKipQcm9qZWN0IElEKiopIHZhbHVlczogYWx0aG91Z2ggZW5kIHVzZXJzIG1heSBiZSBsb2dnZWQgaW4gdG8gbXVsdGlwbGUgcHJvamVjdHMgYXQgb25jZSwgdGhleSBtYXkgb25seSBiZSBsb2dnZWQgaW4gdG8gb25lIGdyb3VwIHBlciBwcm9qZWN0IGF0IGEgdGltZS5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZ3JvdXAuaXNGYWMgKG9wdGlvbmFsKSBEZWZhdWx0cyB0byBgZmFsc2VgLiBTZXQgdG8gYHRydWVgIGlmIHRoZSB1c2VyIGluIHRoZSBzZXNzaW9uIHNob3VsZCBiZSBhIGZhY2lsaXRhdG9yIGluIHRoaXMgZ3JvdXAuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGdyb3VwLmdyb3VwSWQgKG9wdGlvbmFsKSBEZWZhdWx0cyB0byB1bmRlZmluZWQuIE5lZWRlZCBtb3N0bHkgZm9yIHRoZSBNZW1iZXJzIEFQSS5cbiAgICAgKiBAcmV0dXJuIHtPYmplY3R9IHNlc3Npb24gaW5mb3JtYXRpb25cbiAgICAqL1xuICAgIGFkZEdyb3VwczogZnVuY3Rpb24gKGdyb3Vwcykge1xuICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuZ2V0Q3VycmVudFVzZXJTZXNzaW9uSW5mbygpO1xuICAgICAgICB2YXIgaXNBcnJheSA9IEFycmF5LmlzQXJyYXkoZ3JvdXBzKTtcbiAgICAgICAgZ3JvdXBzID0gaXNBcnJheSA/IGdyb3VwcyA6IFtncm91cHNdO1xuXG4gICAgICAgICQuZWFjaChncm91cHMsIGZ1bmN0aW9uIChpbmRleCwgZ3JvdXApIHtcbiAgICAgICAgICAgIHZhciBleHRlbmRlZEdyb3VwID0gJC5leHRlbmQoe30sIHsgaXNGYWM6IGZhbHNlIH0sIGdyb3VwKTtcbiAgICAgICAgICAgIHZhciBwcm9qZWN0ID0gZXh0ZW5kZWRHcm91cC5wcm9qZWN0O1xuICAgICAgICAgICAgdmFyIHZhbGlkUHJvcHMgPSBbJ2dyb3VwTmFtZScsICdncm91cElkJywgJ2lzRmFjJ107XG4gICAgICAgICAgICBpZiAoIXByb2plY3QgfHwgIWV4dGVuZGVkR3JvdXAuZ3JvdXBOYW1lKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyBwcm9qZWN0IG9yIGdyb3VwTmFtZSBzcGVjaWZpZWQuJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBmaWx0ZXIgb2JqZWN0XG4gICAgICAgICAgICBleHRlbmRlZEdyb3VwID0gX3BpY2soZXh0ZW5kZWRHcm91cCwgdmFsaWRQcm9wcyk7XG4gICAgICAgICAgICBzZXNzaW9uLmdyb3Vwc1twcm9qZWN0XSA9IGV4dGVuZGVkR3JvdXA7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLnNlc3Npb25NYW5hZ2VyLnNhdmVTZXNzaW9uKHNlc3Npb24pO1xuICAgICAgICByZXR1cm4gc2Vzc2lvbjtcbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBBdXRoTWFuYWdlcjtcbiIsIid1c2Ugc3RyaWN0JztcblxuLyoqXG4gKiAjIyBDaGFubmVsIE1hbmFnZXJcbiAqXG4gKiBUaGVyZSBhcmUgdHdvIG1haW4gdXNlIGNhc2VzIGZvciB0aGUgY2hhbm5lbDogZXZlbnQgbm90aWZpY2F0aW9ucyBhbmQgY2hhdCBtZXNzYWdlcy5cbiAqXG4gKiBUaGUgQ2hhbm5lbCBNYW5hZ2VyIGlzIGEgd3JhcHBlciBhcm91bmQgdGhlIGRlZmF1bHQgW2NvbWV0ZCBKYXZhU2NyaXB0IGxpYnJhcnldKGh0dHA6Ly9kb2NzLmNvbWV0ZC5vcmcvMi9yZWZlcmVuY2UvamF2YXNjcmlwdC5odG1sKSwgYCQuY29tZXRkYC4gSXQgcHJvdmlkZXMgYSBmZXcgbmljZSBmZWF0dXJlcyB0aGF0IGAkLmNvbWV0ZGAgZG9lc24ndCwgaW5jbHVkaW5nOlxuICpcbiAqICogQXV0b21hdGljIHJlLXN1YnNjcmlwdGlvbiB0byBjaGFubmVscyBpZiB5b3UgbG9zZSB5b3VyIGNvbm5lY3Rpb25cbiAqICogT25saW5lIC8gT2ZmbGluZSBub3RpZmljYXRpb25zXG4gKiAqICdFdmVudHMnIGZvciBjb21ldGQgbm90aWZpY2F0aW9ucyAoaW5zdGVhZCBvZiBoYXZpbmcgdG8gbGlzdGVuIG9uIHNwZWNpZmljIG1ldGEgY2hhbm5lbHMpXG4gKlxuICogV2hpbGUgeW91IGNhbiB3b3JrIGRpcmVjdGx5IHdpdGggdGhlIENoYW5uZWwgTWFuYWdlciB0aHJvdWdoIE5vZGUuanMgKGZvciBleGFtcGxlLCBgcmVxdWlyZSgnbWFuYWdlci9jaGFubmVsLW1hbmFnZXInKWApIC0tIG9yIGV2ZW4gd29yayBkaXJlY3RseSB3aXRoIGAkLmNvbWV0ZGAgYW5kIEVwaWNlbnRlcidzIHVuZGVybHlpbmcgW1B1c2ggQ2hhbm5lbCBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9tdWx0aXBsYXllci9jaGFubmVsLykgLS0gbW9zdCBvZnRlbiBpdCB3aWxsIGJlIGVhc2llc3QgdG8gd29yayB3aXRoIHRoZSBbRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlcl0oLi4vZXBpY2VudGVyLWNoYW5uZWwtbWFuYWdlci8pLiBUaGUgRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlciBpcyBhIHdyYXBwZXIgdGhhdCBpbnN0YW50aWF0ZXMgYSBDaGFubmVsIE1hbmFnZXIgd2l0aCBFcGljZW50ZXItc3BlY2lmaWMgZGVmYXVsdHMuXG4gKlxuICogWW91J2xsIG5lZWQgdG8gaW5jbHVkZSB0aGUgYGVwaWNlbnRlci1tdWx0aXBsYXllci1kZXBlbmRlbmNpZXMuanNgIGxpYnJhcnkgaW4gYWRkaXRpb24gdG8gdGhlIGBlcGljZW50ZXIuanNgIGxpYnJhcnkgaW4geW91ciBwcm9qZWN0IHRvIHVzZSB0aGUgQ2hhbm5lbCBNYW5hZ2VyLiAoU2VlIFtJbmNsdWRpbmcgRXBpY2VudGVyLmpzXSguLi8uLi8jaW5jbHVkZSkuKVxuICpcbiAqIFRvIHVzZSB0aGUgQ2hhbm5lbCBNYW5hZ2VyIGluIGNsaWVudC1zaWRlIEphdmFTY3JpcHQsIGluc3RhbnRpYXRlIHRoZSBbRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlcl0oLi4vZXBpY2VudGVyLWNoYW5uZWwtbWFuYWdlci8pLCBnZXQgdGhlIGNoYW5uZWwsIHRoZW4gdXNlIHRoZSBjaGFubmVsJ3MgYHN1YnNjcmliZSgpYCBhbmQgYHB1Ymxpc2goKWAgbWV0aG9kcyB0byBzdWJzY3JpYmUgdG8gdG9waWNzIG9yIHB1Ymxpc2ggZGF0YSB0byB0b3BpY3MuXG4gKlxuICogICAgICAgIHZhciBjbSA9IG5ldyBGLm1hbmFnZXIuQ2hhbm5lbE1hbmFnZXIoKTtcbiAqICAgICAgICB2YXIgY2hhbm5lbCA9IGNtLmdldENoYW5uZWwoKTtcbiAqXG4gKiAgICAgICAgY2hhbm5lbC5zdWJzY3JpYmUoJ3RvcGljJywgY2FsbGJhY2spO1xuICogICAgICAgIGNoYW5uZWwucHVibGlzaCgndG9waWMnLCB7IG15RGF0YTogMTAwIH0pO1xuICpcbiAqIFRoZSBwYXJhbWV0ZXJzIGZvciBpbnN0YW50aWF0aW5nIGEgQ2hhbm5lbCBNYW5hZ2VyIGluY2x1ZGU6XG4gKlxuICogKiBgb3B0aW9uc2AgVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGNvbmZpZ3VyZSB0aGUgQ2hhbm5lbCBNYW5hZ2VyLiBCZXNpZGVzIHRoZSBjb21tb24gb3B0aW9ucyBsaXN0ZWQgaGVyZSwgc2VlIGh0dHA6Ly9kb2NzLmNvbWV0ZC5vcmcvcmVmZXJlbmNlL2phdmFzY3JpcHQuaHRtbCBmb3Igb3RoZXIgc3VwcG9ydGVkIG9wdGlvbnMuXG4gKiAqIGBvcHRpb25zLnVybGAgVGhlIENvbWV0ZCBlbmRwb2ludCBVUkwuXG4gKiAqIGBvcHRpb25zLndlYnNvY2tldEVuYWJsZWRgIFdoZXRoZXIgd2Vic29ja2V0IHN1cHBvcnQgaXMgYWN0aXZlIChib29sZWFuKS5cbiAqICogYG9wdGlvbnMuY2hhbm5lbGAgT3RoZXIgZGVmYXVsdHMgdG8gcGFzcyBvbiB0byBpbnN0YW5jZXMgb2YgdGhlIHVuZGVybHlpbmcgQ2hhbm5lbCBTZXJ2aWNlLiBTZWUgW0NoYW5uZWwgU2VydmljZV0oLi4vY2hhbm5lbC1zZXJ2aWNlLykgZm9yIGRldGFpbHMuXG4gKlxuICovXG5cbnZhciBDaGFubmVsID0gcmVxdWlyZSgnLi4vc2VydmljZS9jaGFubmVsLXNlcnZpY2UnKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xuXG52YXIgQ2hhbm5lbE1hbmFnZXIgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgIGlmICghJC5jb21ldGQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdDb21ldGQgbGlicmFyeSBub3QgZm91bmQuIFBsZWFzZSBpbmNsdWRlIGVwaWNlbnRlci1tdWx0aXBsYXllci1kZXBlbmRlbmNpZXMuanMnKTtcbiAgICB9XG4gICAgaWYgKCFvcHRpb25zIHx8ICFvcHRpb25zLnVybCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsZWFzZSBwcm92aWRlIGFuIHVybCBmb3IgdGhlIGNvbWV0ZCBzZXJ2ZXInKTtcbiAgICB9XG5cbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgQ29tZXRkIGVuZHBvaW50IFVSTC5cbiAgICAgICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHVybDogJycsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBsb2cgbGV2ZWwgZm9yIHRoZSBjaGFubmVsIChsb2dzIHRvIGNvbnNvbGUpLlxuICAgICAgICAgKiBAdHlwZSB7c3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgbG9nTGV2ZWw6ICdpbmZvJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogV2hldGhlciB3ZWJzb2NrZXQgc3VwcG9ydCBpcyBhY3RpdmUuIERlZmF1bHRzIHRvIGB0cnVlYC5cbiAgICAgICAgICogQHR5cGUge2Jvb2xlYW59XG4gICAgICAgICAqL1xuICAgICAgICB3ZWJzb2NrZXRFbmFibGVkOiB0cnVlLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBXaGV0aGVyIHRoZSBBQ0sgZXh0ZW5zaW9uIGlzIGVuYWJsZWQuIFNlZSBodHRwczovL2RvY3MuY29tZXRkLm9yZy9jdXJyZW50L3JlZmVyZW5jZS8jX2V4dGVuc2lvbnNfYWNrbm93bGVkZ2UgZm9yIG1vcmUgaW5mby5cbiAgICAgICAgICogQHR5cGUge2Jvb2xlYW59XG4gICAgICAgICAqL1xuICAgICAgICBhY2tFbmFibGVkOiB0cnVlLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBJZiBmYWxzZSBlYWNoIGluc3RhbmNlIG9mIENoYW5uZWwgd2lsbCBoYXZlIGEgc2VwYXJhdGUgY29tZXRkIGNvbm5lY3Rpb24gdG8gc2VydmVyLCB3aGljaCBjb3VsZCBiZSBub2lzeS4gU2V0IHRvIHRydWUgdG8gcmUtdXNlIHRoZSBzYW1lIGNvbm5lY3Rpb24gYWNyb3NzIGluc3RhbmNlcy5cbiAgICAgICAgICogQHR5cGUge2Jvb2xlYW59XG4gICAgICAgICAqL1xuICAgICAgICBzaGFyZUNvbm5lY3Rpb246IGZhbHNlLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBPdGhlciBkZWZhdWx0cyB0byBwYXNzIG9uIHRvIGluc3RhbmNlcyBvZiB0aGUgdW5kZXJseWluZyBbQ2hhbm5lbCBTZXJ2aWNlXSguLi9jaGFubmVsLXNlcnZpY2UvKSwgd2hpY2ggYXJlIGNyZWF0ZWQgdGhyb3VnaCBgZ2V0Q2hhbm5lbCgpYC5cbiAgICAgICAgICogQHR5cGUge29iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIGNoYW5uZWw6IHtcblxuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBPcHRpb25zIHRvIHBhc3MgdG8gdGhlIGNoYW5uZWwgaGFuZHNoYWtlLlxuICAgICAgICAgKlxuICAgICAgICAgKiBGb3IgZXhhbXBsZSwgdGhlIFtFcGljZW50ZXIgQ2hhbm5lbCBNYW5hZ2VyXSguLi9lcGljZW50ZXItY2hhbm5lbC1tYW5hZ2VyLykgcGFzc2VzIGBleHRgIGFuZCBhdXRob3JpemF0aW9uIGluZm9ybWF0aW9uLiBNb3JlIGluZm9ybWF0aW9uIG9uIHBvc3NpYmxlIG9wdGlvbnMgaXMgaW4gdGhlIGRldGFpbHMgb2YgdGhlIHVuZGVybHlpbmcgW1B1c2ggQ2hhbm5lbCBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9tdWx0aXBsYXllci9jaGFubmVsLykuXG4gICAgICAgICAqXG4gICAgICAgICAqIEB0eXBlIHtvYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICBoYW5kc2hha2U6IHVuZGVmaW5lZFxuICAgIH07XG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHZhciBkZWZhdWx0Q29tZXRPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKGRlZmF1bHRzLCBvcHRpb25zKTtcbiAgICB0aGlzLmN1cnJlbnRTdWJzY3JpcHRpb25zID0gW107XG4gICAgdGhpcy5vcHRpb25zID0gZGVmYXVsdENvbWV0T3B0aW9ucztcblxuICAgIGlmIChkZWZhdWx0Q29tZXRPcHRpb25zLnNoYXJlQ29ubmVjdGlvbiAmJiBDaGFubmVsTWFuYWdlci5wcm90b3R5cGUuX2NvbWV0ZCkge1xuICAgICAgICB0aGlzLmNvbWV0ZCA9IENoYW5uZWxNYW5hZ2VyLnByb3RvdHlwZS5fY29tZXRkO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgdmFyIGNvbWV0ZCA9IG5ldyAkLkNvbWV0RCgpO1xuICAgIENoYW5uZWxNYW5hZ2VyLnByb3RvdHlwZS5fY29tZXRkID0gY29tZXRkO1xuXG4gICAgY29tZXRkLndlYnNvY2tldEVuYWJsZWQgPSBkZWZhdWx0Q29tZXRPcHRpb25zLndlYnNvY2tldEVuYWJsZWQ7XG4gICAgY29tZXRkLmFja0VuYWJsZWQgPSBkZWZhdWx0Q29tZXRPcHRpb25zLmFja0VuYWJsZWQ7XG5cbiAgICB0aGlzLmlzQ29ubmVjdGVkID0gZmFsc2U7XG4gICAgdmFyIGNvbm5lY3Rpb25Ccm9rZW4gPSBmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICAkKHRoaXMpLnRyaWdnZXIoJ2Rpc2Nvbm5lY3QnLCBtZXNzYWdlKTtcbiAgICB9O1xuICAgIHZhciBjb25uZWN0aW9uU3VjY2VlZGVkID0gZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgJCh0aGlzKS50cmlnZ2VyKCdjb25uZWN0JywgbWVzc2FnZSk7XG4gICAgfTtcbiAgICB2YXIgbWUgPSB0aGlzO1xuXG4gICAgY29tZXRkLmNvbmZpZ3VyZShkZWZhdWx0Q29tZXRPcHRpb25zKTtcblxuICAgIGNvbWV0ZC5hZGRMaXN0ZW5lcignL21ldGEvY29ubmVjdCcsIGZ1bmN0aW9uIChtZXNzYWdlKSB7XG4gICAgICAgIHZhciB3YXNDb25uZWN0ZWQgPSB0aGlzLmlzQ29ubmVjdGVkO1xuICAgICAgICB0aGlzLmlzQ29ubmVjdGVkID0gKG1lc3NhZ2Uuc3VjY2Vzc2Z1bCA9PT0gdHJ1ZSk7XG4gICAgICAgIGlmICghd2FzQ29ubmVjdGVkICYmIHRoaXMuaXNDb25uZWN0ZWQpIHsgLy9Db25uZWN0aW5nIGZvciB0aGUgZmlyc3QgdGltZVxuICAgICAgICAgICAgY29ubmVjdGlvblN1Y2NlZWRlZC5jYWxsKHRoaXMsIG1lc3NhZ2UpO1xuICAgICAgICB9IGVsc2UgaWYgKHdhc0Nvbm5lY3RlZCAmJiAhdGhpcy5pc0Nvbm5lY3RlZCkgeyAvL09ubHkgdGhyb3cgZGlzY29ubmVjdGVkIG1lc3NhZ2UgZnJvIHRoZSBmaXJzdCBkaXNjb25uZWN0LCBub3Qgb25jZSBwZXIgdHJ5XG4gICAgICAgICAgICBjb25uZWN0aW9uQnJva2VuLmNhbGwodGhpcywgbWVzc2FnZSk7XG4gICAgICAgIH1cbiAgICB9LmJpbmQodGhpcykpO1xuXG4gICAgY29tZXRkLmFkZExpc3RlbmVyKCcvbWV0YS9kaXNjb25uZWN0JywgY29ubmVjdGlvbkJyb2tlbik7XG5cbiAgICBjb21ldGQuYWRkTGlzdGVuZXIoJy9tZXRhL2hhbmRzaGFrZScsIGZ1bmN0aW9uIChtZXNzYWdlKSB7XG4gICAgICAgIGlmIChtZXNzYWdlLnN1Y2Nlc3NmdWwpIHtcbiAgICAgICAgICAgIC8vaHR0cDovL2RvY3MuY29tZXRkLm9yZy9yZWZlcmVuY2UvamF2YXNjcmlwdF9zdWJzY3JpYmUuaHRtbCNqYXZhc2NyaXB0X3N1YnNjcmliZV9tZXRhX2NoYW5uZWxzXG4gICAgICAgICAgICAvLyBeIFwiZHluYW1pYyBzdWJzY3JpcHRpb25zIGFyZSBjbGVhcmVkIChsaWtlIGFueSBvdGhlciBzdWJzY3JpcHRpb24pIGFuZCB0aGUgYXBwbGljYXRpb24gbmVlZHMgdG8gZmlndXJlIG91dCB3aGljaCBkeW5hbWljIHN1YnNjcmlwdGlvbiBtdXN0IGJlIHBlcmZvcm1lZCBhZ2FpblwiXG4gICAgICAgICAgICBjb21ldGQuYmF0Y2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICQobWUuY3VycmVudFN1YnNjcmlwdGlvbnMpLmVhY2goZnVuY3Rpb24gKGluZGV4LCBzdWJzKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbWV0ZC5yZXN1YnNjcmliZShzdWJzKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICAvL090aGVyIGludGVyZXN0aW5nIGV2ZW50cyBmb3IgcmVmZXJlbmNlXG4gICAgY29tZXRkLmFkZExpc3RlbmVyKCcvbWV0YS9zdWJzY3JpYmUnLCBmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICAkKG1lKS50cmlnZ2VyKCdzdWJzY3JpYmUnLCBtZXNzYWdlKTtcbiAgICB9KTtcbiAgICBjb21ldGQuYWRkTGlzdGVuZXIoJy9tZXRhL3Vuc3Vic2NyaWJlJywgZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgJChtZSkudHJpZ2dlcigndW5zdWJzY3JpYmUnLCBtZXNzYWdlKTtcbiAgICB9KTtcbiAgICBjb21ldGQuYWRkTGlzdGVuZXIoJy9tZXRhL3B1Ymxpc2gnLCBmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICAkKG1lKS50cmlnZ2VyKCdwdWJsaXNoJywgbWVzc2FnZSk7XG4gICAgfSk7XG4gICAgY29tZXRkLmFkZExpc3RlbmVyKCcvbWV0YS91bnN1Y2Nlc3NmdWwnLCBmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICAkKG1lKS50cmlnZ2VyKCdlcnJvcicsIG1lc3NhZ2UpO1xuICAgIH0pO1xuXG4gICAgY29tZXRkLmhhbmRzaGFrZShkZWZhdWx0Q29tZXRPcHRpb25zLmhhbmRzaGFrZSk7XG5cbiAgICB0aGlzLmNvbWV0ZCA9IGNvbWV0ZDtcbn07XG5cblxuQ2hhbm5lbE1hbmFnZXIucHJvdG90eXBlID0gJC5leHRlbmQoQ2hhbm5lbE1hbmFnZXIucHJvdG90eXBlLCB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuZCByZXR1cm5zIGEgY2hhbm5lbCwgdGhhdCBpcywgYW4gaW5zdGFuY2Ugb2YgYSBbQ2hhbm5lbCBTZXJ2aWNlXSguLi9jaGFubmVsLXNlcnZpY2UvKS5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgIHZhciBjbSA9IG5ldyBGLm1hbmFnZXIuQ2hhbm5lbE1hbmFnZXIoKTtcbiAgICAgKiAgICAgIHZhciBjaGFubmVsID0gY20uZ2V0Q2hhbm5lbCgpO1xuICAgICAqXG4gICAgICogICAgICBjaGFubmVsLnN1YnNjcmliZSgndG9waWMnLCBjYWxsYmFjayk7XG4gICAgICogICAgICBjaGFubmVsLnB1Ymxpc2goJ3RvcGljJywgeyBteURhdGE6IDEwMCB9KTtcbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICogQHBhcmFtIHtPYmplY3R8U3RyaW5nfSBvcHRpb25zIChPcHRpb25hbCkgSWYgc3RyaW5nLCBhc3N1bWVkIHRvIGJlIHRoZSBiYXNlIGNoYW5uZWwgdXJsLiBJZiBvYmplY3QsIGFzc3VtZWQgdG8gYmUgY29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgY29uc3RydWN0b3IuXG4gICAgICogQHJldHVybiB7Q2hhbm5lbH0gQ2hhbm5lbCBpbnN0YW5jZVxuICAgICAqL1xuICAgIGdldENoYW5uZWw6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIC8vSWYgeW91IGp1c3Qgd2FudCB0byBwYXNzIGluIGEgc3RyaW5nXG4gICAgICAgIGlmIChvcHRpb25zICYmICEkLmlzUGxhaW5PYmplY3Qob3B0aW9ucykpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSB7XG4gICAgICAgICAgICAgICAgYmFzZTogb3B0aW9uc1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgICAgICB0cmFuc3BvcnQ6IHRoaXMuY29tZXRkXG4gICAgICAgIH07XG4gICAgICAgIHZhciBjaGFubmVsID0gbmV3IENoYW5uZWwoJC5leHRlbmQodHJ1ZSwge30sIHRoaXMub3B0aW9ucy5jaGFubmVsLCBkZWZhdWx0cywgb3B0aW9ucykpO1xuXG5cbiAgICAgICAgLy9XcmFwIHN1YnMgYW5kIHVuc3VicyBzbyB3ZSBjYW4gdXNlIGl0IHRvIHJlLWF0dGFjaCBoYW5kbGVycyBhZnRlciBiZWluZyBkaXNjb25uZWN0ZWRcbiAgICAgICAgdmFyIHN1YnMgPSBjaGFubmVsLnN1YnNjcmliZTtcbiAgICAgICAgY2hhbm5lbC5zdWJzY3JpYmUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgc3ViaWQgPSBzdWJzLmFwcGx5KGNoYW5uZWwsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB0aGlzLmN1cnJlbnRTdWJzY3JpcHRpb25zID0gdGhpcy5jdXJyZW50U3Vic2NyaXB0aW9ucy5jb25jYXQoc3ViaWQpO1xuICAgICAgICAgICAgcmV0dXJuIHN1YmlkO1xuICAgICAgICB9LmJpbmQodGhpcyk7XG5cblxuICAgICAgICB2YXIgdW5zdWJzID0gY2hhbm5lbC51bnN1YnNjcmliZTtcbiAgICAgICAgY2hhbm5lbC51bnN1YnNjcmliZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciByZW1vdmVkID0gdW5zdWJzLmFwcGx5KGNoYW5uZWwsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMuY3VycmVudFN1YnNjcmlwdGlvbnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5jdXJyZW50U3Vic2NyaXB0aW9uc1tpXS5pZCA9PT0gcmVtb3ZlZC5pZCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRTdWJzY3JpcHRpb25zLnNwbGljZShpLCAxKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gcmVtb3ZlZDtcbiAgICAgICAgfS5iaW5kKHRoaXMpO1xuXG4gICAgICAgIHJldHVybiBjaGFubmVsO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBTdGFydCBsaXN0ZW5pbmcgZm9yIGV2ZW50cyBvbiB0aGlzIGluc3RhbmNlLiBTaWduYXR1cmUgaXMgc2FtZSBhcyBmb3IgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL29uLy5cbiAgICAgKlxuICAgICAqIFN1cHBvcnRlZCBldmVudHMgYXJlOiBgY29ubmVjdGAsIGBkaXNjb25uZWN0YCwgYHN1YnNjcmliZWAsIGB1bnN1YnNjcmliZWAsIGBwdWJsaXNoYCwgYGVycm9yYC5cbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnQgVGhlIGV2ZW50IHR5cGUuIFNlZSBtb3JlIGRldGFpbCBhdCBqUXVlcnkgRXZlbnRzOiBodHRwOi8vYXBpLmpxdWVyeS5jb20vb24vLlxuICAgICAqL1xuICAgIG9uOiBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgJCh0aGlzKS5vbi5hcHBseSgkKHRoaXMpLCBhcmd1bWVudHMpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBTdG9wIGxpc3RlbmluZyBmb3IgZXZlbnRzIG9uIHRoaXMgaW5zdGFuY2UuIFNpZ25hdHVyZSBpcyBzYW1lIGFzIGZvciBqUXVlcnkgRXZlbnRzOiBodHRwOi8vYXBpLmpxdWVyeS5jb20vb2ZmLy5cbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnQgVGhlIGV2ZW50IHR5cGUuIFNlZSBtb3JlIGRldGFpbCBhdCBqUXVlcnkgRXZlbnRzOiBodHRwOi8vYXBpLmpxdWVyeS5jb20vb2ZmLy5cbiAgICAgKi9cbiAgICBvZmY6IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAkKHRoaXMpLm9mZi5hcHBseSgkKHRoaXMpLCBhcmd1bWVudHMpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBUcmlnZ2VyIGV2ZW50cyBhbmQgZXhlY3V0ZSBoYW5kbGVycy4gU2lnbmF0dXJlIGlzIHNhbWUgYXMgZm9yIGpRdWVyeSBFdmVudHM6IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS90cmlnZ2VyLy5cbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnQgVGhlIGV2ZW50IHR5cGUuIFNlZSBtb3JlIGRldGFpbCBhdCBqUXVlcnkgRXZlbnRzOiBodHRwOi8vYXBpLmpxdWVyeS5jb20vdHJpZ2dlci8uXG4gICAgICovXG4gICAgdHJpZ2dlcjogZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICQodGhpcykudHJpZ2dlci5hcHBseSgkKHRoaXMpLCBhcmd1bWVudHMpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IENoYW5uZWxNYW5hZ2VyO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG4vKipcbiAqICMjIEVwaWNlbnRlciBDaGFubmVsIE1hbmFnZXJcbiAqXG4gKiBUaGUgRXBpY2VudGVyIHBsYXRmb3JtIHByb3ZpZGVzIGEgcHVzaCBjaGFubmVsLCB3aGljaCBhbGxvd3MgeW91IHRvIHB1Ymxpc2ggYW5kIHN1YnNjcmliZSB0byBtZXNzYWdlcyB3aXRoaW4gYSBbcHJvamVjdF0oLi4vLi4vLi4vZ2xvc3NhcnkvI3Byb2plY3RzKSwgW2dyb3VwXSguLi8uLi8uLi9nbG9zc2FyeS8jZ3JvdXBzKSwgb3IgW211bHRpcGxheWVyIHdvcmxkXSguLi8uLi8uLi9nbG9zc2FyeS8jd29ybGQpLiBUaGVyZSBhcmUgdHdvIG1haW4gdXNlIGNhc2VzIGZvciB0aGUgY2hhbm5lbDogZXZlbnQgbm90aWZpY2F0aW9ucyBhbmQgY2hhdCBtZXNzYWdlcy5cbiAqXG4gKiBUaGUgRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlciBpcyBhIHdyYXBwZXIgYXJvdW5kIHRoZSAobW9yZSBnZW5lcmljKSBbQ2hhbm5lbCBNYW5hZ2VyXSguLi9jaGFubmVsLW1hbmFnZXIvKSwgdG8gaW5zdGFudGlhdGUgaXQgd2l0aCBFcGljZW50ZXItc3BlY2lmaWMgZGVmYXVsdHMuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBpbmNsdWRpbmcgYSBub3RpZmljYXRpb24gb3IgY2hhdCBmZWF0dXJlIGluIHlvdXIgcHJvamVjdCwgdXNpbmcgYW4gRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlciBpcyBwcm9iYWJseSB0aGUgZWFzaWVzdCB3YXkgdG8gZ2V0IHN0YXJ0ZWQuXG4gKlxuICogWW91J2xsIG5lZWQgdG8gaW5jbHVkZSB0aGUgYGVwaWNlbnRlci1tdWx0aXBsYXllci1kZXBlbmRlbmNpZXMuanNgIGxpYnJhcnkgaW4gYWRkaXRpb24gdG8gdGhlIGBlcGljZW50ZXIuanNgIGxpYnJhcnkgaW4geW91ciBwcm9qZWN0IHRvIHVzZSB0aGUgRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlci4gU2VlIFtJbmNsdWRpbmcgRXBpY2VudGVyLmpzXSguLi8uLi8jaW5jbHVkZSkuXG4gKlxuICogVG8gdXNlIHRoZSBFcGljZW50ZXIgQ2hhbm5lbCBNYW5hZ2VyOiBpbnN0YW50aWF0ZSBpdCwgZ2V0IHRoZSBjaGFubmVsIG9mIHRoZSBzY29wZSB5b3Ugd2FudCAoW3VzZXJdKC4uLy4uLy4uL2dsb3NzYXJ5LyN1c2VycyksIFt3b3JsZF0oLi4vLi4vLi4vZ2xvc3NhcnkvI3dvcmxkKSwgb3IgW2dyb3VwXSguLi8uLi8uLi9nbG9zc2FyeS8jZ3JvdXBzKSksIHRoZW4gdXNlIHRoZSBjaGFubmVsJ3MgYHN1YnNjcmliZSgpYCBhbmQgYHB1Ymxpc2goKWAgbWV0aG9kcyB0byBzdWJzY3JpYmUgdG8gdG9waWNzIG9yIHB1Ymxpc2ggZGF0YSB0byB0b3BpY3MuXG4gKlxuICogICAgIHZhciBjbSA9IG5ldyBGLm1hbmFnZXIuQ2hhbm5lbE1hbmFnZXIoKTtcbiAqICAgICB2YXIgZ2MgPSBjbS5nZXRHcm91cENoYW5uZWwoKTtcbiAqICAgICBnYy5zdWJzY3JpYmUoJ2Jyb2FkY2FzdHMnLCBjYWxsYmFjayk7XG4gKlxuICogRm9yIGFkZGl0aW9uYWwgYmFja2dyb3VuZCBvbiBFcGljZW50ZXIncyBwdXNoIGNoYW5uZWwsIHNlZSB0aGUgaW50cm9kdWN0b3J5IG5vdGVzIG9uIHRoZSBbUHVzaCBDaGFubmVsIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL211bHRpcGxheWVyL2NoYW5uZWwvKSBwYWdlLlxuICpcbiAqIFRoZSBwYXJhbWV0ZXJzIGZvciBpbnN0YW50aWF0aW5nIGFuIEVwaWNlbnRlciBDaGFubmVsIE1hbmFnZXIgaW5jbHVkZTpcbiAqXG4gKiAqIGBvcHRpb25zYCBPYmplY3Qgd2l0aCBkZXRhaWxzIGFib3V0IHRoZSBFcGljZW50ZXIgcHJvamVjdCBmb3IgdGhpcyBFcGljZW50ZXIgQ2hhbm5lbCBNYW5hZ2VyIGluc3RhbmNlLlxuICogKiBgb3B0aW9ucy5hY2NvdW50YCBUaGUgRXBpY2VudGVyIGFjY291bnQgaWQgKCoqVGVhbSBJRCoqIGZvciB0ZWFtIHByb2plY3RzLCAqKlVzZXIgSUQqKiBmb3IgcGVyc29uYWwgcHJvamVjdHMpLlxuICogKiBgb3B0aW9ucy5wcm9qZWN0YCBFcGljZW50ZXIgcHJvamVjdCBpZC5cbiAqICogYG9wdGlvbnMudXNlck5hbWVgIEVwaWNlbnRlciB1c2VyTmFtZSB1c2VkIGZvciBhdXRoZW50aWNhdGlvbi5cbiAqICogYG9wdGlvbnMudXNlcklkYCBFcGljZW50ZXIgdXNlciBpZCB1c2VkIGZvciBhdXRoZW50aWNhdGlvbi4gT3B0aW9uYWw7IGBvcHRpb25zLnVzZXJOYW1lYCBpcyBwcmVmZXJyZWQuXG4gKiAqIGBvcHRpb25zLnRva2VuYCBFcGljZW50ZXIgdG9rZW4gdXNlZCBmb3IgYXV0aGVudGljYXRpb24uIChZb3UgY2FuIHJldHJpZXZlIHRoaXMgdXNpbmcgYGF1dGhNYW5hZ2VyLmdldFRva2VuKClgIGZyb20gdGhlIFtBdXRob3JpemF0aW9uIE1hbmFnZXJdKC4uL2F1dGgtbWFuYWdlci8pLilcbiAqICogYG9wdGlvbnMuYWxsb3dBbGxDaGFubmVsc2AgSWYgbm90IGluY2x1ZGVkIG9yIGlmIHNldCB0byBgZmFsc2VgLCBhbGwgY2hhbm5lbCBwYXRocyBhcmUgdmFsaWRhdGVkOyBpZiB5b3VyIHByb2plY3QgcmVxdWlyZXMgW1B1c2ggQ2hhbm5lbCBBdXRob3JpemF0aW9uXSguLi8uLi8uLi91cGRhdGluZ195b3VyX3NldHRpbmdzLyksIHlvdSBzaG91bGQgdXNlIHRoaXMgb3B0aW9uLiBJZiB5b3Ugd2FudCB0byBhbGxvdyBvdGhlciBjaGFubmVsIHBhdGhzLCBzZXQgdG8gYHRydWVgOyB0aGlzIGlzIG5vdCBjb21tb24uXG4gKi9cblxudmFyIENoYW5uZWxNYW5hZ2VyID0gcmVxdWlyZSgnLi9jaGFubmVsLW1hbmFnZXInKTtcbnZhciBjbGFzc0Zyb20gPSByZXF1aXJlKCcuLi91dGlsL2luaGVyaXQnKTtcbnZhciB1cmxTZXJ2aWNlID0gcmVxdWlyZSgnLi4vc2VydmljZS91cmwtY29uZmlnLXNlcnZpY2UnKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xuXG52YXIgdmFsaWRUeXBlcyA9IHtcbiAgICBwcm9qZWN0OiB0cnVlLFxuICAgIGdyb3VwOiB0cnVlLFxuICAgIHdvcmxkOiB0cnVlLFxuICAgIHVzZXI6IHRydWUsXG4gICAgZGF0YTogdHJ1ZSxcbiAgICBnZW5lcmFsOiB0cnVlLFxuICAgIGNoYXQ6IHRydWVcbn07XG52YXIgZ2V0RnJvbVNlc3Npb25PckVycm9yID0gZnVuY3Rpb24gKHZhbHVlLCBzZXNzaW9uS2V5TmFtZSwgc2V0dGluZ3MpIHtcbiAgICBpZiAoIXZhbHVlKSB7XG4gICAgICAgIGlmIChzZXR0aW5ncyAmJiBzZXR0aW5nc1tzZXNzaW9uS2V5TmFtZV0pIHtcbiAgICAgICAgICAgIHZhbHVlID0gc2V0dGluZ3Nbc2Vzc2lvbktleU5hbWVdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKHNlc3Npb25LZXlOYW1lICsgJyBub3QgZm91bmQuIFBsZWFzZSBsb2ctaW4gYWdhaW4sIG9yIHNwZWNpZnkgJyArIHNlc3Npb25LZXlOYW1lICsgJyBleHBsaWNpdGx5Jyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHZhbHVlO1xufTtcbnZhciBfX3N1cGVyID0gQ2hhbm5lbE1hbmFnZXIucHJvdG90eXBlO1xudmFyIEVwaWNlbnRlckNoYW5uZWxNYW5hZ2VyID0gY2xhc3NGcm9tKENoYW5uZWxNYW5hZ2VyLCB7XG4gICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIob3B0aW9ucyk7XG4gICAgICAgIHZhciBkZWZhdWx0Q29tZXRPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKG9wdGlvbnMpO1xuXG4gICAgICAgIHZhciB1cmxPcHRzID0gdXJsU2VydmljZShkZWZhdWx0Q29tZXRPcHRpb25zLnNlcnZlcik7XG4gICAgICAgIGlmICghZGVmYXVsdENvbWV0T3B0aW9ucy51cmwpIHtcbiAgICAgICAgICAgIC8vRGVmYXVsdCBlcGljZW50ZXIgY29tZXRkIGVuZHBvaW50XG4gICAgICAgICAgICBkZWZhdWx0Q29tZXRPcHRpb25zLnVybCA9IHVybE9wdHMucHJvdG9jb2wgKyAnOi8vJyArIHVybE9wdHMuaG9zdCArICcvY2hhbm5lbC9zdWJzY3JpYmUnO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGRlZmF1bHRDb21ldE9wdGlvbnMuaGFuZHNoYWtlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHZhciB1c2VyTmFtZSA9IGRlZmF1bHRDb21ldE9wdGlvbnMudXNlck5hbWU7XG4gICAgICAgICAgICB2YXIgdXNlcklkID0gZGVmYXVsdENvbWV0T3B0aW9ucy51c2VySWQ7XG4gICAgICAgICAgICB2YXIgdG9rZW4gPSBkZWZhdWx0Q29tZXRPcHRpb25zLnRva2VuO1xuICAgICAgICAgICAgaWYgKCh1c2VyTmFtZSB8fCB1c2VySWQpICYmIHRva2VuKSB7XG4gICAgICAgICAgICAgICAgdmFyIHVzZXJQcm9wID0gdXNlck5hbWUgPyAndXNlck5hbWUnIDogJ3VzZXJJZCc7XG4gICAgICAgICAgICAgICAgdmFyIGV4dCA9IHtcbiAgICAgICAgICAgICAgICAgICAgYXV0aG9yaXphdGlvbjogJ0JlYXJlciAnICsgdG9rZW5cbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIGV4dFt1c2VyUHJvcF0gPSB1c2VyTmFtZSA/IHVzZXJOYW1lIDogdXNlcklkO1xuXG4gICAgICAgICAgICAgICAgZGVmYXVsdENvbWV0T3B0aW9ucy5oYW5kc2hha2UgPSB7XG4gICAgICAgICAgICAgICAgICAgIGV4dDogZXh0XG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMub3B0aW9ucyA9IGRlZmF1bHRDb21ldE9wdGlvbnM7XG4gICAgICAgIHJldHVybiBfX3N1cGVyLmNvbnN0cnVjdG9yLmNhbGwodGhpcywgZGVmYXVsdENvbWV0T3B0aW9ucyk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW5kIHJldHVybnMgYSBjaGFubmVsLCB0aGF0IGlzLCBhbiBpbnN0YW5jZSBvZiBhIFtDaGFubmVsIFNlcnZpY2VdKC4uL2NoYW5uZWwtc2VydmljZS8pLlxuICAgICAqXG4gICAgICogVGhpcyBtZXRob2QgZW5mb3JjZXMgRXBpY2VudGVyLXNwZWNpZmljIGNoYW5uZWwgbmFtaW5nOiBhbGwgY2hhbm5lbHMgcmVxdWVzdGVkIG11c3QgYmUgaW4gdGhlIGZvcm0gYC97dHlwZX0ve2FjY291bnQgaWR9L3twcm9qZWN0IGlkfS97Li4ufWAsIHdoZXJlIGB0eXBlYCBpcyBvbmUgb2YgYHJ1bmAsIGBkYXRhYCwgYHVzZXJgLCBgd29ybGRgLCBvciBgY2hhdGAuXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgICB2YXIgY20gPSBuZXcgRi5tYW5hZ2VyLkVwaWNlbnRlckNoYW5uZWxNYW5hZ2VyKCk7XG4gICAgICogICAgICB2YXIgY2hhbm5lbCA9IGNtLmdldENoYW5uZWwoJy9ncm91cC9hY21lL3N1cHBseS1jaGFpbi1nYW1lLycpO1xuICAgICAqXG4gICAgICogICAgICBjaGFubmVsLnN1YnNjcmliZSgndG9waWMnLCBjYWxsYmFjayk7XG4gICAgICogICAgICBjaGFubmVsLnB1Ymxpc2goJ3RvcGljJywgeyBteURhdGE6IDEwMCB9KTtcbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICogQHBhcmFtIHtPYmplY3R8U3RyaW5nfSBvcHRpb25zIChPcHRpb25hbCkgSWYgc3RyaW5nLCBhc3N1bWVkIHRvIGJlIHRoZSBiYXNlIGNoYW5uZWwgdXJsLiBJZiBvYmplY3QsIGFzc3VtZWQgdG8gYmUgY29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgY29uc3RydWN0b3IuXG4gICAgICogQHJldHVybiB7Q2hhbm5lbH0gQ2hhbm5lbCBpbnN0YW5jZVxuICAgICAqL1xuICAgIGdldENoYW5uZWw6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIGlmIChvcHRpb25zICYmIHR5cGVvZiBvcHRpb25zICE9PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgICAgICBiYXNlOiBvcHRpb25zXG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIHZhciBjaGFubmVsT3B0cyA9ICQuZXh0ZW5kKHt9LCB0aGlzLm9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICB2YXIgYmFzZSA9IGNoYW5uZWxPcHRzLmJhc2U7XG4gICAgICAgIGlmICghYmFzZSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyBiYXNlIHRvcGljIHdhcyBwcm92aWRlZCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFjaGFubmVsT3B0cy5hbGxvd0FsbENoYW5uZWxzKSB7XG4gICAgICAgICAgICB2YXIgYmFzZVBhcnRzID0gYmFzZS5zcGxpdCgnLycpO1xuICAgICAgICAgICAgdmFyIGNoYW5uZWxUeXBlID0gYmFzZVBhcnRzWzFdO1xuICAgICAgICAgICAgaWYgKGJhc2VQYXJ0cy5sZW5ndGggPCA0KSB7IC8vZXNsaW50LWRpc2FibGUtbGluZVxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBjaGFubmVsIGJhc2UgbmFtZSwgaXQgbXVzdCBiZSBpbiB0aGUgZm9ybSAve3R5cGV9L3thY2NvdW50IGlkfS97cHJvamVjdCBpZH0vey4uLn0nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICghdmFsaWRUeXBlc1tjaGFubmVsVHlwZV0pIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgY2hhbm5lbCB0eXBlJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIF9fc3VwZXIuZ2V0Q2hhbm5lbC5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYW5kIHJldHVybiBhIHB1Ymxpc2gvc3Vic2NyaWJlIGNoYW5uZWwgKGZyb20gdGhlIHVuZGVybHlpbmcgW0NoYW5uZWwgTWFuYWdlcl0oLi4vY2hhbm5lbC1tYW5hZ2VyLykpIGZvciB0aGUgZ2l2ZW4gW2dyb3VwXSguLi8uLi8uLi9nbG9zc2FyeS8jZ3JvdXBzKS4gVGhlIGdyb3VwIG11c3QgZXhpc3QgaW4gdGhlIGFjY291bnQgKHRlYW0pIGFuZCBwcm9qZWN0IHByb3ZpZGVkLlxuICAgICAqXG4gICAgICogVGhlcmUgYXJlIG5vIG5vdGlmaWNhdGlvbnMgZnJvbSBFcGljZW50ZXIgb24gdGhpcyBjaGFubmVsOyBhbGwgbWVzc2FnZXMgYXJlIHVzZXItb3JpZ2luYXRlZC5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgdmFyIGNtID0gbmV3IEYubWFuYWdlci5DaGFubmVsTWFuYWdlcigpO1xuICAgICAqICAgICB2YXIgZ2MgPSBjbS5nZXRHcm91cENoYW5uZWwoKTtcbiAgICAgKiAgICAgZ2Muc3Vic2NyaWJlKCdicm9hZGNhc3RzJywgY2FsbGJhY2spO1xuICAgICAqXG4gICAgICogKipSZXR1cm4gVmFsdWUqKlxuICAgICAqXG4gICAgICogKiAqQ2hhbm5lbCogUmV0dXJucyB0aGUgY2hhbm5lbCAoYW4gaW5zdGFuY2Ugb2YgdGhlIFtDaGFubmVsIFNlcnZpY2VdKC4uL2NoYW5uZWwtc2VydmljZS8pKS5cbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICpcbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IGdyb3VwTmFtZSAoT3B0aW9uYWwpIEdyb3VwIHRvIGJyb2FkY2FzdCB0by4gSWYgbm90IHByb3ZpZGVkLCBwaWNrcyB1cCBncm91cCBmcm9tIGN1cnJlbnQgc2Vzc2lvbiBpZiBlbmQgdXNlciBpcyBsb2dnZWQgaW4uXG4gICAgICogQHJldHVybiB7Q2hhbm5lbH0gQ2hhbm5lbCBpbnN0YW5jZVxuICAgICAqL1xuICAgIGdldEdyb3VwQ2hhbm5lbDogZnVuY3Rpb24gKGdyb3VwTmFtZSkge1xuICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyh0aGlzLm9wdGlvbnMpO1xuICAgICAgICBncm91cE5hbWUgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoZ3JvdXBOYW1lLCAnZ3JvdXBOYW1lJywgc2Vzc2lvbik7XG4gICAgICAgIHZhciBhY2NvdW50ID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKCcnLCAnYWNjb3VudCcsIHNlc3Npb24pO1xuICAgICAgICB2YXIgcHJvamVjdCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcignJywgJ3Byb2plY3QnLCBzZXNzaW9uKTtcblxuICAgICAgICB2YXIgYmFzZVRvcGljID0gWycvZ3JvdXAnLCBhY2NvdW50LCBwcm9qZWN0LCBncm91cE5hbWVdLmpvaW4oJy8nKTtcbiAgICAgICAgcmV0dXJuIF9fc3VwZXIuZ2V0Q2hhbm5lbC5jYWxsKHRoaXMsIHsgYmFzZTogYmFzZVRvcGljIH0pO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYW5kIHJldHVybiBhIHB1Ymxpc2gvc3Vic2NyaWJlIGNoYW5uZWwgKGZyb20gdGhlIHVuZGVybHlpbmcgW0NoYW5uZWwgTWFuYWdlcl0oLi4vY2hhbm5lbC1tYW5hZ2VyLykpIGZvciB0aGUgZ2l2ZW4gW3dvcmxkXSguLi8uLi8uLi9nbG9zc2FyeS8jd29ybGQpLlxuICAgICAqXG4gICAgICogVGhpcyBpcyB0eXBpY2FsbHkgdXNlZCB0b2dldGhlciB3aXRoIHRoZSBbV29ybGQgTWFuYWdlcl0oLi4vd29ybGQtbWFuYWdlcikuXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgIHZhciBjbSA9IG5ldyBGLm1hbmFnZXIuQ2hhbm5lbE1hbmFnZXIoKTtcbiAgICAgKiAgICAgdmFyIHdvcmxkTWFuYWdlciA9IG5ldyBGLm1hbmFnZXIuV29ybGRNYW5hZ2VyKHtcbiAgICAgKiAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgKiAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICogICAgICAgICBncm91cDogJ3RlYW0xJyxcbiAgICAgKiAgICAgICAgIHJ1bjogeyBtb2RlbDogJ21vZGVsLmVxbicgfVxuICAgICAqICAgICB9KTtcbiAgICAgKiAgICAgd29ybGRNYW5hZ2VyLmdldEN1cnJlbnRXb3JsZCgpLnRoZW4oZnVuY3Rpb24gKHdvcmxkT2JqZWN0LCB3b3JsZEFkYXB0ZXIpIHtcbiAgICAgKiAgICAgICAgIHZhciB3b3JsZENoYW5uZWwgPSBjbS5nZXRXb3JsZENoYW5uZWwod29ybGRPYmplY3QpO1xuICAgICAqICAgICAgICAgd29ybGRDaGFubmVsLnN1YnNjcmliZSgnJywgZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgKiAgICAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhKTtcbiAgICAgKiAgICAgICAgIH0pO1xuICAgICAqICAgICAgfSk7XG4gICAgICpcbiAgICAgKiAqKlJldHVybiBWYWx1ZSoqXG4gICAgICpcbiAgICAgKiAqICpDaGFubmVsKiBSZXR1cm5zIHRoZSBjaGFubmVsIChhbiBpbnN0YW5jZSBvZiB0aGUgW0NoYW5uZWwgU2VydmljZV0oLi4vY2hhbm5lbC1zZXJ2aWNlLykpLlxuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSAge1N0cmluZ3xPYmplY3R9IHdvcmxkIFRoZSB3b3JsZCBvYmplY3Qgb3IgaWQuXG4gICAgICogQHBhcmFtICB7U3RyaW5nfSBncm91cE5hbWUgKE9wdGlvbmFsKSBHcm91cCB0aGUgd29ybGQgZXhpc3RzIGluLiBJZiBub3QgcHJvdmlkZWQsIHBpY2tzIHVwIGdyb3VwIGZyb20gY3VycmVudCBzZXNzaW9uIGlmIGVuZCB1c2VyIGlzIGxvZ2dlZCBpbi5cbiAgICAgKiBAcmV0dXJuIHtDaGFubmVsfSBDaGFubmVsIGluc3RhbmNlXG4gICAgICovXG4gICAgZ2V0V29ybGRDaGFubmVsOiBmdW5jdGlvbiAod29ybGQsIGdyb3VwTmFtZSkge1xuICAgICAgICB2YXIgd29ybGRpZCA9ICgkLmlzUGxhaW5PYmplY3Qod29ybGQpICYmIHdvcmxkLmlkKSA/IHdvcmxkLmlkIDogd29ybGQ7XG4gICAgICAgIGlmICghd29ybGRpZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQbGVhc2Ugc3BlY2lmeSBhIHdvcmxkIGlkJyk7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnModGhpcy5vcHRpb25zKTtcblxuICAgICAgICBncm91cE5hbWUgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoZ3JvdXBOYW1lLCAnZ3JvdXBOYW1lJywgc2Vzc2lvbik7XG4gICAgICAgIHZhciBhY2NvdW50ID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKCcnLCAnYWNjb3VudCcsIHNlc3Npb24pO1xuICAgICAgICB2YXIgcHJvamVjdCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcignJywgJ3Byb2plY3QnLCBzZXNzaW9uKTtcblxuICAgICAgICB2YXIgYmFzZVRvcGljID0gWycvd29ybGQnLCBhY2NvdW50LCBwcm9qZWN0LCBncm91cE5hbWUsIHdvcmxkaWRdLmpvaW4oJy8nKTtcbiAgICAgICAgcmV0dXJuIF9fc3VwZXIuZ2V0Q2hhbm5lbC5jYWxsKHRoaXMsIHsgYmFzZTogYmFzZVRvcGljIH0pO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYW5kIHJldHVybiBhIHB1Ymxpc2gvc3Vic2NyaWJlIGNoYW5uZWwgKGZyb20gdGhlIHVuZGVybHlpbmcgW0NoYW5uZWwgTWFuYWdlcl0oLi4vY2hhbm5lbC1tYW5hZ2VyLykpIGZvciB0aGUgY3VycmVudCBbZW5kIHVzZXJdKC4uLy4uLy4uL2dsb3NzYXJ5LyN1c2VycykgaW4gdGhhdCB1c2VyJ3MgY3VycmVudCBbd29ybGRdKC4uLy4uLy4uL2dsb3NzYXJ5LyN3b3JsZCkuXG4gICAgICpcbiAgICAgKiBUaGlzIGlzIHR5cGljYWxseSB1c2VkIHRvZ2V0aGVyIHdpdGggdGhlIFtXb3JsZCBNYW5hZ2VyXSguLi93b3JsZC1tYW5hZ2VyKS4gTm90ZSB0aGF0IHRoaXMgY2hhbm5lbCBvbmx5IGdldHMgbm90aWZpY2F0aW9ucyBmb3Igd29ybGRzIGN1cnJlbnRseSBpbiBtZW1vcnkuIChTZWUgbW9yZSBiYWNrZ3JvdW5kIG9uIFtwZXJzaXN0ZW5jZV0oLi4vLi4vLi4vcnVuX3BlcnNpc3RlbmNlKS4pXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgIHZhciBjbSA9IG5ldyBGLm1hbmFnZXIuQ2hhbm5lbE1hbmFnZXIoKTtcbiAgICAgKiAgICAgdmFyIHdvcmxkTWFuYWdlciA9IG5ldyBGLm1hbmFnZXIuV29ybGRNYW5hZ2VyKHtcbiAgICAgKiAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgKiAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICogICAgICAgICBncm91cDogJ3RlYW0xJyxcbiAgICAgKiAgICAgICAgIHJ1bjogeyBtb2RlbDogJ21vZGVsLmVxbicgfVxuICAgICAqICAgICB9KTtcbiAgICAgKiAgICAgd29ybGRNYW5hZ2VyLmdldEN1cnJlbnRXb3JsZCgpLnRoZW4oZnVuY3Rpb24gKHdvcmxkT2JqZWN0LCB3b3JsZEFkYXB0ZXIpIHtcbiAgICAgKiAgICAgICAgIHZhciB1c2VyQ2hhbm5lbCA9IGNtLmdldFVzZXJDaGFubmVsKHdvcmxkT2JqZWN0KTtcbiAgICAgKiAgICAgICAgIHVzZXJDaGFubmVsLnN1YnNjcmliZSgnJywgZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgKiAgICAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhKTtcbiAgICAgKiAgICAgICAgIH0pO1xuICAgICAqICAgICAgfSk7XG4gICAgICpcbiAgICAgKlxuICAgICAqICoqUmV0dXJuIFZhbHVlKipcbiAgICAgKlxuICAgICAqICogKkNoYW5uZWwqIFJldHVybnMgdGhlIGNoYW5uZWwgKGFuIGluc3RhbmNlIG9mIHRoZSBbQ2hhbm5lbCBTZXJ2aWNlXSguLi9jaGFubmVsLXNlcnZpY2UvKSkuXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtICB7U3RyaW5nfE9iamVjdH0gd29ybGQgV29ybGQgb2JqZWN0IG9yIGlkLlxuICAgICAqIEBwYXJhbSAge1N0cmluZ3xPYmplY3R9IHVzZXIgKE9wdGlvbmFsKSBVc2VyIG9iamVjdCBvciBpZC4gSWYgbm90IHByb3ZpZGVkLCBwaWNrcyB1cCB1c2VyIGlkIGZyb20gY3VycmVudCBzZXNzaW9uIGlmIGVuZCB1c2VyIGlzIGxvZ2dlZCBpbi5cbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IGdyb3VwTmFtZSAoT3B0aW9uYWwpIEdyb3VwIHRoZSB3b3JsZCBleGlzdHMgaW4uIElmIG5vdCBwcm92aWRlZCwgcGlja3MgdXAgZ3JvdXAgZnJvbSBjdXJyZW50IHNlc3Npb24gaWYgZW5kIHVzZXIgaXMgbG9nZ2VkIGluLlxuICAgICAqIEByZXR1cm4ge0NoYW5uZWx9IENoYW5uZWwgaW5zdGFuY2VcbiAgICAgKi9cbiAgICBnZXRVc2VyQ2hhbm5lbDogZnVuY3Rpb24gKHdvcmxkLCB1c2VyLCBncm91cE5hbWUpIHtcbiAgICAgICAgdmFyIHdvcmxkaWQgPSAoJC5pc1BsYWluT2JqZWN0KHdvcmxkKSAmJiB3b3JsZC5pZCkgPyB3b3JsZC5pZCA6IHdvcmxkO1xuICAgICAgICBpZiAoIXdvcmxkaWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUGxlYXNlIHNwZWNpZnkgYSB3b3JsZCBpZCcpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKHRoaXMub3B0aW9ucyk7XG5cbiAgICAgICAgdmFyIHVzZXJpZCA9ICgkLmlzUGxhaW5PYmplY3QodXNlcikgJiYgdXNlci5pZCkgPyB1c2VyLmlkIDogdXNlcjtcbiAgICAgICAgdXNlcmlkID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKHVzZXJpZCwgJ3VzZXJJZCcsIHNlc3Npb24pO1xuICAgICAgICBncm91cE5hbWUgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoZ3JvdXBOYW1lLCAnZ3JvdXBOYW1lJywgc2Vzc2lvbik7XG5cbiAgICAgICAgdmFyIGFjY291bnQgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoJycsICdhY2NvdW50Jywgc2Vzc2lvbik7XG4gICAgICAgIHZhciBwcm9qZWN0ID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKCcnLCAncHJvamVjdCcsIHNlc3Npb24pO1xuXG4gICAgICAgIHZhciBiYXNlVG9waWMgPSBbJy91c2VyJywgYWNjb3VudCwgcHJvamVjdCwgZ3JvdXBOYW1lLCB3b3JsZGlkLCB1c2VyaWRdLmpvaW4oJy8nKTtcbiAgICAgICAgcmV0dXJuIF9fc3VwZXIuZ2V0Q2hhbm5lbC5jYWxsKHRoaXMsIHsgYmFzZTogYmFzZVRvcGljIH0pO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYW5kIHJldHVybiBhIHB1Ymxpc2gvc3Vic2NyaWJlIGNoYW5uZWwgKGZyb20gdGhlIHVuZGVybHlpbmcgW0NoYW5uZWwgTWFuYWdlcl0oLi4vY2hhbm5lbC1tYW5hZ2VyLykpIHRoYXQgYXV0b21hdGljYWxseSB0cmFja3MgdGhlIHByZXNlbmNlIG9mIGFuIFtlbmQgdXNlcl0oLi4vLi4vLi4vZ2xvc3NhcnkvI3VzZXJzKSwgdGhhdCBpcywgd2hldGhlciB0aGUgZW5kIHVzZXIgaXMgY3VycmVudGx5IG9ubGluZSBpbiB0aGlzIGdyb3VwIGFuZCB3b3JsZC4gTm90aWZpY2F0aW9ucyBhcmUgYXV0b21hdGljYWxseSBzZW50IHdoZW4gdGhlIGVuZCB1c2VyIGNvbWVzIG9ubGluZSwgYW5kIHdoZW4gdGhlIGVuZCB1c2VyIGdvZXMgb2ZmbGluZSAobm90IHByZXNlbnQgZm9yIG1vcmUgdGhhbiAyIG1pbnV0ZXMpLiBVc2VmdWwgaW4gbXVsdGlwbGF5ZXIgZ2FtZXMgZm9yIGxldHRpbmcgZWFjaCBlbmQgdXNlciBrbm93IHdoZXRoZXIgb3RoZXIgdXNlcnMgaW4gdGhlaXIgc2hhcmVkIHdvcmxkIGFyZSBhbHNvIG9ubGluZS5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgdmFyIGNtID0gbmV3IEYubWFuYWdlci5DaGFubmVsTWFuYWdlcigpO1xuICAgICAqICAgICB2YXIgd29ybGRNYW5hZ2VyID0gbmV3IEYubWFuYWdlci5Xb3JsZE1hbmFnZXIoe1xuICAgICAqICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAqICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgKiAgICAgICAgIG1vZGVsOiAnbW9kZWwuZXFuJ1xuICAgICAqICAgICB9KTtcbiAgICAgKiAgICAgd29ybGRNYW5hZ2VyLmdldEN1cnJlbnRXb3JsZCgpLnRoZW4oZnVuY3Rpb24gKHdvcmxkT2JqZWN0LCB3b3JsZFNlcnZpY2UpIHtcbiAgICAgKiAgICAgICAgIHZhciBwcmVzZW5jZUNoYW5uZWwgPSBjbS5nZXRQcmVzZW5jZUNoYW5uZWwod29ybGRPYmplY3QpO1xuICAgICAqICAgICAgICAgcHJlc2VuY2VDaGFubmVsLm9uKCdwcmVzZW5jZScsIGZ1bmN0aW9uIChldnQsIG5vdGlmaWNhdGlvbikge1xuICAgICAqICAgICAgICAgICAgICBjb25zb2xlLmxvZyhub3RpZmljYXRpb24ub25saW5lLCBub3RpZmljYXRpb24udXNlcklkKTtcbiAgICAgKiAgICAgICAgICB9KTtcbiAgICAgKiAgICAgIH0pO1xuICAgICAqXG4gICAgICpcbiAgICAgKiAqKlJldHVybiBWYWx1ZSoqXG4gICAgICpcbiAgICAgKiAqICpDaGFubmVsKiBSZXR1cm5zIHRoZSBjaGFubmVsIChhbiBpbnN0YW5jZSBvZiB0aGUgW0NoYW5uZWwgU2VydmljZV0oLi4vY2hhbm5lbC1zZXJ2aWNlLykpLlxuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSAge1N0cmluZ3xPYmplY3R9IHdvcmxkIFdvcmxkIG9iamVjdCBvciBpZC5cbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd8T2JqZWN0fSB1c2VyaWQgKE9wdGlvbmFsKSBVc2VyIG9iamVjdCBvciBpZC4gSWYgbm90IHByb3ZpZGVkLCBwaWNrcyB1cCB1c2VyIGlkIGZyb20gY3VycmVudCBzZXNzaW9uIGlmIGVuZCB1c2VyIGlzIGxvZ2dlZCBpbi5cbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IGdyb3VwTmFtZSAoT3B0aW9uYWwpIEdyb3VwIHRoZSB3b3JsZCBleGlzdHMgaW4uIElmIG5vdCBwcm92aWRlZCwgcGlja3MgdXAgZ3JvdXAgZnJvbSBjdXJyZW50IHNlc3Npb24gaWYgZW5kIHVzZXIgaXMgbG9nZ2VkIGluLlxuICAgICAqIEByZXR1cm4ge0NoYW5uZWx9IENoYW5uZWwgaW5zdGFuY2VcbiAgICAgKi9cbiAgICBnZXRQcmVzZW5jZUNoYW5uZWw6IGZ1bmN0aW9uICh3b3JsZCwgdXNlcmlkLCBncm91cE5hbWUpIHtcbiAgICAgICAgdmFyIHdvcmxkaWQgPSAoJC5pc1BsYWluT2JqZWN0KHdvcmxkKSAmJiB3b3JsZC5pZCkgPyB3b3JsZC5pZCA6IHdvcmxkO1xuICAgICAgICBpZiAoIXdvcmxkaWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUGxlYXNlIHNwZWNpZnkgYSB3b3JsZCBpZCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnModGhpcy5vcHRpb25zKTtcbiAgICAgICAgdXNlcmlkID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKHVzZXJpZCwgJ3VzZXJJZCcsIHNlc3Npb24pO1xuICAgICAgICBncm91cE5hbWUgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoZ3JvdXBOYW1lLCAnZ3JvdXBOYW1lJywgc2Vzc2lvbik7XG5cbiAgICAgICAgdmFyIGFjY291bnQgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoJycsICdhY2NvdW50Jywgc2Vzc2lvbik7XG4gICAgICAgIHZhciBwcm9qZWN0ID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKCcnLCAncHJvamVjdCcsIHNlc3Npb24pO1xuXG4gICAgICAgIHZhciBiYXNlVG9waWMgPSBbJy91c2VyJywgYWNjb3VudCwgcHJvamVjdCwgZ3JvdXBOYW1lLCB3b3JsZGlkXS5qb2luKCcvJyk7XG4gICAgICAgIHZhciBjaGFubmVsID0gX19zdXBlci5nZXRDaGFubmVsLmNhbGwodGhpcywgeyBiYXNlOiBiYXNlVG9waWMgfSk7XG5cbiAgICAgICAgdmFyIGxhc3RQaW5nVGltZSA9IHsgfTtcblxuICAgICAgICB2YXIgUElOR19JTlRFUlZBTCA9IDYwMDA7XG4gICAgICAgIGNoYW5uZWwuc3Vic2NyaWJlKCdpbnRlcm5hbC1waW5nLWNoYW5uZWwnLCBmdW5jdGlvbiAobm90aWZpY2F0aW9uKSB7XG4gICAgICAgICAgICB2YXIgaW5jb21pbmdVc2VySWQgPSBub3RpZmljYXRpb24uZGF0YS51c2VyO1xuICAgICAgICAgICAgaWYgKCFsYXN0UGluZ1RpbWVbaW5jb21pbmdVc2VySWRdICYmIGluY29taW5nVXNlcklkICE9PSB1c2VyaWQpIHtcbiAgICAgICAgICAgICAgICBjaGFubmVsLnRyaWdnZXIoJ3ByZXNlbmNlJywgeyB1c2VySWQ6IGluY29taW5nVXNlcklkLCBvbmxpbmU6IHRydWUgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsYXN0UGluZ1RpbWVbaW5jb21pbmdVc2VySWRdID0gKG5ldyBEYXRlKCkpLnZhbHVlT2YoKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgc2V0SW50ZXJ2YWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgY2hhbm5lbC5wdWJsaXNoKCdpbnRlcm5hbC1waW5nLWNoYW5uZWwnLCB7IHVzZXI6IHVzZXJpZCB9KTtcblxuICAgICAgICAgICAgJC5lYWNoKGxhc3RQaW5nVGltZSwgZnVuY3Rpb24gKGtleSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgICB2YXIgbm93ID0gKG5ldyBEYXRlKCkpLnZhbHVlT2YoKTtcbiAgICAgICAgICAgICAgICBpZiAodmFsdWUgJiYgdmFsdWUgKyAoUElOR19JTlRFUlZBTCAqIDIpIDwgbm93KSB7XG4gICAgICAgICAgICAgICAgICAgIGxhc3RQaW5nVGltZVtrZXldID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgY2hhbm5lbC50cmlnZ2VyKCdwcmVzZW5jZScsIHsgdXNlcklkOiBrZXksIG9ubGluZTogZmFsc2UgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sIFBJTkdfSU5URVJWQUwpO1xuXG4gICAgICAgIHJldHVybiBjaGFubmVsO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYW5kIHJldHVybiBhIHB1Ymxpc2gvc3Vic2NyaWJlIGNoYW5uZWwgKGZyb20gdGhlIHVuZGVybHlpbmcgW0NoYW5uZWwgTWFuYWdlcl0oLi4vY2hhbm5lbC1tYW5hZ2VyLykpIGZvciB0aGUgZ2l2ZW4gY29sbGVjdGlvbi4gKFRoZSBjb2xsZWN0aW9uIG5hbWUgaXMgc3BlY2lmaWVkIGluIHRoZSBgcm9vdGAgYXJndW1lbnQgd2hlbiB0aGUgW0RhdGEgU2VydmljZV0oLi4vZGF0YS1hcGktc2VydmljZS8pIGlzIGluc3RhbnRpYXRlZC4pIE11c3QgYmUgb25lIG9mIHRoZSBjb2xsZWN0aW9ucyBpbiB0aGlzIGFjY291bnQgKHRlYW0pIGFuZCBwcm9qZWN0LlxuICAgICAqXG4gICAgICogVGhlcmUgYXJlIGF1dG9tYXRpYyBub3RpZmljYXRpb25zIGZyb20gRXBpY2VudGVyIG9uIHRoaXMgY2hhbm5lbCB3aGVuIGRhdGEgaXMgY3JlYXRlZCwgdXBkYXRlZCwgb3IgZGVsZXRlZCBpbiB0aGlzIGNvbGxlY3Rpb24uIFNlZSBtb3JlIG9uIFthdXRvbWF0aWMgbWVzc2FnZXMgdG8gdGhlIGRhdGEgY2hhbm5lbF0oLi4vLi4vLi4vcmVzdF9hcGlzL211bHRpcGxheWVyL2NoYW5uZWwvI2RhdGEtbWVzc2FnZXMpLlxuICAgICAqXG4gICAgICogKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICB2YXIgY20gPSBuZXcgRi5tYW5hZ2VyLkNoYW5uZWxNYW5hZ2VyKCk7XG4gICAgICogICAgIHZhciBkYyA9IGNtLmdldERhdGFDaGFubmVsKCdzdXJ2ZXktcmVzcG9uc2VzJyk7XG4gICAgICogICAgIGRjLnN1YnNjcmliZSgnJywgZnVuY3Rpb24oZGF0YSwgbWV0YSkge1xuICAgICAqICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEpO1xuICAgICAqXG4gICAgICogICAgICAgICAgLy8gbWV0YS5kYXRlIGlzIHRpbWUgb2YgY2hhbmdlLFxuICAgICAqICAgICAgICAgIC8vIG1ldGEuc3ViVHlwZSBpcyB0aGUga2luZCBvZiBjaGFuZ2U6IG5ldywgdXBkYXRlLCBvciBkZWxldGVcbiAgICAgKiAgICAgICAgICAvLyBtZXRhLnBhdGggaXMgdGhlIGZ1bGwgcGF0aCB0byB0aGUgY2hhbmdlZCBkYXRhXG4gICAgICogICAgICAgICAgY29uc29sZS5sb2cobWV0YSk7XG4gICAgICogICAgIH0pO1xuICAgICAqXG4gICAgICogKipSZXR1cm4gVmFsdWUqKlxuICAgICAqXG4gICAgICogKiAqQ2hhbm5lbCogUmV0dXJucyB0aGUgY2hhbm5lbCAoYW4gaW5zdGFuY2Ugb2YgdGhlIFtDaGFubmVsIFNlcnZpY2VdKC4uL2NoYW5uZWwtc2VydmljZS8pKS5cbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICpcbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IGNvbGxlY3Rpb24gTmFtZSBvZiBjb2xsZWN0aW9uIHdob3NlIGF1dG9tYXRpYyBub3RpZmljYXRpb25zIHlvdSB3YW50IHRvIHJlY2VpdmUuXG4gICAgICogQHJldHVybiB7Q2hhbm5lbH0gQ2hhbm5lbCBpbnN0YW5jZVxuICAgICAqL1xuICAgIGdldERhdGFDaGFubmVsOiBmdW5jdGlvbiAoY29sbGVjdGlvbikge1xuICAgICAgICBpZiAoIWNvbGxlY3Rpb24pIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUGxlYXNlIHNwZWNpZnkgYSBjb2xsZWN0aW9uIHRvIGxpc3RlbiBvbi4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKHRoaXMub3B0aW9ucyk7XG4gICAgICAgIHZhciBhY2NvdW50ID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKCcnLCAnYWNjb3VudCcsIHNlc3Npb24pO1xuICAgICAgICB2YXIgcHJvamVjdCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcignJywgJ3Byb2plY3QnLCBzZXNzaW9uKTtcbiAgICAgICAgdmFyIGJhc2VUb3BpYyA9IFsnL2RhdGEnLCBhY2NvdW50LCBwcm9qZWN0LCBjb2xsZWN0aW9uXS5qb2luKCcvJyk7XG4gICAgICAgIHZhciBjaGFubmVsID0gX19zdXBlci5nZXRDaGFubmVsLmNhbGwodGhpcywgeyBiYXNlOiBiYXNlVG9waWMgfSk7XG5cbiAgICAgICAgLy9UT0RPOiBGaXggYWZ0ZXIgRXBpY2VudGVyIGJ1ZyBpcyByZXNvbHZlZFxuICAgICAgICB2YXIgb2xkc3VicyA9IGNoYW5uZWwuc3Vic2NyaWJlO1xuICAgICAgICBjaGFubmVsLnN1YnNjcmliZSA9IGZ1bmN0aW9uICh0b3BpYywgY2FsbGJhY2ssIGNvbnRleHQsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBjYWxsYmFja1dpdGhDbGVhbkRhdGEgPSBmdW5jdGlvbiAocGF5bG9hZCkge1xuICAgICAgICAgICAgICAgIHZhciBtZXRhID0ge1xuICAgICAgICAgICAgICAgICAgICBwYXRoOiBwYXlsb2FkLmNoYW5uZWwsXG4gICAgICAgICAgICAgICAgICAgIHN1YlR5cGU6IHBheWxvYWQuZGF0YS5zdWJUeXBlLFxuICAgICAgICAgICAgICAgICAgICBkYXRlOiBwYXlsb2FkLmRhdGEuZGF0ZVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgdmFyIGFjdHVhbERhdGEgPSBwYXlsb2FkLmRhdGEuZGF0YTtcbiAgICAgICAgICAgICAgICBpZiAoYWN0dWFsRGF0YS5kYXRhKSB7IC8vRGVsZXRlIG5vdGlmaWNhdGlvbnMgYXJlIG9uZSBkYXRhLWxldmVsIGJlaGluZCBvZiBjb3Vyc2VcbiAgICAgICAgICAgICAgICAgICAgYWN0dWFsRGF0YSA9IGFjdHVhbERhdGEuZGF0YTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBjYWxsYmFjay5jYWxsKGNvbnRleHQsIGFjdHVhbERhdGEsIG1ldGEpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJldHVybiBvbGRzdWJzLmNhbGwoY2hhbm5lbCwgdG9waWMsIGNhbGxiYWNrV2l0aENsZWFuRGF0YSwgY29udGV4dCwgb3B0aW9ucyk7XG4gICAgICAgIH07XG5cbiAgICAgICAgcmV0dXJuIGNoYW5uZWw7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzID0gRXBpY2VudGVyQ2hhbm5lbE1hbmFnZXI7XG4iLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIEVQSV9TRVNTSU9OX0tFWTogJ2VwaWNlbnRlcmpzLnNlc3Npb24nLFxuICAgIFNUUkFURUdZX1NFU1NJT05fS0VZOiAnZXBpY2VudGVyLXNjZW5hcmlvJ1xufTsiLCIvKipcbiogIyMgUnVuIE1hbmFnZXJcbipcbiogVGhlIFJ1biBNYW5hZ2VyIGdpdmVzIHlvdSBhY2Nlc3MgdG8gcnVucyBmb3IgeW91ciBwcm9qZWN0LiBUaGlzIGFsbG93cyB5b3UgdG8gcmVhZCBhbmQgdXBkYXRlIHZhcmlhYmxlcywgY2FsbCBvcGVyYXRpb25zLCBldGMuIEFkZGl0aW9uYWxseSwgdGhlIFJ1biBNYW5hZ2VyIGdpdmVzIHlvdSBjb250cm9sIG92ZXIgcnVuIGNyZWF0aW9uIGRlcGVuZGluZyBvbiBydW4gc3RhdGVzLiBTcGVjaWZpY2FsbHksIHlvdSBjYW4gc2VsZWN0IFtydW4gY3JlYXRpb24gc3RyYXRlZ2llcyAocnVsZXMpXSguLi9zdHJhdGVnaWVzLykgZm9yIHdoaWNoIHJ1bnMgZW5kIHVzZXJzIG9mIHlvdXIgcHJvamVjdCB3b3JrIHdpdGggd2hlbiB0aGV5IGxvZyBpbiB0byB5b3VyIHByb2plY3QuXG4qXG4qIFRoZXJlIGFyZSBtYW55IHdheXMgdG8gY3JlYXRlIG5ldyBydW5zLCBpbmNsdWRpbmcgdGhlIEVwaWNlbnRlci5qcyBbUnVuIFNlcnZpY2VdKC4uL3J1bi1hcGktc2VydmljZS8pLCB0aGUgUkVTRlRmdWwgW1J1biBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9hZ2dyZWdhdGVfcnVuX2FwaSkgYW5kIHRoZSBbTW9kZWwgUnVuIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL290aGVyX2FwaXMvbW9kZWxfYXBpcy9ydW4vKS4gSG93ZXZlciwgZm9yIHNvbWUgcHJvamVjdHMgaXQgbWFrZXMgbW9yZSBzZW5zZSB0byBwaWNrIHVwIHdoZXJlIHRoZSB1c2VyIGxlZnQgb2ZmLCB1c2luZyBhbiBleGlzdGluZyBydW4uIEFuZCBpbiBzb21lIHByb2plY3RzLCB3aGV0aGVyIHRvIGNyZWF0ZSBhIG5ldyBydW4gb3IgdXNlIGFuIGV4aXN0aW5nIG9uZSBpcyBjb25kaXRpb25hbCwgZm9yIGV4YW1wbGUgYmFzZWQgb24gY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBleGlzdGluZyBydW4gb3IgeW91ciBvd24ga25vd2xlZGdlIGFib3V0IHRoZSBtb2RlbC4gVGhlIFJ1biBNYW5hZ2VyIHByb3ZpZGVzIHRoaXMgbGV2ZWwgb2YgY29udHJvbDogeW91ciBjYWxsIHRvIGBnZXRSdW4oKWAsIHJhdGhlciB0aGFuIGFsd2F5cyByZXR1cm5pbmcgYSBuZXcgcnVuLCByZXR1cm5zIGEgcnVuIGJhc2VkIG9uIHRoZSBzdHJhdGVneSB5b3UndmUgc3BlY2lmaWVkLiAoTm90ZSB0aGF0IG1hbnkgb2YgdGhlIEVwaWNlbnRlciBzYW1wbGUgcHJvamVjdHMgdXNlIGEgUnVuIFNlcnZpY2UgZGlyZWN0bHksIGJlY2F1c2UgZ2VuZXJhbGx5IHRoZSBzYW1wbGUgcHJvamVjdHMgYXJlIHBsYXllZCBpbiBvbmUgZW5kIHVzZXIgc2Vzc2lvbiBhbmQgZG9uJ3QgY2FyZSBhYm91dCBydW4gc3RhdGVzIG9yIHJ1biBzdHJhdGVnaWVzLilcbipcbipcbiogIyMjIFVzaW5nIHRoZSBSdW4gTWFuYWdlciB0byBjcmVhdGUgYW5kIGFjY2VzcyBydW5zXG4qXG4qIFRvIHVzZSB0aGUgUnVuIE1hbmFnZXIsIGluc3RhbnRpYXRlIGl0IGJ5IHBhc3NpbmcgaW46XG4qXG4qICAgKiBgcnVuYDogKHJlcXVpcmVkKSBSdW4gb2JqZWN0LiBNdXN0IGNvbnRhaW46XG4qICAgICAgICogYGFjY291bnRgOiBFcGljZW50ZXIgYWNjb3VudCBpZCAoKipUZWFtIElEKiogZm9yIHRlYW0gcHJvamVjdHMsICoqVXNlciBJRCoqIGZvciBwZXJzb25hbCBwcm9qZWN0cykuXG4qICAgICAgICogYHByb2plY3RgOiBFcGljZW50ZXIgcHJvamVjdCBpZC5cbiogICAgICAgKiBgbW9kZWxgOiBUaGUgbmFtZSBvZiB5b3VyIHByaW1hcnkgbW9kZWwgZmlsZS4gKFNlZSBtb3JlIG9uIFtXcml0aW5nIHlvdXIgTW9kZWxdKC4uLy4uLy4uL3dyaXRpbmdfeW91cl9tb2RlbC8pLilcbiogICAgICAgKiBgc2NvcGVgOiAob3B0aW9uYWwpIFNjb3BlIG9iamVjdCBmb3IgdGhlIHJ1biwgZm9yIGV4YW1wbGUgYHNjb3BlLmdyb3VwYCB3aXRoIHZhbHVlIG9mIHRoZSBuYW1lIG9mIHRoZSBncm91cC5cbiogICAgICAgKiBgc2VydmVyYDogKG9wdGlvbmFsKSBBbiBvYmplY3Qgd2l0aCBvbmUgZmllbGQsIGBob3N0YC4gVGhlIHZhbHVlIG9mIGBob3N0YCBpcyB0aGUgc3RyaW5nIGBhcGkuZm9yaW8uY29tYCwgdGhlIFVSSSBvZiB0aGUgRm9yaW8gc2VydmVyLiBUaGlzIGlzIGF1dG9tYXRpY2FsbHkgc2V0LCBidXQgeW91IGNhbiBwYXNzIGl0IGV4cGxpY2l0bHkgaWYgZGVzaXJlZC4gSXQgaXMgbW9zdCBjb21tb25seSB1c2VkIGZvciBjbGFyaXR5IHdoZW4geW91IGFyZSBbaG9zdGluZyBhbiBFcGljZW50ZXIgcHJvamVjdCBvbiB5b3VyIG93biBzZXJ2ZXJdKC4uLy4uLy4uL2hvd190by9zZWxmX2hvc3RpbmcvKS5cbiogICAgICAgKiBgZmlsZXNgOiAob3B0aW9uYWwpIElmIGFuZCBvbmx5IGlmIHlvdSBhcmUgdXNpbmcgYSBWZW5zaW0gbW9kZWwgYW5kIHlvdSBoYXZlIGFkZGl0aW9uYWwgZGF0YSB0byBwYXNzIGluIHRvIHlvdXIgbW9kZWwsIHlvdSBjYW4gcGFzcyBhIGBmaWxlc2Agb2JqZWN0IHdpdGggdGhlIG5hbWVzIG9mIHRoZSBmaWxlcywgZm9yIGV4YW1wbGU6IGBcImZpbGVzXCI6IHtcImRhdGFcIjogXCJteUV4dHJhRGF0YS54bHNcIn1gLiAoTm90ZSB0aGF0IHlvdSdsbCBhbHNvIG5lZWQgdG8gYWRkIHRoaXMgc2FtZSBmaWxlcyBvYmplY3QgdG8geW91ciBWZW5zaW0gW2NvbmZpZ3VyYXRpb24gZmlsZV0oLi4vLi4vLi4vbW9kZWxfY29kZS92ZW5zaW0vKS4pIFNlZSB0aGUgW3VuZGVybHlpbmcgTW9kZWwgUnVuIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL290aGVyX2FwaXMvbW9kZWxfYXBpcy9ydW4vI3Bvc3QtY3JlYXRpbmctYS1uZXctcnVuLWZvci10aGlzLXByb2plY3QpIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uLlxuKlxuKiAgICogYHN0cmF0ZWd5YDogKG9wdGlvbmFsKSBSdW4gY3JlYXRpb24gc3RyYXRlZ3kgZm9yIHdoZW4gdG8gY3JlYXRlIGEgbmV3IHJ1biBhbmQgd2hlbiB0byByZXVzZSBhbiBlbmQgdXNlcidzIGV4aXN0aW5nIHJ1bi4gU2VlIFtSdW4gTWFuYWdlciBTdHJhdGVnaWVzXSguLi9zdHJhdGVnaWVzLykgZm9yIGRldGFpbHMuIERlZmF1bHRzIHRvIGBuZXctaWYtaW5pdGlhbGl6ZWRgLlxuKlxuKiAgICogYHNlc3Npb25LZXlgOiAob3B0aW9uYWwpIE5hbWUgb2YgYnJvd3NlciBjb29raWUgaW4gd2hpY2ggdG8gc3RvcmUgcnVuIGluZm9ybWF0aW9uLCBpbmNsdWRpbmcgcnVuIGlkLiBNYW55IGNvbmRpdGlvbmFsIHN0cmF0ZWdpZXMsIGluY2x1ZGluZyB0aGUgcHJvdmlkZWQgc3RyYXRlZ2llcywgcmVseSBvbiB0aGlzIGJyb3dzZXIgY29va2llIHRvIHN0b3JlIHRoZSBydW4gaWQgYW5kIGhlbHAgbWFrZSB0aGUgZGVjaXNpb24gb2Ygd2hldGhlciB0byBjcmVhdGUgYSBuZXcgcnVuIG9yIHVzZSBhbiBleGlzdGluZyBvbmUuIFRoZSBuYW1lIG9mIHRoaXMgY29va2llIGRlZmF1bHRzIHRvIGBlcGljZW50ZXItc2NlbmFyaW9gIGFuZCBjYW4gYmUgc2V0IHdpdGggdGhlIGBzZXNzaW9uS2V5YCBwYXJhbWV0ZXIuXG4qXG4qXG4qIEFmdGVyIGluc3RhbnRpYXRpbmcgYSBSdW4gTWFuYWdlciwgbWFrZSBhIGNhbGwgdG8gYGdldFJ1bigpYCB3aGVuZXZlciB5b3UgbmVlZCB0byBhY2Nlc3MgYSBydW4gZm9yIHRoaXMgZW5kIHVzZXIuIFRoZSBgUnVuTWFuYWdlci5ydW5gIGNvbnRhaW5zIHRoZSBpbnN0YW50aWF0ZWQgW1J1biBTZXJ2aWNlXSguLi9ydW4tYXBpLXNlcnZpY2UvKS4gVGhlIFJ1biBTZXJ2aWNlIGFsbG93cyB5b3UgdG8gYWNjZXNzIHZhcmlhYmxlcywgY2FsbCBvcGVyYXRpb25zLCBldGMuXG4qXG4qICoqRXhhbXBsZSoqXG4qXG4qICAgICAgIHZhciBybSA9IG5ldyBGLm1hbmFnZXIuUnVuTWFuYWdlcih7XG4qICAgICAgICAgICBydW46IHtcbiogICAgICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4qICAgICAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiogICAgICAgICAgICAgICBtb2RlbDogJ3N1cHBseS1jaGFpbi1tb2RlbC5qbCcsXG4qICAgICAgICAgICAgICAgc2VydmVyOiB7IGhvc3Q6ICdhcGkuZm9yaW8uY29tJyB9XG4qICAgICAgICAgICB9LFxuKiAgICAgICAgICAgc3RyYXRlZ3k6ICdhbHdheXMtbmV3JyxcbiogICAgICAgICAgIHNlc3Npb25LZXk6ICdlcGljZW50ZXItc2Vzc2lvbidcbiogICAgICAgfSk7XG4qICAgICAgIHJtLmdldFJ1bigpXG4qICAgICAgICAgICAudGhlbihmdW5jdGlvbihydW4pIHtcbiogICAgICAgICAgICAgICAvLyB0aGUgcmV0dXJuIHZhbHVlIG9mIGdldFJ1bigpIGlzIGEgcnVuIG9iamVjdFxuKiAgICAgICAgICAgICAgIHZhciB0aGlzUnVuSWQgPSBydW4uaWQ7XG4qICAgICAgICAgICAgICAgLy8gdGhlIFJ1bk1hbmFnZXIucnVuIGFsc28gY29udGFpbnMgdGhlIGluc3RhbnRpYXRlZCBSdW4gU2VydmljZSxcbiogICAgICAgICAgICAgICAvLyBzbyBhbnkgUnVuIFNlcnZpY2UgbWV0aG9kIGlzIHZhbGlkIGhlcmVcbiogICAgICAgICAgICAgICBybS5ydW4uZG8oJ3J1bk1vZGVsJyk7XG4qICAgICAgIH0pXG4qXG4qL1xuXG4ndXNlIHN0cmljdCc7XG52YXIgc3RyYXRlZ2llc01hcCA9IHJlcXVpcmUoJy4vcnVuLXN0cmF0ZWdpZXMvc3RyYXRlZ2llcy1tYXAnKTtcbnZhciBzcGVjaWFsT3BlcmF0aW9ucyA9IHJlcXVpcmUoJy4vc3BlY2lhbC1vcGVyYXRpb25zJyk7XG52YXIgUnVuU2VydmljZSA9IHJlcXVpcmUoJy4uL3NlcnZpY2UvcnVuLWFwaS1zZXJ2aWNlJyk7XG5cblxuZnVuY3Rpb24gcGF0Y2hSdW5TZXJ2aWNlKHNlcnZpY2UsIG1hbmFnZXIpIHtcbiAgICBpZiAoc2VydmljZS5wYXRjaGVkKSB7XG4gICAgICAgIHJldHVybiBzZXJ2aWNlO1xuICAgIH1cblxuICAgIHZhciBvcmlnID0gc2VydmljZS5kbztcbiAgICBzZXJ2aWNlLmRvID0gZnVuY3Rpb24gKG9wZXJhdGlvbiwgcGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgIHZhciByZXNlcnZlZE9wcyA9IE9iamVjdC5rZXlzKHNwZWNpYWxPcGVyYXRpb25zKTtcbiAgICAgICAgaWYgKHJlc2VydmVkT3BzLmluZGV4T2Yob3BlcmF0aW9uKSA9PT0gLTEpIHtcbiAgICAgICAgICAgIHJldHVybiBvcmlnLmFwcGx5KHNlcnZpY2UsIGFyZ3VtZW50cyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gc3BlY2lhbE9wZXJhdGlvbnNbb3BlcmF0aW9uXS5jYWxsKHNlcnZpY2UsIHBhcmFtcywgb3B0aW9ucywgbWFuYWdlcik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgc2VydmljZS5wYXRjaGVkID0gdHJ1ZTtcblxuICAgIHJldHVybiBzZXJ2aWNlO1xufVxuXG5cbnZhciBkZWZhdWx0cyA9IHtcbiAgICAvKipcbiAgICAgKiBSdW4gY3JlYXRpb24gc3RyYXRlZ3kgZm9yIHdoZW4gdG8gY3JlYXRlIGEgbmV3IHJ1biBhbmQgd2hlbiB0byByZXVzZSBhbiBlbmQgdXNlcidzIGV4aXN0aW5nIHJ1bi4gU2VlIFtSdW4gTWFuYWdlciBTdHJhdGVnaWVzXSguLi9zdHJhdGVnaWVzLykgZm9yIGRldGFpbHMuIERlZmF1bHRzIHRvIGBuZXctaWYtaW5pdGlhbGl6ZWRgLlxuICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICovXG5cbiAgICBzdHJhdGVneTogJ25ldy1pZi1pbml0aWFsaXplZCdcbn07XG5cbmZ1bmN0aW9uIFJ1bk1hbmFnZXIob3B0aW9ucykge1xuICAgIHRoaXMub3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBkZWZhdWx0cywgb3B0aW9ucyk7XG5cbiAgICBpZiAodGhpcy5vcHRpb25zLnJ1biBpbnN0YW5jZW9mIFJ1blNlcnZpY2UpIHtcbiAgICAgICAgdGhpcy5ydW4gPSB0aGlzLm9wdGlvbnMucnVuO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMucnVuID0gbmV3IFJ1blNlcnZpY2UodGhpcy5vcHRpb25zLnJ1bik7XG4gICAgfVxuXG4gICAgcGF0Y2hSdW5TZXJ2aWNlKHRoaXMucnVuLCB0aGlzKTtcblxuICAgIHZhciBTdHJhdGVneUN0b3IgPSB0eXBlb2YgdGhpcy5vcHRpb25zLnN0cmF0ZWd5ID09PSAnZnVuY3Rpb24nID8gdGhpcy5vcHRpb25zLnN0cmF0ZWd5IDogc3RyYXRlZ2llc01hcFt0aGlzLm9wdGlvbnMuc3RyYXRlZ3ldO1xuXG4gICAgaWYgKCFTdHJhdGVneUN0b3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdTcGVjaWZpZWQgcnVuIGNyZWF0aW9uIHN0cmF0ZWd5IHdhcyBpbnZhbGlkOicsIHRoaXMub3B0aW9ucy5zdHJhdGVneSk7XG4gICAgfVxuXG4gICAgdGhpcy5zdHJhdGVneSA9IG5ldyBTdHJhdGVneUN0b3IodGhpcy5ydW4sIHRoaXMub3B0aW9ucyk7XG59XG5cblJ1bk1hbmFnZXIucHJvdG90eXBlID0ge1xuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHJ1biBvYmplY3QgZm9yIGEgJ2dvb2QnIHJ1bi5cbiAgICAgKlxuICAgICAqIEEgZ29vZCBydW4gaXMgZGVmaW5lZCBieSB0aGUgc3RyYXRlZ3kuIEZvciBleGFtcGxlLCBpZiB0aGUgc3RyYXRlZ3kgaXMgYGFsd2F5cy1uZXdgLCB0aGUgY2FsbFxuICAgICAqIHRvIGBnZXRSdW4oKWAgYWx3YXlzIHJldHVybnMgYSBuZXdseSBjcmVhdGVkIHJ1bjsgaWYgdGhlIHN0cmF0ZWd5IGlzIGBuZXctaWYtcGVyc2lzdGVkYCxcbiAgICAgKiBgZ2V0UnVuKClgIGNyZWF0ZXMgYSBuZXcgcnVuIGlmIHRoZSBwcmV2aW91cyBydW4gaXMgaW4gYSBwZXJzaXN0ZWQgc3RhdGUsIG90aGVyd2lzZVxuICAgICAqIGl0IHJldHVybnMgdGhlIHByZXZpb3VzIHJ1bi4gU2VlIFtSdW4gTWFuYWdlciBTdHJhdGVnaWVzXSguLi9zdHJhdGVnaWVzLykgZm9yIG1vcmUgb24gc3RyYXRlZ2llcy5cbiAgICAgKlxuICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgICBybS5nZXRSdW4oKS50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgKiAgICAgICAgICAvLyB1c2UgdGhlIHJ1biBvYmplY3RcbiAgICAgKiAgICAgICAgICB2YXIgdGhpc1J1bklkID0gcnVuLmlkO1xuICAgICAqXG4gICAgICogICAgICAgICAgLy8gdXNlIHRoZSBSdW4gU2VydmljZSBvYmplY3RcbiAgICAgKiAgICAgICAgICBybS5ydW4uZG8oJ3J1bk1vZGVsJyk7XG4gICAgICogICAgICB9KTtcbiAgICAgKlxuICAgICAqIEByZXR1cm4geyRwcm9taXNlfSBQcm9taXNlIHRvIGNvbXBsZXRlIHRoZSBjYWxsLlxuICAgICAqL1xuICAgIGdldFJ1bjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5zdHJhdGVneVxuICAgICAgICAgICAgICAgIC5nZXRSdW4oKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcnVuIG9iamVjdCBmb3IgYSBuZXcgcnVuLCByZWdhcmRsZXNzIG9mIHN0cmF0ZWd5OiBmb3JjZSBjcmVhdGlvbiBvZiBhIG5ldyBydW4uXG4gICAgICpcbiAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICAgcm0ucmVzZXQoKS50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgKiAgICAgICAgICAvLyB1c2UgdGhlIChuZXcpIHJ1biBvYmplY3RcbiAgICAgKiAgICAgICAgICB2YXIgdGhpc1J1bklkID0gcnVuLmlkO1xuICAgICAqXG4gICAgICogICAgICAgICAgLy8gdXNlIHRoZSBSdW4gU2VydmljZSBvYmplY3RcbiAgICAgKiAgICAgICAgICBybS5ydW4uZG8oJ3J1bk1vZGVsJyk7XG4gICAgICogICAgICB9KTtcbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHJ1blNlcnZpY2VPcHRpb25zIFRoZSBvcHRpb25zIG9iamVjdCB0byBjb25maWd1cmUgdGhlIFJ1biBTZXJ2aWNlLiBTZWUgW1J1biBBUEkgU2VydmljZV0oLi4vcnVuLWFwaS1zZXJ2aWNlLykgZm9yIG1vcmUuXG4gICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgKi9cbiAgICByZXNldDogZnVuY3Rpb24gKHJ1blNlcnZpY2VPcHRpb25zKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnN0cmF0ZWd5LnJlc2V0KHJ1blNlcnZpY2VPcHRpb25zKTtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJ1bk1hbmFnZXI7XG4iLCIvKipcbiAqIFRoZSBgYWx3YXlzLW5ld2Agc3RyYXRlZ3kgYWx3YXlzIGNyZWF0ZXMgYSBuZXcgcnVuIGZvciB0aGlzIGVuZCB1c2VyIGlycmVzcGVjdGl2ZSBvZiBjdXJyZW50IHN0YXRlLiBUaGlzIGlzIGVxdWl2YWxlbnQgdG8gY2FsbGluZyBgRi5zZXJ2aWNlLlJ1bi5jcmVhdGUoKWAgZnJvbSB0aGUgW1J1biBTZXJ2aWNlXSguLi9ydW4tYXBpLXNlcnZpY2UvKSBldmVyeSB0aW1lLiBcbiAqIFxuICogVGhpcyBzdHJhdGVneSBtZWFucyB0aGF0IGV2ZXJ5IHRpbWUgeW91ciBlbmQgdXNlcnMgcmVmcmVzaCB0aGVpciBicm93c2VycywgdGhleSBnZXQgYSBuZXcgcnVuLiBcbiAqIFxuICogVGhpcyBzdHJhdGVneSBjYW4gYmUgdXNlZnVsIGZvciBiYXNpYywgc2luZ2xlLXBhZ2UgcHJvamVjdHMuIFRoaXMgc3RyYXRlZ3kgaXMgYWxzbyB1c2VmdWwgZm9yIHByb3RvdHlwaW5nIG9yIHByb2plY3QgZGV2ZWxvcG1lbnQ6IGl0IGNyZWF0ZXMgYSBuZXcgcnVuIGVhY2ggdGltZSB5b3UgcmVmcmVzaCB0aGUgcGFnZSwgYW5kIHlvdSBjYW4gZWFzaWx5IGNoZWNrIHRoZSBvdXRwdXRzIG9mIHRoZSBtb2RlbC4gSG93ZXZlciwgdHlwaWNhbGx5IHlvdSB3aWxsIHVzZSBvbmUgb2YgdGhlIG90aGVyIHN0cmF0ZWdpZXMgZm9yIGEgcHJvZHVjdGlvbiBwcm9qZWN0LlxuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIGNsYXNzRnJvbSA9IHJlcXVpcmUoJy4uLy4uL3V0aWwvaW5oZXJpdCcpO1xudmFyIENvbmRpdGlvbmFsU3RyYXRlZ3kgPSByZXF1aXJlKCcuL2NvbmRpdGlvbmFsLWNyZWF0aW9uLXN0cmF0ZWd5Jyk7XG5cbnZhciBfX3N1cGVyID0gQ29uZGl0aW9uYWxTdHJhdGVneS5wcm90b3R5cGU7XG5cbnZhciBTdHJhdGVneSA9IGNsYXNzRnJvbShDb25kaXRpb25hbFN0cmF0ZWd5LCB7XG4gICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uIChydW5TZXJ2aWNlLCBvcHRpb25zKSB7XG4gICAgICAgIF9fc3VwZXIuY29uc3RydWN0b3IuY2FsbCh0aGlzLCBydW5TZXJ2aWNlLCB0aGlzLmNyZWF0ZUlmLCBvcHRpb25zKTtcbiAgICB9LFxuXG4gICAgY3JlYXRlSWY6IGZ1bmN0aW9uIChydW4sIGhlYWRlcnMpIHtcbiAgICAgICAgLy8gYWx3YXlzIGNyZWF0ZSBhIG5ldyBydW4hXG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFN0cmF0ZWd5O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgQmFzZSA9IHJlcXVpcmUoJy4vbm9uZS1zdHJhdGVneScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG52YXIgY2xhc3NGcm9tID0gcmVxdWlyZSgnLi4vLi4vdXRpbC9pbmhlcml0Jyk7XG52YXIgQXV0aE1hbmFnZXIgPSByZXF1aXJlKCcuLi9hdXRoLW1hbmFnZXInKTtcblxudmFyIGtleU5hbWVzID0gcmVxdWlyZSgnLi4va2V5LW5hbWVzJyk7XG5cbnZhciBkZWZhdWx0cyA9IHtcbiAgICBzZXNzaW9uS2V5OiBrZXlOYW1lcy5TVFJBVEVHWV9TRVNTSU9OX0tFWSxcbiAgICBwYXRoOiAnJ1xufTtcblxuZnVuY3Rpb24gc2V0UnVuSW5TZXNzaW9uKHNlc3Npb25LZXksIHJ1biwgc2Vzc2lvbk1hbmFnZXIpIHtcbiAgICBzZXNzaW9uTWFuYWdlci5nZXRTdG9yZSgpLnNldChzZXNzaW9uS2V5LCBKU09OLnN0cmluZ2lmeSh7IHJ1bklkOiBydW4uaWQgfSkpO1xufVxuXG4vKipcbiogQ29uZGl0aW9uYWwgQ3JlYXRpb24gU3RyYXRlZ3lcbiogVGhpcyBzdHJhdGVneSB3aWxsIHRyeSB0byBnZXQgdGhlIHJ1biBzdG9yZWQgaW4gdGhlIGNvb2tpZSBhbmRcbiogZXZhbHVhdGUgaWYgbmVlZHMgdG8gY3JlYXRlIGEgbmV3IHJ1biBieSBjYWxsaW5nIHRoZSAnY29uZGl0aW9uJyBmdW5jdGlvblxuKi9cblxudmFyIFN0cmF0ZWd5ID0gY2xhc3NGcm9tKEJhc2UsIHtcbiAgICBjb25zdHJ1Y3RvcjogZnVuY3Rpb24gU3RyYXRlZ3kocnVuU2VydmljZSwgY29uZGl0aW9uLCBvcHRpb25zKSB7XG4gICAgICAgIGlmIChjb25kaXRpb24gPT0gbnVsbCkgeyAvL2VzbGludC1kaXNhYmxlLWxpbmVcbiAgICAgICAgICAgIC8vVE9ETzogbm90IHN1cmUgd2h5IHRoaXMgaXMgZXhwbGljaXRseSA9PVxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdDb25kaXRpb25hbCBzdHJhdGVneSBuZWVkcyBhIGNvbmRpdGlvbiB0byBjcmVhdGUgYSBydW4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX2F1dGggPSBuZXcgQXV0aE1hbmFnZXIoKTtcbiAgICAgICAgdGhpcy5ydW4gPSBydW5TZXJ2aWNlO1xuICAgICAgICB0aGlzLmNvbmRpdGlvbiA9IHR5cGVvZiBjb25kaXRpb24gIT09ICdmdW5jdGlvbicgPyBmdW5jdGlvbiAoKSB7IHJldHVybiBjb25kaXRpb247IH0gOiBjb25kaXRpb247XG4gICAgICAgIHRoaXMub3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBkZWZhdWx0cywgb3B0aW9ucyk7XG4gICAgICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIob3B0aW9ucyk7XG4gICAgICAgIHRoaXMucnVuT3B0aW9ucyA9IHRoaXMub3B0aW9ucy5ydW47XG4gICAgfSxcblxuICAgIHJ1bk9wdGlvbnNXaXRoU2NvcGU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHVzZXJTZXNzaW9uID0gdGhpcy5fYXV0aC5nZXRDdXJyZW50VXNlclNlc3Npb25JbmZvKCk7XG4gICAgICAgIHJldHVybiAkLmV4dGVuZCh7XG4gICAgICAgICAgICBzY29wZTogeyBncm91cDogdXNlclNlc3Npb24uZ3JvdXBOYW1lIH1cbiAgICAgICAgfSwgdGhpcy5ydW5PcHRpb25zKTtcbiAgICB9LFxuXG4gICAgcmVzZXQ6IGZ1bmN0aW9uIChydW5TZXJ2aWNlT3B0aW9ucykge1xuICAgICAgICB2YXIgbWUgPSB0aGlzO1xuICAgICAgICB2YXIgb3B0ID0gdGhpcy5ydW5PcHRpb25zV2l0aFNjb3BlKCk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMucnVuXG4gICAgICAgICAgICAgICAgLmNyZWF0ZShvcHQsIHJ1blNlcnZpY2VPcHRpb25zKVxuICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKHJ1bikge1xuICAgICAgICAgICAgICAgIHNldFJ1bkluU2Vzc2lvbihtZS5vcHRpb25zLnNlc3Npb25LZXksIHJ1biwgbWUuc2Vzc2lvbk1hbmFnZXIpO1xuICAgICAgICAgICAgICAgIHJ1bi5mcmVzaGx5Q3JlYXRlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHJ1bjtcbiAgICAgICAgICAgIH0pO1xuICAgIH0sXG5cbiAgICBnZXRSdW46IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHNlc3Npb25TdG9yZSA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0U3RvcmUoKTtcbiAgICAgICAgdmFyIHJ1blNlc3Npb24gPSBKU09OLnBhcnNlKHNlc3Npb25TdG9yZS5nZXQodGhpcy5vcHRpb25zLnNlc3Npb25LZXkpKTtcbiAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgaWYgKHJ1blNlc3Npb24gJiYgcnVuU2Vzc2lvbi5ydW5JZCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2xvYWRBbmRDaGVjayhydW5TZXNzaW9uKS5mYWlsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gbWUucmVzZXQoKTsgLy9pZiBpdCBnb3QgdGhlIHdyb25nIGNvb2tpZSBmb3IgZS5nLlxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5yZXNldCgpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9sb2FkQW5kQ2hlY2s6IGZ1bmN0aW9uIChydW5TZXNzaW9uKSB7XG4gICAgICAgIHZhciBzaG91bGRDcmVhdGUgPSBmYWxzZTtcbiAgICAgICAgdmFyIG1lID0gdGhpcztcblxuICAgICAgICByZXR1cm4gdGhpcy5ydW5cbiAgICAgICAgICAgIC5sb2FkKHJ1blNlc3Npb24ucnVuSWQsIG51bGwsIHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiBmdW5jdGlvbiAocnVuLCBtc2csIGhlYWRlcnMpIHtcbiAgICAgICAgICAgICAgICAgICAgc2hvdWxkQ3JlYXRlID0gbWUuY29uZGl0aW9uKHJ1biwgaGVhZGVycyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgICAgICAgICAgICBpZiAoc2hvdWxkQ3JlYXRlKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBvcHQgPSBtZS5ydW5PcHRpb25zV2l0aFNjb3BlKCk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBtZS5ydW4uY3JlYXRlKG9wdClcbiAgICAgICAgICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKHJ1bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2V0UnVuSW5TZXNzaW9uKG1lLm9wdGlvbnMuc2Vzc2lvbktleSwgcnVuLCBtZS5zZXNzaW9uTWFuYWdlcik7XG4gICAgICAgICAgICAgICAgICAgICAgICBydW4uZnJlc2hseUNyZWF0ZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJ1bjtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybiBydW47XG4gICAgICAgICAgICB9KTtcbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBTdHJhdGVneTtcbiIsIi8qKlxuICogVGhlIGBtdWx0aXBsYXllcmAgc3RyYXRlZ3kgaXMgZm9yIHVzZSB3aXRoIFttdWx0aXBsYXllciB3b3JsZHNdKC4uLy4uLy4uL2dsb3NzYXJ5LyN3b3JsZCkuIEl0IGNoZWNrcyB0aGUgY3VycmVudCB3b3JsZCBmb3IgdGhpcyBlbmQgdXNlciwgYW5kIGFsd2F5cyByZXR1cm5zIHRoZSBjdXJyZW50IHJ1biBmb3IgdGhhdCB3b3JsZC4gVGhpcyBpcyBlcXVpdmFsZW50IHRvIGNhbGxpbmcgYGdldEN1cnJlbnRXb3JsZEZvclVzZXIoKWAgYW5kIHRoZW4gYGdldEN1cnJlbnRSdW5JZCgpYCBmcm9tIHRoZSBbV29ybGQgQVBJIEFkYXBhdGVyXSguLi93b3JsZC1hcGktYWRhcHRlci8pLlxuICogXG4gKiBVc2luZyB0aGlzIHN0cmF0ZWd5IG1lYW5zIHRoYXQgZW5kIHVzZXJzIGluIHByb2plY3RzIHdpdGggbXVsdGlwbGF5ZXIgd29ybGRzIGFsd2F5cyBzZWUgdGhlIG1vc3QgY3VycmVudCBydW4gYW5kIHdvcmxkLiBUaGlzIGVuc3VyZXMgdGhhdCB0aGV5IGFyZSBpbiBzeW5jIHdpdGggdGhlIG90aGVyIGVuZCB1c2VycyBzaGFyaW5nIHRoZWlyIHdvcmxkIGFuZCBydW4uIEluIHR1cm4sIHRoaXMgYWxsb3dzIGZvciBjb21wZXRpdGl2ZSBvciBjb2xsYWJvcmF0aXZlIG11bHRpcGxheWVyIHByb2plY3RzLlxuICovXG4ndXNlIHN0cmljdCc7XG5cbnZhciBjbGFzc0Zyb20gPSByZXF1aXJlKCcuLi8uLi91dGlsL2luaGVyaXQnKTtcblxudmFyIElkZW50aXR5U3RyYXRlZ3kgPSByZXF1aXJlKCcuL25vbmUtc3RyYXRlZ3knKTtcbnZhciBXb3JsZEFwaUFkYXB0ZXIgPSByZXF1aXJlKCcuLi8uLi9zZXJ2aWNlL3dvcmxkLWFwaS1hZGFwdGVyJyk7XG52YXIgQXV0aE1hbmFnZXIgPSByZXF1aXJlKCcuLi9hdXRoLW1hbmFnZXInKTtcblxudmFyIGRlZmF1bHRzID0ge1xuICAgIHN0b3JlOiB7XG4gICAgICAgIHN5bmNocm9ub3VzOiB0cnVlXG4gICAgfVxufTtcblxudmFyIFN0cmF0ZWd5ID0gY2xhc3NGcm9tKElkZW50aXR5U3RyYXRlZ3ksIHtcblxuICAgIGNvbnN0cnVjdG9yOiBmdW5jdGlvbiAocnVuU2VydmljZSwgb3B0aW9ucykge1xuICAgICAgICB0aGlzLnJ1blNlcnZpY2UgPSBydW5TZXJ2aWNlO1xuICAgICAgICB0aGlzLm9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgZGVmYXVsdHMsIG9wdGlvbnMpO1xuICAgICAgICB0aGlzLl9hdXRoID0gbmV3IEF1dGhNYW5hZ2VyKCk7XG4gICAgICAgIHRoaXMuX2xvYWRSdW4gPSB0aGlzLl9sb2FkUnVuLmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMud29ybGRBcGkgPSBuZXcgV29ybGRBcGlBZGFwdGVyKHRoaXMub3B0aW9ucy5ydW4pO1xuICAgIH0sXG5cbiAgICByZXNldDogZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuX2F1dGguZ2V0Q3VycmVudFVzZXJTZXNzaW9uSW5mbygpO1xuICAgICAgICB2YXIgY3VyVXNlcklkID0gc2Vzc2lvbi51c2VySWQ7XG4gICAgICAgIHZhciBjdXJHcm91cE5hbWUgPSBzZXNzaW9uLmdyb3VwTmFtZTtcblxuICAgICAgICByZXR1cm4gdGhpcy53b3JsZEFwaVxuICAgICAgICAgICAgLmdldEN1cnJlbnRXb3JsZEZvclVzZXIoY3VyVXNlcklkLCBjdXJHcm91cE5hbWUpXG4gICAgICAgICAgICAudGhlbihmdW5jdGlvbiAod29ybGQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy53b3JsZEFwaS5uZXdSdW5Gb3JXb3JsZCh3b3JsZC5pZCk7XG4gICAgICAgICAgICB9LmJpbmQodGhpcykpO1xuICAgIH0sXG5cbiAgICBnZXRSdW46IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLl9hdXRoLmdldEN1cnJlbnRVc2VyU2Vzc2lvbkluZm8oKTtcbiAgICAgICAgdmFyIGN1clVzZXJJZCA9IHNlc3Npb24udXNlcklkO1xuICAgICAgICB2YXIgY3VyR3JvdXBOYW1lID0gc2Vzc2lvbi5ncm91cE5hbWU7XG4gICAgICAgIHZhciB3b3JsZEFwaSA9IHRoaXMud29ybGRBcGk7XG4gICAgICAgIHZhciBtb2RlbCA9IHRoaXMub3B0aW9ucy5tb2RlbDtcbiAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgdmFyIGR0ZCA9ICQuRGVmZXJyZWQoKTtcblxuICAgICAgICBpZiAoIWN1clVzZXJJZCkge1xuICAgICAgICAgICAgcmV0dXJuIGR0ZC5yZWplY3QoeyBzdGF0dXNDb2RlOiA0MDAsIGVycm9yOiAnV2UgbmVlZCBhbiBhdXRoZW50aWNhdGVkIHVzZXIgdG8gam9pbiBhIG11bHRpcGxheWVyIHdvcmxkLiAoRVJSOiBubyB1c2VySWQgaW4gc2Vzc2lvbiknIH0sIHNlc3Npb24pLnByb21pc2UoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBsb2FkUnVuRnJvbVdvcmxkID0gZnVuY3Rpb24gKHdvcmxkKSB7XG4gICAgICAgICAgICBpZiAoIXdvcmxkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGR0ZC5yZWplY3QoeyBzdGF0dXNDb2RlOiA0MDQsIGVycm9yOiAnVGhlIHVzZXIgaXMgbm90IGluIGFueSB3b3JsZC4nIH0sIHsgb3B0aW9uczogbWUub3B0aW9ucywgc2Vzc2lvbjogc2Vzc2lvbiB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHdvcmxkQXBpLmdldEN1cnJlbnRSdW5JZCh7IG1vZGVsOiBtb2RlbCwgZmlsdGVyOiB3b3JsZC5pZCB9KVxuICAgICAgICAgICAgICAgIC50aGVuKG1lLl9sb2FkUnVuKVxuICAgICAgICAgICAgICAgIC50aGVuKGR0ZC5yZXNvbHZlKVxuICAgICAgICAgICAgICAgIC5mYWlsKGR0ZC5yZWplY3QpO1xuICAgICAgICB9O1xuXG4gICAgICAgIHZhciBzZXJ2ZXJFcnJvciA9IGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgLy8gaXMgdGhpcyBwb3NzaWJsZT9cbiAgICAgICAgICAgIGR0ZC5yZWplY3QoZXJyb3IsIHNlc3Npb24sIG1lLm9wdGlvbnMpO1xuICAgICAgICB9O1xuXG4gICAgICAgIHRoaXMud29ybGRBcGlcbiAgICAgICAgICAgIC5nZXRDdXJyZW50V29ybGRGb3JVc2VyKGN1clVzZXJJZCwgY3VyR3JvdXBOYW1lKVxuICAgICAgICAgICAgLnRoZW4obG9hZFJ1bkZyb21Xb3JsZClcbiAgICAgICAgICAgIC5mYWlsKHNlcnZlckVycm9yKTtcblxuICAgICAgICByZXR1cm4gZHRkLnByb21pc2UoKTtcbiAgICB9LFxuXG4gICAgX2xvYWRSdW46IGZ1bmN0aW9uIChpZCwgb3B0aW9ucykge1xuICAgICAgICByZXR1cm4gdGhpcy5ydW5TZXJ2aWNlLmxvYWQoaWQsIG51bGwsIG9wdGlvbnMpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFN0cmF0ZWd5O1xuIiwiLyoqXG4gKiBUaGUgYG5ldy1pZi1pbml0aWFsaXplZGAgc3RyYXRlZ3kgY3JlYXRlcyBhIG5ldyBydW4gaWYgdGhlIGN1cnJlbnQgb25lIGlzIGluIG1lbW9yeSBvciBoYXMgaXRzIGBpbml0aWFsaXplZGAgZmllbGQgc2V0IHRvIGB0cnVlYC4gVGhlIGBpbml0aWFsaXplZGAgZmllbGQgaW4gdGhlIHJ1biByZWNvcmQgaXMgYXV0b21hdGljYWxseSBzZXQgdG8gYHRydWVgIGF0IHJ1biBjcmVhdGlvbiBmb3IgVmVuc2ltIG1vZGVsczsgaXQgY2FuIGJlIHNldCBtYW51YWxseSBmb3Igb3RoZXIgbW9kZWxzLlxuICogXG4gKiBUaGlzIHN0cmF0ZWd5IGlzIHVzZWZ1bCBpZiB5b3VyIHByb2plY3QgaXMgc3RydWN0dXJlZCBzdWNoIHRoYXQgaW1tZWRpYXRlbHkgYWZ0ZXIgYSBydW4gaXMgY3JlYXRlZCwgdGhlIG1vZGVsIGlzIGV4ZWN1dGVkIGNvbXBsZXRlbHkgKGZvciBleGFtcGxlLCBhIFZlbnNpbSBtb2RlbCBpcyBzdGVwcGVkIHRvIHRoZSBlbmQpLiBJdCBpcyBzaW1pbGFyIHRvIHRoZSBgbmV3LWlmLW1pc3NpbmdgIHN0cmF0ZWd5LCBleGNlcHQgdGhhdCBpdCBjaGVja3MgYSBmaWVsZCBvZiB0aGUgcnVuIHJlY29yZC5cbiAqIFxuICogU3BlY2lmaWNhbGx5LCB0aGUgc3RyYXRlZ3kgaXM6XG4gKlxuICogKiBDaGVjayB0aGUgYHNlc3Npb25LZXlgIGNvb2tpZS4gXG4gKiAgKiBUaGlzIGNvb2tpZSBpcyBzZXQgYnkgdGhlIFtSdW4gTWFuYWdlcl0oLi4vcnVuLW1hbmFnZXIvKSBhbmQgY29uZmlndXJhYmxlIHRocm91Z2ggaXRzIG9wdGlvbnMuXG4gKiAgKiBJZiB0aGUgY29va2llIGV4aXN0cywgY2hlY2sgd2hldGhlciB0aGUgcnVuIGlzIGluIG1lbW9yeSBvciBvbmx5IHBlcnNpc3RlZCBpbiB0aGUgZGF0YWJhc2UuIEFkZGl0aW9uYWxseSwgY2hlY2sgd2hldGhlciB0aGUgcnVuJ3MgYGluaXRpYWxpemVkYCBmaWVsZCBpcyBgdHJ1ZWAuIFxuICogICAgICAqIElmIHRoZSBydW4gaXMgaW4gbWVtb3J5LCBjcmVhdGUgYSBuZXcgcnVuLlxuICogICAgICAqIElmIHRoZSBydW4ncyBgaW5pdGlhbGl6ZWRgIGZpZWxkIGlzIGB0cnVlYCwgY3JlYXRlIGEgbmV3IHJ1bi5cbiAqICAgICAgKiBPdGhlcndpc2UsIHVzZSB0aGUgZXhpc3RpbmcgcnVuLlxuICogICogSWYgdGhlIGNvb2tpZSBkb2VzIG5vdCBleGlzdCwgY3JlYXRlIGEgbmV3IHJ1biBmb3IgdGhpcyBlbmQgdXNlci5cbiAqL1xuXG4ndXNlIHN0cmljdCc7XG52YXIgY2xhc3NGcm9tID0gcmVxdWlyZSgnLi4vLi4vdXRpbC9pbmhlcml0Jyk7XG52YXIgQ29uZGl0aW9uYWxTdHJhdGVneSA9IHJlcXVpcmUoJy4vY29uZGl0aW9uYWwtY3JlYXRpb24tc3RyYXRlZ3knKTtcblxudmFyIF9fc3VwZXIgPSBDb25kaXRpb25hbFN0cmF0ZWd5LnByb3RvdHlwZTtcblxudmFyIFN0cmF0ZWd5ID0gY2xhc3NGcm9tKENvbmRpdGlvbmFsU3RyYXRlZ3ksIHtcbiAgICBjb25zdHJ1Y3RvcjogZnVuY3Rpb24gKHJ1blNlcnZpY2UsIG9wdGlvbnMpIHtcbiAgICAgICAgX19zdXBlci5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMsIHJ1blNlcnZpY2UsIHRoaXMuY3JlYXRlSWYsIG9wdGlvbnMpO1xuICAgIH0sXG5cbiAgICBjcmVhdGVJZjogZnVuY3Rpb24gKHJ1biwgaGVhZGVycykge1xuICAgICAgICByZXR1cm4gaGVhZGVycy5nZXRSZXNwb25zZUhlYWRlcigncHJhZ21hJykgPT09ICdwZXJzaXN0ZW50JyB8fCBydW4uaW5pdGlhbGl6ZWQ7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzID0gU3RyYXRlZ3k7XG4iLCIvKipcbiAqIFRoZSBgbmV3LWlmLW1pc3NpbmdgIHN0cmF0ZWd5IGNyZWF0ZXMgYSBuZXcgcnVuIHdoZW4gdGhlIGN1cnJlbnQgb25lIGlzIG5vdCBpbiB0aGUgYnJvd3NlciBjb29raWUuXG4gKiBcbiAqIFVzaW5nIHRoaXMgc3RyYXRlZ3kgbWVhbnMgdGhhdCB3aGVuIGVuZCB1c2VycyBuYXZpZ2F0ZSBiZXR3ZWVuIHBhZ2VzIGluIHlvdXIgcHJvamVjdCwgb3IgcmVmcmVzaCB0aGVpciBicm93c2VycywgdGhleSB3aWxsIHN0aWxsIGJlIHdvcmtpbmcgd2l0aCB0aGUgc2FtZSBydW4uXG4gKlxuICogVGhpcyBzdHJhdGVneSBpcyB1c2VmdWwgaWYgeW91ciBwcm9qZWN0IGlzIHN0cnVjdHVyZWQgc3VjaCB0aGF0IGltbWVkaWF0ZWx5IGFmdGVyIGEgcnVuIGlzIGNyZWF0ZWQsIHRoZSBtb2RlbCBpcyBleGVjdXRlZCBjb21wbGV0ZWx5IChmb3IgZXhhbXBsZSwgYSBWZW5zaW0gbW9kZWwgdGhhdCBpcyBzdGVwcGVkIHRvIHRoZSBlbmQgYXMgc29vbiBhcyBpdCBpcyBjcmVhdGVkKS4gSW4gb3RoZXIgd29yZHMsIHlvdSBjYXJlIHdoZXRoZXIgeW91IGhhdmUgYSBydW4sIGJ1dCBhcyBsb25nIGFzIHlvdSBoYXZlIG9uZSwgeW91IGFyZSBjZXJ0YWluIHRoYXQgdGhpcyBydW4gaXMgdGhlIG9uZSB5b3UgYXJlIGludGVyZXN0ZWQgaW4uIFxuICogXG4gKiBTcGVjaWZpY2FsbHksIHRoZSBzdHJhdGVneSBpczpcbiAqXG4gKiAqIENoZWNrIHRoZSBgc2Vzc2lvbktleWAgY29va2llLlxuICogICAgICogVGhpcyBjb29raWUgaXMgc2V0IGJ5IHRoZSBbUnVuIE1hbmFnZXJdKC4uL3J1bi1tYW5hZ2VyLykgYW5kIGNvbmZpZ3VyYWJsZSB0aHJvdWdoIGl0cyBvcHRpb25zLiBcbiAqICAgICAqIElmIHRoZSBjb29raWUgZXhpc3RzLCB1c2UgdGhlIHJ1biBpZCBzdG9yZWQgdGhlcmUuIFxuICogICAgICogSWYgdGhlIGNvb2tpZSBkb2VzIG5vdCBleGlzdCwgY3JlYXRlIGEgbmV3IHJ1biBmb3IgdGhpcyBlbmQgdXNlci4gXG4gKi9cblxuJ3VzZSBzdHJpY3QnO1xuXG52YXIgY2xhc3NGcm9tID0gcmVxdWlyZSgnLi4vLi4vdXRpbC9pbmhlcml0Jyk7XG52YXIgQ29uZGl0aW9uYWxTdHJhdGVneSA9IHJlcXVpcmUoJy4vY29uZGl0aW9uYWwtY3JlYXRpb24tc3RyYXRlZ3knKTtcblxudmFyIF9fc3VwZXIgPSBDb25kaXRpb25hbFN0cmF0ZWd5LnByb3RvdHlwZTtcblxuLypcbiogIGNyZWF0ZSBhIG5ldyBydW4gb25seSBpZiBub3RoaW5nIGlzIHN0b3JlZCBpbiB0aGUgY29va2llXG4qICB0aGlzIGlzIHVzZWZ1bCBmb3IgYmFzZVJ1bnMuXG4qL1xudmFyIFN0cmF0ZWd5ID0gY2xhc3NGcm9tKENvbmRpdGlvbmFsU3RyYXRlZ3ksIHtcbiAgICBjb25zdHJ1Y3RvcjogZnVuY3Rpb24gKHJ1blNlcnZpY2UsIG9wdGlvbnMpIHtcbiAgICAgICAgX19zdXBlci5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMsIHJ1blNlcnZpY2UsIHRoaXMuY3JlYXRlSWYsIG9wdGlvbnMpO1xuICAgIH0sXG5cbiAgICBjcmVhdGVJZjogZnVuY3Rpb24gKHJ1biwgaGVhZGVycykge1xuICAgICAgICAvLyBpZiB3ZSBhcmUgaGVyZSwgaXQgbWVhbnMgdGhhdCB0aGUgcnVuIGV4aXN0cy4uLiBzbyB3ZSBkb24ndCBuZWVkIGEgbmV3IG9uZVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzID0gU3RyYXRlZ3k7XG4iLCIvKipcbiAqIFRoZSBgbmV3LWlmLXBlcnNpc3RlZGAgc3RyYXRlZ3kgY3JlYXRlcyBhIG5ldyBydW4gd2hlbiB0aGUgY3VycmVudCBvbmUgYmVjb21lcyBwZXJzaXN0ZWQgKGVuZCB1c2VyIGlzIGlkbGUgZm9yIGEgc2V0IHBlcmlvZCksIGJ1dCBvdGhlcndpc2UgdXNlcyB0aGUgY3VycmVudCBvbmUuIFxuICogXG4gKiBVc2luZyB0aGlzIHN0cmF0ZWd5IG1lYW5zIHRoYXQgd2hlbiBlbmQgdXNlcnMgbmF2aWdhdGUgYmV0d2VlbiBwYWdlcyBpbiB5b3VyIHByb2plY3QsIG9yIHJlZnJlc2ggdGhlaXIgYnJvd3NlcnMsIHRoZXkgd2lsbCBzdGlsbCBiZSB3b3JraW5nIHdpdGggdGhlIHNhbWUgcnVuLiBcbiAqIFxuICogSG93ZXZlciwgaWYgdGhleSBhcmUgaWRsZSBmb3IgbG9uZ2VyIHRoYW4geW91ciBwcm9qZWN0J3MgKipNb2RlbCBTZXNzaW9uIFRpbWVvdXQqKiAoY29uZmlndXJlZCBpbiB5b3VyIHByb2plY3QncyBbU2V0dGluZ3NdKC4uLy4uLy4uL3VwZGF0aW5nX3lvdXJfc2V0dGluZ3MvKSksIHRoZW4gdGhlaXIgcnVuIGlzIHBlcnNpc3RlZDsgdGhlIG5leHQgdGltZSB0aGV5IGludGVyYWN0IHdpdGggdGhlIHByb2plY3QsIHRoZXkgd2lsbCBnZXQgYSBuZXcgcnVuLiAoU2VlIG1vcmUgYmFja2dyb3VuZCBvbiBbUnVuIFBlcnNpc3RlbmNlXSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvKS4pXG4gKiBcbiAqIFRoaXMgc3RyYXRlZ3kgaXMgdXNlZnVsIGZvciBtdWx0aS1wYWdlIHByb2plY3RzIHdoZXJlIGVuZCB1c2VycyBwbGF5IHRocm91Z2ggYSBzaW11bGF0aW9uIGluIG9uZSBzaXR0aW5nLCBzdGVwcGluZyB0aHJvdWdoIHRoZSBtb2RlbCBzZXF1ZW50aWFsbHkgKGZvciBleGFtcGxlLCBhIFZlbnNpbSBtb2RlbCB0aGF0IHVzZXMgdGhlIGBzdGVwYCBvcGVyYXRpb24pIG9yIGNhbGxpbmcgc3BlY2lmaWMgZnVuY3Rpb25zIHVudGlsIHRoZSBtb2RlbCBpcyBcImNvbXBsZXRlLlwiIEhvd2V2ZXIsIHlvdSB3aWxsIG5lZWQgIHRvIGd1YXJhbnRlZSB0aGF0IHlvdXIgZW5kIHVzZXJzIHdpbGwgcmVtYWluIGVuZ2FnZWQgd2l0aCB0aGUgcHJvamVjdCBmcm9tIGJlZ2lubmluZyB0byBlbmQgJm1kYXNoOyBvciBhdCBsZWFzdCwgaWYgdGhleSBhcmUgaWRsZSBmb3IgbG9uZ2VyIHRoYW4gdGhlICoqTW9kZWwgU2Vzc2lvbiBUaW1lb3V0KiosIHRoYXQgaXQgaXMgb2theSBmb3IgdGhlbSB0byBzdGFydCB0aGUgcHJvamVjdCBmcm9tIHNjcmF0Y2ggKHdpdGggYW4gdW5pbml0aWFsaXplZCBtb2RlbCkuIFxuICogXG4gKiBTcGVjaWZpY2FsbHksIHRoZSBzdHJhdGVneSBpczpcbiAqXG4gKiAqIENoZWNrIHRoZSBgc2Vzc2lvbktleWAgY29va2llLlxuICogICAqIFRoaXMgY29va2llIGlzIHNldCBieSB0aGUgW1J1biBNYW5hZ2VyXSguLi9ydW4tbWFuYWdlci8pIGFuZCBjb25maWd1cmFibGUgdGhyb3VnaCBpdHMgb3B0aW9ucy5cbiAqICAgKiBJZiB0aGUgY29va2llIGV4aXN0cywgY2hlY2sgd2hldGhlciB0aGUgcnVuIGlzIGluIG1lbW9yeSBvciBvbmx5IHBlcnNpc3RlZCBpbiB0aGUgZGF0YWJhc2UuIFxuICogICAgICAqIElmIHRoZSBydW4gaXMgaW4gbWVtb3J5LCB1c2UgdGhlIHJ1bi5cbiAqICAgICAgKiBJZiB0aGUgcnVuIGlzIG9ubHkgcGVyc2lzdGVkIChhbmQgbm90IHN0aWxsIGluIG1lbW9yeSksIGNyZWF0ZSBhIG5ldyBydW4gZm9yIHRoaXMgZW5kIHVzZXIuXG4gKiAgICAgICogSWYgdGhlIGNvb2tpZSBkb2VzIG5vdCBleGlzdCwgY3JlYXRlIGEgbmV3IHJ1biBmb3IgdGhpcyBlbmQgdXNlci5cbiAqL1xuXG4ndXNlIHN0cmljdCc7XG52YXIgY2xhc3NGcm9tID0gcmVxdWlyZSgnLi4vLi4vdXRpbC9pbmhlcml0Jyk7XG52YXIgQ29uZGl0aW9uYWxTdHJhdGVneSA9IHJlcXVpcmUoJy4vY29uZGl0aW9uYWwtY3JlYXRpb24tc3RyYXRlZ3knKTtcblxudmFyIF9fc3VwZXIgPSBDb25kaXRpb25hbFN0cmF0ZWd5LnByb3RvdHlwZTtcblxudmFyIFN0cmF0ZWd5ID0gY2xhc3NGcm9tKENvbmRpdGlvbmFsU3RyYXRlZ3ksIHtcbiAgICBjb25zdHJ1Y3RvcjogZnVuY3Rpb24gKHJ1blNlcnZpY2UsIG9wdGlvbnMpIHtcbiAgICAgICAgX19zdXBlci5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMsIHJ1blNlcnZpY2UsIHRoaXMuY3JlYXRlSWYsIG9wdGlvbnMpO1xuICAgIH0sXG5cbiAgICBjcmVhdGVJZjogZnVuY3Rpb24gKHJ1biwgaGVhZGVycykge1xuICAgICAgICByZXR1cm4gaGVhZGVycy5nZXRSZXNwb25zZUhlYWRlcigncHJhZ21hJykgPT09ICdwZXJzaXN0ZW50JztcbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBTdHJhdGVneTtcbiIsIi8qKlxuICogVGhlIGBub25lYCBzdHJhdGVneSBuZXZlciByZXR1cm5zIGEgcnVuIG9yIHRyaWVzIHRvIGNyZWF0ZSBhIG5ldyBydW4uIEl0IHNpbXBseSByZXR1cm5zIHRoZSBjb250ZW50cyBvZiB0aGUgY3VycmVudCBbUnVuIFNlcnZpY2UgaW5zdGFuY2VdKC4uL3J1bi1hcGktc2VydmljZS8pLlxuICogXG4gKiBUaGlzIHN0cmF0ZWd5IGlzIHVzZWZ1bCBpZiB5b3Ugd2FudCB0byBtYW51YWxseSBkZWNpZGUgaG93IHRvIGNyZWF0ZSB5b3VyIG93biBydW5zIGFuZCBkb24ndCB3YW50IGFueSBhdXRvbWF0aWMgYXNzaXN0YW5jZS4gXG4gKiBcbiAqIEFsc28sIHRoaXMgc3RyYXRlZ3kgaXMgbmVjZXNzYXJ5IGlmIHlvdSBhcmUgd29ya2luZyB3aXRoIGEgbXVsdGlwbGF5ZXIgcHJvamVjdCBhbmQgdXNpbmcgdGhlIFtXb3JsZCBNYW5hZ2VyXSguLi93b3JsZC1tYW5hZ2VyLykgJm1kYXNoOyBvciBvdGhlciwgc2ltaWxhciBzaXR1YXRpb25zIHdoZXJlIHlvdSBkbyBub3QgaGF2ZSBkaXJlY3QgY29udHJvbCBvdmVyIGNyZWF0aW5nIHRoZSBbUnVuIFNlcnZpY2VdKC4uL3J1bi1hcGktc2VydmljZS8pIGluc3RhbmNlLlxuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIGNsYXNzRnJvbSA9IHJlcXVpcmUoJy4uLy4uL3V0aWwvaW5oZXJpdCcpO1xudmFyIEJhc2UgPSB7fTtcblxuLy8gSW50ZXJmYWNlIHRoYXQgYWxsIHN0cmF0ZWdpZXMgbmVlZCB0byBpbXBsZW1lbnRcbm1vZHVsZS5leHBvcnRzID0gY2xhc3NGcm9tKEJhc2UsIHtcbiAgICBjb25zdHJ1Y3RvcjogZnVuY3Rpb24gKHJ1blNlcnZpY2UsIG9wdGlvbnMpIHtcbiAgICAgICAgdGhpcy5ydW5TZXJ2aWNlID0gcnVuU2VydmljZTtcbiAgICB9LFxuXG4gICAgcmVzZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gcmV0dXJuIGEgbmV3bHkgY3JlYXRlZCBydW5cbiAgICAgICAgcmV0dXJuICQuRGVmZXJyZWQoKS5yZXNvbHZlKCkucHJvbWlzZSgpO1xuICAgIH0sXG5cbiAgICBnZXRSdW46IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gcmV0dXJuIGEgdXNhYmxlIHJ1blxuICAgICAgICByZXR1cm4gJC5EZWZlcnJlZCgpLnJlc29sdmUodGhpcy5ydW5TZXJ2aWNlKS5wcm9taXNlKCk7XG4gICAgfVxufSk7XG4iLCIvKipcbiAqIFRoZSBgcGVyc2lzdGVudC1zaW5nbGUtcGxheWVyYCBzdHJhdGVneSByZXR1cm5zIHRoZSBsYXRlc3QgKG1vc3QgcmVjZW50KSBydW4gZm9yIHRoaXMgdXNlciwgd2hldGhlciBpdCBpcyBpbiBtZW1vcnkgb3Igbm90LiBJZiB0aGVyZSBhcmUgbm8gcnVucyBmb3IgdGhpcyB1c2VyLCBpdCBjcmVhdGVzIGEgbmV3IG9uZS5cbiAqXG4gKiBUaGlzIHN0cmF0ZWd5IGlzIHVzZWZ1bCBpZiB5b3VyIHByb2plY3QgZXhlY3V0ZXMgeW91ciBtb2RlbCBzdGVwIGJ5IHN0ZXAgKGFzIG9wcG9zZWQgdG8gYSBwcm9qZWN0IHdoZXJlIHRoZSBtb2RlbCBpcyBleGVjdXRlZCBjb21wbGV0ZWx5LCBmb3IgZXhhbXBsZSwgYSBWZW5zaW0gbW9kZWwgdGhhdCBpcyBpbW1lZGlhdGVseSBzdGVwcGVkIHRvIHRoZSBlbmQpLiBJdCBpcyB1c2VmdWwgaWYgZW5kIHVzZXJzIHBsYXkgd2l0aCB5b3VyIHByb2plY3QgZm9yIGFuIGV4dGVuZGVkIHBlcmlvZCBvZiB0aW1lLCBwb3NzaWJseSBvdmVyIHNldmVyYWwgc2Vzc2lvbnMuXG4gKlxuICogU3BlY2lmaWNhbGx5LCB0aGUgc3RyYXRlZ3kgaXM6XG4gKiBcbiAqICogQ2hlY2sgaWYgdGhlcmUgYXJlIGFueSBydW5zIGZvciB0aGlzIGVuZCB1c2VyLlxuICogICAgICogSWYgdGhlcmUgYXJlIG5vIHJ1bnMgKGVpdGhlciBpbiBtZW1vcnkgb3IgaW4gdGhlIGRhdGFiYXNlKSwgY3JlYXRlIGEgbmV3IG9uZS5cbiAqICAgICAqIElmIHRoZXJlIGFyZSBydW5zLCB0YWtlIHRoZSBsYXRlc3QgKG1vc3QgcmVjZW50KSBvbmUuXG4gKiAgICAgICAgICogSWYgdGhlIG1vc3QgcmVjZW50IHJ1biBpcyBjdXJyZW50bHkgaW4gdGhlIGRhdGFiYXNlLCBicmluZyBpdCBiYWNrIGludG8gbWVtb3J5IHNvIHRoYXQgdGhlIGVuZCB1c2VyIGNhbiBjb250aW51ZSB3b3JraW5nIHdpdGggaXQuIChTZWUgbW9yZSBiYWNrZ3JvdW5kIG9uIFtSdW4gUGVyc2lzdGVuY2VdKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZS8pLCBvciByZWFkIG1vcmUgb24gdGhlIHVuZGVybHlpbmcgW1N0YXRlIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL290aGVyX2FwaXMvbW9kZWxfYXBpcy9zdGF0ZS8pIGZvciBicmluZ2luZyBydW5zIGZyb20gdGhlIGRhdGFiYXNlIGJhY2sgaW50byBtZW1vcnkuKSBcbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBjbGFzc0Zyb20gPSByZXF1aXJlKCcuLi8uLi91dGlsL2luaGVyaXQnKTtcbnZhciBJZGVudGl0eVN0cmF0ZWd5ID0gcmVxdWlyZSgnLi9ub25lLXN0cmF0ZWd5Jyk7XG52YXIgU3RvcmFnZUZhY3RvcnkgPSByZXF1aXJlKCcuLi8uLi9zdG9yZS9zdG9yZS1mYWN0b3J5Jyk7XG52YXIgU3RhdGVBcGkgPSByZXF1aXJlKCcuLi8uLi9zZXJ2aWNlL3N0YXRlLWFwaS1hZGFwdGVyJyk7XG52YXIgQXV0aE1hbmFnZXIgPSByZXF1aXJlKCcuLi9hdXRoLW1hbmFnZXInKTtcblxudmFyIGtleU5hbWVzID0gcmVxdWlyZSgnLi4va2V5LW5hbWVzJyk7XG5cbnZhciBkZWZhdWx0cyA9IHtcbiAgICBzdG9yZToge1xuICAgICAgICBzeW5jaHJvbm91czogdHJ1ZVxuICAgIH1cbn07XG5cbnZhciBTdHJhdGVneSA9IGNsYXNzRnJvbShJZGVudGl0eVN0cmF0ZWd5LCB7XG4gICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uIFN0cmF0ZWd5KHJ1blNlcnZpY2UsIG9wdGlvbnMpIHtcbiAgICAgICAgdGhpcy5ydW4gPSBydW5TZXJ2aWNlO1xuICAgICAgICB0aGlzLm9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgZGVmYXVsdHMsIG9wdGlvbnMpO1xuICAgICAgICB0aGlzLnJ1bk9wdGlvbnMgPSB0aGlzLm9wdGlvbnMucnVuO1xuICAgICAgICB0aGlzLl9zdG9yZSA9IG5ldyBTdG9yYWdlRmFjdG9yeSh0aGlzLm9wdGlvbnMuc3RvcmUpO1xuICAgICAgICB0aGlzLnN0YXRlQXBpID0gbmV3IFN0YXRlQXBpKCk7XG4gICAgICAgIHRoaXMuX2F1dGggPSBuZXcgQXV0aE1hbmFnZXIoKTtcblxuICAgICAgICB0aGlzLl9sb2FkQW5kQ2hlY2sgPSB0aGlzLl9sb2FkQW5kQ2hlY2suYmluZCh0aGlzKTtcbiAgICAgICAgdGhpcy5fcmVzdG9yZVJ1biA9IHRoaXMuX3Jlc3RvcmVSdW4uYmluZCh0aGlzKTtcbiAgICAgICAgdGhpcy5fZ2V0QWxsUnVucyA9IHRoaXMuX2dldEFsbFJ1bnMuYmluZCh0aGlzKTtcbiAgICAgICAgdGhpcy5fbG9hZFJ1biA9IHRoaXMuX2xvYWRSdW4uYmluZCh0aGlzKTtcbiAgICB9LFxuXG4gICAgcmVzZXQ6IGZ1bmN0aW9uIChydW5TZXJ2aWNlT3B0aW9ucykge1xuICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuX2F1dGguZ2V0Q3VycmVudFVzZXJTZXNzaW9uSW5mbygpO1xuICAgICAgICB2YXIgb3B0ID0gJC5leHRlbmQoe1xuICAgICAgICAgICAgc2NvcGU6IHsgZ3JvdXA6IHNlc3Npb24uZ3JvdXBOYW1lIH1cbiAgICAgICAgfSwgdGhpcy5ydW5PcHRpb25zKTtcblxuICAgICAgICByZXR1cm4gdGhpcy5ydW5cbiAgICAgICAgICAgIC5jcmVhdGUob3B0LCBydW5TZXJ2aWNlT3B0aW9ucylcbiAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgICAgICAgICAgICBydW4uZnJlc2hseUNyZWF0ZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIHJldHVybiBydW47XG4gICAgICAgICAgICB9KTtcbiAgICB9LFxuXG4gICAgZ2V0UnVuOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9nZXRBbGxSdW5zKClcbiAgICAgICAgICAgIC50aGVuKHRoaXMuX2xvYWRBbmRDaGVjayk7XG4gICAgfSxcblxuICAgIF9nZXRBbGxSdW5zOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBzZXNzaW9uID0gSlNPTi5wYXJzZSh0aGlzLl9zdG9yZS5nZXQoa2V5TmFtZXMuRVBJX1NFU1NJT05fS0VZKSB8fCAne30nKTtcbiAgICAgICAgcmV0dXJuIHRoaXMucnVuLnF1ZXJ5KHtcbiAgICAgICAgICAgICd1c2VyLmlkJzogc2Vzc2lvbi51c2VySWQgfHwgJzAwMDAnLFxuICAgICAgICAgICAgJ3Njb3BlLmdyb3VwJzogc2Vzc2lvbi5ncm91cE5hbWVcbiAgICAgICAgfSk7XG4gICAgfSxcblxuICAgIF9sb2FkQW5kQ2hlY2s6IGZ1bmN0aW9uIChydW5zKSB7XG4gICAgICAgIGlmICghcnVucyB8fCAhcnVucy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnJlc2V0KCk7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgZGF0ZUNvbXAgPSBmdW5jdGlvbiAoYSwgYikgeyByZXR1cm4gbmV3IERhdGUoYi5kYXRlKSAtIG5ldyBEYXRlKGEuZGF0ZSk7IH07XG4gICAgICAgIHZhciBsYXRlc3RSdW4gPSBydW5zLnNvcnQoZGF0ZUNvbXApWzBdO1xuICAgICAgICB2YXIgbWUgPSB0aGlzO1xuICAgICAgICB2YXIgc2hvdWxkUmVwbGF5ID0gZmFsc2U7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMucnVuLmxvYWQobGF0ZXN0UnVuLmlkLCBudWxsLCB7XG4gICAgICAgICAgICBzdWNjZXNzOiBmdW5jdGlvbiAocnVuLCBtc2csIGhlYWRlcnMpIHtcbiAgICAgICAgICAgICAgICBzaG91bGRSZXBsYXkgPSBoZWFkZXJzLmdldFJlc3BvbnNlSGVhZGVyKCdwcmFnbWEnKSA9PT0gJ3BlcnNpc3RlbnQnO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KS50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgICAgICAgIHJldHVybiBzaG91bGRSZXBsYXkgPyBtZS5fcmVzdG9yZVJ1bihydW4uaWQpIDogcnVuO1xuICAgICAgICB9KTtcbiAgICB9LFxuXG4gICAgX3Jlc3RvcmVSdW46IGZ1bmN0aW9uIChydW5JZCkge1xuICAgICAgICB2YXIgbWUgPSB0aGlzO1xuICAgICAgICByZXR1cm4gdGhpcy5zdGF0ZUFwaS5yZXBsYXkoeyBydW5JZDogcnVuSWQgfSlcbiAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChyZXNwKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG1lLl9sb2FkUnVuKHJlc3AucnVuKTtcbiAgICAgICAgICAgIH0pO1xuICAgIH0sXG5cbiAgICBfbG9hZFJ1bjogZnVuY3Rpb24gKGlkLCBvcHRpb25zKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJ1bi5sb2FkKGlkLCBudWxsLCBvcHRpb25zKTtcbiAgICB9XG5cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFN0cmF0ZWd5O1xuIiwibW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgJ25ldy1pZi1pbml0aWFsaXplZCc6IHJlcXVpcmUoJy4vbmV3LWlmLWluaXRpYWxpemVkLXN0cmF0ZWd5JyksXG4gICAgJ25ldy1pZi1wZXJzaXN0ZWQnOiByZXF1aXJlKCcuL25ldy1pZi1wZXJzaXN0ZWQtc3RyYXRlZ3knKSxcbiAgICAnbmV3LWlmLW1pc3NpbmcnOiByZXF1aXJlKCcuL25ldy1pZi1taXNzaW5nLXN0cmF0ZWd5JyksXG4gICAgJ2Fsd2F5cy1uZXcnOiByZXF1aXJlKCcuL2Fsd2F5cy1uZXctc3RyYXRlZ3knKSxcbiAgICBtdWx0aXBsYXllcjogcmVxdWlyZSgnLi9tdWx0aXBsYXllci1zdHJhdGVneScpLFxuICAgICdwZXJzaXN0ZW50LXNpbmdsZS1wbGF5ZXInOiByZXF1aXJlKCcuL3BlcnNpc3RlbnQtc2luZ2xlLXBsYXllci1zdHJhdGVneScpLFxuICAgIG5vbmU6IHJlcXVpcmUoJy4vbm9uZS1zdHJhdGVneScpXG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIFJ1blNlcnZpY2UgPSByZXF1aXJlKCcuLi9zZXJ2aWNlL3J1bi1hcGktc2VydmljZScpO1xuXG52YXIgZGVmYXVsdHMgPSB7XG4gICAgdmFsaWRGaWx0ZXI6IHsgc2F2ZWQ6IHRydWUgfVxufTtcblxuZnVuY3Rpb24gU2NlbmFyaW9NYW5hZ2VyKG9wdGlvbnMpIHtcbiAgICB0aGlzLm9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgZGVmYXVsdHMsIG9wdGlvbnMpO1xuICAgIHRoaXMucnVuU2VydmljZSA9IHRoaXMub3B0aW9ucy5ydW4gfHwgbmV3IFJ1blNlcnZpY2UodGhpcy5vcHRpb25zKTtcbn1cblxuU2NlbmFyaW9NYW5hZ2VyLnByb3RvdHlwZSA9IHtcbiAgICBnZXRSdW5zOiBmdW5jdGlvbiAoZmlsdGVyKSB7XG4gICAgICAgIHRoaXMuZmlsdGVyID0gJC5leHRlbmQodHJ1ZSwge30sIHRoaXMub3B0aW9ucy52YWxpZEZpbHRlciwgZmlsdGVyKTtcbiAgICAgICAgcmV0dXJuIHRoaXMucnVuU2VydmljZS5xdWVyeSh0aGlzLmZpbHRlcik7XG4gICAgfSxcblxuICAgIGxvYWRWYXJpYWJsZXM6IGZ1bmN0aW9uICh2YXJzKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJ1blNlcnZpY2UucXVlcnkodGhpcy5maWx0ZXIsIHsgaW5jbHVkZTogdmFycyB9KTtcbiAgICB9LFxuXG4gICAgc2F2ZTogZnVuY3Rpb24gKHJ1biwgbWV0YSkge1xuICAgICAgICByZXR1cm4gdGhpcy5fZ2V0U2VydmljZShydW4pLnNhdmUoJC5leHRlbmQodHJ1ZSwge30sIHsgc2F2ZWQ6IHRydWUgfSwgbWV0YSkpO1xuICAgIH0sXG5cbiAgICBhcmNoaXZlOiBmdW5jdGlvbiAocnVuKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9nZXRTZXJ2aWNlKHJ1bikuc2F2ZSh7IHNhdmVkOiBmYWxzZSB9KTtcbiAgICB9LFxuXG4gICAgX2dldFNlcnZpY2U6IGZ1bmN0aW9uIChydW4pIHtcbiAgICAgICAgaWYgKHR5cGVvZiBydW4gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICByZXR1cm4gbmV3IFJ1blNlcnZpY2UoJC5leHRlbmQodHJ1ZSwge30sIHRoaXMub3B0aW9ucywgeyBmaWx0ZXI6IHJ1biB9KSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodHlwZW9mIHJ1biA9PT0gJ29iamVjdCcgJiYgcnVuIGluc3RhbmNlb2YgUnVuU2VydmljZSkge1xuICAgICAgICAgICAgcmV0dXJuIHJ1bjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignU2F2ZSBtZXRob2QgcmVxdWlyZXMgYSBydW4gc2VydmljZSBvciBhIHJ1bklkJyk7XG4gICAgfSxcblxuICAgIGdldFJ1bjogZnVuY3Rpb24gKHJ1bklkKSB7XG4gICAgICAgIHJldHVybiBuZXcgUnVuU2VydmljZSgkLmV4dGVuZCh0cnVlLCB7fSwgdGhpcy5vcHRpb25zLCB7IGZpbHRlcjogcnVuSWQgfSkpO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU2NlbmFyaW9NYW5hZ2VyO1xuXG4iLCIndXNlIHN0cmljdCc7XG5cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgcmVzZXQ6IGZ1bmN0aW9uIChwYXJhbXMsIG9wdGlvbnMsIG1hbmFnZXIpIHtcbiAgICAgICAgcmV0dXJuIG1hbmFnZXIucmVzZXQob3B0aW9ucyk7XG4gICAgfVxufTtcbiIsIi8qKlxuKiAjIyBXb3JsZCBNYW5hZ2VyXG4qXG4qIEFzIGRpc2N1c3NlZCB1bmRlciB0aGUgW1dvcmxkIEFQSSBBZGFwdGVyXSguLi93b3JsZC1hcGktYWRhcHRlci8pLCBhIFtydW5dKC4uLy4uLy4uL2dsb3NzYXJ5LyNydW4pIGlzIGEgY29sbGVjdGlvbiBvZiBlbmQgdXNlciBpbnRlcmFjdGlvbnMgd2l0aCBhIHByb2plY3QgYW5kIGl0cyBtb2RlbC4gRm9yIGJ1aWxkaW5nIG11bHRpcGxheWVyIHNpbXVsYXRpb25zIHlvdSB0eXBpY2FsbHkgd2FudCBtdWx0aXBsZSBlbmQgdXNlcnMgdG8gc2hhcmUgdGhlIHNhbWUgc2V0IG9mIGludGVyYWN0aW9ucywgYW5kIHdvcmsgd2l0aGluIGEgY29tbW9uIHN0YXRlLiBFcGljZW50ZXIgYWxsb3dzIHlvdSB0byBjcmVhdGUgXCJ3b3JsZHNcIiB0byBoYW5kbGUgc3VjaCBjYXNlcy5cbipcbiogVGhlIFdvcmxkIE1hbmFnZXIgcHJvdmlkZXMgYW4gZWFzeSB3YXkgdG8gdHJhY2sgYW5kIGFjY2VzcyB0aGUgY3VycmVudCB3b3JsZCBhbmQgcnVuIGZvciBwYXJ0aWN1bGFyIGVuZCB1c2Vycy4gSXQgaXMgdHlwaWNhbGx5IHVzZWQgaW4gcGFnZXMgdGhhdCBlbmQgdXNlcnMgd2lsbCBpbnRlcmFjdCB3aXRoLiAoVGhlIHJlbGF0ZWQgW1dvcmxkIEFQSSBBZGFwdGVyXSguLi93b3JsZC1hcGktYWRhcHRlci8pIGhhbmRsZXMgY3JlYXRpbmcgbXVsdGlwbGF5ZXIgd29ybGRzLCBhbmQgYWRkaW5nIGFuZCByZW1vdmluZyBlbmQgdXNlcnMgYW5kIHJ1bnMgZnJvbSBhIHdvcmxkLiBCZWNhdXNlIG9mIHRoaXMsIHR5cGljYWxseSB0aGUgV29ybGQgQWRhcHRlciBpcyB1c2VkIGZvciBmYWNpbGl0YXRvciBwYWdlcyBpbiB5b3VyIHByb2plY3QuKVxuKlxuKiAjIyMgVXNpbmcgdGhlIFdvcmxkIE1hbmFnZXJcbipcbiogVG8gdXNlIHRoZSBXb3JsZCBNYW5hZ2VyLCBpbnN0YW50aWF0ZSBpdC4gVGhlbiwgbWFrZSBjYWxscyB0byBhbnkgb2YgdGhlIG1ldGhvZHMgeW91IG5lZWQuXG4qXG4qIFdoZW4geW91IGluc3RhbnRpYXRlIGEgV29ybGQgTWFuYWdlciwgdGhlIHdvcmxkJ3MgYWNjb3VudCBpZCwgcHJvamVjdCBpZCwgYW5kIGdyb3VwIGFyZSBhdXRvbWF0aWNhbGx5IHRha2VuIGZyb20gdGhlIHNlc3Npb24gKHRoYW5rcyB0byB0aGUgW0F1dGhlbnRpY2F0aW9uIFNlcnZpY2VdKC4uL2F1dGgtYXBpLXNlcnZpY2UpKS5cbipcbiogTm90ZSB0aGF0IHRoZSBXb3JsZCBNYW5hZ2VyIGRvZXMgKm5vdCogY3JlYXRlIHdvcmxkcyBhdXRvbWF0aWNhbGx5LiAoVGhpcyBpcyBkaWZmZXJlbnQgdGhhbiB0aGUgW1J1biBNYW5hZ2VyXSguLi9ydW4tbWFuYWdlcikuKSBIb3dldmVyLCB5b3UgY2FuIHBhc3MgaW4gc3BlY2lmaWMgb3B0aW9ucyB0byBhbnkgcnVucyBjcmVhdGVkIGJ5IHRoZSBtYW5hZ2VyLCB1c2luZyBhIGBydW5gIG9iamVjdC5cbipcbiogVGhlIHBhcmFtZXRlcnMgZm9yIGNyZWF0aW5nIGEgV29ybGQgTWFuYWdlciBhcmU6XG4qXG4qICAgKiBgYWNjb3VudGA6IFRoZSAqKlRlYW0gSUQqKiBpbiB0aGUgRXBpY2VudGVyIHVzZXIgaW50ZXJmYWNlIGZvciB0aGlzIHByb2plY3QuXG4qICAgKiBgcHJvamVjdGA6IFRoZSAqKlByb2plY3QgSUQqKiBmb3IgdGhpcyBwcm9qZWN0LlxuKiAgICogYGdyb3VwYDogVGhlICoqR3JvdXAgTmFtZSoqIGZvciB0aGlzIHdvcmxkLlxuKiAgICogYHJ1bmA6IE9wdGlvbnMgdG8gdXNlIHdoZW4gY3JlYXRpbmcgbmV3IHJ1bnMgd2l0aCB0aGUgbWFuYWdlciwgZS5nLiBgcnVuOiB7IGZpbGVzOiBbJ2RhdGEueGxzJ10gfWAuXG4qICAgKiBgcnVuLm1vZGVsYDogVGhlIG5hbWUgb2YgdGhlIHByaW1hcnkgbW9kZWwgZmlsZSBmb3IgdGhpcyBwcm9qZWN0LiBSZXF1aXJlZCBpZiB5b3UgaGF2ZSBub3QgYWxyZWFkeSBwYXNzZWQgaXQgaW4gYXMgcGFydCBvZiB0aGUgYG9wdGlvbnNgIHBhcmFtZXRlciBmb3IgYW4gZW5jbG9zaW5nIGNhbGwuXG4qXG4qIEZvciBleGFtcGxlOlxuKlxuKiAgICAgICB2YXIgd01nciA9IG5ldyBGLm1hbmFnZXIuV29ybGRNYW5hZ2VyKHtcbiogICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuKiAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuKiAgICAgICAgICBydW46IHsgbW9kZWw6ICdzdXBwbHktY2hhaW4ucHknIH0sXG4qICAgICAgICAgIGdyb3VwOiAndGVhbTEnXG4qICAgICAgIH0pO1xuKlxuKiAgICAgICB3TWdyLmdldEN1cnJlbnRSdW4oKTtcbiovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIFdvcmxkQXBpID0gcmVxdWlyZSgnLi4vc2VydmljZS93b3JsZC1hcGktYWRhcHRlcicpO1xudmFyIFJ1bk1hbmFnZXIgPSByZXF1aXJlKCcuL3J1bi1tYW5hZ2VyJyk7XG52YXIgQXV0aE1hbmFnZXIgPSByZXF1aXJlKCcuL2F1dGgtbWFuYWdlcicpO1xudmFyIHdvcmxkQXBpO1xuXG5mdW5jdGlvbiBidWlsZFN0cmF0ZWd5KHdvcmxkSWQsIGR0ZCkge1xuXG4gICAgcmV0dXJuIGZ1bmN0aW9uIEN0b3IocnVuU2VydmljZSwgb3B0aW9ucykge1xuICAgICAgICB0aGlzLnJ1blNlcnZpY2UgPSBydW5TZXJ2aWNlO1xuICAgICAgICB0aGlzLm9wdGlvbnMgPSBvcHRpb25zO1xuXG4gICAgICAgICQuZXh0ZW5kKHRoaXMsIHtcbiAgICAgICAgICAgIHJlc2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdub3QgaW1wbGVtZW50ZC4gTmVlZCBhcGkgY2hhbmdlcycpO1xuICAgICAgICAgICAgfSxcblxuICAgICAgICAgICAgZ2V0UnVuOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgICAgICAgICAvL2dldCBvciBjcmVhdGUhXG4gICAgICAgICAgICAgICAgLy8gTW9kZWwgaXMgcmVxdWlyZWQgaW4gdGhlIG9wdGlvbnNcbiAgICAgICAgICAgICAgICB2YXIgbW9kZWwgPSB0aGlzLm9wdGlvbnMucnVuLm1vZGVsIHx8IHRoaXMub3B0aW9ucy5tb2RlbDtcbiAgICAgICAgICAgICAgICByZXR1cm4gd29ybGRBcGkuZ2V0Q3VycmVudFJ1bklkKHsgbW9kZWw6IG1vZGVsLCBmaWx0ZXI6IHdvcmxkSWQgfSlcbiAgICAgICAgICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKHJ1bklkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWUucnVuU2VydmljZS5sb2FkKHJ1bklkKTtcbiAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKHJ1bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgZHRkLnJlc29sdmVXaXRoKG1lLCBbcnVuXSk7XG4gICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgIC5mYWlsKGR0ZC5yZWplY3QpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfTtcbn1cblxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgdGhpcy5vcHRpb25zID0gb3B0aW9ucyB8fCB7IHJ1bjoge30sIHdvcmxkOiB7fSB9O1xuXG4gICAgJC5leHRlbmQodHJ1ZSwgdGhpcy5vcHRpb25zLCB0aGlzLm9wdGlvbnMucnVuKTtcbiAgICAkLmV4dGVuZCh0cnVlLCB0aGlzLm9wdGlvbnMsIHRoaXMub3B0aW9ucy53b3JsZCk7XG5cbiAgICB3b3JsZEFwaSA9IG5ldyBXb3JsZEFwaSh0aGlzLm9wdGlvbnMpO1xuICAgIHRoaXMuX2F1dGggPSBuZXcgQXV0aE1hbmFnZXIoKTtcbiAgICB2YXIgbWUgPSB0aGlzO1xuXG4gICAgdmFyIGFwaSA9IHtcblxuICAgICAgICAvKipcbiAgICAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IHdvcmxkIChvYmplY3QpIGFuZCBhbiBpbnN0YW5jZSBvZiB0aGUgW1dvcmxkIEFQSSBBZGFwdGVyXSguLi93b3JsZC1hcGktYWRhcHRlci8pLlxuICAgICAgICAqXG4gICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgIHdNZ3IuZ2V0Q3VycmVudFdvcmxkKClcbiAgICAgICAgKiAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24od29ybGQsIHdvcmxkQWRhcHRlcikge1xuICAgICAgICAqICAgICAgICAgICAgICAgY29uc29sZS5sb2cod29ybGQuaWQpO1xuICAgICAgICAqICAgICAgICAgICAgICAgd29ybGRBZGFwdGVyLmdldEN1cnJlbnRSdW5JZCgpO1xuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHVzZXJJZCAoT3B0aW9uYWwpIFRoZSBpZCBvZiB0aGUgdXNlciB3aG9zZSB3b3JsZCBpcyBiZWluZyBhY2Nlc3NlZC4gRGVmYXVsdHMgdG8gdGhlIHVzZXIgaW4gdGhlIGN1cnJlbnQgc2Vzc2lvbi5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gZ3JvdXBOYW1lIChPcHRpb25hbCkgVGhlIG5hbWUgb2YgdGhlIGdyb3VwIHdob3NlIHdvcmxkIGlzIGJlaW5nIGFjY2Vzc2VkLiBEZWZhdWx0cyB0byB0aGUgZ3JvdXAgZm9yIHRoZSB1c2VyIGluIHRoZSBjdXJyZW50IHNlc3Npb24uXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZ2V0Q3VycmVudFdvcmxkOiBmdW5jdGlvbiAodXNlcklkLCBncm91cE5hbWUpIHtcbiAgICAgICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5fYXV0aC5nZXRDdXJyZW50VXNlclNlc3Npb25JbmZvKCk7XG4gICAgICAgICAgICBpZiAoIXVzZXJJZCkge1xuICAgICAgICAgICAgICAgIHVzZXJJZCA9IHNlc3Npb24udXNlcklkO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCFncm91cE5hbWUpIHtcbiAgICAgICAgICAgICAgICBncm91cE5hbWUgPSBzZXNzaW9uLmdyb3VwTmFtZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB3b3JsZEFwaS5nZXRDdXJyZW50V29ybGRGb3JVc2VyKHVzZXJJZCwgZ3JvdXBOYW1lKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IHJ1biAob2JqZWN0KSBhbmQgYW4gaW5zdGFuY2Ugb2YgdGhlIFtSdW4gQVBJIFNlcnZpY2VdKC4uL3J1bi1hcGktc2VydmljZS8pLlxuICAgICAgICAqXG4gICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgIHdNZ3IuZ2V0Q3VycmVudFJ1bih7bW9kZWw6ICdteU1vZGVsLnB5J30pXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHJ1biwgcnVuU2VydmljZSkge1xuICAgICAgICAqICAgICAgICAgICAgICAgY29uc29sZS5sb2cocnVuLmlkKTtcbiAgICAgICAgKiAgICAgICAgICAgICAgIHJ1blNlcnZpY2UuZG8oJ3N0YXJ0R2FtZScpO1xuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IG1vZGVsIChPcHRpb25hbCkgVGhlIG5hbWUgb2YgdGhlIG1vZGVsIGZpbGUuIFJlcXVpcmVkIGlmIG5vdCBhbHJlYWR5IHBhc3NlZCBpbiBhcyBgcnVuLm1vZGVsYCB3aGVuIHRoZSBXb3JsZCBNYW5hZ2VyIGlzIGNyZWF0ZWQuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZ2V0Q3VycmVudFJ1bjogZnVuY3Rpb24gKG1vZGVsKSB7XG4gICAgICAgICAgICB2YXIgZHRkID0gJC5EZWZlcnJlZCgpO1xuICAgICAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLl9hdXRoLmdldEN1cnJlbnRVc2VyU2Vzc2lvbkluZm8oKTtcbiAgICAgICAgICAgIHZhciBjdXJVc2VySWQgPSBzZXNzaW9uLnVzZXJJZDtcbiAgICAgICAgICAgIHZhciBjdXJHcm91cE5hbWUgPSBzZXNzaW9uLmdyb3VwTmFtZTtcblxuICAgICAgICAgICAgZnVuY3Rpb24gZ2V0QW5kUmVzdG9yZUxhdGVzdFJ1bih3b3JsZCkge1xuICAgICAgICAgICAgICAgIGlmICghd29ybGQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGR0ZC5yZWplY3QoeyBlcnJvcjogJ1RoZSB1c2VyIGlzIG5vdCBwYXJ0IG9mIGFueSB3b3JsZCEnIH0pO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHZhciBjdXJyZW50V29ybGRJZCA9IHdvcmxkLmlkO1xuICAgICAgICAgICAgICAgIHZhciBydW5PcHRzID0gJC5leHRlbmQodHJ1ZSwgbWUub3B0aW9ucywgeyBtb2RlbDogbW9kZWwgfSk7XG4gICAgICAgICAgICAgICAgdmFyIHN0cmF0ZWd5ID0gYnVpbGRTdHJhdGVneShjdXJyZW50V29ybGRJZCwgZHRkKTtcbiAgICAgICAgICAgICAgICB2YXIgb3B0ID0gJC5leHRlbmQodHJ1ZSwge30sIHtcbiAgICAgICAgICAgICAgICAgICAgc3RyYXRlZ3k6IHN0cmF0ZWd5LFxuICAgICAgICAgICAgICAgICAgICBydW46IHJ1bk9wdHNcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB2YXIgcm0gPSBuZXcgUnVuTWFuYWdlcihvcHQpO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHJtLmdldFJ1bigpXG4gICAgICAgICAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGR0ZC5yZXNvbHZlKHJ1biwgcm0ucnVuU2VydmljZSwgcm0pO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy5nZXRDdXJyZW50V29ybGQoY3VyVXNlcklkLCBjdXJHcm91cE5hbWUpXG4gICAgICAgICAgICAgICAgLnRoZW4oZ2V0QW5kUmVzdG9yZUxhdGVzdFJ1bik7XG5cbiAgICAgICAgICAgIHJldHVybiBkdGQucHJvbWlzZSgpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgICQuZXh0ZW5kKHRoaXMsIGFwaSk7XG59O1xuIiwiLyoqXG4gKiAjIyBGaWxlIEFQSSBTZXJ2aWNlXG4gKlxuICogVGhlIEZpbGUgQVBJIFNlcnZpY2UgYWxsb3dzIHlvdSB0byB1cGxvYWQgYW5kIGRvd25sb2FkIGZpbGVzIGRpcmVjdGx5IG9udG8gRXBpY2VudGVyLCBhbmFsb2dvdXMgdG8gdXNpbmcgdGhlIEZpbGUgTWFuYWdlciBVSSBpbiBFcGljZW50ZXIgZGlyZWN0bHkgb3IgU0ZUUGluZyBmaWxlcyBpbi4gSXQgaXMgYmFzZWQgb24gdGhlIEVwaWNlbnRlciBGaWxlIEFQSS5cbiAqXG4gKiBUaGUgQXNzZXQgQVBJIFNlcnZpY2UgKGh0dHBzOi8vZm9yaW8uY29tL2VwaWNlbnRlci9kb2NzL3B1YmxpYy9hcGlfYWRhcHRlcnMvZ2VuZXJhdGVkL2Fzc2V0LWFwaS1hZGFwdGVyLykgaXMgdHlwaWNhbGx5IHVzZWQgZm9yIGFsbCBwcm9qZWN0IHVzZSBjYXNlcywgYW5kIGl0J3MgdW5saWtlbHkgdGhpcyBGaWxlIFNlcnZpY2Ugd2lsbCBiZSB1c2VkIGRpcmVjdGx5IGV4Y2VwdCBieSBBZG1pbiB0b29scyAoZS5nLiBGbG93IEluc3BlY3RvcikuXG4gKlxuICogUGFydGlhbGx5IGltcGxlbWVudGVkLlxuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEZvciBwcm9qZWN0cyB0aGF0IHJlcXVpcmUgYXV0aGVudGljYXRpb24sIHBhc3MgaW4gdGhlIHVzZXIgYWNjZXNzIHRva2VuIChkZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcpLiBJZiB0aGUgdXNlciBpcyBhbHJlYWR5IGxvZ2dlZCBpbiB0byBFcGljZW50ZXIsIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiBpcyBhbHJlYWR5IHNldCBpbiBhIGNvb2tpZSBhbmQgYXV0b21hdGljYWxseSBsb2FkZWQgZnJvbSB0aGVyZS4gKFNlZSBbbW9yZSBiYWNrZ3JvdW5kIG9uIGFjY2VzcyB0b2tlbnNdKC4uLy4uLy4uL3Byb2plY3RfYWNjZXNzLykpLlxuICAgICAgICAgKiBAc2VlIFtBdXRoZW50aWNhdGlvbiBBUEkgU2VydmljZV0oLi4vYXV0aC1hcGktc2VydmljZS8pIGZvciBnZXR0aW5nIHRva2Vucy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHRva2VuOiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBhY2NvdW50IGlkLiBJbiB0aGUgRXBpY2VudGVyIFVJLCB0aGlzIGlzIHRoZSAqKlRlYW0gSUQqKiAoZm9yIHRlYW0gcHJvamVjdHMpIG9yICoqVXNlciBJRCoqIChmb3IgcGVyc29uYWwgcHJvamVjdHMpLiBEZWZhdWx0cyB0byB1bmRlZmluZWQuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBhY2NvdW50OiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBwcm9qZWN0IGlkLiBEZWZhdWx0cyB0byB1bmRlZmluZWQuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBwcm9qZWN0OiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBmb2xkZXIgdHlwZS4gIE9uZSBvZiBgbW9kZWxgIHwgYHN0YXRpY2AgfCBgbm9kZWAuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBmb2xkZXJUeXBlOiAnc3RhdGljJyxcblxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBPcHRpb25zIHRvIHBhc3Mgb24gdG8gdGhlIHVuZGVybHlpbmcgdHJhbnNwb3J0IGxheWVyLiBBbGwganF1ZXJ5LmFqYXggb3B0aW9ucyBhdCBodHRwOi8vYXBpLmpxdWVyeS5jb20valF1ZXJ5LmFqYXgvIGFyZSBhdmFpbGFibGUuIERlZmF1bHRzIHRvIGVtcHR5IG9iamVjdC5cbiAgICAgICAgICogQHR5cGUge09iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIHRyYW5zcG9ydDoge31cbiAgICB9O1xuXG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgY29uZmlnKTtcbiAgICB2YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2Uoc2VydmljZU9wdGlvbnMpLmdldCgnc2VydmVyJyk7XG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLmFjY291bnQpIHtcbiAgICAgICAgdXJsQ29uZmlnLmFjY291bnRQYXRoID0gc2VydmljZU9wdGlvbnMuYWNjb3VudDtcbiAgICB9XG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnByb2plY3QpIHtcbiAgICAgICAgdXJsQ29uZmlnLnByb2plY3RQYXRoID0gc2VydmljZU9wdGlvbnMucHJvamVjdDtcbiAgICB9XG5cbiAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LCB7XG4gICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoJ2ZpbGUnKVxuICAgIH0pO1xuXG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnRva2VuKSB7XG4gICAgICAgIGh0dHBPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KGh0dHBPcHRpb25zKTtcblxuICAgIGZ1bmN0aW9uIHVwbG9hZEJvZHkoZmlsZU5hbWUsIGNvbnRlbnRzKSB7XG4gICAgICAgIHZhciBib3VuZGFyeSA9ICctLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS03ZGEyNGYyZTUwMDQ2JztcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgYm9keTogJy0tJyArIGJvdW5kYXJ5ICsgJ1xcclxcbicgK1xuICAgICAgICAgICAgICAgICAgICAnQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPVwiZmlsZVwiOycgK1xuICAgICAgICAgICAgICAgICAgICAnZmlsZW5hbWU9XCInICsgZmlsZU5hbWUgKyAnXCJcXHJcXG4nICtcbiAgICAgICAgICAgICAgICAgICAgJ0NvbnRlbnQtdHlwZTogdGV4dC9odG1sXFxyXFxuXFxyXFxuJyArXG4gICAgICAgICAgICAgICAgICAgIGNvbnRlbnRzICsgJ1xcclxcbicgK1xuICAgICAgICAgICAgICAgICAgICAnLS0nICsgYm91bmRhcnkgKyAnLS0nLFxuICAgICAgICAgICAgYm91bmRhcnk6IGJvdW5kYXJ5XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdXBsb2FkRmlsZU9wdGlvbnMoZmlsZVBhdGgsIGNvbnRlbnRzLCBvcHRpb25zKSB7XG4gICAgICAgIGZpbGVQYXRoID0gZmlsZVBhdGguc3BsaXQoJy8nKTtcbiAgICAgICAgdmFyIGZpbGVOYW1lID0gZmlsZVBhdGgucG9wKCk7XG4gICAgICAgIGZpbGVQYXRoID0gZmlsZVBhdGguam9pbignLycpO1xuICAgICAgICB2YXIgcGF0aCA9IHNlcnZpY2VPcHRpb25zLmZvbGRlclR5cGUgKyAnLycgKyBmaWxlUGF0aDtcbiAgICAgICAgdmFyIHVwbG9hZCA9IHVwbG9hZEJvZHkoZmlsZU5hbWUsIGNvbnRlbnRzKTtcblxuICAgICAgICByZXR1cm4gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zLCB7XG4gICAgICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKCdmaWxlJykgKyBwYXRoLFxuICAgICAgICAgICAgZGF0YTogdXBsb2FkLmJvZHksXG4gICAgICAgICAgICBjb250ZW50VHlwZTogJ211bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PScgKyB1cGxvYWQuYm91bmRhcnlcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgdmFyIHB1YmxpY0FzeW5jQVBJID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogR2V0IGEgZGlyZWN0b3J5IGxpc3RpbmcsIG9yIGNvbnRlbnRzIG9mIGEgZmlsZS5cbiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IGZpbGVQYXRoICBQYXRoIHRvIHRoZSBmaWxlXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBnZXRDb250ZW50czogZnVuY3Rpb24gKGZpbGVQYXRoLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcGF0aCA9IHNlcnZpY2VPcHRpb25zLmZvbGRlclR5cGUgKyAnLycgKyBmaWxlUGF0aDtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucywge1xuICAgICAgICAgICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoJ2ZpbGUnKSArIHBhdGhcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KCcnLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJlcGxhY2VzIHRoZSBmaWxlIGF0IHRoZSBnaXZlbiBmaWxlIHBhdGguXG4gICAgICAgICAqIEBwYXJhbSAge1N0cmluZ30gZmlsZVBhdGggUGF0aCB0byB0aGUgZmlsZVxuICAgICAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IGNvbnRlbnRzIENvbnRlbnRzIHRvIHdyaXRlIHRvIGZpbGVcbiAgICAgICAgICogQHBhcmFtICB7T2JqZWN0fSBvcHRpb25zICAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICByZXBsYWNlOiBmdW5jdGlvbiAoZmlsZVBhdGgsIGNvbnRlbnRzLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSB1cGxvYWRGaWxlT3B0aW9ucyhmaWxlUGF0aCwgY29udGVudHMsIG9wdGlvbnMpO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wdXQoaHR0cE9wdGlvbnMuZGF0YSwgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDcmVhdGVzIGEgZmlsZSBpbiB0aGUgZ2l2ZW4gZmlsZSBwYXRoLlxuICAgICAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IGZpbGVQYXRoIFBhdGggdG8gdGhlIGZpbGVcbiAgICAgICAgICogQHBhcmFtICB7U3RyaW5nfSBjb250ZW50cyBDb250ZW50cyB0byB3cml0ZSB0byBmaWxlXG4gICAgICAgICAqIEBwYXJhbSAge0Jvb2xlYW59IHJlcGxhY2VFeGlzdGluZyBSZXBsYWNlIGZpbGUgaWYgaXQgYWxyZWFkeSBleGlzdHM7IGRlZmF1bHRzIHRvIGZhbHNlXG4gICAgICAgICAqIEBwYXJhbSAge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBjcmVhdGU6IGZ1bmN0aW9uIChmaWxlUGF0aCwgY29udGVudHMsIHJlcGxhY2VFeGlzdGluZywgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gdXBsb2FkRmlsZU9wdGlvbnMoZmlsZVBhdGgsIGNvbnRlbnRzLCBvcHRpb25zKTtcbiAgICAgICAgICAgIHZhciBwcm9tID0gaHR0cC5wb3N0KGh0dHBPcHRpb25zLmRhdGEsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgICAgICBpZiAocmVwbGFjZUV4aXN0aW5nID09PSB0cnVlKSB7XG4gICAgICAgICAgICAgICAgcHJvbSA9IHByb20udGhlbihudWxsLCBmdW5jdGlvbiAoeGhyKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBjb25mbGljdFN0YXR1cyA9IDQwOTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHhoci5zdGF0dXMgPT09IGNvbmZsaWN0U3RhdHVzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWUucmVwbGFjZShmaWxlUGF0aCwgY29udGVudHMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gcHJvbTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmVtb3ZlcyB0aGUgZmlsZS5cbiAgICAgICAgICogQHBhcmFtICB7U3RyaW5nfSBmaWxlUGF0aCBQYXRoIHRvIHRoZSBmaWxlXG4gICAgICAgICAqIEBwYXJhbSAge09iamVjdH0gb3B0aW9ucyAgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgcmVtb3ZlOiBmdW5jdGlvbiAoZmlsZVBhdGgsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBwYXRoID0gc2VydmljZU9wdGlvbnMuZm9sZGVyVHlwZSArICcvJyArIGZpbGVQYXRoO1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zLCB7XG4gICAgICAgICAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aCgnZmlsZScpICsgcGF0aFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5kZWxldGUobnVsbCwgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZW5hbWVzIHRoZSBmaWxlLlxuICAgICAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IGZpbGVQYXRoIFBhdGggdG8gdGhlIGZpbGVcbiAgICAgICAgICogQHBhcmFtICB7U3RyaW5nfSBuZXdOYW1lICBOZXcgbmFtZSBvZiBmaWxlXG4gICAgICAgICAqIEBwYXJhbSAge09iamVjdH0gb3B0aW9ucyAgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgcmVuYW1lOiBmdW5jdGlvbiAoZmlsZVBhdGgsIG5ld05hbWUsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBwYXRoID0gc2VydmljZU9wdGlvbnMuZm9sZGVyVHlwZSArICcvJyArIGZpbGVQYXRoO1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zLCB7XG4gICAgICAgICAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aCgnZmlsZScpICsgcGF0aFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wYXRjaCh7IG5hbWU6IG5ld05hbWUgfSwgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FzeW5jQVBJKTtcbn07XG4iLCIvKipcbiAqICMjIEFzc2V0IEFQSSBBZGFwdGVyXG4gKlxuICogVGhlIEFzc2V0IEFQSSBBZGFwdGVyIGFsbG93cyB5b3UgdG8gc3RvcmUgYXNzZXRzIC0tIHJlc291cmNlcyBvciBmaWxlcyBvZiBhbnkga2luZCAtLSB1c2VkIGJ5IGEgcHJvamVjdCB3aXRoIGEgc2NvcGUgdGhhdCBpcyBzcGVjaWZpYyB0byBwcm9qZWN0LCBncm91cCwgb3IgZW5kIHVzZXIuXG4gKlxuICogQXNzZXRzIGFyZSB1c2VkIHdpdGggW3RlYW0gcHJvamVjdHNdKC4uLy4uLy4uL3Byb2plY3RfYWRtaW4vI3RlYW0pLiBPbmUgY29tbW9uIHVzZSBjYXNlIGlzIGhhdmluZyBlbmQgdXNlcnMgaW4gYSBbZ3JvdXBdKC4uLy4uLy4uL2dsb3NzYXJ5LyNncm91cHMpIG9yIGluIGEgW211bHRpcGxheWVyIHdvcmxkXSguLi8uLi8uLi9nbG9zc2FyeS8jd29ybGQpIHVwbG9hZCBkYXRhIC0tIHZpZGVvcyBjcmVhdGVkIGR1cmluZyBnYW1lIHBsYXksIHByb2ZpbGUgcGljdHVyZXMgZm9yIGN1c3RvbWl6aW5nIHRoZWlyIGV4cGVyaWVuY2UsIGV0Yy4gLS0gYXMgcGFydCBvZiBwbGF5aW5nIHRocm91Z2ggdGhlIHByb2plY3QuXG4gKlxuICogUmVzb3VyY2VzIGNyZWF0ZWQgdXNpbmcgdGhlIEFzc2V0IEFkYXB0ZXIgYXJlIHNjb3BlZDpcbiAqXG4gKiAgKiBQcm9qZWN0IGFzc2V0cyBhcmUgd3JpdGFibGUgb25seSBieSBbdGVhbSBtZW1iZXJzXSguLi8uLi8uLi9nbG9zc2FyeS8jdGVhbSksIHRoYXQgaXMsIEVwaWNlbnRlciBhdXRob3JzLlxuICogICogR3JvdXAgYXNzZXRzIGFyZSB3cml0YWJsZSBieSBhbnlvbmUgd2l0aCBhY2Nlc3MgdG8gdGhlIHByb2plY3QgdGhhdCBpcyBwYXJ0IG9mIHRoYXQgcGFydGljdWxhciBbZ3JvdXBdKC4uLy4uLy4uL2dsb3NzYXJ5LyNncm91cHMpLiBUaGlzIGluY2x1ZGVzIGFsbCBbdGVhbSBtZW1iZXJzXSguLi8uLi8uLi9nbG9zc2FyeS8jdGVhbSkgKEVwaWNlbnRlciBhdXRob3JzKSBhbmQgYW55IFtlbmQgdXNlcnNdKC4uLy4uLy4uL2dsb3NzYXJ5LyN1c2Vycykgd2hvIGFyZSBtZW1iZXJzIG9mIHRoZSBncm91cCAtLSBib3RoIGZhY2lsaXRhdG9ycyBhbmQgc3RhbmRhcmQgZW5kIHVzZXJzLlxuICogICogVXNlciBhc3NldHMgYXJlIHdyaXRhYmxlIGJ5IHRoZSBzcGVjaWZpYyBlbmQgdXNlciwgYW5kIGJ5IHRoZSBmYWNpbGl0YXRvciBvZiB0aGUgZ3JvdXAuXG4gKiAgKiBBbGwgYXNzZXRzIGFyZSByZWFkYWJsZSBieSBhbnlvbmUgd2l0aCB0aGUgZXhhY3QgVVJJLlxuICpcbiAqIFRvIHVzZSB0aGUgQXNzZXQgQWRhcHRlciwgaW5zdGFudGlhdGUgaXQgYW5kIHRoZW4gYWNjZXNzIHRoZSBtZXRob2RzIHByb3ZpZGVkLiBJbnN0YW50aWF0aW5nIHJlcXVpcmVzIHRoZSBhY2NvdW50IGlkICgqKlRlYW0gSUQqKiBpbiB0aGUgRXBpY2VudGVyIHVzZXIgaW50ZXJmYWNlKSBhbmQgcHJvamVjdCBpZCAoKipQcm9qZWN0IElEKiopLiBUaGUgZ3JvdXAgbmFtZSBpcyByZXF1aXJlZCBmb3IgYXNzZXRzIHdpdGggYSBncm91cCBzY29wZSwgYW5kIHRoZSBncm91cCBuYW1lIGFuZCB1c2VySWQgYXJlIHJlcXVpcmVkIGZvciBhc3NldHMgd2l0aCBhIHVzZXIgc2NvcGUuIElmIG5vdCBpbmNsdWRlZCwgdGhleSBhcmUgdGFrZW4gZnJvbSB0aGUgbG9nZ2VkIGluIHVzZXIncyBzZXNzaW9uIGluZm9ybWF0aW9uIGlmIG5lZWRlZC5cbiAqXG4gKiBXaGVuIGNyZWF0aW5nIGFuIGFzc2V0LCB5b3UgY2FuIHBhc3MgaW4gdGV4dCAoZW5jb2RlZCBkYXRhKSB0byB0aGUgYGNyZWF0ZSgpYCBjYWxsLiBBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIG1ha2UgdGhlIGBjcmVhdGUoKWAgY2FsbCBhcyBwYXJ0IG9mIGFuIEhUTUwgZm9ybSBhbmQgcGFzcyBpbiBhIGZpbGUgdXBsb2FkZWQgdmlhIHRoZSBmb3JtLlxuICpcbiAqICAgICAgIC8vIGluc3RhbnRpYXRlIHRoZSBBc3NldCBBZGFwdGVyXG4gKiAgICAgICB2YXIgYWEgPSBuZXcgRi5zZXJ2aWNlLkFzc2V0KHtcbiAqICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAqICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gKiAgICAgICAgICBncm91cDogJ3RlYW0xJyxcbiAqICAgICAgICAgIHVzZXJJZDogJzEyMzQ1J1xuICogICAgICAgfSk7XG4gKlxuICogICAgICAgLy8gY3JlYXRlIGEgbmV3IGFzc2V0IHVzaW5nIGVuY29kZWQgdGV4dFxuICogICAgICAgYWEuY3JlYXRlKCd0ZXN0LnR4dCcsIHtcbiAqICAgICAgICAgICBlbmNvZGluZzogJ0JBU0VfNjQnLFxuICogICAgICAgICAgIGRhdGE6ICdWR2hwY3lCcGN5QmhJSFJsYzNRZ1ptbHNaUzQ9JyxcbiAqICAgICAgICAgICBjb250ZW50VHlwZTogJ3RleHQvcGxhaW4nXG4gKiAgICAgICB9LCB7IHNjb3BlOiAndXNlcicgfSk7XG4gKlxuICogICAgICAgLy8gYWx0ZXJuYXRpdmVseSwgY3JlYXRlIGEgbmV3IGFzc2V0IHVzaW5nIGEgZmlsZSB1cGxvYWRlZCB0aHJvdWdoIGEgZm9ybVxuICogICAgICAgLy8gdGhpcyBzYW1wbGUgY29kZSBnb2VzIHdpdGggYW4gaHRtbCBmb3JtIHRoYXQgbG9va3MgbGlrZSB0aGlzOlxuICogICAgICAgLy9cbiAqICAgICAgIC8vIDxmb3JtIGlkPVwidXBsb2FkLWZpbGVcIj5cbiAqICAgICAgIC8vICAgPGlucHV0IGlkPVwiZmlsZVwiIHR5cGU9XCJmaWxlXCI+XG4gKiAgICAgICAvLyAgIDxpbnB1dCBpZD1cImZpbGVuYW1lXCIgdHlwZT1cInRleHRcIiB2YWx1ZT1cIm15RmlsZS50eHRcIj5cbiAqICAgICAgIC8vICAgPGJ1dHRvbiB0eXBlPVwic3VibWl0XCI+VXBsb2FkIG15RmlsZTwvYnV0dG9uPlxuICogICAgICAgLy8gPC9mb3JtPlxuICogICAgICAgLy9cbiAqICAgICAgICQoJyN1cGxvYWQtZmlsZScpLm9uKCdzdWJtaXQnLCBmdW5jdGlvbiAoZSkge1xuICogICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICogICAgICAgICAgdmFyIGZpbGVuYW1lID0gJCgnI2ZpbGVuYW1lJykudmFsKCk7XG4gKiAgICAgICAgICB2YXIgZGF0YSA9IG5ldyBGb3JtRGF0YSgpO1xuICogICAgICAgICAgdmFyIGlucHV0Q29udHJvbCA9ICQoJyNmaWxlJylbMF07XG4gKiAgICAgICAgICBkYXRhLmFwcGVuZCgnZmlsZScsIGlucHV0Q29udHJvbC5maWxlc1swXSwgZmlsZW5hbWUpO1xuICpcbiAqICAgICAgICAgIGFhLmNyZWF0ZShmaWxlbmFtZSwgZGF0YSwgeyBzY29wZTogJ3VzZXInIH0pO1xuICogICAgICAgfSk7XG4gKlxuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIF9waWNrID0gcmVxdWlyZSgnLi4vdXRpbC9vYmplY3QtdXRpbCcpLl9waWNrO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG5cbnZhciBhcGlFbmRwb2ludCA9ICdhc3NldCc7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEZvciBwcm9qZWN0cyB0aGF0IHJlcXVpcmUgYXV0aGVudGljYXRpb24sIHBhc3MgaW4gdGhlIHVzZXIgYWNjZXNzIHRva2VuIChkZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcpLiBJZiB0aGUgdXNlciBpcyBhbHJlYWR5IGxvZ2dlZCBpbiB0byBFcGljZW50ZXIsIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiBpcyBhbHJlYWR5IHNldCBpbiBhIGNvb2tpZSBhbmQgYXV0b21hdGljYWxseSBsb2FkZWQgZnJvbSB0aGVyZS4gKFNlZSBbbW9yZSBiYWNrZ3JvdW5kIG9uIGFjY2VzcyB0b2tlbnNdKC4uLy4uLy4uL3Byb2plY3RfYWNjZXNzLykpLlxuICAgICAgICAgKiBAc2VlIFtBdXRoZW50aWNhdGlvbiBBUEkgU2VydmljZV0oLi4vYXV0aC1hcGktc2VydmljZS8pIGZvciBnZXR0aW5nIHRva2Vucy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHRva2VuOiB1bmRlZmluZWQsXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgYWNjb3VudCBpZC4gSW4gdGhlIEVwaWNlbnRlciBVSSwgdGhpcyBpcyB0aGUgKipUZWFtIElEKiogKGZvciB0ZWFtIHByb2plY3RzKS4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFjY291bnQ6IHVuZGVmaW5lZCxcbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBwcm9qZWN0IGlkLiBJZiBsZWZ0IHVuZGVmaW5lZCwgdGFrZW4gZnJvbSB0aGUgVVJMLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgcHJvamVjdDogdW5kZWZpbmVkLFxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGdyb3VwIG5hbWUuIERlZmF1bHRzIHRvIHNlc3Npb24ncyBgZ3JvdXBOYW1lYC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGdyb3VwOiB1bmRlZmluZWQsXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgdXNlciBpZC4gRGVmYXVsdHMgdG8gc2Vzc2lvbidzIGB1c2VySWRgLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdXNlcklkOiB1bmRlZmluZWQsXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgc2NvcGUgZm9yIHRoZSBhc3NldC4gVmFsaWQgdmFsdWVzIGFyZTogYHVzZXJgLCBgZ3JvdXBgLCBhbmQgYHByb2plY3RgLiBTZWUgYWJvdmUgZm9yIHRoZSByZXF1aXJlZCBwZXJtaXNzaW9ucyB0byB3cml0ZSB0byBlYWNoIHNjb3BlLiBEZWZhdWx0cyB0byBgdXNlcmAsIG1lYW5pbmcgdGhlIGN1cnJlbnQgZW5kIHVzZXIgb3IgYSBmYWNpbGl0YXRvciBpbiB0aGUgZW5kIHVzZXIncyBncm91cCBjYW4gZWRpdCB0aGUgYXNzZXQuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBzY29wZTogJ3VzZXInLFxuICAgICAgICAvKipcbiAgICAgICAgICogRGV0ZXJtaW5lcyBpZiBhIHJlcXVlc3QgdG8gbGlzdCB0aGUgYXNzZXRzIGluIGEgc2NvcGUgaW5jbHVkZXMgdGhlIGNvbXBsZXRlIFVSTCBmb3IgZWFjaCBhc3NldCAoYHRydWVgKSwgb3Igb25seSB0aGUgZmlsZSBuYW1lcyBvZiB0aGUgYXNzZXRzIChgZmFsc2VgKS4gRGVmYXVsdHMgdG8gYHRydWVgLlxuICAgICAgICAgKiBAdHlwZSB7Ym9vbGVhbn1cbiAgICAgICAgICovXG4gICAgICAgIGZ1bGxVcmw6IHRydWUsXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgdHJhbnNwb3J0IG9iamVjdCBjb250YWlucyB0aGUgb3B0aW9ucyBwYXNzZWQgdG8gdGhlIFhIUiByZXF1ZXN0LlxuICAgICAgICAgKiBAdHlwZSB7b2JqZWN0fVxuICAgICAgICAgKi9cbiAgICAgICAgdHJhbnNwb3J0OiB7XG4gICAgICAgICAgICBwcm9jZXNzRGF0YTogZmFsc2VcbiAgICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgY29uZmlnKTtcbiAgICB2YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2Uoc2VydmljZU9wdGlvbnMpLmdldCgnc2VydmVyJyk7XG5cbiAgICBpZiAoIXNlcnZpY2VPcHRpb25zLmFjY291bnQpIHtcbiAgICAgICAgc2VydmljZU9wdGlvbnMuYWNjb3VudCA9IHVybENvbmZpZy5hY2NvdW50UGF0aDtcbiAgICB9XG5cbiAgICBpZiAoIXNlcnZpY2VPcHRpb25zLnByb2plY3QpIHtcbiAgICAgICAgc2VydmljZU9wdGlvbnMucHJvamVjdCA9IHVybENvbmZpZy5wcm9qZWN0UGF0aDtcbiAgICB9XG5cbiAgICB2YXIgdHJhbnNwb3J0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQsIHtcbiAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludClcbiAgICB9KTtcblxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICB0cmFuc3BvcnRPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHZhciBodHRwID0gbmV3IFRyYW5zcG9ydEZhY3RvcnkodHJhbnNwb3J0T3B0aW9ucyk7XG5cbiAgICB2YXIgYXNzZXRBcGlQYXJhbXMgPSBbJ2VuY29kaW5nJywgJ2RhdGEnLCAnY29udGVudFR5cGUnXTtcbiAgICB2YXIgc2NvcGVDb25maWcgPSB7XG4gICAgICAgIHVzZXI6IFsnc2NvcGUnLCAnYWNjb3VudCcsICdwcm9qZWN0JywgJ2dyb3VwJywgJ3VzZXJJZCddLFxuICAgICAgICBncm91cDogWydzY29wZScsICdhY2NvdW50JywgJ3Byb2plY3QnLCAnZ3JvdXAnXSxcbiAgICAgICAgcHJvamVjdDogWydzY29wZScsICdhY2NvdW50JywgJ3Byb2plY3QnXSxcbiAgICB9O1xuXG4gICAgdmFyIHZhbGlkYXRlRmlsZW5hbWUgPSBmdW5jdGlvbiAoZmlsZW5hbWUpIHtcbiAgICAgICAgaWYgKCFmaWxlbmFtZSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdmaWxlbmFtZSBpcyBuZWVkZWQuJyk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgdmFyIHZhbGlkYXRlVXJsUGFyYW1zID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgdmFyIHBhcnRLZXlzID0gc2NvcGVDb25maWdbb3B0aW9ucy5zY29wZV07XG4gICAgICAgIGlmICghcGFydEtleXMpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignc2NvcGUgcGFyYW1ldGVyIGlzIG5lZWRlZC4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgICQuZWFjaChwYXJ0S2V5cywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgaWYgKCFvcHRpb25zW3RoaXNdKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKHRoaXMgKyAnIHBhcmFtZXRlciBpcyBuZWVkZWQuJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICB2YXIgYnVpbGRVcmwgPSBmdW5jdGlvbiAoZmlsZW5hbWUsIG9wdGlvbnMpIHtcbiAgICAgICAgdmFsaWRhdGVVcmxQYXJhbXMob3B0aW9ucyk7XG4gICAgICAgIHZhciBwYXJ0S2V5cyA9IHNjb3BlQ29uZmlnW29wdGlvbnMuc2NvcGVdO1xuICAgICAgICB2YXIgcGFydHMgPSAkLm1hcChwYXJ0S2V5cywgZnVuY3Rpb24gKGtleSkge1xuICAgICAgICAgICAgcmV0dXJuIG9wdGlvbnNba2V5XTtcbiAgICAgICAgfSk7XG4gICAgICAgIGlmIChmaWxlbmFtZSkge1xuICAgICAgICAgICAgLy8gVGhpcyBwcmV2ZW50cyBhZGRpbmcgYSB0cmFpbGluZyAvIGluIHRoZSBVUkwgYXMgdGhlIEFzc2V0IEFQSVxuICAgICAgICAgICAgLy8gZG9lcyBub3Qgd29yayBjb3JyZWN0bHkgd2l0aCBpdFxuICAgICAgICAgICAgZmlsZW5hbWUgPSAnLycgKyBmaWxlbmFtZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpICsgcGFydHMuam9pbignLycpICsgZmlsZW5hbWU7XG4gICAgfTtcblxuICAgIC8vIFByaXZhdGUgZnVuY3Rpb24sIGFsbCByZXF1ZXN0cyBmb2xsb3cgYSBtb3JlIG9yIGxlc3Mgc2FtZSBhcHByb2FjaCB0b1xuICAgIC8vIHVzZSB0aGUgQXNzZXQgQVBJIGFuZCB0aGUgZGlmZmVyZW5jZSBpcyB0aGUgSFRUUCB2ZXJiXG4gICAgLy9cbiAgICAvLyBAcGFyYW0ge3N0cmluZ30gbWV0aG9kYCAoUmVxdWlyZWQpIEhUVFAgdmVyYlxuICAgIC8vIEBwYXJhbSB7c3RyaW5nfSBmaWxlbmFtZWAgKFJlcXVpcmVkKSBOYW1lIG9mIHRoZSBmaWxlIHRvIGRlbGV0ZS9yZXBsYWNlL2NyZWF0ZVxuICAgIC8vIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXNgIChPcHRpb25hbCkgQm9keSBwYXJhbWV0ZXJzIHRvIHNlbmQgdG8gdGhlIEFzc2V0IEFQSVxuICAgIC8vIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zYCAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgIHZhciB1cGxvYWQgPSBmdW5jdGlvbiAobWV0aG9kLCBmaWxlbmFtZSwgcGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgIHZhbGlkYXRlRmlsZW5hbWUoZmlsZW5hbWUpO1xuICAgICAgICAvLyBtYWtlIHN1cmUgdGhlIHBhcmFtZXRlciBpcyBjbGVhblxuICAgICAgICBtZXRob2QgPSBtZXRob2QudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgdmFyIGNvbnRlbnRUeXBlID0gcGFyYW1zIGluc3RhbmNlb2YgRm9ybURhdGEgPT09IHRydWUgPyBmYWxzZSA6ICdhcHBsaWNhdGlvbi9qc29uJztcbiAgICAgICAgaWYgKGNvbnRlbnRUeXBlID09PSAnYXBwbGljYXRpb24vanNvbicpIHtcbiAgICAgICAgICAgIC8vIHdoaXRlbGlzdCB0aGUgZmllbGRzIHRoYXQgd2UgYWN0dWFsbHkgY2FuIHNlbmQgdG8gdGhlIGFwaVxuICAgICAgICAgICAgcGFyYW1zID0gX3BpY2socGFyYW1zLCBhc3NldEFwaVBhcmFtcyk7XG4gICAgICAgIH0gZWxzZSB7IC8vIGVsc2Ugd2UncmUgc2VuZGluZyBmb3JtIGRhdGEgd2hpY2ggZ29lcyBkaXJlY3RseSBpbiByZXF1ZXN0IGJvZHlcbiAgICAgICAgICAgIC8vIEZvciBtdWx0aXBhcnQvZm9ybS1kYXRhIHVwbG9hZHMgdGhlIGZpbGVuYW1lIGlzIG5vdCBzZXQgaW4gdGhlIFVSTCxcbiAgICAgICAgICAgIC8vIGl0J3MgZ2V0dGluZyBwaWNrZWQgYnkgdGhlIEZvcm1EYXRhIGZpZWxkIGZpbGVuYW1lLlxuICAgICAgICAgICAgZmlsZW5hbWUgPSBtZXRob2QgPT09ICdwb3N0JyB8fCBtZXRob2QgPT09ICdwdXQnID8gJycgOiBmaWxlbmFtZTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgdXJsT3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgIHZhciB1cmwgPSBidWlsZFVybChmaWxlbmFtZSwgdXJsT3B0aW9ucyk7XG4gICAgICAgIHZhciBjcmVhdGVPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHVybE9wdGlvbnMsIHsgdXJsOiB1cmwsIGNvbnRlbnRUeXBlOiBjb250ZW50VHlwZSB9KTtcblxuICAgICAgICByZXR1cm4gaHR0cFttZXRob2RdKHBhcmFtcywgY3JlYXRlT3B0aW9ucyk7XG4gICAgfTtcblxuICAgIHZhciBwdWJsaWNBUEkgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAqIENyZWF0ZXMgYSBmaWxlIGluIHRoZSBBc3NldCBBUEkuIFRoZSBzZXJ2ZXIgcmV0dXJucyBhbiBlcnJvciAoc3RhdHVzIGNvZGUgYDQwOWAsIGNvbmZsaWN0KSBpZiB0aGUgZmlsZSBhbHJlYWR5IGV4aXN0cywgc29cbiAgICAgICAgKiBjaGVjayBmaXJzdCB3aXRoIGEgYGxpc3QoKWAgb3IgYSBgZ2V0KClgLlxuICAgICAgICAqXG4gICAgICAgICogICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICB2YXIgYWEgPSBuZXcgRi5zZXJ2aWNlLkFzc2V0KHtcbiAgICAgICAgKiAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICBncm91cDogJ3RlYW0xJyxcbiAgICAgICAgKiAgICAgICAgICB1c2VySWQ6ICcnXG4gICAgICAgICogICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICAvLyBjcmVhdGUgYSBuZXcgYXNzZXQgdXNpbmcgZW5jb2RlZCB0ZXh0XG4gICAgICAgICogICAgICAgYWEuY3JlYXRlKCd0ZXN0LnR4dCcsIHtcbiAgICAgICAgKiAgICAgICAgICAgZW5jb2Rpbmc6ICdCQVNFXzY0JyxcbiAgICAgICAgKiAgICAgICAgICAgZGF0YTogJ1ZHaHBjeUJwY3lCaElIUmxjM1FnWm1sc1pTND0nLFxuICAgICAgICAqICAgICAgICAgICBjb250ZW50VHlwZTogJ3RleHQvcGxhaW4nXG4gICAgICAgICogICAgICAgfSwgeyBzY29wZTogJ3VzZXInIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICAgICAgLy8gYWx0ZXJuYXRpdmVseSwgY3JlYXRlIGEgbmV3IGFzc2V0IHVzaW5nIGEgZmlsZSB1cGxvYWRlZCB0aHJvdWdoIGEgZm9ybVxuICAgICAgICAqICAgICAgIC8vIHRoaXMgc2FtcGxlIGNvZGUgZ29lcyB3aXRoIGFuIGh0bWwgZm9ybSB0aGF0IGxvb2tzIGxpa2UgdGhpczpcbiAgICAgICAgKiAgICAgICAvL1xuICAgICAgICAqICAgICAgIC8vIDxmb3JtIGlkPVwidXBsb2FkLWZpbGVcIj5cbiAgICAgICAgKiAgICAgICAvLyAgIDxpbnB1dCBpZD1cImZpbGVcIiB0eXBlPVwiZmlsZVwiPlxuICAgICAgICAqICAgICAgIC8vICAgPGlucHV0IGlkPVwiZmlsZW5hbWVcIiB0eXBlPVwidGV4dFwiIHZhbHVlPVwibXlGaWxlLnR4dFwiPlxuICAgICAgICAqICAgICAgIC8vICAgPGJ1dHRvbiB0eXBlPVwic3VibWl0XCI+VXBsb2FkIG15RmlsZTwvYnV0dG9uPlxuICAgICAgICAqICAgICAgIC8vIDwvZm9ybT5cbiAgICAgICAgKiAgICAgICAvL1xuICAgICAgICAqICAgICAgICQoJyN1cGxvYWQtZmlsZScpLm9uKCdzdWJtaXQnLCBmdW5jdGlvbiAoZSkge1xuICAgICAgICAqICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgKiAgICAgICAgICB2YXIgZmlsZW5hbWUgPSAkKCcjZmlsZW5hbWUnKS52YWwoKTtcbiAgICAgICAgKiAgICAgICAgICB2YXIgZGF0YSA9IG5ldyBGb3JtRGF0YSgpO1xuICAgICAgICAqICAgICAgICAgIHZhciBpbnB1dENvbnRyb2wgPSAkKCcjZmlsZScpWzBdO1xuICAgICAgICAqICAgICAgICAgIGRhdGEuYXBwZW5kKCdmaWxlJywgaW5wdXRDb250cm9sLmZpbGVzWzBdLCBmaWxlbmFtZSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICAgICBhYS5jcmVhdGUoZmlsZW5hbWUsIGRhdGEsIHsgc2NvcGU6ICd1c2VyJyB9KTtcbiAgICAgICAgKiAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGZpbGVuYW1lIChSZXF1aXJlZCkgTmFtZSBvZiB0aGUgZmlsZSB0byBjcmVhdGUuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IHBhcmFtcyAoT3B0aW9uYWwpIEJvZHkgcGFyYW1ldGVycyB0byBzZW5kIHRvIHRoZSBBc3NldCBBUEkuIFJlcXVpcmVkIGlmIHRoZSBgb3B0aW9ucy50cmFuc3BvcnQuY29udGVudFR5cGVgIGlzIGBhcHBsaWNhdGlvbi9qc29uYCwgb3RoZXJ3aXNlIGlnbm9yZWQuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5lbmNvZGluZyBFaXRoZXIgYEhFWGAgb3IgYEJBU0VfNjRgLiBSZXF1aXJlZCBpZiBgb3B0aW9ucy50cmFuc3BvcnQuY29udGVudFR5cGVgIGlzIGBhcHBsaWNhdGlvbi9qc29uYC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmRhdGEgVGhlIGVuY29kZWQgZGF0YSBmb3IgdGhlIGZpbGUuIFJlcXVpcmVkIGlmIGBvcHRpb25zLnRyYW5zcG9ydC5jb250ZW50VHlwZWAgaXMgYGFwcGxpY2F0aW9uL2pzb25gLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuY29udGVudFR5cGUgVGhlIG1pbWUgdHlwZSBvZiB0aGUgZmlsZS4gT3B0aW9uYWwuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBjcmVhdGU6IGZ1bmN0aW9uIChmaWxlbmFtZSwgcGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICByZXR1cm4gdXBsb2FkKCdwb3N0JywgZmlsZW5hbWUsIHBhcmFtcywgb3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogR2V0cyBhIGZpbGUgZnJvbSB0aGUgQXNzZXQgQVBJLCBmZXRjaGluZyB0aGUgYXNzZXQgY29udGVudC4gKFRvIGdldCBhIGxpc3RcbiAgICAgICAgKiBvZiB0aGUgYXNzZXRzIGluIGEgc2NvcGUsIHVzZSBgbGlzdCgpYC4pXG4gICAgICAgICpcbiAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gZmlsZW5hbWUgKFJlcXVpcmVkKSBOYW1lIG9mIHRoZSBmaWxlIHRvIHJldHJpZXZlLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZ2V0OiBmdW5jdGlvbiAoZmlsZW5hbWUsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBnZXRTZXJ2aWNlT3B0aW9ucyA9IF9waWNrKHNlcnZpY2VPcHRpb25zLCBbJ3Njb3BlJywgJ2FjY291bnQnLCAncHJvamVjdCcsICdncm91cCcsICd1c2VySWQnXSk7XG4gICAgICAgICAgICB2YXIgdXJsT3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBnZXRTZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICB2YXIgdXJsID0gYnVpbGRVcmwoZmlsZW5hbWUsIHVybE9wdGlvbnMpO1xuICAgICAgICAgICAgdmFyIGdldE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgdXJsT3B0aW9ucywgeyB1cmw6IHVybCB9KTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KHt9LCBnZXRPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBHZXRzIHRoZSBsaXN0IG9mIHRoZSBhc3NldHMgaW4gYSBzY29wZS5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICBhYS5saXN0KHsgZnVsbFVybDogdHJ1ZSB9KS50aGVuKGZ1bmN0aW9uKGZpbGVMaXN0KXtcbiAgICAgICAgKiAgICAgICAgICAgY29uc29sZS5sb2coJ2FycmF5IG9mIGZpbGVzID0gJywgZmlsZUxpc3QpO1xuICAgICAgICAqICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5zY29wZSAoT3B0aW9uYWwpIFRoZSBzY29wZSAoYHVzZXJgLCBgZ3JvdXBgLCBgcHJvamVjdGApLlxuICAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gb3B0aW9ucy5mdWxsVXJsIChPcHRpb25hbCkgRGV0ZXJtaW5lcyBpZiB0aGUgbGlzdCBvZiBhc3NldHMgaW4gYSBzY29wZSBpbmNsdWRlcyB0aGUgY29tcGxldGUgVVJMIGZvciBlYWNoIGFzc2V0IChgdHJ1ZWApLCBvciBvbmx5IHRoZSBmaWxlIG5hbWVzIG9mIHRoZSBhc3NldHMgKGBmYWxzZWApLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGxpc3Q6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgZHRkID0gJC5EZWZlcnJlZCgpO1xuICAgICAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgICAgIHZhciB1cmxPcHRpb25zID0gJC5leHRlbmQoe30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIHZhciB1cmwgPSBidWlsZFVybCgnJywgdXJsT3B0aW9ucyk7XG4gICAgICAgICAgICB2YXIgZ2V0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCB1cmxPcHRpb25zLCB7IHVybDogdXJsIH0pO1xuICAgICAgICAgICAgdmFyIGZ1bGxVcmwgPSBnZXRPcHRpb25zLmZ1bGxVcmw7XG5cbiAgICAgICAgICAgIGlmICghZnVsbFVybCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBodHRwLmdldCh7fSwgZ2V0T3B0aW9ucyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGh0dHAuZ2V0KHt9LCBnZXRPcHRpb25zKVxuICAgICAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChmaWxlcykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgZnVsbFBhdGhGaWxlcyA9ICQubWFwKGZpbGVzLCBmdW5jdGlvbiAoZmlsZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGJ1aWxkVXJsKGZpbGUsIHVybE9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgZHRkLnJlc29sdmVXaXRoKG1lLCBbZnVsbFBhdGhGaWxlc10pO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZhaWwoZHRkLnJlamVjdCk7XG5cbiAgICAgICAgICAgIHJldHVybiBkdGQucHJvbWlzZSgpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIFJlcGxhY2VzIGFuIGV4aXN0aW5nIGZpbGUgaW4gdGhlIEFzc2V0IEFQSS5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICAvLyByZXBsYWNlIGFuIGFzc2V0IHVzaW5nIGVuY29kZWQgdGV4dFxuICAgICAgICAqICAgICAgIGFhLnJlcGxhY2UoJ3Rlc3QudHh0Jywge1xuICAgICAgICAqICAgICAgICAgICBlbmNvZGluZzogJ0JBU0VfNjQnLFxuICAgICAgICAqICAgICAgICAgICBkYXRhOiAnVkdocGN5QnBjeUJoSUhObFkyOXVaQ0IwWlhOMElHWnBiR1V1JyxcbiAgICAgICAgKiAgICAgICAgICAgY29udGVudFR5cGU6ICd0ZXh0L3BsYWluJ1xuICAgICAgICAqICAgICAgIH0sIHsgc2NvcGU6ICd1c2VyJyB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgIC8vIGFsdGVybmF0aXZlbHksIHJlcGxhY2UgYW4gYXNzZXQgdXNpbmcgYSBmaWxlIHVwbG9hZGVkIHRocm91Z2ggYSBmb3JtXG4gICAgICAgICogICAgICAgLy8gdGhpcyBzYW1wbGUgY29kZSBnb2VzIHdpdGggYW4gaHRtbCBmb3JtIHRoYXQgbG9va3MgbGlrZSB0aGlzOlxuICAgICAgICAqICAgICAgIC8vXG4gICAgICAgICogICAgICAgLy8gPGZvcm0gaWQ9XCJyZXBsYWNlLWZpbGVcIj5cbiAgICAgICAgKiAgICAgICAvLyAgIDxpbnB1dCBpZD1cImZpbGVcIiB0eXBlPVwiZmlsZVwiPlxuICAgICAgICAqICAgICAgIC8vICAgPGlucHV0IGlkPVwicmVwbGFjZS1maWxlbmFtZVwiIHR5cGU9XCJ0ZXh0XCIgdmFsdWU9XCJteUZpbGUudHh0XCI+XG4gICAgICAgICogICAgICAgLy8gICA8YnV0dG9uIHR5cGU9XCJzdWJtaXRcIj5SZXBsYWNlIG15RmlsZTwvYnV0dG9uPlxuICAgICAgICAqICAgICAgIC8vIDwvZm9ybT5cbiAgICAgICAgKiAgICAgICAvL1xuICAgICAgICAqICAgICAgICQoJyNyZXBsYWNlLWZpbGUnKS5vbignc3VibWl0JywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgKiAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICogICAgICAgICAgdmFyIGZpbGVuYW1lID0gJCgnI3JlcGxhY2UtZmlsZW5hbWUnKS52YWwoKTtcbiAgICAgICAgKiAgICAgICAgICB2YXIgZGF0YSA9IG5ldyBGb3JtRGF0YSgpO1xuICAgICAgICAqICAgICAgICAgIHZhciBpbnB1dENvbnRyb2wgPSAkKCcjZmlsZScpWzBdO1xuICAgICAgICAqICAgICAgICAgIGRhdGEuYXBwZW5kKCdmaWxlJywgaW5wdXRDb250cm9sLmZpbGVzWzBdLCBmaWxlbmFtZSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICAgICBhYS5yZXBsYWNlKGZpbGVuYW1lLCBkYXRhLCB7IHNjb3BlOiAndXNlcicgfSk7XG4gICAgICAgICogICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gZmlsZW5hbWUgKFJlcXVpcmVkKSBOYW1lIG9mIHRoZSBmaWxlIGJlaW5nIHJlcGxhY2VkLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgKE9wdGlvbmFsKSBCb2R5IHBhcmFtZXRlcnMgdG8gc2VuZCB0byB0aGUgQXNzZXQgQVBJLiBSZXF1aXJlZCBpZiB0aGUgYG9wdGlvbnMudHJhbnNwb3J0LmNvbnRlbnRUeXBlYCBpcyBgYXBwbGljYXRpb24vanNvbmAsIG90aGVyd2lzZSBpZ25vcmVkLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuZW5jb2RpbmcgRWl0aGVyIGBIRVhgIG9yIGBCQVNFXzY0YC4gUmVxdWlyZWQgaWYgYG9wdGlvbnMudHJhbnNwb3J0LmNvbnRlbnRUeXBlYCBpcyBgYXBwbGljYXRpb24vanNvbmAuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5kYXRhIFRoZSBlbmNvZGVkIGRhdGEgZm9yIHRoZSBmaWxlLiBSZXF1aXJlZCBpZiBgb3B0aW9ucy50cmFuc3BvcnQuY29udGVudFR5cGVgIGlzIGBhcHBsaWNhdGlvbi9qc29uYC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmNvbnRlbnRUeXBlIFRoZSBtaW1lIHR5cGUgb2YgdGhlIGZpbGUuIE9wdGlvbmFsLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgcmVwbGFjZTogZnVuY3Rpb24gKGZpbGVuYW1lLCBwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHJldHVybiB1cGxvYWQoJ3B1dCcsIGZpbGVuYW1lLCBwYXJhbXMsIG9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIERlbGV0ZXMgYSBmaWxlIGZyb20gdGhlIEFzc2V0IEFQSS5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICBhYS5kZWxldGUoc2FtcGxlRmlsZU5hbWUpO1xuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGZpbGVuYW1lIChSZXF1aXJlZCkgTmFtZSBvZiB0aGUgZmlsZSB0byBkZWxldGUuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBkZWxldGU6IGZ1bmN0aW9uIChmaWxlbmFtZSwgb3B0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIHVwbG9hZCgnZGVsZXRlJywgZmlsZW5hbWUsIHt9LCBvcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICBhc3NldFVybDogZnVuY3Rpb24gKGZpbGVuYW1lLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgdXJsT3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm4gYnVpbGRVcmwoZmlsZW5hbWUsIHVybE9wdGlvbnMpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xufTtcbiIsIi8qKlxuICpcbiAqICMjIEF1dGhlbnRpY2F0aW9uIEFQSSBTZXJ2aWNlXG4gKlxuICogVGhlIEF1dGhlbnRpY2F0aW9uIEFQSSBTZXJ2aWNlIHByb3ZpZGVzIGEgbWV0aG9kIGZvciBsb2dnaW5nIGluLCB3aGljaCBjcmVhdGVzIGFuZCByZXR1cm5zIGEgdXNlciBhY2Nlc3MgdG9rZW4uXG4gKlxuICogVXNlciBhY2Nlc3MgdG9rZW5zIGFyZSByZXF1aXJlZCBmb3IgZWFjaCBjYWxsIHRvIEVwaWNlbnRlci4gKFNlZSBbUHJvamVjdCBBY2Nlc3NdKC4uLy4uLy4uL3Byb2plY3RfYWNjZXNzLykgZm9yIG1vcmUgaW5mb3JtYXRpb24uKVxuICpcbiAqIElmIHlvdSBuZWVkIGFkZGl0aW9uYWwgZnVuY3Rpb25hbGl0eSAtLSBzdWNoIGFzIHRyYWNraW5nIHNlc3Npb24gaW5mb3JtYXRpb24sIGVhc2lseSByZXRyaWV2aW5nIHRoZSB1c2VyIHRva2VuLCBvciBnZXR0aW5nIHRoZSBncm91cHMgdG8gd2hpY2ggYW4gZW5kIHVzZXIgYmVsb25ncyAtLSBjb25zaWRlciB1c2luZyB0aGUgW0F1dGhvcml6YXRpb24gTWFuYWdlcl0oLi4vYXV0aC1tYW5hZ2VyLykgaW5zdGVhZC5cbiAqXG4gKiAgICAgIHZhciBhdXRoID0gbmV3IEYuc2VydmljZS5BdXRoKCk7XG4gKiAgICAgIGF1dGgubG9naW4oeyB1c2VyTmFtZTogJ2pzbWl0aEBhY21lc2ltdWxhdGlvbnMuY29tJyxcbiAqICAgICAgICAgICAgICAgICAgcGFzc3dvcmQ6ICdwYXNzdzByZCcgfSk7XG4gKi9cblxuJ3VzZSBzdHJpY3QnO1xuXG52YXIgQ29uZmlnU2VydmljZSA9IHJlcXVpcmUoJy4vY29uZmlndXJhdGlvbi1zZXJ2aWNlJyk7XG52YXIgVHJhbnNwb3J0RmFjdG9yeSA9IHJlcXVpcmUoJy4uL3RyYW5zcG9ydC9odHRwLXRyYW5zcG9ydC1mYWN0b3J5Jyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEVtYWlsIG9yIHVzZXJuYW1lIHRvIHVzZSBmb3IgbG9nZ2luZyBpbi4gRGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdXNlck5hbWU6ICcnLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBQYXNzd29yZCBmb3Igc3BlY2lmaWVkIGB1c2VyTmFtZWAuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHBhc3N3b3JkOiAnJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFjY291bnQgaWQgZm9yIHRoaXMgYHVzZXJOYW1lYC4gSW4gdGhlIEVwaWNlbnRlciBVSSwgdGhpcyBpcyB0aGUgKipUZWFtIElEKiogKGZvciB0ZWFtIHByb2plY3RzKSBvciB0aGUgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuIFJlcXVpcmVkIGlmIHRoZSBgdXNlck5hbWVgIGlzIGZvciBhbiBbZW5kIHVzZXJdKC4uLy4uLy4uL2dsb3NzYXJ5LyN1c2VycykuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFjY291bnQ6ICcnLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBPcHRpb25zIHRvIHBhc3Mgb24gdG8gdGhlIHVuZGVybHlpbmcgdHJhbnNwb3J0IGxheWVyLiBBbGwganF1ZXJ5LmFqYXggb3B0aW9ucyBhdCBodHRwOi8vYXBpLmpxdWVyeS5jb20valF1ZXJ5LmFqYXgvIGFyZSBhdmFpbGFibGUuIERlZmF1bHRzIHRvIGVtcHR5IG9iamVjdC5cbiAgICAgICAgICogQHR5cGUge09iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIHRyYW5zcG9ydDoge31cbiAgICB9O1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBkZWZhdWx0cywgY29uZmlnKTtcbiAgICB2YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2Uoc2VydmljZU9wdGlvbnMpLmdldCgnc2VydmVyJyk7XG5cbiAgICB2YXIgdHJhbnNwb3J0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQsIHtcbiAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aCgnYXV0aGVudGljYXRpb24nKVxuICAgIH0pO1xuICAgIHZhciBodHRwID0gbmV3IFRyYW5zcG9ydEZhY3RvcnkodHJhbnNwb3J0T3B0aW9ucyk7XG5cbiAgICB2YXIgcHVibGljQVBJID0ge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBMb2dzIHVzZXIgaW4sIHJldHVybmluZyB0aGUgdXNlciBhY2Nlc3MgdG9rZW4uXG4gICAgICAgICAqXG4gICAgICAgICAqIElmIG5vIGB1c2VyTmFtZWAgb3IgYHBhc3N3b3JkYCB3ZXJlIHByb3ZpZGVkIGluIHRoZSBpbml0aWFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgdGhleSBhcmUgcmVxdWlyZWQgaW4gdGhlIGBvcHRpb25zYCBoZXJlLiBJZiBubyBgYWNjb3VudGAgd2FzIHByb3ZpZGVkIGluIHRoZSBpbml0aWFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBhbmQgdGhlIGB1c2VyTmFtZWAgaXMgZm9yIGFuIFtlbmQgdXNlcl0oLi4vLi4vLi4vZ2xvc3NhcnkvI3VzZXJzKSwgdGhlIGBhY2NvdW50YCBpcyByZXF1aXJlZCBhcyB3ZWxsLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIGF1dGgubG9naW4oe1xuICAgICAgICAgKiAgICAgICAgICB1c2VyTmFtZTogJ2pzbWl0aCcsXG4gICAgICAgICAqICAgICAgICAgIHBhc3N3b3JkOiAncGFzc3cwcmQnLFxuICAgICAgICAgKiAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycgfSlcbiAgICAgICAgICogICAgICAudGhlbihmdW5jdGlvbiAodG9rZW4pIHtcbiAgICAgICAgICogICAgICAgICAgY29uc29sZS5sb2coXCJ1c2VyIGFjY2VzcyB0b2tlbiBpczogXCIsIHRva2VuLmFjY2Vzc190b2tlbik7XG4gICAgICAgICAqICAgICAgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBsb2dpbjogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHsgc3VjY2VzczogJC5ub29wIH0sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIGlmICghaHR0cE9wdGlvbnMudXNlck5hbWUgfHwgIWh0dHBPcHRpb25zLnBhc3N3b3JkKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlc3AgPSB7IHN0YXR1czogNDAxLCBzdGF0dXNNZXNzYWdlOiAnTm8gdXNlcm5hbWUgb3IgcGFzc3dvcmQgc3BlY2lmaWVkLicgfTtcbiAgICAgICAgICAgICAgICBpZiAob3B0aW9ucy5lcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBvcHRpb25zLmVycm9yLmNhbGwodGhpcywgcmVzcCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuICQuRGVmZXJyZWQoKS5yZWplY3QocmVzcCkucHJvbWlzZSgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcG9zdFBhcmFtcyA9IHtcbiAgICAgICAgICAgICAgICB1c2VyTmFtZTogaHR0cE9wdGlvbnMudXNlck5hbWUsXG4gICAgICAgICAgICAgICAgcGFzc3dvcmQ6IGh0dHBPcHRpb25zLnBhc3N3b3JkLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGlmIChodHRwT3B0aW9ucy5hY2NvdW50KSB7XG4gICAgICAgICAgICAgICAgLy9wYXNzIGluIG51bGwgZm9yIGFjY291bnQgdW5kZXIgb3B0aW9ucyBpZiB5b3UgZG9uJ3Qgd2FudCBpdCB0byBiZSBzZW50XG4gICAgICAgICAgICAgICAgcG9zdFBhcmFtcy5hY2NvdW50ID0gaHR0cE9wdGlvbnMuYWNjb3VudDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucG9zdChwb3N0UGFyYW1zLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLy8gKHJlcGxhY2Ugd2l0aCAvKiAqLyBjb21tZW50IGJsb2NrLCB0byBtYWtlIHZpc2libGUgaW4gZG9jcywgb25jZSB0aGlzIGlzIG1vcmUgdGhhbiBhIG5vb3ApXG4gICAgICAgIC8vXG4gICAgICAgIC8vIExvZ3MgdXNlciBvdXQgZnJvbSBzcGVjaWZpZWQgYWNjb3VudHMuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIEVwaWNlbnRlciBsb2dvdXQgaXMgbm90IGltcGxlbWVudGVkIHlldCwgc28gZm9yIG5vdyB0aGlzIGlzIGEgZHVtbXkgcHJvbWlzZSB0aGF0IGdldHMgYXV0b21hdGljYWxseSByZXNvbHZlZC5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gKipFeGFtcGxlKipcbiAgICAgICAgLy9cbiAgICAgICAgLy8gICAgICBhdXRoLmxvZ291dCgpO1xuICAgICAgICAvL1xuICAgICAgICAvLyAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAvLyBAcGFyYW0ge09iamVjdH0gYG9wdGlvbnNgIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgIC8vXG4gICAgICAgIGxvZ291dDogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBkdGQgPSAkLkRlZmVycmVkKCk7XG4gICAgICAgICAgICBkdGQucmVzb2x2ZSgpO1xuICAgICAgICAgICAgcmV0dXJuIGR0ZC5wcm9taXNlKCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgJC5leHRlbmQodGhpcywgcHVibGljQVBJKTtcbn07XG4iLCIvKipcbiAqICMjIENoYW5uZWwgU2VydmljZVxuICpcbiAqIFRoZSBFcGljZW50ZXIgcGxhdGZvcm0gcHJvdmlkZXMgYSBwdXNoIGNoYW5uZWwsIHdoaWNoIGFsbG93cyB5b3UgdG8gcHVibGlzaCBhbmQgc3Vic2NyaWJlIHRvIG1lc3NhZ2VzIHdpdGhpbiBhIFtwcm9qZWN0XSguLi8uLi8uLi9nbG9zc2FyeS8jcHJvamVjdHMpLCBbZ3JvdXBdKC4uLy4uLy4uL2dsb3NzYXJ5LyNncm91cHMpLCBvciBbbXVsdGlwbGF5ZXIgd29ybGRdKC4uLy4uLy4uL2dsb3NzYXJ5LyN3b3JsZCkuIFRoZXJlIGFyZSB0d28gbWFpbiB1c2UgY2FzZXMgZm9yIHRoZSBjaGFubmVsOiBldmVudCBub3RpZmljYXRpb25zIGFuZCBjaGF0IG1lc3NhZ2VzLlxuICpcbiAqIFRoZSBDaGFubmVsIFNlcnZpY2UgaXMgYSBidWlsZGluZyBibG9jayBmb3IgdGhpcyBmdW5jdGlvbmFsaXR5LiBJdCBjcmVhdGVzIGEgcHVibGlzaC1zdWJzY3JpYmUgb2JqZWN0LCBhbGxvd2luZyB5b3UgdG8gcHVibGlzaCBtZXNzYWdlcywgc3Vic2NyaWJlIHRvIG1lc3NhZ2VzLCBvciB1bnN1YnNjcmliZSBmcm9tIG1lc3NhZ2VzIGZvciBhIGdpdmVuICd0b3BpYycgb24gYSBgJC5jb21ldGRgIHRyYW5zcG9ydCBpbnN0YW5jZS5cbiAqXG4gKiBUeXBpY2FsbHksIHlvdSB1c2UgdGhlIFtFcGljZW50ZXIgQ2hhbm5lbCBNYW5hZ2VyXSguLi9lcGljZW50ZXItY2hhbm5lbC1tYW5hZ2VyLykgdG8gY3JlYXRlIG9yIHJldHJpZXZlIGNoYW5uZWxzLCB0aGVuIHVzZSB0aGUgQ2hhbm5lbCBTZXJ2aWNlIGBzdWJzY3JpYmUoKWAgYW5kIGBwdWJsaXNoKClgIG1ldGhvZHMgdG8gbGlzdGVuIHRvIG9yIHVwZGF0ZSBkYXRhLiAoRm9yIGFkZGl0aW9uYWwgYmFja2dyb3VuZCBvbiBFcGljZW50ZXIncyBwdXNoIGNoYW5uZWwsIHNlZSB0aGUgaW50cm9kdWN0b3J5IG5vdGVzIG9uIHRoZSBbUHVzaCBDaGFubmVsIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL211bHRpcGxheWVyL2NoYW5uZWwvKSBwYWdlLilcbiAqXG4gKiBZb3UnbGwgbmVlZCB0byBpbmNsdWRlIHRoZSBgZXBpY2VudGVyLW11bHRpcGxheWVyLWRlcGVuZGVuY2llcy5qc2AgbGlicmFyeSBpbiBhZGRpdGlvbiB0byB0aGUgYGVwaWNlbnRlci5qc2AgbGlicmFyeSBpbiB5b3VyIHByb2plY3QgdG8gdXNlIHRoZSBDaGFubmVsIFNlcnZpY2UuIFNlZSBbSW5jbHVkaW5nIEVwaWNlbnRlci5qc10oLi4vLi4vI2luY2x1ZGUpLlxuICpcbiAqIFRvIHVzZSB0aGUgQ2hhbm5lbCBTZXJ2aWNlLCBpbnN0YW50aWF0ZSBpdCwgdGhlbiBtYWtlIGNhbGxzIHRvIGFueSBvZiB0aGUgbWV0aG9kcyB5b3UgbmVlZC5cbiAqXG4gKiAgICAgICAgdmFyIGNzID0gbmV3IEYuc2VydmljZS5DaGFubmVsKCk7XG4gKiAgICAgICAgY3MucHVibGlzaCgnL2FjbWUtc2ltdWxhdGlvbnMvc3VwcGx5LWNoYWluLWdhbWUvZmFsbC1zZW1pbmFyL3J1bi92YXJpYWJsZXMnLCB7IHByaWNlOiA1MCB9KTtcbiAqXG4gKiBUaGUgcGFyYW1ldGVycyBmb3IgaW5zdGFudGlhdGluZyBhIENoYW5uZWwgU2VydmljZSBpbmNsdWRlOlxuICpcbiAqICogYG9wdGlvbnNgIFRoZSBvcHRpb25zIG9iamVjdCB0byBjb25maWd1cmUgdGhlIENoYW5uZWwgU2VydmljZS5cbiAqICogYG9wdGlvbnMuYmFzZWAgVGhlIGJhc2UgdG9waWMuIFRoaXMgaXMgYWRkZWQgYXMgYSBwcmVmaXggdG8gYWxsIGZ1cnRoZXIgdG9waWNzIHlvdSBwdWJsaXNoIG9yIHN1YnNjcmliZSB0byB3aGlsZSB3b3JraW5nIHdpdGggdGhpcyBDaGFubmVsIFNlcnZpY2UuXG4gKiAqIGBvcHRpb25zLnRvcGljUmVzb2x2ZXJgIEEgZnVuY3Rpb24gdGhhdCBwcm9jZXNzZXMgYWxsICd0b3BpY3MnIHBhc3NlZCBpbnRvIHRoZSBgcHVibGlzaGAgYW5kIGBzdWJzY3JpYmVgIG1ldGhvZHMuIFRoaXMgaXMgdXNlZnVsIGlmIHlvdSB3YW50IHRvIGltcGxlbWVudCB5b3VyIG93biBzZXJpYWxpemUgZnVuY3Rpb25zIGZvciBjb252ZXJ0aW5nIGN1c3RvbSBvYmplY3RzIHRvIHRvcGljIG5hbWVzLiBSZXR1cm5zIGEgU3RyaW5nLiBCeSBkZWZhdWx0LCBpdCBqdXN0IGVjaG9lcyB0aGUgdG9waWMuXG4gKiAqIGBvcHRpb25zLnRyYW5zcG9ydGAgVGhlIGluc3RhbmNlIG9mIGAkLmNvbWV0ZGAgdG8gaG9vayBvbnRvLiBTZWUgaHR0cDovL2RvY3MuY29tZXRkLm9yZy9yZWZlcmVuY2UvamF2YXNjcmlwdC5odG1sIGZvciBhZGRpdGlvbmFsIGJhY2tncm91bmQgb24gY29tZXRkLlxuICovXG5cbid1c2Ugc3RyaWN0JztcbnZhciBDaGFubmVsID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBiYXNlIHRvcGljLiBUaGlzIGlzIGFkZGVkIGFzIGEgcHJlZml4IHRvIGFsbCBmdXJ0aGVyIHRvcGljcyB5b3UgcHVibGlzaCBvciBzdWJzY3JpYmUgdG8gd2hpbGUgd29ya2luZyB3aXRoIHRoaXMgQ2hhbm5lbCBTZXJ2aWNlLlxuICAgICAgICAgKiBAdHlwZSB7c3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgYmFzZTogJycsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEEgZnVuY3Rpb24gdGhhdCBwcm9jZXNzZXMgYWxsICd0b3BpY3MnIHBhc3NlZCBpbnRvIHRoZSBgcHVibGlzaGAgYW5kIGBzdWJzY3JpYmVgIG1ldGhvZHMuIFRoaXMgaXMgdXNlZnVsIGlmIHlvdSB3YW50IHRvIGltcGxlbWVudCB5b3VyIG93biBzZXJpYWxpemUgZnVuY3Rpb25zIGZvciBjb252ZXJ0aW5nIGN1c3RvbSBvYmplY3RzIHRvIHRvcGljIG5hbWVzLiBCeSBkZWZhdWx0LCBpdCBqdXN0IGVjaG9lcyB0aGUgdG9waWMuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqXG4gICAgICAgICAqICogYHRvcGljYCBUb3BpYyB0byBwYXJzZS5cbiAgICAgICAgICpcbiAgICAgICAgICogKipSZXR1cm4gVmFsdWUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAqICpTdHJpbmcqOiBUaGlzIGZ1bmN0aW9uIHNob3VsZCByZXR1cm4gYSBzdHJpbmcgdG9waWMuXG4gICAgICAgICAqXG4gICAgICAgICAqIEB0eXBlIHtmdW5jdGlvbn1cbiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IHRvcGljIHRvcGljIHRvIHJlc29sdmVcbiAgICAgICAgICogQHJldHVybiB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdG9waWNSZXNvbHZlcjogZnVuY3Rpb24gKHRvcGljKSB7XG4gICAgICAgICAgICByZXR1cm4gdG9waWM7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBpbnN0YW5jZSBvZiBgJC5jb21ldGRgIHRvIGhvb2sgb250by5cbiAgICAgICAgICogQHR5cGUge29iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIHRyYW5zcG9ydDogbnVsbFxuICAgIH07XG4gICAgdGhpcy5jaGFubmVsT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBkZWZhdWx0cywgb3B0aW9ucyk7XG59O1xuXG52YXIgbWFrZU5hbWUgPSBmdW5jdGlvbiAoY2hhbm5lbE5hbWUsIHRvcGljKSB7XG4gICAgLy9SZXBsYWNlIHRyYWlsaW5nL2RvdWJsZSBzbGFzaGVzXG4gICAgdmFyIG5ld05hbWUgPSAoY2hhbm5lbE5hbWUgPyAoY2hhbm5lbE5hbWUgKyAnLycgKyB0b3BpYykgOiB0b3BpYykucmVwbGFjZSgvXFwvXFwvL2csICcvJykucmVwbGFjZSgvXFwvJC8sICcnKTtcbiAgICByZXR1cm4gbmV3TmFtZTtcbn07XG5cblxuQ2hhbm5lbC5wcm90b3R5cGUgPSAkLmV4dGVuZChDaGFubmVsLnByb3RvdHlwZSwge1xuXG4gICAgLy8gZnV0dXJlIGZ1bmN0aW9uYWxpdHk6XG4gICAgLy8gICAgICAvLyBTZXQgdGhlIGNvbnRleHQgZm9yIHRoZSBjYWxsYmFja1xuICAgIC8vICAgICAgY3Muc3Vic2NyaWJlKCdydW4nLCBmdW5jdGlvbiAoKSB7IHRoaXMuaW5uZXJIVE1MID0gJ1RyaWdnZXJlZCd9LCBkb2N1bWVudC5ib2R5KTtcbiAgICAgLy9cbiAgICAgLy8gICAgICAvLyBDb250cm9sIHRoZSBvcmRlciBvZiBvcGVyYXRpb25zIGJ5IHNldHRpbmcgdGhlIGBwcmlvcml0eWBcbiAgICAgLy8gICAgICBjcy5zdWJzY3JpYmUoJ3J1bicsIGNiLCB0aGlzLCB7cHJpb3JpdHk6IDl9KTtcbiAgICAgLy9cbiAgICAgLy8gICAgICAvLyBPbmx5IGV4ZWN1dGUgdGhlIGNhbGxiYWNrLCBgY2JgLCBpZiB0aGUgdmFsdWUgb2YgdGhlIGBwcmljZWAgdmFyaWFibGUgaXMgNTBcbiAgICAgLy8gICAgICBjcy5zdWJzY3JpYmUoJ3J1bi92YXJpYWJsZXMvcHJpY2UnLCBjYiwgdGhpcywge3ByaW9yaXR5OiAzMCwgdmFsdWU6IDUwfSk7XG4gICAgIC8vXG4gICAgIC8vICAgICAgLy8gT25seSBleGVjdXRlIHRoZSBjYWxsYmFjaywgYGNiYCwgaWYgdGhlIHZhbHVlIG9mIHRoZSBgcHJpY2VgIHZhcmlhYmxlIGlzIGdyZWF0ZXIgdGhhbiA1MFxuICAgICAvLyAgICAgIHN1YnNjcmliZSgncnVuL3ZhcmlhYmxlcy9wcmljZScsIGNiLCB0aGlzLCB7cHJpb3JpdHk6IDMwLCB2YWx1ZTogJz41MCd9KTtcbiAgICAgLy9cbiAgICAgLy8gICAgICAvLyBPbmx5IGV4ZWN1dGUgdGhlIGNhbGxiYWNrLCBgY2JgLCBpZiB0aGUgdmFsdWUgb2YgdGhlIGBwcmljZWAgdmFyaWFibGUgaXMgZXZlblxuICAgICAvLyAgICAgIHN1YnNjcmliZSgncnVuL3ZhcmlhYmxlcy9wcmljZScsIGNiLCB0aGlzLCB7cHJpb3JpdHk6IDMwLCB2YWx1ZTogZnVuY3Rpb24gKHZhbCkge3JldHVybiB2YWwgJSAyID09PSAwfX0pO1xuXG5cbiAgICAvKipcbiAgICAgKiBTdWJzY3JpYmUgdG8gY2hhbmdlcyBvbiBhIHRvcGljLlxuICAgICAqXG4gICAgICogVGhlIHRvcGljIHNob3VsZCBpbmNsdWRlIHRoZSBmdWxsIHBhdGggb2YgdGhlIGFjY291bnQgaWQgKCoqVGVhbSBJRCoqIGZvciB0ZWFtIHByb2plY3RzKSwgcHJvamVjdCBpZCwgYW5kIGdyb3VwIG5hbWUuIChJbiBtb3N0IGNhc2VzLCBpdCBpcyBzaW1wbGVyIHRvIHVzZSB0aGUgW0VwaWNlbnRlciBDaGFubmVsIE1hbmFnZXJdKC4uL2VwaWNlbnRlci1jaGFubmVsLW1hbmFnZXIvKSBpbnN0ZWFkLCBpbiB3aGljaCBjYXNlIHRoaXMgaXMgY29uZmlndXJlZCBmb3IgeW91LilcbiAgICAgKlxuICAgICAqICAqKkV4YW1wbGVzKipcbiAgICAgKlxuICAgICAqICAgICAgdmFyIGNiID0gZnVuY3Rpb24odmFsKSB7IGNvbnNvbGUubG9nKHZhbC5kYXRhKTsgfTtcbiAgICAgKlxuICAgICAqICAgICAgLy8gU3Vic2NyaWJlIHRvIGNoYW5nZXMgb24gYSB0b3AtbGV2ZWwgJ3J1bicgdG9waWNcbiAgICAgKiAgICAgIGNzLnN1YnNjcmliZSgnL2FjbWUtc2ltdWxhdGlvbnMvc3VwcGx5LWNoYWluLWdhbWUvZmFsbC1zZW1pbmFyL3J1bicsIGNiKTtcbiAgICAgKlxuICAgICAqICAgICAgLy8gU3Vic2NyaWJlIHRvIGNoYW5nZXMgb24gY2hpbGRyZW4gb2YgdGhlICdydW4nIHRvcGljLiBOb3RlIHRoaXMgd2lsbCBhbHNvIGJlIHRyaWdnZXJlZCBmb3IgY2hhbmdlcyB0byBydW4ueC55LnouXG4gICAgICogICAgICBjcy5zdWJzY3JpYmUoJy9hY21lLXNpbXVsYXRpb25zL3N1cHBseS1jaGFpbi1nYW1lL2ZhbGwtc2VtaW5hci9ydW4vKicsIGNiKTtcbiAgICAgKlxuICAgICAqICAgICAgLy8gU3Vic2NyaWJlIHRvIGNoYW5nZXMgb24gYm90aCB0aGUgdG9wLWxldmVsICdydW4nIHRvcGljIGFuZCBpdHMgY2hpbGRyZW5cbiAgICAgKiAgICAgIGNzLnN1YnNjcmliZShbJy9hY21lLXNpbXVsYXRpb25zL3N1cHBseS1jaGFpbi1nYW1lL2ZhbGwtc2VtaW5hci9ydW4nLFxuICAgICAqICAgICAgICAgICcvYWNtZS1zaW11bGF0aW9ucy9zdXBwbHktY2hhaW4tZ2FtZS9mYWxsLXNlbWluYXIvcnVuLyonXSwgY2IpO1xuICAgICAqXG4gICAgICogICAgICAvLyBTdWJzY3JpYmUgdG8gY2hhbmdlcyBvbiBhIHBhcnRpY3VsYXIgdmFyaWFibGVcbiAgICAgKiAgICAgIHN1YnNjcmliZSgnL2FjbWUtc2ltdWxhdGlvbnMvc3VwcGx5LWNoYWluLWdhbWUvZmFsbC1zZW1pbmFyL3J1bi92YXJpYWJsZXMvcHJpY2UnLCBjYik7XG4gICAgICpcbiAgICAgKlxuICAgICAqICoqUmV0dXJuIFZhbHVlKipcbiAgICAgKlxuICAgICAqICogKlN0cmluZyogUmV0dXJucyBhIHRva2VuIHlvdSBjYW4gbGF0ZXIgdXNlIHRvIHVuc3Vic2NyaWJlLlxuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd8QXJyYXl9ICAgdG9waWMgICAgTGlzdCBvZiB0b3BpY3MgdG8gbGlzdGVuIGZvciBjaGFuZ2VzIG9uLlxuICAgICAqIEBwYXJhbSAge0Z1bmN0aW9ufSBjYWxsYmFjayBDYWxsYmFjayBmdW5jdGlvbiB0byBleGVjdXRlLiBDYWxsYmFjayBpcyBjYWxsZWQgd2l0aCBzaWduYXR1cmUgYChldnQsIHBheWxvYWQsIG1ldGFkYXRhKWAuXG4gICAgICogQHBhcmFtICB7T2JqZWN0fSAgIGNvbnRleHQgIENvbnRleHQgaW4gd2hpY2ggdGhlIGBjYWxsYmFja2AgaXMgZXhlY3V0ZWQuXG4gICAgICogQHBhcmFtICB7T2JqZWN0fSAgIG9wdGlvbnMgIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICogQHBhcmFtICB7TnVtYmVyfSAgIG9wdGlvbnMucHJpb3JpdHkgIFVzZWQgdG8gY29udHJvbCBvcmRlciBvZiBvcGVyYXRpb25zLiBEZWZhdWx0cyB0byAwLiBDYW4gYmUgYW55ICt2ZSBvciAtdmUgbnVtYmVyLlxuICAgICAqIEBwYXJhbSAge1N0cmluZ3xOdW1iZXJ8RnVuY3Rpb259ICAgb3B0aW9ucy52YWx1ZSBUaGUgYGNhbGxiYWNrYCBpcyBvbmx5IHRyaWdnZXJlZCBpZiB0aGlzIGNvbmRpdGlvbiBtYXRjaGVzLiBTZWUgZXhhbXBsZXMgZm9yIGRldGFpbHMuXG4gICAgICogQHJldHVybiB7c3RyaW5nfSBTdWJzY3JpcHRpb24gSURcbiAgICAgKi9cbiAgICBzdWJzY3JpYmU6IGZ1bmN0aW9uICh0b3BpYywgY2FsbGJhY2ssIGNvbnRleHQsIG9wdGlvbnMpIHtcblxuICAgICAgICB2YXIgdG9waWNzID0gW10uY29uY2F0KHRvcGljKTtcbiAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgdmFyIHN1YnNjcmlwdGlvbklkcyA9IFtdO1xuICAgICAgICB2YXIgb3B0cyA9IG1lLmNoYW5uZWxPcHRpb25zO1xuXG4gICAgICAgIG9wdHMudHJhbnNwb3J0LmJhdGNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICQuZWFjaCh0b3BpY3MsIGZ1bmN0aW9uIChpbmRleCwgdG9waWMpIHtcbiAgICAgICAgICAgICAgICB0b3BpYyA9IG1ha2VOYW1lKG9wdHMuYmFzZSwgb3B0cy50b3BpY1Jlc29sdmVyKHRvcGljKSk7XG4gICAgICAgICAgICAgICAgc3Vic2NyaXB0aW9uSWRzLnB1c2gob3B0cy50cmFuc3BvcnQuc3Vic2NyaWJlKHRvcGljLCBjYWxsYmFjaykpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gKHN1YnNjcmlwdGlvbklkc1sxXSA/IHN1YnNjcmlwdGlvbklkcyA6IHN1YnNjcmlwdGlvbklkc1swXSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFB1Ymxpc2ggZGF0YSB0byBhIHRvcGljLlxuICAgICAqXG4gICAgICogKipFeGFtcGxlcyoqXG4gICAgICpcbiAgICAgKiAgICAgIC8vIFNlbmQgZGF0YSB0byBhbGwgc3Vic2NyaWJlcnMgb2YgdGhlICdydW4nIHRvcGljXG4gICAgICogICAgICBjcy5wdWJsaXNoKCcvYWNtZS1zaW11bGF0aW9ucy9zdXBwbHktY2hhaW4tZ2FtZS9mYWxsLXNlbWluYXIvcnVuJywgeyBjb21wbGV0ZWQ6IGZhbHNlIH0pO1xuICAgICAqXG4gICAgICogICAgICAvLyBTZW5kIGRhdGEgdG8gYWxsIHN1YnNjcmliZXJzIG9mIHRoZSAncnVuL3ZhcmlhYmxlcycgdG9waWNcbiAgICAgKiAgICAgIGNzLnB1Ymxpc2goJy9hY21lLXNpbXVsYXRpb25zL3N1cHBseS1jaGFpbi1nYW1lL2ZhbGwtc2VtaW5hci9ydW4vdmFyaWFibGVzJywgeyBwcmljZTogNTAgfSk7XG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtICB7U3RyaW5nfSB0b3BpYyBUb3BpYyB0byBwdWJsaXNoIHRvLlxuICAgICAqIEBwYXJhbSAgeyp9IGRhdGEgIERhdGEgdG8gcHVibGlzaCB0byB0b3BpYy5cbiAgICAgKiBAcmV0dXJuIHtBcnJheSB8IE9iamVjdH0gUmVzcG9uc2VzIHRvIHB1Ymxpc2hlZCBkYXRhXG4gICAgICpcbiAgICAgKi9cbiAgICBwdWJsaXNoOiBmdW5jdGlvbiAodG9waWMsIGRhdGEpIHtcbiAgICAgICAgdmFyIHRvcGljcyA9IFtdLmNvbmNhdCh0b3BpYyk7XG4gICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgIHZhciByZXR1cm5PYmpzID0gW107XG4gICAgICAgIHZhciBvcHRzID0gbWUuY2hhbm5lbE9wdGlvbnM7XG5cblxuICAgICAgICBvcHRzLnRyYW5zcG9ydC5iYXRjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAkLmVhY2godG9waWNzLCBmdW5jdGlvbiAoaW5kZXgsIHRvcGljKSB7XG4gICAgICAgICAgICAgICAgdG9waWMgPSBtYWtlTmFtZShvcHRzLmJhc2UsIG9wdHMudG9waWNSZXNvbHZlcih0b3BpYykpO1xuICAgICAgICAgICAgICAgIGlmICh0b3BpYy5jaGFyQXQodG9waWMubGVuZ3RoIC0gMSkgPT09ICcqJykge1xuICAgICAgICAgICAgICAgICAgICB0b3BpYyA9IHRvcGljLnJlcGxhY2UoL1xcKiskLywgJycpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ1lvdSBjYW4gY2Fubm90IHB1Ymxpc2ggdG8gY2hhbm5lbHMgd2l0aCB3aWxkY2FyZHMuIFB1Ymxpc2hpbmcgdG8gJywgdG9waWMsICdpbnN0ZWFkJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybk9ianMucHVzaChvcHRzLnRyYW5zcG9ydC5wdWJsaXNoKHRvcGljLCBkYXRhKSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiAocmV0dXJuT2Jqc1sxXSA/IHJldHVybk9ianMgOiByZXR1cm5PYmpzWzBdKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVW5zdWJzY3JpYmUgZnJvbSBjaGFuZ2VzIHRvIGEgdG9waWMuXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgICBjcy51bnN1YnNjcmliZSgnc2FtcGxlVG9rZW4nKTtcbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICogQHBhcmFtICB7U3RyaW5nfSB0b2tlbiBUaGUgdG9rZW4gZm9yIHRvcGljIGlzIHJldHVybmVkIHdoZW4geW91IGluaXRpYWxseSBzdWJzY3JpYmUuIFBhc3MgaXQgaGVyZSB0byB1bnN1YnNjcmliZSBmcm9tIHRoYXQgdG9waWMuXG4gICAgICogQHJldHVybiB7T2JqZWN0fSByZWZlcmVuY2UgdG8gY3VycmVudCBpbnN0YW5jZVxuICAgICAqL1xuICAgIHVuc3Vic2NyaWJlOiBmdW5jdGlvbiAodG9rZW4pIHtcbiAgICAgICAgdGhpcy5jaGFubmVsT3B0aW9ucy50cmFuc3BvcnQudW5zdWJzY3JpYmUodG9rZW4pO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogU3RhcnQgbGlzdGVuaW5nIGZvciBldmVudHMgb24gdGhpcyBpbnN0YW5jZS4gU2lnbmF0dXJlIGlzIHNhbWUgYXMgZm9yIGpRdWVyeSBFdmVudHM6IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS9vbi8uXG4gICAgICpcbiAgICAgKiBTdXBwb3J0ZWQgZXZlbnRzIGFyZTogYGNvbm5lY3RgLCBgZGlzY29ubmVjdGAsIGBzdWJzY3JpYmVgLCBgdW5zdWJzY3JpYmVgLCBgcHVibGlzaGAsIGBlcnJvcmAuXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50IFRoZSBldmVudCB0eXBlLiBTZWUgbW9yZSBkZXRhaWwgYXQgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL29uLy5cbiAgICAgKi9cbiAgICBvbjogZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICQodGhpcykub24uYXBwbHkoJCh0aGlzKSwgYXJndW1lbnRzKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogU3RvcCBsaXN0ZW5pbmcgZm9yIGV2ZW50cyBvbiB0aGlzIGluc3RhbmNlLiBTaWduYXR1cmUgaXMgc2FtZSBhcyBmb3IgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL29mZi8uXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50IFRoZSBldmVudCB0eXBlLiBTZWUgbW9yZSBkZXRhaWwgYXQgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL29mZi8uXG4gICAgICovXG4gICAgb2ZmOiBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgJCh0aGlzKS5vZmYuYXBwbHkoJCh0aGlzKSwgYXJndW1lbnRzKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVHJpZ2dlciBldmVudHMgYW5kIGV4ZWN1dGUgaGFuZGxlcnMuIFNpZ25hdHVyZSBpcyBzYW1lIGFzIGZvciBqUXVlcnkgRXZlbnRzOiBodHRwOi8vYXBpLmpxdWVyeS5jb20vdHJpZ2dlci8uXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50IFRoZSBldmVudCB0eXBlLiBTZWUgbW9yZSBkZXRhaWwgYXQgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL3RyaWdnZXIvLlxuICAgICAqL1xuICAgIHRyaWdnZXI6IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAkKHRoaXMpLnRyaWdnZXIuYXBwbHkoJCh0aGlzKSwgYXJndW1lbnRzKTtcbiAgICB9XG5cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IENoYW5uZWw7XG4iLCIvKipcbiAqIEBjbGFzcyBDb25maWd1cmF0aW9uU2VydmljZVxuICpcbiAqIEFsbCBzZXJ2aWNlcyB0YWtlIGluIGEgY29uZmlndXJhdGlvbiBzZXR0aW5ncyBvYmplY3QgdG8gY29uZmlndXJlIHRoZW1zZWx2ZXMuIEEgSlMgaGFzaCB7fSBpcyBhIHZhbGlkIGNvbmZpZ3VyYXRpb24gb2JqZWN0LCBidXQgb3B0aW9uYWxseSB5b3UgY2FuIHVzZSB0aGUgY29uZmlndXJhdGlvbiBzZXJ2aWNlIHRvIHRvZ2dsZSBjb25maWdzIGJhc2VkIG9uIHRoZSBlbnZpcm9ubWVudFxuICpcbiAqIEBleGFtcGxlXG4gKiAgICAgdmFyIGNzID0gcmVxdWlyZSgnY29uZmlndXJhdGlvbi1zZXJ2aWNlJykoe1xuICogICAgICAgICAgZGV2OiB7IC8vZW52aXJvbm1lbnRcbiAgICAgICAgICAgICAgICBwb3J0OiAzMDAwLFxuICAgICAgICAgICAgICAgIGhvc3Q6ICdsb2NhbGhvc3QnLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHByb2Q6IHtcbiAgICAgICAgICAgICAgICBwb3J0OiA4MDgwLFxuICAgICAgICAgICAgICAgIGhvc3Q6ICdhcGkuZm9yaW8uY29tJyxcbiAgICAgICAgICAgICAgICBsb2dMZXZlbDogJ25vbmUnXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgbG9nTGV2ZWw6ICdERUJVRycgLy9nbG9iYWxcbiAqICAgICB9KTtcbiAqXG4gKiAgICAgIGNzLmdldCgnbG9nTGV2ZWwnKTsgLy9yZXR1cm5zICdERUJVRydcbiAqXG4gKiAgICAgIGNzLnNldEVudignZGV2Jyk7XG4gKiAgICAgIGNzLmdldCgnbG9nTGV2ZWwnKTsgLy9yZXR1cm5zICdERUJVRydcbiAqXG4gKiAgICAgIGNzLnNldEVudigncHJvZCcpO1xuICogICAgICBjcy5nZXQoJ2xvZ0xldmVsJyk7IC8vcmV0dXJucyAnbm9uZSdcbiAqXG4gKi9cblxuJ3VzZSBzdHJpY3QnO1xudmFyIHVybFNlcnZpY2UgPSByZXF1aXJlKCcuL3VybC1jb25maWctc2VydmljZScpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICAvL1RPRE86IEVudmlyb25tZW50c1xuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgbG9nTGV2ZWw6ICdOT05FJ1xuICAgIH07XG4gICAgdmFyIHNlcnZpY2VPcHRpb25zID0gJC5leHRlbmQoe30sIGRlZmF1bHRzLCBjb25maWcpO1xuICAgIHNlcnZpY2VPcHRpb25zLnNlcnZlciA9IHVybFNlcnZpY2Uoc2VydmljZU9wdGlvbnMuc2VydmVyKTtcblxuICAgIHJldHVybiB7XG5cbiAgICAgICAgZGF0YTogc2VydmljZU9wdGlvbnMsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNldCB0aGUgZW52aXJvbm1lbnQga2V5IHRvIGdldCBjb25maWd1cmF0aW9uIG9wdGlvbnMgZnJvbVxuICAgICAgICAgKiBAcGFyYW0geyBzdHJpbmd9IGVudlxuICAgICAgICAgKi9cbiAgICAgICAgc2V0RW52OiBmdW5jdGlvbiAoZW52KSB7XG5cbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogR2V0IGNvbmZpZ3VyYXRpb24uXG4gICAgICAgICAqIEBwYXJhbSAgeyBzdHJpbmd9IHByb3BlcnR5IG9wdGlvbmFsXG4gICAgICAgICAqIEByZXR1cm4geyp9ICAgICAgICAgIFZhbHVlIG9mIHByb3BlcnR5IGlmIHNwZWNpZmllZCwgdGhlIGVudGlyZSBjb25maWcgb2JqZWN0IG90aGVyd2lzZVxuICAgICAgICAgKi9cbiAgICAgICAgZ2V0OiBmdW5jdGlvbiAocHJvcGVydHkpIHtcbiAgICAgICAgICAgIHJldHVybiBzZXJ2aWNlT3B0aW9uc1twcm9wZXJ0eV07XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNldCBjb25maWd1cmF0aW9uLlxuICAgICAgICAgKiBAcGFyYW0gIHsgc3RyaW5nfE9iamVjdH0ga2V5IGlmIGEga2V5IGlzIHByb3ZpZGVkLCBzZXQgYSBrZXkgdG8gdGhhdCB2YWx1ZS4gT3RoZXJ3aXNlIG1lcmdlIG9iamVjdCB3aXRoIGN1cnJlbnQgY29uZmlnXG4gICAgICAgICAqIEBwYXJhbSAgeyp9IHZhbHVlICB2YWx1ZSBmb3IgcHJvdmlkZWQga2V5XG4gICAgICAgICAqL1xuICAgICAgICBzZXQ6IGZ1bmN0aW9uIChrZXksIHZhbHVlKSB7XG4gICAgICAgICAgICBzZXJ2aWNlT3B0aW9uc1trZXldID0gdmFsdWU7XG4gICAgICAgIH1cbiAgICB9O1xufTtcblxuIiwiLyoqXG4gKiAjIyBEYXRhIEFQSSBTZXJ2aWNlXG4gKlxuICogVGhlIERhdGEgQVBJIFNlcnZpY2UgYWxsb3dzIHlvdSB0byBjcmVhdGUsIGFjY2VzcywgYW5kIG1hbmlwdWxhdGUgZGF0YSByZWxhdGVkIHRvIGFueSBvZiB5b3VyIHByb2plY3RzLiBEYXRhIGFyZSBvcmdhbml6ZWQgaW4gY29sbGVjdGlvbnMuIEVhY2ggY29sbGVjdGlvbiBjb250YWlucyBhIGRvY3VtZW50OyBlYWNoIGVsZW1lbnQgb2YgdGhpcyB0b3AtbGV2ZWwgZG9jdW1lbnQgaXMgYSBKU09OIG9iamVjdC4gKFNlZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIG9uIHRoZSB1bmRlcmx5aW5nIFtEYXRhIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL2RhdGFfYXBpLykuKVxuICpcbiAqIEFsbCBBUEkgY2FsbHMgdGFrZSBpbiBhbiBcIm9wdGlvbnNcIiBvYmplY3QgYXMgdGhlIGxhc3QgcGFyYW1ldGVyLiBUaGUgb3B0aW9ucyBjYW4gYmUgdXNlZCB0byBleHRlbmQvb3ZlcnJpZGUgdGhlIERhdGEgQVBJIFNlcnZpY2UgZGVmYXVsdHMuIEluIHBhcnRpY3VsYXIsIHRoZXJlIGFyZSB0aHJlZSByZXF1aXJlZCBwYXJhbWV0ZXJzIHdoZW4geW91IGluc3RhbnRpYXRlIHRoZSBEYXRhIFNlcnZpY2U6XG4gKlxuICogKiBgYWNjb3VudGA6IEVwaWNlbnRlciBhY2NvdW50IGlkICgqKlRlYW0gSUQqKiBmb3IgdGVhbSBwcm9qZWN0cywgKipVc2VyIElEKiogZm9yIHBlcnNvbmFsIHByb2plY3RzKS5cbiAqICogYHByb2plY3RgOiBFcGljZW50ZXIgcHJvamVjdCBpZC5cbiAqICogYHJvb3RgOiBUaGUgdGhlIG5hbWUgb2YgdGhlIGNvbGxlY3Rpb24uIElmIHlvdSBoYXZlIG11bHRpcGxlIGNvbGxlY3Rpb25zIHdpdGhpbiBlYWNoIG9mIHlvdXIgcHJvamVjdHMsIHlvdSBjYW4gYWxzbyBwYXNzIHRoZSBjb2xsZWN0aW9uIG5hbWUgYXMgYW4gb3B0aW9uIGZvciBlYWNoIGNhbGwuXG4gKlxuICogICAgICAgdmFyIGRzID0gbmV3IEYuc2VydmljZS5EYXRhKHtcbiAqICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAqICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gKiAgICAgICAgICByb290OiAnc3VydmV5LXJlc3BvbnNlcycsXG4gKiAgICAgICAgICBzZXJ2ZXI6IHsgaG9zdDogJ2FwaS5mb3Jpby5jb20nIH1cbiAqICAgICAgIH0pO1xuICogICAgICAgZHMuc2F2ZUFzKCd1c2VyMScsXG4gKiAgICAgICAgICB7ICdxdWVzdGlvbjEnOiAyLCAncXVlc3Rpb24yJzogMTAsXG4gKiAgICAgICAgICAncXVlc3Rpb24zJzogZmFsc2UsICdxdWVzdGlvbjQnOiAnc29tZXRpbWVzJyB9ICk7XG4gKiAgICAgICBkcy5zYXZlQXMoJ3VzZXIyJyxcbiAqICAgICAgICAgIHsgJ3F1ZXN0aW9uMSc6IDMsICdxdWVzdGlvbjInOiA4LFxuICogICAgICAgICAgJ3F1ZXN0aW9uMyc6IHRydWUsICdxdWVzdGlvbjQnOiAnYWx3YXlzJyB9ICk7XG4gKiAgICAgICBkcy5xdWVyeSgnJyx7ICdxdWVzdGlvbjInOiB7ICckZ3QnOiA5fSB9KTtcbiAqXG4gKiBOb3RlIHRoYXQgaW4gYWRkaXRpb24gdG8gdGhlIGBhY2NvdW50YCwgYHByb2plY3RgLCBhbmQgYHJvb3RgLCB0aGUgRGF0YSBTZXJ2aWNlIHBhcmFtZXRlcnMgb3B0aW9uYWxseSBpbmNsdWRlIGEgYHNlcnZlcmAgb2JqZWN0LCB3aG9zZSBgaG9zdGAgZmllbGQgY29udGFpbnMgdGhlIFVSSSBvZiB0aGUgRm9yaW8gc2VydmVyLiBUaGlzIGlzIGF1dG9tYXRpY2FsbHkgc2V0LCBidXQgeW91IGNhbiBwYXNzIGl0IGV4cGxpY2l0bHkgaWYgZGVzaXJlZC4gSXQgaXMgbW9zdCBjb21tb25seSB1c2VkIGZvciBjbGFyaXR5IHdoZW4geW91IGFyZSBbaG9zdGluZyBhbiBFcGljZW50ZXIgcHJvamVjdCBvbiB5b3VyIG93biBzZXJ2ZXJdKC4uLy4uLy4uL2hvd190by9zZWxmX2hvc3RpbmcvKS5cbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBxdXRpbCA9IHJlcXVpcmUoJy4uL3V0aWwvcXVlcnktdXRpbCcpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIE5hbWUgb2YgY29sbGVjdGlvbi4gUmVxdWlyZWQuIERlZmF1bHRzIHRvIGAvYCwgdGhhdCBpcywgdGhlIHJvb3QgbGV2ZWwgb2YgeW91ciBwcm9qZWN0IGF0IGBmb3Jpby5jb20vYXBwL3lvdXItYWNjb3VudC1pZC95b3VyLXByb2plY3QtaWQvYCwgYnV0IG11c3QgYmUgc2V0IHRvIGEgY29sbGVjdGlvbiBuYW1lLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgcm9vdDogJy8nLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgYWNjb3VudCBpZC4gSW4gdGhlIEVwaWNlbnRlciBVSSwgdGhpcyBpcyB0aGUgKipUZWFtIElEKiogKGZvciB0ZWFtIHByb2plY3RzKSBvciAqKlVzZXIgSUQqKiAoZm9yIHBlcnNvbmFsIHByb2plY3RzKS4gRGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nLiBJZiBsZWZ0IHVuZGVmaW5lZCwgdGFrZW4gZnJvbSB0aGUgVVJMLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgYWNjb3VudDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgcHJvamVjdCBpZC4gRGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nLiBJZiBsZWZ0IHVuZGVmaW5lZCwgdGFrZW4gZnJvbSB0aGUgVVJMLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgcHJvamVjdDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBGb3Igb3BlcmF0aW9ucyB0aGF0IHJlcXVpcmUgYXV0aGVudGljYXRpb24sIHBhc3MgaW4gdGhlIHVzZXIgYWNjZXNzIHRva2VuIChkZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcpLiBJZiB0aGUgdXNlciBpcyBhbHJlYWR5IGxvZ2dlZCBpbiB0byBFcGljZW50ZXIsIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiBpcyBhbHJlYWR5IHNldCBpbiBhIGNvb2tpZSBhbmQgYXV0b21hdGljYWxseSBsb2FkZWQgZnJvbSB0aGVyZS4gKFNlZSBbbW9yZSBiYWNrZ3JvdW5kIG9uIGFjY2VzcyB0b2tlbnNdKC4uLy4uLy4uL3Byb2plY3RfYWNjZXNzLykpLlxuICAgICAgICAgKiBAc2VlIFtBdXRoZW50aWNhdGlvbiBBUEkgU2VydmljZV0oLi4vYXV0aC1hcGktc2VydmljZS8pIGZvciBnZXR0aW5nIHRva2Vucy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHRva2VuOiB1bmRlZmluZWQsXG5cbiAgICAgICAgLy9PcHRpb25zIHRvIHBhc3Mgb24gdG8gdGhlIHVuZGVybHlpbmcgdHJhbnNwb3J0IGxheWVyXG4gICAgICAgIHRyYW5zcG9ydDoge31cbiAgICB9O1xuICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICB2YXIgc2VydmljZU9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMoZGVmYXVsdHMsIGNvbmZpZyk7XG5cbiAgICB2YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2Uoc2VydmljZU9wdGlvbnMpLmdldCgnc2VydmVyJyk7XG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLmFjY291bnQpIHtcbiAgICAgICAgdXJsQ29uZmlnLmFjY291bnRQYXRoID0gc2VydmljZU9wdGlvbnMuYWNjb3VudDtcbiAgICB9XG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnByb2plY3QpIHtcbiAgICAgICAgdXJsQ29uZmlnLnByb2plY3RQYXRoID0gc2VydmljZU9wdGlvbnMucHJvamVjdDtcbiAgICB9XG5cbiAgICB2YXIgZ2V0VVJMID0gZnVuY3Rpb24gKGtleSwgcm9vdCkge1xuICAgICAgICBpZiAoIXJvb3QpIHtcbiAgICAgICAgICAgIHJvb3QgPSBzZXJ2aWNlT3B0aW9ucy5yb290O1xuICAgICAgICB9XG4gICAgICAgIHZhciB1cmwgPSB1cmxDb25maWcuZ2V0QVBJUGF0aCgnZGF0YScpICsgcXV0aWwuYWRkVHJhaWxpbmdTbGFzaChyb290KTtcbiAgICAgICAgaWYgKGtleSkge1xuICAgICAgICAgICAgdXJsICs9IHF1dGlsLmFkZFRyYWlsaW5nU2xhc2goa2V5KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdXJsO1xuICAgIH07XG5cbiAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LCB7XG4gICAgICAgIHVybDogZ2V0VVJMXG4gICAgfSk7XG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnRva2VuKSB7XG4gICAgICAgIGh0dHBPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KGh0dHBPcHRpb25zKTtcblxuICAgIHZhciBwdWJsaWNBUEkgPSB7XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNlYXJjaCBmb3IgZGF0YSB3aXRoaW4gYSBjb2xsZWN0aW9uLlxuICAgICAgICAgKlxuICAgICAgICAgKiBTZWFyY2hpbmcgdXNpbmcgY29tcGFyaXNvbiBvciBsb2dpY2FsIG9wZXJhdG9ycyAoYXMgb3Bwb3NlZCB0byBleGFjdCBtYXRjaGVzKSByZXF1aXJlcyBNb25nb0RCIHN5bnRheC4gU2VlIHRoZSB1bmRlcmx5aW5nIFtEYXRhIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL2RhdGFfYXBpLyNzZWFyY2hpbmcpIGZvciBhZGRpdGlvbmFsIGRldGFpbHMuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZXMqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIHJlcXVlc3QgYWxsIGRhdGEgYXNzb2NpYXRlZCB3aXRoIGRvY3VtZW50ICd1c2VyMSdcbiAgICAgICAgICogICAgICBkcy5xdWVyeSgndXNlcjEnKTtcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBleGFjdCBtYXRjaGluZzpcbiAgICAgICAgICogICAgICAvLyByZXF1ZXN0IGFsbCBkb2N1bWVudHMgaW4gY29sbGVjdGlvbiB3aGVyZSAncXVlc3Rpb24yJyBpcyA5XG4gICAgICAgICAqICAgICAgZHMucXVlcnkoJycsIHsgJ3F1ZXN0aW9uMic6IDl9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBjb21wYXJpc29uIG9wZXJhdG9yczpcbiAgICAgICAgICogICAgICAvLyByZXF1ZXN0IGFsbCBkb2N1bWVudHMgaW4gY29sbGVjdGlvblxuICAgICAgICAgKiAgICAgIC8vIHdoZXJlICdxdWVzdGlvbjInIGlzIGdyZWF0ZXIgdGhhbiA5XG4gICAgICAgICAqICAgICAgZHMucXVlcnkoJycsIHsgJ3F1ZXN0aW9uMic6IHsgJyRndCc6IDl9IH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIGxvZ2ljYWwgb3BlcmF0b3JzOlxuICAgICAgICAgKiAgICAgIC8vIHJlcXVlc3QgYWxsIGRvY3VtZW50cyBpbiBjb2xsZWN0aW9uXG4gICAgICAgICAqICAgICAgLy8gd2hlcmUgJ3F1ZXN0aW9uMicgaXMgbGVzcyB0aGFuIDEwLCBhbmQgJ3F1ZXN0aW9uMycgaXMgZmFsc2VcbiAgICAgICAgICogICAgICBkcy5xdWVyeSgnJywgeyAnJGFuZCc6IFsgeyAncXVlc3Rpb24yJzogeyAnJGx0JzoxMH0gfSwgeyAncXVlc3Rpb24zJzogZmFsc2UgfV0gfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gcmVndWxhciBleHByZXNzc2lvbnM6IHVzZSBhbnkgUGVybC1jb21wYXRpYmxlIHJlZ3VsYXIgZXhwcmVzc2lvbnNcbiAgICAgICAgICogICAgICAvLyByZXF1ZXN0IGFsbCBkb2N1bWVudHMgaW4gY29sbGVjdGlvblxuICAgICAgICAgKiAgICAgIC8vIHdoZXJlICdxdWVzdGlvbjUnIGNvbnRhaW5zIHRoZSBzdHJpbmcgJy4qZGF5J1xuICAgICAgICAgKiAgICAgIGRzLnF1ZXJ5KCcnLCB7ICdxdWVzdGlvbjUnOiB7ICckcmVnZXgnOiAnLipkYXknIH0gfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSBrZXkgVGhlIG5hbWUgb2YgdGhlIGRvY3VtZW50IHRvIHNlYXJjaC4gUGFzcyB0aGUgZW1wdHkgc3RyaW5nICgnJykgdG8gc2VhcmNoIHRoZSBlbnRpcmUgY29sbGVjdGlvbi5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IHF1ZXJ5IFRoZSBxdWVyeSBvYmplY3QuIEZvciBleGFjdCBtYXRjaGluZywgdGhpcyBvYmplY3QgY29udGFpbnMgdGhlIGZpZWxkIG5hbWUgYW5kIGZpZWxkIHZhbHVlIHRvIG1hdGNoLiBGb3IgbWF0Y2hpbmcgYmFzZWQgb24gY29tcGFyaXNvbiwgdGhpcyBvYmplY3QgY29udGFpbnMgdGhlIGZpZWxkIG5hbWUgYW5kIHRoZSBjb21wYXJpc29uIGV4cHJlc3Npb24uIEZvciBtYXRjaGluZyBiYXNlZCBvbiBsb2dpY2FsIG9wZXJhdG9ycywgdGhpcyBvYmplY3QgY29udGFpbnMgYW4gZXhwcmVzc2lvbiB1c2luZyBNb25nb0RCIHN5bnRheC4gU2VlIHRoZSB1bmRlcmx5aW5nIFtEYXRhIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL2RhdGFfYXBpLyNzZWFyY2hpbmcpIGZvciBhZGRpdGlvbmFsIGV4YW1wbGVzLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3V0cHV0TW9kaWZpZXIgKE9wdGlvbmFsKSBBdmFpbGFibGUgZmllbGRzIGluY2x1ZGU6IGBzdGFydHJlY29yZGAsIGBlbmRyZWNvcmRgLCBgc29ydGAsIGFuZCBgZGlyZWN0aW9uYCAoYGFzY2Agb3IgYGRlc2NgKS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX0gXG4gICAgICAgICAqL1xuICAgICAgICBxdWVyeTogZnVuY3Rpb24gKGtleSwgcXVlcnksIG91dHB1dE1vZGlmaWVyLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcGFyYW1zID0gJC5leHRlbmQodHJ1ZSwgeyBxOiBxdWVyeSB9LCBvdXRwdXRNb2RpZmllcik7XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgaHR0cE9wdGlvbnMudXJsID0gZ2V0VVJMKGtleSwgaHR0cE9wdGlvbnMucm9vdCk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQocGFyYW1zLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNhdmUgZGF0YSBpbiBhbiBhbm9ueW1vdXMgZG9jdW1lbnQgd2l0aGluIHRoZSBjb2xsZWN0aW9uLlxuICAgICAgICAgKlxuICAgICAgICAgKiBUaGUgYHJvb3RgIG9mIHRoZSBjb2xsZWN0aW9uIG11c3QgYmUgc3BlY2lmaWVkLiBCeSBkZWZhdWx0IHRoZSBgcm9vdGAgaXMgdGFrZW4gZnJvbSB0aGUgRGF0YSBTZXJ2aWNlIGNvbmZpZ3VyYXRpb24gb3B0aW9uczsgeW91IGNhbiBhbHNvIHBhc3MgdGhlIGByb290YCB0byB0aGUgYHNhdmVgIGNhbGwgZXhwbGljaXRseSBieSBvdmVycmlkaW5nIHRoZSBvcHRpb25zICh0aGlyZCBwYXJhbWV0ZXIpLlxuICAgICAgICAgKlxuICAgICAgICAgKiAoQWRkaXRpb25hbCBiYWNrZ3JvdW5kOiBEb2N1bWVudHMgYXJlIHRvcC1sZXZlbCBlbGVtZW50cyB3aXRoaW4gYSBjb2xsZWN0aW9uLiBDb2xsZWN0aW9ucyBtdXN0IGJlIHVuaXF1ZSB3aXRoaW4gdGhpcyBhY2NvdW50ICh0ZWFtIG9yIHBlcnNvbmFsIGFjY291bnQpIGFuZCBwcm9qZWN0IGFuZCBhcmUgc2V0IHdpdGggdGhlIGByb290YCBmaWVsZCBpbiB0aGUgYG9wdGlvbmAgcGFyYW1ldGVyLiBTZWUgdGhlIHVuZGVybHlpbmcgW0RhdGEgQVBJXSguLi8uLi8uLi9yZXN0X2FwaXMvZGF0YV9hcGkvKSBmb3IgbW9yZSBpbmZvcm1hdGlvbi4gVGhlIGBzYXZlYCBtZXRob2QgaXMgbWFraW5nIGEgYFBPU1RgIHJlcXVlc3QuKVxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIENyZWF0ZSBhIG5ldyBkb2N1bWVudCwgd2l0aCBvbmUgZWxlbWVudCwgYXQgdGhlIGRlZmF1bHQgcm9vdCBsZXZlbFxuICAgICAgICAgKiAgICAgIGRzLnNhdmUoJ3F1ZXN0aW9uMScsICd5ZXMnKTtcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBDcmVhdGUgYSBuZXcgZG9jdW1lbnQsIHdpdGggdHdvIGVsZW1lbnRzLCBhdCB0aGUgZGVmYXVsdCByb290IGxldmVsXG4gICAgICAgICAqICAgICAgZHMuc2F2ZSh7IHF1ZXN0aW9uMToneWVzJywgcXVlc3Rpb24yOiAzMiB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBDcmVhdGUgYSBuZXcgZG9jdW1lbnQsIHdpdGggdHdvIGVsZW1lbnRzLCBhdCBgL3N0dWRlbnRzL2BcbiAgICAgICAgICogICAgICBkcy5zYXZlKHsgbmFtZTonSm9obicsIGNsYXNzTmFtZTogJ0NTMTAxJyB9LCB7IHJvb3Q6ICdzdHVkZW50cycgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfE9iamVjdH0ga2V5IElmIGBrZXlgIGlzIGEgc3RyaW5nLCBpdCBpcyB0aGUgaWQgb2YgdGhlIGVsZW1lbnQgdG8gc2F2ZSAoY3JlYXRlKSBpbiB0aGlzIGRvY3VtZW50LiBJZiBga2V5YCBpcyBhbiBvYmplY3QsIHRoZSBvYmplY3QgaXMgdGhlIGRhdGEgdG8gc2F2ZSAoY3JlYXRlKSBpbiB0aGlzIGRvY3VtZW50LiBJbiBib3RoIGNhc2VzLCB0aGUgaWQgZm9yIHRoZSBkb2N1bWVudCBpcyBnZW5lcmF0ZWQgYXV0b21hdGljYWxseS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IHZhbHVlIChPcHRpb25hbCkgVGhlIGRhdGEgdG8gc2F2ZS4gSWYgYGtleWAgaXMgYSBzdHJpbmcsIHRoaXMgaXMgdGhlIHZhbHVlIHRvIHNhdmUuIElmIGBrZXlgIGlzIGFuIG9iamVjdCwgdGhlIHZhbHVlKHMpIHRvIHNhdmUgYXJlIGFscmVhZHkgcGFydCBvZiBga2V5YCBhbmQgdGhpcyBhcmd1bWVudCBpcyBub3QgcmVxdWlyZWQuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuIElmIHlvdSB3YW50IHRvIG92ZXJyaWRlIHRoZSBkZWZhdWx0IGByb290YCBvZiB0aGUgY29sbGVjdGlvbiwgZG8gc28gaGVyZS5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX0gXG4gICAgICAgICAqL1xuICAgICAgICBzYXZlOiBmdW5jdGlvbiAoa2V5LCB2YWx1ZSwgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIGF0dHJzO1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBrZXkgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICAgICAgYXR0cnMgPSBrZXk7XG4gICAgICAgICAgICAgICAgb3B0aW9ucyA9IHZhbHVlO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAoYXR0cnMgPSB7fSlba2V5XSA9IHZhbHVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIGh0dHBPcHRpb25zLnVybCA9IGdldFVSTCgnJywgaHR0cE9wdGlvbnMucm9vdCk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLnBvc3QoYXR0cnMsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogU2F2ZSAoY3JlYXRlIG9yIHJlcGxhY2UpIGRhdGEgaW4gYSBuYW1lZCBkb2N1bWVudCBvciBlbGVtZW50IHdpdGhpbiB0aGUgY29sbGVjdGlvbi4gXG4gICAgICAgICAqIFxuICAgICAgICAgKiBUaGUgYHJvb3RgIG9mIHRoZSBjb2xsZWN0aW9uIG11c3QgYmUgc3BlY2lmaWVkLiBCeSBkZWZhdWx0IHRoZSBgcm9vdGAgaXMgdGFrZW4gZnJvbSB0aGUgRGF0YSBTZXJ2aWNlIGNvbmZpZ3VyYXRpb24gb3B0aW9uczsgeW91IGNhbiBhbHNvIHBhc3MgdGhlIGByb290YCB0byB0aGUgYHNhdmVBc2AgY2FsbCBleHBsaWNpdGx5IGJ5IG92ZXJyaWRpbmcgdGhlIG9wdGlvbnMgKHRoaXJkIHBhcmFtZXRlcikuXG4gICAgICAgICAqXG4gICAgICAgICAqIE9wdGlvbmFsbHksIHRoZSBuYW1lZCBkb2N1bWVudCBvciBlbGVtZW50IGNhbiBpbmNsdWRlIHBhdGggaW5mb3JtYXRpb24sIHNvIHRoYXQgeW91IGFyZSBzYXZpbmcganVzdCBwYXJ0IG9mIHRoZSBkb2N1bWVudC5cbiAgICAgICAgICpcbiAgICAgICAgICogKEFkZGl0aW9uYWwgYmFja2dyb3VuZDogRG9jdW1lbnRzIGFyZSB0b3AtbGV2ZWwgZWxlbWVudHMgd2l0aGluIGEgY29sbGVjdGlvbi4gQ29sbGVjdGlvbnMgbXVzdCBiZSB1bmlxdWUgd2l0aGluIHRoaXMgYWNjb3VudCAodGVhbSBvciBwZXJzb25hbCBhY2NvdW50KSBhbmQgcHJvamVjdCBhbmQgYXJlIHNldCB3aXRoIHRoZSBgcm9vdGAgZmllbGQgaW4gdGhlIGBvcHRpb25gIHBhcmFtZXRlci4gU2VlIHRoZSB1bmRlcmx5aW5nIFtEYXRhIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL2RhdGFfYXBpLykgZm9yIG1vcmUgaW5mb3JtYXRpb24uIFRoZSBgc2F2ZUFzYCBtZXRob2QgaXMgbWFraW5nIGEgYFBVVGAgcmVxdWVzdC4pXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gQ3JlYXRlIChvciByZXBsYWNlKSB0aGUgYHVzZXIxYCBkb2N1bWVudCBhdCB0aGUgZGVmYXVsdCByb290IGxldmVsLlxuICAgICAgICAgKiAgICAgIC8vIE5vdGUgdGhhdCB0aGlzIHJlcGxhY2VzIGFueSBleGlzdGluZyBjb250ZW50IGluIHRoZSBgdXNlcjFgIGRvY3VtZW50LlxuICAgICAgICAgKiAgICAgIGRzLnNhdmVBcygndXNlcjEnLFxuICAgICAgICAgKiAgICAgICAgICB7ICdxdWVzdGlvbjEnOiAyLCAncXVlc3Rpb24yJzogMTAsXG4gICAgICAgICAqICAgICAgICAgICAncXVlc3Rpb24zJzogZmFsc2UsICdxdWVzdGlvbjQnOiAnc29tZXRpbWVzJyB9ICk7XG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gQ3JlYXRlIChvciByZXBsYWNlKSB0aGUgYHN0dWRlbnQxYCBkb2N1bWVudCBhdCB0aGUgYHN0dWRlbnRzYCByb290LCBcbiAgICAgICAgICogICAgICAvLyB0aGF0IGlzLCB0aGUgZGF0YSBhdCBgL3N0dWRlbnRzL3N0dWRlbnQxL2AuXG4gICAgICAgICAqICAgICAgLy8gTm90ZSB0aGF0IHRoaXMgcmVwbGFjZXMgYW55IGV4aXN0aW5nIGNvbnRlbnQgaW4gdGhlIGAvc3R1ZGVudHMvc3R1ZGVudDEvYCBkb2N1bWVudC5cbiAgICAgICAgICogICAgICAvLyBIb3dldmVyLCB0aGlzIHdpbGwga2VlcCBleGlzdGluZyBjb250ZW50IGluIG90aGVyIHBhdGhzIG9mIHRoaXMgY29sbGVjdGlvbi5cbiAgICAgICAgICogICAgICAvLyBGb3IgZXhhbXBsZSwgdGhlIGRhdGEgYXQgYC9zdHVkZW50cy9zdHVkZW50Mi9gIGlzIHVuY2hhbmdlZCBieSB0aGlzIGNhbGwuXG4gICAgICAgICAqICAgICAgZHMuc2F2ZUFzKCdzdHVkZW50MScsXG4gICAgICAgICAqICAgICAgICAgIHsgZmlyc3ROYW1lOiAnam9obicsIGxhc3ROYW1lOiAnc21pdGgnIH0sXG4gICAgICAgICAqICAgICAgICAgIHsgcm9vdDogJ3N0dWRlbnRzJyB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBDcmVhdGUgKG9yIHJlcGxhY2UpIHRoZSBgbWdtdDEwMC9ncm91cEJgIGRvY3VtZW50IGF0IHRoZSBgbXljbGFzc2VzYCByb290LFxuICAgICAgICAgKiAgICAgIC8vIHRoYXQgaXMsIHRoZSBkYXRhIGF0IGAvbXljbGFzc2VzL21nbXQxMDAvZ3JvdXBCL2AuXG4gICAgICAgICAqICAgICAgLy8gTm90ZSB0aGF0IHRoaXMgcmVwbGFjZXMgYW55IGV4aXN0aW5nIGNvbnRlbnQgaW4gdGhlIGAvbXljbGFzc2VzL21nbXQxMDAvZ3JvdXBCL2AgZG9jdW1lbnQuXG4gICAgICAgICAqICAgICAgLy8gSG93ZXZlciwgdGhpcyB3aWxsIGtlZXAgZXhpc3RpbmcgY29udGVudCBpbiBvdGhlciBwYXRocyBvZiB0aGlzIGNvbGxlY3Rpb24uXG4gICAgICAgICAqICAgICAgLy8gRm9yIGV4YW1wbGUsIHRoZSBkYXRhIGF0IGAvbXljbGFzc2VzL21nbXQxMDAvZ3JvdXBBL2AgaXMgdW5jaGFuZ2VkIGJ5IHRoaXMgY2FsbC5cbiAgICAgICAgICogICAgICBkcy5zYXZlQXMoJ21nbXQxMDAvZ3JvdXBCJyxcbiAgICAgICAgICogICAgICAgICAgeyBzY2VuYXJpb1llYXI6ICcyMDE1JyB9LFxuICAgICAgICAgKiAgICAgICAgICB7IHJvb3Q6ICdteWNsYXNzZXMnIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30ga2V5IElkIG9mIHRoZSBkb2N1bWVudC5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IHZhbHVlIChPcHRpb25hbCkgVGhlIGRhdGEgdG8gc2F2ZSwgaW4ga2V5OnZhbHVlIHBhaXJzLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLiBJZiB5b3Ugd2FudCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdCBgcm9vdGAgb2YgdGhlIGNvbGxlY3Rpb24sIGRvIHNvIGhlcmUuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9IFxuICAgICAgICAgKi9cbiAgICAgICAgc2F2ZUFzOiBmdW5jdGlvbiAoa2V5LCB2YWx1ZSwgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIGh0dHBPcHRpb25zLnVybCA9IGdldFVSTChrZXksIGh0dHBPcHRpb25zLnJvb3QpO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wdXQodmFsdWUsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogR2V0IGRhdGEgZm9yIGEgc3BlY2lmaWMgZG9jdW1lbnQgb3IgZmllbGQuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgZHMubG9hZCgndXNlcjEnKTtcbiAgICAgICAgICogICAgICBkcy5sb2FkKCd1c2VyMS9xdWVzdGlvbjMnKTtcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtICB7U3RyaW5nfE9iamVjdH0ga2V5IFRoZSBpZCBvZiB0aGUgZGF0YSB0byByZXR1cm4uIENhbiBiZSB0aGUgaWQgb2YgYSBkb2N1bWVudCwgb3IgYSBwYXRoIHRvIGRhdGEgd2l0aGluIHRoYXQgZG9jdW1lbnQuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvdXRwdXRNb2RpZmllciAoT3B0aW9uYWwpIEF2YWlsYWJsZSBmaWVsZHMgaW5jbHVkZTogYHN0YXJ0cmVjb3JkYCwgYGVuZHJlY29yZGAsIGBzb3J0YCwgYW5kIGBkaXJlY3Rpb25gIChgYXNjYCBvciBgZGVzY2ApLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX0gXG4gICAgICAgICAqL1xuICAgICAgICBsb2FkOiBmdW5jdGlvbiAoa2V5LCBvdXRwdXRNb2RpZmllciwgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIGh0dHBPcHRpb25zLnVybCA9IGdldFVSTChrZXksIGh0dHBPcHRpb25zLnJvb3QpO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KG91dHB1dE1vZGlmaWVyLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJlbW92ZXMgZGF0YSBmcm9tIGNvbGxlY3Rpb24uIE9ubHkgZG9jdW1lbnRzICh0b3AtbGV2ZWwgZWxlbWVudHMgaW4gZWFjaCBjb2xsZWN0aW9uKSBjYW4gYmUgZGVsZXRlZC5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgIGRzLnJlbW92ZSgndXNlcjEnKTtcbiAgICAgICAgICpcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd8QXJyYXl9IGtleXMgVGhlIGlkIG9mIHRoZSBkb2N1bWVudCB0byByZW1vdmUgZnJvbSB0aGlzIGNvbGxlY3Rpb24sIG9yIGFuIGFycmF5IG9mIHN1Y2ggaWRzLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfSBcbiAgICAgICAgICovXG4gICAgICAgIHJlbW92ZTogZnVuY3Rpb24gKGtleXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICB2YXIgcGFyYW1zO1xuICAgICAgICAgICAgaWYgKCQuaXNBcnJheShrZXlzKSkge1xuICAgICAgICAgICAgICAgIHBhcmFtcyA9IHsgaWQ6IGtleXMgfTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcGFyYW1zID0gJyc7XG4gICAgICAgICAgICAgICAgaHR0cE9wdGlvbnMudXJsID0gZ2V0VVJMKGtleXMsIGh0dHBPcHRpb25zLnJvb3QpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZGVsZXRlKHBhcmFtcywgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gRXBpY2VudGVyIGRvZXNuJ3QgYWxsb3cgbnVraW5nIGNvbGxlY3Rpb25zXG4gICAgICAgIC8vICAgICAvKipcbiAgICAgICAgLy8gICAgICAqIFJlbW92ZXMgY29sbGVjdGlvbiBiZWluZyByZWZlcmVuY2VkXG4gICAgICAgIC8vICAgICAgKiBAcmV0dXJuIG51bGxcbiAgICAgICAgLy8gICAgICAqL1xuICAgICAgICAvLyAgICAgZGVzdHJveTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgLy8gICAgICAgICByZXR1cm4gdGhpcy5yZW1vdmUoJycsIG9wdGlvbnMpO1xuICAgICAgICAvLyAgICAgfVxuICAgIH07XG5cbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xufTtcbiIsIi8qKlxuICpcbiAqICMjIEdyb3VwIEFQSSBBZGFwdGVyXG4gKlxuICogVGhlIEdyb3VwIEFQSSBBZGFwdGVyIHByb3ZpZGVzIG1ldGhvZHMgdG8gbG9vayB1cCwgY3JlYXRlLCBjaGFuZ2Ugb3IgcmVtb3ZlIGluZm9ybWF0aW9uIGFib3V0IGdyb3VwcyBpbiBhIHByb2plY3QuIEl0IGlzIGJhc2VkIG9uIHF1ZXJ5IGNhcGFiaWxpdGllcyBvZiB0aGUgdW5kZXJseWluZyBSRVNUZnVsIFtHcm91cCBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy91c2VyX21hbmFnZW1lbnQvZ3JvdXAvKS5cbiAqXG4gKiBUaGlzIGlzIG9ubHkgbmVlZGVkIGZvciBBdXRoZW50aWNhdGVkIHByb2plY3RzLCB0aGF0IGlzLCB0ZWFtIHByb2plY3RzIHdpdGggW2VuZCB1c2VycyBhbmQgZ3JvdXBzXSguLi8uLi8uLi9ncm91cHNfYW5kX2VuZF91c2Vycy8pLlxuICpcbiAqICAgICAgdmFyIG1hID0gbmV3IEYuc2VydmljZS5Hcm91cCh7IHRva2VuOiAndXNlci1vci1wcm9qZWN0LWFjY2Vzcy10b2tlbicgfSk7XG4gKiAgICAgIG1hLmdldEdyb3Vwc0ZvclByb2plY3QoeyBhY2NvdW50OiAnYWNtZScsIHByb2plY3Q6ICdzYW1wbGUnIH0pO1xuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIHNlcnZpY2VVdGlscyA9IHJlcXVpcmUoJy4vc2VydmljZS11dGlscycpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIG9iamVjdEFzc2lnbiA9IHJlcXVpcmUoJ29iamVjdC1hc3NpZ24nKTtcblxudmFyIGFwaUVuZHBvaW50ID0gJ2dyb3VwL2xvY2FsJztcblxudmFyIEdyb3VwU2VydmljZSA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBFcGljZW50ZXIgYWNjb3VudCBuYW1lLiBEZWZhdWx0cyB0byB1bmRlZmluZWQuXG4gICAgICAgICAqIEB0eXBlIHtzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBhY2NvdW50OiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEVwaWNlbnRlciBwcm9qZWN0IG5hbWUuIERlZmF1bHRzIHRvIHVuZGVmaW5lZC5cbiAgICAgICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHByb2plY3Q6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT3B0aW9ucyB0byBwYXNzIG9uIHRvIHRoZSB1bmRlcmx5aW5nIHRyYW5zcG9ydCBsYXllci4gQWxsIGpxdWVyeS5hamF4IG9wdGlvbnMgYXQgaHR0cDovL2FwaS5qcXVlcnkuY29tL2pRdWVyeS5hamF4LyBhcmUgYXZhaWxhYmxlLiBEZWZhdWx0cyB0byBlbXB0eSBvYmplY3QuXG4gICAgICAgICAqIEB0eXBlIHtvYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICB0cmFuc3BvcnQ6IHt9XG4gICAgfTtcbiAgICB2YXIgc2VydmljZU9wdGlvbnMgPSBzZXJ2aWNlVXRpbHMuZ2V0RGVmYXVsdE9wdGlvbnMoZGVmYXVsdHMsIGNvbmZpZywgeyBhcGlFbmRwb2ludDogYXBpRW5kcG9pbnQgfSk7XG4gICAgdmFyIHRyYW5zcG9ydE9wdGlvbnMgPSBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQ7XG4gICAgZGVsZXRlIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydDtcbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KHRyYW5zcG9ydE9wdGlvbnMsIHNlcnZpY2VPcHRpb25zKTtcbiAgICB2YXIgcHVibGljQVBJID0ge1xuICAgICAgICAvKipcbiAgICAgICAgKiBHZXRzIGluZm9ybWF0aW9uIGZvciBhIGdyb3VwIG9yIG11bHRpcGxlIGdyb3Vwcy5cbiAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gcGFyYW1zIG9iamVjdCB3aXRoIHF1ZXJ5IHBhcmFtZXRlcnNcbiAgICAgICAgKiBAcGF0YW0ge3N0cmluZ30gcGFyYW1zLnEgcGFydGlhbCBtYXRjaCBmb3IgbmFtZSwgb3JnYW5pemF0aW9uIG9yIGV2ZW50LlxuICAgICAgICAqIEBwYXRhbSB7c3RyaW5nfSBwYXJhbXMuYWNjb3VudCBFcGljZW50ZXIncyBUZWFtIElEXG4gICAgICAgICogQHBhdGFtIHtzdHJpbmd9IHBhcmFtcy5wcm9qZWN0IEVwaWNlbnRlcidzIFByb2plY3QgSURcbiAgICAgICAgKiBAcGF0YW0ge3N0cmluZ30gcGFyYW1zLm5hbWUgRXBpY2VudGVyJ3MgR3JvdXAgTmFtZVxuICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZ2V0R3JvdXBzOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICAvL2dyb3VwSUQgaXMgcGFydCBvZiB0aGUgVVJMXG4gICAgICAgICAgICAvL3EsIGFjY291bnQgYW5kIHByb2plY3QgYXJlIHBhcnQgb2YgdGhlIHF1ZXJ5IHN0cmluZ1xuICAgICAgICAgICAgdmFyIGZpbmFsT3B0cyA9IG9iamVjdEFzc2lnbih7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgdmFyIGZpbmFsUGFyYW1zO1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBwYXJhbXMgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgZmluYWxPcHRzLnVybCA9IHNlcnZpY2VVdGlscy5nZXRBcGlVcmwoYXBpRW5kcG9pbnQgKyAnLycgKyBwYXJhbXMsIGZpbmFsT3B0cyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGZpbmFsUGFyYW1zID0gcGFyYW1zO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KGZpbmFsUGFyYW1zLCBmaW5hbE9wdHMpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICBvYmplY3RBc3NpZ24odGhpcywgcHVibGljQVBJKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gR3JvdXBTZXJ2aWNlO1xuIiwiLyoqXG4gKlxuICogIyMgSW50cm9zcGVjdGlvbiBBUEkgU2VydmljZVxuICpcbiAqIFRoZSBJbnRyb3NwZWN0aW9uIEFQSSBTZXJ2aWNlIGFsbG93cyB5b3UgdG8gdmlldyBhIGxpc3Qgb2YgdGhlIHZhcmlhYmxlcyBhbmQgb3BlcmF0aW9ucyBpbiBhIG1vZGVsLiBUeXBpY2FsbHkgdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIHRoZSBbUnVuIEFQSSBTZXJ2aWNlXSguLi9ydW4tYXBpLXNlcnZpY2UvKS5cbiAqXG4gKiBUaGUgSW50cm9zcGVjdGlvbiBBUEkgU2VydmljZSBpcyBub3QgYXZhaWxhYmxlIGZvciBGb3JpbyBTaW1MYW5nLlxuICpcbiAqICAgICAgIHZhciBpbnRybyA9IG5ldyBGLnNlcnZpY2UuSW50cm9zcGVjdCh7XG4gKiAgICAgICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAqICAgICAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJ1xuICogICAgICAgfSk7XG4gKiAgICAgICBpbnRyby5ieU1vZGVsKCdzdXBwbHktY2hhaW4ucHknKS50aGVuKGZ1bmN0aW9uKGRhdGEpeyAuLi4gfSk7XG4gKiAgICAgICBpbnRyby5ieVJ1bklEKCcyYjRkOGY3MS01YzM0LTQzNWEtOGMxNi05ZGU2NzRhYjcyZTYnKS50aGVuKGZ1bmN0aW9uKGRhdGEpeyAuLi4gfSk7XG4gKlxuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG5cbnZhciBhcGlFbmRwb2ludCA9ICdtb2RlbC9pbnRyb3NwZWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogRm9yIHByb2plY3RzIHRoYXQgcmVxdWlyZSBhdXRoZW50aWNhdGlvbiwgcGFzcyBpbiB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gKGRlZmF1bHRzIHRvIGVtcHR5IHN0cmluZykuIElmIHRoZSB1c2VyIGlzIGFscmVhZHkgbG9nZ2VkIGluIHRvIEVwaWNlbnRlciwgdGhlIHVzZXIgYWNjZXNzIHRva2VuIGlzIGFscmVhZHkgc2V0IGluIGEgY29va2llIGFuZCBhdXRvbWF0aWNhbGx5IGxvYWRlZCBmcm9tIHRoZXJlLiAoU2VlIFttb3JlIGJhY2tncm91bmQgb24gYWNjZXNzIHRva2Vuc10oLi4vLi4vLi4vcHJvamVjdF9hY2Nlc3MvKSkuXG4gICAgICAgICAqIEBzZWUgW0F1dGhlbnRpY2F0aW9uIEFQSSBTZXJ2aWNlXSguLi9hdXRoLWFwaS1zZXJ2aWNlLykgZm9yIGdldHRpbmcgdG9rZW5zLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdG9rZW46IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFjY291bnQgaWQuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykgb3IgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFjY291bnQ6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHByb2plY3QgaWQuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHByb2plY3Q6IHVuZGVmaW5lZCxcblxuICAgIH07XG5cbiAgICB2YXIgc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICB2YXIgc2VydmljZU9wdGlvbnMgPSBzZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKGRlZmF1bHRzLCBjb25maWcpO1xuXG4gICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5hY2NvdW50KSB7XG4gICAgICAgIHVybENvbmZpZy5hY2NvdW50UGF0aCA9IHNlcnZpY2VPcHRpb25zLmFjY291bnQ7XG4gICAgfVxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5wcm9qZWN0KSB7XG4gICAgICAgIHVybENvbmZpZy5wcm9qZWN0UGF0aCA9IHNlcnZpY2VPcHRpb25zLnByb2plY3Q7XG4gICAgfVxuXG4gICAgdmFyIHRyYW5zcG9ydE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LCB7XG4gICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpXG4gICAgfSk7XG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnRva2VuKSB7XG4gICAgICAgIHRyYW5zcG9ydE9wdGlvbnMuaGVhZGVycyA9IHtcbiAgICAgICAgICAgIEF1dGhvcml6YXRpb246ICdCZWFyZXIgJyArIHNlcnZpY2VPcHRpb25zLnRva2VuXG4gICAgICAgIH07XG4gICAgfVxuICAgIHZhciBodHRwID0gbmV3IFRyYW5zcG9ydEZhY3RvcnkodHJhbnNwb3J0T3B0aW9ucyk7XG5cbiAgICB2YXIgcHVibGljQVBJID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogR2V0IHRoZSBhdmFpbGFibGUgdmFyaWFibGVzIGFuZCBvcGVyYXRpb25zIGZvciBhIGdpdmVuIG1vZGVsIGZpbGUuXG4gICAgICAgICAqXG4gICAgICAgICAqIE5vdGU6IFRoaXMgZG9lcyBub3Qgd29yayBmb3IgYW55IG1vZGVsIHdoaWNoIHJlcXVpcmVzIGFkZGl0aW9uYWwgcGFyYW1ldGVycywgc3VjaCBhcyBgZmlsZXNgLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIGludHJvLmJ5TW9kZWwoJ2FiYy52bWYnKVxuICAgICAgICAgKiAgICAgICAgICAudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgICAqICAgICAgICAgICAgICAvLyBkYXRhIGNvbnRhaW5zIGFuIG9iamVjdCB3aXRoIGF2YWlsYWJsZSBmdW5jdGlvbnMgKHVzZWQgd2l0aCBvcGVyYXRpb25zIEFQSSkgYW5kIGF2YWlsYWJsZSB2YXJpYWJsZXMgKHVzZWQgd2l0aCB2YXJpYWJsZXMgQVBJKVxuICAgICAgICAgKiAgICAgICAgICAgICAgY29uc29sZS5sb2coZGF0YS5mdW5jdGlvbnMpO1xuICAgICAgICAgKiAgICAgICAgICAgICAgY29uc29sZS5sb2coZGF0YS52YXJpYWJsZXMpO1xuICAgICAgICAgKiAgICAgICAgICB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtICB7U3RyaW5nfSBtb2RlbEZpbGUgTmFtZSBvZiB0aGUgbW9kZWwgZmlsZSB0byBpbnRyb3NwZWN0LlxuICAgICAgICAgKiBAcGFyYW0gIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX0gXG4gICAgICAgICAqL1xuICAgICAgICBieU1vZGVsOiBmdW5jdGlvbiAobW9kZWxGaWxlLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgb3B0cyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICBpZiAoIW9wdHMuYWNjb3VudCB8fCAhb3B0cy5wcm9qZWN0KSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBY2NvdW50IGFuZCBwcm9qZWN0IGFyZSByZXF1aXJlZCB3aGVuIHVzaW5nIGludHJvc3BlY3QjYnlNb2RlbCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCFtb2RlbEZpbGUpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ21vZGVsRmlsZSBpcyByZXF1aXJlZCB3aGVuIHVzaW5nIGludHJvc3BlY3QjYnlNb2RlbCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHVybCA9IHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBbb3B0cy5hY2NvdW50LCBvcHRzLnByb2plY3QsIG1vZGVsRmlsZV0uam9pbignLycpIH07XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMsIHVybCk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQoJycsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogR2V0IHRoZSBhdmFpbGFibGUgdmFyaWFibGVzIGFuZCBvcGVyYXRpb25zIGZvciBhIGdpdmVuIG1vZGVsIGZpbGUuXG4gICAgICAgICAqXG4gICAgICAgICAqIE5vdGU6IFRoaXMgZG9lcyBub3Qgd29yayBmb3IgYW55IG1vZGVsIHdoaWNoIHJlcXVpcmVzIGFkZGl0aW9uYWwgcGFyYW1ldGVycyBzdWNoIGFzIGBmaWxlc2AuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgaW50cm8uYnlSdW5JRCgnMmI0ZDhmNzEtNWMzNC00MzVhLThjMTYtOWRlNjc0YWI3MmU2JylcbiAgICAgICAgICogICAgICAgICAgLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICAgKiAgICAgICAgICAgICAgLy8gZGF0YSBjb250YWlucyBhbiBvYmplY3Qgd2l0aCBhdmFpbGFibGUgZnVuY3Rpb25zICh1c2VkIHdpdGggb3BlcmF0aW9ucyBBUEkpIGFuZCBhdmFpbGFibGUgdmFyaWFibGVzICh1c2VkIHdpdGggdmFyaWFibGVzIEFQSSlcbiAgICAgICAgICogICAgICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEuZnVuY3Rpb25zKTtcbiAgICAgICAgICogICAgICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEudmFyaWFibGVzKTtcbiAgICAgICAgICogICAgICAgICAgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSAge1N0cmluZ30gcnVuSUQgSWQgb2YgdGhlIHJ1biB0byBpbnRyb3NwZWN0LlxuICAgICAgICAgKiBAcGFyYW0gIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX0gXG4gICAgICAgICAqL1xuICAgICAgICBieVJ1bklEOiBmdW5jdGlvbiAocnVuSUQsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGlmICghcnVuSUQpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3J1bklEIGlzIHJlcXVpcmVkIHdoZW4gdXNpbmcgaW50cm9zcGVjdCNieU1vZGVsJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgdXJsID0geyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHJ1bklEIH07XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMsIHVybCk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQoJycsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgJC5leHRlbmQodGhpcywgcHVibGljQVBJKTtcbn07XG4iLCIvKipcbiAqXG4gKiAjIyBNZW1iZXIgQVBJIEFkYXB0ZXJcbiAqXG4gKiBUaGUgTWVtYmVyIEFQSSBBZGFwdGVyIHByb3ZpZGVzIG1ldGhvZHMgdG8gbG9vayB1cCBpbmZvcm1hdGlvbiBhYm91dCBlbmQgdXNlcnMgZm9yIHlvdXIgcHJvamVjdCBhbmQgaG93IHRoZXkgYXJlIGRpdmlkZWQgYWNyb3NzIGdyb3Vwcy4gSXQgaXMgYmFzZWQgb24gcXVlcnkgY2FwYWJpbGl0aWVzIG9mIHRoZSB1bmRlcmx5aW5nIFJFU1RmdWwgW01lbWJlciBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy91c2VyX21hbmFnZW1lbnQvbWVtYmVyLykuXG4gKlxuICogVGhpcyBpcyBvbmx5IG5lZWRlZCBmb3IgQXV0aGVudGljYXRlZCBwcm9qZWN0cywgdGhhdCBpcywgdGVhbSBwcm9qZWN0cyB3aXRoIFtlbmQgdXNlcnMgYW5kIGdyb3Vwc10oLi4vLi4vLi4vZ3JvdXBzX2FuZF9lbmRfdXNlcnMvKS4gRm9yIGV4YW1wbGUsIGlmIHNvbWUgb2YgeW91ciBlbmQgdXNlcnMgYXJlIGZhY2lsaXRhdG9ycywgb3IgaWYgeW91ciBlbmQgdXNlcnMgc2hvdWxkIGJlIHRyZWF0ZWQgZGlmZmVyZW50bHkgYmFzZWQgb24gd2hpY2ggZ3JvdXAgdGhleSBhcmUgaW4sIHVzZSB0aGUgTWVtYmVyIEFQSSB0byBmaW5kIHRoYXQgaW5mb3JtYXRpb24uXG4gKlxuICogICAgICB2YXIgbWEgPSBuZXcgRi5zZXJ2aWNlLk1lbWJlcih7IHRva2VuOiAndXNlci1vci1wcm9qZWN0LWFjY2Vzcy10b2tlbicgfSk7XG4gKiAgICAgIG1hLmdldEdyb3Vwc0ZvclVzZXIoeyB1c2VySWQ6ICdiNmIzMTNhMy1hYjg0LTQ3OWMtYmFlYS0yMDZmNmJmZjMzNycgfSk7XG4gKiAgICAgIG1hLmdldEdyb3VwRGV0YWlscyh7IGdyb3VwSWQ6ICcwMGI1MzMwOC05ODMzLTQ3ZjItYjIxZS0xMjc4YzA3ZDUzYjgnIH0pO1xuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG52YXIgX3BpY2sgPSByZXF1aXJlKCcuLi91dGlsL29iamVjdC11dGlsJykuX3BpY2s7XG52YXIgYXBpRW5kcG9pbnQgPSAnbWVtYmVyL2xvY2FsJztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogRXBpY2VudGVyIHVzZXIgaWQuIERlZmF1bHRzIHRvIGEgYmxhbmsgc3RyaW5nLlxuICAgICAgICAgKiBAdHlwZSB7c3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdXNlcklkOiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEVwaWNlbnRlciBncm91cCBpZC4gRGVmYXVsdHMgdG8gYSBibGFuayBzdHJpbmcuIE5vdGUgdGhhdCB0aGlzIGlzIHRoZSBncm91cCAqaWQqLCBub3QgdGhlIGdyb3VwICpuYW1lKi5cbiAgICAgICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGdyb3VwSWQ6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT3B0aW9ucyB0byBwYXNzIG9uIHRvIHRoZSB1bmRlcmx5aW5nIHRyYW5zcG9ydCBsYXllci4gQWxsIGpxdWVyeS5hamF4IG9wdGlvbnMgYXQgaHR0cDovL2FwaS5qcXVlcnkuY29tL2pRdWVyeS5hamF4LyBhcmUgYXZhaWxhYmxlLiBEZWZhdWx0cyB0byBlbXB0eSBvYmplY3QuXG4gICAgICAgICAqIEB0eXBlIHtvYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICB0cmFuc3BvcnQ6IHt9XG4gICAgfTtcbiAgICB0aGlzLnNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKCk7XG4gICAgdmFyIHNlcnZpY2VPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKGRlZmF1bHRzLCBjb25maWcpO1xuICAgIHZhciB1cmxDb25maWcgPSBuZXcgQ29uZmlnU2VydmljZShzZXJ2aWNlT3B0aW9ucykuZ2V0KCdzZXJ2ZXInKTtcblxuICAgIHZhciB0cmFuc3BvcnRPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydCwge1xuICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KVxuICAgIH0pO1xuXG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnRva2VuKSB7XG4gICAgICAgIHRyYW5zcG9ydE9wdGlvbnMuaGVhZGVycyA9IHtcbiAgICAgICAgICAgIEF1dGhvcml6YXRpb246ICdCZWFyZXIgJyArIHNlcnZpY2VPcHRpb25zLnRva2VuXG4gICAgICAgIH07XG4gICAgfVxuICAgIHZhciBodHRwID0gbmV3IFRyYW5zcG9ydEZhY3RvcnkodHJhbnNwb3J0T3B0aW9ucywgc2VydmljZU9wdGlvbnMpO1xuXG4gICAgdmFyIGdldEZpbmFsUGFyYW1zID0gZnVuY3Rpb24gKHBhcmFtcykge1xuICAgICAgICBpZiAodHlwZW9mIHBhcmFtcyA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHJldHVybiAkLmV4dGVuZCh0cnVlLCBzZXJ2aWNlT3B0aW9ucywgcGFyYW1zKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gc2VydmljZU9wdGlvbnM7XG4gICAgfTtcblxuICAgIHZhciBwYXRjaFVzZXJBY3RpdmVGaWVsZCA9IGZ1bmN0aW9uIChwYXJhbXMsIGFjdGl2ZSwgb3B0aW9ucykge1xuICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucywge1xuICAgICAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBwYXJhbXMuZ3JvdXBJZCArICcvJyArIHBhcmFtcy51c2VySWRcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIGh0dHAucGF0Y2goeyBhY3RpdmU6IGFjdGl2ZSB9LCBodHRwT3B0aW9ucyk7XG4gICAgfTtcblxuICAgIHZhciBwdWJsaWNBUEkgPSB7XG5cbiAgICAgICAgLyoqXG4gICAgICAgICogUmV0cmlldmUgZGV0YWlscyBhYm91dCBhbGwgb2YgdGhlIGdyb3VwIG1lbWJlcnNoaXBzIGZvciBvbmUgZW5kIHVzZXIuIFRoZSBtZW1iZXJzaGlwIGRldGFpbHMgYXJlIHJldHVybmVkIGluIGFuIGFycmF5LCB3aXRoIG9uZSBlbGVtZW50IChncm91cCByZWNvcmQpIGZvciBlYWNoIGdyb3VwIHRvIHdoaWNoIHRoZSBlbmQgdXNlciBiZWxvbmdzLlxuICAgICAgICAqXG4gICAgICAgICogSW4gdGhlIG1lbWJlcnNoaXAgYXJyYXksIGVhY2ggZ3JvdXAgcmVjb3JkIGluY2x1ZGVzIHRoZSBncm91cCBpZCwgcHJvamVjdCBpZCwgYWNjb3VudCAodGVhbSkgaWQsIGFuZCBhbiBhcnJheSBvZiBtZW1iZXJzLiBIb3dldmVyLCBvbmx5IHRoZSB1c2VyIHdob3NlIHVzZXJJZCBpcyBpbmNsdWRlZCBpbiB0aGUgY2FsbCBpcyBsaXN0ZWQgaW4gdGhlIG1lbWJlcnMgYXJyYXkgKHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciB0aGVyZSBhcmUgb3RoZXIgbWVtYmVycyBpbiB0aGlzIGdyb3VwKS5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICB2YXIgbWEgPSBuZXcgRi5zZXJ2aWNlLk1lbWJlcih7IHRva2VuOiAndXNlci1vci1wcm9qZWN0LWFjY2Vzcy10b2tlbicgfSk7XG4gICAgICAgICogICAgICAgbWEuZ2V0R3JvdXBzRm9yVXNlcignNDI4MzZkNGItNWI2MS00ZmU0LTgwZWItMzEzNmU5NTZlZTVjJylcbiAgICAgICAgKiAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24obWVtYmVyc2hpcHMpe1xuICAgICAgICAqICAgICAgICAgICAgICAgZm9yICh2YXIgaT0wOyBpPG1lbWJlcnNoaXBzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICogICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2cobWVtYmVyc2hpcHNbaV0uZ3JvdXBJZCk7XG4gICAgICAgICogICAgICAgICAgICAgICB9XG4gICAgICAgICogICAgICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICAgICAgbWEuZ2V0R3JvdXBzRm9yVXNlcih7IHVzZXJJZDogJzQyODM2ZDRiLTViNjEtNGZlNC04MGViLTMxMzZlOTU2ZWU1YycgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfG9iamVjdH0gcGFyYW1zIFRoZSB1c2VyIGlkIGZvciB0aGUgZW5kIHVzZXIuIEFsdGVybmF0aXZlbHksIGFuIG9iamVjdCB3aXRoIGZpZWxkIGB1c2VySWRgIGFuZCB2YWx1ZSB0aGUgdXNlciBpZC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAqL1xuXG4gICAgICAgIGdldEdyb3Vwc0ZvclVzZXI6IGZ1bmN0aW9uIChwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgdmFyIGlzU3RyaW5nID0gdHlwZW9mIHBhcmFtcyA9PT0gJ3N0cmluZyc7XG4gICAgICAgICAgICB2YXIgb2JqUGFyYW1zID0gZ2V0RmluYWxQYXJhbXMocGFyYW1zKTtcbiAgICAgICAgICAgIGlmICghaXNTdHJpbmcgJiYgIW9ialBhcmFtcy51c2VySWQpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHVzZXJJZCBzcGVjaWZpZWQuJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBnZXRQYXJtcyA9IGlzU3RyaW5nID8geyB1c2VySWQ6IHBhcmFtcyB9IDogX3BpY2sob2JqUGFyYW1zLCAndXNlcklkJyk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQoZ2V0UGFybXMsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBSZXRyaWV2ZSBkZXRhaWxzIGFib3V0IG9uZSBncm91cCwgaW5jbHVkaW5nIGFuIGFycmF5IG9mIGFsbCBpdHMgbWVtYmVycy5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICB2YXIgbWEgPSBuZXcgRi5zZXJ2aWNlLk1lbWJlcih7IHRva2VuOiAndXNlci1vci1wcm9qZWN0LWFjY2Vzcy10b2tlbicgfSk7XG4gICAgICAgICogICAgICAgbWEuZ2V0R3JvdXBEZXRhaWxzKCc4MDI1N2EyNS1hYTEwLTQ5NTktOTY4Yi1mZDA1MzkwMWY3MmYnKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbihncm91cCl7XG4gICAgICAgICogICAgICAgICAgICAgICBmb3IgKHZhciBpPTA7IGk8Z3JvdXAubWVtYmVycy5sZW5ndGg7IGkrKykge1xuICAgICAgICAqICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGdyb3VwLm1lbWJlcnNbaV0udXNlck5hbWUpO1xuICAgICAgICAqICAgICAgICAgICAgICAgfVxuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgIG1hLmdldEdyb3VwRGV0YWlscyh7IGdyb3VwSWQ6ICc4MDI1N2EyNS1hYTEwLTQ5NTktOTY4Yi1mZDA1MzkwMWY3MmYnIH0pO1xuICAgICAgICAqXG4gICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ3xvYmplY3R9IHBhcmFtcyBUaGUgZ3JvdXAgaWQuIEFsdGVybmF0aXZlbHksIGFuIG9iamVjdCB3aXRoIGZpZWxkIGBncm91cElkYCBhbmQgdmFsdWUgdGhlIGdyb3VwIGlkLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZ2V0R3JvdXBEZXRhaWxzOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICAgICAgICAgIHZhciBpc1N0cmluZyA9IHR5cGVvZiBwYXJhbXMgPT09ICdzdHJpbmcnO1xuICAgICAgICAgICAgdmFyIG9ialBhcmFtcyA9IGdldEZpbmFsUGFyYW1zKHBhcmFtcyk7XG4gICAgICAgICAgICBpZiAoIWlzU3RyaW5nICYmICFvYmpQYXJhbXMuZ3JvdXBJZCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTm8gZ3JvdXBJZCBzcGVjaWZpZWQuJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBncm91cElkID0gaXNTdHJpbmcgPyBwYXJhbXMgOiBvYmpQYXJhbXMuZ3JvdXBJZDtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIGdyb3VwSWQgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KHt9LCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogU2V0IGEgcGFydGljdWxhciBlbmQgdXNlciBhcyBgYWN0aXZlYC4gQWN0aXZlIGVuZCB1c2VycyBjYW4gYmUgYXNzaWduZWQgdG8gW3dvcmxkc10oLi4vd29ybGQtbWFuYWdlci8pIGluIG11bHRpcGxheWVyIGdhbWVzIGR1cmluZyBhdXRvbWF0aWMgYXNzaWdubWVudC5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICB2YXIgbWEgPSBuZXcgRi5zZXJ2aWNlLk1lbWJlcih7IHRva2VuOiAndXNlci1vci1wcm9qZWN0LWFjY2Vzcy10b2tlbicgfSk7XG4gICAgICAgICogICAgICAgbWEubWFrZVVzZXJBY3RpdmUoeyB1c2VySWQ6ICc0MjgzNmQ0Yi01YjYxLTRmZTQtODBlYi0zMTM2ZTk1NmVlNWMnLFxuICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBJZDogJzgwMjU3YTI1LWFhMTAtNDk1OS05NjhiLWZkMDUzOTAxZjcyZicgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgVGhlIGVuZCB1c2VyIGFuZCBncm91cCBpbmZvcm1hdGlvbi5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnVzZXJJZCBUaGUgaWQgb2YgdGhlIGVuZCB1c2VyIHRvIG1ha2UgYWN0aXZlLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuZ3JvdXBJZCBUaGUgaWQgb2YgdGhlIGdyb3VwIHRvIHdoaWNoIHRoaXMgZW5kIHVzZXIgYmVsb25ncywgYW5kIGluIHdoaWNoIHRoZSBlbmQgdXNlciBzaG91bGQgYmVjb21lIGFjdGl2ZS5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIG1ha2VVc2VyQWN0aXZlOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICByZXR1cm4gcGF0Y2hVc2VyQWN0aXZlRmllbGQocGFyYW1zLCB0cnVlLCBvcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBTZXQgYSBwYXJ0aWN1bGFyIGVuZCB1c2VyIGFzIGBpbmFjdGl2ZWAuIEluYWN0aXZlIGVuZCB1c2VycyBhcmUgbm90IGFzc2lnbmVkIHRvIFt3b3JsZHNdKC4uL3dvcmxkLW1hbmFnZXIvKSBpbiBtdWx0aXBsYXllciBnYW1lcyBkdXJpbmcgYXV0b21hdGljIGFzc2lnbm1lbnQuXG4gICAgICAgICpcbiAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgdmFyIG1hID0gbmV3IEYuc2VydmljZS5NZW1iZXIoeyB0b2tlbjogJ3VzZXItb3ItcHJvamVjdC1hY2Nlc3MtdG9rZW4nIH0pO1xuICAgICAgICAqICAgICAgIG1hLm1ha2VVc2VySW5hY3RpdmUoeyB1c2VySWQ6ICc0MjgzNmQ0Yi01YjYxLTRmZTQtODBlYi0zMTM2ZTk1NmVlNWMnLFxuICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBJZDogJzgwMjU3YTI1LWFhMTAtNDk1OS05NjhiLWZkMDUzOTAxZjcyZicgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgVGhlIGVuZCB1c2VyIGFuZCBncm91cCBpbmZvcm1hdGlvbi5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnVzZXJJZCBUaGUgaWQgb2YgdGhlIGVuZCB1c2VyIHRvIG1ha2UgaW5hY3RpdmUuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5ncm91cElkIFRoZSBpZCBvZiB0aGUgZ3JvdXAgdG8gd2hpY2ggdGhpcyBlbmQgdXNlciBiZWxvbmdzLCBhbmQgaW4gd2hpY2ggdGhlIGVuZCB1c2VyIHNob3VsZCBiZWNvbWUgaW5hY3RpdmUuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBtYWtlVXNlckluYWN0aXZlOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICByZXR1cm4gcGF0Y2hVc2VyQWN0aXZlRmllbGQocGFyYW1zLCBmYWxzZSwgb3B0aW9ucyk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgJC5leHRlbmQodGhpcywgcHVibGljQVBJKTtcbn07XG4iLCIvKipcbiAqXG4gKiAjIyBSdW4gQVBJIFNlcnZpY2VcbiAqXG4gKiBUaGUgUnVuIEFQSSBTZXJ2aWNlIGFsbG93cyB5b3UgdG8gcGVyZm9ybSBjb21tb24gdGFza3MgYXJvdW5kIGNyZWF0aW5nIGFuZCB1cGRhdGluZyBydW5zLCB2YXJpYWJsZXMsIGFuZCBkYXRhLlxuICpcbiAqIFdoZW4gYnVpbGRpbmcgaW50ZXJmYWNlcyB0byBzaG93IHJ1biBvbmUgYXQgYSB0aW1lIChhcyBmb3Igc3RhbmRhcmQgZW5kIHVzZXJzKSwgdHlwaWNhbGx5IHlvdSBmaXJzdCBpbnN0YW50aWF0ZSBhIFtSdW4gTWFuYWdlcl0oLi4vcnVuLW1hbmFnZXIvKSBhbmQgdGhlbiBhY2Nlc3MgdGhlIFJ1biBTZXJ2aWNlIHRoYXQgaXMgYXV0b21hdGljYWxseSBwYXJ0IG9mIHRoZSBtYW5hZ2VyLCByYXRoZXIgdGhhbiBpbnN0YW50aWF0aW5nIHRoZSBSdW4gU2VydmljZSBkaXJlY3RseS4gVGhpcyBpcyBiZWNhdXNlIHRoZSBSdW4gTWFuYWdlciBnaXZlcyB5b3UgY29udHJvbCBvdmVyIHJ1biBjcmVhdGlvbiBkZXBlbmRpbmcgb24gcnVuIHN0YXRlcy5cbiAqXG4gKiBIb3dldmVyLCBtYW55IG9mIHRoZSBFcGljZW50ZXIgc2FtcGxlIHByb2plY3RzIHVzZSBhIFJ1biBTZXJ2aWNlLCBiZWNhdXNlIGdlbmVyYWxseSB0aGUgc2FtcGxlIHByb2plY3RzIGFyZSBwbGF5ZWQgaW4gb25lIGVuZCB1c2VyIHNlc3Npb24gYW5kIGRvbid0IGNhcmUgYWJvdXQgcnVuIHN0YXRlcyBvciBbcnVuIHN0cmF0ZWdpZXNdKC4uL3N0cmF0ZWdpZXMvKS4gVGhlIFJ1biBBUEkgU2VydmljZSBpcyBhbHNvIHVzZWZ1bCBmb3IgYnVpbGRpbmcgYW4gaW50ZXJmYWNlIGZvciBhIGZhY2lsaXRhdG9yLCBiZWNhdXNlIGl0IG1ha2VzIGl0IGVhc3kgdG8gbGlzdCBkYXRhIGFjcm9zcyBtdWx0aXBsZSBydW5zICh1c2luZyB0aGUgYGZpbHRlcigpYCBhbmQgYHF1ZXJ5KClgIG1ldGhvZHMpLlxuICpcbiAqIFRvIHVzZSB0aGUgUnVuIEFQSSBTZXJ2aWNlLCBpbnN0YW50aWF0ZSBpdCBieSBwYXNzaW5nIGluOlxuICpcbiAqICogYGFjY291bnRgOiBFcGljZW50ZXIgYWNjb3VudCBpZCAoKipUZWFtIElEKiogZm9yIHRlYW0gcHJvamVjdHMsICoqVXNlciBJRCoqIGZvciBwZXJzb25hbCBwcm9qZWN0cykuXG4gKiAqIGBwcm9qZWN0YDogRXBpY2VudGVyIHByb2plY3QgaWQuXG4gKlxuICogRm9yIGV4YW1wbGUsXG4gKlxuICogICAgICAgdmFyIHJzID0gbmV3IEYuc2VydmljZS5SdW4oe1xuICogICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gKiAgICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gKiAgICAgIH0pO1xuICogICAgICBycy5jcmVhdGUoJ3N1cHBseV9jaGFpbl9nYW1lLnB5JykudGhlbihmdW5jdGlvbihydW4pIHtcbiAqICAgICAgICAgICAgIHJzLmRvKCdzb21lT3BlcmF0aW9uJyk7XG4gKiAgICAgIH0pO1xuICpcbiAqXG4gKiBBZGRpdGlvbmFsbHksIGFsbCBBUEkgY2FsbHMgdGFrZSBpbiBhbiBcIm9wdGlvbnNcIiBvYmplY3QgYXMgdGhlIGxhc3QgcGFyYW1ldGVyLiBUaGUgb3B0aW9ucyBjYW4gYmUgdXNlZCB0byBleHRlbmQvb3ZlcnJpZGUgdGhlIFJ1biBBUEkgU2VydmljZSBkZWZhdWx0cyBsaXN0ZWQgYmVsb3cuXG4gKlxuICogTm90ZSB0aGF0IGluIGFkZGl0aW9uIHRvIHRoZSBgYWNjb3VudGAsIGBwcm9qZWN0YCwgYW5kIGBtb2RlbGAsIHRoZSBSdW4gU2VydmljZSBwYXJhbWV0ZXJzIG9wdGlvbmFsbHkgaW5jbHVkZSBhIGBzZXJ2ZXJgIG9iamVjdCwgd2hvc2UgYGhvc3RgIGZpZWxkIGNvbnRhaW5zIHRoZSBVUkkgb2YgdGhlIEZvcmlvIHNlcnZlci4gVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHNldCwgYnV0IHlvdSBjYW4gcGFzcyBpdCBleHBsaWNpdGx5IGlmIGRlc2lyZWQuIEl0IGlzIG1vc3QgY29tbW9ubHkgdXNlZCBmb3IgY2xhcml0eSB3aGVuIHlvdSBhcmUgW2hvc3RpbmcgYW4gRXBpY2VudGVyIHByb2plY3Qgb24geW91ciBvd24gc2VydmVyXSguLi8uLi8uLi9ob3dfdG8vc2VsZl9ob3N0aW5nLykuXG4gKlxuICogICAgICAgdmFyIHJtID0gbmV3IEYubWFuYWdlci5SdW5NYW5hZ2VyKHtcbiAqICAgICAgICAgICBydW46IHtcbiAqICAgICAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICogICAgICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICogICAgICAgICAgICAgICBtb2RlbDogJ3N1cHBseV9jaGFpbl9nYW1lLnB5JyxcbiAqICAgICAgICAgICAgICAgc2VydmVyOiB7IGhvc3Q6ICdhcGkuZm9yaW8uY29tJyB9XG4gKiAgICAgICAgICAgfVxuICogICAgICAgfSk7XG4gKiAgICAgICBybS5nZXRSdW4oKVxuICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHJ1bikge1xuICogICAgICAgICAgICAgICAvLyB0aGUgUnVuTWFuYWdlci5ydW4gY29udGFpbnMgdGhlIGluc3RhbnRpYXRlZCBSdW4gU2VydmljZSxcbiAqICAgICAgICAgICAgICAgLy8gc28gYW55IFJ1biBTZXJ2aWNlIG1ldGhvZCBpcyB2YWxpZCBoZXJlXG4gKiAgICAgICAgICAgICAgIHZhciBycyA9IHJtLnJ1bjtcbiAqICAgICAgICAgICAgICAgcnMuZG8oJ3NvbWVPcGVyYXRpb24nKTtcbiAqICAgICAgIH0pXG4gKlxuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIHF1dGlsID0gcmVxdWlyZSgnLi4vdXRpbC9xdWVyeS11dGlsJyk7XG52YXIgcnV0aWwgPSByZXF1aXJlKCcuLi91dGlsL3J1bi11dGlsJyk7XG52YXIgX3BpY2sgPSByZXF1aXJlKCcuLi91dGlsL29iamVjdC11dGlsJykuX3BpY2s7XG52YXIgVHJhbnNwb3J0RmFjdG9yeSA9IHJlcXVpcmUoJy4uL3RyYW5zcG9ydC9odHRwLXRyYW5zcG9ydC1mYWN0b3J5Jyk7XG52YXIgVmFyaWFibGVzU2VydmljZSA9IHJlcXVpcmUoJy4vdmFyaWFibGVzLWFwaS1zZXJ2aWNlJyk7XG52YXIgSW50cm9zcGVjdGlvblNlcnZpY2UgPSByZXF1aXJlKCcuL2ludHJvc3BlY3Rpb24tYXBpLXNlcnZpY2UnKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBGb3IgcHJvamVjdHMgdGhhdCByZXF1aXJlIGF1dGhlbnRpY2F0aW9uLCBwYXNzIGluIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiAoZGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nKS4gSWYgdGhlIHVzZXIgaXMgYWxyZWFkeSBsb2dnZWQgaW4gdG8gRXBpY2VudGVyLCB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gaXMgYWxyZWFkeSBzZXQgaW4gYSBjb29raWUgYW5kIGF1dG9tYXRpY2FsbHkgbG9hZGVkIGZyb20gdGhlcmUuIChTZWUgW21vcmUgYmFja2dyb3VuZCBvbiBhY2Nlc3MgdG9rZW5zXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pKS5cbiAgICAgICAgICogQHNlZSBbQXV0aGVudGljYXRpb24gQVBJIFNlcnZpY2VdKC4uL2F1dGgtYXBpLXNlcnZpY2UvKSBmb3IgZ2V0dGluZyB0b2tlbnMuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICB0b2tlbjogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgYWNjb3VudCBpZC4gSW4gdGhlIEVwaWNlbnRlciBVSSwgdGhpcyBpcyB0aGUgKipUZWFtIElEKiogKGZvciB0ZWFtIHByb2plY3RzKSBvciAqKlVzZXIgSUQqKiAoZm9yIHBlcnNvbmFsIHByb2plY3RzKS4gRGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nLiBJZiBsZWZ0IHVuZGVmaW5lZCwgdGFrZW4gZnJvbSB0aGUgVVJMLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgYWNjb3VudDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgcHJvamVjdCBpZC4gRGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nLiBJZiBsZWZ0IHVuZGVmaW5lZCwgdGFrZW4gZnJvbSB0aGUgVVJMLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgcHJvamVjdDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDcml0ZXJpYSBieSB3aGljaCB0byBmaWx0ZXIgcnVucy4gRGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgZmlsdGVyOiAnJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ29udmVuaWVuY2UgYWxpYXMgZm9yIGZpbHRlci5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGlkOiAnJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogRmxhZyBkZXRlcm1pbmVzIGlmIGBYLUF1dG9SZXN0b3JlOiB0cnVlYCBoZWFkZXIgaXMgc2VudCB0byBFcGljZW50ZXIuIERlZmF1bHRzIHRvIGB0cnVlYC5cbiAgICAgICAgICogQHR5cGUge2Jvb2xlYW59XG4gICAgICAgICAqL1xuICAgICAgICBhdXRvUmVzdG9yZTogdHJ1ZSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ2FsbGVkIHdoZW4gdGhlIGNhbGwgY29tcGxldGVzIHN1Y2Nlc3NmdWxseS4gRGVmYXVsdHMgdG8gYCQubm9vcGAuXG4gICAgICAgICAqIEB0eXBlIHtmdW5jdGlvbn1cbiAgICAgICAgICovXG4gICAgICAgIHN1Y2Nlc3M6ICQubm9vcCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ2FsbGVkIHdoZW4gdGhlIGNhbGwgZmFpbHMuIERlZmF1bHRzIHRvIGAkLm5vb3BgLlxuICAgICAgICAgKiBAdHlwZSB7ZnVuY3Rpb259XG4gICAgICAgICAqL1xuICAgICAgICBlcnJvcjogJC5ub29wLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBPcHRpb25zIHRvIHBhc3Mgb24gdG8gdGhlIHVuZGVybHlpbmcgdHJhbnNwb3J0IGxheWVyLiBBbGwganF1ZXJ5LmFqYXggb3B0aW9ucyBhdCBodHRwOi8vYXBpLmpxdWVyeS5jb20valF1ZXJ5LmFqYXgvIGFyZSBhdmFpbGFibGUuIERlZmF1bHRzIHRvIGVtcHR5IG9iamVjdC5cbiAgICAgICAgICogQHR5cGUge09iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIHRyYW5zcG9ydDoge31cbiAgICB9O1xuXG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgY29uZmlnKTtcbiAgICBpZiAoc2VydmljZU9wdGlvbnMuaWQpIHtcbiAgICAgICAgc2VydmljZU9wdGlvbnMuZmlsdGVyID0gc2VydmljZU9wdGlvbnMuaWQ7XG4gICAgfVxuXG4gICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5hY2NvdW50KSB7XG4gICAgICAgIHVybENvbmZpZy5hY2NvdW50UGF0aCA9IHNlcnZpY2VPcHRpb25zLmFjY291bnQ7XG4gICAgfVxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5wcm9qZWN0KSB7XG4gICAgICAgIHVybENvbmZpZy5wcm9qZWN0UGF0aCA9IHNlcnZpY2VPcHRpb25zLnByb2plY3Q7XG4gICAgfVxuXG4gICAgdXJsQ29uZmlnLmZpbHRlciA9ICc7JztcbiAgICB1cmxDb25maWcuZ2V0RmlsdGVyVVJMID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgdXJsID0gdXJsQ29uZmlnLmdldEFQSVBhdGgoJ3J1bicpO1xuICAgICAgICB2YXIgZmlsdGVyID0gcXV0aWwudG9NYXRyaXhGb3JtYXQoc2VydmljZU9wdGlvbnMuZmlsdGVyKTtcblxuICAgICAgICBpZiAoZmlsdGVyKSB7XG4gICAgICAgICAgICB1cmwgKz0gZmlsdGVyICsgJy8nO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB1cmw7XG4gICAgfTtcblxuICAgIHVybENvbmZpZy5hZGRBdXRvUmVzdG9yZUhlYWRlciA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIHZhciBmaWx0ZXIgPSBzZXJ2aWNlT3B0aW9ucy5maWx0ZXI7XG4gICAgICAgIC8vIFRoZSBzZW1pY29sb24gc2VwYXJhdGVkIGZpbHRlciBpcyB1c2VkIHdoZW4gZmlsdGVyIGlzIGFuIG9iamVjdFxuICAgICAgICB2YXIgaXNGaWx0ZXJSdW5JZCA9IGZpbHRlciAmJiAkLnR5cGUoZmlsdGVyKSA9PT0gJ3N0cmluZyc7XG4gICAgICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5hdXRvUmVzdG9yZSAmJiBpc0ZpbHRlclJ1bklkKSB7XG4gICAgICAgICAgICAvLyBCeSBkZWZhdWx0IGF1dG9yZXBsYXkgdGhlIHJ1biBieSBzZW5kaW5nIHRoaXMgaGVhZGVyIHRvIGVwaWNlbnRlclxuICAgICAgICAgICAgLy8gaHR0cHM6Ly9mb3Jpby5jb20vZXBpY2VudGVyL2RvY3MvcHVibGljL3Jlc3RfYXBpcy9hZ2dyZWdhdGVfcnVuX2FwaS8jcmV0cmlldmluZ1xuICAgICAgICAgICAgdmFyIGF1dG9yZXN0b3JlT3B0cyA9IHtcbiAgICAgICAgICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgICAgICAgICAgICdYLUF1dG9SZXN0b3JlJzogdHJ1ZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXR1cm4gJC5leHRlbmQodHJ1ZSwgYXV0b3Jlc3RvcmVPcHRzLCBvcHRpb25zKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBvcHRpb25zO1xuICAgIH07XG5cbiAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LCB7XG4gICAgICAgIHVybDogdXJsQ29uZmlnLmdldEZpbHRlclVSTFxuICAgIH0pO1xuXG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnRva2VuKSB7XG4gICAgICAgIGh0dHBPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KGh0dHBPcHRpb25zKTtcbiAgICBodHRwLnNwbGl0R2V0ID0gcnV0aWwuc3BsaXRHZXRGYWN0b3J5KGh0dHBPcHRpb25zKTtcblxuICAgIHZhciBzZXRGaWx0ZXJPclRocm93RXJyb3IgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICBpZiAob3B0aW9ucy5pZCkge1xuICAgICAgICAgICAgc2VydmljZU9wdGlvbnMuZmlsdGVyID0gb3B0aW9ucy5pZDtcbiAgICAgICAgfVxuICAgICAgICBpZiAob3B0aW9ucy5maWx0ZXIpIHtcbiAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IG9wdGlvbnMuZmlsdGVyO1xuICAgICAgICB9XG4gICAgICAgIGlmICghc2VydmljZU9wdGlvbnMuZmlsdGVyKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIGZpbHRlciBzcGVjaWZpZWQgdG8gYXBwbHkgb3BlcmF0aW9ucyBhZ2FpbnN0Jyk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgdmFyIHB1YmxpY0FzeW5jQVBJID0ge1xuICAgICAgICB1cmxDb25maWc6IHVybENvbmZpZyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ3JlYXRlIGEgbmV3IHJ1bi5cbiAgICAgICAgICpcbiAgICAgICAgICogTk9URTogVHlwaWNhbGx5IHRoaXMgaXMgbm90IHVzZWQhIFVzZSBgUnVuTWFuYWdlci5nZXRSdW4oKWAgd2l0aCBhIGBzdHJhdGVneWAgb2YgYGFsd2F5cy1uZXdgLCBvciB1c2UgYFJ1bk1hbmFnZXIucmVzZXQoKWAuIFNlZSBbUnVuIE1hbmFnZXJdKC4uL3J1bi1tYW5hZ2VyLykgZm9yIG1vcmUgZGV0YWlscy5cbiAgICAgICAgICpcbiAgICAgICAgICogICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgcnMuY3JlYXRlKCdoZWxsb193b3JsZC5qbCcpO1xuICAgICAgICAgKlxuICAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd8T2JqZWN0fSBwYXJhbXMgSWYgYSBzdHJpbmcsIHRoZSBuYW1lIG9mIHRoZSBwcmltYXJ5IFttb2RlbCBmaWxlXSguLi8uLi8uLi93cml0aW5nX3lvdXJfbW9kZWwvKS4gVGhpcyBpcyB0aGUgb25lIGZpbGUgaW4gdGhlIHByb2plY3QgdGhhdCBleHBsaWNpdGx5IGV4cG9zZXMgdmFyaWFibGVzIGFuZCBtZXRob2RzLCBhbmQgaXQgbXVzdCBiZSBzdG9yZWQgaW4gdGhlIE1vZGVsIGZvbGRlciBvZiB5b3VyIEVwaWNlbnRlciBwcm9qZWN0LiBJZiBhbiBvYmplY3QsIG1heSBpbmNsdWRlIGBtb2RlbGAsIGBzY29wZWAsIGFuZCBgZmlsZXNgLiAoU2VlIHRoZSBbUnVuIE1hbmFnZXJdKC4uL3J1bl9tYW5hZ2VyLykgZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gYHNjb3BlYCBhbmQgYGZpbGVzYC4pXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBjcmVhdGU6IGZ1bmN0aW9uIChwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBjcmVhdGVPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zLCB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoJ3J1bicpIH0pO1xuICAgICAgICAgICAgdmFyIHJ1bkFwaVBhcmFtcyA9IFsnbW9kZWwnLCAnc2NvcGUnLCAnZmlsZXMnXTtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgcGFyYW1zID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIC8vIHRoaXMgaXMganVzdCB0aGUgbW9kZWwgbmFtZVxuICAgICAgICAgICAgICAgIHBhcmFtcyA9IHsgbW9kZWw6IHBhcmFtcyB9O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyB3aGl0ZWxpc3QgdGhlIGZpZWxkcyB0aGF0IHdlIGFjdHVhbGx5IGNhbiBzZW5kIHRvIHRoZSBhcGlcbiAgICAgICAgICAgICAgICBwYXJhbXMgPSBfcGljayhwYXJhbXMsIHJ1bkFwaVBhcmFtcyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBvbGRTdWNjZXNzID0gY3JlYXRlT3B0aW9ucy5zdWNjZXNzO1xuICAgICAgICAgICAgY3JlYXRlT3B0aW9ucy5zdWNjZXNzID0gZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMuZmlsdGVyID0gcmVzcG9uc2UuaWQ7IC8vYWxsIGZ1dHVyZSBjaGFpbmVkIGNhbGxzIHRvIG9wZXJhdGUgb24gdGhpcyBpZFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmlkID0gcmVzcG9uc2UuaWQ7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG9sZFN1Y2Nlc3MuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLnBvc3QocGFyYW1zLCBjcmVhdGVPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmV0dXJucyBwYXJ0aWN1bGFyIHJ1bnMsIGJhc2VkIG9uIGNvbmRpdGlvbnMgc3BlY2lmaWVkIGluIHRoZSBgcXNgIG9iamVjdC5cbiAgICAgICAgICpcbiAgICAgICAgICogVGhlIGVsZW1lbnRzIG9mIHRoZSBgcXNgIG9iamVjdCBhcmUgQU5EZWQgdG9nZXRoZXIgd2l0aGluIGEgc2luZ2xlIGNhbGwgdG8gYC5xdWVyeSgpYC5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyByZXR1cm5zIHJ1bnMgd2l0aCBzYXZlZCA9IHRydWUgYW5kIHZhcmlhYmxlcy5wcmljZSA+IDEsXG4gICAgICAgICAqICAgICAgLy8gd2hlcmUgdmFyaWFibGVzLnByaWNlIGhhcyBiZWVuIHBlcnNpc3RlZCAocmVjb3JkZWQpXG4gICAgICAgICAqICAgICAgLy8gaW4gdGhlIG1vZGVsLlxuICAgICAgICAgKiAgICAgcnMucXVlcnkoe1xuICAgICAgICAgKiAgICAgICAgICAnc2F2ZWQnOiAndHJ1ZScsXG4gICAgICAgICAqICAgICAgICAgICcucHJpY2UnOiAnPjEnXG4gICAgICAgICAqICAgICAgIH0sXG4gICAgICAgICAqICAgICAgIHtcbiAgICAgICAgICogICAgICAgICAgc3RhcnRyZWNvcmQ6IDIsXG4gICAgICAgICAqICAgICAgICAgIGVuZHJlY29yZDogNVxuICAgICAgICAgKiAgICAgICB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IHFzIFF1ZXJ5IG9iamVjdC4gRWFjaCBrZXkgY2FuIGJlIGEgcHJvcGVydHkgb2YgdGhlIHJ1biBvciB0aGUgbmFtZSBvZiB2YXJpYWJsZSB0aGF0IGhhcyBiZWVuIHNhdmVkIGluIHRoZSBydW4gKHByZWZhY2VkIGJ5IGB2YXJpYWJsZXMuYCkuIEVhY2ggdmFsdWUgY2FuIGJlIGEgbGl0ZXJhbCB2YWx1ZSwgb3IgYSBjb21wYXJpc29uIG9wZXJhdG9yIGFuZCB2YWx1ZS4gKFNlZSBbbW9yZSBvbiBmaWx0ZXJpbmddKC4uLy4uLy4uL3Jlc3RfYXBpcy9hZ2dyZWdhdGVfcnVuX2FwaS8jZmlsdGVycykgYWxsb3dlZCBpbiB0aGUgdW5kZXJseWluZyBSdW4gQVBJLikgUXVlcnlpbmcgZm9yIHZhcmlhYmxlcyBpcyBhdmFpbGFibGUgZm9yIHJ1bnMgW2luIG1lbW9yeV0oLi4vLi4vLi4vcnVuX3BlcnNpc3RlbmNlLyNydW5zLWluLW1lbW9yeSkgYW5kIGZvciBydW5zIFtpbiB0aGUgZGF0YWJhc2VdKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZS8jcnVucy1pbi1tZW1vcnkpIGlmIHRoZSB2YXJpYWJsZXMgYXJlIHBlcnNpc3RlZCAoZS5nLiB0aGF0IGhhdmUgYmVlbiBgcmVjb3JkYGVkIGluIHlvdXIgSnVsaWEgbW9kZWwpLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3V0cHV0TW9kaWZpZXIgKE9wdGlvbmFsKSBBdmFpbGFibGUgZmllbGRzIGluY2x1ZGU6IGBzdGFydHJlY29yZGAsIGBlbmRyZWNvcmRgLCBgc29ydGAsIGFuZCBgZGlyZWN0aW9uYCAoYGFzY2Agb3IgYGRlc2NgKS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgIHF1ZXJ5OiBmdW5jdGlvbiAocXMsIG91dHB1dE1vZGlmaWVyLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgPSBxczsgLy9zaG91bGRuJ3QgYmUgYWJsZSB0byBvdmVyLXJpZGVcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICBodHRwT3B0aW9ucyA9IHVybENvbmZpZy5hZGRBdXRvUmVzdG9yZUhlYWRlcihodHRwT3B0aW9ucyk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLnNwbGl0R2V0KG91dHB1dE1vZGlmaWVyLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybnMgcGFydGljdWxhciBydW5zLCBiYXNlZCBvbiBjb25kaXRpb25zIHNwZWNpZmllZCBpbiB0aGUgYHFzYCBvYmplY3QuXG4gICAgICAgICAqXG4gICAgICAgICAqIFNpbWlsYXIgdG8gYC5xdWVyeSgpYC5cbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IGZpbHRlciBGaWx0ZXIgb2JqZWN0LiBFYWNoIGtleSBjYW4gYmUgYSBwcm9wZXJ0eSBvZiB0aGUgcnVuIG9yIHRoZSBuYW1lIG9mIHZhcmlhYmxlIHRoYXQgaGFzIGJlZW4gc2F2ZWQgaW4gdGhlIHJ1biAocHJlZmFjZWQgYnkgYHZhcmlhYmxlcy5gKS4gRWFjaCB2YWx1ZSBjYW4gYmUgYSBsaXRlcmFsIHZhbHVlLCBvciBhIGNvbXBhcmlzb24gb3BlcmF0b3IgYW5kIHZhbHVlLiAoU2VlIFttb3JlIG9uIGZpbHRlcmluZ10oLi4vLi4vLi4vcmVzdF9hcGlzL2FnZ3JlZ2F0ZV9ydW5fYXBpLyNmaWx0ZXJzKSBhbGxvd2VkIGluIHRoZSB1bmRlcmx5aW5nIFJ1biBBUEkuKSBGaWx0ZXJpbmcgZm9yIHZhcmlhYmxlcyBpcyBhdmFpbGFibGUgZm9yIHJ1bnMgW2luIG1lbW9yeV0oLi4vLi4vLi4vcnVuX3BlcnNpc3RlbmNlLyNydW5zLWluLW1lbW9yeSkgYW5kIGZvciBydW5zIFtpbiB0aGUgZGF0YWJhc2VdKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZS8jcnVucy1pbi1tZW1vcnkpIGlmIHRoZSB2YXJpYWJsZXMgYXJlIHBlcnNpc3RlZCAoZS5nLiB0aGF0IGhhdmUgYmVlbiBgcmVjb3JkYGVkIGluIHlvdXIgSnVsaWEgbW9kZWwpLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3V0cHV0TW9kaWZpZXIgKE9wdGlvbmFsKSBBdmFpbGFibGUgZmllbGRzIGluY2x1ZGU6IGBzdGFydHJlY29yZGAsIGBlbmRyZWNvcmRgLCBgc29ydGAsIGFuZCBgZGlyZWN0aW9uYCAoYGFzY2Agb3IgYGRlc2NgKS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgIGZpbHRlcjogZnVuY3Rpb24gKGZpbHRlciwgb3V0cHV0TW9kaWZpZXIsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGlmICgkLmlzUGxhaW5PYmplY3Qoc2VydmljZU9wdGlvbnMuZmlsdGVyKSkge1xuICAgICAgICAgICAgICAgICQuZXh0ZW5kKHNlcnZpY2VPcHRpb25zLmZpbHRlciwgZmlsdGVyKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMuZmlsdGVyID0gZmlsdGVyO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIGh0dHBPcHRpb25zID0gdXJsQ29uZmlnLmFkZEF1dG9SZXN0b3JlSGVhZGVyKGh0dHBPcHRpb25zKTtcbiAgICAgICAgICAgIHJldHVybiBodHRwLnNwbGl0R2V0KG91dHB1dE1vZGlmaWVyLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCBkYXRhIGZvciBhIHNwZWNpZmljIHJ1bi4gVGhpcyBpbmNsdWRlcyBzdGFuZGFyZCBydW4gZGF0YSBzdWNoIGFzIHRoZSBhY2NvdW50LCBtb2RlbCwgcHJvamVjdCwgYW5kIGNyZWF0ZWQgYW5kIGxhc3QgbW9kaWZpZWQgZGF0ZXMuIFRvIHJlcXVlc3Qgc3BlY2lmaWMgbW9kZWwgdmFyaWFibGVzLCBwYXNzIHRoZW0gYXMgcGFydCBvZiB0aGUgYGZpbHRlcnNgIHBhcmFtZXRlci5cbiAgICAgICAgICpcbiAgICAgICAgICogTm90ZSB0aGF0IGlmIHRoZSBydW4gaXMgW2luIG1lbW9yeV0oLi4vLi4vLi4vcnVuX3BlcnNpc3RlbmNlLyNydW5zLWluLW1lbW9yeSksIGFueSBtb2RlbCB2YXJpYWJsZXMgYXJlIGF2YWlsYWJsZTsgaWYgdGhlIHJ1biBpcyBbaW4gdGhlIGRhdGFiYXNlXSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvI3J1bnMtaW4tZGIpLCBvbmx5IG1vZGVsIHZhcmlhYmxlcyB0aGF0IGhhdmUgYmVlbiBwZXJzaXN0ZWQgJm1kYXNoOyB0aGF0IGlzLCBgcmVjb3JkYGVkIGluIHlvdXIgSnVsaWEgbW9kZWwgJm1kYXNoOyBhcmUgYXZhaWxhYmxlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgcnMubG9hZCgnYmI1ODk2NzctZDQ3Ni00OTcxLWE2OGUtMGM1OGQxOTFlNDUwJywgeyBpbmNsdWRlOiBbJy5wcmljZScsICcuc2FsZXMnXSB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IHJ1bklEIFRoZSBydW4gaWQuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBmaWx0ZXJzIChPcHRpb25hbCkgT2JqZWN0IGNvbnRhaW5pbmcgZmlsdGVycyBhbmQgb3BlcmF0aW9uIG1vZGlmaWVycy4gVXNlIGtleSBgaW5jbHVkZWAgdG8gbGlzdCBtb2RlbCB2YXJpYWJsZXMgdGhhdCB5b3Ugd2FudCB0byBpbmNsdWRlIGluIHRoZSByZXNwb25zZS4gT3RoZXIgYXZhaWxhYmxlIGZpZWxkcyBpbmNsdWRlOiBgc3RhcnRyZWNvcmRgLCBgZW5kcmVjb3JkYCwgYHNvcnRgLCBhbmQgYGRpcmVjdGlvbmAgKGBhc2NgIG9yIGBkZXNjYCkuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBsb2FkOiBmdW5jdGlvbiAocnVuSUQsIGZpbHRlcnMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGlmIChydW5JRCkge1xuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IHJ1bklEOyAvL3Nob3VsZG4ndCBiZSBhYmxlIHRvIG92ZXItcmlkZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIGh0dHBPcHRpb25zID0gdXJsQ29uZmlnLmFkZEF1dG9SZXN0b3JlSGVhZGVyKGh0dHBPcHRpb25zKTtcbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldChmaWx0ZXJzLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cblxuICAgICAgICAvKipcbiAgICAgICAgICogU2F2ZSBhdHRyaWJ1dGVzIChkYXRhLCBtb2RlbCB2YXJpYWJsZXMpIG9mIHRoZSBydW4uXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZXMqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgLy8gYWRkICdjb21wbGV0ZWQnIGZpZWxkIHRvIHJ1biByZWNvcmRcbiAgICAgICAgICogICAgIHJzLnNhdmUoeyBjb21wbGV0ZWQ6IHRydWUgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAvLyB1cGRhdGUgJ3NhdmVkJyBmaWVsZCBvZiBydW4gcmVjb3JkLCBhbmQgdXBkYXRlIHZhbHVlcyBvZiBtb2RlbCB2YXJpYWJsZXMgZm9yIHRoaXMgcnVuXG4gICAgICAgICAqICAgICBycy5zYXZlKHsgc2F2ZWQ6IHRydWUsIHZhcmlhYmxlczogeyBhOiAyMywgYjogMjMgfSB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IGF0dHJpYnV0ZXMgVGhlIHJ1biBkYXRhIGFuZCB2YXJpYWJsZXMgdG8gc2F2ZS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IGF0dHJpYnV0ZXMudmFyaWFibGVzIE1vZGVsIHZhcmlhYmxlcyBtdXN0IGJlIGluY2x1ZGVkIGluIGEgYHZhcmlhYmxlc2AgZmllbGQgd2l0aGluIHRoZSBgYXR0cmlidXRlc2Agb2JqZWN0LiAoT3RoZXJ3aXNlIHRoZXkgYXJlIHRyZWF0ZWQgYXMgcnVuIGRhdGEgYW5kIGFkZGVkIHRvIHRoZSBydW4gcmVjb3JkIGRpcmVjdGx5LilcbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgIHNhdmU6IGZ1bmN0aW9uIChhdHRyaWJ1dGVzLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgc2V0RmlsdGVyT3JUaHJvd0Vycm9yKGh0dHBPcHRpb25zKTtcbiAgICAgICAgICAgIHJldHVybiBodHRwLnBhdGNoKGF0dHJpYnV0ZXMsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ2FsbCBhIG1ldGhvZCBmcm9tIHRoZSBtb2RlbC5cbiAgICAgICAgICpcbiAgICAgICAgICogRGVwZW5kaW5nIG9uIHRoZSBsYW5ndWFnZSBpbiB3aGljaCB5b3UgaGF2ZSB3cml0dGVuIHlvdXIgbW9kZWwsIHRoZSBtZXRob2QgbWF5IG5lZWQgdG8gYmUgZXhwb3NlZCAoZS5nLiBgZXhwb3J0YCBmb3IgYSBKdWxpYSBtb2RlbCkgaW4gdGhlIG1vZGVsIGZpbGUgaW4gb3JkZXIgdG8gYmUgY2FsbGVkIHRocm91Z2ggdGhlIEFQSS4gU2VlIFtXcml0aW5nIHlvdXIgTW9kZWxdKC4uLy4uLy4uL3dyaXRpbmdfeW91cl9tb2RlbC8pKS5cbiAgICAgICAgICpcbiAgICAgICAgICogVGhlIGBwYXJhbXNgIGFyZ3VtZW50IGlzIG5vcm1hbGx5IGFuIGFycmF5IG9mIGFyZ3VtZW50cyB0byB0aGUgYG9wZXJhdGlvbmAuIEluIHRoZSBzcGVjaWFsIGNhc2Ugd2hlcmUgYG9wZXJhdGlvbmAgb25seSB0YWtlcyBvbmUgYXJndW1lbnQsIHlvdSBhcmUgbm90IHJlcXVpcmVkIHRvIHB1dCB0aGF0IGFyZ3VtZW50IGludG8gYW4gYXJyYXkuXG4gICAgICAgICAqXG4gICAgICAgICAqIE5vdGUgdGhhdCB5b3UgY2FuIGNvbWJpbmUgdGhlIGBvcGVyYXRpb25gIGFuZCBgcGFyYW1zYCBhcmd1bWVudHMgaW50byBhIHNpbmdsZSBvYmplY3QgaWYgeW91IHByZWZlciwgYXMgaW4gdGhlIGxhc3QgZXhhbXBsZS5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlcyoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gbWV0aG9kIFwic29sdmVcIiB0YWtlcyBubyBhcmd1bWVudHNcbiAgICAgICAgICogICAgIHJzLmRvKCdzb2x2ZScpO1xuICAgICAgICAgKiAgICAgIC8vIG1ldGhvZCBcImVjaG9cIiB0YWtlcyBvbmUgYXJndW1lbnQsIGEgc3RyaW5nXG4gICAgICAgICAqICAgICBycy5kbygnZWNobycsIFsnaGVsbG8nXSk7XG4gICAgICAgICAqICAgICAgLy8gbWV0aG9kIFwiZWNob1wiIHRha2VzIG9uZSBhcmd1bWVudCwgYSBzdHJpbmdcbiAgICAgICAgICogICAgIHJzLmRvKCdlY2hvJywgJ2hlbGxvJyk7XG4gICAgICAgICAqICAgICAgLy8gbWV0aG9kIFwic3VtQXJyYXlcIiB0YWtlcyBvbmUgYXJndW1lbnQsIGFuIGFycmF5XG4gICAgICAgICAqICAgICBycy5kbygnc3VtQXJyYXknLCBbWzQsMiwxXV0pO1xuICAgICAgICAgKiAgICAgIC8vIG1ldGhvZCBcImFkZFwiIHRha2VzIHR3byBhcmd1bWVudHMsIGJvdGggaW50ZWdlcnNcbiAgICAgICAgICogICAgIHJzLmRvKHsgbmFtZTonYWRkJywgcGFyYW1zOlsyLDRdIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gb3BlcmF0aW9uIE5hbWUgb2YgbWV0aG9kLlxuICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSBwYXJhbXMgKE9wdGlvbmFsKSBBbnkgcGFyYW1ldGVycyB0aGUgb3BlcmF0aW9uIHRha2VzLCBwYXNzZWQgYXMgYW4gYXJyYXkuIEluIHRoZSBzcGVjaWFsIGNhc2Ugd2hlcmUgYG9wZXJhdGlvbmAgb25seSB0YWtlcyBvbmUgYXJndW1lbnQsIHlvdSBhcmUgbm90IHJlcXVpcmVkIHRvIHB1dCB0aGF0IGFyZ3VtZW50IGludG8gYW4gYXJyYXksIGFuZCBjYW4ganVzdCBwYXNzIGl0IGRpcmVjdGx5LlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgZG86IGZ1bmN0aW9uIChvcGVyYXRpb24sIHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICAgICAgLy8gY29uc29sZS5sb2coJ2RvJywgb3BlcmF0aW9uLCBwYXJhbXMpO1xuICAgICAgICAgICAgdmFyIG9wc0FyZ3M7XG4gICAgICAgICAgICB2YXIgcG9zdE9wdGlvbnM7XG4gICAgICAgICAgICBpZiAob3B0aW9ucykge1xuICAgICAgICAgICAgICAgIG9wc0FyZ3MgPSBwYXJhbXM7XG4gICAgICAgICAgICAgICAgcG9zdE9wdGlvbnMgPSBvcHRpb25zO1xuICAgICAgICAgICAgfSBlbHNlIGlmICgkLmlzUGxhaW5PYmplY3QocGFyYW1zKSkge1xuICAgICAgICAgICAgICAgIG9wc0FyZ3MgPSBudWxsO1xuICAgICAgICAgICAgICAgIHBvc3RPcHRpb25zID0gcGFyYW1zO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBvcHNBcmdzID0gcGFyYW1zO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHJlc3VsdCA9IHJ1dGlsLm5vcm1hbGl6ZU9wZXJhdGlvbnMob3BlcmF0aW9uLCBvcHNBcmdzKTtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgcG9zdE9wdGlvbnMpO1xuXG4gICAgICAgICAgICBzZXRGaWx0ZXJPclRocm93RXJyb3IoaHR0cE9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgcHJtcyA9IChyZXN1bHQuYXJnc1swXS5sZW5ndGggJiYgKHJlc3VsdC5hcmdzWzBdICE9PSBudWxsICYmIHJlc3VsdC5hcmdzWzBdICE9PSB1bmRlZmluZWQpKSA/IHJlc3VsdC5hcmdzWzBdIDogW107XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wb3N0KHsgYXJndW1lbnRzOiBwcm1zIH0sICQuZXh0ZW5kKHRydWUsIHt9LCBodHRwT3B0aW9ucywge1xuICAgICAgICAgICAgICAgIHVybDogdXJsQ29uZmlnLmdldEZpbHRlclVSTCgpICsgJ29wZXJhdGlvbnMvJyArIHJlc3VsdC5vcHNbMF0gKyAnLydcbiAgICAgICAgICAgIH0pKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ2FsbCBzZXZlcmFsIG1ldGhvZHMgZnJvbSB0aGUgbW9kZWwsIHNlcXVlbnRpYWxseS5cbiAgICAgICAgICpcbiAgICAgICAgICogRGVwZW5kaW5nIG9uIHRoZSBsYW5ndWFnZSBpbiB3aGljaCB5b3UgaGF2ZSB3cml0dGVuIHlvdXIgbW9kZWwsIHRoZSBtZXRob2RzIG1heSBuZWVkIHRvIGJlIGV4cG9zZWQgKGUuZy4gYGV4cG9ydGAgZm9yIGEgSnVsaWEgbW9kZWwpIGluIHRoZSBtb2RlbCBmaWxlIGluIG9yZGVyIHRvIGJlIGNhbGxlZCB0aHJvdWdoIHRoZSBBUEkuIFNlZSBbV3JpdGluZyB5b3VyIE1vZGVsXSguLi8uLi8uLi93cml0aW5nX3lvdXJfbW9kZWwvKSkuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZXMqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIG1ldGhvZHMgXCJpbml0aWFsaXplXCIgYW5kIFwic29sdmVcIiBkbyBub3QgdGFrZSBhbnkgYXJndW1lbnRzXG4gICAgICAgICAqICAgICBycy5zZXJpYWwoWydpbml0aWFsaXplJywgJ3NvbHZlJ10pO1xuICAgICAgICAgKiAgICAgIC8vIG1ldGhvZHMgXCJpbml0XCIgYW5kIFwicmVzZXRcIiB0YWtlIHR3byBhcmd1bWVudHMgZWFjaFxuICAgICAgICAgKiAgICAgcnMuc2VyaWFsKFsgIHsgbmFtZTogJ2luaXQnLCBwYXJhbXM6IFsxLDJdIH0sXG4gICAgICAgICAqICAgICAgICAgICAgICAgICAgeyBuYW1lOiAncmVzZXQnLCBwYXJhbXM6IFsyLDNdIH1dKTtcbiAgICAgICAgICogICAgICAvLyBtZXRob2QgXCJpbml0XCIgdGFrZXMgdHdvIGFyZ3VtZW50cyxcbiAgICAgICAgICogICAgICAvLyBtZXRob2QgXCJydW5tb2RlbFwiIHRha2VzIG5vbmVcbiAgICAgICAgICogICAgIHJzLnNlcmlhbChbICB7IG5hbWU6ICdpbml0JywgcGFyYW1zOiBbMSwyXSB9LFxuICAgICAgICAgKiAgICAgICAgICAgICAgICAgIHsgbmFtZTogJ3J1bm1vZGVsJywgcGFyYW1zOiBbXSB9XSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7QXJyYXl9IG9wZXJhdGlvbnMgSWYgbm9uZSBvZiB0aGUgbWV0aG9kcyB0YWtlIHBhcmFtZXRlcnMsIHBhc3MgYW4gYXJyYXkgb2YgdGhlIG1ldGhvZCBuYW1lcyAoc3RyaW5ncykuIElmIGFueSBvZiB0aGUgbWV0aG9kcyBkbyB0YWtlIHBhcmFtZXRlcnMsIHBhc3MgYW4gYXJyYXkgb2Ygb2JqZWN0cywgZWFjaCBvZiB3aGljaCBjb250YWlucyBhIG1ldGhvZCBuYW1lIGFuZCBpdHMgb3duIChwb3NzaWJseSBlbXB0eSkgYXJyYXkgb2YgcGFyYW1ldGVycy5cbiAgICAgICAgICogQHBhcmFtIHsqfSBwYXJhbXMgUGFyYW1ldGVycyB0byBwYXNzIHRvIG9wZXJhdGlvbnMuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBzZXJpYWw6IGZ1bmN0aW9uIChvcGVyYXRpb25zLCBwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBvcFBhcmFtcyA9IHJ1dGlsLm5vcm1hbGl6ZU9wZXJhdGlvbnMob3BlcmF0aW9ucywgcGFyYW1zKTtcbiAgICAgICAgICAgIHZhciBvcHMgPSBvcFBhcmFtcy5vcHM7XG4gICAgICAgICAgICB2YXIgYXJncyA9IG9wUGFyYW1zLmFyZ3M7XG4gICAgICAgICAgICB2YXIgbWUgPSB0aGlzO1xuXG4gICAgICAgICAgICB2YXIgJGQgPSAkLkRlZmVycmVkKCk7XG4gICAgICAgICAgICB2YXIgcG9zdE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgZG9TaW5nbGVPcCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICB2YXIgb3AgPSBvcHMuc2hpZnQoKTtcbiAgICAgICAgICAgICAgICB2YXIgYXJnID0gYXJncy5zaGlmdCgpO1xuXG4gICAgICAgICAgICAgICAgbWUuZG8ob3AsIGFyZywge1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAob3BzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvU2luZ2xlT3AoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJGQucmVzb2x2ZS5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc3RPcHRpb25zLnN1Y2Nlc3MuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZXJyb3I6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICRkLnJlamVjdC5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgcG9zdE9wdGlvbnMuZXJyb3IuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgZG9TaW5nbGVPcCgpO1xuXG4gICAgICAgICAgICByZXR1cm4gJGQucHJvbWlzZSgpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDYWxsIHNldmVyYWwgbWV0aG9kcyBmcm9tIHRoZSBtb2RlbCwgZXhlY3V0aW5nIHRoZW0gaW4gcGFyYWxsZWwuXG4gICAgICAgICAqXG4gICAgICAgICAqIERlcGVuZGluZyBvbiB0aGUgbGFuZ3VhZ2UgaW4gd2hpY2ggeW91IGhhdmUgd3JpdHRlbiB5b3VyIG1vZGVsLCB0aGUgbWV0aG9kcyBtYXkgbmVlZCB0byBiZSBleHBvc2VkIChlLmcuIGBleHBvcnRgIGZvciBhIEp1bGlhIG1vZGVsKSBpbiB0aGUgbW9kZWwgZmlsZSBpbiBvcmRlciB0byBiZSBjYWxsZWQgdGhyb3VnaCB0aGUgQVBJLiBTZWUgW1dyaXRpbmcgeW91ciBNb2RlbF0oLi4vLi4vLi4vd3JpdGluZ195b3VyX21vZGVsLykpLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIG1ldGhvZHMgXCJzb2x2ZVwiIGFuZCBcInJlc2V0XCIgZG8gbm90IHRha2UgYW55IGFyZ3VtZW50c1xuICAgICAgICAgKiAgICAgcnMucGFyYWxsZWwoWydzb2x2ZScsICdyZXNldCddKTtcbiAgICAgICAgICogICAgICAvLyBtZXRob2RzIFwiYWRkXCIgYW5kIFwic3VidHJhY3RcIiB0YWtlIHR3byBhcmd1bWVudHMgZWFjaFxuICAgICAgICAgKiAgICAgcnMucGFyYWxsZWwoWyB7IG5hbWU6ICdhZGQnLCBwYXJhbXM6IFsxLDJdIH0sXG4gICAgICAgICAqICAgICAgICAgICAgICAgICAgIHsgbmFtZTogJ3N1YnRyYWN0JywgcGFyYW1zOlsyLDNdIH1dKTtcbiAgICAgICAgICogICAgICAvLyBtZXRob2RzIFwiYWRkXCIgYW5kIFwic3VidHJhY3RcIiB0YWtlIHR3byBhcmd1bWVudHMgZWFjaFxuICAgICAgICAgKiAgICAgcnMucGFyYWxsZWwoeyBhZGQ6IFsxLDJdLCBzdWJ0cmFjdDogWzIsNF0gfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7QXJyYXl8T2JqZWN0fSBvcGVyYXRpb25zIElmIG5vbmUgb2YgdGhlIG1ldGhvZHMgdGFrZSBwYXJhbWV0ZXJzLCBwYXNzIGFuIGFycmF5IG9mIHRoZSBtZXRob2QgbmFtZXMgKGFzIHN0cmluZ3MpLiBJZiBhbnkgb2YgdGhlIG1ldGhvZHMgZG8gdGFrZSBwYXJhbWV0ZXJzLCB5b3UgaGF2ZSB0d28gb3B0aW9ucy4gWW91IGNhbiBwYXNzIGFuIGFycmF5IG9mIG9iamVjdHMsIGVhY2ggb2Ygd2hpY2ggY29udGFpbnMgYSBtZXRob2QgbmFtZSBhbmQgaXRzIG93biAocG9zc2libHkgZW1wdHkpIGFycmF5IG9mIHBhcmFtZXRlcnMuIEFsdGVybmF0aXZlbHksIHlvdSBjYW4gcGFzcyBhIHNpbmdsZSBvYmplY3Qgd2l0aCB0aGUgbWV0aG9kIG5hbWUgYW5kIGEgKHBvc3NpYmx5IGVtcHR5KSBhcnJheSBvZiBwYXJhbWV0ZXJzLlxuICAgICAgICAgKiBAcGFyYW0geyp9IHBhcmFtcyBQYXJhbWV0ZXJzIHRvIHBhc3MgdG8gb3BlcmF0aW9ucy5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgIHBhcmFsbGVsOiBmdW5jdGlvbiAob3BlcmF0aW9ucywgcGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgJGQgPSAkLkRlZmVycmVkKCk7XG5cbiAgICAgICAgICAgIHZhciBvcFBhcmFtcyA9IHJ1dGlsLm5vcm1hbGl6ZU9wZXJhdGlvbnMob3BlcmF0aW9ucywgcGFyYW1zKTtcbiAgICAgICAgICAgIHZhciBvcHMgPSBvcFBhcmFtcy5vcHM7XG4gICAgICAgICAgICB2YXIgYXJncyA9IG9wUGFyYW1zLmFyZ3M7XG4gICAgICAgICAgICB2YXIgcG9zdE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgcXVldWUgPSBbXTtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgb3BzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgcXVldWUucHVzaChcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kbyhvcHNbaV0sIGFyZ3NbaV0pXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgICQud2hlbi5hcHBseSh0aGlzLCBxdWV1ZSlcbiAgICAgICAgICAgICAgICAudGhlbihmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICRkLnJlc29sdmUuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgcG9zdE9wdGlvbnMuc3VjY2Vzcy5hcHBseSh0aGlzLmFyZ3VtZW50cyk7XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAuZmFpbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICRkLnJlamVjdC5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgICAgICAgICBwb3N0T3B0aW9ucy5lcnJvci5hcHBseSh0aGlzLmFyZ3VtZW50cyk7XG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHJldHVybiAkZC5wcm9taXNlKCk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNob3J0Y3V0IHRvIHVzaW5nIHRoZSBbSW50cm9zcGVjdGlvbiBBUEkgU2VydmljZV0oLi4vaW50cm9zcGVjdGlvbi1hcGktc2VydmljZS8pLiBBbGxvd3MgeW91IHRvIHZpZXcgYSBsaXN0IG9mIHRoZSB2YXJpYWJsZXMgYW5kIG9wZXJhdGlvbnMgaW4gYSBtb2RlbC5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgIHJzLmludHJvc3BlY3QoeyBydW5JRDogJ2NiZjg1NDM3LWI1MzktNDk3Ny1hMWZjLTIzNTE1Y2YwNzFiYicgfSkudGhlbihmdW5jdGlvbiAoZGF0YSkge1xuICAgICAgICAgKiAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhLmZ1bmN0aW9ucyk7XG4gICAgICAgICAqICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEudmFyaWFibGVzKTtcbiAgICAgICAgICogICAgIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0gIHtPYmplY3R9IG9wdGlvbnMgT3B0aW9ucyBjYW4gZWl0aGVyIGJlIG9mIHRoZSBmb3JtIGB7IHJ1bklEOiA8cnVuaWQ+IH1gIG9yIGB7IG1vZGVsOiA8bW9kZWxGaWxlTmFtZT4gfWAuXG4gICAgICAgICAqIEBwYXJhbSAge09iamVjdH0gaW50cm9zcGVjdGlvbkNvbmZpZyAoT3B0aW9uYWwpIFNlcnZpY2Ugb3B0aW9ucyBmb3IgSW50cm9zcGVjdGlvbiBTZXJ2aWNlXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBpbnRyb3NwZWN0OiBmdW5jdGlvbiAob3B0aW9ucywgaW50cm9zcGVjdGlvbkNvbmZpZykge1xuICAgICAgICAgICAgdmFyIGludHJvc3BlY3Rpb24gPSBuZXcgSW50cm9zcGVjdGlvblNlcnZpY2UoJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBpbnRyb3NwZWN0aW9uQ29uZmlnKSk7XG4gICAgICAgICAgICBpZiAob3B0aW9ucykge1xuICAgICAgICAgICAgICAgIGlmIChvcHRpb25zLnJ1bklEKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBpbnRyb3NwZWN0aW9uLmJ5UnVuSUQob3B0aW9ucy5ydW5JRCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChvcHRpb25zLm1vZGVsKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBpbnRyb3NwZWN0aW9uLmJ5TW9kZWwob3B0aW9ucy5tb2RlbCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChzZXJ2aWNlT3B0aW9ucy5pZCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBpbnRyb3NwZWN0aW9uLmJ5UnVuSUQoc2VydmljZU9wdGlvbnMuaWQpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsZWFzZSBzcGVjaWZ5IGVpdGhlciB0aGUgbW9kZWwgb3IgcnVuaWQgdG8gaW50cm9zcGVjdCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIHZhciBwdWJsaWNTeW5jQVBJID0ge1xuICAgICAgICBnZXRDdXJyZW50Q29uZmlnOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gc2VydmljZU9wdGlvbnM7XG4gICAgICAgIH0sXG4gICAgICAgIC8qKlxuICAgICAgICAgICogUmV0dXJucyBhIFZhcmlhYmxlcyBTZXJ2aWNlIGluc3RhbmNlLiBVc2UgdGhlIHZhcmlhYmxlcyBpbnN0YW5jZSB0byBsb2FkLCBzYXZlLCBhbmQgcXVlcnkgZm9yIHNwZWNpZmljIG1vZGVsIHZhcmlhYmxlcy4gU2VlIHRoZSBbVmFyaWFibGUgQVBJIFNlcnZpY2VdKC4uL3ZhcmlhYmxlcy1hcGktc2VydmljZS8pIGZvciBtb3JlIGluZm9ybWF0aW9uLlxuICAgICAgICAgICpcbiAgICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAgKlxuICAgICAgICAgICogICAgICB2YXIgdnMgPSBycy52YXJpYWJsZXMoKTtcbiAgICAgICAgICAqICAgICAgdnMuc2F2ZSh7IHNhbXBsZV9pbnQ6IDQgfSk7XG4gICAgICAgICAgKlxuICAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICAqIEByZXR1cm4ge09iamVjdH0gdmFyaWFibGVzU2VydmljZSBJbnN0YW5jZVxuICAgICAgICAgICovXG4gICAgICAgIHZhcmlhYmxlczogZnVuY3Rpb24gKGNvbmZpZykge1xuICAgICAgICAgICAgdmFyIHZzID0gbmV3IFZhcmlhYmxlc1NlcnZpY2UoJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBjb25maWcsIHtcbiAgICAgICAgICAgICAgICBydW5TZXJ2aWNlOiB0aGlzXG4gICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICByZXR1cm4gdnM7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgJC5leHRlbmQodGhpcywgcHVibGljQXN5bmNBUEkpO1xuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY1N5bmNBUEkpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG52YXIgb2JqZWN0QXNzaWduID0gcmVxdWlyZSgnb2JqZWN0LWFzc2lnbicpO1xuXG52YXIgc2VydmljZVV0aWxzID0ge1xuICAgIC8qXG4gICAgKiBHZXRzIHRoZSBkZWZhdWx0IG9wdGlvbnMgZm9yIGEgYXBpIHNlcnZpY2UuXG4gICAgKiBJdCB3aWxsIG1lcmdlOlxuICAgICogLSBUaGUgU2Vzc2lvbiBvcHRpb25zIChVc2luZyB0aGUgU2Vzc2lvbiBNYW5hZ2VyKVxuICAgICogLSBUaGUgQXV0aG9yaXphdGlvbiBIZWFkZXIgZnJvbSB0aGUgdG9rZW4gb3B0aW9uXG4gICAgKiAtIFRoZSBmdWxsIHVybCBmcm9tIHRoZSBlbmRwb2ludCBvcHRpb25cbiAgICAqIFdpdGggdGhlIHN1cHBsaWVkIG92ZXJyaWRlcyBhbmQgZGVmYXVsdHNcbiAgICAqXG4gICAgKi9cbiAgICBnZXREZWZhdWx0T3B0aW9uczogZnVuY3Rpb24gKGRlZmF1bHRzKSB7XG4gICAgICAgIHZhciByZXN0ID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKTtcbiAgICAgICAgdmFyIHNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKCk7XG4gICAgICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9IHNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMuYXBwbHkoc2Vzc2lvbk1hbmFnZXIsIFtkZWZhdWx0c10uY29uY2F0KHJlc3QpKTtcblxuICAgICAgICBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQgPSBvYmplY3RBc3NpZ24oe30sIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydCwge1xuICAgICAgICAgICAgdXJsOiB0aGlzLmdldEFwaVVybChzZXJ2aWNlT3B0aW9ucy5hcGlFbmRwb2ludCwgc2VydmljZU9wdGlvbnMpXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICAgICAgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LmhlYWRlcnMgPSB7XG4gICAgICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogJ0JlYXJlciAnICsgc2VydmljZU9wdGlvbnMudG9rZW5cbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHNlcnZpY2VPcHRpb25zO1xuICAgIH0sXG5cbiAgICBnZXRBcGlVcmw6IGZ1bmN0aW9uIChhcGlFbmRwb2ludCwgc2VydmljZU9wdGlvbnMpIHtcbiAgICAgICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuICAgICAgICByZXR1cm4gdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gc2VydmljZVV0aWxzOyIsIid1c2Ugc3RyaWN0Jztcbi8qKlxuICogIyMgU3RhdGUgQVBJIEFkYXB0ZXJcbiAqXG4gKiBUaGUgU3RhdGUgQVBJIEFkYXB0ZXIgYWxsb3dzIHlvdSB0byByZXBsYXkgb3IgY2xvbmUgcnVucy4gSXQgYnJpbmdzIGV4aXN0aW5nLCBwZXJzaXN0ZWQgcnVuIGRhdGEgZnJvbSB0aGUgZGF0YWJhc2UgYmFjayBpbnRvIG1lbW9yeSwgdXNpbmcgdGhlIHNhbWUgcnVuIGlkIChgcmVwbGF5YCkgb3IgYSBuZXcgcnVuIGlkIChgY2xvbmVgKS4gUnVucyBtdXN0IGJlIGluIG1lbW9yeSBpbiBvcmRlciBmb3IgeW91IHRvIHVwZGF0ZSB2YXJpYWJsZXMgb3IgY2FsbCBvcGVyYXRpb25zIG9uIHRoZW0uXG4gKlxuICogU3BlY2lmaWNhbGx5LCB0aGUgU3RhdGUgQVBJIEFkYXB0ZXIgd29ya3MgYnkgXCJyZS1ydW5uaW5nXCIgdGhlIHJ1biAodXNlciBpbnRlcmFjdGlvbnMpIGZyb20gdGhlIGNyZWF0aW9uIG9mIHRoZSBydW4gdXAgdG8gdGhlIHRpbWUgaXQgd2FzIGxhc3QgcGVyc2lzdGVkIGluIHRoZSBkYXRhYmFzZS4gVGhpcyBwcm9jZXNzIHVzZXMgdGhlIGN1cnJlbnQgdmVyc2lvbiBvZiB0aGUgcnVuJ3MgbW9kZWwuIFRoZXJlZm9yZSwgaWYgdGhlIG1vZGVsIGhhcyBjaGFuZ2VkIHNpbmNlIHRoZSBvcmlnaW5hbCBydW4gd2FzIGNyZWF0ZWQsIHRoZSByZXRyaWV2ZWQgcnVuIHdpbGwgdXNlIHRoZSBuZXcgbW9kZWwg4oCUIGFuZCBtYXkgZW5kIHVwIGhhdmluZyBkaWZmZXJlbnQgdmFsdWVzIG9yIGJlaGF2aW9yIGFzIGEgcmVzdWx0LiBVc2Ugd2l0aCBjYXJlIVxuICpcbiAqIFRvIHVzZSB0aGUgU3RhdGUgQVBJIEFkYXB0ZXIsIGluc3RhbnRpYXRlIGl0IGFuZCB0aGVuIGNhbGwgaXRzIG1ldGhvZHM6XG4gKlxuICogICAgICB2YXIgc2EgPSBuZXcgRi5zZXJ2aWNlLlN0YXRlKCk7XG4gKiAgICAgIHNhLnJlcGxheSh7cnVuSWQ6ICcxODQyYmI1Yy04M2FkLTRiYTgtYTk1NS1iZDEzY2MyZmRiNGYnfSk7XG4gKlxuICogVGhlIGNvbnN0cnVjdG9yIHRha2VzIGFuIG9wdGlvbmFsIGBvcHRpb25zYCBwYXJhbWV0ZXIgaW4gd2hpY2ggeW91IGNhbiBzcGVjaWZ5IHRoZSBgYWNjb3VudGAgYW5kIGBwcm9qZWN0YCBpZiB0aGV5IGFyZSBub3QgYWxyZWFkeSBhdmFpbGFibGUgaW4gdGhlIGN1cnJlbnQgY29udGV4dC5cbiAqXG4gKi9cblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIF9waWNrID0gcmVxdWlyZSgnLi4vdXRpbC9vYmplY3QtdXRpbCcpLl9waWNrO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG52YXIgYXBpRW5kcG9pbnQgPSAnbW9kZWwvc3RhdGUnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcblxuICAgIHZhciBkZWZhdWx0cyA9IHtcblxuICAgIH07XG5cbiAgICB0aGlzLnNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKCk7XG4gICAgdmFyIHNlcnZpY2VPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKGRlZmF1bHRzLCBjb25maWcpO1xuICAgIHZhciB1cmxDb25maWcgPSBuZXcgQ29uZmlnU2VydmljZShzZXJ2aWNlT3B0aW9ucykuZ2V0KCdzZXJ2ZXInKTtcblxuICAgIHZhciB0cmFuc3BvcnRPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydCwge1xuICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KVxuICAgIH0pO1xuXG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnRva2VuKSB7XG4gICAgICAgIHRyYW5zcG9ydE9wdGlvbnMuaGVhZGVycyA9IHtcbiAgICAgICAgICAgIEF1dGhvcml6YXRpb246ICdCZWFyZXIgJyArIHNlcnZpY2VPcHRpb25zLnRva2VuXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgdmFyIGh0dHAgPSBuZXcgVHJhbnNwb3J0RmFjdG9yeSh0cmFuc3BvcnRPcHRpb25zKTtcbiAgICB2YXIgcGFyc2VSdW5JZE9yRXJyb3IgPSBmdW5jdGlvbiAocGFyYW1zKSB7XG4gICAgICAgIGlmICgkLmlzUGxhaW5PYmplY3QocGFyYW1zKSAmJiBwYXJhbXMucnVuSWQpIHtcbiAgICAgICAgICAgIHJldHVybiBwYXJhbXMucnVuSWQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsZWFzZSBwYXNzIGluIGEgcnVuIGlkJyk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgdmFyIHB1YmxpY0FQSSA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICogUmVwbGF5IGEgcnVuLiBBZnRlciB0aGlzIGNhbGwsIHRoZSBydW4sIHdpdGggaXRzIG9yaWdpbmFsIHJ1biBpZCwgaXMgbm93IGF2YWlsYWJsZSBbaW4gbWVtb3J5XSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvI3J1bnMtaW4tbWVtb3J5KS4gKEl0IGNvbnRpbnVlcyB0byBiZSBwZXJzaXN0ZWQgaW50byB0aGUgRXBpY2VudGVyIGRhdGFiYXNlIGF0IHJlZ3VsYXIgaW50ZXJ2YWxzLilcbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgc2EgPSBuZXcgRi5zZXJ2aWNlLlN0YXRlKCk7XG4gICAgICAgICogICAgICBzYS5yZXBsYXkoe3J1bklkOiAnMTg0MmJiNWMtODNhZC00YmE4LWE5NTUtYmQxM2NjMmZkYjRmJywgc3RvcEJlZm9yZTogJ2NhbGN1bGF0ZVNjb3JlJ30pO1xuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IHBhcmFtcyBQYXJhbWV0ZXJzIG9iamVjdC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnJ1bklkIFRoZSBpZCBvZiB0aGUgcnVuIHRvIGJyaW5nIGJhY2sgdG8gbWVtb3J5LlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuc3RvcEJlZm9yZSAoT3B0aW9uYWwpIFRoZSBydW4gaXMgYWR2YW5jZWQgb25seSB1cCB0byB0aGUgZmlyc3Qgb2NjdXJyZW5jZSBvZiB0aGlzIG1ldGhvZC5cbiAgICAgICAgKiBAcGFyYW0ge2FycmF5fSBwYXJhbXMuZXhjbHVkZSAoT3B0aW9uYWwpIEFycmF5IG9mIG1ldGhvZHMgdG8gZXhjbHVkZSB3aGVuIGFkdmFuY2luZyB0aGUgcnVuLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgcmVwbGF5OiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcnVuSWQgPSBwYXJzZVJ1bklkT3JFcnJvcihwYXJhbXMpO1xuXG4gICAgICAgICAgICB2YXIgcmVwbGF5T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHJ1bklkIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHBhcmFtcyA9ICQuZXh0ZW5kKHRydWUsIHsgYWN0aW9uOiAncmVwbGF5JyB9LCBfcGljayhwYXJhbXMsIFsnc3RvcEJlZm9yZScsICdleGNsdWRlJ10pKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucG9zdChwYXJhbXMsIHJlcGxheU9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIENsb25lIGEgZ2l2ZW4gcnVuIGFuZCByZXR1cm4gYSBuZXcgcnVuIGluIHRoZSBzYW1lIHN0YXRlIGFzIHRoZSBnaXZlbiBydW4uXG4gICAgICAgICpcbiAgICAgICAgKiBUaGUgbmV3IHJ1biBpZCBpcyBub3cgYXZhaWxhYmxlIFtpbiBtZW1vcnldKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZS8jcnVucy1pbi1tZW1vcnkpLiBUaGUgbmV3IHJ1biBpbmNsdWRlcyBhIGNvcHkgb2YgYWxsIG9mIHRoZSBkYXRhIGZyb20gdGhlIG9yaWdpbmFsIHJ1biwgRVhDRVBUOlxuICAgICAgICAqXG4gICAgICAgICogKiBUaGUgYHNhdmVkYCBmaWVsZCBpbiB0aGUgbmV3IHJ1biByZWNvcmQgaXMgbm90IGNvcGllZCBmcm9tIHRoZSBvcmlnaW5hbCBydW4gcmVjb3JkLiBJdCBkZWZhdWx0cyB0byBgZmFsc2VgLlxuICAgICAgICAqICogVGhlIGBpbml0aWFsaXplZGAgZmllbGQgaW4gdGhlIG5ldyBydW4gcmVjb3JkIGlzIG5vdCBjb3BpZWQgZnJvbSB0aGUgb3JpZ2luYWwgcnVuIHJlY29yZC4gSXQgZGVmYXVsdHMgdG8gYGZhbHNlYCBidXQgbWF5IGNoYW5nZSB0byBgdHJ1ZWAgYXMgdGhlIG5ldyBydW4gaXMgYWR2YW5jZWQuIEZvciBleGFtcGxlLCBpZiB0aGVyZSBoYXMgYmVlbiBhIGNhbGwgdG8gdGhlIGBzdGVwYCBmdW5jdGlvbiAoZm9yIFZlbnNpbSBtb2RlbHMpLCB0aGUgYGluaXRpYWxpemVkYCBmaWVsZCBpcyBzZXQgdG8gYHRydWVgLlxuICAgICAgICAqICogVGhlIGBjcmVhdGVkYCBmaWVsZCBpbiB0aGUgbmV3IHJ1biByZWNvcmQgaXMgdGhlIGRhdGUgYW5kIHRpbWUgYXQgd2hpY2ggdGhlIGNsb25lIHdhcyBjcmVhdGVkIChub3QgdGhlIHRpbWUgdGhhdCB0aGUgb3JpZ2luYWwgcnVuIHdhcyBjcmVhdGVkLilcbiAgICAgICAgKlxuICAgICAgICAqIFRoZSBvcmlnaW5hbCBydW4gcmVtYWlucyBvbmx5IFtpbiB0aGUgZGF0YWJhc2VdKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZS8jcnVucy1pbi1kYikuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHNhID0gbmV3IEYuc2VydmljZS5TdGF0ZSgpO1xuICAgICAgICAqICAgICAgc2EuY2xvbmUoe3J1bklkOiAnMTg0MmJiNWMtODNhZC00YmE4LWE5NTUtYmQxM2NjMmZkYjRmJywgc3RvcEJlZm9yZTogJ2NhbGN1bGF0ZVNjb3JlJywgZXhjbHVkZTogWydpbnRlcmltQ2FsY3VsYXRpb24nXSB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgUGFyYW1ldGVycyBvYmplY3QuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5ydW5JZCBUaGUgaWQgb2YgdGhlIHJ1biB0byBjbG9uZSBmcm9tIG1lbW9yeS5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnN0b3BCZWZvcmUgKE9wdGlvbmFsKSBUaGUgbmV3bHkgY2xvbmVkIHJ1biBpcyBhZHZhbmNlZCBvbmx5IHVwIHRvIHRoZSBmaXJzdCBvY2N1cnJlbmNlIG9mIHRoaXMgbWV0aG9kLlxuICAgICAgICAqIEBwYXJhbSB7YXJyYXl9IHBhcmFtcy5leGNsdWRlIChPcHRpb25hbCkgQXJyYXkgb2YgbWV0aG9kcyB0byBleGNsdWRlIHdoZW4gYWR2YW5jaW5nIHRoZSBuZXdseSBjbG9uZWQgcnVuLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgY2xvbmU6IGZ1bmN0aW9uIChwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBydW5JZCA9IHBhcnNlUnVuSWRPckVycm9yKHBhcmFtcyk7XG5cbiAgICAgICAgICAgIHZhciByZXBsYXlPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sXG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMsXG4gICAgICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgICAgICB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpICsgcnVuSWQgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcGFyYW1zID0gJC5leHRlbmQodHJ1ZSwgeyBhY3Rpb246ICdjbG9uZScgfSwgX3BpY2socGFyYW1zLCBbJ3N0b3BCZWZvcmUnLCAnZXhjbHVkZSddKSk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLnBvc3QocGFyYW1zLCByZXBsYXlPcHRpb25zKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGVwaVZlcnNpb24gPSByZXF1aXJlKCcuLi9hcGktdmVyc2lvbi5qc29uJyk7XG5cbi8vVE9ETzogdXJsdXRpbHMgdG8gZ2V0IGhvc3QsIHNpbmNlIG5vIHdpbmRvdyBvbiBub2RlXG52YXIgZGVmYXVsdHMgPSB7XG4gICAgaG9zdDogd2luZG93LmxvY2F0aW9uLmhvc3QsXG4gICAgcGF0aG5hbWU6IHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZVxufTtcblxuZnVuY3Rpb24gZ2V0TG9jYWxIb3N0KGV4aXN0aW5nRm4sIGhvc3QpIHtcbiAgICB2YXIgbG9jYWxIb3N0Rm47XG4gICAgaWYgKGV4aXN0aW5nRm4gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAoISQuaXNGdW5jdGlvbihleGlzdGluZ0ZuKSkge1xuICAgICAgICAgICAgbG9jYWxIb3N0Rm4gPSBmdW5jdGlvbiAoKSB7IHJldHVybiBleGlzdGluZ0ZuOyB9O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbG9jYWxIb3N0Rm4gPSBleGlzdGluZ0ZuO1xuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgbG9jYWxIb3N0Rm4gPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgaXNMb2NhbCA9ICFob3N0IHx8IC8vcGhhbnRvbWpzXG4gICAgICAgICAgICAgICAgaG9zdCA9PT0gJzEyNy4wLjAuMScgfHwgXG4gICAgICAgICAgICAgICAgaG9zdC5pbmRleE9mKCdsb2NhbC4nKSA9PT0gMCB8fCBcbiAgICAgICAgICAgICAgICBob3N0LmluZGV4T2YoJ2xvY2FsaG9zdCcpID09PSAwO1xuICAgICAgICAgICAgcmV0dXJuIGlzTG9jYWw7XG4gICAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiBsb2NhbEhvc3RGbjtcbn1cblxudmFyIFVybENvbmZpZ1NlcnZpY2UgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdmFyIGVudkNvbmYgPSBVcmxDb25maWdTZXJ2aWNlLmRlZmF1bHRzO1xuXG4gICAgaWYgKCFjb25maWcpIHtcbiAgICAgICAgY29uZmlnID0ge307XG4gICAgfVxuICAgIC8vIGNvbnNvbGUubG9nKHRoaXMuZGVmYXVsdHMpO1xuICAgIHZhciBvdmVycmlkZXMgPSAkLmV4dGVuZCh7fSwgZW52Q29uZiwgY29uZmlnKTtcbiAgICB2YXIgb3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBkZWZhdWx0cywgb3ZlcnJpZGVzKTtcblxuICAgIG92ZXJyaWRlcy5pc0xvY2FsaG9zdCA9IG9wdGlvbnMuaXNMb2NhbGhvc3QgPSBnZXRMb2NhbEhvc3Qob3B0aW9ucy5pc0xvY2FsaG9zdCwgb3B0aW9ucy5ob3N0KTtcbiAgICBcbiAgICAvLyBjb25zb2xlLmxvZyhpc0xvY2FsaG9zdCgpLCAnX19fX19fX19fX18nKTtcbiAgICB2YXIgYWN0aW5nSG9zdCA9IGNvbmZpZyAmJiBjb25maWcuaG9zdDtcbiAgICBpZiAoIWFjdGluZ0hvc3QgJiYgb3B0aW9ucy5pc0xvY2FsaG9zdCgpKSB7XG4gICAgICAgIGFjdGluZ0hvc3QgPSAnZm9yaW8uY29tJztcbiAgICB9IGVsc2Uge1xuICAgICAgICBhY3RpbmdIb3N0ID0gb3B0aW9ucy5ob3N0O1xuICAgIH1cblxuICAgIHZhciBBUElfUFJPVE9DT0wgPSAnaHR0cHMnO1xuICAgIHZhciBIT1NUX0FQSV9NQVBQSU5HID0ge1xuICAgICAgICAnZm9yaW8uY29tJzogJ2FwaS5mb3Jpby5jb20nLFxuICAgICAgICAnZm9yaW9kZXYuY29tJzogJ2FwaS5lcGljZW50ZXIuZm9yaW9kZXYuY29tJ1xuICAgIH07XG5cbiAgICB2YXIgcHVibGljRXhwb3J0cyA9IHtcbiAgICAgICAgcHJvdG9jb2w6IEFQSV9QUk9UT0NPTCxcblxuICAgICAgICBhcGk6ICcnLFxuXG4gICAgICAgIC8vVE9ETzogdGhpcyBzaG91bGQgcmVhbGx5IGJlIGNhbGxlZCAnYXBpaG9zdCcsIGJ1dCBjYW4ndCBiZWNhdXNlIHRoYXQgd291bGQgYnJlYWsgdG9vIG1hbnkgdGhpbmdzXG4gICAgICAgIGhvc3Q6IChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgYXBpSG9zdCA9IChIT1NUX0FQSV9NQVBQSU5HW2FjdGluZ0hvc3RdKSA/IEhPU1RfQVBJX01BUFBJTkdbYWN0aW5nSG9zdF0gOiBhY3RpbmdIb3N0O1xuICAgICAgICAgICAgLy8gY29uc29sZS5sb2coYWN0aW5nSG9zdCwgY29uZmlnLCBhcGlIb3N0KTtcbiAgICAgICAgICAgIHJldHVybiBhcGlIb3N0O1xuICAgICAgICB9KCkpLFxuXG4gICAgICAgIGlzQ3VzdG9tRG9tYWluOiAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHBhdGggPSBvcHRpb25zLnBhdGhuYW1lLnNwbGl0KCdcXC8nKTtcbiAgICAgICAgICAgIHZhciBwYXRoSGFzQXBwID0gcGF0aCAmJiBwYXRoWzFdID09PSAnYXBwJztcbiAgICAgICAgICAgIHJldHVybiAoIW9wdGlvbnMuaXNMb2NhbGhvc3QoKSAmJiAhcGF0aEhhc0FwcCk7XG4gICAgICAgIH0oKSksXG5cbiAgICAgICAgYXBwUGF0aDogKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBwYXRoID0gb3B0aW9ucy5wYXRobmFtZS5zcGxpdCgnXFwvJyk7XG5cbiAgICAgICAgICAgIHJldHVybiBwYXRoICYmIHBhdGhbMV0gfHwgJyc7XG4gICAgICAgIH0oKSksXG5cbiAgICAgICAgYWNjb3VudFBhdGg6IChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgYWNjbnQgPSAnJztcbiAgICAgICAgICAgIHZhciBwYXRoID0gb3B0aW9ucy5wYXRobmFtZS5zcGxpdCgnXFwvJyk7XG4gICAgICAgICAgICBpZiAocGF0aCAmJiBwYXRoWzFdID09PSAnYXBwJykge1xuICAgICAgICAgICAgICAgIGFjY250ID0gcGF0aFsyXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBhY2NudDtcbiAgICAgICAgfSgpKSxcblxuICAgICAgICBwcm9qZWN0UGF0aDogKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBwcmogPSAnJztcbiAgICAgICAgICAgIHZhciBwYXRoID0gb3B0aW9ucy5wYXRobmFtZS5zcGxpdCgnXFwvJyk7XG4gICAgICAgICAgICBpZiAocGF0aCAmJiBwYXRoWzFdID09PSAnYXBwJykge1xuICAgICAgICAgICAgICAgIHByaiA9IHBhdGhbM107IC8vZXNsaW50LWRpc2FibGUtbGluZSBuby1tYWdpYy1udW1iZXJzXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gcHJqO1xuICAgICAgICB9KCkpLFxuXG4gICAgICAgIHZlcnNpb25QYXRoOiAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHZlcnNpb24gPSBlcGlWZXJzaW9uLnZlcnNpb24gPyBlcGlWZXJzaW9uLnZlcnNpb24gKyAnLycgOiAnJztcbiAgICAgICAgICAgIHJldHVybiB2ZXJzaW9uO1xuICAgICAgICB9KCkpLFxuXG4gICAgICAgIGdldEFQSVBhdGg6IGZ1bmN0aW9uIChhcGkpIHtcbiAgICAgICAgICAgIHZhciBQUk9KRUNUX0FQSVMgPSBbJ3J1bicsICdkYXRhJywgJ2ZpbGUnXTtcblxuICAgICAgICAgICAgaWYgKGFwaSA9PT0gJ2NvbmZpZycpIHtcbiAgICAgICAgICAgICAgICB2YXIgYWN0dWFsUHJvdG9jb2wgPSB3aW5kb3cubG9jYXRpb24ucHJvdG9jb2wucmVwbGFjZSgnOicsICcnKTtcbiAgICAgICAgICAgICAgICB2YXIgY29uZmlnUHJvdG9jb2wgPSAob3B0aW9ucy5pc0xvY2FsaG9zdCgpKSA/IHRoaXMucHJvdG9jb2wgOiBhY3R1YWxQcm90b2NvbDtcbiAgICAgICAgICAgICAgICByZXR1cm4gY29uZmlnUHJvdG9jb2wgKyAnOi8vJyArIGFjdGluZ0hvc3QgKyAnL2VwaWNlbnRlci8nICsgdGhpcy52ZXJzaW9uUGF0aCArICdjb25maWcnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIGFwaVBhdGggPSB0aGlzLnByb3RvY29sICsgJzovLycgKyB0aGlzLmhvc3QgKyAnLycgKyB0aGlzLnZlcnNpb25QYXRoICsgYXBpICsgJy8nO1xuXG4gICAgICAgICAgICBpZiAoJC5pbkFycmF5KGFwaSwgUFJPSkVDVF9BUElTKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICBhcGlQYXRoICs9IHRoaXMuYWNjb3VudFBhdGggKyAnLycgKyB0aGlzLnByb2plY3RQYXRoICsgJy8nO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGFwaVBhdGg7XG4gICAgICAgIH1cbiAgICB9O1xuXG5cbiAgICAkLmV4dGVuZChwdWJsaWNFeHBvcnRzLCBvdmVycmlkZXMpO1xuICAgIHJldHVybiBwdWJsaWNFeHBvcnRzO1xufTtcbi8vIFRoaXMgZGF0YSBjYW4gYmUgc2V0IGJ5IGV4dGVybmFsIHNjcmlwdHMsIGZvciBsb2FkaW5nIGZyb20gYW4gZW52IHNlcnZlciBmb3IgZWc7XG5VcmxDb25maWdTZXJ2aWNlLmRlZmF1bHRzID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gVXJsQ29uZmlnU2VydmljZTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8qKlxuKiAjIyBVc2VyIEFQSSBBZGFwdGVyXG4qXG4qIFRoZSBVc2VyIEFQSSBBZGFwdGVyIGFsbG93cyB5b3UgdG8gcmV0cmlldmUgZGV0YWlscyBhYm91dCBlbmQgdXNlcnMgaW4geW91ciB0ZWFtIChhY2NvdW50KS4gSXQgaXMgYmFzZWQgb24gdGhlIHF1ZXJ5aW5nIGNhcGFiaWxpdGllcyBvZiB0aGUgdW5kZXJseWluZyBSRVNUZnVsIFtVc2VyIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL3VzZXJfbWFuYWdlbWVudC91c2VyLykuXG4qXG4qIFRvIHVzZSB0aGUgVXNlciBBUEkgQWRhcHRlciwgaW5zdGFudGlhdGUgaXQgYW5kIHRoZW4gY2FsbCBpdHMgbWV0aG9kcy5cbipcbiogICAgICAgdmFyIHVhID0gbmV3IEYuc2VydmljZS5Vc2VyKHtcbiogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiogICAgICAgICAgIHRva2VuOiAndXNlci1vci1wcm9qZWN0LWFjY2Vzcy10b2tlbidcbiogICAgICAgfSk7XG4qICAgICAgIHVhLmdldEJ5SWQoJzQyODM2ZDRiLTViNjEtNGZlNC04MGViLTMxMzZlOTU2ZWU1YycpO1xuKiAgICAgICB1YS5nZXQoeyB1c2VyTmFtZTogJ2pzbWl0aCcgfSk7XG4qICAgICAgIHVhLmdldCh7IGlkOiBbJzQyODM2ZDRiLTViNjEtNGZlNC04MGViLTMxMzZlOTU2ZWU1YycsXG4qICAgICAgICAgICAgICAgICAgICc0ZWE3NTYzMS00YzhkLTQ4NzItOWQ4MC1iNDYwMDE0NjQ3OGUnXSB9KTtcbipcbiogVGhlIGNvbnN0cnVjdG9yIHRha2VzIGFuIG9wdGlvbmFsIG9wdGlvbnMgcGFyYW1ldGVyIGluIHdoaWNoIHlvdSBjYW4gc3BlY2lmeSB0aGUgYGFjY291bnRgIGFuZCBgdG9rZW5gIGlmIHRoZXkgYXJlIG5vdCBhbHJlYWR5IGF2YWlsYWJsZSBpbiB0aGUgY3VycmVudCBjb250ZXh0LlxuKi9cblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG52YXIgcXV0aWwgPSByZXF1aXJlKCcuLi91dGlsL3F1ZXJ5LXV0aWwnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdmFyIGRlZmF1bHRzID0ge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgYWNjb3VudCBpZC4gSW4gdGhlIEVwaWNlbnRlciBVSSwgdGhpcyBpcyB0aGUgKipUZWFtIElEKiogKGZvciB0ZWFtIHByb2plY3RzKSBvciAqKlVzZXIgSUQqKiAoZm9yIHBlcnNvbmFsIHByb2plY3RzKS4gRGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgYWNjb3VudDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgYWNjZXNzIHRva2VuIHRvIHVzZSB3aGVuIHNlYXJjaGluZyBmb3IgZW5kIHVzZXJzLiAoU2VlIFttb3JlIGJhY2tncm91bmQgb24gYWNjZXNzIHRva2Vuc10oLi4vLi4vLi4vcHJvamVjdF9hY2Nlc3MvKSkuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICB0b2tlbjogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBPcHRpb25zIHRvIHBhc3Mgb24gdG8gdGhlIHVuZGVybHlpbmcgdHJhbnNwb3J0IGxheWVyLiBBbGwganF1ZXJ5LmFqYXggb3B0aW9ucyBhdCBodHRwOi8vYXBpLmpxdWVyeS5jb20valF1ZXJ5LmFqYXgvIGFyZSBhdmFpbGFibGUuIERlZmF1bHRzIHRvIGVtcHR5IG9iamVjdC5cbiAgICAgICAgICogQHR5cGUge09iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIHRyYW5zcG9ydDoge31cbiAgICB9O1xuXG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgY29uZmlnKTtcbiAgICB2YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2Uoc2VydmljZU9wdGlvbnMpLmdldCgnc2VydmVyJyk7XG4gICAgdmFyIHRyYW5zcG9ydE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LCB7XG4gICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoJ3VzZXInKVxuICAgIH0pO1xuXG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLnRva2VuKSB7XG4gICAgICAgIHRyYW5zcG9ydE9wdGlvbnMuaGVhZGVycyA9IHtcbiAgICAgICAgICAgIEF1dGhvcml6YXRpb246ICdCZWFyZXIgJyArIHNlcnZpY2VPcHRpb25zLnRva2VuXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgdmFyIGh0dHAgPSBuZXcgVHJhbnNwb3J0RmFjdG9yeSh0cmFuc3BvcnRPcHRpb25zKTtcblxuICAgIHZhciBwdWJsaWNBUEkgPSB7XG5cbiAgICAgICAgLyoqXG4gICAgICAgICogUmV0cmlldmUgZGV0YWlscyBhYm91dCBwYXJ0aWN1bGFyIGVuZCB1c2VycyBpbiB5b3VyIHRlYW0sIGJhc2VkIG9uIHVzZXIgbmFtZSBvciB1c2VyIGlkLlxuICAgICAgICAqXG4gICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgIHZhciB1YSA9IG5ldyBGLnNlcnZpY2UuVXNlcih7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgdG9rZW46ICd1c2VyLW9yLXByb2plY3QtYWNjZXNzLXRva2VuJ1xuICAgICAgICAqICAgICAgIH0pO1xuICAgICAgICAqICAgICAgIHVhLmdldCh7IHVzZXJOYW1lOiAnanNtaXRoJyB9KTtcbiAgICAgICAgKiAgICAgICB1YS5nZXQoeyBpZDogWyc0MjgzNmQ0Yi01YjYxLTRmZTQtODBlYi0zMTM2ZTk1NmVlNWMnLFxuICAgICAgICAqICAgICAgICAgICAgICAgICAgICc0ZWE3NTYzMS00YzhkLTQ4NzItOWQ4MC1iNDYwMDE0NjQ3OGUnXSB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IGZpbHRlciBPYmplY3Qgd2l0aCBmaWVsZCBgdXNlck5hbWVgIGFuZCB2YWx1ZSBvZiB0aGUgdXNlcm5hbWUuIEFsdGVybmF0aXZlbHksIG9iamVjdCB3aXRoIGZpZWxkIGBpZGAgYW5kIHZhbHVlIG9mIGFuIGFycmF5IG9mIHVzZXIgaWRzLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cblxuICAgICAgICBnZXQ6IGZ1bmN0aW9uIChmaWx0ZXIsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgICAgICAgICAgZmlsdGVyID0gZmlsdGVyIHx8IHt9O1xuXG4gICAgICAgICAgICB2YXIgZ2V0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnNcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHZhciB0b1FGaWx0ZXIgPSBmdW5jdGlvbiAoZmlsdGVyKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlcyA9IHt9O1xuXG4gICAgICAgICAgICAgICAgLy8gQVBJIG9ubHkgc3VwcG9ydHMgZmlsdGVyaW5nIGJ5IHVzZXJuYW1lIGZvciBub3dcbiAgICAgICAgICAgICAgICBpZiAoZmlsdGVyLnVzZXJOYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlcy5xID0gZmlsdGVyLnVzZXJOYW1lO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybiByZXM7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICB2YXIgdG9JZEZpbHRlcnMgPSBmdW5jdGlvbiAoaWQpIHtcbiAgICAgICAgICAgICAgICBpZiAoIWlkKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiAnJztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZCA9ICQuaXNBcnJheShpZCkgPyBpZCA6IFtpZF07XG4gICAgICAgICAgICAgICAgcmV0dXJuICdpZD0nICsgaWQuam9pbignJmlkPScpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgdmFyIGdldEZpbHRlcnMgPSBbXG4gICAgICAgICAgICAgICAgJ2FjY291bnQ9JyArIGdldE9wdGlvbnMuYWNjb3VudCxcbiAgICAgICAgICAgICAgICB0b0lkRmlsdGVycyhmaWx0ZXIuaWQpLFxuICAgICAgICAgICAgICAgIHF1dGlsLnRvUXVlcnlGb3JtYXQodG9RRmlsdGVyKGZpbHRlcikpXG4gICAgICAgICAgICBdLmpvaW4oJyYnKTtcblxuICAgICAgICAgICAgLy8gc3BlY2lhbCBjYXNlIGZvciBxdWVyaWVzIHdpdGggbGFyZ2UgbnVtYmVyIG9mIGlkc1xuICAgICAgICAgICAgLy8gbWFrZSBpdCBhcyBhIHBvc3Qgd2l0aCBHRVQgc2VtYW50aWNzXG4gICAgICAgICAgICB2YXIgdGhyZXNob2xkID0gMzA7XG4gICAgICAgICAgICBpZiAoZmlsdGVyLmlkICYmICQuaXNBcnJheShmaWx0ZXIuaWQpICYmIGZpbHRlci5pZC5sZW5ndGggPj0gdGhyZXNob2xkKSB7XG4gICAgICAgICAgICAgICAgZ2V0T3B0aW9ucy51cmwgPSB1cmxDb25maWcuZ2V0QVBJUGF0aCgndXNlcicpICsgJz9fbWV0aG9kPUdFVCc7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGh0dHAucG9zdCh7IGlkOiBmaWx0ZXIuaWQgfSwgZ2V0T3B0aW9ucyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiBodHRwLmdldChnZXRGaWx0ZXJzLCBnZXRPcHRpb25zKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBSZXRyaWV2ZSBkZXRhaWxzIGFib3V0IGEgc2luZ2xlIGVuZCB1c2VyIGluIHlvdXIgdGVhbSwgYmFzZWQgb24gdXNlciBpZC5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICB2YXIgdWEgPSBuZXcgRi5zZXJ2aWNlLlVzZXIoe1xuICAgICAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgIHRva2VuOiAndXNlci1vci1wcm9qZWN0LWFjY2Vzcy10b2tlbidcbiAgICAgICAgKiAgICAgICB9KTtcbiAgICAgICAgKiAgICAgICB1YS5nZXRCeUlkKCc0MjgzNmQ0Yi01YjYxLTRmZTQtODBlYi0zMTM2ZTk1NmVlNWMnKTtcbiAgICAgICAgKlxuICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHVzZXJJZCBUaGUgdXNlciBpZCBmb3IgdGhlIGVuZCB1c2VyIGluIHlvdXIgdGVhbS5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG5cbiAgICAgICAgZ2V0QnlJZDogZnVuY3Rpb24gKHVzZXJJZCwgb3B0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIHB1YmxpY0FQSS5nZXQoeyBpZDogdXNlcklkIH0sIG9wdGlvbnMpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FQSSk7XG59O1xuXG5cbiIsIi8qKlxuICpcbiAqICMjIFZhcmlhYmxlcyBBUEkgU2VydmljZVxuICpcbiAqIFVzZWQgaW4gY29uanVuY3Rpb24gd2l0aCB0aGUgW1J1biBBUEkgU2VydmljZV0oLi4vcnVuLWFwaS1zZXJ2aWNlLykgdG8gcmVhZCwgd3JpdGUsIGFuZCBzZWFyY2ggZm9yIHNwZWNpZmljIG1vZGVsIHZhcmlhYmxlcy5cbiAqXG4gKiAgICAgdmFyIHJtID0gbmV3IEYubWFuYWdlci5SdW5NYW5hZ2VyKHtcbiAqICAgICAgICAgICBydW46IHtcbiAqICAgICAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICogICAgICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICogICAgICAgICAgICAgICBtb2RlbDogJ3N1cHBseS1jaGFpbi1tb2RlbC5qbCdcbiAqICAgICAgICAgICB9XG4gKiAgICAgIH0pO1xuICogICAgIHJtLmdldFJ1bigpXG4gKiAgICAgICAudGhlbihmdW5jdGlvbigpIHtcbiAqICAgICAgICAgIHZhciB2cyA9IHJtLnJ1bi52YXJpYWJsZXMoKTtcbiAqICAgICAgICAgIHZzLnNhdmUoe3NhbXBsZV9pbnQ6IDR9KTtcbiAqICAgICAgICB9KTtcbiAqXG4gKi9cblxuXG4gJ3VzZSBzdHJpY3QnO1xuXG4gdmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xuIHZhciBydXRpbCA9IHJlcXVpcmUoJy4uL3V0aWwvcnVuLXV0aWwnKTtcblxuIG1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgcnVucyBvYmplY3QgdG8gd2hpY2ggdGhlIHZhcmlhYmxlIGZpbHRlcnMgYXBwbHkuIERlZmF1bHRzIHRvIG51bGwuXG4gICAgICAgICAqIEB0eXBlIHtydW5TZXJ2aWNlfVxuICAgICAgICAgKi9cbiAgICAgICAgIHJ1blNlcnZpY2U6IG51bGxcbiAgICAgfTtcbiAgICAgdmFyIHNlcnZpY2VPcHRpb25zID0gJC5leHRlbmQoe30sIGRlZmF1bHRzLCBjb25maWcpO1xuXG4gICAgIHZhciBnZXRVUkwgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICByZXR1cm4gc2VydmljZU9wdGlvbnMucnVuU2VydmljZS51cmxDb25maWcuZ2V0RmlsdGVyVVJMKCkgKyAndmFyaWFibGVzLyc7XG4gICAgIH07XG5cbiAgICAgdmFyIGFkZEF1dG9SZXN0b3JlSGVhZGVyID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgIHJldHVybiBzZXJ2aWNlT3B0aW9ucy5ydW5TZXJ2aWNlLnVybENvbmZpZy5hZGRBdXRvUmVzdG9yZUhlYWRlcihvcHRpb25zKTtcbiAgICAgfTtcblxuICAgICB2YXIgaHR0cE9wdGlvbnMgPSB7XG4gICAgICAgICB1cmw6IGdldFVSTFxuICAgICB9O1xuICAgICBpZiAoc2VydmljZU9wdGlvbnMudG9rZW4pIHtcbiAgICAgICAgIGh0dHBPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogJ0JlYXJlciAnICsgc2VydmljZU9wdGlvbnMudG9rZW5cbiAgICAgICAgIH07XG4gICAgIH1cbiAgICAgdmFyIGh0dHAgPSBuZXcgVHJhbnNwb3J0RmFjdG9yeShodHRwT3B0aW9ucyk7XG4gICAgIGh0dHAuc3BsaXRHZXQgPSBydXRpbC5zcGxpdEdldEZhY3RvcnkoaHR0cE9wdGlvbnMpO1xuXG4gICAgIHZhciBwdWJsaWNBUEkgPSB7XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCB2YWx1ZXMgZm9yIGEgdmFyaWFibGUuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgdnMubG9hZCgnc2FtcGxlX2ludCcpXG4gICAgICAgICAqICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHZhbCl7XG4gICAgICAgICAqICAgICAgICAgICAgICAvLyB2YWwgY29udGFpbnMgdGhlIHZhbHVlIG9mIHNhbXBsZV9pbnRcbiAgICAgICAgICogICAgICAgICAgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSB2YXJpYWJsZSBOYW1lIG9mIHZhcmlhYmxlIHRvIGxvYWQuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvdXRwdXRNb2RpZmllciAoT3B0aW9uYWwpIEF2YWlsYWJsZSBmaWVsZHMgaW5jbHVkZTogYHN0YXJ0cmVjb3JkYCwgYGVuZHJlY29yZGAsIGBzb3J0YCwgYW5kIGBkaXJlY3Rpb25gIChgYXNjYCBvciBgZGVzY2ApLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgIGxvYWQ6IGZ1bmN0aW9uICh2YXJpYWJsZSwgb3V0cHV0TW9kaWZpZXIsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgIGh0dHBPcHRpb25zID0gYWRkQXV0b1Jlc3RvcmVIZWFkZXIoaHR0cE9wdGlvbnMpO1xuICAgICAgICAgICAgIHJldHVybiBodHRwLmdldChvdXRwdXRNb2RpZmllciwgJC5leHRlbmQoe30sIGh0dHBPcHRpb25zLCB7XG4gICAgICAgICAgICAgICAgIHVybDogZ2V0VVJMKCkgKyB2YXJpYWJsZSArICcvJ1xuICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybnMgcGFydGljdWxhciB2YXJpYWJsZXMsIGJhc2VkIG9uIGNvbmRpdGlvbnMgc3BlY2lmaWVkIGluIHRoZSBgcXVlcnlgIG9iamVjdC5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICB2cy5xdWVyeShbJ3ByaWNlJywgJ3NhbGVzJ10pXG4gICAgICAgICAqICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHZhbCkge1xuICAgICAgICAgKiAgICAgICAgICAgICAgLy8gdmFsIGlzIGFuIG9iamVjdCB3aXRoIHRoZSB2YWx1ZXMgb2YgdGhlIHJlcXVlc3RlZCB2YXJpYWJsZXM6IHZhbC5wcmljZSwgdmFsLnNhbGVzXG4gICAgICAgICAqICAgICAgICAgIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIHZzLnF1ZXJ5KHsgaW5jbHVkZTpbJ3ByaWNlJywgJ3NhbGVzJ10gfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fEFycmF5fSBxdWVyeSBUaGUgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcyByZXF1ZXN0ZWQuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvdXRwdXRNb2RpZmllciAoT3B0aW9uYWwpIEF2YWlsYWJsZSBmaWVsZHMgaW5jbHVkZTogYHN0YXJ0cmVjb3JkYCwgYGVuZHJlY29yZGAsIGBzb3J0YCwgYW5kIGBkaXJlY3Rpb25gIChgYXNjYCBvciBgZGVzY2ApLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgIHF1ZXJ5OiBmdW5jdGlvbiAocXVlcnksIG91dHB1dE1vZGlmaWVyLCBvcHRpb25zKSB7XG4gICAgICAgICAgICAvL1F1ZXJ5IGFuZCBvdXRwdXRNb2RpZmllciBhcmUgYm90aCBxdWVyeXN0cmluZ3MgaW4gdGhlIHVybDsgb25seSBjYWxsaW5nIHRoZW0gb3V0IHNlcGFyYXRlbHkgaGVyZSB0byBiZSBjb25zaXN0ZW50IHdpdGggdGhlIG90aGVyIGNhbGxzXG4gICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgICBodHRwT3B0aW9ucyA9IGFkZEF1dG9SZXN0b3JlSGVhZGVyKGh0dHBPcHRpb25zKTtcblxuICAgICAgICAgICAgIGlmICgkLmlzQXJyYXkocXVlcnkpKSB7XG4gICAgICAgICAgICAgICAgIHF1ZXJ5ID0geyBpbmNsdWRlOiBxdWVyeSB9O1xuICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAkLmV4dGVuZChxdWVyeSwgb3V0cHV0TW9kaWZpZXIpO1xuICAgICAgICAgICAgIHJldHVybiBodHRwLnNwbGl0R2V0KHF1ZXJ5LCBodHRwT3B0aW9ucyk7XG4gICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBTYXZlIHZhbHVlcyB0byBtb2RlbCB2YXJpYWJsZXMuIE92ZXJ3cml0ZXMgZXhpc3RpbmcgdmFsdWVzLiBOb3RlIHRoYXQgeW91IGNhbiBvbmx5IHVwZGF0ZSBtb2RlbCB2YXJpYWJsZXMgaWYgdGhlIHJ1biBpcyBbaW4gbWVtb3J5XSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvI3J1bnMtaW4tbWVtb3J5KS4gKEFuIGFsdGVybmF0ZSB3YXkgdG8gdXBkYXRlIG1vZGVsIHZhcmlhYmxlcyBpcyB0byBjYWxsIGEgbWV0aG9kIGZyb20gdGhlIG1vZGVsIGFuZCBtYWtlIHN1cmUgdGhhdCB0aGUgbWV0aG9kIHBlcnNpc3RzIHRoZSB2YXJpYWJsZXMuIFNlZSBgZG9gLCBgc2VyaWFsYCwgYW5kIGBwYXJhbGxlbGAgaW4gdGhlIFtSdW4gQVBJIFNlcnZpY2VdKC4uL3J1bi1hcGktc2VydmljZS8pIGZvciBjYWxsaW5nIG1ldGhvZHMgZnJvbSB0aGUgbW9kZWwuKVxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIHZzLnNhdmUoJ3ByaWNlJywgNCk7XG4gICAgICAgICAqICAgICAgdnMuc2F2ZSh7IHByaWNlOiA0LCBxdWFudGl0eTogNSwgcHJvZHVjdHM6IFsyLDMsNF0gfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fFN0cmluZ30gdmFyaWFibGUgQW4gb2JqZWN0IGNvbXBvc2VkIG9mIHRoZSBtb2RlbCB2YXJpYWJsZXMgYW5kIHRoZSB2YWx1ZXMgdG8gc2F2ZS4gQWx0ZXJuYXRpdmVseSwgYSBzdHJpbmcgd2l0aCB0aGUgbmFtZSBvZiB0aGUgdmFyaWFibGUuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSB2YWwgKE9wdGlvbmFsKSBJZiBwYXNzaW5nIGEgc3RyaW5nIGZvciBgdmFyaWFibGVgLCB1c2UgdGhpcyBhcmd1bWVudCBmb3IgdGhlIHZhbHVlIHRvIHNhdmUuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICAgc2F2ZTogZnVuY3Rpb24gKHZhcmlhYmxlLCB2YWwsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgICB2YXIgYXR0cnM7XG4gICAgICAgICAgICAgaWYgKHR5cGVvZiB2YXJpYWJsZSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgICAgICAgYXR0cnMgPSB2YXJpYWJsZTtcbiAgICAgICAgICAgICAgICAgb3B0aW9ucyA9IHZhbDtcbiAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAoYXR0cnMgPSB7fSlbdmFyaWFibGVdID0gdmFsO1xuICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuXG4gICAgICAgICAgICAgcmV0dXJuIGh0dHAucGF0Y2guY2FsbCh0aGlzLCBhdHRycywgaHR0cE9wdGlvbnMpO1xuICAgICAgICAgfVxuXG4gICAgICAgIC8vIE5vdCBBdmFpbGFibGUgdW50aWwgdW5kZXJseWluZyBBUEkgc3VwcG9ydHMgUFVULiBPdGhlcndpc2Ugc2F2ZSB3b3VsZCBiZSBQVVQgYW5kIG1lcmdlIHdvdWxkIGJlIFBBVENIXG4gICAgICAgIC8vICpcbiAgICAgICAgLy8gICogU2F2ZSB2YWx1ZXMgdG8gdGhlIGFwaS4gTWVyZ2VzIGFycmF5cywgYnV0IG90aGVyd2lzZSBzYW1lIGFzIHNhdmVcbiAgICAgICAgLy8gICogQHBhcmFtIHtPYmplY3R8U3RyaW5nfSB2YXJpYWJsZSBPYmplY3Qgd2l0aCBhdHRyaWJ1dGVzLCBvciBzdHJpbmcga2V5XG4gICAgICAgIC8vICAqIEBwYXJhbSB7T2JqZWN0fSB2YWwgT3B0aW9uYWwgaWYgcHJldiBwYXJhbWV0ZXIgd2FzIGEgc3RyaW5nLCBzZXQgdmFsdWUgaGVyZVxuICAgICAgICAvLyAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xuICAgICAgICAvLyAgKlxuICAgICAgICAvLyAgKiBAZXhhbXBsZVxuICAgICAgICAvLyAgKiAgICAgdnMubWVyZ2UoeyBwcmljZTogNCwgcXVhbnRpdHk6IDUsIHByb2R1Y3RzOiBbMiwzLDRdIH0pXG4gICAgICAgIC8vICAqICAgICB2cy5tZXJnZSgncHJpY2UnLCA0KTtcblxuICAgICAgICAvLyBtZXJnZTogZnVuY3Rpb24gKHZhcmlhYmxlLCB2YWwsIG9wdGlvbnMpIHtcbiAgICAgICAgLy8gICAgIHZhciBhdHRycztcbiAgICAgICAgLy8gICAgIGlmICh0eXBlb2YgdmFyaWFibGUgPT09ICdvYmplY3QnKSB7XG4gICAgICAgIC8vICAgICAgIGF0dHJzID0gdmFyaWFibGU7XG4gICAgICAgIC8vICAgICAgIG9wdGlvbnMgPSB2YWw7XG4gICAgICAgIC8vICAgICB9IGVsc2Uge1xuICAgICAgICAvLyAgICAgICAoYXR0cnMgPSB7fSlbdmFyaWFibGVdID0gdmFsO1xuICAgICAgICAvLyAgICAgfVxuICAgICAgICAvLyAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcblxuICAgICAgICAvLyAgICAgcmV0dXJuIGh0dHAucGF0Y2guY2FsbCh0aGlzLCBhdHRycywgaHR0cE9wdGlvbnMpO1xuICAgICAgICAvLyB9XG4gICAgIH07XG4gICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FQSSk7XG4gfTtcbiIsIi8qKlxuICogIyMgV29ybGQgQVBJIEFkYXB0ZXJcbiAqXG4gKiBBIFtydW5dKC4uLy4uLy4uL2dsb3NzYXJ5LyNydW4pIGlzIGEgY29sbGVjdGlvbiBvZiBlbmQgdXNlciBpbnRlcmFjdGlvbnMgd2l0aCBhIHByb2plY3QgYW5kIGl0cyBtb2RlbCAtLSBpbmNsdWRpbmcgc2V0dGluZyB2YXJpYWJsZXMsIG1ha2luZyBkZWNpc2lvbnMsIGFuZCBjYWxsaW5nIG9wZXJhdGlvbnMuIEZvciBidWlsZGluZyBtdWx0aXBsYXllciBzaW11bGF0aW9ucyB5b3UgdHlwaWNhbGx5IHdhbnQgbXVsdGlwbGUgZW5kIHVzZXJzIHRvIHNoYXJlIHRoZSBzYW1lIHNldCBvZiBpbnRlcmFjdGlvbnMsIGFuZCB3b3JrIHdpdGhpbiBhIGNvbW1vbiBzdGF0ZS4gRXBpY2VudGVyIGFsbG93cyB5b3UgdG8gY3JlYXRlIFwid29ybGRzXCIgdG8gaGFuZGxlIHN1Y2ggY2FzZXMuIE9ubHkgW3RlYW0gcHJvamVjdHNdKC4uLy4uLy4uL2dsb3NzYXJ5LyN0ZWFtKSBjYW4gYmUgbXVsdGlwbGF5ZXIuXG4gKlxuICogVGhlIFdvcmxkIEFQSSBBZGFwdGVyIGFsbG93cyB5b3UgdG8gY3JlYXRlLCBhY2Nlc3MsIGFuZCBtYW5pcHVsYXRlIG11bHRpcGxheWVyIHdvcmxkcyB3aXRoaW4geW91ciBFcGljZW50ZXIgcHJvamVjdC4gWW91IGNhbiB1c2UgdGhpcyB0byBhZGQgYW5kIHJlbW92ZSBlbmQgdXNlcnMgZnJvbSB0aGUgd29ybGQsIGFuZCB0byBjcmVhdGUsIGFjY2VzcywgYW5kIHJlbW92ZSB0aGVpciBydW5zLiBCZWNhdXNlIG9mIHRoaXMsIHR5cGljYWxseSB0aGUgV29ybGQgQWRhcHRlciBpcyB1c2VkIGZvciBmYWNpbGl0YXRvciBwYWdlcyBpbiB5b3VyIHByb2plY3QuIChUaGUgcmVsYXRlZCBbV29ybGQgTWFuYWdlcl0oLi4vd29ybGQtbWFuYWdlci8pIHByb3ZpZGVzIGFuIGVhc3kgd2F5IHRvIGFjY2VzcyBydW5zIGFuZCB3b3JsZHMgZm9yIHBhcnRpY3VsYXIgZW5kIHVzZXJzLCBzbyBpcyB0eXBpY2FsbHkgdXNlZCBpbiBwYWdlcyB0aGF0IGVuZCB1c2VycyB3aWxsIGludGVyYWN0IHdpdGguKVxuICpcbiAqIEFzIHdpdGggYWxsIHRoZSBvdGhlciBbQVBJIEFkYXB0ZXJzXSguLi8uLi8pLCBhbGwgbWV0aG9kcyB0YWtlIGluIGFuIFwib3B0aW9uc1wiIG9iamVjdCBhcyB0aGUgbGFzdCBwYXJhbWV0ZXIuIFRoZSBvcHRpb25zIGNhbiBiZSB1c2VkIHRvIGV4dGVuZC9vdmVycmlkZSB0aGUgV29ybGQgQVBJIFNlcnZpY2UgZGVmYXVsdHMuXG4gKlxuICogVG8gdXNlIHRoZSBXb3JsZCBBZGFwdGVyLCBpbnN0YW50aWF0ZSBpdCBhbmQgdGhlbiBhY2Nlc3MgdGhlIG1ldGhvZHMgcHJvdmlkZWQuIEluc3RhbnRpYXRpbmcgcmVxdWlyZXMgdGhlIGFjY291bnQgaWQgKCoqVGVhbSBJRCoqIGluIHRoZSBFcGljZW50ZXIgdXNlciBpbnRlcmZhY2UpLCBwcm9qZWN0IGlkICgqKlByb2plY3QgSUQqKiksIGFuZCBncm91cCAoKipHcm91cCBOYW1lKiopLlxuICpcbiAqICAgICAgIHZhciB3YSA9IG5ldyBGLnNlcnZpY2UuV29ybGQoe1xuICogICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICogICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAqICAgICAgICAgIGdyb3VwOiAndGVhbTEnIH0pO1xuICogICAgICAgd2EuY3JlYXRlKClcbiAqICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHdvcmxkKSB7XG4gKiAgICAgICAgICAgICAgLy8gY2FsbCBtZXRob2RzLCBlLmcuIHdhLmFkZFVzZXJzKClcbiAqICAgICAgICAgIH0pO1xuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xuLy8gdmFyIHF1dGlsID0gcmVxdWlyZSgnLi4vdXRpbC9xdWVyeS11dGlsJyk7XG52YXIgVHJhbnNwb3J0RmFjdG9yeSA9IHJlcXVpcmUoJy4uL3RyYW5zcG9ydC9odHRwLXRyYW5zcG9ydC1mYWN0b3J5Jyk7XG52YXIgU2Vzc2lvbk1hbmFnZXIgPSByZXF1aXJlKCcuLi9zdG9yZS9zZXNzaW9uLW1hbmFnZXInKTtcbnZhciBfcGljayA9IHJlcXVpcmUoJy4uL3V0aWwvb2JqZWN0LXV0aWwnKS5fcGljaztcblxudmFyIGFwaUJhc2UgPSAnbXVsdGlwbGF5ZXIvJztcbnZhciBhc3NpZ25tZW50RW5kcG9pbnQgPSBhcGlCYXNlICsgJ2Fzc2lnbic7XG52YXIgYXBpRW5kcG9pbnQgPSBhcGlCYXNlICsgJ3dvcmxkJztcbnZhciBwcm9qZWN0RW5kcG9pbnQgPSBhcGlCYXNlICsgJ3Byb2plY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBGb3IgcHJvamVjdHMgdGhhdCByZXF1aXJlIGF1dGhlbnRpY2F0aW9uLCBwYXNzIGluIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiAoZGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nKS4gSWYgdGhlIHVzZXIgaXMgYWxyZWFkeSBsb2dnZWQgaW4gdG8gRXBpY2VudGVyLCB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gaXMgYWxyZWFkeSBzZXQgaW4gYSBjb29raWUgYW5kIGF1dG9tYXRpY2FsbHkgbG9hZGVkIGZyb20gdGhlcmUuIChTZWUgW21vcmUgYmFja2dyb3VuZCBvbiBhY2Nlc3MgdG9rZW5zXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pKS5cbiAgICAgICAgICogQHNlZSBbQXV0aGVudGljYXRpb24gQVBJIFNlcnZpY2VdKC4uL2F1dGgtYXBpLXNlcnZpY2UvKSBmb3IgZ2V0dGluZyB0b2tlbnMuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICB0b2tlbjogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgcHJvamVjdCBpZC4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHByb2plY3Q6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFjY291bnQgaWQuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBVUkwuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBhY2NvdW50OiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBncm91cCBuYW1lLiBEZWZhdWx0cyB0byB1bmRlZmluZWQuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBncm91cDogdW5kZWZpbmVkLFxuXG4gICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBtb2RlbCBmaWxlIHRvIHVzZSB0byBjcmVhdGUgcnVucyBpbiB0aGlzIHdvcmxkLiBEZWZhdWx0cyB0byB1bmRlZmluZWQuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBtb2RlbDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDcml0ZXJpYSBieSB3aGljaCB0byBmaWx0ZXIgd29ybGQuIEN1cnJlbnRseSBvbmx5IHN1cHBvcnRzIHdvcmxkLWlkcyBhcyBmaWx0ZXJzLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgZmlsdGVyOiAnJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ29udmVuaWVuY2UgYWxpYXMgZm9yIGZpbHRlclxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgaWQ6ICcnLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBPcHRpb25zIHRvIHBhc3Mgb24gdG8gdGhlIHVuZGVybHlpbmcgdHJhbnNwb3J0IGxheWVyLiBBbGwganF1ZXJ5LmFqYXggb3B0aW9ucyBhdCBodHRwOi8vYXBpLmpxdWVyeS5jb20valF1ZXJ5LmFqYXgvIGFyZSBhdmFpbGFibGUuIERlZmF1bHRzIHRvIGVtcHR5IG9iamVjdC5cbiAgICAgICAgICogQHR5cGUge09iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIHRyYW5zcG9ydDoge30sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENhbGxlZCB3aGVuIHRoZSBjYWxsIGNvbXBsZXRlcyBzdWNjZXNzZnVsbHkuIERlZmF1bHRzIHRvIGAkLm5vb3BgLlxuICAgICAgICAgKiBAdHlwZSB7ZnVuY3Rpb259XG4gICAgICAgICAqL1xuICAgICAgICBzdWNjZXNzOiAkLm5vb3AsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENhbGxlZCB3aGVuIHRoZSBjYWxsIGZhaWxzLiBEZWZhdWx0cyB0byBgJC5ub29wYC5cbiAgICAgICAgICogQHR5cGUge2Z1bmN0aW9ufVxuICAgICAgICAgKi9cbiAgICAgICAgZXJyb3I6ICQubm9vcFxuICAgIH07XG5cbiAgICB0aGlzLnNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKCk7XG4gICAgdmFyIHNlcnZpY2VPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKGRlZmF1bHRzLCBjb25maWcpO1xuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5pZCkge1xuICAgICAgICBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgPSBzZXJ2aWNlT3B0aW9ucy5pZDtcbiAgICB9XG5cbiAgICB2YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2Uoc2VydmljZU9wdGlvbnMpLmdldCgnc2VydmVyJyk7XG5cbiAgICBpZiAoIXNlcnZpY2VPcHRpb25zLmFjY291bnQpIHtcbiAgICAgICAgc2VydmljZU9wdGlvbnMuYWNjb3VudCA9IHVybENvbmZpZy5hY2NvdW50UGF0aDtcbiAgICB9XG5cbiAgICBpZiAoIXNlcnZpY2VPcHRpb25zLnByb2plY3QpIHtcbiAgICAgICAgc2VydmljZU9wdGlvbnMucHJvamVjdCA9IHVybENvbmZpZy5wcm9qZWN0UGF0aDtcbiAgICB9XG5cbiAgICB2YXIgdHJhbnNwb3J0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQsIHtcbiAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludClcbiAgICB9KTtcblxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICB0cmFuc3BvcnRPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHZhciBodHRwID0gbmV3IFRyYW5zcG9ydEZhY3RvcnkodHJhbnNwb3J0T3B0aW9ucyk7XG5cbiAgICB2YXIgc2V0SWRGaWx0ZXJPclRocm93RXJyb3IgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICBpZiAob3B0aW9ucy5pZCkge1xuICAgICAgICAgICAgc2VydmljZU9wdGlvbnMuZmlsdGVyID0gb3B0aW9ucy5pZDtcbiAgICAgICAgfVxuICAgICAgICBpZiAob3B0aW9ucy5maWx0ZXIpIHtcbiAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IG9wdGlvbnMuZmlsdGVyO1xuICAgICAgICB9XG4gICAgICAgIGlmICghc2VydmljZU9wdGlvbnMuZmlsdGVyKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHdvcmxkIGlkIHNwZWNpZmllZCB0byBhcHBseSBvcGVyYXRpb25zIGFnYWluc3QuIFRoaXMgY291bGQgaGFwcGVuIGlmIHRoZSB1c2VyIGlzIG5vdCBhc3NpZ25lZCB0byBhIHdvcmxkIGFuZCBpcyB0cnlpbmcgdG8gd29yayB3aXRoIHJ1bnMgZnJvbSB0aGF0IHdvcmxkLicpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHZhciB2YWxpZGF0ZU1vZGVsT3JUaHJvd0Vycm9yID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgaWYgKCFvcHRpb25zLm1vZGVsKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIG1vZGVsIHNwZWNpZmllZCB0byBnZXQgdGhlIGN1cnJlbnQgcnVuJyk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgdmFyIHB1YmxpY0FQSSA9IHtcblxuICAgICAgICAvKipcbiAgICAgICAgKiBDcmVhdGVzIGEgbmV3IFdvcmxkLlxuICAgICAgICAqXG4gICAgICAgICogVXNpbmcgdGhpcyBtZXRob2QgaXMgcmFyZS4gSXQgaXMgbW9yZSBjb21tb24gdG8gY3JlYXRlIHdvcmxkcyBhdXRvbWF0aWNhbGx5IHdoaWxlIHlvdSBgYXV0b0Fzc2lnbigpYCBlbmQgdXNlcnMgdG8gd29ybGRzLiAoSW4gdGhpcyBjYXNlLCBjb25maWd1cmF0aW9uIGRhdGEgZm9yIHRoZSB3b3JsZCwgc3VjaCBhcyB0aGUgcm9sZXMsIGFyZSByZWFkIGZyb20gdGhlIHByb2plY3QtbGV2ZWwgd29ybGQgY29uZmlndXJhdGlvbiBpbmZvcm1hdGlvbiwgZm9yIGV4YW1wbGUgYnkgYGdldFByb2plY3RTZXR0aW5ncygpYC4pXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5jcmVhdGUoe1xuICAgICAgICAqICAgICAgICAgICByb2xlczogWydWUCBNYXJrZXRpbmcnLCAnVlAgU2FsZXMnLCAnVlAgRW5naW5lZXJpbmcnXVxuICAgICAgICAqICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IHBhcmFtcyBQYXJhbWV0ZXJzIHRvIGNyZWF0ZSB0aGUgd29ybGQuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5ncm91cCAoT3B0aW9uYWwpIFRoZSAqKkdyb3VwIE5hbWUqKiB0byBjcmVhdGUgdGhpcyB3b3JsZCB1bmRlci4gT25seSBlbmQgdXNlcnMgaW4gdGhpcyBncm91cCBhcmUgZWxpZ2libGUgdG8gam9pbiB0aGUgd29ybGQuIE9wdGlvbmFsIGhlcmU7IHJlcXVpcmVkIHdoZW4gaW5zdGFudGlhdGluZyB0aGUgc2VydmljZSAoYG5ldyBGLnNlcnZpY2UuV29ybGQoKWApLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMucm9sZXMgKE9wdGlvbmFsKSBUaGUgbGlzdCBvZiByb2xlcyAoc3RyaW5ncykgZm9yIHRoaXMgd29ybGQuIFNvbWUgd29ybGRzIGhhdmUgc3BlY2lmaWMgcm9sZXMgdGhhdCAqKm11c3QqKiBiZSBmaWxsZWQgYnkgZW5kIHVzZXJzLiBMaXN0aW5nIHRoZSByb2xlcyBhbGxvd3MgeW91IHRvIGF1dG9hc3NpZ24gdXNlcnMgdG8gd29ybGRzIGFuZCBlbnN1cmUgdGhhdCBhbGwgcm9sZXMgYXJlIGZpbGxlZCBpbiBlYWNoIHdvcmxkLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMub3B0aW9uYWxSb2xlcyAoT3B0aW9uYWwpIFRoZSBsaXN0IG9mIG9wdGlvbmFsIHJvbGVzIChzdHJpbmdzKSBmb3IgdGhpcyB3b3JsZC4gU29tZSB3b3JsZHMgaGF2ZSBzcGVjaWZpYyByb2xlcyB0aGF0ICoqbWF5KiogYmUgZmlsbGVkIGJ5IGVuZCB1c2Vycy4gTGlzdGluZyB0aGUgb3B0aW9uYWwgcm9sZXMgYXMgcGFydCBvZiB0aGUgd29ybGQgb2JqZWN0IGFsbG93cyB5b3UgdG8gYXV0b2Fzc2lnbiB1c2VycyB0byB3b3JsZHMgYW5kIGVuc3VyZSB0aGF0IGFsbCByb2xlcyBhcmUgZmlsbGVkIGluIGVhY2ggd29ybGQuXG4gICAgICAgICogQHBhcmFtIHtpbnRlZ2VyfSBwYXJhbXMubWluVXNlcnMgKE9wdGlvbmFsKSBUaGUgbWluaW11bSBudW1iZXIgb2YgdXNlcnMgZm9yIHRoZSB3b3JsZC4gSW5jbHVkaW5nIHRoaXMgbnVtYmVyIGFsbG93cyB5b3UgdG8gYXV0b2Fzc2lnbiBlbmQgdXNlcnMgdG8gd29ybGRzIGFuZCBlbnN1cmUgdGhhdCB0aGUgY29ycmVjdCBudW1iZXIgb2YgdXNlcnMgYXJlIGluIGVhY2ggd29ybGQuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBjcmVhdGU6IGZ1bmN0aW9uIChwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBjcmVhdGVPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zLCB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpIH0pO1xuICAgICAgICAgICAgdmFyIHdvcmxkQXBpUGFyYW1zID0gWydzY29wZScsICdmaWxlcycsICdyb2xlcycsICdvcHRpb25hbFJvbGVzJywgJ21pblVzZXJzJywgJ2dyb3VwJywgJ25hbWUnXTtcbiAgICAgICAgICAgIHZhciB2YWxpZFBhcmFtcyA9IF9waWNrKHNlcnZpY2VPcHRpb25zLCBbJ2FjY291bnQnLCAncHJvamVjdCcsICdncm91cCddKTtcbiAgICAgICAgICAgIC8vIHdoaXRlbGlzdCB0aGUgZmllbGRzIHRoYXQgd2UgYWN0dWFsbHkgY2FuIHNlbmQgdG8gdGhlIGFwaVxuICAgICAgICAgICAgcGFyYW1zID0gX3BpY2socGFyYW1zLCB3b3JsZEFwaVBhcmFtcyk7XG5cbiAgICAgICAgICAgIC8vIGFjY291bnQgYW5kIHByb2plY3QgZ28gaW4gdGhlIGJvZHksIG5vdCBpbiB0aGUgdXJsXG4gICAgICAgICAgICBwYXJhbXMgPSAkLmV4dGVuZCh7fSwgdmFsaWRQYXJhbXMsIHBhcmFtcyk7XG5cbiAgICAgICAgICAgIHZhciBvbGRTdWNjZXNzID0gY3JlYXRlT3B0aW9ucy5zdWNjZXNzO1xuICAgICAgICAgICAgY3JlYXRlT3B0aW9ucy5zdWNjZXNzID0gZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMuZmlsdGVyID0gcmVzcG9uc2UuaWQ7IC8vYWxsIGZ1dHVyZSBjaGFpbmVkIGNhbGxzIHRvIG9wZXJhdGUgb24gdGhpcyBpZFxuICAgICAgICAgICAgICAgIHJldHVybiBvbGRTdWNjZXNzLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wb3N0KHBhcmFtcywgY3JlYXRlT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogVXBkYXRlcyBhIFdvcmxkLCBmb3IgZXhhbXBsZSB0byByZXBsYWNlIHRoZSByb2xlcyBpbiB0aGUgd29ybGQuXG4gICAgICAgICpcbiAgICAgICAgKiBUeXBpY2FsbHksIHlvdSBjb21wbGV0ZSB3b3JsZCBjb25maWd1cmF0aW9uIGF0IHRoZSBwcm9qZWN0IGxldmVsLCByYXRoZXIgdGhhbiBhdCB0aGUgd29ybGQgbGV2ZWwuIEZvciBleGFtcGxlLCBlYWNoIHdvcmxkIGluIHlvdXIgcHJvamVjdCBwcm9iYWJseSBoYXMgdGhlIHNhbWUgcm9sZXMgZm9yIGVuZCB1c2Vycy4gQW5kIHlvdXIgcHJvamVjdCBpcyBwcm9iYWJseSBlaXRoZXIgY29uZmlndXJlZCBzbyB0aGF0IGFsbCBlbmQgdXNlcnMgc2hhcmUgdGhlIHNhbWUgd29ybGQgKGFuZCBydW4pLCBvciBzbWFsbGVyIHNldHMgb2YgZW5kIHVzZXJzIHNoYXJlIHdvcmxkcyDigJQgYnV0IG5vdCBib3RoLiBIb3dldmVyLCB0aGlzIG1ldGhvZCBpcyBhdmFpbGFibGUgaWYgeW91IG5lZWQgdG8gdXBkYXRlIHRoZSBjb25maWd1cmF0aW9uIG9mIGEgcGFydGljdWxhciB3b3JsZC5cbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAgICAqICAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAgICAgICAgKiAgICAgIHdhLmNyZWF0ZSgpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHdvcmxkKSB7XG4gICAgICAgICogICAgICAgICAgICAgICB3YS51cGRhdGUoeyByb2xlczogWydWUCBNYXJrZXRpbmcnLCAnVlAgU2FsZXMnLCAnVlAgRW5naW5lZXJpbmcnXSB9KTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zIFBhcmFtZXRlcnMgdG8gdXBkYXRlIHRoZSB3b3JsZC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLm5hbWUgQSBzdHJpbmcgaWRlbnRpZmllciBmb3IgdGhlIGxpbmtlZCBlbmQgdXNlcnMsIGZvciBleGFtcGxlLCBcIm5hbWVcIjogXCJPdXIgVGVhbVwiLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMucm9sZXMgKE9wdGlvbmFsKSBUaGUgbGlzdCBvZiByb2xlcyAoc3RyaW5ncykgZm9yIHRoaXMgd29ybGQuIFNvbWUgd29ybGRzIGhhdmUgc3BlY2lmaWMgcm9sZXMgdGhhdCAqKm11c3QqKiBiZSBmaWxsZWQgYnkgZW5kIHVzZXJzLiBMaXN0aW5nIHRoZSByb2xlcyBhbGxvd3MgeW91IHRvIGF1dG9hc3NpZ24gdXNlcnMgdG8gd29ybGRzIGFuZCBlbnN1cmUgdGhhdCBhbGwgcm9sZXMgYXJlIGZpbGxlZCBpbiBlYWNoIHdvcmxkLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMub3B0aW9uYWxSb2xlcyAoT3B0aW9uYWwpIFRoZSBsaXN0IG9mIG9wdGlvbmFsIHJvbGVzIChzdHJpbmdzKSBmb3IgdGhpcyB3b3JsZC4gU29tZSB3b3JsZHMgaGF2ZSBzcGVjaWZpYyByb2xlcyB0aGF0ICoqbWF5KiogYmUgZmlsbGVkIGJ5IGVuZCB1c2Vycy4gTGlzdGluZyB0aGUgb3B0aW9uYWwgcm9sZXMgYXMgcGFydCBvZiB0aGUgd29ybGQgb2JqZWN0IGFsbG93cyB5b3UgdG8gYXV0b2Fzc2lnbiB1c2VycyB0byB3b3JsZHMgYW5kIGVuc3VyZSB0aGF0IGFsbCByb2xlcyBhcmUgZmlsbGVkIGluIGVhY2ggd29ybGQuXG4gICAgICAgICogQHBhcmFtIHtpbnRlZ2VyfSBwYXJhbXMubWluVXNlcnMgKE9wdGlvbmFsKSBUaGUgbWluaW11bSBudW1iZXIgb2YgdXNlcnMgZm9yIHRoZSB3b3JsZC4gSW5jbHVkaW5nIHRoaXMgbnVtYmVyIGFsbG93cyB5b3UgdG8gYXV0b2Fzc2lnbiBlbmQgdXNlcnMgdG8gd29ybGRzIGFuZCBlbnN1cmUgdGhhdCB0aGUgY29ycmVjdCBudW1iZXIgb2YgdXNlcnMgYXJlIGluIGVhY2ggd29ybGQuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICB1cGRhdGU6IGZ1bmN0aW9uIChwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciB3aGl0ZWxpc3QgPSBbJ3JvbGVzJywgJ29wdGlvbmFsUm9sZXMnLCAnbWluVXNlcnMnXTtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgICAgICAgICAgc2V0SWRGaWx0ZXJPclRocm93RXJyb3Iob3B0aW9ucyk7XG5cbiAgICAgICAgICAgIHZhciB1cGRhdGVPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sXG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMsXG4gICAgICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgICAgICB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpICsgc2VydmljZU9wdGlvbnMuZmlsdGVyIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHBhcmFtcyA9IF9waWNrKHBhcmFtcyB8fCB7fSwgd2hpdGVsaXN0KTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucGF0Y2gocGFyYW1zLCB1cGRhdGVPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBEZWxldGVzIGFuIGV4aXN0aW5nIHdvcmxkLlxuICAgICAgICAqXG4gICAgICAgICogVGhpcyBmdW5jdGlvbiBvcHRpb25hbGx5IHRha2VzIG9uZSBhcmd1bWVudC4gSWYgdGhlIGFyZ3VtZW50IGlzIGEgc3RyaW5nLCBpdCBpcyB0aGUgaWQgb2YgdGhlIHdvcmxkIHRvIGRlbGV0ZS4gSWYgdGhlIGFyZ3VtZW50IGlzIGFuIG9iamVjdCwgaXQgaXMgdGhlIG92ZXJyaWRlIGZvciBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAgICAqICAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAgICAgICAgKiAgICAgIHdhLmNyZWF0ZSgpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHdvcmxkKSB7XG4gICAgICAgICogICAgICAgICAgICAgICB3YS5kZWxldGUoKTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge1N0cmluZ3xPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBUaGUgaWQgb2YgdGhlIHdvcmxkIHRvIGRlbGV0ZSwgb3Igb3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZGVsZXRlOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IChvcHRpb25zICYmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ3N0cmluZycpKSA/IHsgZmlsdGVyOiBvcHRpb25zIH0gOiB7fTtcbiAgICAgICAgICAgIHNldElkRmlsdGVyT3JUaHJvd0Vycm9yKG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgZGVsZXRlT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHNlcnZpY2VPcHRpb25zLmZpbHRlciB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5kZWxldGUobnVsbCwgZGVsZXRlT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogVXBkYXRlcyB0aGUgY29uZmlndXJhdGlvbiBmb3IgdGhlIGN1cnJlbnQgaW5zdGFuY2Ugb2YgdGhlIFdvcmxkIEFQSSBBZGFwdGVyIChpbmNsdWRpbmcgYWxsIHN1YnNlcXVlbnQgZnVuY3Rpb24gY2FsbHMsIHVudGlsIHRoZSBjb25maWd1cmF0aW9uIGlzIHVwZGF0ZWQgYWdhaW4pLlxuICAgICAgICAqXG4gICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7Li4ufSkudXBkYXRlQ29uZmlnKHsgZmlsdGVyOiAnMTIzJyB9KS5hZGRVc2VyKHsgdXNlcklkOiAnMTIzJyB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IGNvbmZpZyBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgdG8gdXNlIGluIHVwZGF0aW5nIGV4aXN0aW5nIGNvbmZpZ3VyYXRpb24uXG4gICAgICAgICogQHJldHVybiB7T2JqZWN0fSByZWZlcmVuY2UgdG8gY3VycmVudCBpbnN0YW5jZVxuICAgICAgICAqL1xuICAgICAgICB1cGRhdGVDb25maWc6IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICAgICAgICAgICQuZXh0ZW5kKHNlcnZpY2VPcHRpb25zLCBjb25maWcpO1xuXG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBMaXN0cyBhbGwgd29ybGRzIGZvciBhIGdpdmVuIGFjY291bnQsIHByb2plY3QsIGFuZCBncm91cC4gQWxsIHRocmVlIGFyZSByZXF1aXJlZCwgYW5kIGlmIG5vdCBzcGVjaWZpZWQgYXMgcGFyYW1ldGVycywgYXJlIHJlYWQgZnJvbSB0aGUgc2VydmljZS5cbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAgICAqICAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAgICAgICAgKiAgICAgIHdhLmNyZWF0ZSgpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHdvcmxkKSB7XG4gICAgICAgICogICAgICAgICAgICAgICAvLyBsaXN0cyBhbGwgd29ybGRzIGluIGdyb3VwIFwidGVhbTFcIlxuICAgICAgICAqICAgICAgICAgICAgICAgd2EubGlzdCgpO1xuICAgICAgICAqXG4gICAgICAgICogICAgICAgICAgICAgICAvLyBsaXN0cyBhbGwgd29ybGRzIGluIGdyb3VwIFwib3RoZXItZ3JvdXAtbmFtZVwiXG4gICAgICAgICogICAgICAgICAgICAgICB3YS5saXN0KHsgZ3JvdXA6ICdvdGhlci1ncm91cC1uYW1lJyB9KTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGxpc3Q6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgICAgICAgICAgdmFyIGdldE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgdmFyIGZpbHRlcnMgPSBfcGljayhnZXRPcHRpb25zLCBbJ2FjY291bnQnLCAncHJvamVjdCcsICdncm91cCddKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KGZpbHRlcnMsIGdldE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIEdldHMgYWxsIHdvcmxkcyB0aGF0IGFuIGVuZCB1c2VyIGJlbG9uZ3MgdG8gZm9yIGEgZ2l2ZW4gYWNjb3VudCAodGVhbSksIHByb2plY3QsIGFuZCBncm91cC5cbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAgICAqICAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAgICAgICAgKiAgICAgIHdhLmNyZWF0ZSgpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHdvcmxkKSB7XG4gICAgICAgICogICAgICAgICAgICAgICB3YS5nZXRXb3JsZHNGb3JVc2VyKCdiMWMxOWRkYS0yZDJlLTQ3NzctYWQ1ZC0zOTI5ZjE3ZTg2ZDMnKVxuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqIFBhcmFtZXRlcnMgKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdXNlcklkIFRoZSBgdXNlcklkYCBvZiB0aGUgdXNlciB3aG9zZSB3b3JsZHMgYXJlIGJlaW5nIHJldHJpZXZlZC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldFdvcmxkc0ZvclVzZXI6IGZ1bmN0aW9uICh1c2VySWQsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgICAgICB2YXIgZ2V0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICB2YXIgZmlsdGVycyA9ICQuZXh0ZW5kKFxuICAgICAgICAgICAgICAgIF9waWNrKGdldE9wdGlvbnMsIFsnYWNjb3VudCcsICdwcm9qZWN0JywgJ2dyb3VwJ10pLFxuICAgICAgICAgICAgICAgIHsgdXNlcklkOiB1c2VySWQgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KGZpbHRlcnMsIGdldE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBMb2FkIGluZm9ybWF0aW9uIGZvciBhIHNwZWNpZmljIHdvcmxkLiBBbGwgZnVydGhlciBjYWxscyB0byB0aGUgd29ybGQgc2VydmljZSB3aWxsIHVzZSB0aGUgaWQgcHJvdmlkZWQuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSB3b3JsZElkIFRoZSBpZCBvZiB0aGUgd29ybGQgdG8gbG9hZC5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgIGxvYWQ6IGZ1bmN0aW9uICh3b3JsZElkLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBpZiAod29ybGRJZCkge1xuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IHdvcmxkSWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoIXNlcnZpY2VPcHRpb25zLmZpbHRlcikge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUGxlYXNlIHByb3ZpZGUgYSB3b3JsZGlkIHRvIGxvYWQnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucywgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHNlcnZpY2VPcHRpb25zLmZpbHRlciArICcvJyB9KTtcbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldCgnJywgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIEFkZHMgYW4gZW5kIHVzZXIgb3IgbGlzdCBvZiBlbmQgdXNlcnMgdG8gYSBnaXZlbiB3b3JsZC4gVGhlIGVuZCB1c2VyIG11c3QgYmUgYSBtZW1iZXIgb2YgdGhlIGBncm91cGAgdGhhdCBpcyBhc3NvY2lhdGVkIHdpdGggdGhpcyB3b3JsZC5cbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAgICAqICAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAgICAgICAgKiAgICAgIHdhLmNyZWF0ZSgpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHdvcmxkKSB7XG4gICAgICAgICogICAgICAgICAgICAgICAvLyBhZGQgb25lIHVzZXJcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdhLmFkZFVzZXJzKCdiMWMxOWRkYS0yZDJlLTQ3NzctYWQ1ZC0zOTI5ZjE3ZTg2ZDMnKTtcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdhLmFkZFVzZXJzKFsnYjFjMTlkZGEtMmQyZS00Nzc3LWFkNWQtMzkyOWYxN2U4NmQzJ10pO1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EuYWRkVXNlcnMoeyB1c2VySWQ6ICdiMWMxOWRkYS0yZDJlLTQ3NzctYWQ1ZC0zOTI5ZjE3ZTg2ZDMnLCByb2xlOiAnVlAgU2FsZXMnIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICAgICAgICAgICAgICAvLyBhZGQgc2V2ZXJhbCB1c2Vyc1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EuYWRkVXNlcnMoW1xuICAgICAgICAqICAgICAgICAgICAgICAgICAgIHsgdXNlcklkOiAnYTZmZTBjMWUtZjRiOC00ZjAxLTlmNWYtMDFjY2Y0YzJlZDQ0JyxcbiAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgIHJvbGU6ICdWUCBNYXJrZXRpbmcnIH0sXG4gICAgICAgICogICAgICAgICAgICAgICAgICAgeyB1c2VySWQ6ICc4ZjI2MDRjZi05NmNkLTQ0OWYtODJmYS1lMzMxNTMwNzM0ZWUnLFxuICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgcm9sZTogJ1ZQIEVuZ2luZWVyaW5nJyB9XG4gICAgICAgICogICAgICAgICAgICAgICBdKTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgICAgICAgICAgLy8gYWRkIG9uZSB1c2VyIHRvIGEgc3BlY2lmaWMgd29ybGRcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdhLmFkZFVzZXJzKCdiMWMxOWRkYS0yZDJlLTQ3NzctYWQ1ZC0zOTI5ZjE3ZTg2ZDMnLCB3b3JsZC5pZCk7XG4gICAgICAgICogICAgICAgICAgICAgICB3YS5hZGRVc2VycygnYjFjMTlkZGEtMmQyZS00Nzc3LWFkNWQtMzkyOWYxN2U4NmQzJywgeyBmaWx0ZXI6IHdvcmxkLmlkIH0pO1xuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqIFBhcmFtZXRlcnMgKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ3xvYmplY3R8YXJyYXl9IHVzZXJzIFVzZXIgaWQsIGFycmF5IG9mIHVzZXIgaWRzLCBvYmplY3QsIG9yIGFycmF5IG9mIG9iamVjdHMgb2YgdGhlIHVzZXJzIHRvIGFkZCB0byB0aGlzIHdvcmxkLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB1c2Vycy5yb2xlIFRoZSBgcm9sZWAgdGhlIHVzZXIgc2hvdWxkIGhhdmUgaW4gdGhlIHdvcmxkLiBJdCBpcyB1cCB0byB0aGUgY2FsbGVyIHRvIGVuc3VyZSwgaWYgbmVlZGVkLCB0aGF0IHRoZSBgcm9sZWAgcGFzc2VkIGluIGlzIG9uZSBvZiB0aGUgYHJvbGVzYCBvciBgb3B0aW9uYWxSb2xlc2Agb2YgdGhpcyB3b3JsZC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gd29ybGRJZCBUaGUgd29ybGQgdG8gd2hpY2ggdGhlIHVzZXJzIHNob3VsZCBiZSBhZGRlZC4gSWYgbm90IHNwZWNpZmllZCwgdGhlIGZpbHRlciBwYXJhbWV0ZXIgb2YgdGhlIGBvcHRpb25zYCBvYmplY3QgaXMgdXNlZC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGFkZFVzZXJzOiBmdW5jdGlvbiAodXNlcnMsIHdvcmxkSWQsIG9wdGlvbnMpIHtcblxuICAgICAgICAgICAgaWYgKCF1c2Vycykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUGxlYXNlIHByb3ZpZGUgYSBsaXN0IG9mIHVzZXJzIHRvIGFkZCB0byB0aGUgd29ybGQnKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gbm9ybWFsaXplIHRoZSBsaXN0IG9mIHVzZXJzIHRvIGFuIGFycmF5IG9mIHVzZXIgb2JqZWN0c1xuICAgICAgICAgICAgdXNlcnMgPSAkLm1hcChbXS5jb25jYXQodXNlcnMpLCBmdW5jdGlvbiAodSkge1xuICAgICAgICAgICAgICAgIHZhciBpc09iamVjdCA9ICQuaXNQbGFpbk9iamVjdCh1KTtcblxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdSAhPT0gJ3N0cmluZycgJiYgIWlzT2JqZWN0KSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignU29tZSBvZiB0aGUgdXNlcnMgaW4gdGhlIGxpc3QgYXJlIG5vdCBpbiB0aGUgdmFsaWQgZm9ybWF0OiAnICsgdSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuIGlzT2JqZWN0ID8gdSA6IHsgdXNlcklkOiB1IH07XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgLy8gY2hlY2sgaWYgb3B0aW9ucyB3ZXJlIHBhc3NlZCBhcyB0aGUgc2Vjb25kIHBhcmFtZXRlclxuICAgICAgICAgICAgaWYgKCQuaXNQbGFpbk9iamVjdCh3b3JsZElkKSAmJiAhb3B0aW9ucykge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMgPSB3b3JsZElkO1xuICAgICAgICAgICAgICAgIHdvcmxkSWQgPSBudWxsO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgICAgICAgICAgLy8gd2UgbXVzdCBoYXZlIG9wdGlvbnMgYnkgbm93XG4gICAgICAgICAgICBpZiAodHlwZW9mIHdvcmxkSWQgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgb3B0aW9ucy5maWx0ZXIgPSB3b3JsZElkO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzZXRJZEZpbHRlck9yVGhyb3dFcnJvcihvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIHVwZGF0ZU9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgKyAnL3VzZXJzJyB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wb3N0KHVzZXJzLCB1cGRhdGVPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBVcGRhdGVzIHRoZSByb2xlIG9mIGFuIGVuZCB1c2VyIGluIGEgZ2l2ZW4gd29ybGQuIChZb3UgY2FuIG9ubHkgdXBkYXRlIG9uZSBlbmQgdXNlciBhdCBhIHRpbWUuKVxuICAgICAgICAqXG4gICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHdhLmNyZWF0ZSgpLnRoZW4oZnVuY3Rpb24od29ybGQpIHtcbiAgICAgICAgKiAgICAgICAgICAgd2EuYWRkVXNlcnMoJ2IxYzE5ZGRhLTJkMmUtNDc3Ny1hZDVkLTM5MjlmMTdlODZkMycpO1xuICAgICAgICAqICAgICAgICAgICB3YS51cGRhdGVVc2VyKHsgdXNlcklkOiAnYjFjMTlkZGEtMmQyZS00Nzc3LWFkNWQtMzkyOWYxN2U4NmQzJywgcm9sZTogJ2xlYWRlcicgfSk7XG4gICAgICAgICogICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IHVzZXIgVXNlciBvYmplY3Qgd2l0aCBgdXNlcklkYCBhbmQgdGhlIG5ldyBgcm9sZWAuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICB1cGRhdGVVc2VyOiBmdW5jdGlvbiAodXNlciwgb3B0aW9ucykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAgICAgICAgIGlmICghdXNlciB8fCAhdXNlci51c2VySWQpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1lvdSBuZWVkIHRvIHBhc3MgYSB1c2VySWQgdG8gdXBkYXRlIGZyb20gdGhlIHdvcmxkJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHNldElkRmlsdGVyT3JUaHJvd0Vycm9yKG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgcGF0Y2hPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sXG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMsXG4gICAgICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgICAgICB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpICsgc2VydmljZU9wdGlvbnMuZmlsdGVyICsgJy91c2Vycy8nICsgdXNlci51c2VySWQgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucGF0Y2goX3BpY2sodXNlciwgJ3JvbGUnKSwgcGF0Y2hPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBSZW1vdmVzIGFuIGVuZCB1c2VyIGZyb20gYSBnaXZlbiB3b3JsZC5cbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAgICAqICAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAgICAgICAgKiAgICAgIHdhLmNyZWF0ZSgpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHdvcmxkKSB7XG4gICAgICAgICogICAgICAgICAgICAgICB3YS5hZGRVc2VycyhbJ2E2ZmUwYzFlLWY0YjgtNGYwMS05ZjVmLTAxY2NmNGMyZWQ0NCcsICc4ZjI2MDRjZi05NmNkLTQ0OWYtODJmYS1lMzMxNTMwNzM0ZWUnXSk7XG4gICAgICAgICogICAgICAgICAgICAgICB3YS5yZW1vdmVVc2VyKCdhNmZlMGMxZS1mNGI4LTRmMDEtOWY1Zi0wMWNjZjRjMmVkNDQnKTtcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdhLnJlbW92ZVVzZXIoeyB1c2VySWQ6ICc4ZjI2MDRjZi05NmNkLTQ0OWYtODJmYS1lMzMxNTMwNzM0ZWUnIH0pO1xuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqIFBhcmFtZXRlcnMgKipcbiAgICAgICAgKiBAcGFyYW0ge29iamVjdHxzdHJpbmd9IHVzZXIgVGhlIGB1c2VySWRgIG9mIHRoZSB1c2VyIHRvIHJlbW92ZSBmcm9tIHRoZSB3b3JsZCwgb3IgYW4gb2JqZWN0IGNvbnRhaW5pbmcgdGhlIGB1c2VySWRgIGZpZWxkLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgcmVtb3ZlVXNlcjogZnVuY3Rpb24gKHVzZXIsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgICAgICBpZiAodHlwZW9mIHVzZXIgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgdXNlciA9IHsgdXNlcklkOiB1c2VyIH07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghdXNlci51c2VySWQpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1lvdSBuZWVkIHRvIHBhc3MgYSB1c2VySWQgdG8gcmVtb3ZlIGZyb20gdGhlIHdvcmxkJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHNldElkRmlsdGVyT3JUaHJvd0Vycm9yKG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgZ2V0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHNlcnZpY2VPcHRpb25zLmZpbHRlciArICcvdXNlcnMvJyArIHVzZXIudXNlcklkIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLmRlbGV0ZShudWxsLCBnZXRPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBHZXRzIHRoZSBydW4gaWQgb2YgY3VycmVudCBydW4gZm9yIHRoZSBnaXZlbiB3b3JsZC4gSWYgdGhlIHdvcmxkIGRvZXMgbm90IGhhdmUgYSBydW4sIGNyZWF0ZXMgYSBuZXcgb25lIGFuZCByZXR1cm5zIHRoZSBydW4gaWQuXG4gICAgICAgICpcbiAgICAgICAgKiBSZW1lbWJlciB0aGF0IGEgW3J1bl0oLi4vLi4vZ2xvc3NhcnkvI3J1bikgaXMgYSBjb2xsZWN0aW9uIG9mIGludGVyYWN0aW9ucyB3aXRoIGEgcHJvamVjdCBhbmQgaXRzIG1vZGVsLiBJbiB0aGUgY2FzZSBvZiBtdWx0aXBsYXllciBwcm9qZWN0cywgdGhlIHJ1biBpcyBzaGFyZWQgYnkgYWxsIGVuZCB1c2VycyBpbiB0aGUgd29ybGQuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5jcmVhdGUoKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EuZ2V0Q3VycmVudFJ1bklkKHsgbW9kZWw6ICdtb2RlbC5weScgfSk7XG4gICAgICAgICogICAgICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogKiogUGFyYW1ldGVycyAqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMubW9kZWwgVGhlIG1vZGVsIGZpbGUgdG8gdXNlIHRvIGNyZWF0ZSBhIHJ1biBpZiBuZWVkZWQuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZ2V0Q3VycmVudFJ1bklkOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAgICAgICAgIHNldElkRmlsdGVyT3JUaHJvd0Vycm9yKG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgZ2V0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHNlcnZpY2VPcHRpb25zLmZpbHRlciArICcvcnVuJyB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICB2YWxpZGF0ZU1vZGVsT3JUaHJvd0Vycm9yKGdldE9wdGlvbnMpO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAucG9zdChfcGljayhnZXRPcHRpb25zLCAnbW9kZWwnKSwgZ2V0T3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogR2V0cyB0aGUgY3VycmVudCAobW9zdCByZWNlbnQpIHdvcmxkIGZvciB0aGUgZ2l2ZW4gZW5kIHVzZXIgaW4gdGhlIGdpdmVuIGdyb3VwLiBCcmluZ3MgdGhpcyBtb3N0IHJlY2VudCB3b3JsZCBpbnRvIG1lbW9yeSBpZiBuZWVkZWQuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5nZXRDdXJyZW50V29ybGRGb3JVc2VyKCc4ZjI2MDRjZi05NmNkLTQ0OWYtODJmYS1lMzMxNTMwNzM0ZWUnKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgLy8gdXNlIGRhdGEgZnJvbSB3b3JsZFxuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqIFBhcmFtZXRlcnMgKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdXNlcklkIFRoZSBgdXNlcklkYCBvZiB0aGUgdXNlciB3aG9zZSBjdXJyZW50IChtb3N0IHJlY2VudCkgd29ybGQgaXMgYmVpbmcgcmV0cmlldmVkLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBncm91cE5hbWUgKE9wdGlvbmFsKSBUaGUgbmFtZSBvZiB0aGUgZ3JvdXAuIElmIG5vdCBwcm92aWRlZCwgZGVmYXVsdHMgdG8gdGhlIGdyb3VwIHVzZWQgdG8gY3JlYXRlIHRoZSBzZXJ2aWNlLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldEN1cnJlbnRXb3JsZEZvclVzZXI6IGZ1bmN0aW9uICh1c2VySWQsIGdyb3VwTmFtZSkge1xuICAgICAgICAgICAgdmFyIGR0ZCA9ICQuRGVmZXJyZWQoKTtcbiAgICAgICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgICAgICB0aGlzLmdldFdvcmxkc0ZvclVzZXIodXNlcklkLCB7IGdyb3VwOiBncm91cE5hbWUgfSlcbiAgICAgICAgICAgICAgICAudGhlbihmdW5jdGlvbiAod29ybGRzKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGFzc3VtZSB0aGUgbW9zdCByZWNlbnQgd29ybGQgYXMgdGhlICdhY3RpdmUnIHdvcmxkXG4gICAgICAgICAgICAgICAgICAgIHdvcmxkcy5zb3J0KGZ1bmN0aW9uIChhLCBiKSB7IHJldHVybiBuZXcgRGF0ZShiLmxhc3RNb2RpZmllZCkgLSBuZXcgRGF0ZShhLmxhc3RNb2RpZmllZCk7IH0pO1xuICAgICAgICAgICAgICAgICAgICB2YXIgY3VycmVudFdvcmxkID0gd29ybGRzWzBdO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChjdXJyZW50V29ybGQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IGN1cnJlbnRXb3JsZC5pZDtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGR0ZC5yZXNvbHZlV2l0aChtZSwgW2N1cnJlbnRXb3JsZF0pO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZhaWwoZHRkLnJlamVjdCk7XG5cbiAgICAgICAgICAgIHJldHVybiBkdGQucHJvbWlzZSgpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIERlbGV0ZXMgdGhlIGN1cnJlbnQgcnVuIGZyb20gdGhlIHdvcmxkLlxuICAgICAgICAqXG4gICAgICAgICogKE5vdGUgdGhhdCB0aGUgd29ybGQgaWQgcmVtYWlucyBwYXJ0IG9mIHRoZSBydW4gcmVjb3JkLCBpbmRpY2F0aW5nIHRoYXQgdGhlIHJ1biB3YXMgZm9ybWVybHkgYW4gYWN0aXZlIHJ1biBmb3IgdGhlIHdvcmxkLilcbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAgICAqICAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgd2EuZGVsZXRlUnVuKCdzYW1wbGUtd29ybGQtaWQnKTtcbiAgICAgICAgKlxuICAgICAgICAqICAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB3b3JsZElkIFRoZSBgd29ybGRJZGAgb2YgdGhlIHdvcmxkIGZyb20gd2hpY2ggdGhlIGN1cnJlbnQgcnVuIGlzIGJlaW5nIGRlbGV0ZWQuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBkZWxldGVSdW46IGZ1bmN0aW9uICh3b3JsZElkLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgICAgICAgICAgaWYgKHdvcmxkSWQpIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zLmZpbHRlciA9IHdvcmxkSWQ7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHNldElkRmlsdGVyT3JUaHJvd0Vycm9yKG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgZGVsZXRlT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHNlcnZpY2VPcHRpb25zLmZpbHRlciArICcvcnVuJyB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5kZWxldGUobnVsbCwgZGVsZXRlT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogQ3JlYXRlcyBhIG5ldyBydW4gZm9yIHRoZSB3b3JsZC5cbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAgICAqICAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgd2EuZ2V0Q3VycmVudFdvcmxkRm9yVXNlcignOGYyNjA0Y2YtOTZjZC00NDlmLTgyZmEtZTMzMTUzMDczNGVlJylcbiAgICAgICAgKiAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKHdvcmxkKSB7XG4gICAgICAgICogICAgICAgICAgICAgICAgICAgd2EubmV3UnVuRm9yV29ybGQod29ybGQuaWQpO1xuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB3b3JsZElkIHdvcmxkSWQgaW4gd2hpY2ggd2UgY3JlYXRlIHRoZSBuZXcgcnVuLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMubW9kZWwgVGhlIG1vZGVsIGZpbGUgdG8gdXNlIHRvIGNyZWF0ZSBhIHJ1biBpZiBuZWVkZWQuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgbmV3UnVuRm9yV29ybGQ6IGZ1bmN0aW9uICh3b3JsZElkLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgY3VycmVudFJ1bk9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgZmlsdGVyOiB3b3JsZElkIHx8IHNlcnZpY2VPcHRpb25zLmZpbHRlciB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgdmFyIG1lID0gdGhpcztcblxuICAgICAgICAgICAgdmFsaWRhdGVNb2RlbE9yVGhyb3dFcnJvcihjdXJyZW50UnVuT3B0aW9ucyk7XG5cbiAgICAgICAgICAgIHJldHVybiB0aGlzLmRlbGV0ZVJ1bih3b3JsZElkLCBvcHRpb25zKVxuICAgICAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1lLmdldEN1cnJlbnRSdW5JZChjdXJyZW50UnVuT3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogQXNzaWducyBlbmQgdXNlcnMgdG8gd29ybGRzLCBjcmVhdGluZyBuZXcgd29ybGRzIGFzIGFwcHJvcHJpYXRlLCBhdXRvbWF0aWNhbGx5LiBBc3NpZ25zIGFsbCBlbmQgdXNlcnMgaW4gdGhlIGdyb3VwLCBhbmQgY3JlYXRlcyBuZXcgd29ybGRzIGFzIG5lZWRlZCBiYXNlZCBvbiB0aGUgcHJvamVjdC1sZXZlbCB3b3JsZCBjb25maWd1cmF0aW9uIChyb2xlcywgb3B0aW9uYWwgcm9sZXMsIGFuZCBtaW5pbXVtIGVuZCB1c2VycyBwZXIgd29ybGQpLlxuICAgICAgICAqXG4gICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHdhLmF1dG9Bc3NpZ24oKTtcbiAgICAgICAgKlxuICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqXG4gICAgICAgICovXG4gICAgICAgIGF1dG9Bc3NpZ246IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgICAgICAgICAgdmFyIG9wdCA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFzc2lnbm1lbnRFbmRwb2ludCkgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgdmFyIHBhcmFtcyA9IHtcbiAgICAgICAgICAgICAgICBhY2NvdW50OiBvcHQuYWNjb3VudCxcbiAgICAgICAgICAgICAgICBwcm9qZWN0OiBvcHQucHJvamVjdCxcbiAgICAgICAgICAgICAgICBncm91cDogb3B0Lmdyb3VwXG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBpZiAob3B0Lm1heFVzZXJzKSB7XG4gICAgICAgICAgICAgICAgcGFyYW1zLm1heFVzZXJzID0gb3B0Lm1heFVzZXJzO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wb3N0KHBhcmFtcywgb3B0KTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBHZXRzIHRoZSBwcm9qZWN0J3Mgd29ybGQgY29uZmlndXJhdGlvbi5cbiAgICAgICAgKlxuICAgICAgICAqIFR5cGljYWxseSwgZXZlcnkgaW50ZXJhY3Rpb24gd2l0aCB5b3VyIHByb2plY3QgdXNlcyB0aGUgc2FtZSBjb25maWd1cmF0aW9uIG9mIGVhY2ggd29ybGQuIEZvciBleGFtcGxlLCBlYWNoIHdvcmxkIGluIHlvdXIgcHJvamVjdCBwcm9iYWJseSBoYXMgdGhlIHNhbWUgcm9sZXMgZm9yIGVuZCB1c2Vycy4gQW5kIHlvdXIgcHJvamVjdCBpcyBwcm9iYWJseSBlaXRoZXIgY29uZmlndXJlZCBzbyB0aGF0IGFsbCBlbmQgdXNlcnMgc2hhcmUgdGhlIHNhbWUgd29ybGQgKGFuZCBydW4pLCBvciBzbWFsbGVyIHNldHMgb2YgZW5kIHVzZXJzIHNoYXJlIHdvcmxkcyDigJQgYnV0IG5vdCBib3RoLlxuICAgICAgICAqXG4gICAgICAgICogKFRoZSBbTXVsdGlwbGF5ZXIgUHJvamVjdCBSRVNUIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL211bHRpcGxheWVyL211bHRpcGxheWVyX3Byb2plY3QvKSBhbGxvd3MgeW91IHRvIHNldCB0aGVzZSBwcm9qZWN0LWxldmVsIHdvcmxkIGNvbmZpZ3VyYXRpb25zLiBUaGUgV29ybGQgQWRhcHRlciBzaW1wbHkgcmV0cmlldmVzIHRoZW0sIGZvciBleGFtcGxlIHNvIHRoZXkgY2FuIGJlIHVzZWQgaW4gYXV0by1hc3NpZ25tZW50IG9mIGVuZCB1c2VycyB0byB3b3JsZHMuKVxuICAgICAgICAqXG4gICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHdhLmdldFByb2plY3RTZXR0aW5ncygpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHNldHRpbmdzKSB7XG4gICAgICAgICogICAgICAgICAgICAgICBjb25zb2xlLmxvZyhzZXR0aW5ncy5yb2xlcyk7XG4gICAgICAgICogICAgICAgICAgICAgICBjb25zb2xlLmxvZyhzZXR0aW5ncy5vcHRpb25hbFJvbGVzKTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZ2V0UHJvamVjdFNldHRpbmdzOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAgICAgICAgIHZhciBvcHQgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChwcm9qZWN0RW5kcG9pbnQpIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIG9wdC51cmwgKz0gW29wdC5hY2NvdW50LCBvcHQucHJvamVjdF0uam9pbignLycpO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KG51bGwsIG9wdCk7XG4gICAgICAgIH1cblxuICAgIH07XG5cbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xufTtcbiIsIi8qKlxuICogQGNsYXNzIENvb2tpZSBTdG9yYWdlIFNlcnZpY2VcbiAqXG4gKiBAZXhhbXBsZVxuICogICAgICB2YXIgcGVvcGxlID0gcmVxdWlyZSgnY29va2llLXN0b3JlJykoeyByb290OiAncGVvcGxlJyB9KTtcbiAgICAgICAgcGVvcGxlXG4gICAgICAgICAgICAuc2F2ZSh7bGFzdE5hbWU6ICdzbWl0aCcgfSlcblxuICovXG5cblxuJ3VzZSBzdHJpY3QnO1xuXG4vLyBUaGluIGRvY3VtZW50LmNvb2tpZSB3cmFwcGVyIHRvIGFsbG93IHVuaXQgdGVzdGluZ1xudmFyIENvb2tpZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLmdldCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGRvY3VtZW50LmNvb2tpZTtcbiAgICB9O1xuXG4gICAgdGhpcy5zZXQgPSBmdW5jdGlvbiAobmV3Q29va2llKSB7XG4gICAgICAgIGRvY3VtZW50LmNvb2tpZSA9IG5ld0Nvb2tpZTtcbiAgICB9O1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdmFyIGhvc3QgPSB3aW5kb3cubG9jYXRpb24uaG9zdG5hbWU7XG4gICAgdmFyIHZhbGlkSG9zdCA9IGhvc3Quc3BsaXQoJy4nKS5sZW5ndGggPiAxO1xuICAgIHZhciBkb21haW4gPSB2YWxpZEhvc3QgPyAnLicgKyBob3N0IDogbnVsbDtcblxuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIE5hbWUgb2YgY29sbGVjdGlvblxuICAgICAgICAgKiBAdHlwZSB7IHN0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHJvb3Q6ICcvJyxcblxuICAgICAgICBkb21haW46IGRvbWFpbixcbiAgICAgICAgY29va2llOiBuZXcgQ29va2llKClcbiAgICB9O1xuICAgIHRoaXMuc2VydmljZU9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgZGVmYXVsdHMsIGNvbmZpZyk7XG5cbiAgICB2YXIgcHVibGljQVBJID0ge1xuICAgICAgICAvLyAqIFRCRFxuICAgICAgICAvLyAgKiBRdWVyeSBjb2xsZWN0aW9uOyB1c2VzIE1vbmdvREIgc3ludGF4XG4gICAgICAgIC8vICAqIEBzZWUgIDxUQkQ6IERhdGEgQVBJIFVSTD5cbiAgICAgICAgLy8gICpcbiAgICAgICAgLy8gICogQHBhcmFtIHsgc3RyaW5nfSBxcyBRdWVyeSBGaWx0ZXJcbiAgICAgICAgLy8gICogQHBhcmFtIHsgc3RyaW5nfSBsaW1pdGVycyBAc2VlIDxUQkQ6IHVybCBmb3IgbGltaXRzLCBwYWdpbmcgZXRjPlxuICAgICAgICAvLyAgKlxuICAgICAgICAvLyAgKiBAZXhhbXBsZVxuICAgICAgICAvLyAgKiAgICAgY3MucXVlcnkoXG4gICAgICAgIC8vICAqICAgICAgeyBuYW1lOiAnSm9obicsIGNsYXNzTmFtZTogJ0NTQzEwMScgfSxcbiAgICAgICAgLy8gICogICAgICB7bGltaXQ6IDEwfVxuICAgICAgICAvLyAgKiAgICAgKVxuXG4gICAgICAgIC8vIHF1ZXJ5OiBmdW5jdGlvbiAocXMsIGxpbWl0ZXJzKSB7XG5cbiAgICAgICAgLy8gfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogU2F2ZSBjb29raWUgdmFsdWVcbiAgICAgICAgICogQHBhcmFtICB7IHN0cmluZ3xPYmplY3R9IGtleSAgIElmIGdpdmVuIGEga2V5IHNhdmUgdmFsdWVzIHVuZGVyIGl0LCBpZiBnaXZlbiBhbiBvYmplY3QgZGlyZWN0bHksIHNhdmUgdG8gdG9wLWxldmVsIGFwaVxuICAgICAgICAgKiBAcGFyYW0gIHtPYmplY3R9IHZhbHVlIChPcHRpb25hbClcbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgT3ZlcnJpZGVzIGZvciBzZXJ2aWNlIG9wdGlvbnNcbiAgICAgICAgICpcbiAgICAgICAgICogQHJldHVybiB7Kn0gVGhlIHNhdmVkIHZhbHVlXG4gICAgICAgICAqXG4gICAgICAgICAqIEBleGFtcGxlXG4gICAgICAgICAqICAgICBjcy5zZXQoJ3BlcnNvbicsIHsgZmlyc3ROYW1lOiAnam9obicsIGxhc3ROYW1lOiAnc21pdGgnIH0pO1xuICAgICAgICAgKiAgICAgY3Muc2V0KHsgbmFtZTonc21pdGgnLCBhZ2U6JzMyJyB9KTtcbiAgICAgICAgICovXG4gICAgICAgIHNldDogZnVuY3Rpb24gKGtleSwgdmFsdWUsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBzZXRPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHRoaXMuc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgZG9tYWluID0gc2V0T3B0aW9ucy5kb21haW47XG4gICAgICAgICAgICB2YXIgcGF0aCA9IHNldE9wdGlvbnMucm9vdDtcbiAgICAgICAgICAgIHZhciBjb29raWUgPSBzZXRPcHRpb25zLmNvb2tpZTtcblxuICAgICAgICAgICAgY29va2llLnNldChlbmNvZGVVUklDb21wb25lbnQoa2V5KSArICc9JyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuY29kZVVSSUNvbXBvbmVudCh2YWx1ZSkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZG9tYWluID8gJzsgZG9tYWluPScgKyBkb21haW4gOiAnJykgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAocGF0aCA/ICc7IHBhdGg9JyArIHBhdGggOiAnJylcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTG9hZCBjb29raWUgdmFsdWVcbiAgICAgICAgICogQHBhcmFtICB7IHN0cmluZ3xPYmplY3R9IGtleSAgIElmIGdpdmVuIGEga2V5IHNhdmUgdmFsdWVzIHVuZGVyIGl0LCBpZiBnaXZlbiBhbiBvYmplY3QgZGlyZWN0bHksIHNhdmUgdG8gdG9wLWxldmVsIGFwaVxuICAgICAgICAgKiBAcmV0dXJuIHsqfSBUaGUgdmFsdWUgc3RvcmVkXG4gICAgICAgICAqXG4gICAgICAgICAqIEBleGFtcGxlXG4gICAgICAgICAqICAgICBjcy5nZXQoJ3BlcnNvbicpO1xuICAgICAgICAgKi9cbiAgICAgICAgZ2V0OiBmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgICAgICB2YXIgY29va2llID0gdGhpcy5zZXJ2aWNlT3B0aW9ucy5jb29raWU7XG4gICAgICAgICAgICB2YXIgY29va2llUmVnID0gbmV3IFJlZ0V4cCgnKD86Xnw7KVxcXFxzKicgKyBlbmNvZGVVUklDb21wb25lbnQoa2V5KS5yZXBsYWNlKC9bXFwtXFwuXFwrXFwqXS9nLCAnXFxcXCQmJykgKyAnXFxcXHMqXFxcXD1cXFxccyooW147XSopLiokJyk7XG4gICAgICAgICAgICB2YXIgcmVzID0gY29va2llUmVnLmV4ZWMoY29va2llLmdldCgpKTtcbiAgICAgICAgICAgIHZhciB2YWwgPSByZXMgPyBkZWNvZGVVUklDb21wb25lbnQocmVzWzFdKSA6IG51bGw7XG4gICAgICAgICAgICByZXR1cm4gdmFsO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZW1vdmVzIGtleSBmcm9tIGNvbGxlY3Rpb25cbiAgICAgICAgICogQHBhcmFtIHsgc3RyaW5nfSBrZXkga2V5IHRvIHJlbW92ZVxuICAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAob3B0aW9uYWwpIG92ZXJyaWRlcyBmb3Igc2VydmljZSBvcHRpb25zXG4gICAgICAgICAqIEByZXR1cm4geyBzdHJpbmd9IGtleSBUaGUga2V5IHJlbW92ZWRcbiAgICAgICAgICpcbiAgICAgICAgICogQGV4YW1wbGVcbiAgICAgICAgICogICAgIGNzLnJlbW92ZSgncGVyc29uJyk7XG4gICAgICAgICAqL1xuICAgICAgICByZW1vdmU6IGZ1bmN0aW9uIChrZXksIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciByZW1PcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHRoaXMuc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgZG9tYWluID0gcmVtT3B0aW9ucy5kb21haW47XG4gICAgICAgICAgICB2YXIgcGF0aCA9IHJlbU9wdGlvbnMucm9vdDtcbiAgICAgICAgICAgIHZhciBjb29raWUgPSByZW1PcHRpb25zLmNvb2tpZTtcblxuICAgICAgICAgICAgY29va2llLnNldChlbmNvZGVVUklDb21wb25lbnQoa2V5KSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJz07IGV4cGlyZXM9VGh1LCAwMSBKYW4gMTk3MCAwMDowMDowMCBHTVQnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZG9tYWluID8gJzsgZG9tYWluPScgKyBkb21haW4gOiAnJykgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIChwYXRoID8gJzsgcGF0aD0nICsgcGF0aCA6ICcnKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHJldHVybiBrZXk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJlbW92ZXMgY29sbGVjdGlvbiBiZWluZyByZWZlcmVuY2VkXG4gICAgICAgICAqIEByZXR1cm4geyBhcnJheX0ga2V5cyBBbGwgdGhlIGtleXMgcmVtb3ZlZFxuICAgICAgICAgKi9cbiAgICAgICAgZGVzdHJveTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGNvb2tpZSA9IHRoaXMuc2VydmljZU9wdGlvbnMuY29va2llO1xuICAgICAgICAgICAgdmFyIGFLZXlzID0gY29va2llLmdldCgpLnJlcGxhY2UoLygoPzpefFxccyo7KVteXFw9XSspKD89O3wkKXxeXFxzKnxcXHMqKD86XFw9W147XSopPyg/OlxcMXwkKS9nLCAnJykuc3BsaXQoL1xccyooPzpcXD1bXjtdKik/O1xccyovKTtcbiAgICAgICAgICAgIGZvciAodmFyIG5JZHggPSAwOyBuSWR4IDwgYUtleXMubGVuZ3RoOyBuSWR4KyspIHtcbiAgICAgICAgICAgICAgICB2YXIgY29va2llS2V5ID0gZGVjb2RlVVJJQ29tcG9uZW50KGFLZXlzW25JZHhdKTtcbiAgICAgICAgICAgICAgICB0aGlzLnJlbW92ZShjb29raWVLZXkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGFLZXlzO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FQSSk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIga2V5TmFtZXMgPSByZXF1aXJlKCcuLi9tYW5hZ2Vycy9rZXktbmFtZXMnKTtcbnZhciBTdG9yYWdlRmFjdG9yeSA9IHJlcXVpcmUoJy4vc3RvcmUtZmFjdG9yeScpO1xudmFyIG9wdGlvblV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbC9vcHRpb24tdXRpbHMnKTtcblxudmFyIEVQSV9TRVNTSU9OX0tFWSA9IGtleU5hbWVzLkVQSV9TRVNTSU9OX0tFWTtcbnZhciBkZWZhdWx0cyA9IHtcbiAgICAvKipcbiAgICAgKiBXaGVyZSB0byBzdG9yZSB1c2VyIGFjY2VzcyB0b2tlbnMgZm9yIHRlbXBvcmFyeSBhY2Nlc3MuIERlZmF1bHRzIHRvIHN0b3JpbmcgaW4gYSBjb29raWUgaW4gdGhlIGJyb3dzZXIuXG4gICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgKi9cbiAgICBzdG9yZTogeyBzeW5jaHJvbm91czogdHJ1ZSB9XG59O1xuXG52YXIgU2Vzc2lvbk1hbmFnZXIgPSBmdW5jdGlvbiAobWFuYWdlck9wdGlvbnMpIHtcbiAgICBtYW5hZ2VyT3B0aW9ucyA9IG1hbmFnZXJPcHRpb25zIHx8IHt9O1xuICAgIGZ1bmN0aW9uIGdldEJhc2VPcHRpb25zKG92ZXJyaWRlcykge1xuICAgICAgICBvdmVycmlkZXMgPSBvdmVycmlkZXMgfHwge307XG4gICAgICAgIHZhciBsaWJPcHRpb25zID0gb3B0aW9uVXRpbHMuZ2V0T3B0aW9ucygpO1xuICAgICAgICB2YXIgZmluYWxPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIGRlZmF1bHRzLCBsaWJPcHRpb25zLCBtYW5hZ2VyT3B0aW9ucywgb3ZlcnJpZGVzKTtcbiAgICAgICAgcmV0dXJuIGZpbmFsT3B0aW9ucztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRTdG9yZShvdmVycmlkZXMpIHtcbiAgICAgICAgdmFyIGJhc2VPcHRpb25zID0gZ2V0QmFzZU9wdGlvbnMob3ZlcnJpZGVzKTtcbiAgICAgICAgdmFyIHN0b3JlT3B0cyA9IGJhc2VPcHRpb25zLnN0b3JlIHx8IHt9O1xuICAgICAgICB2YXIgaXNFcGljZW50ZXJEb21haW4gPSAhYmFzZU9wdGlvbnMuaXNMb2NhbCAmJiAhYmFzZU9wdGlvbnMuaXNDdXN0b21Eb21haW47XG4gICAgICAgIGlmIChzdG9yZU9wdHMucm9vdCA9PT0gdW5kZWZpbmVkICYmIGJhc2VPcHRpb25zLmFjY291bnQgJiYgYmFzZU9wdGlvbnMucHJvamVjdCAmJiBpc0VwaWNlbnRlckRvbWFpbikge1xuICAgICAgICAgICAgc3RvcmVPcHRzLnJvb3QgPSAnL2FwcC8nICsgYmFzZU9wdGlvbnMuYWNjb3VudCArICcvJyArIGJhc2VPcHRpb25zLnByb2plY3Q7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ldyBTdG9yYWdlRmFjdG9yeShzdG9yZU9wdHMpO1xuICAgIH1cblxuICAgIHZhciBwdWJsaWNBUEkgPSB7XG4gICAgICAgIHNhdmVTZXNzaW9uOiBmdW5jdGlvbiAodXNlckluZm8sIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBzZXJpYWxpemVkID0gSlNPTi5zdHJpbmdpZnkodXNlckluZm8pO1xuICAgICAgICAgICAgZ2V0U3RvcmUob3B0aW9ucykuc2V0KEVQSV9TRVNTSU9OX0tFWSwgc2VyaWFsaXplZCk7XG4gICAgICAgIH0sXG4gICAgICAgIGdldFNlc3Npb246IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgICAgICAvLyB2YXIgc2Vzc2lvbiA9IGdldFN0b3JlKG9wdGlvbnMpLmdldChFUElfU0VTU0lPTl9LRVkpIHx8ICd7fSc7XG4gICAgICAgICAgICAvLyByZXR1cm4gSlNPTi5wYXJzZShzZXNzaW9uKTtcbiAgICAgICAgICAgIHZhciBzdG9yZSA9IGdldFN0b3JlKG9wdGlvbnMpO1xuICAgICAgICAgICAgdmFyIGZpbmFsT3B0cyA9IHN0b3JlLnNlcnZpY2VPcHRpb25zO1xuICAgICAgICAgICAgdmFyIHNlcmlhbGl6ZWQgPSBzdG9yZS5nZXQoRVBJX1NFU1NJT05fS0VZKSB8fCAne30nO1xuICAgICAgICAgICAgdmFyIHNlc3Npb24gPSBKU09OLnBhcnNlKHNlcmlhbGl6ZWQpO1xuICAgICAgICAgICAgLy8gSWYgdGhlIHVybCBjb250YWlucyB0aGUgcHJvamVjdCBhbmQgYWNjb3VudFxuICAgICAgICAgICAgLy8gdmFsaWRhdGUgdGhlIGFjY291bnQgYW5kIHByb2plY3QgaW4gdGhlIHNlc3Npb25cbiAgICAgICAgICAgIC8vIGFuZCBvdmVycmlkZSBwcm9qZWN0LCBncm91cE5hbWUsIGdyb3VwSWQgYW5kIGlzRmFjXG4gICAgICAgICAgICAvLyBPdGhlcndpc2UgKGkuZS4gbG9jYWxob3N0KSB1c2UgdGhlIHNhdmVkIHNlc3Npb24gdmFsdWVzXG4gICAgICAgICAgICB2YXIgYWNjb3VudCA9IGZpbmFsT3B0cy5hY2NvdW50O1xuICAgICAgICAgICAgdmFyIHByb2plY3QgPSBmaW5hbE9wdHMucHJvamVjdDtcbiAgICAgICAgICAgIGlmIChhY2NvdW50ICYmIHNlc3Npb24uYWNjb3VudCAhPT0gYWNjb3VudCkge1xuICAgICAgICAgICAgICAgIC8vIFRoaXMgbWVhbnMgdGhhdCB0aGUgdG9rZW4gd2FzIG5vdCB1c2VkIHRvIGxvZ2luIHRvIHRoZSBzYW1lIGFjY291bnRcbiAgICAgICAgICAgICAgICByZXR1cm4ge307XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoc2Vzc2lvbi5ncm91cHMgJiYgYWNjb3VudCAmJiBwcm9qZWN0KSB7XG4gICAgICAgICAgICAgICAgdmFyIGdyb3VwID0gc2Vzc2lvbi5ncm91cHNbcHJvamVjdF0gfHwgeyBncm91cElkOiAnJywgZ3JvdXBOYW1lOiAnJywgaXNGYWM6IGZhbHNlIH07XG4gICAgICAgICAgICAgICAgJC5leHRlbmQoc2Vzc2lvbiwgeyBwcm9qZWN0OiBwcm9qZWN0IH0sIGdyb3VwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBzZXNzaW9uO1xuICAgICAgICB9LFxuICAgICAgICByZW1vdmVTZXNzaW9uOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIHN0b3JlID0gZ2V0U3RvcmUob3B0aW9ucyk7XG4gICAgICAgICAgICBPYmplY3Qua2V5cyhrZXlOYW1lcykuZm9yRWFjaChmdW5jdGlvbiAoY29va2llS2V5KSB7XG4gICAgICAgICAgICAgICAgdmFyIGNvb2tpZU5hbWUgPSBrZXlOYW1lc1tjb29raWVLZXldO1xuICAgICAgICAgICAgICAgIHN0b3JlLnJlbW92ZShjb29raWVOYW1lKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH0sXG4gICAgICAgIGdldFN0b3JlOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIGdldFN0b3JlKG9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIGdldE1lcmdlZE9wdGlvbnM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICAgICAgICAgIHZhciBvdmVycmlkZXMgPSAkLmV4dGVuZC5hcHBseSgkLCBbdHJ1ZSwge31dLmNvbmNhdChhcmdzKSk7XG4gICAgICAgICAgICB2YXIgYmFzZU9wdGlvbnMgPSBnZXRCYXNlT3B0aW9ucyhvdmVycmlkZXMpO1xuICAgICAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLmdldFNlc3Npb24ob3ZlcnJpZGVzKTtcblxuICAgICAgICAgICAgdmFyIHNlc3Npb25EZWZhdWx0cyA9IHtcbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBGb3IgcHJvamVjdHMgdGhhdCByZXF1aXJlIGF1dGhlbnRpY2F0aW9uLCBwYXNzIGluIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiAoZGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nKS4gSWYgdGhlIHVzZXIgaXMgYWxyZWFkeSBsb2dnZWQgaW4gdG8gRXBpY2VudGVyLCB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gaXMgYWxyZWFkeSBzZXQgaW4gYSBjb29raWUgYW5kIGF1dG9tYXRpY2FsbHkgbG9hZGVkIGZyb20gdGhlcmUuIChTZWUgW21vcmUgYmFja2dyb3VuZCBvbiBhY2Nlc3MgdG9rZW5zXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pKS5cbiAgICAgICAgICAgICAgICAgKiBAc2VlIFtBdXRoZW50aWNhdGlvbiBBUEkgU2VydmljZV0oLi4vYXV0aC1hcGktc2VydmljZS8pIGZvciBnZXR0aW5nIHRva2Vucy5cbiAgICAgICAgICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIHRva2VuOiBzZXNzaW9uLmF1dGhfdG9rZW4sXG5cbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBUaGUgYWNjb3VudC4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIGNvb2tpZSBzZXNzaW9uLlxuICAgICAgICAgICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgYWNjb3VudDogc2Vzc2lvbi5hY2NvdW50LFxuXG4gICAgICAgICAgICAgICAgLyoqXG4gICAgICAgICAgICAgICAgICogVGhlIHByb2plY3QuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBjb29raWUgc2Vzc2lvbi5cbiAgICAgICAgICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIHByb2plY3Q6IHNlc3Npb24ucHJvamVjdCxcblxuXG4gICAgICAgICAgICAgICAgLyoqXG4gICAgICAgICAgICAgICAgICogVGhlIGdyb3VwIG5hbWUuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBjb29raWUgc2Vzc2lvbi5cbiAgICAgICAgICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIGdyb3VwOiBzZXNzaW9uLmdyb3VwTmFtZSxcbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBBbGlhcyBmb3IgZ3JvdXAuIFxuICAgICAgICAgICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgZ3JvdXBOYW1lOiBzZXNzaW9uLmdyb3VwTmFtZSwgLy9JdCdzIGEgbGl0dGxlIHdlaXJkIHRoYXQgaXQncyBjYWxsZWQgZ3JvdXBOYW1lIGluIHRoZSBjb29raWUsIGJ1dCAnZ3JvdXAnIGluIGFsbCB0aGUgc2VydmljZSBvcHRpb25zLCBzbyBub3JtYWxpemUgZm9yIGJvdGhcbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBUaGUgZ3JvdXAgaWQuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBjb29raWUgc2Vzc2lvbi5cbiAgICAgICAgICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIGdyb3VwSWQ6IHNlc3Npb24uZ3JvdXBJZCxcbiAgICAgICAgICAgICAgICB1c2VySWQ6IHNlc3Npb24udXNlcklkXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmV0dXJuICQuZXh0ZW5kKHRydWUsIHNlc3Npb25EZWZhdWx0cywgYmFzZU9wdGlvbnMpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBTZXNzaW9uTWFuYWdlcjsiLCIvKipcbiAgICBEZWNpZGVzIHR5cGUgb2Ygc3RvcmUgdG8gcHJvdmlkZVxuKi9cblxuJ3VzZSBzdHJpY3QnO1xuLy8gdmFyIGlzTm9kZSA9IGZhbHNlOyBGSVhNRTogQnJvd3NlcmlmeS9taW5pZnlpZnkgaGFzIGlzc3VlcyB3aXRoIHRoZSBuZXh0IGxpbmtcbi8vIHZhciBzdG9yZSA9IChpc05vZGUpID8gcmVxdWlyZSgnLi9zZXNzaW9uLXN0b3JlJykgOiByZXF1aXJlKCcuL2Nvb2tpZS1zdG9yZScpO1xudmFyIHN0b3JlID0gcmVxdWlyZSgnLi9jb29raWUtc3RvcmUnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBzdG9yZTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHF1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWwvcXVlcnktdXRpbCcpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcblxuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgdXJsOiAnJyxcblxuICAgICAgICBjb250ZW50VHlwZTogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgICBoZWFkZXJzOiB7fSxcbiAgICAgICAgc3RhdHVzQ29kZToge1xuICAgICAgICAgICAgNDA0OiAkLm5vb3BcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT05MWSBmb3Igc3RyaW5ncyBpbiB0aGUgdXJsLiBBbGwgR0VUICYgREVMRVRFIHBhcmFtcyBhcmUgcnVuIHRocm91Z2ggdGhpc1xuICAgICAgICAgKiBAdHlwZSB7W3R5cGVdIH1cbiAgICAgICAgICovXG4gICAgICAgIHBhcmFtZXRlclBhcnNlcjogcXV0aWxzLnRvUXVlcnlGb3JtYXQsXG5cbiAgICAgICAgLy8gVG8gYWxsb3cgZXBpY2VudGVyLnRva2VuIGFuZCBvdGhlciBzZXNzaW9uIGNvb2tpZXMgdG8gYmUgcGFzc2VkXG4gICAgICAgIC8vIHdpdGggdGhlIHJlcXVlc3RzXG4gICAgICAgIHhockZpZWxkczoge1xuICAgICAgICAgICAgd2l0aENyZWRlbnRpYWxzOiB0cnVlXG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgdmFyIHRyYW5zcG9ydE9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgZGVmYXVsdHMsIGNvbmZpZyk7XG5cbiAgICB2YXIgcmVzdWx0ID0gZnVuY3Rpb24gKGQpIHtcbiAgICAgICAgcmV0dXJuICgkLmlzRnVuY3Rpb24oZCkpID8gZCgpIDogZDtcbiAgICB9O1xuXG4gICAgdmFyIGNvbm5lY3QgPSBmdW5jdGlvbiAobWV0aG9kLCBwYXJhbXMsIGNvbm5lY3RPcHRpb25zKSB7XG4gICAgICAgIHBhcmFtcyA9IHJlc3VsdChwYXJhbXMpO1xuICAgICAgICBwYXJhbXMgPSAoJC5pc1BsYWluT2JqZWN0KHBhcmFtcykgfHwgJC5pc0FycmF5KHBhcmFtcykpID8gSlNPTi5zdHJpbmdpZnkocGFyYW1zKSA6IHBhcmFtcztcblxuICAgICAgICB2YXIgb3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCB0cmFuc3BvcnRPcHRpb25zLCBjb25uZWN0T3B0aW9ucywge1xuICAgICAgICAgICAgdHlwZTogbWV0aG9kLFxuICAgICAgICAgICAgZGF0YTogcGFyYW1zXG4gICAgICAgIH0pO1xuICAgICAgICB2YXIgQUxMT1dFRF9UT19CRV9GVU5DVElPTlMgPSBbJ2RhdGEnLCAndXJsJ107XG4gICAgICAgICQuZWFjaChvcHRpb25zLCBmdW5jdGlvbiAoa2V5LCB2YWx1ZSkge1xuICAgICAgICAgICAgaWYgKCQuaXNGdW5jdGlvbih2YWx1ZSkgJiYgJC5pbkFycmF5KGtleSwgQUxMT1dFRF9UT19CRV9GVU5DVElPTlMpICE9PSAtMSkge1xuICAgICAgICAgICAgICAgIG9wdGlvbnNba2V5XSA9IHZhbHVlKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmIChvcHRpb25zLmxvZ0xldmVsICYmIG9wdGlvbnMubG9nTGV2ZWwgPT09ICdERUJVRycpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKG9wdGlvbnMudXJsKTtcbiAgICAgICAgICAgIHZhciBvbGRTdWNjZXNzRm4gPSBvcHRpb25zLnN1Y2Nlc3MgfHwgJC5ub29wO1xuICAgICAgICAgICAgb3B0aW9ucy5zdWNjZXNzID0gZnVuY3Rpb24gKHJlc3BvbnNlLCBhamF4U3RhdHVzLCBhamF4UmVxKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2cocmVzcG9uc2UpO1xuICAgICAgICAgICAgICAgIG9sZFN1Y2Nlc3NGbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBiZWZvcmVTZW5kID0gb3B0aW9ucy5iZWZvcmVTZW5kO1xuICAgICAgICBvcHRpb25zLmJlZm9yZVNlbmQgPSBmdW5jdGlvbiAoeGhyLCBzZXR0aW5ncykge1xuICAgICAgICAgICAgeGhyLnJlcXVlc3RVcmwgPSAoY29ubmVjdE9wdGlvbnMgfHwge30pLnVybDtcbiAgICAgICAgICAgIGlmIChiZWZvcmVTZW5kKSB7XG4gICAgICAgICAgICAgICAgYmVmb3JlU2VuZC5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIHJldHVybiAkLmFqYXgob3B0aW9ucyk7XG4gICAgfTtcblxuICAgIHZhciBwdWJsaWNBUEkgPSB7XG4gICAgICAgIGdldDogZnVuY3Rpb24gKHBhcmFtcywgYWpheE9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBvcHRpb25zID0gJC5leHRlbmQoe30sIHRyYW5zcG9ydE9wdGlvbnMsIGFqYXhPcHRpb25zKTtcbiAgICAgICAgICAgIHBhcmFtcyA9IG9wdGlvbnMucGFyYW1ldGVyUGFyc2VyKHJlc3VsdChwYXJhbXMpKTtcbiAgICAgICAgICAgIHJldHVybiBjb25uZWN0LmNhbGwodGhpcywgJ0dFVCcsIHBhcmFtcywgb3B0aW9ucyk7XG4gICAgICAgIH0sXG4gICAgICAgIHNwbGl0R2V0OiBmdW5jdGlvbiAoKSB7XG5cbiAgICAgICAgfSxcbiAgICAgICAgcG9zdDogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3QuYXBwbHkodGhpcywgWydwb3N0J10uY29uY2F0KFtdLnNsaWNlLmNhbGwoYXJndW1lbnRzKSkpO1xuICAgICAgICB9LFxuICAgICAgICBwYXRjaDogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3QuYXBwbHkodGhpcywgWydwYXRjaCddLmNvbmNhdChbXS5zbGljZS5jYWxsKGFyZ3VtZW50cykpKTtcbiAgICAgICAgfSxcbiAgICAgICAgcHV0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gY29ubmVjdC5hcHBseSh0aGlzLCBbJ3B1dCddLmNvbmNhdChbXS5zbGljZS5jYWxsKGFyZ3VtZW50cykpKTtcbiAgICAgICAgfSxcbiAgICAgICAgZGVsZXRlOiBmdW5jdGlvbiAocGFyYW1zLCBhamF4T3B0aW9ucykge1xuICAgICAgICAgICAgLy9ERUxFVEUgZG9lc24ndCBzdXBwb3J0IGJvZHkgcGFyYW1zLCBidXQgalF1ZXJ5IHRoaW5rcyBpdCBkb2VzLlxuICAgICAgICAgICAgdmFyIG9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgdHJhbnNwb3J0T3B0aW9ucywgYWpheE9wdGlvbnMpO1xuICAgICAgICAgICAgcGFyYW1zID0gb3B0aW9ucy5wYXJhbWV0ZXJQYXJzZXIocmVzdWx0KHBhcmFtcykpO1xuICAgICAgICAgICAgaWYgKCQudHJpbShwYXJhbXMpKSB7XG4gICAgICAgICAgICAgICAgdmFyIGRlbGltaXRlciA9IChyZXN1bHQob3B0aW9ucy51cmwpLmluZGV4T2YoJz8nKSA9PT0gLTEpID8gJz8nIDogJyYnO1xuICAgICAgICAgICAgICAgIG9wdGlvbnMudXJsID0gcmVzdWx0KG9wdGlvbnMudXJsKSArIGRlbGltaXRlciArIHBhcmFtcztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBjb25uZWN0LmNhbGwodGhpcywgJ0RFTEVURScsIG51bGwsIG9wdGlvbnMpO1xuICAgICAgICB9LFxuICAgICAgICBoZWFkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gY29ubmVjdC5hcHBseSh0aGlzLCBbJ2hlYWQnXS5jb25jYXQoW10uc2xpY2UuY2FsbChhcmd1bWVudHMpKSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9wdGlvbnM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiBjb25uZWN0LmFwcGx5KHRoaXMsIFsnb3B0aW9ucyddLmNvbmNhdChbXS5zbGljZS5jYWxsKGFyZ3VtZW50cykpKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICByZXR1cm4gJC5leHRlbmQodGhpcywgcHVibGljQVBJKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbi8vIHZhciBpc05vZGUgPSBmYWxzZTsgRklYTUU6IEJyb3dzZXJpZnkvbWluaWZ5aWZ5IGhhcyBpc3N1ZXMgd2l0aCB0aGUgbmV4dCBsaW5rXG4vLyB2YXIgdHJhbnNwb3J0ID0gKGlzTm9kZSkgPyByZXF1aXJlKCcuL25vZGUtaHR0cC10cmFuc3BvcnQnKSA6IHJlcXVpcmUoJy4vYWpheC1odHRwLXRyYW5zcG9ydCcpO1xudmFyIHRyYW5zcG9ydCA9IHJlcXVpcmUoJy4vYWpheC1odHRwLXRyYW5zcG9ydCcpO1xubW9kdWxlLmV4cG9ydHMgPSB0cmFuc3BvcnQ7XG4iLCIvKipcbi8qIEluaGVyaXQgZnJvbSBhIGNsYXNzICh1c2luZyBwcm90b3R5cGUgYm9ycm93aW5nKVxuKi9cbid1c2Ugc3RyaWN0JztcblxuZnVuY3Rpb24gaW5oZXJpdChDLCBQKSB7XG4gICAgdmFyIEYgPSBmdW5jdGlvbiAoKSB7fTtcbiAgICBGLnByb3RvdHlwZSA9IFAucHJvdG90eXBlO1xuICAgIEMucHJvdG90eXBlID0gbmV3IEYoKTtcbiAgICBDLl9fc3VwZXIgPSBQLnByb3RvdHlwZTtcbiAgICBDLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEM7XG59XG5cbi8qKlxuKiBTaGFsbG93IGNvcHkgb2YgYW4gb2JqZWN0XG4qIEBwYXJhbSB7T2JqZWN0fSBkZXN0IG9iamVjdCB0byBleHRlbmRcbiogQHJldHVybiB7T2JqZWN0fSBleHRlbmRlZCBvYmplY3RcbiovXG52YXIgZXh0ZW5kID0gZnVuY3Rpb24gKGRlc3QgLyosIHZhcl9hcmdzKi8pIHtcbiAgICB2YXIgb2JqID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKTtcbiAgICB2YXIgY3VycmVudDtcbiAgICBmb3IgKHZhciBqID0gMDsgaiA8IG9iai5sZW5ndGg7IGorKykge1xuICAgICAgICBpZiAoIShjdXJyZW50ID0gb2JqW2pdKSkgeyAvL2VzbGludC1kaXNhYmxlLWxpbmVcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gZG8gbm90IHdyYXAgaW5uZXIgaW4gZGVzdC5oYXNPd25Qcm9wZXJ0eSBvciBiYWQgdGhpbmdzIHdpbGwgaGFwcGVuXG4gICAgICAgIGZvciAodmFyIGtleSBpbiBjdXJyZW50KSB7IC8vZXNsaW50LWRpc2FibGUtbGluZVxuICAgICAgICAgICAgZGVzdFtrZXldID0gY3VycmVudFtrZXldO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGRlc3Q7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChiYXNlLCBwcm9wcywgc3RhdGljUHJvcHMpIHtcbiAgICB2YXIgcGFyZW50ID0gYmFzZTtcbiAgICB2YXIgY2hpbGQ7XG5cbiAgICBjaGlsZCA9IHByb3BzICYmIHByb3BzLmhhc093blByb3BlcnR5KCdjb25zdHJ1Y3RvcicpID8gcHJvcHMuY29uc3RydWN0b3IgOiBmdW5jdGlvbiAoKSB7IHJldHVybiBwYXJlbnQuYXBwbHkodGhpcywgYXJndW1lbnRzKTsgfTtcblxuICAgIC8vIGFkZCBzdGF0aWMgcHJvcGVydGllcyB0byB0aGUgY2hpbGQgY29uc3RydWN0b3IgZnVuY3Rpb25cbiAgICBleHRlbmQoY2hpbGQsIHBhcmVudCwgc3RhdGljUHJvcHMpO1xuXG4gICAgLy8gYXNzb2NpYXRlIHByb3RvdHlwZSBjaGFpblxuICAgIGluaGVyaXQoY2hpbGQsIHBhcmVudCk7XG5cbiAgICAvLyBhZGQgaW5zdGFuY2UgcHJvcGVydGllc1xuICAgIGlmIChwcm9wcykge1xuICAgICAgICBleHRlbmQoY2hpbGQucHJvdG90eXBlLCBwcm9wcyk7XG4gICAgfVxuXG4gICAgLy8gZG9uZVxuICAgIHJldHVybiBjaGlsZDtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIF9waWNrOiBmdW5jdGlvbiAob2JqLCBwcm9wcykge1xuICAgICAgICB2YXIgcmVzID0ge307XG4gICAgICAgIGZvciAodmFyIHAgaW4gb2JqKSB7XG4gICAgICAgICAgICBpZiAocHJvcHMuaW5kZXhPZihwKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICByZXNbcF0gPSBvYmpbcF07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gcmVzO1xuICAgIH1cbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi4vc2VydmljZS9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcblxudmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKCkuZ2V0KCdzZXJ2ZXInKTtcbnZhciBjdXN0b21EZWZhdWx0cyA9IHt9O1xudmFyIGxpYkRlZmF1bHRzID0ge1xuICAgIC8qKlxuICAgICAqIFRoZSBhY2NvdW50IGlkLiBJbiB0aGUgRXBpY2VudGVyIFVJLCB0aGlzIGlzIHRoZSAqKlRlYW0gSUQqKiAoZm9yIHRlYW0gcHJvamVjdHMpIG9yICoqVXNlciBJRCoqIChmb3IgcGVyc29uYWwgcHJvamVjdHMpLiBEZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBVUkwuXG4gICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgKi9cbiAgICBhY2NvdW50OiB1cmxDb25maWcuYWNjb3VudFBhdGggfHwgdW5kZWZpbmVkLFxuICAgIC8qKlxuICAgICAqIFRoZSBhY2NvdW50IGlkLiBJbiB0aGUgRXBpY2VudGVyIFVJLCB0aGlzIGlzIHRoZSAqKlRlYW0gSUQqKiAoZm9yIHRlYW0gcHJvamVjdHMpIG9yICoqVXNlciBJRCoqIChmb3IgcGVyc29uYWwgcHJvamVjdHMpLiBEZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBVUkwuXG4gICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgKi9cbiAgICBwcm9qZWN0OiB1cmxDb25maWcucHJvamVjdFBhdGggfHwgdW5kZWZpbmVkLFxuICAgIGlzTG9jYWw6IHVybENvbmZpZy5pc0xvY2FsaG9zdCgpLFxuICAgIGlzQ3VzdG9tRG9tYWluOiB1cmxDb25maWcuaXNDdXN0b21Eb21haW4sXG4gICAgc3RvcmU6IHt9XG59O1xuXG52YXIgb3B0aW9uVXRpbHMgPSB7XG4gICAgLyoqXG4gICAgICogR2V0cyB0aGUgZmluYWwgb3B0aW9ucyBieSBvdmVycmlkaW5nIHRoZSBnbG9iYWwgb3B0aW9ucyBzZXQgd2l0aFxuICAgICAqIG9wdGlvblV0aWxzI3NldERlZmF1bHRzKCkgYW5kIHRoZSBsaWIgZGVmYXVsdHMuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgVGhlIGZpbmFsIG9wdGlvbnMgb2JqZWN0LlxuICAgICAqIEByZXR1cm4ge29iamVjdH0gRXh0ZW5kZWQgb2JqZWN0XG4gICAgICovXG4gICAgZ2V0T3B0aW9uczogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgcmV0dXJuICQuZXh0ZW5kKHRydWUsIHt9LCBsaWJEZWZhdWx0cywgY3VzdG9tRGVmYXVsdHMsIG9wdGlvbnMpO1xuICAgIH0sXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgZ2xvYmFsIGRlZmF1bHRzIGZvciB0aGUgb3B0aW9uVXRpbHMjZ2V0T3B0aW9ucygpIG1ldGhvZC5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gZGVmYXVsdHMgVGhlIGRlZmF1bHRzIG9iamVjdC5cbiAgICAgKi9cbiAgICBzZXREZWZhdWx0czogZnVuY3Rpb24gKGRlZmF1bHRzKSB7XG4gICAgICAgIGN1c3RvbURlZmF1bHRzID0gZGVmYXVsdHM7XG4gICAgfVxufTtcbm1vZHVsZS5leHBvcnRzID0gb3B0aW9uVXRpbHM7XG4iLCIvKipcbiAqIFV0aWxpdGllcyBmb3Igd29ya2luZyB3aXRoIHF1ZXJ5IHN0cmluZ3NcbiovXG4ndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gKGZ1bmN0aW9uICgpIHtcblxuICAgIHJldHVybiB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDb252ZXJ0cyB0byBtYXRyaXggZm9ybWF0XG4gICAgICAgICAqIEBwYXJhbSAge09iamVjdH0gcXMgT2JqZWN0IHRvIGNvbnZlcnQgdG8gcXVlcnkgc3RyaW5nXG4gICAgICAgICAqIEByZXR1cm4geyBzdHJpbmd9ICAgIE1hdHJpeC1mb3JtYXQgcXVlcnkgcGFyYW1ldGVyc1xuICAgICAgICAgKi9cbiAgICAgICAgdG9NYXRyaXhGb3JtYXQ6IGZ1bmN0aW9uIChxcykge1xuICAgICAgICAgICAgaWYgKHFzID09PSBudWxsIHx8IHFzID09PSB1bmRlZmluZWQgfHwgcXMgPT09ICcnKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuICc7JztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0eXBlb2YgcXMgPT09ICdzdHJpbmcnIHx8IHFzIGluc3RhbmNlb2YgU3RyaW5nKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHFzO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcmV0dXJuQXJyYXkgPSBbXTtcbiAgICAgICAgICAgIHZhciBPUEVSQVRPUlMgPSBbJzwnLCAnPicsICchJ107XG4gICAgICAgICAgICAkLmVhY2gocXMsIGZ1bmN0aW9uIChrZXksIHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ3N0cmluZycgfHwgJC5pbkFycmF5KCQudHJpbSh2YWx1ZSkuY2hhckF0KDApLCBPUEVSQVRPUlMpID09PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9ICc9JyArIHZhbHVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm5BcnJheS5wdXNoKGtleSArIHZhbHVlKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICB2YXIgbXRyeCA9ICc7JyArIHJldHVybkFycmF5LmpvaW4oJzsnKTtcbiAgICAgICAgICAgIHJldHVybiBtdHJ4O1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDb252ZXJ0cyBzdHJpbmdzL2FycmF5cy9vYmplY3RzIHRvIHR5cGUgJ2E9YiZiPWMnXG4gICAgICAgICAqIEBwYXJhbSAgeyBzdHJpbmd8QXJyYXl8T2JqZWN0fSBxc1xuICAgICAgICAgKiBAcmV0dXJuIHsgc3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdG9RdWVyeUZvcm1hdDogZnVuY3Rpb24gKHFzKSB7XG4gICAgICAgICAgICBpZiAocXMgPT09IG51bGwgfHwgcXMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHJldHVybiAnJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0eXBlb2YgcXMgPT09ICdzdHJpbmcnIHx8IHFzIGluc3RhbmNlb2YgU3RyaW5nKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHFzO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcmV0dXJuQXJyYXkgPSBbXTtcbiAgICAgICAgICAgICQuZWFjaChxcywgZnVuY3Rpb24gKGtleSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgICBpZiAoJC5pc0FycmF5KHZhbHVlKSkge1xuICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IHZhbHVlLmpvaW4oJywnKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKCQuaXNQbGFpbk9iamVjdCh2YWx1ZSkpIHtcbiAgICAgICAgICAgICAgICAgICAgLy9Nb3N0bHkgZm9yIGRhdGEgYXBpXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gSlNPTi5zdHJpbmdpZnkodmFsdWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm5BcnJheS5wdXNoKGtleSArICc9JyArIHZhbHVlKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICB2YXIgcmVzdWx0ID0gcmV0dXJuQXJyYXkuam9pbignJicpO1xuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ29udmVydHMgc3RyaW5ncyBvZiB0eXBlICdhPWImYj1jJyB0byB7IGE6YiwgYjpjfVxuICAgICAgICAgKiBAcGFyYW0gIHsgc3RyaW5nfSBxc1xuICAgICAgICAgKiBAcmV0dXJuIHtvYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICBxc1RvT2JqZWN0OiBmdW5jdGlvbiAocXMpIHtcbiAgICAgICAgICAgIGlmIChxcyA9PT0gbnVsbCB8fCBxcyA9PT0gdW5kZWZpbmVkIHx8IHFzID09PSAnJykge1xuICAgICAgICAgICAgICAgIHJldHVybiB7fTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHFzQXJyYXkgPSBxcy5zcGxpdCgnJicpO1xuICAgICAgICAgICAgdmFyIHJldHVybk9iaiA9IHt9O1xuICAgICAgICAgICAgJC5lYWNoKHFzQXJyYXksIGZ1bmN0aW9uIChpbmRleCwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgICB2YXIgcUtleSA9IHZhbHVlLnNwbGl0KCc9JylbMF07XG4gICAgICAgICAgICAgICAgdmFyIHFWYWwgPSB2YWx1ZS5zcGxpdCgnPScpWzFdO1xuXG4gICAgICAgICAgICAgICAgaWYgKHFWYWwuaW5kZXhPZignLCcpICE9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICBxVmFsID0gcVZhbC5zcGxpdCgnLCcpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybk9ialtxS2V5XSA9IHFWYWw7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgcmV0dXJuIHJldHVybk9iajtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTm9ybWFsaXplcyBhbmQgbWVyZ2VzIHN0cmluZ3Mgb2YgdHlwZSAnYT1iJywgeyBiOmN9IHRvIHsgYTpiLCBiOmN9XG4gICAgICAgICAqIEBwYXJhbSAgeyBzdHJpbmd8QXJyYXl8T2JqZWN0fSBxczFcbiAgICAgICAgICogQHBhcmFtICB7IHN0cmluZ3xBcnJheXxPYmplY3R9IHFzMlxuICAgICAgICAgKiBAcmV0dXJuIHtPYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICBtZXJnZVFTOiBmdW5jdGlvbiAocXMxLCBxczIpIHtcbiAgICAgICAgICAgIHZhciBvYmoxID0gdGhpcy5xc1RvT2JqZWN0KHRoaXMudG9RdWVyeUZvcm1hdChxczEpKTtcbiAgICAgICAgICAgIHZhciBvYmoyID0gdGhpcy5xc1RvT2JqZWN0KHRoaXMudG9RdWVyeUZvcm1hdChxczIpKTtcbiAgICAgICAgICAgIHJldHVybiAkLmV4dGVuZCh0cnVlLCB7fSwgb2JqMSwgb2JqMik7XG4gICAgICAgIH0sXG5cbiAgICAgICAgYWRkVHJhaWxpbmdTbGFzaDogZnVuY3Rpb24gKHVybCkge1xuICAgICAgICAgICAgaWYgKCF1cmwpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gJyc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gKHVybC5jaGFyQXQodXJsLmxlbmd0aCAtIDEpID09PSAnLycpID8gdXJsIDogKHVybCArICcvJyk7XG4gICAgICAgIH1cbiAgICB9O1xufSgpKTtcblxuXG4iLCIvKipcbiAqIFV0aWxpdGllcyBmb3Igd29ya2luZyB3aXRoIHRoZSBydW4gc2VydmljZVxuKi9cbid1c2Ugc3RyaWN0JztcbnZhciBxdXRpbCA9IHJlcXVpcmUoJy4vcXVlcnktdXRpbCcpO1xudmFyIE1BWF9VUkxfTEVOR1RIID0gMjA0ODtcblxubW9kdWxlLmV4cG9ydHMgPSAoZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiByZXR1cm5zIG9wZXJhdGlvbnMgb2YgdGhlIGZvcm0gYFtbb3AxLG9wMl0sIFthcmcxLCBhcmcyXV1gXG4gICAgICAgICAqIEBwYXJhbSAge09iamVjdHxBcnJheXxTdHJpbmd9IG9wZXJhdGlvbnMgb3BlcmF0aW9ucyB0byBwZXJmb3JtXG4gICAgICAgICAqIEBwYXJhbSAge0FycmF5fSBhcmdzIGFyZ3VtZW50cyBmb3Igb3BlcmF0aW9uXG4gICAgICAgICAqIEByZXR1cm4ge1N0cmluZ30gICAgTWF0cml4LWZvcm1hdCBxdWVyeSBwYXJhbWV0ZXJzXG4gICAgICAgICAqL1xuICAgICAgICBub3JtYWxpemVPcGVyYXRpb25zOiBmdW5jdGlvbiAob3BlcmF0aW9ucywgYXJncykge1xuICAgICAgICAgICAgaWYgKCFhcmdzKSB7XG4gICAgICAgICAgICAgICAgYXJncyA9IFtdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHJldHVybkxpc3QgPSB7XG4gICAgICAgICAgICAgICAgb3BzOiBbXSxcbiAgICAgICAgICAgICAgICBhcmdzOiBbXVxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgdmFyIF9jb25jYXQgPSBmdW5jdGlvbiAoYXJyKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIChhcnIgIT09IG51bGwgJiYgYXJyICE9PSB1bmRlZmluZWQpID8gW10uY29uY2F0KGFycikgOiBbXTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIC8veyBhZGQ6IFsxLDJdLCBzdWJ0cmFjdDogWzIsNF0gfVxuICAgICAgICAgICAgdmFyIF9ub3JtYWxpemVQbGFpbk9iamVjdHMgPSBmdW5jdGlvbiAob3BlcmF0aW9ucywgcmV0dXJuTGlzdCkge1xuICAgICAgICAgICAgICAgIGlmICghcmV0dXJuTGlzdCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm5MaXN0ID0geyBvcHM6IFtdLCBhcmdzOiBbXSB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAkLmVhY2gob3BlcmF0aW9ucywgZnVuY3Rpb24gKG9wbiwgYXJnKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybkxpc3Qub3BzLnB1c2gob3BuKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuTGlzdC5hcmdzLnB1c2goX2NvbmNhdChhcmcpKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICByZXR1cm4gcmV0dXJuTGlzdDtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICAvL3sgbmFtZTogJ2FkZCcsIHBhcmFtczogWzFdIH1cbiAgICAgICAgICAgIHZhciBfbm9ybWFsaXplU3RydWN0dXJlZE9iamVjdHMgPSBmdW5jdGlvbiAob3BlcmF0aW9uLCByZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgaWYgKCFyZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybkxpc3QgPSB7IG9wczogW10sIGFyZ3M6IFtdIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybkxpc3Qub3BzLnB1c2gob3BlcmF0aW9uLm5hbWUpO1xuICAgICAgICAgICAgICAgIHJldHVybkxpc3QuYXJncy5wdXNoKF9jb25jYXQob3BlcmF0aW9uLnBhcmFtcykpO1xuICAgICAgICAgICAgICAgIHJldHVybiByZXR1cm5MaXN0O1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgdmFyIF9ub3JtYWxpemVPYmplY3QgPSBmdW5jdGlvbiAob3BlcmF0aW9uLCByZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuICgob3BlcmF0aW9uLm5hbWUpID8gX25vcm1hbGl6ZVN0cnVjdHVyZWRPYmplY3RzIDogX25vcm1hbGl6ZVBsYWluT2JqZWN0cykob3BlcmF0aW9uLCByZXR1cm5MaXN0KTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHZhciBfbm9ybWFsaXplTGl0ZXJhbHMgPSBmdW5jdGlvbiAob3BlcmF0aW9uLCBhcmdzLCByZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgaWYgKCFyZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybkxpc3QgPSB7IG9wczogW10sIGFyZ3M6IFtdIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybkxpc3Qub3BzLnB1c2gob3BlcmF0aW9uKTtcbiAgICAgICAgICAgICAgICByZXR1cm5MaXN0LmFyZ3MucHVzaChfY29uY2F0KGFyZ3MpKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gcmV0dXJuTGlzdDtcbiAgICAgICAgICAgIH07XG5cblxuICAgICAgICAgICAgdmFyIF9ub3JtYWxpemVBcnJheXMgPSBmdW5jdGlvbiAob3BlcmF0aW9ucywgYXJnLCByZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgaWYgKCFyZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybkxpc3QgPSB7IG9wczogW10sIGFyZ3M6IFtdIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICQuZWFjaChvcGVyYXRpb25zLCBmdW5jdGlvbiAoaW5kZXgsIG9wbikge1xuICAgICAgICAgICAgICAgICAgICBpZiAoJC5pc1BsYWluT2JqZWN0KG9wbikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIF9ub3JtYWxpemVPYmplY3Qob3BuLCByZXR1cm5MaXN0KTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIF9ub3JtYWxpemVMaXRlcmFscyhvcG4sIGFyZ3NbaW5kZXhdLCByZXR1cm5MaXN0KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJldHVybiByZXR1cm5MaXN0O1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgaWYgKCQuaXNQbGFpbk9iamVjdChvcGVyYXRpb25zKSkge1xuICAgICAgICAgICAgICAgIF9ub3JtYWxpemVPYmplY3Qob3BlcmF0aW9ucywgcmV0dXJuTGlzdCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKCQuaXNBcnJheShvcGVyYXRpb25zKSkge1xuICAgICAgICAgICAgICAgIF9ub3JtYWxpemVBcnJheXMob3BlcmF0aW9ucywgYXJncywgcmV0dXJuTGlzdCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIF9ub3JtYWxpemVMaXRlcmFscyhvcGVyYXRpb25zLCBhcmdzLCByZXR1cm5MaXN0KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHJldHVybkxpc3Q7XG4gICAgICAgIH0sXG5cbiAgICAgICAgc3BsaXRHZXRGYWN0b3J5OiBmdW5jdGlvbiAoaHR0cE9wdGlvbnMpIHtcbiAgICAgICAgICAgIHJldHVybiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICAgICAgdmFyIGh0dHAgPSB0aGlzOyAvL2VzbGludC1kaXNhYmxlLWxpbmVcbiAgICAgICAgICAgICAgICB2YXIgZ2V0VmFsdWUgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgdmFsdWUgPSBvcHRpb25zW25hbWVdIHx8IGh0dHBPcHRpb25zW25hbWVdO1xuICAgICAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IHZhbHVlKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgdmFyIGdldEZpbmFsVXJsID0gZnVuY3Rpb24gKHBhcmFtcykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgdXJsID0gZ2V0VmFsdWUoJ3VybCcsIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgZGF0YSA9IHBhcmFtcztcbiAgICAgICAgICAgICAgICAgICAgLy8gVGhlcmUgaXMgZWFzeSAob3Iga25vd24pIHdheSB0byBnZXQgdGhlIGZpbmFsIFVSTCBqcXVlcnkgaXMgZ29pbmcgdG8gc2VuZCBzb1xuICAgICAgICAgICAgICAgICAgICAvLyB3ZSdyZSByZXBsaWNhdGluZyBpdC4gVGhlIHByb2Nlc3MgbWlnaHQgY2hhbmdlIGF0IHNvbWUgcG9pbnQgYnV0IGl0IHByb2JhYmx5IHdpbGwgbm90LlxuICAgICAgICAgICAgICAgICAgICAvLyAxLiBSZW1vdmUgaGFzaFxuICAgICAgICAgICAgICAgICAgICB1cmwgPSB1cmwucmVwbGFjZSgvIy4qJC8sICcnKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gMS4gQXBwZW5kIHF1ZXJ5IHN0cmluZ1xuICAgICAgICAgICAgICAgICAgICB2YXIgcXVlcnlQYXJhbXMgPSBxdXRpbC50b1F1ZXJ5Rm9ybWF0KGRhdGEpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgcXVlc3Rpb25JZHggPSB1cmwuaW5kZXhPZignPycpO1xuICAgICAgICAgICAgICAgICAgICBpZiAocXVlcnlQYXJhbXMgJiYgcXVlc3Rpb25JZHggPiAtMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHVybCArICcmJyArIHF1ZXJ5UGFyYW1zO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHF1ZXJ5UGFyYW1zKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gdXJsICsgJz8nICsgcXVlcnlQYXJhbXM7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHVybDtcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIHZhciB1cmwgPSBnZXRGaW5hbFVybChwYXJhbXMpO1xuICAgICAgICAgICAgICAgIC8vIFdlIG11c3Qgc3BsaXQgdGhlIEdFVCBpbiBtdWx0aXBsZSBzaG9ydCBVUkwnc1xuICAgICAgICAgICAgICAgIC8vIFRoZSBvbmx5IHByb3BlcnR5IGFsbG93ZWQgdG8gYmUgc3BsaXQgaXMgXCJpbmNsdWRlXCJcbiAgICAgICAgICAgICAgICBpZiAocGFyYW1zICYmIHBhcmFtcy5pbmNsdWRlICYmIGVuY29kZVVSSSh1cmwpLmxlbmd0aCA+IE1BWF9VUkxfTEVOR1RIKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBkdGQgPSAkLkRlZmVycmVkKCk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBwYXJhbXNDb3B5ID0gJC5leHRlbmQodHJ1ZSwge30sIHBhcmFtcyk7XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSBwYXJhbXNDb3B5LmluY2x1ZGU7XG4gICAgICAgICAgICAgICAgICAgIHZhciB1cmxOb0luY2x1ZGVzID0gZ2V0RmluYWxVcmwocGFyYW1zQ29weSk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBkaWZmID0gTUFYX1VSTF9MRU5HVEggLSB1cmxOb0luY2x1ZGVzLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG9sZFN1Y2Nlc3MgPSBvcHRpb25zLnN1Y2Nlc3MgfHwgaHR0cE9wdGlvbnMuc3VjY2VzcyB8fCAkLm5vb3A7XG4gICAgICAgICAgICAgICAgICAgIHZhciBvbGRFcnJvciA9IG9wdGlvbnMuZXJyb3IgfHwgaHR0cE9wdGlvbnMuZXJyb3IgfHwgJC5ub29wO1xuICAgICAgICAgICAgICAgICAgICAvLyByZW1vdmUgdGhlIG9yaWdpbmFsIHN1Y2Nlc3MgYW5kIGVycm9yIGNhbGxiYWNrc1xuICAgICAgICAgICAgICAgICAgICBvcHRpb25zLnN1Y2Nlc3MgPSAkLm5vb3A7XG4gICAgICAgICAgICAgICAgICAgIG9wdGlvbnMuZXJyb3IgPSAkLm5vb3A7XG5cbiAgICAgICAgICAgICAgICAgICAgdmFyIGluY2x1ZGUgPSBwYXJhbXMuaW5jbHVkZTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGN1cnJJbmNsdWRlcyA9IFtdO1xuICAgICAgICAgICAgICAgICAgICB2YXIgaW5jbHVkZU9wdHMgPSBbY3VyckluY2x1ZGVzXTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGN1cnJMZW5ndGggPSBlbmNvZGVVUklDb21wb25lbnQoJz9pbmNsdWRlPScpLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHZhcmlhYmxlID0gaW5jbHVkZS5wb3AoKTtcbiAgICAgICAgICAgICAgICAgICAgd2hpbGUgKHZhcmlhYmxlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgdmFyTGVuZ2h0ID0gZW5jb2RlVVJJQ29tcG9uZW50KHZhcmlhYmxlKS5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBVc2UgYSBncmVlZHkgYXBwcm9hY2ggZm9yIG5vdywgY2FuIGJlIG9wdGltaXplZCB0byBiZSBzb2x2ZWQgaW4gYSBtb3JlXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBlZmZpY2llbnQgd2F5XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyArIDEgaXMgdGhlIGNvbW1hXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoY3Vyckxlbmd0aCArIHZhckxlbmdodCArIDEgPCBkaWZmKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VyckluY2x1ZGVzLnB1c2godmFyaWFibGUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJMZW5ndGggKz0gdmFyTGVuZ2h0ICsgMTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VyckluY2x1ZGVzID0gW3ZhcmlhYmxlXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlT3B0cy5wdXNoKGN1cnJJbmNsdWRlcyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY3Vyckxlbmd0aCA9ICc/aW5jbHVkZT0nLmxlbmd0aCArIHZhckxlbmdodDtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gaW5jbHVkZS5wb3AoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB2YXIgcmVxcyA9ICQubWFwKGluY2x1ZGVPcHRzLCBmdW5jdGlvbiAoaW5jbHVkZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHJlcVBhcmFtcyA9ICQuZXh0ZW5kKHt9LCBwYXJhbXMsIHsgaW5jbHVkZTogaW5jbHVkZSB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBodHRwLmdldChyZXFQYXJhbXMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgJC53aGVuLmFwcGx5KCQsIHJlcXMpLnRoZW4oZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRWFjaCBhcmd1bWVudCBhcmUgYXJyYXlzIG9mIHRoZSBhcmd1bWVudHMgb2YgZWFjaCBkb25lIHJlcXVlc3RcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNvIHRoZSBmaXJzdCBhcmd1bWVudCBvZiB0aGUgZmlyc3QgYXJyYXkgb2YgYXJndW1lbnRzIGlzIHRoZSBkYXRhXG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgaXNWYWxpZCA9IGFyZ3VtZW50c1swXSAmJiBhcmd1bWVudHNbMF1bMF07XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIWlzVmFsaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTaG91bGQgbmV2ZXIgaGFwcGVuLi4uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb2xkRXJyb3IoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZHRkLnJlamVjdCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGZpcnN0UmVzcG9uc2UgPSBhcmd1bWVudHNbMF1bMF07XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgaXNPYmplY3QgPSAkLmlzUGxhaW5PYmplY3QoZmlyc3RSZXNwb25zZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgaXNSdW5BUEkgPSAoaXNPYmplY3QgJiYgJC5pc1BsYWluT2JqZWN0KGZpcnN0UmVzcG9uc2UudmFyaWFibGVzKSkgfHwgIWlzT2JqZWN0O1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzUnVuQVBJKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzT2JqZWN0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFnZ3JlZ2F0ZSB0aGUgdmFyaWFibGVzIHByb3BlcnR5IG9ubHlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFnZ3JlZ2F0ZVJ1biA9IGFyZ3VtZW50c1swXVswXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5lYWNoKGFyZ3VtZW50cywgZnVuY3Rpb24gKGlkeCwgYXJncykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHJ1biA9IGFyZ3NbMF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLmV4dGVuZCh0cnVlLCBhZ2dyZWdhdGVSdW4udmFyaWFibGVzLCBydW4udmFyaWFibGVzKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9sZFN1Y2Nlc3MoYWdncmVnYXRlUnVuLCBhcmd1bWVudHNbMF1bMV0sIGFyZ3VtZW50c1swXVsyXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGR0ZC5yZXNvbHZlKGFnZ3JlZ2F0ZVJ1biwgYXJndW1lbnRzWzBdWzFdLCBhcmd1bWVudHNbMF1bMl0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFycmF5IG9mIHJ1bnNcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQWdyZWdhdGUgdmFyaWFibGVzIGluIGVhY2ggcnVuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBhZ2dyZWdhdGVkUnVucyA9IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLmVhY2goYXJndW1lbnRzLCBmdW5jdGlvbiAoaWR4LCBhcmdzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgcnVucyA9IGFyZ3NbMF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoISQuaXNBcnJheShydW5zKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICQuZWFjaChydW5zLCBmdW5jdGlvbiAoaWR4UnVuLCBydW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocnVuLmlkICYmICFhZ2dyZWdhdGVkUnVuc1tydW4uaWRdKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJ1bi52YXJpYWJsZXMgPSBydW4udmFyaWFibGVzIHx8IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2dyZWdhdGVkUnVuc1tydW4uaWRdID0gcnVuO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAocnVuLmlkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICQuZXh0ZW5kKHRydWUsIGFnZ3JlZ2F0ZWRSdW5zW3J1bi5pZF0udmFyaWFibGVzLCBydW4udmFyaWFibGVzKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHR1cm4gaXQgaW50byBhbiBhcnJheVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2dyZWdhdGVkUnVucyA9ICQubWFwKGFnZ3JlZ2F0ZWRSdW5zLCBmdW5jdGlvbiAocnVuKSB7IHJldHVybiBydW47IH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbGRTdWNjZXNzKGFnZ3JlZ2F0ZWRSdW5zLCBhcmd1bWVudHNbMF1bMV0sIGFyZ3VtZW50c1swXVsyXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGR0ZC5yZXNvbHZlKGFnZ3JlZ2F0ZWRSdW5zLCBhcmd1bWVudHNbMF1bMV0sIGFyZ3VtZW50c1swXVsyXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBpcyB2YXJpYWJsZXMgQVBJXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gYWdncmVnYXRlIHRoZSByZXNwb25zZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBhZ2dyZWdhdGVkVmFyaWFibGVzID0ge307XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5lYWNoKGFyZ3VtZW50cywgZnVuY3Rpb24gKGlkeCwgYXJncykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgdmFycyA9IGFyZ3NbMF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICQuZXh0ZW5kKHRydWUsIGFnZ3JlZ2F0ZWRWYXJpYWJsZXMsIHZhcnMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9sZFN1Y2Nlc3MoYWdncmVnYXRlZFZhcmlhYmxlcywgYXJndW1lbnRzWzBdWzFdLCBhcmd1bWVudHNbMF1bMl0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGR0ZC5yZXNvbHZlKGFnZ3JlZ2F0ZWRWYXJpYWJsZXMsIGFyZ3VtZW50c1swXVsxXSwgYXJndW1lbnRzWzBdWzJdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSwgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgb2xkRXJyb3IuYXBwbHkoaHR0cCwgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGR0ZC5yZWplY3QuYXBwbHkoZHRkLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGR0ZC5wcm9taXNlKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KHBhcmFtcywgb3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgIH07XG59KCkpO1xuIl19 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvQmFzZTY0L2Jhc2U2NC5qcyIsIm5vZGVfbW9kdWxlcy9vYmplY3QtYXNzaWduL2luZGV4LmpzIiwic3JjL2FwaS12ZXJzaW9uLmpzb24iLCJzcmMvYXBwLmpzIiwic3JjL2Vudi1sb2FkLmpzIiwic3JjL21hbmFnZXJzL2F1dGgtbWFuYWdlci5qcyIsInNyYy9tYW5hZ2Vycy9jaGFubmVsLW1hbmFnZXIuanMiLCJzcmMvbWFuYWdlcnMvZXBpY2VudGVyLWNoYW5uZWwtbWFuYWdlci5qcyIsInNyYy9tYW5hZ2Vycy9rZXktbmFtZXMuanMiLCJzcmMvbWFuYWdlcnMvcnVuLW1hbmFnZXIuanMiLCJzcmMvbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvYWx3YXlzLW5ldy1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9jb25kaXRpb25hbC1jcmVhdGlvbi1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9tdWx0aXBsYXllci1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9uZXctaWYtaW5pdGlhbGl6ZWQtc3RyYXRlZ3kuanMiLCJzcmMvbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvbmV3LWlmLW1pc3Npbmctc3RyYXRlZ3kuanMiLCJzcmMvbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvbmV3LWlmLXBlcnNpc3RlZC1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9ub25lLXN0cmF0ZWd5LmpzIiwic3JjL21hbmFnZXJzL3J1bi1zdHJhdGVnaWVzL3BlcnNpc3RlbnQtc2luZ2xlLXBsYXllci1zdHJhdGVneS5qcyIsInNyYy9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9zdHJhdGVnaWVzLW1hcC5qcyIsInNyYy9tYW5hZ2Vycy9zY2VuYXJpby1tYW5hZ2VyLmpzIiwic3JjL21hbmFnZXJzL3NwZWNpYWwtb3BlcmF0aW9ucy5qcyIsInNyYy9tYW5hZ2Vycy93b3JsZC1tYW5hZ2VyLmpzIiwic3JjL3NlcnZpY2UvYWRtaW4tZmlsZS1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvYXNzZXQtYXBpLWFkYXB0ZXIuanMiLCJzcmMvc2VydmljZS9hdXRoLWFwaS1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvY2hhbm5lbC1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvY29uZmlndXJhdGlvbi1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvZGF0YS1hcGktc2VydmljZS5qcyIsInNyYy9zZXJ2aWNlL2dyb3VwLWFwaS1zZXJ2aWNlLmpzIiwic3JjL3NlcnZpY2UvaW50cm9zcGVjdGlvbi1hcGktc2VydmljZS5qcyIsInNyYy9zZXJ2aWNlL21lbWJlci1hcGktYWRhcHRlci5qcyIsInNyYy9zZXJ2aWNlL3J1bi1hcGktc2VydmljZS5qcyIsInNyYy9zZXJ2aWNlL3NlcnZpY2UtdXRpbHMuanMiLCJzcmMvc2VydmljZS9zdGF0ZS1hcGktYWRhcHRlci5qcyIsInNyYy9zZXJ2aWNlL3VybC1jb25maWctc2VydmljZS5qcyIsInNyYy9zZXJ2aWNlL3VzZXItYXBpLWFkYXB0ZXIuanMiLCJzcmMvc2VydmljZS92YXJpYWJsZXMtYXBpLXNlcnZpY2UuanMiLCJzcmMvc2VydmljZS93b3JsZC1hcGktYWRhcHRlci5qcyIsInNyYy9zdG9yZS9jb29raWUtc3RvcmUuanMiLCJzcmMvc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyLmpzIiwic3JjL3N0b3JlL3N0b3JlLWZhY3RvcnkuanMiLCJzcmMvdHJhbnNwb3J0L2FqYXgtaHR0cC10cmFuc3BvcnQuanMiLCJzcmMvdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnkuanMiLCJzcmMvdXRpbC9pbmhlcml0LmpzIiwic3JjL3V0aWwvb2JqZWN0LXV0aWwuanMiLCJzcmMvdXRpbC9vcHRpb24tdXRpbHMuanMiLCJzcmMvdXRpbC9xdWVyeS11dGlsLmpzIiwic3JjL3V0aWwvcnVuLXV0aWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkZBO0FBQ0E7QUFDQTtBQUNBOzs7QUNIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDdEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDallBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN1BBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RZQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdkxBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFYQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4SEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4T0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hTQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcElBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1TEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNWhCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1SEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3J2QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaEpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDL0dBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIjsoZnVuY3Rpb24gKCkge1xuXG4gIHZhciBvYmplY3QgPSB0eXBlb2YgZXhwb3J0cyAhPSAndW5kZWZpbmVkJyA/IGV4cG9ydHMgOiBzZWxmOyAvLyAjODogd2ViIHdvcmtlcnNcbiAgdmFyIGNoYXJzID0gJ0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5Ky89JztcblxuICBmdW5jdGlvbiBJbnZhbGlkQ2hhcmFjdGVyRXJyb3IobWVzc2FnZSkge1xuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XG4gIH1cbiAgSW52YWxpZENoYXJhY3RlckVycm9yLnByb3RvdHlwZSA9IG5ldyBFcnJvcjtcbiAgSW52YWxpZENoYXJhY3RlckVycm9yLnByb3RvdHlwZS5uYW1lID0gJ0ludmFsaWRDaGFyYWN0ZXJFcnJvcic7XG5cbiAgLy8gZW5jb2RlclxuICAvLyBbaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vOTk5MTY2XSBieSBbaHR0cHM6Ly9naXRodWIuY29tL25pZ25hZ11cbiAgb2JqZWN0LmJ0b2EgfHwgKFxuICBvYmplY3QuYnRvYSA9IGZ1bmN0aW9uIChpbnB1dCkge1xuICAgIHZhciBzdHIgPSBTdHJpbmcoaW5wdXQpO1xuICAgIGZvciAoXG4gICAgICAvLyBpbml0aWFsaXplIHJlc3VsdCBhbmQgY291bnRlclxuICAgICAgdmFyIGJsb2NrLCBjaGFyQ29kZSwgaWR4ID0gMCwgbWFwID0gY2hhcnMsIG91dHB1dCA9ICcnO1xuICAgICAgLy8gaWYgdGhlIG5leHQgc3RyIGluZGV4IGRvZXMgbm90IGV4aXN0OlxuICAgICAgLy8gICBjaGFuZ2UgdGhlIG1hcHBpbmcgdGFibGUgdG8gXCI9XCJcbiAgICAgIC8vICAgY2hlY2sgaWYgZCBoYXMgbm8gZnJhY3Rpb25hbCBkaWdpdHNcbiAgICAgIHN0ci5jaGFyQXQoaWR4IHwgMCkgfHwgKG1hcCA9ICc9JywgaWR4ICUgMSk7XG4gICAgICAvLyBcIjggLSBpZHggJSAxICogOFwiIGdlbmVyYXRlcyB0aGUgc2VxdWVuY2UgMiwgNCwgNiwgOFxuICAgICAgb3V0cHV0ICs9IG1hcC5jaGFyQXQoNjMgJiBibG9jayA+PiA4IC0gaWR4ICUgMSAqIDgpXG4gICAgKSB7XG4gICAgICBjaGFyQ29kZSA9IHN0ci5jaGFyQ29kZUF0KGlkeCArPSAzLzQpO1xuICAgICAgaWYgKGNoYXJDb2RlID4gMHhGRikge1xuICAgICAgICB0aHJvdyBuZXcgSW52YWxpZENoYXJhY3RlckVycm9yKFwiJ2J0b2EnIGZhaWxlZDogVGhlIHN0cmluZyB0byBiZSBlbmNvZGVkIGNvbnRhaW5zIGNoYXJhY3RlcnMgb3V0c2lkZSBvZiB0aGUgTGF0aW4xIHJhbmdlLlwiKTtcbiAgICAgIH1cbiAgICAgIGJsb2NrID0gYmxvY2sgPDwgOCB8IGNoYXJDb2RlO1xuICAgIH1cbiAgICByZXR1cm4gb3V0cHV0O1xuICB9KTtcblxuICAvLyBkZWNvZGVyXG4gIC8vIFtodHRwczovL2dpc3QuZ2l0aHViLmNvbS8xMDIwMzk2XSBieSBbaHR0cHM6Ly9naXRodWIuY29tL2F0a11cbiAgb2JqZWN0LmF0b2IgfHwgKFxuICBvYmplY3QuYXRvYiA9IGZ1bmN0aW9uIChpbnB1dCkge1xuICAgIHZhciBzdHIgPSBTdHJpbmcoaW5wdXQpLnJlcGxhY2UoLz0rJC8sICcnKTtcbiAgICBpZiAoc3RyLmxlbmd0aCAlIDQgPT0gMSkge1xuICAgICAgdGhyb3cgbmV3IEludmFsaWRDaGFyYWN0ZXJFcnJvcihcIidhdG9iJyBmYWlsZWQ6IFRoZSBzdHJpbmcgdG8gYmUgZGVjb2RlZCBpcyBub3QgY29ycmVjdGx5IGVuY29kZWQuXCIpO1xuICAgIH1cbiAgICBmb3IgKFxuICAgICAgLy8gaW5pdGlhbGl6ZSByZXN1bHQgYW5kIGNvdW50ZXJzXG4gICAgICB2YXIgYmMgPSAwLCBicywgYnVmZmVyLCBpZHggPSAwLCBvdXRwdXQgPSAnJztcbiAgICAgIC8vIGdldCBuZXh0IGNoYXJhY3RlclxuICAgICAgYnVmZmVyID0gc3RyLmNoYXJBdChpZHgrKyk7XG4gICAgICAvLyBjaGFyYWN0ZXIgZm91bmQgaW4gdGFibGU/IGluaXRpYWxpemUgYml0IHN0b3JhZ2UgYW5kIGFkZCBpdHMgYXNjaWkgdmFsdWU7XG4gICAgICB+YnVmZmVyICYmIChicyA9IGJjICUgNCA/IGJzICogNjQgKyBidWZmZXIgOiBidWZmZXIsXG4gICAgICAgIC8vIGFuZCBpZiBub3QgZmlyc3Qgb2YgZWFjaCA0IGNoYXJhY3RlcnMsXG4gICAgICAgIC8vIGNvbnZlcnQgdGhlIGZpcnN0IDggYml0cyB0byBvbmUgYXNjaWkgY2hhcmFjdGVyXG4gICAgICAgIGJjKysgJSA0KSA/IG91dHB1dCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKDI1NSAmIGJzID4+ICgtMiAqIGJjICYgNikpIDogMFxuICAgICkge1xuICAgICAgLy8gdHJ5IHRvIGZpbmQgY2hhcmFjdGVyIGluIHRhYmxlICgwLTYzLCBub3QgZm91bmQgPT4gLTEpXG4gICAgICBidWZmZXIgPSBjaGFycy5pbmRleE9mKGJ1ZmZlcik7XG4gICAgfVxuICAgIHJldHVybiBvdXRwdXQ7XG4gIH0pO1xuXG59KCkpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuLyogZXNsaW50LWRpc2FibGUgbm8tdW51c2VkLXZhcnMgKi9cbnZhciBoYXNPd25Qcm9wZXJ0eSA9IE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHk7XG52YXIgcHJvcElzRW51bWVyYWJsZSA9IE9iamVjdC5wcm90b3R5cGUucHJvcGVydHlJc0VudW1lcmFibGU7XG5cbmZ1bmN0aW9uIHRvT2JqZWN0KHZhbCkge1xuXHRpZiAodmFsID09PSBudWxsIHx8IHZhbCA9PT0gdW5kZWZpbmVkKSB7XG5cdFx0dGhyb3cgbmV3IFR5cGVFcnJvcignT2JqZWN0LmFzc2lnbiBjYW5ub3QgYmUgY2FsbGVkIHdpdGggbnVsbCBvciB1bmRlZmluZWQnKTtcblx0fVxuXG5cdHJldHVybiBPYmplY3QodmFsKTtcbn1cblxuZnVuY3Rpb24gc2hvdWxkVXNlTmF0aXZlKCkge1xuXHR0cnkge1xuXHRcdGlmICghT2JqZWN0LmFzc2lnbikge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdC8vIERldGVjdCBidWdneSBwcm9wZXJ0eSBlbnVtZXJhdGlvbiBvcmRlciBpbiBvbGRlciBWOCB2ZXJzaW9ucy5cblxuXHRcdC8vIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC92OC9pc3N1ZXMvZGV0YWlsP2lkPTQxMThcblx0XHR2YXIgdGVzdDEgPSBuZXcgU3RyaW5nKCdhYmMnKTsgIC8vIGVzbGludC1kaXNhYmxlLWxpbmVcblx0XHR0ZXN0MVs1XSA9ICdkZSc7XG5cdFx0aWYgKE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKHRlc3QxKVswXSA9PT0gJzUnKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXG5cdFx0Ly8gaHR0cHM6Ly9idWdzLmNocm9taXVtLm9yZy9wL3Y4L2lzc3Vlcy9kZXRhaWw/aWQ9MzA1NlxuXHRcdHZhciB0ZXN0MiA9IHt9O1xuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgMTA7IGkrKykge1xuXHRcdFx0dGVzdDJbJ18nICsgU3RyaW5nLmZyb21DaGFyQ29kZShpKV0gPSBpO1xuXHRcdH1cblx0XHR2YXIgb3JkZXIyID0gT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXModGVzdDIpLm1hcChmdW5jdGlvbiAobikge1xuXHRcdFx0cmV0dXJuIHRlc3QyW25dO1xuXHRcdH0pO1xuXHRcdGlmIChvcmRlcjIuam9pbignJykgIT09ICcwMTIzNDU2Nzg5Jykge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdC8vIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC92OC9pc3N1ZXMvZGV0YWlsP2lkPTMwNTZcblx0XHR2YXIgdGVzdDMgPSB7fTtcblx0XHQnYWJjZGVmZ2hpamtsbW5vcHFyc3QnLnNwbGl0KCcnKS5mb3JFYWNoKGZ1bmN0aW9uIChsZXR0ZXIpIHtcblx0XHRcdHRlc3QzW2xldHRlcl0gPSBsZXR0ZXI7XG5cdFx0fSk7XG5cdFx0aWYgKE9iamVjdC5rZXlzKE9iamVjdC5hc3NpZ24oe30sIHRlc3QzKSkuam9pbignJykgIT09XG5cdFx0XHRcdCdhYmNkZWZnaGlqa2xtbm9wcXJzdCcpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSBjYXRjaCAoZSkge1xuXHRcdC8vIFdlIGRvbid0IGV4cGVjdCBhbnkgb2YgdGhlIGFib3ZlIHRvIHRocm93LCBidXQgYmV0dGVyIHRvIGJlIHNhZmUuXG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gc2hvdWxkVXNlTmF0aXZlKCkgPyBPYmplY3QuYXNzaWduIDogZnVuY3Rpb24gKHRhcmdldCwgc291cmNlKSB7XG5cdHZhciBmcm9tO1xuXHR2YXIgdG8gPSB0b09iamVjdCh0YXJnZXQpO1xuXHR2YXIgc3ltYm9scztcblxuXHRmb3IgKHZhciBzID0gMTsgcyA8IGFyZ3VtZW50cy5sZW5ndGg7IHMrKykge1xuXHRcdGZyb20gPSBPYmplY3QoYXJndW1lbnRzW3NdKTtcblxuXHRcdGZvciAodmFyIGtleSBpbiBmcm9tKSB7XG5cdFx0XHRpZiAoaGFzT3duUHJvcGVydHkuY2FsbChmcm9tLCBrZXkpKSB7XG5cdFx0XHRcdHRvW2tleV0gPSBmcm9tW2tleV07XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMpIHtcblx0XHRcdHN5bWJvbHMgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKGZyb20pO1xuXHRcdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCBzeW1ib2xzLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHRcdGlmIChwcm9wSXNFbnVtZXJhYmxlLmNhbGwoZnJvbSwgc3ltYm9sc1tpXSkpIHtcblx0XHRcdFx0XHR0b1tzeW1ib2xzW2ldXSA9IGZyb21bc3ltYm9sc1tpXV07XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRyZXR1cm4gdG87XG59O1xuIiwibW9kdWxlLmV4cG9ydHM9e1xuICAgIFwidmVyc2lvblwiOiBcInYyXCJcbn1cbiIsIi8qKlxuICogRXBpY2VudGVyIEphdmFzY3JpcHQgbGlicmFyaWVzXG4gKiB2PCU9IHZlcnNpb24gJT5cbiAqIGh0dHBzOi8vZ2l0aHViLmNvbS9mb3Jpby9lcGljZW50ZXItanMtbGlic1xuICovXG5cbnZhciBGID0ge1xuICAgIHV0aWw6IHt9LFxuICAgIGZhY3Rvcnk6IHt9LFxuICAgIHRyYW5zcG9ydDoge30sXG4gICAgc3RvcmU6IHt9LFxuICAgIHNlcnZpY2U6IHt9LFxuICAgIG1hbmFnZXI6IHtcbiAgICAgICAgc3RyYXRlZ3k6IHt9XG4gICAgfSxcblxufTtcblxuRi5sb2FkID0gcmVxdWlyZSgnLi9lbnYtbG9hZCcpO1xuXG5pZiAoIWdsb2JhbC5TS0lQX0VOVl9MT0FEKSB7XG4gICAgRi5sb2FkKCk7XG59XG5cbkYudXRpbC5xdWVyeSA9IHJlcXVpcmUoJy4vdXRpbC9xdWVyeS11dGlsJyk7XG5GLnV0aWwucnVuID0gcmVxdWlyZSgnLi91dGlsL3J1bi11dGlsJyk7XG5GLnV0aWwuY2xhc3NGcm9tID0gcmVxdWlyZSgnLi91dGlsL2luaGVyaXQnKTtcblxuRi5mYWN0b3J5LlRyYW5zcG9ydCA9IHJlcXVpcmUoJy4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbkYudHJhbnNwb3J0LkFqYXggPSByZXF1aXJlKCcuL3RyYW5zcG9ydC9hamF4LWh0dHAtdHJhbnNwb3J0Jyk7XG5cbkYuc2VydmljZS5VUkwgPSByZXF1aXJlKCcuL3NlcnZpY2UvdXJsLWNvbmZpZy1zZXJ2aWNlJyk7XG5GLnNlcnZpY2UuQ29uZmlnID0gcmVxdWlyZSgnLi9zZXJ2aWNlL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xuRi5zZXJ2aWNlLlJ1biA9IHJlcXVpcmUoJy4vc2VydmljZS9ydW4tYXBpLXNlcnZpY2UnKTtcbkYuc2VydmljZS5GaWxlID0gcmVxdWlyZSgnLi9zZXJ2aWNlL2FkbWluLWZpbGUtc2VydmljZScpO1xuRi5zZXJ2aWNlLlZhcmlhYmxlcyA9IHJlcXVpcmUoJy4vc2VydmljZS92YXJpYWJsZXMtYXBpLXNlcnZpY2UnKTtcbkYuc2VydmljZS5EYXRhID0gcmVxdWlyZSgnLi9zZXJ2aWNlL2RhdGEtYXBpLXNlcnZpY2UnKTtcbkYuc2VydmljZS5BdXRoID0gcmVxdWlyZSgnLi9zZXJ2aWNlL2F1dGgtYXBpLXNlcnZpY2UnKTtcbkYuc2VydmljZS5Xb3JsZCA9IHJlcXVpcmUoJy4vc2VydmljZS93b3JsZC1hcGktYWRhcHRlcicpO1xuRi5zZXJ2aWNlLlN0YXRlID0gcmVxdWlyZSgnLi9zZXJ2aWNlL3N0YXRlLWFwaS1hZGFwdGVyJyk7XG5GLnNlcnZpY2UuVXNlciA9IHJlcXVpcmUoJy4vc2VydmljZS91c2VyLWFwaS1hZGFwdGVyJyk7XG5GLnNlcnZpY2UuTWVtYmVyID0gcmVxdWlyZSgnLi9zZXJ2aWNlL21lbWJlci1hcGktYWRhcHRlcicpO1xuRi5zZXJ2aWNlLkFzc2V0ID0gcmVxdWlyZSgnLi9zZXJ2aWNlL2Fzc2V0LWFwaS1hZGFwdGVyJyk7XG5GLnNlcnZpY2UuR3JvdXAgPSByZXF1aXJlKCcuL3NlcnZpY2UvZ3JvdXAtYXBpLXNlcnZpY2UnKTtcbkYuc2VydmljZS5JbnRyb3NwZWN0ID0gcmVxdWlyZSgnLi9zZXJ2aWNlL2ludHJvc3BlY3Rpb24tYXBpLXNlcnZpY2UnKTtcblxuRi5zdG9yZS5Db29raWUgPSByZXF1aXJlKCcuL3N0b3JlL2Nvb2tpZS1zdG9yZScpO1xuRi5mYWN0b3J5LlN0b3JlID0gcmVxdWlyZSgnLi9zdG9yZS9zdG9yZS1mYWN0b3J5Jyk7XG5cbkYubWFuYWdlci5TY2VuYXJpb01hbmFnZXIgPSByZXF1aXJlKCcuL21hbmFnZXJzL3NjZW5hcmlvLW1hbmFnZXInKTtcbkYubWFuYWdlci5SdW5NYW5hZ2VyID0gcmVxdWlyZSgnLi9tYW5hZ2Vycy9ydW4tbWFuYWdlcicpO1xuRi5tYW5hZ2VyLkF1dGhNYW5hZ2VyID0gcmVxdWlyZSgnLi9tYW5hZ2Vycy9hdXRoLW1hbmFnZXInKTtcbkYubWFuYWdlci5Xb3JsZE1hbmFnZXIgPSByZXF1aXJlKCcuL21hbmFnZXJzL3dvcmxkLW1hbmFnZXInKTtcblxuRi5tYW5hZ2VyLnN0cmF0ZWd5WydhbHdheXMtbmV3J10gPSByZXF1aXJlKCcuL21hbmFnZXJzL3J1bi1zdHJhdGVnaWVzL2Fsd2F5cy1uZXctc3RyYXRlZ3knKTtcbkYubWFuYWdlci5zdHJhdGVneVsnY29uZGl0aW9uYWwtY3JlYXRpb24nXSA9IHJlcXVpcmUoJy4vbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvY29uZGl0aW9uYWwtY3JlYXRpb24tc3RyYXRlZ3knKTtcbkYubWFuYWdlci5zdHJhdGVneS5pZGVudGl0eSA9IHJlcXVpcmUoJy4vbWFuYWdlcnMvcnVuLXN0cmF0ZWdpZXMvbm9uZS1zdHJhdGVneScpO1xuRi5tYW5hZ2VyLnN0cmF0ZWd5WyduZXctaWYtbWlzc2luZyddID0gcmVxdWlyZSgnLi9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9uZXctaWYtbWlzc2luZy1zdHJhdGVneScpO1xuRi5tYW5hZ2VyLnN0cmF0ZWd5WyduZXctaWYtbWlzc2luZyddID0gcmVxdWlyZSgnLi9tYW5hZ2Vycy9ydW4tc3RyYXRlZ2llcy9uZXctaWYtbWlzc2luZy1zdHJhdGVneScpO1xuRi5tYW5hZ2VyLnN0cmF0ZWd5WyduZXctaWYtcGVyc2lzdGVkJ10gPSByZXF1aXJlKCcuL21hbmFnZXJzL3J1bi1zdHJhdGVnaWVzL25ldy1pZi1wZXJzaXN0ZWQtc3RyYXRlZ3knKTtcbkYubWFuYWdlci5zdHJhdGVneVsnbmV3LWlmLWluaXRpYWxpemVkJ10gPSByZXF1aXJlKCcuL21hbmFnZXJzL3J1bi1zdHJhdGVnaWVzL25ldy1pZi1pbml0aWFsaXplZC1zdHJhdGVneScpO1xuXG5GLm1hbmFnZXIuQ2hhbm5lbE1hbmFnZXIgPSByZXF1aXJlKCcuL21hbmFnZXJzL2VwaWNlbnRlci1jaGFubmVsLW1hbmFnZXInKTtcbkYuc2VydmljZS5DaGFubmVsID0gcmVxdWlyZSgnLi9zZXJ2aWNlL2NoYW5uZWwtc2VydmljZScpO1xuXG5GLnZlcnNpb24gPSAnPCU9IHZlcnNpb24gJT4nO1xuRi5hcGkgPSByZXF1aXJlKCcuL2FwaS12ZXJzaW9uLmpzb24nKTtcblxuZ2xvYmFsLkYgPSBGO1xubW9kdWxlLmV4cG9ydHMgPSBGO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgVVJMQ29uZmlnU2VydmljZSA9IHJlcXVpcmUoJy4vc2VydmljZS91cmwtY29uZmlnLXNlcnZpY2UnKTtcblxudmFyIGVudkxvYWQgPSBmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICB2YXIgdXJsU2VydmljZSA9IG5ldyBVUkxDb25maWdTZXJ2aWNlKCk7XG4gICAgdmFyIGluZm9VcmwgPSB1cmxTZXJ2aWNlLmdldEFQSVBhdGgoJ2NvbmZpZycpO1xuICAgIHZhciBlbnZQcm9taXNlID0gJC5hamF4KHsgdXJsOiBpbmZvVXJsLCBhc3luYzogZmFsc2UgfSk7XG4gICAgZW52UHJvbWlzZSA9IGVudlByb21pc2UudGhlbihmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgIHZhciBvdmVycmlkZXMgPSByZXMuYXBpO1xuICAgICAgICBVUkxDb25maWdTZXJ2aWNlLmRlZmF1bHRzID0gJC5leHRlbmQoVVJMQ29uZmlnU2VydmljZS5kZWZhdWx0cywgb3ZlcnJpZGVzKTtcbiAgICB9KTtcbiAgICByZXR1cm4gZW52UHJvbWlzZS50aGVuKGNhbGxiYWNrKS5mYWlsKGNhbGxiYWNrKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gZW52TG9hZDtcbiIsIi8qKlxuKiAjIyBBdXRob3JpemF0aW9uIE1hbmFnZXJcbipcbiogVGhlIEF1dGhvcml6YXRpb24gTWFuYWdlciBwcm92aWRlcyBhbiBlYXN5IHdheSB0byBtYW5hZ2UgdXNlciBhdXRoZW50aWNhdGlvbiAobG9nZ2luZyBpbiBhbmQgb3V0KSBhbmQgYXV0aG9yaXphdGlvbiAoa2VlcGluZyB0cmFjayBvZiB0b2tlbnMsIHNlc3Npb25zLCBhbmQgZ3JvdXBzKSBmb3IgcHJvamVjdHMuXG4qXG4qIFRoZSBBdXRob3JpemF0aW9uIE1hbmFnZXIgaXMgbW9zdCB1c2VmdWwgZm9yIFt0ZWFtIHByb2plY3RzXSguLi8uLi8uLi9nbG9zc2FyeS8jdGVhbSkgd2l0aCBhbiBhY2Nlc3MgbGV2ZWwgb2YgW0F1dGhlbnRpY2F0ZWRdKC4uLy4uLy4uL2dsb3NzYXJ5LyNhY2Nlc3MpLiBUaGVzZSBwcm9qZWN0cyBhcmUgYWNjZXNzZWQgYnkgW2VuZCB1c2Vyc10oLi4vLi4vLi4vZ2xvc3NhcnkvI3VzZXJzKSB3aG8gYXJlIG1lbWJlcnMgb2Ygb25lIG9yIG1vcmUgW2dyb3Vwc10oLi4vLi4vLi4vZ2xvc3NhcnkvI2dyb3VwcykuXG4qXG4qICMjIyMgVXNpbmcgdGhlIEF1dGhvcml6YXRpb24gTWFuYWdlclxuKlxuKiBUbyB1c2UgdGhlIEF1dGhvcml6YXRpb24gTWFuYWdlciwgaW5zdGFudGlhdGUgaXQuIFRoZW4sIG1ha2UgY2FsbHMgdG8gYW55IG9mIHRoZSBtZXRob2RzIHlvdSBuZWVkOlxuKlxuKiAgICAgICB2YXIgYXV0aE1nciA9IG5ldyBGLm1hbmFnZXIuQXV0aE1hbmFnZXIoe1xuKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuKiAgICAgICAgICAgdXNlck5hbWU6ICdlbmR1c2VyMScsXG4qICAgICAgICAgICBwYXNzd29yZDogJ3Bhc3N3MHJkJ1xuKiAgICAgICB9KTtcbiogICAgICAgYXV0aE1nci5sb2dpbigpLnRoZW4oZnVuY3Rpb24gKCkge1xuKiAgICAgICAgICAgYXV0aE1nci5nZXRDdXJyZW50VXNlclNlc3Npb25JbmZvKCk7XG4qICAgICAgIH0pO1xuKlxuKlxuKiBUaGUgYG9wdGlvbnNgIG9iamVjdCBwYXNzZWQgdG8gdGhlIGBGLm1hbmFnZXIuQXV0aE1hbmFnZXIoKWAgY2FsbCBjYW4gaW5jbHVkZTpcbipcbiogICAqIGBhY2NvdW50YDogVGhlIGFjY291bnQgaWQgZm9yIHRoaXMgYHVzZXJOYW1lYC4gSW4gdGhlIEVwaWNlbnRlciBVSSwgdGhpcyBpcyB0aGUgKipUZWFtIElEKiogKGZvciB0ZWFtIHByb2plY3RzKSBvciB0aGUgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuXG4qICAgKiBgdXNlck5hbWVgOiBFbWFpbCBvciB1c2VybmFtZSB0byB1c2UgZm9yIGxvZ2dpbmcgaW4uXG4qICAgKiBgcGFzc3dvcmRgOiBQYXNzd29yZCBmb3Igc3BlY2lmaWVkIGB1c2VyTmFtZWAuXG4qICAgKiBgcHJvamVjdGA6IFRoZSAqKlByb2plY3QgSUQqKiBmb3IgdGhlIHByb2plY3QgdG8gbG9nIHRoaXMgdXNlciBpbnRvLiBPcHRpb25hbC5cbiogICAqIGBncm91cElkYDogSWQgb2YgdGhlIGdyb3VwIHRvIHdoaWNoIGB1c2VyTmFtZWAgYmVsb25ncy4gUmVxdWlyZWQgZm9yIGVuZCB1c2VycyBpZiB0aGUgYHByb2plY3RgIGlzIHNwZWNpZmllZC5cbipcbiogSWYgeW91IHByZWZlciBzdGFydGluZyBmcm9tIGEgdGVtcGxhdGUsIHRoZSBFcGljZW50ZXIgSlMgTGlicyBbTG9naW4gQ29tcG9uZW50XSguLi8uLi8jY29tcG9uZW50cykgdXNlcyB0aGUgQXV0aG9yaXphdGlvbiBNYW5hZ2VyIGFzIHdlbGwuIFRoaXMgc2FtcGxlIEhUTUwgcGFnZSAoYW5kIGFzc29jaWF0ZWQgQ1NTIGFuZCBKUyBmaWxlcykgcHJvdmlkZXMgYSBsb2dpbiBmb3JtIGZvciB0ZWFtIG1lbWJlcnMgYW5kIGVuZCB1c2VycyBvZiB5b3VyIHByb2plY3QuIEl0IGFsc28gaW5jbHVkZXMgYSBncm91cCBzZWxlY3RvciBmb3IgZW5kIHVzZXJzIHRoYXQgYXJlIG1lbWJlcnMgb2YgbXVsdGlwbGUgZ3JvdXBzLlxuKi9cblxuJ3VzZSBzdHJpY3QnO1xudmFyIEF1dGhBZGFwdGVyID0gcmVxdWlyZSgnLi4vc2VydmljZS9hdXRoLWFwaS1zZXJ2aWNlJyk7XG52YXIgTWVtYmVyQWRhcHRlciA9IHJlcXVpcmUoJy4uL3NlcnZpY2UvbWVtYmVyLWFwaS1hZGFwdGVyJyk7XG52YXIgR3JvdXBTZXJ2aWNlID0gcmVxdWlyZSgnLi4vc2VydmljZS9ncm91cC1hcGktc2VydmljZScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG52YXIgX3BpY2sgPSByZXF1aXJlKCcuLi91dGlsL29iamVjdC11dGlsJykuX3BpY2s7XG52YXIgb2JqZWN0QXNzaWduID0gcmVxdWlyZSgnb2JqZWN0LWFzc2lnbicpO1xuXG52YXIgYXRvYiA9IHdpbmRvdy5hdG9iIHx8IHJlcXVpcmUoJ0Jhc2U2NCcpLmF0b2I7XG5cbnZhciBkZWZhdWx0cyA9IHtcbiAgICByZXF1aXJlc0dyb3VwOiB0cnVlXG59O1xuXG5mdW5jdGlvbiBBdXRoTWFuYWdlcihvcHRpb25zKSB7XG4gICAgb3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBkZWZhdWx0cywgb3B0aW9ucyk7XG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcihvcHRpb25zKTtcbiAgICB0aGlzLm9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMoKTtcblxuICAgIHRoaXMuYXV0aEFkYXB0ZXIgPSBuZXcgQXV0aEFkYXB0ZXIodGhpcy5vcHRpb25zKTtcbn1cblxudmFyIF9maW5kVXNlckluR3JvdXAgPSBmdW5jdGlvbiAobWVtYmVycywgaWQpIHtcbiAgICBmb3IgKHZhciBqID0gMDsgaiA8IG1lbWJlcnMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgaWYgKG1lbWJlcnNbal0udXNlcklkID09PSBpZCkge1xuICAgICAgICAgICAgcmV0dXJuIG1lbWJlcnNbal07XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG59O1xuXG5BdXRoTWFuYWdlci5wcm90b3R5cGUgPSAkLmV4dGVuZChBdXRoTWFuYWdlci5wcm90b3R5cGUsIHtcblxuICAgIC8qKlxuICAgICogTG9ncyB1c2VyIGluLlxuICAgICpcbiAgICAqICoqRXhhbXBsZSoqXG4gICAgKlxuICAgICogICAgICAgYXV0aE1nci5sb2dpbih7XG4gICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICogICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgKiAgICAgICAgICAgdXNlck5hbWU6ICdlbmR1c2VyMScsXG4gICAgKiAgICAgICAgICAgcGFzc3dvcmQ6ICdwYXNzdzByZCdcbiAgICAqICAgICAgIH0pXG4gICAgKiAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24oc3RhdHVzT2JqKSB7XG4gICAgKiAgICAgICAgICAgICAgIC8vIGlmIGVuZHVzZXIxIGJlbG9uZ3MgdG8gZXhhY3RseSBvbmUgZ3JvdXBcbiAgICAqICAgICAgICAgICAgICAgLy8gKG9yIGlmIHRoZSBsb2dpbigpIGNhbGwgaXMgbW9kaWZpZWQgdG8gaW5jbHVkZSB0aGUgZ3JvdXAgaWQpXG4gICAgKiAgICAgICAgICAgICAgIC8vIGNvbnRpbnVlIGhlcmVcbiAgICAqICAgICAgICAgICB9KVxuICAgICogICAgICAgICAgIC5mYWlsKGZ1bmN0aW9uKHN0YXR1c09iaikge1xuICAgICogICAgICAgICAgICAgICAvLyBpZiBlbmR1c2VyMSBiZWxvbmdzIHRvIG11bHRpcGxlIGdyb3VwcyxcbiAgICAqICAgICAgICAgICAgICAgLy8gdGhlIGxvZ2luKCkgY2FsbCBmYWlsc1xuICAgICogICAgICAgICAgICAgICAvLyBhbmQgcmV0dXJucyBhbGwgZ3JvdXBzIG9mIHdoaWNoIHRoZSB1c2VyIGlzIGEgbWVtYmVyXG4gICAgKiAgICAgICAgICAgICAgIGZvciAodmFyIGk9MDsgaSA8IHN0YXR1c09iai51c2VyR3JvdXBzLmxlbmd0aDsgaSsrKSB7XG4gICAgKiAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhzdGF0dXNPYmoudXNlckdyb3Vwc1tpXS5uYW1lLCBzdGF0dXNPYmoudXNlckdyb3Vwc1tpXS5ncm91cElkKTtcbiAgICAqICAgICAgICAgICAgICAgfVxuICAgICogICAgICAgICAgIH0pO1xuICAgICpcbiAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgKlxuICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy4gSWYgbm90IHBhc3NlZCBpbiB3aGVuIGNyZWF0aW5nIGFuIGluc3RhbmNlIG9mIHRoZSBtYW5hZ2VyIChgRi5tYW5hZ2VyLkF1dGhNYW5hZ2VyKClgKSwgdGhlc2Ugb3B0aW9ucyBzaG91bGQgaW5jbHVkZTpcbiAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLmFjY291bnQgVGhlIGFjY291bnQgaWQgZm9yIHRoaXMgYHVzZXJOYW1lYC4gSW4gdGhlIEVwaWNlbnRlciBVSSwgdGhpcyBpcyB0aGUgKipUZWFtIElEKiogKGZvciB0ZWFtIHByb2plY3RzKSBvciB0aGUgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy51c2VyTmFtZSBFbWFpbCBvciB1c2VybmFtZSB0byB1c2UgZm9yIGxvZ2dpbmcgaW4uXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5wYXNzd29yZCBQYXNzd29yZCBmb3Igc3BlY2lmaWVkIGB1c2VyTmFtZWAuXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5wcm9qZWN0IChPcHRpb25hbCkgVGhlICoqUHJvamVjdCBJRCoqIGZvciB0aGUgcHJvamVjdCB0byBsb2cgdGhpcyB1c2VyIGludG8uXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5ncm91cElkIFRoZSBpZCBvZiB0aGUgZ3JvdXAgdG8gd2hpY2ggYHVzZXJOYW1lYCBiZWxvbmdzLiBSZXF1aXJlZCBmb3IgW2VuZCB1c2Vyc10oLi4vLi4vLi4vZ2xvc3NhcnkvI3VzZXJzKSBpZiB0aGUgYHByb2plY3RgIGlzIHNwZWNpZmllZCBhbmQgaWYgdGhlIGVuZCB1c2VycyBhcmUgbWVtYmVycyBvZiBtdWx0aXBsZSBbZ3JvdXBzXSguLi8uLi8uLi9nbG9zc2FyeS8jZ3JvdXBzKSwgb3RoZXJ3aXNlIG9wdGlvbmFsLlxuICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAqL1xuICAgIGxvZ2luOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICB2YXIgbWUgPSB0aGlzO1xuICAgICAgICB2YXIgJGQgPSAkLkRlZmVycmVkKCk7XG4gICAgICAgIHZhciBzZXNzaW9uTWFuYWdlciA9IHRoaXMuc2Vzc2lvbk1hbmFnZXI7XG4gICAgICAgIHZhciBhZGFwdGVyT3B0aW9ucyA9IHNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMoeyBzdWNjZXNzOiAkLm5vb3AsIGVycm9yOiAkLm5vb3AgfSwgb3B0aW9ucyk7XG4gICAgICAgIHZhciBvdXRTdWNjZXNzID0gYWRhcHRlck9wdGlvbnMuc3VjY2VzcztcbiAgICAgICAgdmFyIG91dEVycm9yID0gYWRhcHRlck9wdGlvbnMuZXJyb3I7XG4gICAgICAgIHZhciBncm91cElkID0gYWRhcHRlck9wdGlvbnMuZ3JvdXBJZDtcblxuICAgICAgICB2YXIgZGVjb2RlVG9rZW4gPSBmdW5jdGlvbiAodG9rZW4pIHtcbiAgICAgICAgICAgIHZhciBlbmNvZGVkID0gdG9rZW4uc3BsaXQoJy4nKVsxXTtcbiAgICAgICAgICAgIHdoaWxlIChlbmNvZGVkLmxlbmd0aCAlIDQgIT09IDApIHsgLy9lc2xpbnQtZGlzYWJsZS1saW5lXG4gICAgICAgICAgICAgICAgZW5jb2RlZCArPSAnPSc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShhdG9iKGVuY29kZWQpKTtcbiAgICAgICAgfTtcblxuICAgICAgICB2YXIgaGFuZGxlR3JvdXBFcnJvciA9IGZ1bmN0aW9uIChtZXNzYWdlLCBzdGF0dXNDb2RlLCBkYXRhKSB7XG4gICAgICAgICAgICAvLyBsb2dvdXQgdGhlIHVzZXIgc2luY2UgaXQncyBpbiBhbiBpbnZhbGlkIHN0YXRlIHdpdGggbm8gZ3JvdXAgc2VsZWN0ZWRcbiAgICAgICAgICAgIG1lLmxvZ291dCgpLnRoZW4oZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHZhciBlcnJvciA9ICQuZXh0ZW5kKHRydWUsIHt9LCBkYXRhLCB7IHN0YXR1c1RleHQ6IG1lc3NhZ2UsIHN0YXR1czogc3RhdHVzQ29kZSB9KTtcbiAgICAgICAgICAgICAgICAkZC5yZWplY3QoZXJyb3IpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH07XG5cbiAgICAgICAgdmFyIGhhbmRsZVN1Y2Nlc3MgPSBmdW5jdGlvbiAocmVzcG9uc2UpIHtcbiAgICAgICAgICAgIHZhciB0b2tlbiA9IHJlc3BvbnNlLmFjY2Vzc190b2tlbjtcbiAgICAgICAgICAgIHZhciB1c2VySW5mbyA9IGRlY29kZVRva2VuKHRva2VuKTtcbiAgICAgICAgICAgIHZhciBvbGRHcm91cHMgPSBzZXNzaW9uTWFuYWdlci5nZXRTZXNzaW9uKGFkYXB0ZXJPcHRpb25zKS5ncm91cHMgfHwge307XG4gICAgICAgICAgICB2YXIgdXNlckdyb3VwT3B0cyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBhZGFwdGVyT3B0aW9ucywgeyBzdWNjZXNzOiAkLm5vb3AgfSk7XG4gICAgICAgICAgICB2YXIgZGF0YSA9IHsgYXV0aDogcmVzcG9uc2UsIHVzZXI6IHVzZXJJbmZvIH07XG4gICAgICAgICAgICB2YXIgcHJvamVjdCA9IGFkYXB0ZXJPcHRpb25zLnByb2plY3Q7XG4gICAgICAgICAgICB2YXIgaXNUZWFtTWVtYmVyID0gdXNlckluZm8ucGFyZW50X2FjY291bnRfaWQgPT09IG51bGw7XG4gICAgICAgICAgICB2YXIgcmVxdWlyZXNHcm91cCA9IGFkYXB0ZXJPcHRpb25zLnJlcXVpcmVzR3JvdXAgJiYgcHJvamVjdDtcblxuICAgICAgICAgICAgdmFyIHNlc3Npb25JbmZvID0ge1xuICAgICAgICAgICAgICAgIGF1dGhfdG9rZW46IHRva2VuLFxuICAgICAgICAgICAgICAgIGFjY291bnQ6IGFkYXB0ZXJPcHRpb25zLmFjY291bnQsXG4gICAgICAgICAgICAgICAgcHJvamVjdDogcHJvamVjdCxcbiAgICAgICAgICAgICAgICB1c2VySWQ6IHVzZXJJbmZvLnVzZXJfaWQsXG4gICAgICAgICAgICAgICAgZ3JvdXBzOiBvbGRHcm91cHMsXG4gICAgICAgICAgICAgICAgaXNUZWFtTWVtYmVyOiBpc1RlYW1NZW1iZXJcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICAvLyBUaGUgZ3JvdXAgaXMgbm90IHJlcXVpcmVkIGlmIHRoZSB1c2VyIGlzIG5vdCBsb2dnaW5nIGludG8gYSBwcm9qZWN0XG4gICAgICAgICAgICBpZiAoIXJlcXVpcmVzR3JvdXApIHtcbiAgICAgICAgICAgICAgICBzZXNzaW9uTWFuYWdlci5zYXZlU2Vzc2lvbihzZXNzaW9uSW5mbyk7XG4gICAgICAgICAgICAgICAgb3V0U3VjY2Vzcy5hcHBseSh0aGlzLCBbZGF0YV0pO1xuICAgICAgICAgICAgICAgICRkLnJlc29sdmUoZGF0YSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgaGFuZGxlR3JvdXBMaXN0ID0gZnVuY3Rpb24gKGdyb3VwTGlzdCkge1xuICAgICAgICAgICAgICAgIGRhdGEudXNlckdyb3VwcyA9IGdyb3VwTGlzdDtcblxuICAgICAgICAgICAgICAgIHZhciBncm91cCA9IG51bGw7XG4gICAgICAgICAgICAgICAgaWYgKGdyb3VwTGlzdC5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgaGFuZGxlR3JvdXBFcnJvcignVGhlIHVzZXIgaGFzIG5vIGdyb3VwcyBhc3NvY2lhdGVkIGluIHRoaXMgYWNjb3VudCcsIDQwMSwgZGF0YSk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGdyb3VwTGlzdC5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gU2VsZWN0IHRoZSBvbmx5IGdyb3VwXG4gICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXBMaXN0WzBdO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoZ3JvdXBMaXN0Lmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGdyb3VwSWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBmaWx0ZXJlZEdyb3VwcyA9ICQuZ3JlcChncm91cExpc3QsIGZ1bmN0aW9uIChyZXNHcm91cCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiByZXNHcm91cC5ncm91cElkID09PSBncm91cElkO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGZpbHRlcmVkR3JvdXBzLmxlbmd0aCA9PT0gMSA/IGZpbHRlcmVkR3JvdXBzWzBdIDogbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChncm91cCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBBIHRlYW0gbWVtYmVyIGRvZXMgbm90IGdldCB0aGUgZ3JvdXAgbWVtYmVycyBiZWNhdXNlIGlzIGNhbGxpbmcgdGhlIEdyb3VwIEFQSVxuICAgICAgICAgICAgICAgICAgICAvLyBidXQgaXQncyBhdXRvbWF0aWNhbGx5IGEgZmFjIHVzZXJcbiAgICAgICAgICAgICAgICAgICAgdmFyIGlzRmFjID0gaXNUZWFtTWVtYmVyID8gdHJ1ZSA6IF9maW5kVXNlckluR3JvdXAoZ3JvdXAubWVtYmVycywgdXNlckluZm8udXNlcl9pZCkucm9sZSA9PT0gJ2ZhY2lsaXRhdG9yJztcbiAgICAgICAgICAgICAgICAgICAgdmFyIGdyb3VwRGF0YSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwSWQ6IGdyb3VwLmdyb3VwSWQsXG4gICAgICAgICAgICAgICAgICAgICAgICBncm91cE5hbWU6IGdyb3VwLm5hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBpc0ZhYzogaXNGYWNcbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHNlc3Npb25JbmZvV2l0aEdyb3VwID0gb2JqZWN0QXNzaWduKHt9LCBzZXNzaW9uSW5mbywgZ3JvdXBEYXRhKTtcbiAgICAgICAgICAgICAgICAgICAgc2Vzc2lvbkluZm8uZ3JvdXBzW3Byb2plY3RdID0gZ3JvdXBEYXRhO1xuICAgICAgICAgICAgICAgICAgICBtZS5zZXNzaW9uTWFuYWdlci5zYXZlU2Vzc2lvbihzZXNzaW9uSW5mb1dpdGhHcm91cCwgYWRhcHRlck9wdGlvbnMpO1xuICAgICAgICAgICAgICAgICAgICBvdXRTdWNjZXNzLmFwcGx5KHRoaXMsIFtkYXRhXSk7XG4gICAgICAgICAgICAgICAgICAgICRkLnJlc29sdmUoZGF0YSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaGFuZGxlR3JvdXBFcnJvcignVGhpcyB1c2VyIGlzIGFzc29jaWF0ZWQgd2l0aCBtb3JlIHRoYW4gb25lIGdyb3VwLiBQbGVhc2Ugc3BlY2lmeSBhIGdyb3VwIGlkIHRvIGxvZyBpbnRvIGFuZCB0cnkgYWdhaW4nLCA0MDMsIGRhdGEpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIGlmICghaXNUZWFtTWVtYmVyKSB7XG4gICAgICAgICAgICAgICAgbWUuZ2V0VXNlckdyb3Vwcyh7IHVzZXJJZDogdXNlckluZm8udXNlcl9pZCwgdG9rZW46IHRva2VuIH0sIHVzZXJHcm91cE9wdHMpXG4gICAgICAgICAgICAgICAgICAgIC50aGVuKGhhbmRsZUdyb3VwTGlzdCwgJGQucmVqZWN0KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdmFyIG9wdHMgPSBvYmplY3RBc3NpZ24oe30sIHVzZXJHcm91cE9wdHMsIHsgdG9rZW46IHRva2VuIH0pO1xuICAgICAgICAgICAgICAgIHZhciBncm91cFNlcnZpY2UgPSBuZXcgR3JvdXBTZXJ2aWNlKG9wdHMpO1xuICAgICAgICAgICAgICAgIGdyb3VwU2VydmljZS5nZXRHcm91cHMoeyBhY2NvdW50OiBhZGFwdGVyT3B0aW9ucy5hY2NvdW50LCBwcm9qZWN0OiBwcm9qZWN0IH0pXG4gICAgICAgICAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChncm91cHMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEdyb3VwIEFQSSByZXR1cm5zIGlkIGluc3RlYWQgb2YgZ3JvdXBJZFxuICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBzLmZvckVhY2goZnVuY3Rpb24gKGdyb3VwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuZ3JvdXBJZCA9IGdyb3VwLmlkO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBoYW5kbGVHcm91cExpc3QoZ3JvdXBzKTtcbiAgICAgICAgICAgICAgICAgICAgfSwgJGQucmVqZWN0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICBhZGFwdGVyT3B0aW9ucy5zdWNjZXNzID0gaGFuZGxlU3VjY2VzcztcbiAgICAgICAgYWRhcHRlck9wdGlvbnMuZXJyb3IgPSBmdW5jdGlvbiAocmVzcG9uc2UpIHtcbiAgICAgICAgICAgIGlmIChhZGFwdGVyT3B0aW9ucy5hY2NvdW50KSB7XG4gICAgICAgICAgICAgICAgLy8gVHJ5IHRvIGxvZ2luIGFzIGEgc3lzdGVtIHVzZXJcbiAgICAgICAgICAgICAgICBhZGFwdGVyT3B0aW9ucy5hY2NvdW50ID0gbnVsbDtcbiAgICAgICAgICAgICAgICBhZGFwdGVyT3B0aW9ucy5lcnJvciA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgb3V0RXJyb3IuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgJGQucmVqZWN0KHJlc3BvbnNlKTtcbiAgICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAgICAgbWUuYXV0aEFkYXB0ZXIubG9naW4oYWRhcHRlck9wdGlvbnMpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgb3V0RXJyb3IuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICRkLnJlamVjdChyZXNwb25zZSk7XG4gICAgICAgIH07XG5cbiAgICAgICAgdGhpcy5hdXRoQWRhcHRlci5sb2dpbihhZGFwdGVyT3B0aW9ucyk7XG4gICAgICAgIHJldHVybiAkZC5wcm9taXNlKCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICogTG9ncyB1c2VyIG91dCBieSBjbGVhcmluZyBhbGwgc2Vzc2lvbiBpbmZvcm1hdGlvbi5cbiAgICAqXG4gICAgKiAqKkV4YW1wbGUqKlxuICAgICpcbiAgICAqICAgICAgIGF1dGhNZ3IubG9nb3V0KCk7XG4gICAgKlxuICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAqXG4gICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAqL1xuICAgIGxvZ291dDogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgdmFyIGFkYXB0ZXJPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKG9wdGlvbnMpO1xuXG4gICAgICAgIHZhciByZW1vdmVDb29raWVGbiA9IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgbWUuc2Vzc2lvbk1hbmFnZXIucmVtb3ZlU2Vzc2lvbigpO1xuICAgICAgICB9O1xuXG4gICAgICAgIHJldHVybiB0aGlzLmF1dGhBZGFwdGVyLmxvZ291dChhZGFwdGVyT3B0aW9ucykudGhlbihyZW1vdmVDb29raWVGbik7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGV4aXN0aW5nIHVzZXIgYWNjZXNzIHRva2VuIGlmIHRoZSB1c2VyIGlzIGFscmVhZHkgbG9nZ2VkIGluLiBPdGhlcndpc2UsIGxvZ3MgdGhlIHVzZXIgaW4sIGNyZWF0aW5nIGEgbmV3IHVzZXIgYWNjZXNzIHRva2VuLCBhbmQgcmV0dXJucyB0aGUgbmV3IHRva2VuLiAoU2VlIFttb3JlIGJhY2tncm91bmQgb24gYWNjZXNzIHRva2Vuc10oLi4vLi4vLi4vcHJvamVjdF9hY2Nlc3MvKSkuXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgICBhdXRoTWdyLmdldFRva2VuKClcbiAgICAgKiAgICAgICAgICAudGhlbihmdW5jdGlvbiAodG9rZW4pIHtcbiAgICAgKiAgICAgICAgICAgICAgY29uc29sZS5sb2coJ015IHRva2VuIGlzICcsIHRva2VuKTtcbiAgICAgKiAgICAgICAgICB9KTtcbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAqL1xuICAgIGdldFRva2VuOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMob3B0aW9ucyk7XG5cbiAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldFNlc3Npb24oaHR0cE9wdGlvbnMpO1xuICAgICAgICB2YXIgJGQgPSAkLkRlZmVycmVkKCk7XG4gICAgICAgIGlmIChzZXNzaW9uLmF1dGhfdG9rZW4pIHtcbiAgICAgICAgICAgICRkLnJlc29sdmUoc2Vzc2lvbi5hdXRoX3Rva2VuKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMubG9naW4oaHR0cE9wdGlvbnMpLnRoZW4oJGQucmVzb2x2ZSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuICRkLnByb21pc2UoKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhbiBhcnJheSBvZiBncm91cCByZWNvcmRzLCBvbmUgZm9yIGVhY2ggZ3JvdXAgb2Ygd2hpY2ggdGhlIGN1cnJlbnQgdXNlciBpcyBhIG1lbWJlci4gRWFjaCBncm91cCByZWNvcmQgaW5jbHVkZXMgdGhlIGdyb3VwIGBuYW1lYCwgYGFjY291bnRgLCBgcHJvamVjdGAsIGFuZCBgZ3JvdXBJZGAuXG4gICAgICpcbiAgICAgKiBJZiBzb21lIGVuZCB1c2VycyBpbiB5b3VyIHByb2plY3QgYXJlIG1lbWJlcnMgb2YgbXVsdGlwbGUgZ3JvdXBzLCB0aGlzIGlzIGEgdXNlZnVsIG1ldGhvZCB0byBjYWxsIG9uIHlvdXIgcHJvamVjdCdzIGxvZ2luIHBhZ2UuIFdoZW4gdGhlIHVzZXIgYXR0ZW1wdHMgdG8gbG9nIGluLCB5b3UgY2FuIHVzZSB0aGlzIHRvIGRpc3BsYXkgdGhlIGdyb3VwcyBvZiB3aGljaCB0aGUgdXNlciBpcyBtZW1iZXIsIGFuZCBoYXZlIHRoZSB1c2VyIHNlbGVjdCB0aGUgY29ycmVjdCBncm91cCB0byBsb2cgaW4gdG8gZm9yIHRoaXMgc2Vzc2lvbi5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgIC8vIGdldCBncm91cHMgZm9yIGN1cnJlbnQgdXNlclxuICAgICAqICAgICAgdmFyIHNlc3Npb25PYmogPSBhdXRoTWdyLmdldEN1cnJlbnRVc2VyU2Vzc2lvbkluZm8oKTtcbiAgICAgKiAgICAgIGF1dGhNZ3IuZ2V0VXNlckdyb3Vwcyh7IHVzZXJJZDogc2Vzc2lvbk9iai51c2VySWQsIHRva2VuOiBzZXNzaW9uT2JqLmF1dGhfdG9rZW4gfSlcbiAgICAgKiAgICAgICAgICAudGhlbihmdW5jdGlvbiAoZ3JvdXBzKSB7XG4gICAgICogICAgICAgICAgICAgIGZvciAodmFyIGk9MDsgaSA8IGdyb3Vwcy5sZW5ndGg7IGkrKylcbiAgICAgKiAgICAgICAgICAgICAgICAgIHsgY29uc29sZS5sb2coZ3JvdXBzW2ldLm5hbWUpOyB9XG4gICAgICogICAgICAgICAgfSk7XG4gICAgICpcbiAgICAgKiAgICAgIC8vIGdldCBncm91cHMgZm9yIHBhcnRpY3VsYXIgdXNlclxuICAgICAqICAgICAgYXV0aE1nci5nZXRVc2VyR3JvdXBzKHt1c2VySWQ6ICdiMWMxOWRkYS0yZDJlLTQ3NzctYWQ1ZC0zOTI5ZjE3ZTg2ZDMnLCB0b2tlbjogc2F2ZWRQcm9qQWNjZXNzVG9rZW4gfSk7XG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwYXJhbXMgT2JqZWN0IHdpdGggYSB1c2VySWQgYW5kIHRva2VuIHByb3BlcnRpZXMuXG4gICAgICogQHBhcmFtIHtTdHJpbmd9IHBhcmFtcy51c2VySWQgVGhlIHVzZXJJZC4gSWYgbG9va2luZyB1cCBncm91cHMgZm9yIHRoZSBjdXJyZW50bHkgbG9nZ2VkIGluIHVzZXIsIHRoaXMgaXMgaW4gdGhlIHNlc3Npb24gaW5mb3JtYXRpb24uIE90aGVyd2lzZSwgcGFzcyBhIHN0cmluZy5cbiAgICAgKiBAcGFyYW0ge1N0cmluZ30gcGFyYW1zLnRva2VuIFRoZSBhdXRob3JpemF0aW9uIGNyZWRlbnRpYWxzIChhY2Nlc3MgdG9rZW4pIHRvIHVzZSBmb3IgY2hlY2tpbmcgdGhlIGdyb3VwcyBmb3IgdGhpcyB1c2VyLiBJZiBsb29raW5nIHVwIGdyb3VwcyBmb3IgdGhlIGN1cnJlbnRseSBsb2dnZWQgaW4gdXNlciwgdGhpcyBpcyBpbiB0aGUgc2Vzc2lvbiBpbmZvcm1hdGlvbi4gQSB0ZWFtIG1lbWJlcidzIHRva2VuIG9yIGEgcHJvamVjdCBhY2Nlc3MgdG9rZW4gY2FuIGFjY2VzcyBhbGwgdGhlIGdyb3VwcyBmb3IgYWxsIGVuZCB1c2VycyBpbiB0aGUgdGVhbSBvciBwcm9qZWN0LlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgKi9cbiAgICBnZXRVc2VyR3JvdXBzOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgIHZhciBhZGFwdGVyT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyh7IHN1Y2Nlc3M6ICQubm9vcCB9LCBvcHRpb25zKTtcbiAgICAgICAgdmFyICRkID0gJC5EZWZlcnJlZCgpO1xuICAgICAgICB2YXIgb3V0U3VjY2VzcyA9IGFkYXB0ZXJPcHRpb25zLnN1Y2Nlc3M7XG5cbiAgICAgICAgYWRhcHRlck9wdGlvbnMuc3VjY2VzcyA9IGZ1bmN0aW9uIChtZW1iZXJJbmZvKSB7XG4gICAgICAgICAgICAvLyBUaGUgbWVtYmVyIEFQSSBpcyBhdCB0aGUgYWNjb3VudCBzY29wZSwgd2UgZmlsdGVyIGJ5IHByb2plY3RcbiAgICAgICAgICAgIGlmIChhZGFwdGVyT3B0aW9ucy5wcm9qZWN0KSB7XG4gICAgICAgICAgICAgICAgbWVtYmVySW5mbyA9ICQuZ3JlcChtZW1iZXJJbmZvLCBmdW5jdGlvbiAoZ3JvdXApIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGdyb3VwLnByb2plY3QgPT09IGFkYXB0ZXJPcHRpb25zLnByb2plY3Q7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIG91dFN1Y2Nlc3MuYXBwbHkodGhpcywgW21lbWJlckluZm9dKTtcbiAgICAgICAgICAgICRkLnJlc29sdmUobWVtYmVySW5mbyk7XG4gICAgICAgIH07XG5cbiAgICAgICAgdmFyIG1lbWJlckFkYXB0ZXIgPSBuZXcgTWVtYmVyQWRhcHRlcih7IHRva2VuOiBwYXJhbXMudG9rZW4sIHNlcnZlcjogYWRhcHRlck9wdGlvbnMuc2VydmVyIH0pO1xuICAgICAgICBtZW1iZXJBZGFwdGVyLmdldEdyb3Vwc0ZvclVzZXIocGFyYW1zLCBhZGFwdGVyT3B0aW9ucykuZmFpbCgkZC5yZWplY3QpO1xuICAgICAgICByZXR1cm4gJGQucHJvbWlzZSgpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHNlc3Npb24gaW5mb3JtYXRpb24gZm9yIHRoZSBjdXJyZW50IHVzZXIsIGluY2x1ZGluZyB0aGUgYHVzZXJJZGAsIGBhY2NvdW50YCwgYHByb2plY3RgLCBgZ3JvdXBJZGAsIGBncm91cE5hbWVgLCBgaXNGYWNgICh3aGV0aGVyIHRoZSBlbmQgdXNlciBpcyBhIGZhY2lsaXRhdG9yIG9mIHRoaXMgZ3JvdXApLCBhbmQgYGF1dGhfdG9rZW5gICh1c2VyIGFjY2VzcyB0b2tlbikuXG4gICAgICpcbiAgICAgKiAqSW1wb3J0YW50KjogVGhpcyBtZXRob2QgaXMgc3luY2hyb25vdXMuIFRoZSBzZXNzaW9uIGluZm9ybWF0aW9uIGlzIHJldHVybmVkIGltbWVkaWF0ZWx5IGluIGFuIG9iamVjdDsgbm8gY2FsbGJhY2tzIG9yIHByb21pc2VzIGFyZSBuZWVkZWQuXG4gICAgICpcbiAgICAgKiBTZXNzaW9uIGluZm9ybWF0aW9uIGlzIHN0b3JlZCBpbiBhIGNvb2tpZSBpbiB0aGUgYnJvd3Nlci5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgIHZhciBzZXNzaW9uT2JqID0gYXV0aE1nci5nZXRDdXJyZW50VXNlclNlc3Npb25JbmZvKCk7XG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICogQHJldHVybiB7T2JqZWN0fSBzZXNzaW9uIGluZm9ybWF0aW9uXG4gICAgICovXG4gICAgZ2V0Q3VycmVudFVzZXJTZXNzaW9uSW5mbzogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgdmFyIGFkYXB0ZXJPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKHsgc3VjY2VzczogJC5ub29wIH0sIG9wdGlvbnMpO1xuICAgICAgICByZXR1cm4gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRTZXNzaW9uKGFkYXB0ZXJPcHRpb25zKTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgKiBBZGRzIG9uZSBvciBtb3JlIGdyb3VwcyB0byB0aGUgY3VycmVudCBzZXNzaW9uLiBcbiAgICAgKlxuICAgICAqIFRoaXMgbWV0aG9kIGFzc3VtZXMgdGhhdCB0aGUgcHJvamVjdCBhbmQgZ3JvdXAgZXhpc3QgYW5kIHRoZSB1c2VyIHNwZWNpZmllZCBpbiB0aGUgc2Vzc2lvbiBpcyBwYXJ0IG9mIHRoaXMgcHJvamVjdCBhbmQgZ3JvdXAuXG4gICAgICpcbiAgICAgKiBSZXR1cm5zIHRoZSBuZXcgc2Vzc2lvbiBvYmplY3QuXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgICBhdXRoTWdyLmFkZEdyb3Vwcyh7IHByb2plY3Q6ICdoZWxsby13b3JsZCcsIGdyb3VwTmFtZTogJ2dyb3VwTmFtZScsIGdyb3VwSWQ6ICdncm91cElkJyB9KTtcbiAgICAgKiAgICAgIGF1dGhNZ3IuYWRkR3JvdXBzKFt7IHByb2plY3Q6ICdoZWxsby13b3JsZCcsIGdyb3VwTmFtZTogJ2dyb3VwTmFtZScsIGdyb3VwSWQ6ICdncm91cElkJyB9LCB7IHByb2plY3Q6ICdoZWxsby13b3JsZCcsIGdyb3VwTmFtZTogJy4uLicgfV0pO1xuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKiBAcGFyYW0ge29iamVjdHxhcnJheX0gZ3JvdXBzIChSZXF1aXJlZCkgVGhlIGdyb3VwIG9iamVjdCBtdXN0IGNvbnRhaW4gdGhlIGBwcm9qZWN0YCAoKipQcm9qZWN0IElEKiopIGFuZCBgZ3JvdXBOYW1lYCBwcm9wZXJ0aWVzLiBJZiBwYXNzaW5nIGFuIGFycmF5IG9mIHN1Y2ggb2JqZWN0cywgYWxsIG9mIHRoZSBvYmplY3RzIG11c3QgY29udGFpbiAqZGlmZmVyZW50KiBgcHJvamVjdGAgKCoqUHJvamVjdCBJRCoqKSB2YWx1ZXM6IGFsdGhvdWdoIGVuZCB1c2VycyBtYXkgYmUgbG9nZ2VkIGluIHRvIG11bHRpcGxlIHByb2plY3RzIGF0IG9uY2UsIHRoZXkgbWF5IG9ubHkgYmUgbG9nZ2VkIGluIHRvIG9uZSBncm91cCBwZXIgcHJvamVjdCBhdCBhIHRpbWUuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGdyb3VwLmlzRmFjIChvcHRpb25hbCkgRGVmYXVsdHMgdG8gYGZhbHNlYC4gU2V0IHRvIGB0cnVlYCBpZiB0aGUgdXNlciBpbiB0aGUgc2Vzc2lvbiBzaG91bGQgYmUgYSBmYWNpbGl0YXRvciBpbiB0aGlzIGdyb3VwLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBncm91cC5ncm91cElkIChvcHRpb25hbCkgRGVmYXVsdHMgdG8gdW5kZWZpbmVkLiBOZWVkZWQgbW9zdGx5IGZvciB0aGUgTWVtYmVycyBBUEkuXG4gICAgICogQHJldHVybiB7T2JqZWN0fSBzZXNzaW9uIGluZm9ybWF0aW9uXG4gICAgKi9cbiAgICBhZGRHcm91cHM6IGZ1bmN0aW9uIChncm91cHMpIHtcbiAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLmdldEN1cnJlbnRVc2VyU2Vzc2lvbkluZm8oKTtcbiAgICAgICAgdmFyIGlzQXJyYXkgPSBBcnJheS5pc0FycmF5KGdyb3Vwcyk7XG4gICAgICAgIGdyb3VwcyA9IGlzQXJyYXkgPyBncm91cHMgOiBbZ3JvdXBzXTtcblxuICAgICAgICAkLmVhY2goZ3JvdXBzLCBmdW5jdGlvbiAoaW5kZXgsIGdyb3VwKSB7XG4gICAgICAgICAgICB2YXIgZXh0ZW5kZWRHcm91cCA9ICQuZXh0ZW5kKHt9LCB7IGlzRmFjOiBmYWxzZSB9LCBncm91cCk7XG4gICAgICAgICAgICB2YXIgcHJvamVjdCA9IGV4dGVuZGVkR3JvdXAucHJvamVjdDtcbiAgICAgICAgICAgIHZhciB2YWxpZFByb3BzID0gWydncm91cE5hbWUnLCAnZ3JvdXBJZCcsICdpc0ZhYyddO1xuICAgICAgICAgICAgaWYgKCFwcm9qZWN0IHx8ICFleHRlbmRlZEdyb3VwLmdyb3VwTmFtZSkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTm8gcHJvamVjdCBvciBncm91cE5hbWUgc3BlY2lmaWVkLicpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gZmlsdGVyIG9iamVjdFxuICAgICAgICAgICAgZXh0ZW5kZWRHcm91cCA9IF9waWNrKGV4dGVuZGVkR3JvdXAsIHZhbGlkUHJvcHMpO1xuICAgICAgICAgICAgc2Vzc2lvbi5ncm91cHNbcHJvamVjdF0gPSBleHRlbmRlZEdyb3VwO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5zZXNzaW9uTWFuYWdlci5zYXZlU2Vzc2lvbihzZXNzaW9uKTtcbiAgICAgICAgcmV0dXJuIHNlc3Npb247XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzID0gQXV0aE1hbmFnZXI7XG4iLCIndXNlIHN0cmljdCc7XG5cbi8qKlxuICogIyMgQ2hhbm5lbCBNYW5hZ2VyXG4gKlxuICogVGhlcmUgYXJlIHR3byBtYWluIHVzZSBjYXNlcyBmb3IgdGhlIGNoYW5uZWw6IGV2ZW50IG5vdGlmaWNhdGlvbnMgYW5kIGNoYXQgbWVzc2FnZXMuXG4gKlxuICogVGhlIENoYW5uZWwgTWFuYWdlciBpcyBhIHdyYXBwZXIgYXJvdW5kIHRoZSBkZWZhdWx0IFtjb21ldGQgSmF2YVNjcmlwdCBsaWJyYXJ5XShodHRwOi8vZG9jcy5jb21ldGQub3JnLzIvcmVmZXJlbmNlL2phdmFzY3JpcHQuaHRtbCksIGAkLmNvbWV0ZGAuIEl0IHByb3ZpZGVzIGEgZmV3IG5pY2UgZmVhdHVyZXMgdGhhdCBgJC5jb21ldGRgIGRvZXNuJ3QsIGluY2x1ZGluZzpcbiAqXG4gKiAqIEF1dG9tYXRpYyByZS1zdWJzY3JpcHRpb24gdG8gY2hhbm5lbHMgaWYgeW91IGxvc2UgeW91ciBjb25uZWN0aW9uXG4gKiAqIE9ubGluZSAvIE9mZmxpbmUgbm90aWZpY2F0aW9uc1xuICogKiAnRXZlbnRzJyBmb3IgY29tZXRkIG5vdGlmaWNhdGlvbnMgKGluc3RlYWQgb2YgaGF2aW5nIHRvIGxpc3RlbiBvbiBzcGVjaWZpYyBtZXRhIGNoYW5uZWxzKVxuICpcbiAqIFdoaWxlIHlvdSBjYW4gd29yayBkaXJlY3RseSB3aXRoIHRoZSBDaGFubmVsIE1hbmFnZXIgdGhyb3VnaCBOb2RlLmpzIChmb3IgZXhhbXBsZSwgYHJlcXVpcmUoJ21hbmFnZXIvY2hhbm5lbC1tYW5hZ2VyJylgKSAtLSBvciBldmVuIHdvcmsgZGlyZWN0bHkgd2l0aCBgJC5jb21ldGRgIGFuZCBFcGljZW50ZXIncyB1bmRlcmx5aW5nIFtQdXNoIENoYW5uZWwgQVBJXSguLi8uLi8uLi9yZXN0X2FwaXMvbXVsdGlwbGF5ZXIvY2hhbm5lbC8pIC0tIG1vc3Qgb2Z0ZW4gaXQgd2lsbCBiZSBlYXNpZXN0IHRvIHdvcmsgd2l0aCB0aGUgW0VwaWNlbnRlciBDaGFubmVsIE1hbmFnZXJdKC4uL2VwaWNlbnRlci1jaGFubmVsLW1hbmFnZXIvKS4gVGhlIEVwaWNlbnRlciBDaGFubmVsIE1hbmFnZXIgaXMgYSB3cmFwcGVyIHRoYXQgaW5zdGFudGlhdGVzIGEgQ2hhbm5lbCBNYW5hZ2VyIHdpdGggRXBpY2VudGVyLXNwZWNpZmljIGRlZmF1bHRzLlxuICpcbiAqIFlvdSdsbCBuZWVkIHRvIGluY2x1ZGUgdGhlIGBlcGljZW50ZXItbXVsdGlwbGF5ZXItZGVwZW5kZW5jaWVzLmpzYCBsaWJyYXJ5IGluIGFkZGl0aW9uIHRvIHRoZSBgZXBpY2VudGVyLmpzYCBsaWJyYXJ5IGluIHlvdXIgcHJvamVjdCB0byB1c2UgdGhlIENoYW5uZWwgTWFuYWdlci4gKFNlZSBbSW5jbHVkaW5nIEVwaWNlbnRlci5qc10oLi4vLi4vI2luY2x1ZGUpLilcbiAqXG4gKiBUbyB1c2UgdGhlIENoYW5uZWwgTWFuYWdlciBpbiBjbGllbnQtc2lkZSBKYXZhU2NyaXB0LCBpbnN0YW50aWF0ZSB0aGUgW0VwaWNlbnRlciBDaGFubmVsIE1hbmFnZXJdKC4uL2VwaWNlbnRlci1jaGFubmVsLW1hbmFnZXIvKSwgZ2V0IHRoZSBjaGFubmVsLCB0aGVuIHVzZSB0aGUgY2hhbm5lbCdzIGBzdWJzY3JpYmUoKWAgYW5kIGBwdWJsaXNoKClgIG1ldGhvZHMgdG8gc3Vic2NyaWJlIHRvIHRvcGljcyBvciBwdWJsaXNoIGRhdGEgdG8gdG9waWNzLlxuICpcbiAqICAgICAgICB2YXIgY20gPSBuZXcgRi5tYW5hZ2VyLkNoYW5uZWxNYW5hZ2VyKCk7XG4gKiAgICAgICAgdmFyIGNoYW5uZWwgPSBjbS5nZXRDaGFubmVsKCk7XG4gKlxuICogICAgICAgIGNoYW5uZWwuc3Vic2NyaWJlKCd0b3BpYycsIGNhbGxiYWNrKTtcbiAqICAgICAgICBjaGFubmVsLnB1Ymxpc2goJ3RvcGljJywgeyBteURhdGE6IDEwMCB9KTtcbiAqXG4gKiBUaGUgcGFyYW1ldGVycyBmb3IgaW5zdGFudGlhdGluZyBhIENoYW5uZWwgTWFuYWdlciBpbmNsdWRlOlxuICpcbiAqICogYG9wdGlvbnNgIFRoZSBvcHRpb25zIG9iamVjdCB0byBjb25maWd1cmUgdGhlIENoYW5uZWwgTWFuYWdlci4gQmVzaWRlcyB0aGUgY29tbW9uIG9wdGlvbnMgbGlzdGVkIGhlcmUsIHNlZSBodHRwOi8vZG9jcy5jb21ldGQub3JnL3JlZmVyZW5jZS9qYXZhc2NyaXB0Lmh0bWwgZm9yIG90aGVyIHN1cHBvcnRlZCBvcHRpb25zLlxuICogKiBgb3B0aW9ucy51cmxgIFRoZSBDb21ldGQgZW5kcG9pbnQgVVJMLlxuICogKiBgb3B0aW9ucy53ZWJzb2NrZXRFbmFibGVkYCBXaGV0aGVyIHdlYnNvY2tldCBzdXBwb3J0IGlzIGFjdGl2ZSAoYm9vbGVhbikuXG4gKiAqIGBvcHRpb25zLmNoYW5uZWxgIE90aGVyIGRlZmF1bHRzIHRvIHBhc3Mgb24gdG8gaW5zdGFuY2VzIG9mIHRoZSB1bmRlcmx5aW5nIENoYW5uZWwgU2VydmljZS4gU2VlIFtDaGFubmVsIFNlcnZpY2VdKC4uL2NoYW5uZWwtc2VydmljZS8pIGZvciBkZXRhaWxzLlxuICpcbiAqL1xuXG52YXIgQ2hhbm5lbCA9IHJlcXVpcmUoJy4uL3NlcnZpY2UvY2hhbm5lbC1zZXJ2aWNlJyk7XG52YXIgU2Vzc2lvbk1hbmFnZXIgPSByZXF1aXJlKCcuLi9zdG9yZS9zZXNzaW9uLW1hbmFnZXInKTtcblxudmFyIENoYW5uZWxNYW5hZ2VyID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICBpZiAoISQuY29tZXRkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQ29tZXRkIGxpYnJhcnkgbm90IGZvdW5kLiBQbGVhc2UgaW5jbHVkZSBlcGljZW50ZXItbXVsdGlwbGF5ZXItZGVwZW5kZW5jaWVzLmpzJyk7XG4gICAgfVxuICAgIGlmICghb3B0aW9ucyB8fCAhb3B0aW9ucy51cmwpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQbGVhc2UgcHJvdmlkZSBhbiB1cmwgZm9yIHRoZSBjb21ldGQgc2VydmVyJyk7XG4gICAgfVxuXG4gICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIENvbWV0ZCBlbmRwb2ludCBVUkwuXG4gICAgICAgICAqIEB0eXBlIHtzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICB1cmw6ICcnLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgbG9nIGxldmVsIGZvciB0aGUgY2hhbm5lbCAobG9ncyB0byBjb25zb2xlKS5cbiAgICAgICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGxvZ0xldmVsOiAnaW5mbycsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFdoZXRoZXIgd2Vic29ja2V0IHN1cHBvcnQgaXMgYWN0aXZlLiBEZWZhdWx0cyB0byBgdHJ1ZWAuXG4gICAgICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAgICAgKi9cbiAgICAgICAgd2Vic29ja2V0RW5hYmxlZDogdHJ1ZSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogV2hldGhlciB0aGUgQUNLIGV4dGVuc2lvbiBpcyBlbmFibGVkLiBTZWUgaHR0cHM6Ly9kb2NzLmNvbWV0ZC5vcmcvY3VycmVudC9yZWZlcmVuY2UvI19leHRlbnNpb25zX2Fja25vd2xlZGdlIGZvciBtb3JlIGluZm8uXG4gICAgICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAgICAgKi9cbiAgICAgICAgYWNrRW5hYmxlZDogdHJ1ZSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogSWYgZmFsc2UgZWFjaCBpbnN0YW5jZSBvZiBDaGFubmVsIHdpbGwgaGF2ZSBhIHNlcGFyYXRlIGNvbWV0ZCBjb25uZWN0aW9uIHRvIHNlcnZlciwgd2hpY2ggY291bGQgYmUgbm9pc3kuIFNldCB0byB0cnVlIHRvIHJlLXVzZSB0aGUgc2FtZSBjb25uZWN0aW9uIGFjcm9zcyBpbnN0YW5jZXMuXG4gICAgICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAgICAgKi9cbiAgICAgICAgc2hhcmVDb25uZWN0aW9uOiBmYWxzZSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT3RoZXIgZGVmYXVsdHMgdG8gcGFzcyBvbiB0byBpbnN0YW5jZXMgb2YgdGhlIHVuZGVybHlpbmcgW0NoYW5uZWwgU2VydmljZV0oLi4vY2hhbm5lbC1zZXJ2aWNlLyksIHdoaWNoIGFyZSBjcmVhdGVkIHRocm91Z2ggYGdldENoYW5uZWwoKWAuXG4gICAgICAgICAqIEB0eXBlIHtvYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICBjaGFubmVsOiB7XG5cbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT3B0aW9ucyB0byBwYXNzIHRvIHRoZSBjaGFubmVsIGhhbmRzaGFrZS5cbiAgICAgICAgICpcbiAgICAgICAgICogRm9yIGV4YW1wbGUsIHRoZSBbRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlcl0oLi4vZXBpY2VudGVyLWNoYW5uZWwtbWFuYWdlci8pIHBhc3NlcyBgZXh0YCBhbmQgYXV0aG9yaXphdGlvbiBpbmZvcm1hdGlvbi4gTW9yZSBpbmZvcm1hdGlvbiBvbiBwb3NzaWJsZSBvcHRpb25zIGlzIGluIHRoZSBkZXRhaWxzIG9mIHRoZSB1bmRlcmx5aW5nIFtQdXNoIENoYW5uZWwgQVBJXSguLi8uLi8uLi9yZXN0X2FwaXMvbXVsdGlwbGF5ZXIvY2hhbm5lbC8pLlxuICAgICAgICAgKlxuICAgICAgICAgKiBAdHlwZSB7b2JqZWN0fVxuICAgICAgICAgKi9cbiAgICAgICAgaGFuZHNoYWtlOiB1bmRlZmluZWRcbiAgICB9O1xuICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICB2YXIgZGVmYXVsdENvbWV0T3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgb3B0aW9ucyk7XG4gICAgdGhpcy5jdXJyZW50U3Vic2NyaXB0aW9ucyA9IFtdO1xuICAgIHRoaXMub3B0aW9ucyA9IGRlZmF1bHRDb21ldE9wdGlvbnM7XG5cbiAgICBpZiAoZGVmYXVsdENvbWV0T3B0aW9ucy5zaGFyZUNvbm5lY3Rpb24gJiYgQ2hhbm5lbE1hbmFnZXIucHJvdG90eXBlLl9jb21ldGQpIHtcbiAgICAgICAgdGhpcy5jb21ldGQgPSBDaGFubmVsTWFuYWdlci5wcm90b3R5cGUuX2NvbWV0ZDtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIHZhciBjb21ldGQgPSBuZXcgJC5Db21ldEQoKTtcbiAgICBDaGFubmVsTWFuYWdlci5wcm90b3R5cGUuX2NvbWV0ZCA9IGNvbWV0ZDtcblxuICAgIGNvbWV0ZC53ZWJzb2NrZXRFbmFibGVkID0gZGVmYXVsdENvbWV0T3B0aW9ucy53ZWJzb2NrZXRFbmFibGVkO1xuICAgIGNvbWV0ZC5hY2tFbmFibGVkID0gZGVmYXVsdENvbWV0T3B0aW9ucy5hY2tFbmFibGVkO1xuXG4gICAgdGhpcy5pc0Nvbm5lY3RlZCA9IGZhbHNlO1xuICAgIHZhciBjb25uZWN0aW9uQnJva2VuID0gZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgJCh0aGlzKS50cmlnZ2VyKCdkaXNjb25uZWN0JywgbWVzc2FnZSk7XG4gICAgfTtcbiAgICB2YXIgY29ubmVjdGlvblN1Y2NlZWRlZCA9IGZ1bmN0aW9uIChtZXNzYWdlKSB7XG4gICAgICAgICQodGhpcykudHJpZ2dlcignY29ubmVjdCcsIG1lc3NhZ2UpO1xuICAgIH07XG4gICAgdmFyIG1lID0gdGhpcztcblxuICAgIGNvbWV0ZC5jb25maWd1cmUoZGVmYXVsdENvbWV0T3B0aW9ucyk7XG5cbiAgICBjb21ldGQuYWRkTGlzdGVuZXIoJy9tZXRhL2Nvbm5lY3QnLCBmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICB2YXIgd2FzQ29ubmVjdGVkID0gdGhpcy5pc0Nvbm5lY3RlZDtcbiAgICAgICAgdGhpcy5pc0Nvbm5lY3RlZCA9IChtZXNzYWdlLnN1Y2Nlc3NmdWwgPT09IHRydWUpO1xuICAgICAgICBpZiAoIXdhc0Nvbm5lY3RlZCAmJiB0aGlzLmlzQ29ubmVjdGVkKSB7IC8vQ29ubmVjdGluZyBmb3IgdGhlIGZpcnN0IHRpbWVcbiAgICAgICAgICAgIGNvbm5lY3Rpb25TdWNjZWVkZWQuY2FsbCh0aGlzLCBtZXNzYWdlKTtcbiAgICAgICAgfSBlbHNlIGlmICh3YXNDb25uZWN0ZWQgJiYgIXRoaXMuaXNDb25uZWN0ZWQpIHsgLy9Pbmx5IHRocm93IGRpc2Nvbm5lY3RlZCBtZXNzYWdlIGZybyB0aGUgZmlyc3QgZGlzY29ubmVjdCwgbm90IG9uY2UgcGVyIHRyeVxuICAgICAgICAgICAgY29ubmVjdGlvbkJyb2tlbi5jYWxsKHRoaXMsIG1lc3NhZ2UpO1xuICAgICAgICB9XG4gICAgfS5iaW5kKHRoaXMpKTtcblxuICAgIGNvbWV0ZC5hZGRMaXN0ZW5lcignL21ldGEvZGlzY29ubmVjdCcsIGNvbm5lY3Rpb25Ccm9rZW4pO1xuXG4gICAgY29tZXRkLmFkZExpc3RlbmVyKCcvbWV0YS9oYW5kc2hha2UnLCBmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICBpZiAobWVzc2FnZS5zdWNjZXNzZnVsKSB7XG4gICAgICAgICAgICAvL2h0dHA6Ly9kb2NzLmNvbWV0ZC5vcmcvcmVmZXJlbmNlL2phdmFzY3JpcHRfc3Vic2NyaWJlLmh0bWwjamF2YXNjcmlwdF9zdWJzY3JpYmVfbWV0YV9jaGFubmVsc1xuICAgICAgICAgICAgLy8gXiBcImR5bmFtaWMgc3Vic2NyaXB0aW9ucyBhcmUgY2xlYXJlZCAobGlrZSBhbnkgb3RoZXIgc3Vic2NyaXB0aW9uKSBhbmQgdGhlIGFwcGxpY2F0aW9uIG5lZWRzIHRvIGZpZ3VyZSBvdXQgd2hpY2ggZHluYW1pYyBzdWJzY3JpcHRpb24gbXVzdCBiZSBwZXJmb3JtZWQgYWdhaW5cIlxuICAgICAgICAgICAgY29tZXRkLmJhdGNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAkKG1lLmN1cnJlbnRTdWJzY3JpcHRpb25zKS5lYWNoKGZ1bmN0aW9uIChpbmRleCwgc3Vicykge1xuICAgICAgICAgICAgICAgICAgICBjb21ldGQucmVzdWJzY3JpYmUoc3Vicyk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy9PdGhlciBpbnRlcmVzdGluZyBldmVudHMgZm9yIHJlZmVyZW5jZVxuICAgIGNvbWV0ZC5hZGRMaXN0ZW5lcignL21ldGEvc3Vic2NyaWJlJywgZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgJChtZSkudHJpZ2dlcignc3Vic2NyaWJlJywgbWVzc2FnZSk7XG4gICAgfSk7XG4gICAgY29tZXRkLmFkZExpc3RlbmVyKCcvbWV0YS91bnN1YnNjcmliZScsIGZ1bmN0aW9uIChtZXNzYWdlKSB7XG4gICAgICAgICQobWUpLnRyaWdnZXIoJ3Vuc3Vic2NyaWJlJywgbWVzc2FnZSk7XG4gICAgfSk7XG4gICAgY29tZXRkLmFkZExpc3RlbmVyKCcvbWV0YS9wdWJsaXNoJywgZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgJChtZSkudHJpZ2dlcigncHVibGlzaCcsIG1lc3NhZ2UpO1xuICAgIH0pO1xuICAgIGNvbWV0ZC5hZGRMaXN0ZW5lcignL21ldGEvdW5zdWNjZXNzZnVsJywgZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgJChtZSkudHJpZ2dlcignZXJyb3InLCBtZXNzYWdlKTtcbiAgICB9KTtcblxuICAgIGNvbWV0ZC5oYW5kc2hha2UoZGVmYXVsdENvbWV0T3B0aW9ucy5oYW5kc2hha2UpO1xuXG4gICAgdGhpcy5jb21ldGQgPSBjb21ldGQ7XG59O1xuXG5cbkNoYW5uZWxNYW5hZ2VyLnByb3RvdHlwZSA9ICQuZXh0ZW5kKENoYW5uZWxNYW5hZ2VyLnByb3RvdHlwZSwge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbmQgcmV0dXJucyBhIGNoYW5uZWwsIHRoYXQgaXMsIGFuIGluc3RhbmNlIG9mIGEgW0NoYW5uZWwgU2VydmljZV0oLi4vY2hhbm5lbC1zZXJ2aWNlLykuXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgICB2YXIgY20gPSBuZXcgRi5tYW5hZ2VyLkNoYW5uZWxNYW5hZ2VyKCk7XG4gICAgICogICAgICB2YXIgY2hhbm5lbCA9IGNtLmdldENoYW5uZWwoKTtcbiAgICAgKlxuICAgICAqICAgICAgY2hhbm5lbC5zdWJzY3JpYmUoJ3RvcGljJywgY2FsbGJhY2spO1xuICAgICAqICAgICAgY2hhbm5lbC5wdWJsaXNoKCd0b3BpYycsIHsgbXlEYXRhOiAxMDAgfSk7XG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fFN0cmluZ30gb3B0aW9ucyAoT3B0aW9uYWwpIElmIHN0cmluZywgYXNzdW1lZCB0byBiZSB0aGUgYmFzZSBjaGFubmVsIHVybC4gSWYgb2JqZWN0LCBhc3N1bWVkIHRvIGJlIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIGNvbnN0cnVjdG9yLlxuICAgICAqIEByZXR1cm4ge0NoYW5uZWx9IENoYW5uZWwgaW5zdGFuY2VcbiAgICAgKi9cbiAgICBnZXRDaGFubmVsOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAvL0lmIHlvdSBqdXN0IHdhbnQgdG8gcGFzcyBpbiBhIHN0cmluZ1xuICAgICAgICBpZiAob3B0aW9ucyAmJiAhJC5pc1BsYWluT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgICAgICAgICBvcHRpb25zID0ge1xuICAgICAgICAgICAgICAgIGJhc2U6IG9wdGlvbnNcbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAgICAgdHJhbnNwb3J0OiB0aGlzLmNvbWV0ZFxuICAgICAgICB9O1xuICAgICAgICB2YXIgY2hhbm5lbCA9IG5ldyBDaGFubmVsKCQuZXh0ZW5kKHRydWUsIHt9LCB0aGlzLm9wdGlvbnMuY2hhbm5lbCwgZGVmYXVsdHMsIG9wdGlvbnMpKTtcblxuXG4gICAgICAgIC8vV3JhcCBzdWJzIGFuZCB1bnN1YnMgc28gd2UgY2FuIHVzZSBpdCB0byByZS1hdHRhY2ggaGFuZGxlcnMgYWZ0ZXIgYmVpbmcgZGlzY29ubmVjdGVkXG4gICAgICAgIHZhciBzdWJzID0gY2hhbm5lbC5zdWJzY3JpYmU7XG4gICAgICAgIGNoYW5uZWwuc3Vic2NyaWJlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHN1YmlkID0gc3Vicy5hcHBseShjaGFubmVsLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgdGhpcy5jdXJyZW50U3Vic2NyaXB0aW9ucyA9IHRoaXMuY3VycmVudFN1YnNjcmlwdGlvbnMuY29uY2F0KHN1YmlkKTtcbiAgICAgICAgICAgIHJldHVybiBzdWJpZDtcbiAgICAgICAgfS5iaW5kKHRoaXMpO1xuXG5cbiAgICAgICAgdmFyIHVuc3VicyA9IGNoYW5uZWwudW5zdWJzY3JpYmU7XG4gICAgICAgIGNoYW5uZWwudW5zdWJzY3JpYmUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgcmVtb3ZlZCA9IHVuc3Vicy5hcHBseShjaGFubmVsLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLmN1cnJlbnRTdWJzY3JpcHRpb25zLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuY3VycmVudFN1YnNjcmlwdGlvbnNbaV0uaWQgPT09IHJlbW92ZWQuaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50U3Vic2NyaXB0aW9ucy5zcGxpY2UoaSwgMSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHJlbW92ZWQ7XG4gICAgICAgIH0uYmluZCh0aGlzKTtcblxuICAgICAgICByZXR1cm4gY2hhbm5lbDtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogU3RhcnQgbGlzdGVuaW5nIGZvciBldmVudHMgb24gdGhpcyBpbnN0YW5jZS4gU2lnbmF0dXJlIGlzIHNhbWUgYXMgZm9yIGpRdWVyeSBFdmVudHM6IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS9vbi8uXG4gICAgICpcbiAgICAgKiBTdXBwb3J0ZWQgZXZlbnRzIGFyZTogYGNvbm5lY3RgLCBgZGlzY29ubmVjdGAsIGBzdWJzY3JpYmVgLCBgdW5zdWJzY3JpYmVgLCBgcHVibGlzaGAsIGBlcnJvcmAuXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50IFRoZSBldmVudCB0eXBlLiBTZWUgbW9yZSBkZXRhaWwgYXQgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL29uLy5cbiAgICAgKi9cbiAgICBvbjogZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICQodGhpcykub24uYXBwbHkoJCh0aGlzKSwgYXJndW1lbnRzKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogU3RvcCBsaXN0ZW5pbmcgZm9yIGV2ZW50cyBvbiB0aGlzIGluc3RhbmNlLiBTaWduYXR1cmUgaXMgc2FtZSBhcyBmb3IgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL29mZi8uXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50IFRoZSBldmVudCB0eXBlLiBTZWUgbW9yZSBkZXRhaWwgYXQgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL29mZi8uXG4gICAgICovXG4gICAgb2ZmOiBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgJCh0aGlzKS5vZmYuYXBwbHkoJCh0aGlzKSwgYXJndW1lbnRzKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVHJpZ2dlciBldmVudHMgYW5kIGV4ZWN1dGUgaGFuZGxlcnMuIFNpZ25hdHVyZSBpcyBzYW1lIGFzIGZvciBqUXVlcnkgRXZlbnRzOiBodHRwOi8vYXBpLmpxdWVyeS5jb20vdHJpZ2dlci8uXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50IFRoZSBldmVudCB0eXBlLiBTZWUgbW9yZSBkZXRhaWwgYXQgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL3RyaWdnZXIvLlxuICAgICAqL1xuICAgIHRyaWdnZXI6IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAkKHRoaXMpLnRyaWdnZXIuYXBwbHkoJCh0aGlzKSwgYXJndW1lbnRzKTtcbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBDaGFubmVsTWFuYWdlcjtcbiIsIid1c2Ugc3RyaWN0JztcblxuLyoqXG4gKiAjIyBFcGljZW50ZXIgQ2hhbm5lbCBNYW5hZ2VyXG4gKlxuICogVGhlIEVwaWNlbnRlciBwbGF0Zm9ybSBwcm92aWRlcyBhIHB1c2ggY2hhbm5lbCwgd2hpY2ggYWxsb3dzIHlvdSB0byBwdWJsaXNoIGFuZCBzdWJzY3JpYmUgdG8gbWVzc2FnZXMgd2l0aGluIGEgW3Byb2plY3RdKC4uLy4uLy4uL2dsb3NzYXJ5LyNwcm9qZWN0cyksIFtncm91cF0oLi4vLi4vLi4vZ2xvc3NhcnkvI2dyb3VwcyksIG9yIFttdWx0aXBsYXllciB3b3JsZF0oLi4vLi4vLi4vZ2xvc3NhcnkvI3dvcmxkKS4gVGhlcmUgYXJlIHR3byBtYWluIHVzZSBjYXNlcyBmb3IgdGhlIGNoYW5uZWw6IGV2ZW50IG5vdGlmaWNhdGlvbnMgYW5kIGNoYXQgbWVzc2FnZXMuXG4gKlxuICogVGhlIEVwaWNlbnRlciBDaGFubmVsIE1hbmFnZXIgaXMgYSB3cmFwcGVyIGFyb3VuZCB0aGUgKG1vcmUgZ2VuZXJpYykgW0NoYW5uZWwgTWFuYWdlcl0oLi4vY2hhbm5lbC1tYW5hZ2VyLyksIHRvIGluc3RhbnRpYXRlIGl0IHdpdGggRXBpY2VudGVyLXNwZWNpZmljIGRlZmF1bHRzLiBJZiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gaW5jbHVkaW5nIGEgbm90aWZpY2F0aW9uIG9yIGNoYXQgZmVhdHVyZSBpbiB5b3VyIHByb2plY3QsIHVzaW5nIGFuIEVwaWNlbnRlciBDaGFubmVsIE1hbmFnZXIgaXMgcHJvYmFibHkgdGhlIGVhc2llc3Qgd2F5IHRvIGdldCBzdGFydGVkLlxuICpcbiAqIFlvdSdsbCBuZWVkIHRvIGluY2x1ZGUgdGhlIGBlcGljZW50ZXItbXVsdGlwbGF5ZXItZGVwZW5kZW5jaWVzLmpzYCBsaWJyYXJ5IGluIGFkZGl0aW9uIHRvIHRoZSBgZXBpY2VudGVyLmpzYCBsaWJyYXJ5IGluIHlvdXIgcHJvamVjdCB0byB1c2UgdGhlIEVwaWNlbnRlciBDaGFubmVsIE1hbmFnZXIuIFNlZSBbSW5jbHVkaW5nIEVwaWNlbnRlci5qc10oLi4vLi4vI2luY2x1ZGUpLlxuICpcbiAqIFRvIHVzZSB0aGUgRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlcjogaW5zdGFudGlhdGUgaXQsIGdldCB0aGUgY2hhbm5lbCBvZiB0aGUgc2NvcGUgeW91IHdhbnQgKFt1c2VyXSguLi8uLi8uLi9nbG9zc2FyeS8jdXNlcnMpLCBbd29ybGRdKC4uLy4uLy4uL2dsb3NzYXJ5LyN3b3JsZCksIG9yIFtncm91cF0oLi4vLi4vLi4vZ2xvc3NhcnkvI2dyb3VwcykpLCB0aGVuIHVzZSB0aGUgY2hhbm5lbCdzIGBzdWJzY3JpYmUoKWAgYW5kIGBwdWJsaXNoKClgIG1ldGhvZHMgdG8gc3Vic2NyaWJlIHRvIHRvcGljcyBvciBwdWJsaXNoIGRhdGEgdG8gdG9waWNzLlxuICpcbiAqICAgICB2YXIgY20gPSBuZXcgRi5tYW5hZ2VyLkNoYW5uZWxNYW5hZ2VyKCk7XG4gKiAgICAgdmFyIGdjID0gY20uZ2V0R3JvdXBDaGFubmVsKCk7XG4gKiAgICAgZ2Muc3Vic2NyaWJlKCdicm9hZGNhc3RzJywgY2FsbGJhY2spO1xuICpcbiAqIEZvciBhZGRpdGlvbmFsIGJhY2tncm91bmQgb24gRXBpY2VudGVyJ3MgcHVzaCBjaGFubmVsLCBzZWUgdGhlIGludHJvZHVjdG9yeSBub3RlcyBvbiB0aGUgW1B1c2ggQ2hhbm5lbCBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9tdWx0aXBsYXllci9jaGFubmVsLykgcGFnZS5cbiAqXG4gKiBUaGUgcGFyYW1ldGVycyBmb3IgaW5zdGFudGlhdGluZyBhbiBFcGljZW50ZXIgQ2hhbm5lbCBNYW5hZ2VyIGluY2x1ZGU6XG4gKlxuICogKiBgb3B0aW9uc2AgT2JqZWN0IHdpdGggZGV0YWlscyBhYm91dCB0aGUgRXBpY2VudGVyIHByb2plY3QgZm9yIHRoaXMgRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlciBpbnN0YW5jZS5cbiAqICogYG9wdGlvbnMuYWNjb3VudGAgVGhlIEVwaWNlbnRlciBhY2NvdW50IGlkICgqKlRlYW0gSUQqKiBmb3IgdGVhbSBwcm9qZWN0cywgKipVc2VyIElEKiogZm9yIHBlcnNvbmFsIHByb2plY3RzKS5cbiAqICogYG9wdGlvbnMucHJvamVjdGAgRXBpY2VudGVyIHByb2plY3QgaWQuXG4gKiAqIGBvcHRpb25zLnVzZXJOYW1lYCBFcGljZW50ZXIgdXNlck5hbWUgdXNlZCBmb3IgYXV0aGVudGljYXRpb24uXG4gKiAqIGBvcHRpb25zLnVzZXJJZGAgRXBpY2VudGVyIHVzZXIgaWQgdXNlZCBmb3IgYXV0aGVudGljYXRpb24uIE9wdGlvbmFsOyBgb3B0aW9ucy51c2VyTmFtZWAgaXMgcHJlZmVycmVkLlxuICogKiBgb3B0aW9ucy50b2tlbmAgRXBpY2VudGVyIHRva2VuIHVzZWQgZm9yIGF1dGhlbnRpY2F0aW9uLiAoWW91IGNhbiByZXRyaWV2ZSB0aGlzIHVzaW5nIGBhdXRoTWFuYWdlci5nZXRUb2tlbigpYCBmcm9tIHRoZSBbQXV0aG9yaXphdGlvbiBNYW5hZ2VyXSguLi9hdXRoLW1hbmFnZXIvKS4pXG4gKiAqIGBvcHRpb25zLmFsbG93QWxsQ2hhbm5lbHNgIElmIG5vdCBpbmNsdWRlZCBvciBpZiBzZXQgdG8gYGZhbHNlYCwgYWxsIGNoYW5uZWwgcGF0aHMgYXJlIHZhbGlkYXRlZDsgaWYgeW91ciBwcm9qZWN0IHJlcXVpcmVzIFtQdXNoIENoYW5uZWwgQXV0aG9yaXphdGlvbl0oLi4vLi4vLi4vdXBkYXRpbmdfeW91cl9zZXR0aW5ncy8pLCB5b3Ugc2hvdWxkIHVzZSB0aGlzIG9wdGlvbi4gSWYgeW91IHdhbnQgdG8gYWxsb3cgb3RoZXIgY2hhbm5lbCBwYXRocywgc2V0IHRvIGB0cnVlYDsgdGhpcyBpcyBub3QgY29tbW9uLlxuICovXG5cbnZhciBDaGFubmVsTWFuYWdlciA9IHJlcXVpcmUoJy4vY2hhbm5lbC1tYW5hZ2VyJyk7XG52YXIgY2xhc3NGcm9tID0gcmVxdWlyZSgnLi4vdXRpbC9pbmhlcml0Jyk7XG52YXIgdXJsU2VydmljZSA9IHJlcXVpcmUoJy4uL3NlcnZpY2UvdXJsLWNvbmZpZy1zZXJ2aWNlJyk7XG52YXIgU2Vzc2lvbk1hbmFnZXIgPSByZXF1aXJlKCcuLi9zdG9yZS9zZXNzaW9uLW1hbmFnZXInKTtcblxudmFyIHZhbGlkVHlwZXMgPSB7XG4gICAgcHJvamVjdDogdHJ1ZSxcbiAgICBncm91cDogdHJ1ZSxcbiAgICB3b3JsZDogdHJ1ZSxcbiAgICB1c2VyOiB0cnVlLFxuICAgIGRhdGE6IHRydWUsXG4gICAgZ2VuZXJhbDogdHJ1ZSxcbiAgICBjaGF0OiB0cnVlXG59O1xudmFyIGdldEZyb21TZXNzaW9uT3JFcnJvciA9IGZ1bmN0aW9uICh2YWx1ZSwgc2Vzc2lvbktleU5hbWUsIHNldHRpbmdzKSB7XG4gICAgaWYgKCF2YWx1ZSkge1xuICAgICAgICBpZiAoc2V0dGluZ3MgJiYgc2V0dGluZ3Nbc2Vzc2lvbktleU5hbWVdKSB7XG4gICAgICAgICAgICB2YWx1ZSA9IHNldHRpbmdzW3Nlc3Npb25LZXlOYW1lXTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihzZXNzaW9uS2V5TmFtZSArICcgbm90IGZvdW5kLiBQbGVhc2UgbG9nLWluIGFnYWluLCBvciBzcGVjaWZ5ICcgKyBzZXNzaW9uS2V5TmFtZSArICcgZXhwbGljaXRseScpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiB2YWx1ZTtcbn07XG52YXIgX19zdXBlciA9IENoYW5uZWxNYW5hZ2VyLnByb3RvdHlwZTtcbnZhciBFcGljZW50ZXJDaGFubmVsTWFuYWdlciA9IGNsYXNzRnJvbShDaGFubmVsTWFuYWdlciwge1xuICAgIGNvbnN0cnVjdG9yOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICB0aGlzLnNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKG9wdGlvbnMpO1xuICAgICAgICB2YXIgZGVmYXVsdENvbWV0T3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhvcHRpb25zKTtcblxuICAgICAgICB2YXIgdXJsT3B0cyA9IHVybFNlcnZpY2UoZGVmYXVsdENvbWV0T3B0aW9ucy5zZXJ2ZXIpO1xuICAgICAgICBpZiAoIWRlZmF1bHRDb21ldE9wdGlvbnMudXJsKSB7XG4gICAgICAgICAgICAvL0RlZmF1bHQgZXBpY2VudGVyIGNvbWV0ZCBlbmRwb2ludFxuICAgICAgICAgICAgZGVmYXVsdENvbWV0T3B0aW9ucy51cmwgPSB1cmxPcHRzLnByb3RvY29sICsgJzovLycgKyB1cmxPcHRzLmhvc3QgKyAnL2NoYW5uZWwvc3Vic2NyaWJlJztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChkZWZhdWx0Q29tZXRPcHRpb25zLmhhbmRzaGFrZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICB2YXIgdXNlck5hbWUgPSBkZWZhdWx0Q29tZXRPcHRpb25zLnVzZXJOYW1lO1xuICAgICAgICAgICAgdmFyIHVzZXJJZCA9IGRlZmF1bHRDb21ldE9wdGlvbnMudXNlcklkO1xuICAgICAgICAgICAgdmFyIHRva2VuID0gZGVmYXVsdENvbWV0T3B0aW9ucy50b2tlbjtcbiAgICAgICAgICAgIGlmICgodXNlck5hbWUgfHwgdXNlcklkKSAmJiB0b2tlbikge1xuICAgICAgICAgICAgICAgIHZhciB1c2VyUHJvcCA9IHVzZXJOYW1lID8gJ3VzZXJOYW1lJyA6ICd1c2VySWQnO1xuICAgICAgICAgICAgICAgIHZhciBleHQgPSB7XG4gICAgICAgICAgICAgICAgICAgIGF1dGhvcml6YXRpb246ICdCZWFyZXIgJyArIHRva2VuXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBleHRbdXNlclByb3BdID0gdXNlck5hbWUgPyB1c2VyTmFtZSA6IHVzZXJJZDtcblxuICAgICAgICAgICAgICAgIGRlZmF1bHRDb21ldE9wdGlvbnMuaGFuZHNoYWtlID0ge1xuICAgICAgICAgICAgICAgICAgICBleHQ6IGV4dFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLm9wdGlvbnMgPSBkZWZhdWx0Q29tZXRPcHRpb25zO1xuICAgICAgICByZXR1cm4gX19zdXBlci5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMsIGRlZmF1bHRDb21ldE9wdGlvbnMpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuZCByZXR1cm5zIGEgY2hhbm5lbCwgdGhhdCBpcywgYW4gaW5zdGFuY2Ugb2YgYSBbQ2hhbm5lbCBTZXJ2aWNlXSguLi9jaGFubmVsLXNlcnZpY2UvKS5cbiAgICAgKlxuICAgICAqIFRoaXMgbWV0aG9kIGVuZm9yY2VzIEVwaWNlbnRlci1zcGVjaWZpYyBjaGFubmVsIG5hbWluZzogYWxsIGNoYW5uZWxzIHJlcXVlc3RlZCBtdXN0IGJlIGluIHRoZSBmb3JtIGAve3R5cGV9L3thY2NvdW50IGlkfS97cHJvamVjdCBpZH0vey4uLn1gLCB3aGVyZSBgdHlwZWAgaXMgb25lIG9mIGBydW5gLCBgZGF0YWAsIGB1c2VyYCwgYHdvcmxkYCwgb3IgYGNoYXRgLlxuICAgICAqXG4gICAgICogKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICAgdmFyIGNtID0gbmV3IEYubWFuYWdlci5FcGljZW50ZXJDaGFubmVsTWFuYWdlcigpO1xuICAgICAqICAgICAgdmFyIGNoYW5uZWwgPSBjbS5nZXRDaGFubmVsKCcvZ3JvdXAvYWNtZS9zdXBwbHktY2hhaW4tZ2FtZS8nKTtcbiAgICAgKlxuICAgICAqICAgICAgY2hhbm5lbC5zdWJzY3JpYmUoJ3RvcGljJywgY2FsbGJhY2spO1xuICAgICAqICAgICAgY2hhbm5lbC5wdWJsaXNoKCd0b3BpYycsIHsgbXlEYXRhOiAxMDAgfSk7XG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fFN0cmluZ30gb3B0aW9ucyAoT3B0aW9uYWwpIElmIHN0cmluZywgYXNzdW1lZCB0byBiZSB0aGUgYmFzZSBjaGFubmVsIHVybC4gSWYgb2JqZWN0LCBhc3N1bWVkIHRvIGJlIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIGNvbnN0cnVjdG9yLlxuICAgICAqIEByZXR1cm4ge0NoYW5uZWx9IENoYW5uZWwgaW5zdGFuY2VcbiAgICAgKi9cbiAgICBnZXRDaGFubmVsOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICBpZiAob3B0aW9ucyAmJiB0eXBlb2Ygb3B0aW9ucyAhPT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSB7XG4gICAgICAgICAgICAgICAgYmFzZTogb3B0aW9uc1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgY2hhbm5lbE9wdHMgPSAkLmV4dGVuZCh7fSwgdGhpcy5vcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgdmFyIGJhc2UgPSBjaGFubmVsT3B0cy5iYXNlO1xuICAgICAgICBpZiAoIWJhc2UpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTm8gYmFzZSB0b3BpYyB3YXMgcHJvdmlkZWQnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghY2hhbm5lbE9wdHMuYWxsb3dBbGxDaGFubmVscykge1xuICAgICAgICAgICAgdmFyIGJhc2VQYXJ0cyA9IGJhc2Uuc3BsaXQoJy8nKTtcbiAgICAgICAgICAgIHZhciBjaGFubmVsVHlwZSA9IGJhc2VQYXJ0c1sxXTtcbiAgICAgICAgICAgIGlmIChiYXNlUGFydHMubGVuZ3RoIDwgNCkgeyAvL2VzbGludC1kaXNhYmxlLWxpbmVcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgY2hhbm5lbCBiYXNlIG5hbWUsIGl0IG11c3QgYmUgaW4gdGhlIGZvcm0gL3t0eXBlfS97YWNjb3VudCBpZH0ve3Byb2plY3QgaWR9L3suLi59Jyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoIXZhbGlkVHlwZXNbY2hhbm5lbFR5cGVdKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGNoYW5uZWwgdHlwZScpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBfX3N1cGVyLmdldENoYW5uZWwuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGFuZCByZXR1cm4gYSBwdWJsaXNoL3N1YnNjcmliZSBjaGFubmVsIChmcm9tIHRoZSB1bmRlcmx5aW5nIFtDaGFubmVsIE1hbmFnZXJdKC4uL2NoYW5uZWwtbWFuYWdlci8pKSBmb3IgdGhlIGdpdmVuIFtncm91cF0oLi4vLi4vLi4vZ2xvc3NhcnkvI2dyb3VwcykuIFRoZSBncm91cCBtdXN0IGV4aXN0IGluIHRoZSBhY2NvdW50ICh0ZWFtKSBhbmQgcHJvamVjdCBwcm92aWRlZC5cbiAgICAgKlxuICAgICAqIFRoZXJlIGFyZSBubyBub3RpZmljYXRpb25zIGZyb20gRXBpY2VudGVyIG9uIHRoaXMgY2hhbm5lbDsgYWxsIG1lc3NhZ2VzIGFyZSB1c2VyLW9yaWdpbmF0ZWQuXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgIHZhciBjbSA9IG5ldyBGLm1hbmFnZXIuQ2hhbm5lbE1hbmFnZXIoKTtcbiAgICAgKiAgICAgdmFyIGdjID0gY20uZ2V0R3JvdXBDaGFubmVsKCk7XG4gICAgICogICAgIGdjLnN1YnNjcmliZSgnYnJvYWRjYXN0cycsIGNhbGxiYWNrKTtcbiAgICAgKlxuICAgICAqICoqUmV0dXJuIFZhbHVlKipcbiAgICAgKlxuICAgICAqICogKkNoYW5uZWwqIFJldHVybnMgdGhlIGNoYW5uZWwgKGFuIGluc3RhbmNlIG9mIHRoZSBbQ2hhbm5lbCBTZXJ2aWNlXSguLi9jaGFubmVsLXNlcnZpY2UvKSkuXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtICB7U3RyaW5nfSBncm91cE5hbWUgKE9wdGlvbmFsKSBHcm91cCB0byBicm9hZGNhc3QgdG8uIElmIG5vdCBwcm92aWRlZCwgcGlja3MgdXAgZ3JvdXAgZnJvbSBjdXJyZW50IHNlc3Npb24gaWYgZW5kIHVzZXIgaXMgbG9nZ2VkIGluLlxuICAgICAqIEByZXR1cm4ge0NoYW5uZWx9IENoYW5uZWwgaW5zdGFuY2VcbiAgICAgKi9cbiAgICBnZXRHcm91cENoYW5uZWw6IGZ1bmN0aW9uIChncm91cE5hbWUpIHtcbiAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnModGhpcy5vcHRpb25zKTtcbiAgICAgICAgZ3JvdXBOYW1lID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKGdyb3VwTmFtZSwgJ2dyb3VwTmFtZScsIHNlc3Npb24pO1xuICAgICAgICB2YXIgYWNjb3VudCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcignJywgJ2FjY291bnQnLCBzZXNzaW9uKTtcbiAgICAgICAgdmFyIHByb2plY3QgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoJycsICdwcm9qZWN0Jywgc2Vzc2lvbik7XG5cbiAgICAgICAgdmFyIGJhc2VUb3BpYyA9IFsnL2dyb3VwJywgYWNjb3VudCwgcHJvamVjdCwgZ3JvdXBOYW1lXS5qb2luKCcvJyk7XG4gICAgICAgIHJldHVybiBfX3N1cGVyLmdldENoYW5uZWwuY2FsbCh0aGlzLCB7IGJhc2U6IGJhc2VUb3BpYyB9KTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGFuZCByZXR1cm4gYSBwdWJsaXNoL3N1YnNjcmliZSBjaGFubmVsIChmcm9tIHRoZSB1bmRlcmx5aW5nIFtDaGFubmVsIE1hbmFnZXJdKC4uL2NoYW5uZWwtbWFuYWdlci8pKSBmb3IgdGhlIGdpdmVuIFt3b3JsZF0oLi4vLi4vLi4vZ2xvc3NhcnkvI3dvcmxkKS5cbiAgICAgKlxuICAgICAqIFRoaXMgaXMgdHlwaWNhbGx5IHVzZWQgdG9nZXRoZXIgd2l0aCB0aGUgW1dvcmxkIE1hbmFnZXJdKC4uL3dvcmxkLW1hbmFnZXIpLlxuICAgICAqXG4gICAgICogKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICB2YXIgY20gPSBuZXcgRi5tYW5hZ2VyLkNoYW5uZWxNYW5hZ2VyKCk7XG4gICAgICogICAgIHZhciB3b3JsZE1hbmFnZXIgPSBuZXcgRi5tYW5hZ2VyLldvcmxkTWFuYWdlcih7XG4gICAgICogICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICogICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAqICAgICAgICAgZ3JvdXA6ICd0ZWFtMScsXG4gICAgICogICAgICAgICBydW46IHsgbW9kZWw6ICdtb2RlbC5lcW4nIH1cbiAgICAgKiAgICAgfSk7XG4gICAgICogICAgIHdvcmxkTWFuYWdlci5nZXRDdXJyZW50V29ybGQoKS50aGVuKGZ1bmN0aW9uICh3b3JsZE9iamVjdCwgd29ybGRBZGFwdGVyKSB7XG4gICAgICogICAgICAgICB2YXIgd29ybGRDaGFubmVsID0gY20uZ2V0V29ybGRDaGFubmVsKHdvcmxkT2JqZWN0KTtcbiAgICAgKiAgICAgICAgIHdvcmxkQ2hhbm5lbC5zdWJzY3JpYmUoJycsIGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICogICAgICAgICAgICAgY29uc29sZS5sb2coZGF0YSk7XG4gICAgICogICAgICAgICB9KTtcbiAgICAgKiAgICAgIH0pO1xuICAgICAqXG4gICAgICogKipSZXR1cm4gVmFsdWUqKlxuICAgICAqXG4gICAgICogKiAqQ2hhbm5lbCogUmV0dXJucyB0aGUgY2hhbm5lbCAoYW4gaW5zdGFuY2Ugb2YgdGhlIFtDaGFubmVsIFNlcnZpY2VdKC4uL2NoYW5uZWwtc2VydmljZS8pKS5cbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICpcbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd8T2JqZWN0fSB3b3JsZCBUaGUgd29ybGQgb2JqZWN0IG9yIGlkLlxuICAgICAqIEBwYXJhbSAge1N0cmluZ30gZ3JvdXBOYW1lIChPcHRpb25hbCkgR3JvdXAgdGhlIHdvcmxkIGV4aXN0cyBpbi4gSWYgbm90IHByb3ZpZGVkLCBwaWNrcyB1cCBncm91cCBmcm9tIGN1cnJlbnQgc2Vzc2lvbiBpZiBlbmQgdXNlciBpcyBsb2dnZWQgaW4uXG4gICAgICogQHJldHVybiB7Q2hhbm5lbH0gQ2hhbm5lbCBpbnN0YW5jZVxuICAgICAqL1xuICAgIGdldFdvcmxkQ2hhbm5lbDogZnVuY3Rpb24gKHdvcmxkLCBncm91cE5hbWUpIHtcbiAgICAgICAgdmFyIHdvcmxkaWQgPSAoJC5pc1BsYWluT2JqZWN0KHdvcmxkKSAmJiB3b3JsZC5pZCkgPyB3b3JsZC5pZCA6IHdvcmxkO1xuICAgICAgICBpZiAoIXdvcmxkaWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUGxlYXNlIHNwZWNpZnkgYSB3b3JsZCBpZCcpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKHRoaXMub3B0aW9ucyk7XG5cbiAgICAgICAgZ3JvdXBOYW1lID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKGdyb3VwTmFtZSwgJ2dyb3VwTmFtZScsIHNlc3Npb24pO1xuICAgICAgICB2YXIgYWNjb3VudCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcignJywgJ2FjY291bnQnLCBzZXNzaW9uKTtcbiAgICAgICAgdmFyIHByb2plY3QgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoJycsICdwcm9qZWN0Jywgc2Vzc2lvbik7XG5cbiAgICAgICAgdmFyIGJhc2VUb3BpYyA9IFsnL3dvcmxkJywgYWNjb3VudCwgcHJvamVjdCwgZ3JvdXBOYW1lLCB3b3JsZGlkXS5qb2luKCcvJyk7XG4gICAgICAgIHJldHVybiBfX3N1cGVyLmdldENoYW5uZWwuY2FsbCh0aGlzLCB7IGJhc2U6IGJhc2VUb3BpYyB9KTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGFuZCByZXR1cm4gYSBwdWJsaXNoL3N1YnNjcmliZSBjaGFubmVsIChmcm9tIHRoZSB1bmRlcmx5aW5nIFtDaGFubmVsIE1hbmFnZXJdKC4uL2NoYW5uZWwtbWFuYWdlci8pKSBmb3IgdGhlIGN1cnJlbnQgW2VuZCB1c2VyXSguLi8uLi8uLi9nbG9zc2FyeS8jdXNlcnMpIGluIHRoYXQgdXNlcidzIGN1cnJlbnQgW3dvcmxkXSguLi8uLi8uLi9nbG9zc2FyeS8jd29ybGQpLlxuICAgICAqXG4gICAgICogVGhpcyBpcyB0eXBpY2FsbHkgdXNlZCB0b2dldGhlciB3aXRoIHRoZSBbV29ybGQgTWFuYWdlcl0oLi4vd29ybGQtbWFuYWdlcikuIE5vdGUgdGhhdCB0aGlzIGNoYW5uZWwgb25seSBnZXRzIG5vdGlmaWNhdGlvbnMgZm9yIHdvcmxkcyBjdXJyZW50bHkgaW4gbWVtb3J5LiAoU2VlIG1vcmUgYmFja2dyb3VuZCBvbiBbcGVyc2lzdGVuY2VdKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZSkuKVxuICAgICAqXG4gICAgICogKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICB2YXIgY20gPSBuZXcgRi5tYW5hZ2VyLkNoYW5uZWxNYW5hZ2VyKCk7XG4gICAgICogICAgIHZhciB3b3JsZE1hbmFnZXIgPSBuZXcgRi5tYW5hZ2VyLldvcmxkTWFuYWdlcih7XG4gICAgICogICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICogICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICAgICAqICAgICAgICAgZ3JvdXA6ICd0ZWFtMScsXG4gICAgICogICAgICAgICBydW46IHsgbW9kZWw6ICdtb2RlbC5lcW4nIH1cbiAgICAgKiAgICAgfSk7XG4gICAgICogICAgIHdvcmxkTWFuYWdlci5nZXRDdXJyZW50V29ybGQoKS50aGVuKGZ1bmN0aW9uICh3b3JsZE9iamVjdCwgd29ybGRBZGFwdGVyKSB7XG4gICAgICogICAgICAgICB2YXIgdXNlckNoYW5uZWwgPSBjbS5nZXRVc2VyQ2hhbm5lbCh3b3JsZE9iamVjdCk7XG4gICAgICogICAgICAgICB1c2VyQ2hhbm5lbC5zdWJzY3JpYmUoJycsIGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICogICAgICAgICAgICAgY29uc29sZS5sb2coZGF0YSk7XG4gICAgICogICAgICAgICB9KTtcbiAgICAgKiAgICAgIH0pO1xuICAgICAqXG4gICAgICpcbiAgICAgKiAqKlJldHVybiBWYWx1ZSoqXG4gICAgICpcbiAgICAgKiAqICpDaGFubmVsKiBSZXR1cm5zIHRoZSBjaGFubmVsIChhbiBpbnN0YW5jZSBvZiB0aGUgW0NoYW5uZWwgU2VydmljZV0oLi4vY2hhbm5lbC1zZXJ2aWNlLykpLlxuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSAge1N0cmluZ3xPYmplY3R9IHdvcmxkIFdvcmxkIG9iamVjdCBvciBpZC5cbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd8T2JqZWN0fSB1c2VyIChPcHRpb25hbCkgVXNlciBvYmplY3Qgb3IgaWQuIElmIG5vdCBwcm92aWRlZCwgcGlja3MgdXAgdXNlciBpZCBmcm9tIGN1cnJlbnQgc2Vzc2lvbiBpZiBlbmQgdXNlciBpcyBsb2dnZWQgaW4uXG4gICAgICogQHBhcmFtICB7U3RyaW5nfSBncm91cE5hbWUgKE9wdGlvbmFsKSBHcm91cCB0aGUgd29ybGQgZXhpc3RzIGluLiBJZiBub3QgcHJvdmlkZWQsIHBpY2tzIHVwIGdyb3VwIGZyb20gY3VycmVudCBzZXNzaW9uIGlmIGVuZCB1c2VyIGlzIGxvZ2dlZCBpbi5cbiAgICAgKiBAcmV0dXJuIHtDaGFubmVsfSBDaGFubmVsIGluc3RhbmNlXG4gICAgICovXG4gICAgZ2V0VXNlckNoYW5uZWw6IGZ1bmN0aW9uICh3b3JsZCwgdXNlciwgZ3JvdXBOYW1lKSB7XG4gICAgICAgIHZhciB3b3JsZGlkID0gKCQuaXNQbGFpbk9iamVjdCh3b3JsZCkgJiYgd29ybGQuaWQpID8gd29ybGQuaWQgOiB3b3JsZDtcbiAgICAgICAgaWYgKCF3b3JsZGlkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsZWFzZSBzcGVjaWZ5IGEgd29ybGQgaWQnKTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyh0aGlzLm9wdGlvbnMpO1xuXG4gICAgICAgIHZhciB1c2VyaWQgPSAoJC5pc1BsYWluT2JqZWN0KHVzZXIpICYmIHVzZXIuaWQpID8gdXNlci5pZCA6IHVzZXI7XG4gICAgICAgIHVzZXJpZCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcih1c2VyaWQsICd1c2VySWQnLCBzZXNzaW9uKTtcbiAgICAgICAgZ3JvdXBOYW1lID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKGdyb3VwTmFtZSwgJ2dyb3VwTmFtZScsIHNlc3Npb24pO1xuXG4gICAgICAgIHZhciBhY2NvdW50ID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKCcnLCAnYWNjb3VudCcsIHNlc3Npb24pO1xuICAgICAgICB2YXIgcHJvamVjdCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcignJywgJ3Byb2plY3QnLCBzZXNzaW9uKTtcblxuICAgICAgICB2YXIgYmFzZVRvcGljID0gWycvdXNlcicsIGFjY291bnQsIHByb2plY3QsIGdyb3VwTmFtZSwgd29ybGRpZCwgdXNlcmlkXS5qb2luKCcvJyk7XG4gICAgICAgIHJldHVybiBfX3N1cGVyLmdldENoYW5uZWwuY2FsbCh0aGlzLCB7IGJhc2U6IGJhc2VUb3BpYyB9KTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGFuZCByZXR1cm4gYSBwdWJsaXNoL3N1YnNjcmliZSBjaGFubmVsIChmcm9tIHRoZSB1bmRlcmx5aW5nIFtDaGFubmVsIE1hbmFnZXJdKC4uL2NoYW5uZWwtbWFuYWdlci8pKSB0aGF0IGF1dG9tYXRpY2FsbHkgdHJhY2tzIHRoZSBwcmVzZW5jZSBvZiBhbiBbZW5kIHVzZXJdKC4uLy4uLy4uL2dsb3NzYXJ5LyN1c2VycyksIHRoYXQgaXMsIHdoZXRoZXIgdGhlIGVuZCB1c2VyIGlzIGN1cnJlbnRseSBvbmxpbmUgaW4gdGhpcyBncm91cCBhbmQgd29ybGQuIE5vdGlmaWNhdGlvbnMgYXJlIGF1dG9tYXRpY2FsbHkgc2VudCB3aGVuIHRoZSBlbmQgdXNlciBjb21lcyBvbmxpbmUsIGFuZCB3aGVuIHRoZSBlbmQgdXNlciBnb2VzIG9mZmxpbmUgKG5vdCBwcmVzZW50IGZvciBtb3JlIHRoYW4gMiBtaW51dGVzKS4gVXNlZnVsIGluIG11bHRpcGxheWVyIGdhbWVzIGZvciBsZXR0aW5nIGVhY2ggZW5kIHVzZXIga25vdyB3aGV0aGVyIG90aGVyIHVzZXJzIGluIHRoZWlyIHNoYXJlZCB3b3JsZCBhcmUgYWxzbyBvbmxpbmUuXG4gICAgICpcbiAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAqXG4gICAgICogICAgIHZhciBjbSA9IG5ldyBGLm1hbmFnZXIuQ2hhbm5lbE1hbmFnZXIoKTtcbiAgICAgKiAgICAgdmFyIHdvcmxkTWFuYWdlciA9IG5ldyBGLm1hbmFnZXIuV29ybGRNYW5hZ2VyKHtcbiAgICAgKiAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgKiAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICogICAgICAgICBtb2RlbDogJ21vZGVsLmVxbidcbiAgICAgKiAgICAgfSk7XG4gICAgICogICAgIHdvcmxkTWFuYWdlci5nZXRDdXJyZW50V29ybGQoKS50aGVuKGZ1bmN0aW9uICh3b3JsZE9iamVjdCwgd29ybGRTZXJ2aWNlKSB7XG4gICAgICogICAgICAgICB2YXIgcHJlc2VuY2VDaGFubmVsID0gY20uZ2V0UHJlc2VuY2VDaGFubmVsKHdvcmxkT2JqZWN0KTtcbiAgICAgKiAgICAgICAgIHByZXNlbmNlQ2hhbm5lbC5vbigncHJlc2VuY2UnLCBmdW5jdGlvbiAoZXZ0LCBub3RpZmljYXRpb24pIHtcbiAgICAgKiAgICAgICAgICAgICAgY29uc29sZS5sb2cobm90aWZpY2F0aW9uLm9ubGluZSwgbm90aWZpY2F0aW9uLnVzZXJJZCk7XG4gICAgICogICAgICAgICAgfSk7XG4gICAgICogICAgICB9KTtcbiAgICAgKlxuICAgICAqXG4gICAgICogKipSZXR1cm4gVmFsdWUqKlxuICAgICAqXG4gICAgICogKiAqQ2hhbm5lbCogUmV0dXJucyB0aGUgY2hhbm5lbCAoYW4gaW5zdGFuY2Ugb2YgdGhlIFtDaGFubmVsIFNlcnZpY2VdKC4uL2NoYW5uZWwtc2VydmljZS8pKS5cbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICpcbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd8T2JqZWN0fSB3b3JsZCBXb3JsZCBvYmplY3Qgb3IgaWQuXG4gICAgICogQHBhcmFtICB7U3RyaW5nfE9iamVjdH0gdXNlcmlkIChPcHRpb25hbCkgVXNlciBvYmplY3Qgb3IgaWQuIElmIG5vdCBwcm92aWRlZCwgcGlja3MgdXAgdXNlciBpZCBmcm9tIGN1cnJlbnQgc2Vzc2lvbiBpZiBlbmQgdXNlciBpcyBsb2dnZWQgaW4uXG4gICAgICogQHBhcmFtICB7U3RyaW5nfSBncm91cE5hbWUgKE9wdGlvbmFsKSBHcm91cCB0aGUgd29ybGQgZXhpc3RzIGluLiBJZiBub3QgcHJvdmlkZWQsIHBpY2tzIHVwIGdyb3VwIGZyb20gY3VycmVudCBzZXNzaW9uIGlmIGVuZCB1c2VyIGlzIGxvZ2dlZCBpbi5cbiAgICAgKiBAcmV0dXJuIHtDaGFubmVsfSBDaGFubmVsIGluc3RhbmNlXG4gICAgICovXG4gICAgZ2V0UHJlc2VuY2VDaGFubmVsOiBmdW5jdGlvbiAod29ybGQsIHVzZXJpZCwgZ3JvdXBOYW1lKSB7XG4gICAgICAgIHZhciB3b3JsZGlkID0gKCQuaXNQbGFpbk9iamVjdCh3b3JsZCkgJiYgd29ybGQuaWQpID8gd29ybGQuaWQgOiB3b3JsZDtcbiAgICAgICAgaWYgKCF3b3JsZGlkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsZWFzZSBzcGVjaWZ5IGEgd29ybGQgaWQnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKHRoaXMub3B0aW9ucyk7XG4gICAgICAgIHVzZXJpZCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcih1c2VyaWQsICd1c2VySWQnLCBzZXNzaW9uKTtcbiAgICAgICAgZ3JvdXBOYW1lID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKGdyb3VwTmFtZSwgJ2dyb3VwTmFtZScsIHNlc3Npb24pO1xuXG4gICAgICAgIHZhciBhY2NvdW50ID0gZ2V0RnJvbVNlc3Npb25PckVycm9yKCcnLCAnYWNjb3VudCcsIHNlc3Npb24pO1xuICAgICAgICB2YXIgcHJvamVjdCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcignJywgJ3Byb2plY3QnLCBzZXNzaW9uKTtcblxuICAgICAgICB2YXIgYmFzZVRvcGljID0gWycvdXNlcicsIGFjY291bnQsIHByb2plY3QsIGdyb3VwTmFtZSwgd29ybGRpZF0uam9pbignLycpO1xuICAgICAgICB2YXIgY2hhbm5lbCA9IF9fc3VwZXIuZ2V0Q2hhbm5lbC5jYWxsKHRoaXMsIHsgYmFzZTogYmFzZVRvcGljIH0pO1xuXG4gICAgICAgIHZhciBsYXN0UGluZ1RpbWUgPSB7IH07XG5cbiAgICAgICAgdmFyIFBJTkdfSU5URVJWQUwgPSA2MDAwO1xuICAgICAgICBjaGFubmVsLnN1YnNjcmliZSgnaW50ZXJuYWwtcGluZy1jaGFubmVsJywgZnVuY3Rpb24gKG5vdGlmaWNhdGlvbikge1xuICAgICAgICAgICAgdmFyIGluY29taW5nVXNlcklkID0gbm90aWZpY2F0aW9uLmRhdGEudXNlcjtcbiAgICAgICAgICAgIGlmICghbGFzdFBpbmdUaW1lW2luY29taW5nVXNlcklkXSAmJiBpbmNvbWluZ1VzZXJJZCAhPT0gdXNlcmlkKSB7XG4gICAgICAgICAgICAgICAgY2hhbm5lbC50cmlnZ2VyKCdwcmVzZW5jZScsIHsgdXNlcklkOiBpbmNvbWluZ1VzZXJJZCwgb25saW5lOiB0cnVlIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGFzdFBpbmdUaW1lW2luY29taW5nVXNlcklkXSA9IChuZXcgRGF0ZSgpKS52YWx1ZU9mKCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHNldEludGVydmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNoYW5uZWwucHVibGlzaCgnaW50ZXJuYWwtcGluZy1jaGFubmVsJywgeyB1c2VyOiB1c2VyaWQgfSk7XG5cbiAgICAgICAgICAgICQuZWFjaChsYXN0UGluZ1RpbWUsIGZ1bmN0aW9uIChrZXksIHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgdmFyIG5vdyA9IChuZXcgRGF0ZSgpKS52YWx1ZU9mKCk7XG4gICAgICAgICAgICAgICAgaWYgKHZhbHVlICYmIHZhbHVlICsgKFBJTkdfSU5URVJWQUwgKiAyKSA8IG5vdykge1xuICAgICAgICAgICAgICAgICAgICBsYXN0UGluZ1RpbWVba2V5XSA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgIGNoYW5uZWwudHJpZ2dlcigncHJlc2VuY2UnLCB7IHVzZXJJZDoga2V5LCBvbmxpbmU6IGZhbHNlIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9LCBQSU5HX0lOVEVSVkFMKTtcblxuICAgICAgICByZXR1cm4gY2hhbm5lbDtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGFuZCByZXR1cm4gYSBwdWJsaXNoL3N1YnNjcmliZSBjaGFubmVsIChmcm9tIHRoZSB1bmRlcmx5aW5nIFtDaGFubmVsIE1hbmFnZXJdKC4uL2NoYW5uZWwtbWFuYWdlci8pKSBmb3IgdGhlIGdpdmVuIGNvbGxlY3Rpb24uIChUaGUgY29sbGVjdGlvbiBuYW1lIGlzIHNwZWNpZmllZCBpbiB0aGUgYHJvb3RgIGFyZ3VtZW50IHdoZW4gdGhlIFtEYXRhIFNlcnZpY2VdKC4uL2RhdGEtYXBpLXNlcnZpY2UvKSBpcyBpbnN0YW50aWF0ZWQuKSBNdXN0IGJlIG9uZSBvZiB0aGUgY29sbGVjdGlvbnMgaW4gdGhpcyBhY2NvdW50ICh0ZWFtKSBhbmQgcHJvamVjdC5cbiAgICAgKlxuICAgICAqIFRoZXJlIGFyZSBhdXRvbWF0aWMgbm90aWZpY2F0aW9ucyBmcm9tIEVwaWNlbnRlciBvbiB0aGlzIGNoYW5uZWwgd2hlbiBkYXRhIGlzIGNyZWF0ZWQsIHVwZGF0ZWQsIG9yIGRlbGV0ZWQgaW4gdGhpcyBjb2xsZWN0aW9uLiBTZWUgbW9yZSBvbiBbYXV0b21hdGljIG1lc3NhZ2VzIHRvIHRoZSBkYXRhIGNoYW5uZWxdKC4uLy4uLy4uL3Jlc3RfYXBpcy9tdWx0aXBsYXllci9jaGFubmVsLyNkYXRhLW1lc3NhZ2VzKS5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgdmFyIGNtID0gbmV3IEYubWFuYWdlci5DaGFubmVsTWFuYWdlcigpO1xuICAgICAqICAgICB2YXIgZGMgPSBjbS5nZXREYXRhQ2hhbm5lbCgnc3VydmV5LXJlc3BvbnNlcycpO1xuICAgICAqICAgICBkYy5zdWJzY3JpYmUoJycsIGZ1bmN0aW9uKGRhdGEsIG1ldGEpIHtcbiAgICAgKiAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhKTtcbiAgICAgKlxuICAgICAqICAgICAgICAgIC8vIG1ldGEuZGF0ZSBpcyB0aW1lIG9mIGNoYW5nZSxcbiAgICAgKiAgICAgICAgICAvLyBtZXRhLnN1YlR5cGUgaXMgdGhlIGtpbmQgb2YgY2hhbmdlOiBuZXcsIHVwZGF0ZSwgb3IgZGVsZXRlXG4gICAgICogICAgICAgICAgLy8gbWV0YS5wYXRoIGlzIHRoZSBmdWxsIHBhdGggdG8gdGhlIGNoYW5nZWQgZGF0YVxuICAgICAqICAgICAgICAgIGNvbnNvbGUubG9nKG1ldGEpO1xuICAgICAqICAgICB9KTtcbiAgICAgKlxuICAgICAqICoqUmV0dXJuIFZhbHVlKipcbiAgICAgKlxuICAgICAqICogKkNoYW5uZWwqIFJldHVybnMgdGhlIGNoYW5uZWwgKGFuIGluc3RhbmNlIG9mIHRoZSBbQ2hhbm5lbCBTZXJ2aWNlXSguLi9jaGFubmVsLXNlcnZpY2UvKSkuXG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqXG4gICAgICogQHBhcmFtICB7U3RyaW5nfSBjb2xsZWN0aW9uIE5hbWUgb2YgY29sbGVjdGlvbiB3aG9zZSBhdXRvbWF0aWMgbm90aWZpY2F0aW9ucyB5b3Ugd2FudCB0byByZWNlaXZlLlxuICAgICAqIEByZXR1cm4ge0NoYW5uZWx9IENoYW5uZWwgaW5zdGFuY2VcbiAgICAgKi9cbiAgICBnZXREYXRhQ2hhbm5lbDogZnVuY3Rpb24gKGNvbGxlY3Rpb24pIHtcbiAgICAgICAgaWYgKCFjb2xsZWN0aW9uKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsZWFzZSBzcGVjaWZ5IGEgY29sbGVjdGlvbiB0byBsaXN0ZW4gb24uJyk7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyh0aGlzLm9wdGlvbnMpO1xuICAgICAgICB2YXIgYWNjb3VudCA9IGdldEZyb21TZXNzaW9uT3JFcnJvcignJywgJ2FjY291bnQnLCBzZXNzaW9uKTtcbiAgICAgICAgdmFyIHByb2plY3QgPSBnZXRGcm9tU2Vzc2lvbk9yRXJyb3IoJycsICdwcm9qZWN0Jywgc2Vzc2lvbik7XG4gICAgICAgIHZhciBiYXNlVG9waWMgPSBbJy9kYXRhJywgYWNjb3VudCwgcHJvamVjdCwgY29sbGVjdGlvbl0uam9pbignLycpO1xuICAgICAgICB2YXIgY2hhbm5lbCA9IF9fc3VwZXIuZ2V0Q2hhbm5lbC5jYWxsKHRoaXMsIHsgYmFzZTogYmFzZVRvcGljIH0pO1xuXG4gICAgICAgIC8vVE9ETzogRml4IGFmdGVyIEVwaWNlbnRlciBidWcgaXMgcmVzb2x2ZWRcbiAgICAgICAgdmFyIG9sZHN1YnMgPSBjaGFubmVsLnN1YnNjcmliZTtcbiAgICAgICAgY2hhbm5lbC5zdWJzY3JpYmUgPSBmdW5jdGlvbiAodG9waWMsIGNhbGxiYWNrLCBjb250ZXh0LCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgY2FsbGJhY2tXaXRoQ2xlYW5EYXRhID0gZnVuY3Rpb24gKHBheWxvYWQpIHtcbiAgICAgICAgICAgICAgICB2YXIgbWV0YSA9IHtcbiAgICAgICAgICAgICAgICAgICAgcGF0aDogcGF5bG9hZC5jaGFubmVsLFxuICAgICAgICAgICAgICAgICAgICBzdWJUeXBlOiBwYXlsb2FkLmRhdGEuc3ViVHlwZSxcbiAgICAgICAgICAgICAgICAgICAgZGF0ZTogcGF5bG9hZC5kYXRhLmRhdGVcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIHZhciBhY3R1YWxEYXRhID0gcGF5bG9hZC5kYXRhLmRhdGE7XG4gICAgICAgICAgICAgICAgaWYgKGFjdHVhbERhdGEuZGF0YSkgeyAvL0RlbGV0ZSBub3RpZmljYXRpb25zIGFyZSBvbmUgZGF0YS1sZXZlbCBiZWhpbmQgb2YgY291cnNlXG4gICAgICAgICAgICAgICAgICAgIGFjdHVhbERhdGEgPSBhY3R1YWxEYXRhLmRhdGE7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgY2FsbGJhY2suY2FsbChjb250ZXh0LCBhY3R1YWxEYXRhLCBtZXRhKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXR1cm4gb2xkc3Vicy5jYWxsKGNoYW5uZWwsIHRvcGljLCBjYWxsYmFja1dpdGhDbGVhbkRhdGEsIGNvbnRleHQsIG9wdGlvbnMpO1xuICAgICAgICB9O1xuXG4gICAgICAgIHJldHVybiBjaGFubmVsO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEVwaWNlbnRlckNoYW5uZWxNYW5hZ2VyO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBFUElfU0VTU0lPTl9LRVk6ICdlcGljZW50ZXJqcy5zZXNzaW9uJyxcbiAgICBTVFJBVEVHWV9TRVNTSU9OX0tFWTogJ2VwaWNlbnRlci1zY2VuYXJpbydcbn07IiwiLyoqXG4qICMjIFJ1biBNYW5hZ2VyXG4qXG4qIFRoZSBSdW4gTWFuYWdlciBnaXZlcyB5b3UgYWNjZXNzIHRvIHJ1bnMgZm9yIHlvdXIgcHJvamVjdC4gVGhpcyBhbGxvd3MgeW91IHRvIHJlYWQgYW5kIHVwZGF0ZSB2YXJpYWJsZXMsIGNhbGwgb3BlcmF0aW9ucywgZXRjLiBBZGRpdGlvbmFsbHksIHRoZSBSdW4gTWFuYWdlciBnaXZlcyB5b3UgY29udHJvbCBvdmVyIHJ1biBjcmVhdGlvbiBkZXBlbmRpbmcgb24gcnVuIHN0YXRlcy4gU3BlY2lmaWNhbGx5LCB5b3UgY2FuIHNlbGVjdCBbcnVuIGNyZWF0aW9uIHN0cmF0ZWdpZXMgKHJ1bGVzKV0oLi4vc3RyYXRlZ2llcy8pIGZvciB3aGljaCBydW5zIGVuZCB1c2VycyBvZiB5b3VyIHByb2plY3Qgd29yayB3aXRoIHdoZW4gdGhleSBsb2cgaW4gdG8geW91ciBwcm9qZWN0LlxuKlxuKiBUaGVyZSBhcmUgbWFueSB3YXlzIHRvIGNyZWF0ZSBuZXcgcnVucywgaW5jbHVkaW5nIHRoZSBFcGljZW50ZXIuanMgW1J1biBTZXJ2aWNlXSguLi9ydW4tYXBpLXNlcnZpY2UvKSwgdGhlIFJFU0ZUZnVsIFtSdW4gQVBJXSguLi8uLi8uLi9yZXN0X2FwaXMvYWdncmVnYXRlX3J1bl9hcGkpIGFuZCB0aGUgW01vZGVsIFJ1biBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9vdGhlcl9hcGlzL21vZGVsX2FwaXMvcnVuLykuIEhvd2V2ZXIsIGZvciBzb21lIHByb2plY3RzIGl0IG1ha2VzIG1vcmUgc2Vuc2UgdG8gcGljayB1cCB3aGVyZSB0aGUgdXNlciBsZWZ0IG9mZiwgdXNpbmcgYW4gZXhpc3RpbmcgcnVuLiBBbmQgaW4gc29tZSBwcm9qZWN0cywgd2hldGhlciB0byBjcmVhdGUgYSBuZXcgcnVuIG9yIHVzZSBhbiBleGlzdGluZyBvbmUgaXMgY29uZGl0aW9uYWwsIGZvciBleGFtcGxlIGJhc2VkIG9uIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgZXhpc3RpbmcgcnVuIG9yIHlvdXIgb3duIGtub3dsZWRnZSBhYm91dCB0aGUgbW9kZWwuIFRoZSBSdW4gTWFuYWdlciBwcm92aWRlcyB0aGlzIGxldmVsIG9mIGNvbnRyb2w6IHlvdXIgY2FsbCB0byBgZ2V0UnVuKClgLCByYXRoZXIgdGhhbiBhbHdheXMgcmV0dXJuaW5nIGEgbmV3IHJ1biwgcmV0dXJucyBhIHJ1biBiYXNlZCBvbiB0aGUgc3RyYXRlZ3kgeW91J3ZlIHNwZWNpZmllZC4gKE5vdGUgdGhhdCBtYW55IG9mIHRoZSBFcGljZW50ZXIgc2FtcGxlIHByb2plY3RzIHVzZSBhIFJ1biBTZXJ2aWNlIGRpcmVjdGx5LCBiZWNhdXNlIGdlbmVyYWxseSB0aGUgc2FtcGxlIHByb2plY3RzIGFyZSBwbGF5ZWQgaW4gb25lIGVuZCB1c2VyIHNlc3Npb24gYW5kIGRvbid0IGNhcmUgYWJvdXQgcnVuIHN0YXRlcyBvciBydW4gc3RyYXRlZ2llcy4pXG4qXG4qXG4qICMjIyBVc2luZyB0aGUgUnVuIE1hbmFnZXIgdG8gY3JlYXRlIGFuZCBhY2Nlc3MgcnVuc1xuKlxuKiBUbyB1c2UgdGhlIFJ1biBNYW5hZ2VyLCBpbnN0YW50aWF0ZSBpdCBieSBwYXNzaW5nIGluOlxuKlxuKiAgICogYHJ1bmA6IChyZXF1aXJlZCkgUnVuIG9iamVjdC4gTXVzdCBjb250YWluOlxuKiAgICAgICAqIGBhY2NvdW50YDogRXBpY2VudGVyIGFjY291bnQgaWQgKCoqVGVhbSBJRCoqIGZvciB0ZWFtIHByb2plY3RzLCAqKlVzZXIgSUQqKiBmb3IgcGVyc29uYWwgcHJvamVjdHMpLlxuKiAgICAgICAqIGBwcm9qZWN0YDogRXBpY2VudGVyIHByb2plY3QgaWQuXG4qICAgICAgICogYG1vZGVsYDogVGhlIG5hbWUgb2YgeW91ciBwcmltYXJ5IG1vZGVsIGZpbGUuIChTZWUgbW9yZSBvbiBbV3JpdGluZyB5b3VyIE1vZGVsXSguLi8uLi8uLi93cml0aW5nX3lvdXJfbW9kZWwvKS4pXG4qICAgICAgICogYHNjb3BlYDogKG9wdGlvbmFsKSBTY29wZSBvYmplY3QgZm9yIHRoZSBydW4sIGZvciBleGFtcGxlIGBzY29wZS5ncm91cGAgd2l0aCB2YWx1ZSBvZiB0aGUgbmFtZSBvZiB0aGUgZ3JvdXAuXG4qICAgICAgICogYHNlcnZlcmA6IChvcHRpb25hbCkgQW4gb2JqZWN0IHdpdGggb25lIGZpZWxkLCBgaG9zdGAuIFRoZSB2YWx1ZSBvZiBgaG9zdGAgaXMgdGhlIHN0cmluZyBgYXBpLmZvcmlvLmNvbWAsIHRoZSBVUkkgb2YgdGhlIEZvcmlvIHNlcnZlci4gVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHNldCwgYnV0IHlvdSBjYW4gcGFzcyBpdCBleHBsaWNpdGx5IGlmIGRlc2lyZWQuIEl0IGlzIG1vc3QgY29tbW9ubHkgdXNlZCBmb3IgY2xhcml0eSB3aGVuIHlvdSBhcmUgW2hvc3RpbmcgYW4gRXBpY2VudGVyIHByb2plY3Qgb24geW91ciBvd24gc2VydmVyXSguLi8uLi8uLi9ob3dfdG8vc2VsZl9ob3N0aW5nLykuXG4qICAgICAgICogYGZpbGVzYDogKG9wdGlvbmFsKSBJZiBhbmQgb25seSBpZiB5b3UgYXJlIHVzaW5nIGEgVmVuc2ltIG1vZGVsIGFuZCB5b3UgaGF2ZSBhZGRpdGlvbmFsIGRhdGEgdG8gcGFzcyBpbiB0byB5b3VyIG1vZGVsLCB5b3UgY2FuIHBhc3MgYSBgZmlsZXNgIG9iamVjdCB3aXRoIHRoZSBuYW1lcyBvZiB0aGUgZmlsZXMsIGZvciBleGFtcGxlOiBgXCJmaWxlc1wiOiB7XCJkYXRhXCI6IFwibXlFeHRyYURhdGEueGxzXCJ9YC4gKE5vdGUgdGhhdCB5b3UnbGwgYWxzbyBuZWVkIHRvIGFkZCB0aGlzIHNhbWUgZmlsZXMgb2JqZWN0IHRvIHlvdXIgVmVuc2ltIFtjb25maWd1cmF0aW9uIGZpbGVdKC4uLy4uLy4uL21vZGVsX2NvZGUvdmVuc2ltLykuKSBTZWUgdGhlIFt1bmRlcmx5aW5nIE1vZGVsIFJ1biBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9vdGhlcl9hcGlzL21vZGVsX2FwaXMvcnVuLyNwb3N0LWNyZWF0aW5nLWEtbmV3LXJ1bi1mb3ItdGhpcy1wcm9qZWN0KSBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvbi5cbipcbiogICAqIGBzdHJhdGVneWA6IChvcHRpb25hbCkgUnVuIGNyZWF0aW9uIHN0cmF0ZWd5IGZvciB3aGVuIHRvIGNyZWF0ZSBhIG5ldyBydW4gYW5kIHdoZW4gdG8gcmV1c2UgYW4gZW5kIHVzZXIncyBleGlzdGluZyBydW4uIFNlZSBbUnVuIE1hbmFnZXIgU3RyYXRlZ2llc10oLi4vc3RyYXRlZ2llcy8pIGZvciBkZXRhaWxzLiBEZWZhdWx0cyB0byBgbmV3LWlmLWluaXRpYWxpemVkYC5cbipcbiogICAqIGBzZXNzaW9uS2V5YDogKG9wdGlvbmFsKSBOYW1lIG9mIGJyb3dzZXIgY29va2llIGluIHdoaWNoIHRvIHN0b3JlIHJ1biBpbmZvcm1hdGlvbiwgaW5jbHVkaW5nIHJ1biBpZC4gTWFueSBjb25kaXRpb25hbCBzdHJhdGVnaWVzLCBpbmNsdWRpbmcgdGhlIHByb3ZpZGVkIHN0cmF0ZWdpZXMsIHJlbHkgb24gdGhpcyBicm93c2VyIGNvb2tpZSB0byBzdG9yZSB0aGUgcnVuIGlkIGFuZCBoZWxwIG1ha2UgdGhlIGRlY2lzaW9uIG9mIHdoZXRoZXIgdG8gY3JlYXRlIGEgbmV3IHJ1biBvciB1c2UgYW4gZXhpc3Rpbmcgb25lLiBUaGUgbmFtZSBvZiB0aGlzIGNvb2tpZSBkZWZhdWx0cyB0byBgZXBpY2VudGVyLXNjZW5hcmlvYCBhbmQgY2FuIGJlIHNldCB3aXRoIHRoZSBgc2Vzc2lvbktleWAgcGFyYW1ldGVyLlxuKlxuKlxuKiBBZnRlciBpbnN0YW50aWF0aW5nIGEgUnVuIE1hbmFnZXIsIG1ha2UgYSBjYWxsIHRvIGBnZXRSdW4oKWAgd2hlbmV2ZXIgeW91IG5lZWQgdG8gYWNjZXNzIGEgcnVuIGZvciB0aGlzIGVuZCB1c2VyLiBUaGUgYFJ1bk1hbmFnZXIucnVuYCBjb250YWlucyB0aGUgaW5zdGFudGlhdGVkIFtSdW4gU2VydmljZV0oLi4vcnVuLWFwaS1zZXJ2aWNlLykuIFRoZSBSdW4gU2VydmljZSBhbGxvd3MgeW91IHRvIGFjY2VzcyB2YXJpYWJsZXMsIGNhbGwgb3BlcmF0aW9ucywgZXRjLlxuKlxuKiAqKkV4YW1wbGUqKlxuKlxuKiAgICAgICB2YXIgcm0gPSBuZXcgRi5tYW5hZ2VyLlJ1bk1hbmFnZXIoe1xuKiAgICAgICAgICAgcnVuOiB7XG4qICAgICAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuKiAgICAgICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4qICAgICAgICAgICAgICAgbW9kZWw6ICdzdXBwbHktY2hhaW4tbW9kZWwuamwnLFxuKiAgICAgICAgICAgICAgIHNlcnZlcjogeyBob3N0OiAnYXBpLmZvcmlvLmNvbScgfVxuKiAgICAgICAgICAgfSxcbiogICAgICAgICAgIHN0cmF0ZWd5OiAnYWx3YXlzLW5ldycsXG4qICAgICAgICAgICBzZXNzaW9uS2V5OiAnZXBpY2VudGVyLXNlc3Npb24nXG4qICAgICAgIH0pO1xuKiAgICAgICBybS5nZXRSdW4oKVxuKiAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24ocnVuKSB7XG4qICAgICAgICAgICAgICAgLy8gdGhlIHJldHVybiB2YWx1ZSBvZiBnZXRSdW4oKSBpcyBhIHJ1biBvYmplY3RcbiogICAgICAgICAgICAgICB2YXIgdGhpc1J1bklkID0gcnVuLmlkO1xuKiAgICAgICAgICAgICAgIC8vIHRoZSBSdW5NYW5hZ2VyLnJ1biBhbHNvIGNvbnRhaW5zIHRoZSBpbnN0YW50aWF0ZWQgUnVuIFNlcnZpY2UsXG4qICAgICAgICAgICAgICAgLy8gc28gYW55IFJ1biBTZXJ2aWNlIG1ldGhvZCBpcyB2YWxpZCBoZXJlXG4qICAgICAgICAgICAgICAgcm0ucnVuLmRvKCdydW5Nb2RlbCcpO1xuKiAgICAgICB9KVxuKlxuKi9cblxuJ3VzZSBzdHJpY3QnO1xudmFyIHN0cmF0ZWdpZXNNYXAgPSByZXF1aXJlKCcuL3J1bi1zdHJhdGVnaWVzL3N0cmF0ZWdpZXMtbWFwJyk7XG52YXIgc3BlY2lhbE9wZXJhdGlvbnMgPSByZXF1aXJlKCcuL3NwZWNpYWwtb3BlcmF0aW9ucycpO1xudmFyIFJ1blNlcnZpY2UgPSByZXF1aXJlKCcuLi9zZXJ2aWNlL3J1bi1hcGktc2VydmljZScpO1xuXG5cbmZ1bmN0aW9uIHBhdGNoUnVuU2VydmljZShzZXJ2aWNlLCBtYW5hZ2VyKSB7XG4gICAgaWYgKHNlcnZpY2UucGF0Y2hlZCkge1xuICAgICAgICByZXR1cm4gc2VydmljZTtcbiAgICB9XG5cbiAgICB2YXIgb3JpZyA9IHNlcnZpY2UuZG87XG4gICAgc2VydmljZS5kbyA9IGZ1bmN0aW9uIChvcGVyYXRpb24sIHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICB2YXIgcmVzZXJ2ZWRPcHMgPSBPYmplY3Qua2V5cyhzcGVjaWFsT3BlcmF0aW9ucyk7XG4gICAgICAgIGlmIChyZXNlcnZlZE9wcy5pbmRleE9mKG9wZXJhdGlvbikgPT09IC0xKSB7XG4gICAgICAgICAgICByZXR1cm4gb3JpZy5hcHBseShzZXJ2aWNlLCBhcmd1bWVudHMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHNwZWNpYWxPcGVyYXRpb25zW29wZXJhdGlvbl0uY2FsbChzZXJ2aWNlLCBwYXJhbXMsIG9wdGlvbnMsIG1hbmFnZXIpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHNlcnZpY2UucGF0Y2hlZCA9IHRydWU7XG5cbiAgICByZXR1cm4gc2VydmljZTtcbn1cblxuXG52YXIgZGVmYXVsdHMgPSB7XG4gICAgLyoqXG4gICAgICogUnVuIGNyZWF0aW9uIHN0cmF0ZWd5IGZvciB3aGVuIHRvIGNyZWF0ZSBhIG5ldyBydW4gYW5kIHdoZW4gdG8gcmV1c2UgYW4gZW5kIHVzZXIncyBleGlzdGluZyBydW4uIFNlZSBbUnVuIE1hbmFnZXIgU3RyYXRlZ2llc10oLi4vc3RyYXRlZ2llcy8pIGZvciBkZXRhaWxzLiBEZWZhdWx0cyB0byBgbmV3LWlmLWluaXRpYWxpemVkYC5cbiAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAqL1xuXG4gICAgc3RyYXRlZ3k6ICduZXctaWYtaW5pdGlhbGl6ZWQnXG59O1xuXG5mdW5jdGlvbiBSdW5NYW5hZ2VyKG9wdGlvbnMpIHtcbiAgICB0aGlzLm9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgZGVmYXVsdHMsIG9wdGlvbnMpO1xuXG4gICAgaWYgKHRoaXMub3B0aW9ucy5ydW4gaW5zdGFuY2VvZiBSdW5TZXJ2aWNlKSB7XG4gICAgICAgIHRoaXMucnVuID0gdGhpcy5vcHRpb25zLnJ1bjtcbiAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnJ1biA9IG5ldyBSdW5TZXJ2aWNlKHRoaXMub3B0aW9ucy5ydW4pO1xuICAgIH1cblxuICAgIHBhdGNoUnVuU2VydmljZSh0aGlzLnJ1biwgdGhpcyk7XG5cbiAgICB2YXIgU3RyYXRlZ3lDdG9yID0gdHlwZW9mIHRoaXMub3B0aW9ucy5zdHJhdGVneSA9PT0gJ2Z1bmN0aW9uJyA/IHRoaXMub3B0aW9ucy5zdHJhdGVneSA6IHN0cmF0ZWdpZXNNYXBbdGhpcy5vcHRpb25zLnN0cmF0ZWd5XTtcblxuICAgIGlmICghU3RyYXRlZ3lDdG9yKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignU3BlY2lmaWVkIHJ1biBjcmVhdGlvbiBzdHJhdGVneSB3YXMgaW52YWxpZDonLCB0aGlzLm9wdGlvbnMuc3RyYXRlZ3kpO1xuICAgIH1cblxuICAgIHRoaXMuc3RyYXRlZ3kgPSBuZXcgU3RyYXRlZ3lDdG9yKHRoaXMucnVuLCB0aGlzLm9wdGlvbnMpO1xufVxuXG5SdW5NYW5hZ2VyLnByb3RvdHlwZSA9IHtcbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBydW4gb2JqZWN0IGZvciBhICdnb29kJyBydW4uXG4gICAgICpcbiAgICAgKiBBIGdvb2QgcnVuIGlzIGRlZmluZWQgYnkgdGhlIHN0cmF0ZWd5LiBGb3IgZXhhbXBsZSwgaWYgdGhlIHN0cmF0ZWd5IGlzIGBhbHdheXMtbmV3YCwgdGhlIGNhbGxcbiAgICAgKiB0byBgZ2V0UnVuKClgIGFsd2F5cyByZXR1cm5zIGEgbmV3bHkgY3JlYXRlZCBydW47IGlmIHRoZSBzdHJhdGVneSBpcyBgbmV3LWlmLXBlcnNpc3RlZGAsXG4gICAgICogYGdldFJ1bigpYCBjcmVhdGVzIGEgbmV3IHJ1biBpZiB0aGUgcHJldmlvdXMgcnVuIGlzIGluIGEgcGVyc2lzdGVkIHN0YXRlLCBvdGhlcndpc2VcbiAgICAgKiBpdCByZXR1cm5zIHRoZSBwcmV2aW91cyBydW4uIFNlZSBbUnVuIE1hbmFnZXIgU3RyYXRlZ2llc10oLi4vc3RyYXRlZ2llcy8pIGZvciBtb3JlIG9uIHN0cmF0ZWdpZXMuXG4gICAgICpcbiAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICAgcm0uZ2V0UnVuKCkudGhlbihmdW5jdGlvbiAocnVuKSB7XG4gICAgICogICAgICAgICAgLy8gdXNlIHRoZSBydW4gb2JqZWN0XG4gICAgICogICAgICAgICAgdmFyIHRoaXNSdW5JZCA9IHJ1bi5pZDtcbiAgICAgKlxuICAgICAqICAgICAgICAgIC8vIHVzZSB0aGUgUnVuIFNlcnZpY2Ugb2JqZWN0XG4gICAgICogICAgICAgICAgcm0ucnVuLmRvKCdydW5Nb2RlbCcpO1xuICAgICAqICAgICAgfSk7XG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHskcHJvbWlzZX0gUHJvbWlzZSB0byBjb21wbGV0ZSB0aGUgY2FsbC5cbiAgICAgKi9cbiAgICBnZXRSdW46IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RyYXRlZ3lcbiAgICAgICAgICAgICAgICAuZ2V0UnVuKCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHJ1biBvYmplY3QgZm9yIGEgbmV3IHJ1biwgcmVnYXJkbGVzcyBvZiBzdHJhdGVneTogZm9yY2UgY3JlYXRpb24gb2YgYSBuZXcgcnVuLlxuICAgICAqXG4gICAgICogICoqRXhhbXBsZSoqXG4gICAgICpcbiAgICAgKiAgICAgIHJtLnJlc2V0KCkudGhlbihmdW5jdGlvbiAocnVuKSB7XG4gICAgICogICAgICAgICAgLy8gdXNlIHRoZSAobmV3KSBydW4gb2JqZWN0XG4gICAgICogICAgICAgICAgdmFyIHRoaXNSdW5JZCA9IHJ1bi5pZDtcbiAgICAgKlxuICAgICAqICAgICAgICAgIC8vIHVzZSB0aGUgUnVuIFNlcnZpY2Ugb2JqZWN0XG4gICAgICogICAgICAgICAgcm0ucnVuLmRvKCdydW5Nb2RlbCcpO1xuICAgICAqICAgICAgfSk7XG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBydW5TZXJ2aWNlT3B0aW9ucyBUaGUgb3B0aW9ucyBvYmplY3QgdG8gY29uZmlndXJlIHRoZSBSdW4gU2VydmljZS4gU2VlIFtSdW4gQVBJIFNlcnZpY2VdKC4uL3J1bi1hcGktc2VydmljZS8pIGZvciBtb3JlLlxuICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICovXG4gICAgcmVzZXQ6IGZ1bmN0aW9uIChydW5TZXJ2aWNlT3B0aW9ucykge1xuICAgICAgICByZXR1cm4gdGhpcy5zdHJhdGVneS5yZXNldChydW5TZXJ2aWNlT3B0aW9ucyk7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBSdW5NYW5hZ2VyO1xuIiwiLyoqXG4gKiBUaGUgYGFsd2F5cy1uZXdgIHN0cmF0ZWd5IGFsd2F5cyBjcmVhdGVzIGEgbmV3IHJ1biBmb3IgdGhpcyBlbmQgdXNlciBpcnJlc3BlY3RpdmUgb2YgY3VycmVudCBzdGF0ZS4gVGhpcyBpcyBlcXVpdmFsZW50IHRvIGNhbGxpbmcgYEYuc2VydmljZS5SdW4uY3JlYXRlKClgIGZyb20gdGhlIFtSdW4gU2VydmljZV0oLi4vcnVuLWFwaS1zZXJ2aWNlLykgZXZlcnkgdGltZS4gXG4gKiBcbiAqIFRoaXMgc3RyYXRlZ3kgbWVhbnMgdGhhdCBldmVyeSB0aW1lIHlvdXIgZW5kIHVzZXJzIHJlZnJlc2ggdGhlaXIgYnJvd3NlcnMsIHRoZXkgZ2V0IGEgbmV3IHJ1bi4gXG4gKiBcbiAqIFRoaXMgc3RyYXRlZ3kgY2FuIGJlIHVzZWZ1bCBmb3IgYmFzaWMsIHNpbmdsZS1wYWdlIHByb2plY3RzLiBUaGlzIHN0cmF0ZWd5IGlzIGFsc28gdXNlZnVsIGZvciBwcm90b3R5cGluZyBvciBwcm9qZWN0IGRldmVsb3BtZW50OiBpdCBjcmVhdGVzIGEgbmV3IHJ1biBlYWNoIHRpbWUgeW91IHJlZnJlc2ggdGhlIHBhZ2UsIGFuZCB5b3UgY2FuIGVhc2lseSBjaGVjayB0aGUgb3V0cHV0cyBvZiB0aGUgbW9kZWwuIEhvd2V2ZXIsIHR5cGljYWxseSB5b3Ugd2lsbCB1c2Ugb25lIG9mIHRoZSBvdGhlciBzdHJhdGVnaWVzIGZvciBhIHByb2R1Y3Rpb24gcHJvamVjdC5cbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBjbGFzc0Zyb20gPSByZXF1aXJlKCcuLi8uLi91dGlsL2luaGVyaXQnKTtcbnZhciBDb25kaXRpb25hbFN0cmF0ZWd5ID0gcmVxdWlyZSgnLi9jb25kaXRpb25hbC1jcmVhdGlvbi1zdHJhdGVneScpO1xuXG52YXIgX19zdXBlciA9IENvbmRpdGlvbmFsU3RyYXRlZ3kucHJvdG90eXBlO1xuXG52YXIgU3RyYXRlZ3kgPSBjbGFzc0Zyb20oQ29uZGl0aW9uYWxTdHJhdGVneSwge1xuICAgIGNvbnN0cnVjdG9yOiBmdW5jdGlvbiAocnVuU2VydmljZSwgb3B0aW9ucykge1xuICAgICAgICBfX3N1cGVyLmNvbnN0cnVjdG9yLmNhbGwodGhpcywgcnVuU2VydmljZSwgdGhpcy5jcmVhdGVJZiwgb3B0aW9ucyk7XG4gICAgfSxcblxuICAgIGNyZWF0ZUlmOiBmdW5jdGlvbiAocnVuLCBoZWFkZXJzKSB7XG4gICAgICAgIC8vIGFsd2F5cyBjcmVhdGUgYSBuZXcgcnVuIVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBTdHJhdGVneTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIEJhc2UgPSByZXF1aXJlKCcuL25vbmUtc3RyYXRlZ3knKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uLy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xudmFyIGNsYXNzRnJvbSA9IHJlcXVpcmUoJy4uLy4uL3V0aWwvaW5oZXJpdCcpO1xudmFyIEF1dGhNYW5hZ2VyID0gcmVxdWlyZSgnLi4vYXV0aC1tYW5hZ2VyJyk7XG5cbnZhciBrZXlOYW1lcyA9IHJlcXVpcmUoJy4uL2tleS1uYW1lcycpO1xuXG52YXIgZGVmYXVsdHMgPSB7XG4gICAgc2Vzc2lvbktleToga2V5TmFtZXMuU1RSQVRFR1lfU0VTU0lPTl9LRVksXG4gICAgcGF0aDogJydcbn07XG5cbmZ1bmN0aW9uIHNldFJ1bkluU2Vzc2lvbihzZXNzaW9uS2V5LCBydW4sIHNlc3Npb25NYW5hZ2VyKSB7XG4gICAgc2Vzc2lvbk1hbmFnZXIuZ2V0U3RvcmUoKS5zZXQoc2Vzc2lvbktleSwgSlNPTi5zdHJpbmdpZnkoeyBydW5JZDogcnVuLmlkIH0pKTtcbn1cblxuLyoqXG4qIENvbmRpdGlvbmFsIENyZWF0aW9uIFN0cmF0ZWd5XG4qIFRoaXMgc3RyYXRlZ3kgd2lsbCB0cnkgdG8gZ2V0IHRoZSBydW4gc3RvcmVkIGluIHRoZSBjb29raWUgYW5kXG4qIGV2YWx1YXRlIGlmIG5lZWRzIHRvIGNyZWF0ZSBhIG5ldyBydW4gYnkgY2FsbGluZyB0aGUgJ2NvbmRpdGlvbicgZnVuY3Rpb25cbiovXG5cbnZhciBTdHJhdGVneSA9IGNsYXNzRnJvbShCYXNlLCB7XG4gICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uIFN0cmF0ZWd5KHJ1blNlcnZpY2UsIGNvbmRpdGlvbiwgb3B0aW9ucykge1xuICAgICAgICBpZiAoY29uZGl0aW9uID09IG51bGwpIHsgLy9lc2xpbnQtZGlzYWJsZS1saW5lXG4gICAgICAgICAgICAvL1RPRE86IG5vdCBzdXJlIHdoeSB0aGlzIGlzIGV4cGxpY2l0bHkgPT1cbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQ29uZGl0aW9uYWwgc3RyYXRlZ3kgbmVlZHMgYSBjb25kaXRpb24gdG8gY3JlYXRlIGEgcnVuJyk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9hdXRoID0gbmV3IEF1dGhNYW5hZ2VyKCk7XG4gICAgICAgIHRoaXMucnVuID0gcnVuU2VydmljZTtcbiAgICAgICAgdGhpcy5jb25kaXRpb24gPSB0eXBlb2YgY29uZGl0aW9uICE9PSAnZnVuY3Rpb24nID8gZnVuY3Rpb24gKCkgeyByZXR1cm4gY29uZGl0aW9uOyB9IDogY29uZGl0aW9uO1xuICAgICAgICB0aGlzLm9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgZGVmYXVsdHMsIG9wdGlvbnMpO1xuICAgICAgICB0aGlzLnNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKG9wdGlvbnMpO1xuICAgICAgICB0aGlzLnJ1bk9wdGlvbnMgPSB0aGlzLm9wdGlvbnMucnVuO1xuICAgIH0sXG5cbiAgICBydW5PcHRpb25zV2l0aFNjb3BlOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciB1c2VyU2Vzc2lvbiA9IHRoaXMuX2F1dGguZ2V0Q3VycmVudFVzZXJTZXNzaW9uSW5mbygpO1xuICAgICAgICByZXR1cm4gJC5leHRlbmQoe1xuICAgICAgICAgICAgc2NvcGU6IHsgZ3JvdXA6IHVzZXJTZXNzaW9uLmdyb3VwTmFtZSB9XG4gICAgICAgIH0sIHRoaXMucnVuT3B0aW9ucyk7XG4gICAgfSxcblxuICAgIHJlc2V0OiBmdW5jdGlvbiAocnVuU2VydmljZU9wdGlvbnMpIHtcbiAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgdmFyIG9wdCA9IHRoaXMucnVuT3B0aW9uc1dpdGhTY29wZSgpO1xuXG4gICAgICAgIHJldHVybiB0aGlzLnJ1blxuICAgICAgICAgICAgICAgIC5jcmVhdGUob3B0LCBydW5TZXJ2aWNlT3B0aW9ucylcbiAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgICAgICAgICAgICBzZXRSdW5JblNlc3Npb24obWUub3B0aW9ucy5zZXNzaW9uS2V5LCBydW4sIG1lLnNlc3Npb25NYW5hZ2VyKTtcbiAgICAgICAgICAgICAgICBydW4uZnJlc2hseUNyZWF0ZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIHJldHVybiBydW47XG4gICAgICAgICAgICB9KTtcbiAgICB9LFxuXG4gICAgZ2V0UnVuOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBzZXNzaW9uU3RvcmUgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldFN0b3JlKCk7XG4gICAgICAgIHZhciBydW5TZXNzaW9uID0gSlNPTi5wYXJzZShzZXNzaW9uU3RvcmUuZ2V0KHRoaXMub3B0aW9ucy5zZXNzaW9uS2V5KSk7XG4gICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgIGlmIChydW5TZXNzaW9uICYmIHJ1blNlc3Npb24ucnVuSWQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLl9sb2FkQW5kQ2hlY2socnVuU2Vzc2lvbikuZmFpbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG1lLnJlc2V0KCk7IC8vaWYgaXQgZ290IHRoZSB3cm9uZyBjb29raWUgZm9yIGUuZy5cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucmVzZXQoKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfbG9hZEFuZENoZWNrOiBmdW5jdGlvbiAocnVuU2Vzc2lvbikge1xuICAgICAgICB2YXIgc2hvdWxkQ3JlYXRlID0gZmFsc2U7XG4gICAgICAgIHZhciBtZSA9IHRoaXM7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMucnVuXG4gICAgICAgICAgICAubG9hZChydW5TZXNzaW9uLnJ1bklkLCBudWxsLCB7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogZnVuY3Rpb24gKHJ1biwgbXNnLCBoZWFkZXJzKSB7XG4gICAgICAgICAgICAgICAgICAgIHNob3VsZENyZWF0ZSA9IG1lLmNvbmRpdGlvbihydW4sIGhlYWRlcnMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAudGhlbihmdW5jdGlvbiAocnVuKSB7XG4gICAgICAgICAgICAgICAgaWYgKHNob3VsZENyZWF0ZSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgb3B0ID0gbWUucnVuT3B0aW9uc1dpdGhTY29wZSgpO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWUucnVuLmNyZWF0ZShvcHQpXG4gICAgICAgICAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNldFJ1bkluU2Vzc2lvbihtZS5vcHRpb25zLnNlc3Npb25LZXksIHJ1biwgbWUuc2Vzc2lvbk1hbmFnZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgcnVuLmZyZXNobHlDcmVhdGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBydW47XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gcnVuO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzID0gU3RyYXRlZ3k7XG4iLCIvKipcbiAqIFRoZSBgbXVsdGlwbGF5ZXJgIHN0cmF0ZWd5IGlzIGZvciB1c2Ugd2l0aCBbbXVsdGlwbGF5ZXIgd29ybGRzXSguLi8uLi8uLi9nbG9zc2FyeS8jd29ybGQpLiBJdCBjaGVja3MgdGhlIGN1cnJlbnQgd29ybGQgZm9yIHRoaXMgZW5kIHVzZXIsIGFuZCBhbHdheXMgcmV0dXJucyB0aGUgY3VycmVudCBydW4gZm9yIHRoYXQgd29ybGQuIFRoaXMgaXMgZXF1aXZhbGVudCB0byBjYWxsaW5nIGBnZXRDdXJyZW50V29ybGRGb3JVc2VyKClgIGFuZCB0aGVuIGBnZXRDdXJyZW50UnVuSWQoKWAgZnJvbSB0aGUgW1dvcmxkIEFQSSBBZGFwYXRlcl0oLi4vd29ybGQtYXBpLWFkYXB0ZXIvKS5cbiAqIFxuICogVXNpbmcgdGhpcyBzdHJhdGVneSBtZWFucyB0aGF0IGVuZCB1c2VycyBpbiBwcm9qZWN0cyB3aXRoIG11bHRpcGxheWVyIHdvcmxkcyBhbHdheXMgc2VlIHRoZSBtb3N0IGN1cnJlbnQgcnVuIGFuZCB3b3JsZC4gVGhpcyBlbnN1cmVzIHRoYXQgdGhleSBhcmUgaW4gc3luYyB3aXRoIHRoZSBvdGhlciBlbmQgdXNlcnMgc2hhcmluZyB0aGVpciB3b3JsZCBhbmQgcnVuLiBJbiB0dXJuLCB0aGlzIGFsbG93cyBmb3IgY29tcGV0aXRpdmUgb3IgY29sbGFib3JhdGl2ZSBtdWx0aXBsYXllciBwcm9qZWN0cy5cbiAqL1xuJ3VzZSBzdHJpY3QnO1xuXG52YXIgY2xhc3NGcm9tID0gcmVxdWlyZSgnLi4vLi4vdXRpbC9pbmhlcml0Jyk7XG5cbnZhciBJZGVudGl0eVN0cmF0ZWd5ID0gcmVxdWlyZSgnLi9ub25lLXN0cmF0ZWd5Jyk7XG52YXIgV29ybGRBcGlBZGFwdGVyID0gcmVxdWlyZSgnLi4vLi4vc2VydmljZS93b3JsZC1hcGktYWRhcHRlcicpO1xudmFyIEF1dGhNYW5hZ2VyID0gcmVxdWlyZSgnLi4vYXV0aC1tYW5hZ2VyJyk7XG5cbnZhciBkZWZhdWx0cyA9IHtcbiAgICBzdG9yZToge1xuICAgICAgICBzeW5jaHJvbm91czogdHJ1ZVxuICAgIH1cbn07XG5cbnZhciBTdHJhdGVneSA9IGNsYXNzRnJvbShJZGVudGl0eVN0cmF0ZWd5LCB7XG5cbiAgICBjb25zdHJ1Y3RvcjogZnVuY3Rpb24gKHJ1blNlcnZpY2UsIG9wdGlvbnMpIHtcbiAgICAgICAgdGhpcy5ydW5TZXJ2aWNlID0gcnVuU2VydmljZTtcbiAgICAgICAgdGhpcy5vcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIGRlZmF1bHRzLCBvcHRpb25zKTtcbiAgICAgICAgdGhpcy5fYXV0aCA9IG5ldyBBdXRoTWFuYWdlcigpO1xuICAgICAgICB0aGlzLl9sb2FkUnVuID0gdGhpcy5fbG9hZFJ1bi5iaW5kKHRoaXMpO1xuICAgICAgICB0aGlzLndvcmxkQXBpID0gbmV3IFdvcmxkQXBpQWRhcHRlcih0aGlzLm9wdGlvbnMucnVuKTtcbiAgICB9LFxuXG4gICAgcmVzZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLl9hdXRoLmdldEN1cnJlbnRVc2VyU2Vzc2lvbkluZm8oKTtcbiAgICAgICAgdmFyIGN1clVzZXJJZCA9IHNlc3Npb24udXNlcklkO1xuICAgICAgICB2YXIgY3VyR3JvdXBOYW1lID0gc2Vzc2lvbi5ncm91cE5hbWU7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMud29ybGRBcGlcbiAgICAgICAgICAgIC5nZXRDdXJyZW50V29ybGRGb3JVc2VyKGN1clVzZXJJZCwgY3VyR3JvdXBOYW1lKVxuICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKHdvcmxkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMud29ybGRBcGkubmV3UnVuRm9yV29ybGQod29ybGQuaWQpO1xuICAgICAgICAgICAgfS5iaW5kKHRoaXMpKTtcbiAgICB9LFxuXG4gICAgZ2V0UnVuOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5fYXV0aC5nZXRDdXJyZW50VXNlclNlc3Npb25JbmZvKCk7XG4gICAgICAgIHZhciBjdXJVc2VySWQgPSBzZXNzaW9uLnVzZXJJZDtcbiAgICAgICAgdmFyIGN1ckdyb3VwTmFtZSA9IHNlc3Npb24uZ3JvdXBOYW1lO1xuICAgICAgICB2YXIgd29ybGRBcGkgPSB0aGlzLndvcmxkQXBpO1xuICAgICAgICB2YXIgbW9kZWwgPSB0aGlzLm9wdGlvbnMubW9kZWw7XG4gICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgIHZhciBkdGQgPSAkLkRlZmVycmVkKCk7XG5cbiAgICAgICAgaWYgKCFjdXJVc2VySWQpIHtcbiAgICAgICAgICAgIHJldHVybiBkdGQucmVqZWN0KHsgc3RhdHVzQ29kZTogNDAwLCBlcnJvcjogJ1dlIG5lZWQgYW4gYXV0aGVudGljYXRlZCB1c2VyIHRvIGpvaW4gYSBtdWx0aXBsYXllciB3b3JsZC4gKEVSUjogbm8gdXNlcklkIGluIHNlc3Npb24pJyB9LCBzZXNzaW9uKS5wcm9taXNlKCk7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgbG9hZFJ1bkZyb21Xb3JsZCA9IGZ1bmN0aW9uICh3b3JsZCkge1xuICAgICAgICAgICAgaWYgKCF3b3JsZCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBkdGQucmVqZWN0KHsgc3RhdHVzQ29kZTogNDA0LCBlcnJvcjogJ1RoZSB1c2VyIGlzIG5vdCBpbiBhbnkgd29ybGQuJyB9LCB7IG9wdGlvbnM6IG1lLm9wdGlvbnMsIHNlc3Npb246IHNlc3Npb24gfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB3b3JsZEFwaS5nZXRDdXJyZW50UnVuSWQoeyBtb2RlbDogbW9kZWwsIGZpbHRlcjogd29ybGQuaWQgfSlcbiAgICAgICAgICAgICAgICAudGhlbihtZS5fbG9hZFJ1bilcbiAgICAgICAgICAgICAgICAudGhlbihkdGQucmVzb2x2ZSlcbiAgICAgICAgICAgICAgICAuZmFpbChkdGQucmVqZWN0KTtcbiAgICAgICAgfTtcblxuICAgICAgICB2YXIgc2VydmVyRXJyb3IgPSBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgIC8vIGlzIHRoaXMgcG9zc2libGU/XG4gICAgICAgICAgICBkdGQucmVqZWN0KGVycm9yLCBzZXNzaW9uLCBtZS5vcHRpb25zKTtcbiAgICAgICAgfTtcblxuICAgICAgICB0aGlzLndvcmxkQXBpXG4gICAgICAgICAgICAuZ2V0Q3VycmVudFdvcmxkRm9yVXNlcihjdXJVc2VySWQsIGN1ckdyb3VwTmFtZSlcbiAgICAgICAgICAgIC50aGVuKGxvYWRSdW5Gcm9tV29ybGQpXG4gICAgICAgICAgICAuZmFpbChzZXJ2ZXJFcnJvcik7XG5cbiAgICAgICAgcmV0dXJuIGR0ZC5wcm9taXNlKCk7XG4gICAgfSxcblxuICAgIF9sb2FkUnVuOiBmdW5jdGlvbiAoaWQsIG9wdGlvbnMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucnVuU2VydmljZS5sb2FkKGlkLCBudWxsLCBvcHRpb25zKTtcbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBTdHJhdGVneTtcbiIsIi8qKlxuICogVGhlIGBuZXctaWYtaW5pdGlhbGl6ZWRgIHN0cmF0ZWd5IGNyZWF0ZXMgYSBuZXcgcnVuIGlmIHRoZSBjdXJyZW50IG9uZSBpcyBpbiBtZW1vcnkgb3IgaGFzIGl0cyBgaW5pdGlhbGl6ZWRgIGZpZWxkIHNldCB0byBgdHJ1ZWAuIFRoZSBgaW5pdGlhbGl6ZWRgIGZpZWxkIGluIHRoZSBydW4gcmVjb3JkIGlzIGF1dG9tYXRpY2FsbHkgc2V0IHRvIGB0cnVlYCBhdCBydW4gY3JlYXRpb24gZm9yIFZlbnNpbSBtb2RlbHM7IGl0IGNhbiBiZSBzZXQgbWFudWFsbHkgZm9yIG90aGVyIG1vZGVscy5cbiAqIFxuICogVGhpcyBzdHJhdGVneSBpcyB1c2VmdWwgaWYgeW91ciBwcm9qZWN0IGlzIHN0cnVjdHVyZWQgc3VjaCB0aGF0IGltbWVkaWF0ZWx5IGFmdGVyIGEgcnVuIGlzIGNyZWF0ZWQsIHRoZSBtb2RlbCBpcyBleGVjdXRlZCBjb21wbGV0ZWx5IChmb3IgZXhhbXBsZSwgYSBWZW5zaW0gbW9kZWwgaXMgc3RlcHBlZCB0byB0aGUgZW5kKS4gSXQgaXMgc2ltaWxhciB0byB0aGUgYG5ldy1pZi1taXNzaW5nYCBzdHJhdGVneSwgZXhjZXB0IHRoYXQgaXQgY2hlY2tzIGEgZmllbGQgb2YgdGhlIHJ1biByZWNvcmQuXG4gKiBcbiAqIFNwZWNpZmljYWxseSwgdGhlIHN0cmF0ZWd5IGlzOlxuICpcbiAqICogQ2hlY2sgdGhlIGBzZXNzaW9uS2V5YCBjb29raWUuIFxuICogICogVGhpcyBjb29raWUgaXMgc2V0IGJ5IHRoZSBbUnVuIE1hbmFnZXJdKC4uL3J1bi1tYW5hZ2VyLykgYW5kIGNvbmZpZ3VyYWJsZSB0aHJvdWdoIGl0cyBvcHRpb25zLlxuICogICogSWYgdGhlIGNvb2tpZSBleGlzdHMsIGNoZWNrIHdoZXRoZXIgdGhlIHJ1biBpcyBpbiBtZW1vcnkgb3Igb25seSBwZXJzaXN0ZWQgaW4gdGhlIGRhdGFiYXNlLiBBZGRpdGlvbmFsbHksIGNoZWNrIHdoZXRoZXIgdGhlIHJ1bidzIGBpbml0aWFsaXplZGAgZmllbGQgaXMgYHRydWVgLiBcbiAqICAgICAgKiBJZiB0aGUgcnVuIGlzIGluIG1lbW9yeSwgY3JlYXRlIGEgbmV3IHJ1bi5cbiAqICAgICAgKiBJZiB0aGUgcnVuJ3MgYGluaXRpYWxpemVkYCBmaWVsZCBpcyBgdHJ1ZWAsIGNyZWF0ZSBhIG5ldyBydW4uXG4gKiAgICAgICogT3RoZXJ3aXNlLCB1c2UgdGhlIGV4aXN0aW5nIHJ1bi5cbiAqICAqIElmIHRoZSBjb29raWUgZG9lcyBub3QgZXhpc3QsIGNyZWF0ZSBhIG5ldyBydW4gZm9yIHRoaXMgZW5kIHVzZXIuXG4gKi9cblxuJ3VzZSBzdHJpY3QnO1xudmFyIGNsYXNzRnJvbSA9IHJlcXVpcmUoJy4uLy4uL3V0aWwvaW5oZXJpdCcpO1xudmFyIENvbmRpdGlvbmFsU3RyYXRlZ3kgPSByZXF1aXJlKCcuL2NvbmRpdGlvbmFsLWNyZWF0aW9uLXN0cmF0ZWd5Jyk7XG5cbnZhciBfX3N1cGVyID0gQ29uZGl0aW9uYWxTdHJhdGVneS5wcm90b3R5cGU7XG5cbnZhciBTdHJhdGVneSA9IGNsYXNzRnJvbShDb25kaXRpb25hbFN0cmF0ZWd5LCB7XG4gICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uIChydW5TZXJ2aWNlLCBvcHRpb25zKSB7XG4gICAgICAgIF9fc3VwZXIuY29uc3RydWN0b3IuY2FsbCh0aGlzLCBydW5TZXJ2aWNlLCB0aGlzLmNyZWF0ZUlmLCBvcHRpb25zKTtcbiAgICB9LFxuXG4gICAgY3JlYXRlSWY6IGZ1bmN0aW9uIChydW4sIGhlYWRlcnMpIHtcbiAgICAgICAgcmV0dXJuIGhlYWRlcnMuZ2V0UmVzcG9uc2VIZWFkZXIoJ3ByYWdtYScpID09PSAncGVyc2lzdGVudCcgfHwgcnVuLmluaXRpYWxpemVkO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFN0cmF0ZWd5O1xuIiwiLyoqXG4gKiBUaGUgYG5ldy1pZi1taXNzaW5nYCBzdHJhdGVneSBjcmVhdGVzIGEgbmV3IHJ1biB3aGVuIHRoZSBjdXJyZW50IG9uZSBpcyBub3QgaW4gdGhlIGJyb3dzZXIgY29va2llLlxuICogXG4gKiBVc2luZyB0aGlzIHN0cmF0ZWd5IG1lYW5zIHRoYXQgd2hlbiBlbmQgdXNlcnMgbmF2aWdhdGUgYmV0d2VlbiBwYWdlcyBpbiB5b3VyIHByb2plY3QsIG9yIHJlZnJlc2ggdGhlaXIgYnJvd3NlcnMsIHRoZXkgd2lsbCBzdGlsbCBiZSB3b3JraW5nIHdpdGggdGhlIHNhbWUgcnVuLlxuICpcbiAqIFRoaXMgc3RyYXRlZ3kgaXMgdXNlZnVsIGlmIHlvdXIgcHJvamVjdCBpcyBzdHJ1Y3R1cmVkIHN1Y2ggdGhhdCBpbW1lZGlhdGVseSBhZnRlciBhIHJ1biBpcyBjcmVhdGVkLCB0aGUgbW9kZWwgaXMgZXhlY3V0ZWQgY29tcGxldGVseSAoZm9yIGV4YW1wbGUsIGEgVmVuc2ltIG1vZGVsIHRoYXQgaXMgc3RlcHBlZCB0byB0aGUgZW5kIGFzIHNvb24gYXMgaXQgaXMgY3JlYXRlZCkuIEluIG90aGVyIHdvcmRzLCB5b3UgY2FyZSB3aGV0aGVyIHlvdSBoYXZlIGEgcnVuLCBidXQgYXMgbG9uZyBhcyB5b3UgaGF2ZSBvbmUsIHlvdSBhcmUgY2VydGFpbiB0aGF0IHRoaXMgcnVuIGlzIHRoZSBvbmUgeW91IGFyZSBpbnRlcmVzdGVkIGluLiBcbiAqIFxuICogU3BlY2lmaWNhbGx5LCB0aGUgc3RyYXRlZ3kgaXM6XG4gKlxuICogKiBDaGVjayB0aGUgYHNlc3Npb25LZXlgIGNvb2tpZS5cbiAqICAgICAqIFRoaXMgY29va2llIGlzIHNldCBieSB0aGUgW1J1biBNYW5hZ2VyXSguLi9ydW4tbWFuYWdlci8pIGFuZCBjb25maWd1cmFibGUgdGhyb3VnaCBpdHMgb3B0aW9ucy4gXG4gKiAgICAgKiBJZiB0aGUgY29va2llIGV4aXN0cywgdXNlIHRoZSBydW4gaWQgc3RvcmVkIHRoZXJlLiBcbiAqICAgICAqIElmIHRoZSBjb29raWUgZG9lcyBub3QgZXhpc3QsIGNyZWF0ZSBhIG5ldyBydW4gZm9yIHRoaXMgZW5kIHVzZXIuIFxuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIGNsYXNzRnJvbSA9IHJlcXVpcmUoJy4uLy4uL3V0aWwvaW5oZXJpdCcpO1xudmFyIENvbmRpdGlvbmFsU3RyYXRlZ3kgPSByZXF1aXJlKCcuL2NvbmRpdGlvbmFsLWNyZWF0aW9uLXN0cmF0ZWd5Jyk7XG5cbnZhciBfX3N1cGVyID0gQ29uZGl0aW9uYWxTdHJhdGVneS5wcm90b3R5cGU7XG5cbi8qXG4qICBjcmVhdGUgYSBuZXcgcnVuIG9ubHkgaWYgbm90aGluZyBpcyBzdG9yZWQgaW4gdGhlIGNvb2tpZVxuKiAgdGhpcyBpcyB1c2VmdWwgZm9yIGJhc2VSdW5zLlxuKi9cbnZhciBTdHJhdGVneSA9IGNsYXNzRnJvbShDb25kaXRpb25hbFN0cmF0ZWd5LCB7XG4gICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uIChydW5TZXJ2aWNlLCBvcHRpb25zKSB7XG4gICAgICAgIF9fc3VwZXIuY29uc3RydWN0b3IuY2FsbCh0aGlzLCBydW5TZXJ2aWNlLCB0aGlzLmNyZWF0ZUlmLCBvcHRpb25zKTtcbiAgICB9LFxuXG4gICAgY3JlYXRlSWY6IGZ1bmN0aW9uIChydW4sIGhlYWRlcnMpIHtcbiAgICAgICAgLy8gaWYgd2UgYXJlIGhlcmUsIGl0IG1lYW5zIHRoYXQgdGhlIHJ1biBleGlzdHMuLi4gc28gd2UgZG9uJ3QgbmVlZCBhIG5ldyBvbmVcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IFN0cmF0ZWd5O1xuIiwiLyoqXG4gKiBUaGUgYG5ldy1pZi1wZXJzaXN0ZWRgIHN0cmF0ZWd5IGNyZWF0ZXMgYSBuZXcgcnVuIHdoZW4gdGhlIGN1cnJlbnQgb25lIGJlY29tZXMgcGVyc2lzdGVkIChlbmQgdXNlciBpcyBpZGxlIGZvciBhIHNldCBwZXJpb2QpLCBidXQgb3RoZXJ3aXNlIHVzZXMgdGhlIGN1cnJlbnQgb25lLiBcbiAqIFxuICogVXNpbmcgdGhpcyBzdHJhdGVneSBtZWFucyB0aGF0IHdoZW4gZW5kIHVzZXJzIG5hdmlnYXRlIGJldHdlZW4gcGFnZXMgaW4geW91ciBwcm9qZWN0LCBvciByZWZyZXNoIHRoZWlyIGJyb3dzZXJzLCB0aGV5IHdpbGwgc3RpbGwgYmUgd29ya2luZyB3aXRoIHRoZSBzYW1lIHJ1bi4gXG4gKiBcbiAqIEhvd2V2ZXIsIGlmIHRoZXkgYXJlIGlkbGUgZm9yIGxvbmdlciB0aGFuIHlvdXIgcHJvamVjdCdzICoqTW9kZWwgU2Vzc2lvbiBUaW1lb3V0KiogKGNvbmZpZ3VyZWQgaW4geW91ciBwcm9qZWN0J3MgW1NldHRpbmdzXSguLi8uLi8uLi91cGRhdGluZ195b3VyX3NldHRpbmdzLykpLCB0aGVuIHRoZWlyIHJ1biBpcyBwZXJzaXN0ZWQ7IHRoZSBuZXh0IHRpbWUgdGhleSBpbnRlcmFjdCB3aXRoIHRoZSBwcm9qZWN0LCB0aGV5IHdpbGwgZ2V0IGEgbmV3IHJ1bi4gKFNlZSBtb3JlIGJhY2tncm91bmQgb24gW1J1biBQZXJzaXN0ZW5jZV0oLi4vLi4vLi4vcnVuX3BlcnNpc3RlbmNlLykuKVxuICogXG4gKiBUaGlzIHN0cmF0ZWd5IGlzIHVzZWZ1bCBmb3IgbXVsdGktcGFnZSBwcm9qZWN0cyB3aGVyZSBlbmQgdXNlcnMgcGxheSB0aHJvdWdoIGEgc2ltdWxhdGlvbiBpbiBvbmUgc2l0dGluZywgc3RlcHBpbmcgdGhyb3VnaCB0aGUgbW9kZWwgc2VxdWVudGlhbGx5IChmb3IgZXhhbXBsZSwgYSBWZW5zaW0gbW9kZWwgdGhhdCB1c2VzIHRoZSBgc3RlcGAgb3BlcmF0aW9uKSBvciBjYWxsaW5nIHNwZWNpZmljIGZ1bmN0aW9ucyB1bnRpbCB0aGUgbW9kZWwgaXMgXCJjb21wbGV0ZS5cIiBIb3dldmVyLCB5b3Ugd2lsbCBuZWVkICB0byBndWFyYW50ZWUgdGhhdCB5b3VyIGVuZCB1c2VycyB3aWxsIHJlbWFpbiBlbmdhZ2VkIHdpdGggdGhlIHByb2plY3QgZnJvbSBiZWdpbm5pbmcgdG8gZW5kICZtZGFzaDsgb3IgYXQgbGVhc3QsIGlmIHRoZXkgYXJlIGlkbGUgZm9yIGxvbmdlciB0aGFuIHRoZSAqKk1vZGVsIFNlc3Npb24gVGltZW91dCoqLCB0aGF0IGl0IGlzIG9rYXkgZm9yIHRoZW0gdG8gc3RhcnQgdGhlIHByb2plY3QgZnJvbSBzY3JhdGNoICh3aXRoIGFuIHVuaW5pdGlhbGl6ZWQgbW9kZWwpLiBcbiAqIFxuICogU3BlY2lmaWNhbGx5LCB0aGUgc3RyYXRlZ3kgaXM6XG4gKlxuICogKiBDaGVjayB0aGUgYHNlc3Npb25LZXlgIGNvb2tpZS5cbiAqICAgKiBUaGlzIGNvb2tpZSBpcyBzZXQgYnkgdGhlIFtSdW4gTWFuYWdlcl0oLi4vcnVuLW1hbmFnZXIvKSBhbmQgY29uZmlndXJhYmxlIHRocm91Z2ggaXRzIG9wdGlvbnMuXG4gKiAgICogSWYgdGhlIGNvb2tpZSBleGlzdHMsIGNoZWNrIHdoZXRoZXIgdGhlIHJ1biBpcyBpbiBtZW1vcnkgb3Igb25seSBwZXJzaXN0ZWQgaW4gdGhlIGRhdGFiYXNlLiBcbiAqICAgICAgKiBJZiB0aGUgcnVuIGlzIGluIG1lbW9yeSwgdXNlIHRoZSBydW4uXG4gKiAgICAgICogSWYgdGhlIHJ1biBpcyBvbmx5IHBlcnNpc3RlZCAoYW5kIG5vdCBzdGlsbCBpbiBtZW1vcnkpLCBjcmVhdGUgYSBuZXcgcnVuIGZvciB0aGlzIGVuZCB1c2VyLlxuICogICAgICAqIElmIHRoZSBjb29raWUgZG9lcyBub3QgZXhpc3QsIGNyZWF0ZSBhIG5ldyBydW4gZm9yIHRoaXMgZW5kIHVzZXIuXG4gKi9cblxuJ3VzZSBzdHJpY3QnO1xudmFyIGNsYXNzRnJvbSA9IHJlcXVpcmUoJy4uLy4uL3V0aWwvaW5oZXJpdCcpO1xudmFyIENvbmRpdGlvbmFsU3RyYXRlZ3kgPSByZXF1aXJlKCcuL2NvbmRpdGlvbmFsLWNyZWF0aW9uLXN0cmF0ZWd5Jyk7XG5cbnZhciBfX3N1cGVyID0gQ29uZGl0aW9uYWxTdHJhdGVneS5wcm90b3R5cGU7XG5cbnZhciBTdHJhdGVneSA9IGNsYXNzRnJvbShDb25kaXRpb25hbFN0cmF0ZWd5LCB7XG4gICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uIChydW5TZXJ2aWNlLCBvcHRpb25zKSB7XG4gICAgICAgIF9fc3VwZXIuY29uc3RydWN0b3IuY2FsbCh0aGlzLCBydW5TZXJ2aWNlLCB0aGlzLmNyZWF0ZUlmLCBvcHRpb25zKTtcbiAgICB9LFxuXG4gICAgY3JlYXRlSWY6IGZ1bmN0aW9uIChydW4sIGhlYWRlcnMpIHtcbiAgICAgICAgcmV0dXJuIGhlYWRlcnMuZ2V0UmVzcG9uc2VIZWFkZXIoJ3ByYWdtYScpID09PSAncGVyc2lzdGVudCc7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzID0gU3RyYXRlZ3k7XG4iLCIvKipcbiAqIFRoZSBgbm9uZWAgc3RyYXRlZ3kgbmV2ZXIgcmV0dXJucyBhIHJ1biBvciB0cmllcyB0byBjcmVhdGUgYSBuZXcgcnVuLiBJdCBzaW1wbHkgcmV0dXJucyB0aGUgY29udGVudHMgb2YgdGhlIGN1cnJlbnQgW1J1biBTZXJ2aWNlIGluc3RhbmNlXSguLi9ydW4tYXBpLXNlcnZpY2UvKS5cbiAqIFxuICogVGhpcyBzdHJhdGVneSBpcyB1c2VmdWwgaWYgeW91IHdhbnQgdG8gbWFudWFsbHkgZGVjaWRlIGhvdyB0byBjcmVhdGUgeW91ciBvd24gcnVucyBhbmQgZG9uJ3Qgd2FudCBhbnkgYXV0b21hdGljIGFzc2lzdGFuY2UuIFxuICogXG4gKiBBbHNvLCB0aGlzIHN0cmF0ZWd5IGlzIG5lY2Vzc2FyeSBpZiB5b3UgYXJlIHdvcmtpbmcgd2l0aCBhIG11bHRpcGxheWVyIHByb2plY3QgYW5kIHVzaW5nIHRoZSBbV29ybGQgTWFuYWdlcl0oLi4vd29ybGQtbWFuYWdlci8pICZtZGFzaDsgb3Igb3RoZXIsIHNpbWlsYXIgc2l0dWF0aW9ucyB3aGVyZSB5b3UgZG8gbm90IGhhdmUgZGlyZWN0IGNvbnRyb2wgb3ZlciBjcmVhdGluZyB0aGUgW1J1biBTZXJ2aWNlXSguLi9ydW4tYXBpLXNlcnZpY2UvKSBpbnN0YW5jZS5cbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBjbGFzc0Zyb20gPSByZXF1aXJlKCcuLi8uLi91dGlsL2luaGVyaXQnKTtcbnZhciBCYXNlID0ge307XG5cbi8vIEludGVyZmFjZSB0aGF0IGFsbCBzdHJhdGVnaWVzIG5lZWQgdG8gaW1wbGVtZW50XG5tb2R1bGUuZXhwb3J0cyA9IGNsYXNzRnJvbShCYXNlLCB7XG4gICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uIChydW5TZXJ2aWNlLCBvcHRpb25zKSB7XG4gICAgICAgIHRoaXMucnVuU2VydmljZSA9IHJ1blNlcnZpY2U7XG4gICAgfSxcblxuICAgIHJlc2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIHJldHVybiBhIG5ld2x5IGNyZWF0ZWQgcnVuXG4gICAgICAgIHJldHVybiAkLkRlZmVycmVkKCkucmVzb2x2ZSgpLnByb21pc2UoKTtcbiAgICB9LFxuXG4gICAgZ2V0UnVuOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIHJldHVybiBhIHVzYWJsZSBydW5cbiAgICAgICAgcmV0dXJuICQuRGVmZXJyZWQoKS5yZXNvbHZlKHRoaXMucnVuU2VydmljZSkucHJvbWlzZSgpO1xuICAgIH1cbn0pO1xuIiwiLyoqXG4gKiBUaGUgYHBlcnNpc3RlbnQtc2luZ2xlLXBsYXllcmAgc3RyYXRlZ3kgcmV0dXJucyB0aGUgbGF0ZXN0IChtb3N0IHJlY2VudCkgcnVuIGZvciB0aGlzIHVzZXIsIHdoZXRoZXIgaXQgaXMgaW4gbWVtb3J5IG9yIG5vdC4gSWYgdGhlcmUgYXJlIG5vIHJ1bnMgZm9yIHRoaXMgdXNlciwgaXQgY3JlYXRlcyBhIG5ldyBvbmUuXG4gKlxuICogVGhpcyBzdHJhdGVneSBpcyB1c2VmdWwgaWYgeW91ciBwcm9qZWN0IGV4ZWN1dGVzIHlvdXIgbW9kZWwgc3RlcCBieSBzdGVwIChhcyBvcHBvc2VkIHRvIGEgcHJvamVjdCB3aGVyZSB0aGUgbW9kZWwgaXMgZXhlY3V0ZWQgY29tcGxldGVseSwgZm9yIGV4YW1wbGUsIGEgVmVuc2ltIG1vZGVsIHRoYXQgaXMgaW1tZWRpYXRlbHkgc3RlcHBlZCB0byB0aGUgZW5kKS4gSXQgaXMgdXNlZnVsIGlmIGVuZCB1c2VycyBwbGF5IHdpdGggeW91ciBwcm9qZWN0IGZvciBhbiBleHRlbmRlZCBwZXJpb2Qgb2YgdGltZSwgcG9zc2libHkgb3ZlciBzZXZlcmFsIHNlc3Npb25zLlxuICpcbiAqIFNwZWNpZmljYWxseSwgdGhlIHN0cmF0ZWd5IGlzOlxuICogXG4gKiAqIENoZWNrIGlmIHRoZXJlIGFyZSBhbnkgcnVucyBmb3IgdGhpcyBlbmQgdXNlci5cbiAqICAgICAqIElmIHRoZXJlIGFyZSBubyBydW5zIChlaXRoZXIgaW4gbWVtb3J5IG9yIGluIHRoZSBkYXRhYmFzZSksIGNyZWF0ZSBhIG5ldyBvbmUuXG4gKiAgICAgKiBJZiB0aGVyZSBhcmUgcnVucywgdGFrZSB0aGUgbGF0ZXN0IChtb3N0IHJlY2VudCkgb25lLlxuICogICAgICAgICAqIElmIHRoZSBtb3N0IHJlY2VudCBydW4gaXMgY3VycmVudGx5IGluIHRoZSBkYXRhYmFzZSwgYnJpbmcgaXQgYmFjayBpbnRvIG1lbW9yeSBzbyB0aGF0IHRoZSBlbmQgdXNlciBjYW4gY29udGludWUgd29ya2luZyB3aXRoIGl0LiAoU2VlIG1vcmUgYmFja2dyb3VuZCBvbiBbUnVuIFBlcnNpc3RlbmNlXSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvKSwgb3IgcmVhZCBtb3JlIG9uIHRoZSB1bmRlcmx5aW5nIFtTdGF0ZSBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9vdGhlcl9hcGlzL21vZGVsX2FwaXMvc3RhdGUvKSBmb3IgYnJpbmdpbmcgcnVucyBmcm9tIHRoZSBkYXRhYmFzZSBiYWNrIGludG8gbWVtb3J5LikgXG4gKi9cblxuJ3VzZSBzdHJpY3QnO1xuXG52YXIgY2xhc3NGcm9tID0gcmVxdWlyZSgnLi4vLi4vdXRpbC9pbmhlcml0Jyk7XG52YXIgSWRlbnRpdHlTdHJhdGVneSA9IHJlcXVpcmUoJy4vbm9uZS1zdHJhdGVneScpO1xudmFyIFN0b3JhZ2VGYWN0b3J5ID0gcmVxdWlyZSgnLi4vLi4vc3RvcmUvc3RvcmUtZmFjdG9yeScpO1xudmFyIFN0YXRlQXBpID0gcmVxdWlyZSgnLi4vLi4vc2VydmljZS9zdGF0ZS1hcGktYWRhcHRlcicpO1xudmFyIEF1dGhNYW5hZ2VyID0gcmVxdWlyZSgnLi4vYXV0aC1tYW5hZ2VyJyk7XG5cbnZhciBrZXlOYW1lcyA9IHJlcXVpcmUoJy4uL2tleS1uYW1lcycpO1xuXG52YXIgZGVmYXVsdHMgPSB7XG4gICAgc3RvcmU6IHtcbiAgICAgICAgc3luY2hyb25vdXM6IHRydWVcbiAgICB9XG59O1xuXG52YXIgU3RyYXRlZ3kgPSBjbGFzc0Zyb20oSWRlbnRpdHlTdHJhdGVneSwge1xuICAgIGNvbnN0cnVjdG9yOiBmdW5jdGlvbiBTdHJhdGVneShydW5TZXJ2aWNlLCBvcHRpb25zKSB7XG4gICAgICAgIHRoaXMucnVuID0gcnVuU2VydmljZTtcbiAgICAgICAgdGhpcy5vcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIGRlZmF1bHRzLCBvcHRpb25zKTtcbiAgICAgICAgdGhpcy5ydW5PcHRpb25zID0gdGhpcy5vcHRpb25zLnJ1bjtcbiAgICAgICAgdGhpcy5fc3RvcmUgPSBuZXcgU3RvcmFnZUZhY3RvcnkodGhpcy5vcHRpb25zLnN0b3JlKTtcbiAgICAgICAgdGhpcy5zdGF0ZUFwaSA9IG5ldyBTdGF0ZUFwaSgpO1xuICAgICAgICB0aGlzLl9hdXRoID0gbmV3IEF1dGhNYW5hZ2VyKCk7XG5cbiAgICAgICAgdGhpcy5fbG9hZEFuZENoZWNrID0gdGhpcy5fbG9hZEFuZENoZWNrLmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMuX3Jlc3RvcmVSdW4gPSB0aGlzLl9yZXN0b3JlUnVuLmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMuX2dldEFsbFJ1bnMgPSB0aGlzLl9nZXRBbGxSdW5zLmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMuX2xvYWRSdW4gPSB0aGlzLl9sb2FkUnVuLmJpbmQodGhpcyk7XG4gICAgfSxcblxuICAgIHJlc2V0OiBmdW5jdGlvbiAocnVuU2VydmljZU9wdGlvbnMpIHtcbiAgICAgICAgdmFyIHNlc3Npb24gPSB0aGlzLl9hdXRoLmdldEN1cnJlbnRVc2VyU2Vzc2lvbkluZm8oKTtcbiAgICAgICAgdmFyIG9wdCA9ICQuZXh0ZW5kKHtcbiAgICAgICAgICAgIHNjb3BlOiB7IGdyb3VwOiBzZXNzaW9uLmdyb3VwTmFtZSB9XG4gICAgICAgIH0sIHRoaXMucnVuT3B0aW9ucyk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMucnVuXG4gICAgICAgICAgICAuY3JlYXRlKG9wdCwgcnVuU2VydmljZU9wdGlvbnMpXG4gICAgICAgICAgICAudGhlbihmdW5jdGlvbiAocnVuKSB7XG4gICAgICAgICAgICAgICAgcnVuLmZyZXNobHlDcmVhdGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICByZXR1cm4gcnVuO1xuICAgICAgICAgICAgfSk7XG4gICAgfSxcblxuICAgIGdldFJ1bjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fZ2V0QWxsUnVucygpXG4gICAgICAgICAgICAudGhlbih0aGlzLl9sb2FkQW5kQ2hlY2spO1xuICAgIH0sXG5cbiAgICBfZ2V0QWxsUnVuczogZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgc2Vzc2lvbiA9IEpTT04ucGFyc2UodGhpcy5fc3RvcmUuZ2V0KGtleU5hbWVzLkVQSV9TRVNTSU9OX0tFWSkgfHwgJ3t9Jyk7XG4gICAgICAgIHJldHVybiB0aGlzLnJ1bi5xdWVyeSh7XG4gICAgICAgICAgICAndXNlci5pZCc6IHNlc3Npb24udXNlcklkIHx8ICcwMDAwJyxcbiAgICAgICAgICAgICdzY29wZS5ncm91cCc6IHNlc3Npb24uZ3JvdXBOYW1lXG4gICAgICAgIH0pO1xuICAgIH0sXG5cbiAgICBfbG9hZEFuZENoZWNrOiBmdW5jdGlvbiAocnVucykge1xuICAgICAgICBpZiAoIXJ1bnMgfHwgIXJ1bnMubGVuZ3RoKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5yZXNldCgpO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGRhdGVDb21wID0gZnVuY3Rpb24gKGEsIGIpIHsgcmV0dXJuIG5ldyBEYXRlKGIuZGF0ZSkgLSBuZXcgRGF0ZShhLmRhdGUpOyB9O1xuICAgICAgICB2YXIgbGF0ZXN0UnVuID0gcnVucy5zb3J0KGRhdGVDb21wKVswXTtcbiAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgdmFyIHNob3VsZFJlcGxheSA9IGZhbHNlO1xuXG4gICAgICAgIHJldHVybiB0aGlzLnJ1bi5sb2FkKGxhdGVzdFJ1bi5pZCwgbnVsbCwge1xuICAgICAgICAgICAgc3VjY2VzczogZnVuY3Rpb24gKHJ1biwgbXNnLCBoZWFkZXJzKSB7XG4gICAgICAgICAgICAgICAgc2hvdWxkUmVwbGF5ID0gaGVhZGVycy5nZXRSZXNwb25zZUhlYWRlcigncHJhZ21hJykgPT09ICdwZXJzaXN0ZW50JztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSkudGhlbihmdW5jdGlvbiAocnVuKSB7XG4gICAgICAgICAgICByZXR1cm4gc2hvdWxkUmVwbGF5ID8gbWUuX3Jlc3RvcmVSdW4ocnVuLmlkKSA6IHJ1bjtcbiAgICAgICAgfSk7XG4gICAgfSxcblxuICAgIF9yZXN0b3JlUnVuOiBmdW5jdGlvbiAocnVuSWQpIHtcbiAgICAgICAgdmFyIG1lID0gdGhpcztcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGVBcGkucmVwbGF5KHsgcnVuSWQ6IHJ1bklkIH0pXG4gICAgICAgICAgICAudGhlbihmdW5jdGlvbiAocmVzcCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBtZS5fbG9hZFJ1bihyZXNwLnJ1bik7XG4gICAgICAgICAgICB9KTtcbiAgICB9LFxuXG4gICAgX2xvYWRSdW46IGZ1bmN0aW9uIChpZCwgb3B0aW9ucykge1xuICAgICAgICByZXR1cm4gdGhpcy5ydW4ubG9hZChpZCwgbnVsbCwgb3B0aW9ucyk7XG4gICAgfVxuXG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBTdHJhdGVneTtcbiIsIm1vZHVsZS5leHBvcnRzID0ge1xuICAgICduZXctaWYtaW5pdGlhbGl6ZWQnOiByZXF1aXJlKCcuL25ldy1pZi1pbml0aWFsaXplZC1zdHJhdGVneScpLFxuICAgICduZXctaWYtcGVyc2lzdGVkJzogcmVxdWlyZSgnLi9uZXctaWYtcGVyc2lzdGVkLXN0cmF0ZWd5JyksXG4gICAgJ25ldy1pZi1taXNzaW5nJzogcmVxdWlyZSgnLi9uZXctaWYtbWlzc2luZy1zdHJhdGVneScpLFxuICAgICdhbHdheXMtbmV3JzogcmVxdWlyZSgnLi9hbHdheXMtbmV3LXN0cmF0ZWd5JyksXG4gICAgbXVsdGlwbGF5ZXI6IHJlcXVpcmUoJy4vbXVsdGlwbGF5ZXItc3RyYXRlZ3knKSxcbiAgICAncGVyc2lzdGVudC1zaW5nbGUtcGxheWVyJzogcmVxdWlyZSgnLi9wZXJzaXN0ZW50LXNpbmdsZS1wbGF5ZXItc3RyYXRlZ3knKSxcbiAgICBub25lOiByZXF1aXJlKCcuL25vbmUtc3RyYXRlZ3knKVxufTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciBSdW5TZXJ2aWNlID0gcmVxdWlyZSgnLi4vc2VydmljZS9ydW4tYXBpLXNlcnZpY2UnKTtcblxudmFyIGRlZmF1bHRzID0ge1xuICAgIHZhbGlkRmlsdGVyOiB7IHNhdmVkOiB0cnVlIH1cbn07XG5cbmZ1bmN0aW9uIFNjZW5hcmlvTWFuYWdlcihvcHRpb25zKSB7XG4gICAgdGhpcy5vcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIGRlZmF1bHRzLCBvcHRpb25zKTtcbiAgICB0aGlzLnJ1blNlcnZpY2UgPSB0aGlzLm9wdGlvbnMucnVuIHx8IG5ldyBSdW5TZXJ2aWNlKHRoaXMub3B0aW9ucyk7XG59XG5cblNjZW5hcmlvTWFuYWdlci5wcm90b3R5cGUgPSB7XG4gICAgZ2V0UnVuczogZnVuY3Rpb24gKGZpbHRlcikge1xuICAgICAgICB0aGlzLmZpbHRlciA9ICQuZXh0ZW5kKHRydWUsIHt9LCB0aGlzLm9wdGlvbnMudmFsaWRGaWx0ZXIsIGZpbHRlcik7XG4gICAgICAgIHJldHVybiB0aGlzLnJ1blNlcnZpY2UucXVlcnkodGhpcy5maWx0ZXIpO1xuICAgIH0sXG5cbiAgICBsb2FkVmFyaWFibGVzOiBmdW5jdGlvbiAodmFycykge1xuICAgICAgICByZXR1cm4gdGhpcy5ydW5TZXJ2aWNlLnF1ZXJ5KHRoaXMuZmlsdGVyLCB7IGluY2x1ZGU6IHZhcnMgfSk7XG4gICAgfSxcblxuICAgIHNhdmU6IGZ1bmN0aW9uIChydW4sIG1ldGEpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2dldFNlcnZpY2UocnVuKS5zYXZlKCQuZXh0ZW5kKHRydWUsIHt9LCB7IHNhdmVkOiB0cnVlIH0sIG1ldGEpKTtcbiAgICB9LFxuXG4gICAgYXJjaGl2ZTogZnVuY3Rpb24gKHJ1bikge1xuICAgICAgICByZXR1cm4gdGhpcy5fZ2V0U2VydmljZShydW4pLnNhdmUoeyBzYXZlZDogZmFsc2UgfSk7XG4gICAgfSxcblxuICAgIF9nZXRTZXJ2aWNlOiBmdW5jdGlvbiAocnVuKSB7XG4gICAgICAgIGlmICh0eXBlb2YgcnVuID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgcmV0dXJuIG5ldyBSdW5TZXJ2aWNlKCQuZXh0ZW5kKHRydWUsIHt9LCB0aGlzLm9wdGlvbnMsIHsgZmlsdGVyOiBydW4gfSkpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHR5cGVvZiBydW4gPT09ICdvYmplY3QnICYmIHJ1biBpbnN0YW5jZW9mIFJ1blNlcnZpY2UpIHtcbiAgICAgICAgICAgIHJldHVybiBydW47XG4gICAgICAgIH1cblxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NhdmUgbWV0aG9kIHJlcXVpcmVzIGEgcnVuIHNlcnZpY2Ugb3IgYSBydW5JZCcpO1xuICAgIH0sXG5cbiAgICBnZXRSdW46IGZ1bmN0aW9uIChydW5JZCkge1xuICAgICAgICByZXR1cm4gbmV3IFJ1blNlcnZpY2UoJC5leHRlbmQodHJ1ZSwge30sIHRoaXMub3B0aW9ucywgeyBmaWx0ZXI6IHJ1bklkIH0pKTtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNjZW5hcmlvTWFuYWdlcjtcblxuIiwiJ3VzZSBzdHJpY3QnO1xuXG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIHJlc2V0OiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zLCBtYW5hZ2VyKSB7XG4gICAgICAgIHJldHVybiBtYW5hZ2VyLnJlc2V0KG9wdGlvbnMpO1xuICAgIH1cbn07XG4iLCIvKipcbiogIyMgV29ybGQgTWFuYWdlclxuKlxuKiBBcyBkaXNjdXNzZWQgdW5kZXIgdGhlIFtXb3JsZCBBUEkgQWRhcHRlcl0oLi4vd29ybGQtYXBpLWFkYXB0ZXIvKSwgYSBbcnVuXSguLi8uLi8uLi9nbG9zc2FyeS8jcnVuKSBpcyBhIGNvbGxlY3Rpb24gb2YgZW5kIHVzZXIgaW50ZXJhY3Rpb25zIHdpdGggYSBwcm9qZWN0IGFuZCBpdHMgbW9kZWwuIEZvciBidWlsZGluZyBtdWx0aXBsYXllciBzaW11bGF0aW9ucyB5b3UgdHlwaWNhbGx5IHdhbnQgbXVsdGlwbGUgZW5kIHVzZXJzIHRvIHNoYXJlIHRoZSBzYW1lIHNldCBvZiBpbnRlcmFjdGlvbnMsIGFuZCB3b3JrIHdpdGhpbiBhIGNvbW1vbiBzdGF0ZS4gRXBpY2VudGVyIGFsbG93cyB5b3UgdG8gY3JlYXRlIFwid29ybGRzXCIgdG8gaGFuZGxlIHN1Y2ggY2FzZXMuXG4qXG4qIFRoZSBXb3JsZCBNYW5hZ2VyIHByb3ZpZGVzIGFuIGVhc3kgd2F5IHRvIHRyYWNrIGFuZCBhY2Nlc3MgdGhlIGN1cnJlbnQgd29ybGQgYW5kIHJ1biBmb3IgcGFydGljdWxhciBlbmQgdXNlcnMuIEl0IGlzIHR5cGljYWxseSB1c2VkIGluIHBhZ2VzIHRoYXQgZW5kIHVzZXJzIHdpbGwgaW50ZXJhY3Qgd2l0aC4gKFRoZSByZWxhdGVkIFtXb3JsZCBBUEkgQWRhcHRlcl0oLi4vd29ybGQtYXBpLWFkYXB0ZXIvKSBoYW5kbGVzIGNyZWF0aW5nIG11bHRpcGxheWVyIHdvcmxkcywgYW5kIGFkZGluZyBhbmQgcmVtb3ZpbmcgZW5kIHVzZXJzIGFuZCBydW5zIGZyb20gYSB3b3JsZC4gQmVjYXVzZSBvZiB0aGlzLCB0eXBpY2FsbHkgdGhlIFdvcmxkIEFkYXB0ZXIgaXMgdXNlZCBmb3IgZmFjaWxpdGF0b3IgcGFnZXMgaW4geW91ciBwcm9qZWN0LilcbipcbiogIyMjIFVzaW5nIHRoZSBXb3JsZCBNYW5hZ2VyXG4qXG4qIFRvIHVzZSB0aGUgV29ybGQgTWFuYWdlciwgaW5zdGFudGlhdGUgaXQuIFRoZW4sIG1ha2UgY2FsbHMgdG8gYW55IG9mIHRoZSBtZXRob2RzIHlvdSBuZWVkLlxuKlxuKiBXaGVuIHlvdSBpbnN0YW50aWF0ZSBhIFdvcmxkIE1hbmFnZXIsIHRoZSB3b3JsZCdzIGFjY291bnQgaWQsIHByb2plY3QgaWQsIGFuZCBncm91cCBhcmUgYXV0b21hdGljYWxseSB0YWtlbiBmcm9tIHRoZSBzZXNzaW9uICh0aGFua3MgdG8gdGhlIFtBdXRoZW50aWNhdGlvbiBTZXJ2aWNlXSguLi9hdXRoLWFwaS1zZXJ2aWNlKSkuXG4qXG4qIE5vdGUgdGhhdCB0aGUgV29ybGQgTWFuYWdlciBkb2VzICpub3QqIGNyZWF0ZSB3b3JsZHMgYXV0b21hdGljYWxseS4gKFRoaXMgaXMgZGlmZmVyZW50IHRoYW4gdGhlIFtSdW4gTWFuYWdlcl0oLi4vcnVuLW1hbmFnZXIpLikgSG93ZXZlciwgeW91IGNhbiBwYXNzIGluIHNwZWNpZmljIG9wdGlvbnMgdG8gYW55IHJ1bnMgY3JlYXRlZCBieSB0aGUgbWFuYWdlciwgdXNpbmcgYSBgcnVuYCBvYmplY3QuXG4qXG4qIFRoZSBwYXJhbWV0ZXJzIGZvciBjcmVhdGluZyBhIFdvcmxkIE1hbmFnZXIgYXJlOlxuKlxuKiAgICogYGFjY291bnRgOiBUaGUgKipUZWFtIElEKiogaW4gdGhlIEVwaWNlbnRlciB1c2VyIGludGVyZmFjZSBmb3IgdGhpcyBwcm9qZWN0LlxuKiAgICogYHByb2plY3RgOiBUaGUgKipQcm9qZWN0IElEKiogZm9yIHRoaXMgcHJvamVjdC5cbiogICAqIGBncm91cGA6IFRoZSAqKkdyb3VwIE5hbWUqKiBmb3IgdGhpcyB3b3JsZC5cbiogICAqIGBydW5gOiBPcHRpb25zIHRvIHVzZSB3aGVuIGNyZWF0aW5nIG5ldyBydW5zIHdpdGggdGhlIG1hbmFnZXIsIGUuZy4gYHJ1bjogeyBmaWxlczogWydkYXRhLnhscyddIH1gLlxuKiAgICogYHJ1bi5tb2RlbGA6IFRoZSBuYW1lIG9mIHRoZSBwcmltYXJ5IG1vZGVsIGZpbGUgZm9yIHRoaXMgcHJvamVjdC4gUmVxdWlyZWQgaWYgeW91IGhhdmUgbm90IGFscmVhZHkgcGFzc2VkIGl0IGluIGFzIHBhcnQgb2YgdGhlIGBvcHRpb25zYCBwYXJhbWV0ZXIgZm9yIGFuIGVuY2xvc2luZyBjYWxsLlxuKlxuKiBGb3IgZXhhbXBsZTpcbipcbiogICAgICAgdmFyIHdNZ3IgPSBuZXcgRi5tYW5hZ2VyLldvcmxkTWFuYWdlcih7XG4qICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiogICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiogICAgICAgICAgcnVuOiB7IG1vZGVsOiAnc3VwcGx5LWNoYWluLnB5JyB9LFxuKiAgICAgICAgICBncm91cDogJ3RlYW0xJ1xuKiAgICAgICB9KTtcbipcbiogICAgICAgd01nci5nZXRDdXJyZW50UnVuKCk7XG4qL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBXb3JsZEFwaSA9IHJlcXVpcmUoJy4uL3NlcnZpY2Uvd29ybGQtYXBpLWFkYXB0ZXInKTtcbnZhciBSdW5NYW5hZ2VyID0gcmVxdWlyZSgnLi9ydW4tbWFuYWdlcicpO1xudmFyIEF1dGhNYW5hZ2VyID0gcmVxdWlyZSgnLi9hdXRoLW1hbmFnZXInKTtcbnZhciB3b3JsZEFwaTtcblxuZnVuY3Rpb24gYnVpbGRTdHJhdGVneSh3b3JsZElkLCBkdGQpIHtcblxuICAgIHJldHVybiBmdW5jdGlvbiBDdG9yKHJ1blNlcnZpY2UsIG9wdGlvbnMpIHtcbiAgICAgICAgdGhpcy5ydW5TZXJ2aWNlID0gcnVuU2VydmljZTtcbiAgICAgICAgdGhpcy5vcHRpb25zID0gb3B0aW9ucztcblxuICAgICAgICAkLmV4dGVuZCh0aGlzLCB7XG4gICAgICAgICAgICByZXNldDogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignbm90IGltcGxlbWVudGQuIE5lZWQgYXBpIGNoYW5nZXMnKTtcbiAgICAgICAgICAgIH0sXG5cbiAgICAgICAgICAgIGdldFJ1bjogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgICAgICAgICAgLy9nZXQgb3IgY3JlYXRlIVxuICAgICAgICAgICAgICAgIC8vIE1vZGVsIGlzIHJlcXVpcmVkIGluIHRoZSBvcHRpb25zXG4gICAgICAgICAgICAgICAgdmFyIG1vZGVsID0gdGhpcy5vcHRpb25zLnJ1bi5tb2RlbCB8fCB0aGlzLm9wdGlvbnMubW9kZWw7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHdvcmxkQXBpLmdldEN1cnJlbnRSdW5JZCh7IG1vZGVsOiBtb2RlbCwgZmlsdGVyOiB3b3JsZElkIH0pXG4gICAgICAgICAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChydW5JZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1lLnJ1blNlcnZpY2UubG9hZChydW5JZCk7XG4gICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChydW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGR0ZC5yZXNvbHZlV2l0aChtZSwgW3J1bl0pO1xuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAuZmFpbChkdGQucmVqZWN0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICApO1xuICAgIH07XG59XG5cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgIHRoaXMub3B0aW9ucyA9IG9wdGlvbnMgfHwgeyBydW46IHt9LCB3b3JsZDoge30gfTtcblxuICAgICQuZXh0ZW5kKHRydWUsIHRoaXMub3B0aW9ucywgdGhpcy5vcHRpb25zLnJ1bik7XG4gICAgJC5leHRlbmQodHJ1ZSwgdGhpcy5vcHRpb25zLCB0aGlzLm9wdGlvbnMud29ybGQpO1xuXG4gICAgd29ybGRBcGkgPSBuZXcgV29ybGRBcGkodGhpcy5vcHRpb25zKTtcbiAgICB0aGlzLl9hdXRoID0gbmV3IEF1dGhNYW5hZ2VyKCk7XG4gICAgdmFyIG1lID0gdGhpcztcblxuICAgIHZhciBhcGkgPSB7XG5cbiAgICAgICAgLyoqXG4gICAgICAgICogUmV0dXJucyB0aGUgY3VycmVudCB3b3JsZCAob2JqZWN0KSBhbmQgYW4gaW5zdGFuY2Ugb2YgdGhlIFtXb3JsZCBBUEkgQWRhcHRlcl0oLi4vd29ybGQtYXBpLWFkYXB0ZXIvKS5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICB3TWdyLmdldEN1cnJlbnRXb3JsZCgpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKHdvcmxkLCB3b3JsZEFkYXB0ZXIpIHtcbiAgICAgICAgKiAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHdvcmxkLmlkKTtcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdvcmxkQWRhcHRlci5nZXRDdXJyZW50UnVuSWQoKTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB1c2VySWQgKE9wdGlvbmFsKSBUaGUgaWQgb2YgdGhlIHVzZXIgd2hvc2Ugd29ybGQgaXMgYmVpbmcgYWNjZXNzZWQuIERlZmF1bHRzIHRvIHRoZSB1c2VyIGluIHRoZSBjdXJyZW50IHNlc3Npb24uXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGdyb3VwTmFtZSAoT3B0aW9uYWwpIFRoZSBuYW1lIG9mIHRoZSBncm91cCB3aG9zZSB3b3JsZCBpcyBiZWluZyBhY2Nlc3NlZC4gRGVmYXVsdHMgdG8gdGhlIGdyb3VwIGZvciB0aGUgdXNlciBpbiB0aGUgY3VycmVudCBzZXNzaW9uLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldEN1cnJlbnRXb3JsZDogZnVuY3Rpb24gKHVzZXJJZCwgZ3JvdXBOYW1lKSB7XG4gICAgICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuX2F1dGguZ2V0Q3VycmVudFVzZXJTZXNzaW9uSW5mbygpO1xuICAgICAgICAgICAgaWYgKCF1c2VySWQpIHtcbiAgICAgICAgICAgICAgICB1c2VySWQgPSBzZXNzaW9uLnVzZXJJZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICghZ3JvdXBOYW1lKSB7XG4gICAgICAgICAgICAgICAgZ3JvdXBOYW1lID0gc2Vzc2lvbi5ncm91cE5hbWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gd29ybGRBcGkuZ2V0Q3VycmVudFdvcmxkRm9yVXNlcih1c2VySWQsIGdyb3VwTmFtZSk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogUmV0dXJucyB0aGUgY3VycmVudCBydW4gKG9iamVjdCkgYW5kIGFuIGluc3RhbmNlIG9mIHRoZSBbUnVuIEFQSSBTZXJ2aWNlXSguLi9ydW4tYXBpLXNlcnZpY2UvKS5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICB3TWdyLmdldEN1cnJlbnRSdW4oe21vZGVsOiAnbXlNb2RlbC5weSd9KVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbihydW4sIHJ1blNlcnZpY2UpIHtcbiAgICAgICAgKiAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHJ1bi5pZCk7XG4gICAgICAgICogICAgICAgICAgICAgICBydW5TZXJ2aWNlLmRvKCdzdGFydEdhbWUnKTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBtb2RlbCAoT3B0aW9uYWwpIFRoZSBuYW1lIG9mIHRoZSBtb2RlbCBmaWxlLiBSZXF1aXJlZCBpZiBub3QgYWxyZWFkeSBwYXNzZWQgaW4gYXMgYHJ1bi5tb2RlbGAgd2hlbiB0aGUgV29ybGQgTWFuYWdlciBpcyBjcmVhdGVkLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldEN1cnJlbnRSdW46IGZ1bmN0aW9uIChtb2RlbCkge1xuICAgICAgICAgICAgdmFyIGR0ZCA9ICQuRGVmZXJyZWQoKTtcbiAgICAgICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5fYXV0aC5nZXRDdXJyZW50VXNlclNlc3Npb25JbmZvKCk7XG4gICAgICAgICAgICB2YXIgY3VyVXNlcklkID0gc2Vzc2lvbi51c2VySWQ7XG4gICAgICAgICAgICB2YXIgY3VyR3JvdXBOYW1lID0gc2Vzc2lvbi5ncm91cE5hbWU7XG5cbiAgICAgICAgICAgIGZ1bmN0aW9uIGdldEFuZFJlc3RvcmVMYXRlc3RSdW4od29ybGQpIHtcbiAgICAgICAgICAgICAgICBpZiAoIXdvcmxkKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBkdGQucmVqZWN0KHsgZXJyb3I6ICdUaGUgdXNlciBpcyBub3QgcGFydCBvZiBhbnkgd29ybGQhJyB9KTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB2YXIgY3VycmVudFdvcmxkSWQgPSB3b3JsZC5pZDtcbiAgICAgICAgICAgICAgICB2YXIgcnVuT3B0cyA9ICQuZXh0ZW5kKHRydWUsIG1lLm9wdGlvbnMsIHsgbW9kZWw6IG1vZGVsIH0pO1xuICAgICAgICAgICAgICAgIHZhciBzdHJhdGVneSA9IGJ1aWxkU3RyYXRlZ3koY3VycmVudFdvcmxkSWQsIGR0ZCk7XG4gICAgICAgICAgICAgICAgdmFyIG9wdCA9ICQuZXh0ZW5kKHRydWUsIHt9LCB7XG4gICAgICAgICAgICAgICAgICAgIHN0cmF0ZWd5OiBzdHJhdGVneSxcbiAgICAgICAgICAgICAgICAgICAgcnVuOiBydW5PcHRzXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgdmFyIHJtID0gbmV3IFJ1bk1hbmFnZXIob3B0KTtcblxuICAgICAgICAgICAgICAgIHJldHVybiBybS5nZXRSdW4oKVxuICAgICAgICAgICAgICAgICAgICAudGhlbihmdW5jdGlvbiAocnVuKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBkdGQucmVzb2x2ZShydW4sIHJtLnJ1blNlcnZpY2UsIHJtKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRoaXMuZ2V0Q3VycmVudFdvcmxkKGN1clVzZXJJZCwgY3VyR3JvdXBOYW1lKVxuICAgICAgICAgICAgICAgIC50aGVuKGdldEFuZFJlc3RvcmVMYXRlc3RSdW4pO1xuXG4gICAgICAgICAgICByZXR1cm4gZHRkLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAkLmV4dGVuZCh0aGlzLCBhcGkpO1xufTtcbiIsIi8qKlxuICogIyMgRmlsZSBBUEkgU2VydmljZVxuICpcbiAqIFRoZSBGaWxlIEFQSSBTZXJ2aWNlIGFsbG93cyB5b3UgdG8gdXBsb2FkIGFuZCBkb3dubG9hZCBmaWxlcyBkaXJlY3RseSBvbnRvIEVwaWNlbnRlciwgYW5hbG9nb3VzIHRvIHVzaW5nIHRoZSBGaWxlIE1hbmFnZXIgVUkgaW4gRXBpY2VudGVyIGRpcmVjdGx5IG9yIFNGVFBpbmcgZmlsZXMgaW4uIEl0IGlzIGJhc2VkIG9uIHRoZSBFcGljZW50ZXIgRmlsZSBBUEkuXG4gKlxuICogVGhlIEFzc2V0IEFQSSBTZXJ2aWNlIChodHRwczovL2ZvcmlvLmNvbS9lcGljZW50ZXIvZG9jcy9wdWJsaWMvYXBpX2FkYXB0ZXJzL2dlbmVyYXRlZC9hc3NldC1hcGktYWRhcHRlci8pIGlzIHR5cGljYWxseSB1c2VkIGZvciBhbGwgcHJvamVjdCB1c2UgY2FzZXMsIGFuZCBpdCdzIHVubGlrZWx5IHRoaXMgRmlsZSBTZXJ2aWNlIHdpbGwgYmUgdXNlZCBkaXJlY3RseSBleGNlcHQgYnkgQWRtaW4gdG9vbHMgKGUuZy4gRmxvdyBJbnNwZWN0b3IpLlxuICpcbiAqIFBhcnRpYWxseSBpbXBsZW1lbnRlZC5cbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBGb3IgcHJvamVjdHMgdGhhdCByZXF1aXJlIGF1dGhlbnRpY2F0aW9uLCBwYXNzIGluIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiAoZGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nKS4gSWYgdGhlIHVzZXIgaXMgYWxyZWFkeSBsb2dnZWQgaW4gdG8gRXBpY2VudGVyLCB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gaXMgYWxyZWFkeSBzZXQgaW4gYSBjb29raWUgYW5kIGF1dG9tYXRpY2FsbHkgbG9hZGVkIGZyb20gdGhlcmUuIChTZWUgW21vcmUgYmFja2dyb3VuZCBvbiBhY2Nlc3MgdG9rZW5zXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pKS5cbiAgICAgICAgICogQHNlZSBbQXV0aGVudGljYXRpb24gQVBJIFNlcnZpY2VdKC4uL2F1dGgtYXBpLXNlcnZpY2UvKSBmb3IgZ2V0dGluZyB0b2tlbnMuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICB0b2tlbjogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgYWNjb3VudCBpZC4gSW4gdGhlIEVwaWNlbnRlciBVSSwgdGhpcyBpcyB0aGUgKipUZWFtIElEKiogKGZvciB0ZWFtIHByb2plY3RzKSBvciAqKlVzZXIgSUQqKiAoZm9yIHBlcnNvbmFsIHByb2plY3RzKS4gRGVmYXVsdHMgdG8gdW5kZWZpbmVkLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgYWNjb3VudDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgcHJvamVjdCBpZC4gRGVmYXVsdHMgdG8gdW5kZWZpbmVkLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgcHJvamVjdDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgZm9sZGVyIHR5cGUuICBPbmUgb2YgYG1vZGVsYCB8IGBzdGF0aWNgIHwgYG5vZGVgLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgZm9sZGVyVHlwZTogJ3N0YXRpYycsXG5cblxuICAgICAgICAvKipcbiAgICAgICAgICogT3B0aW9ucyB0byBwYXNzIG9uIHRvIHRoZSB1bmRlcmx5aW5nIHRyYW5zcG9ydCBsYXllci4gQWxsIGpxdWVyeS5hamF4IG9wdGlvbnMgYXQgaHR0cDovL2FwaS5qcXVlcnkuY29tL2pRdWVyeS5hamF4LyBhcmUgYXZhaWxhYmxlLiBEZWZhdWx0cyB0byBlbXB0eSBvYmplY3QuXG4gICAgICAgICAqIEB0eXBlIHtPYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICB0cmFuc3BvcnQ6IHt9XG4gICAgfTtcblxuICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICB2YXIgc2VydmljZU9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMoZGVmYXVsdHMsIGNvbmZpZyk7XG4gICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5hY2NvdW50KSB7XG4gICAgICAgIHVybENvbmZpZy5hY2NvdW50UGF0aCA9IHNlcnZpY2VPcHRpb25zLmFjY291bnQ7XG4gICAgfVxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5wcm9qZWN0KSB7XG4gICAgICAgIHVybENvbmZpZy5wcm9qZWN0UGF0aCA9IHNlcnZpY2VPcHRpb25zLnByb2plY3Q7XG4gICAgfVxuXG4gICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydCwge1xuICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKCdmaWxlJylcbiAgICB9KTtcblxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICBodHRwT3B0aW9ucy5oZWFkZXJzID0ge1xuICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogJ0JlYXJlciAnICsgc2VydmljZU9wdGlvbnMudG9rZW5cbiAgICAgICAgfTtcbiAgICB9XG4gICAgdmFyIGh0dHAgPSBuZXcgVHJhbnNwb3J0RmFjdG9yeShodHRwT3B0aW9ucyk7XG5cbiAgICBmdW5jdGlvbiB1cGxvYWRCb2R5KGZpbGVOYW1lLCBjb250ZW50cykge1xuICAgICAgICB2YXIgYm91bmRhcnkgPSAnLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tN2RhMjRmMmU1MDA0Nic7XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGJvZHk6ICctLScgKyBib3VuZGFyeSArICdcXHJcXG4nICtcbiAgICAgICAgICAgICAgICAgICAgJ0NvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT1cImZpbGVcIjsnICtcbiAgICAgICAgICAgICAgICAgICAgJ2ZpbGVuYW1lPVwiJyArIGZpbGVOYW1lICsgJ1wiXFxyXFxuJyArXG4gICAgICAgICAgICAgICAgICAgICdDb250ZW50LXR5cGU6IHRleHQvaHRtbFxcclxcblxcclxcbicgK1xuICAgICAgICAgICAgICAgICAgICBjb250ZW50cyArICdcXHJcXG4nICtcbiAgICAgICAgICAgICAgICAgICAgJy0tJyArIGJvdW5kYXJ5ICsgJy0tJyxcbiAgICAgICAgICAgIGJvdW5kYXJ5OiBib3VuZGFyeVxuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHVwbG9hZEZpbGVPcHRpb25zKGZpbGVQYXRoLCBjb250ZW50cywgb3B0aW9ucykge1xuICAgICAgICBmaWxlUGF0aCA9IGZpbGVQYXRoLnNwbGl0KCcvJyk7XG4gICAgICAgIHZhciBmaWxlTmFtZSA9IGZpbGVQYXRoLnBvcCgpO1xuICAgICAgICBmaWxlUGF0aCA9IGZpbGVQYXRoLmpvaW4oJy8nKTtcbiAgICAgICAgdmFyIHBhdGggPSBzZXJ2aWNlT3B0aW9ucy5mb2xkZXJUeXBlICsgJy8nICsgZmlsZVBhdGg7XG4gICAgICAgIHZhciB1cGxvYWQgPSB1cGxvYWRCb2R5KGZpbGVOYW1lLCBjb250ZW50cyk7XG5cbiAgICAgICAgcmV0dXJuICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucywge1xuICAgICAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aCgnZmlsZScpICsgcGF0aCxcbiAgICAgICAgICAgIGRhdGE6IHVwbG9hZC5ib2R5LFxuICAgICAgICAgICAgY29udGVudFR5cGU6ICdtdWx0aXBhcnQvZm9ybS1kYXRhOyBib3VuZGFyeT0nICsgdXBsb2FkLmJvdW5kYXJ5XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHZhciBwdWJsaWNBc3luY0FQSSA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCBhIGRpcmVjdG9yeSBsaXN0aW5nLCBvciBjb250ZW50cyBvZiBhIGZpbGUuXG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSBmaWxlUGF0aCAgUGF0aCB0byB0aGUgZmlsZVxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgZ2V0Q29udGVudHM6IGZ1bmN0aW9uIChmaWxlUGF0aCwgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIHBhdGggPSBzZXJ2aWNlT3B0aW9ucy5mb2xkZXJUeXBlICsgJy8nICsgZmlsZVBhdGg7XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMsIHtcbiAgICAgICAgICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKCdmaWxlJykgKyBwYXRoXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldCgnJywgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZXBsYWNlcyB0aGUgZmlsZSBhdCB0aGUgZ2l2ZW4gZmlsZSBwYXRoLlxuICAgICAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IGZpbGVQYXRoIFBhdGggdG8gdGhlIGZpbGVcbiAgICAgICAgICogQHBhcmFtICB7U3RyaW5nfSBjb250ZW50cyBDb250ZW50cyB0byB3cml0ZSB0byBmaWxlXG4gICAgICAgICAqIEBwYXJhbSAge09iamVjdH0gb3B0aW9ucyAgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgcmVwbGFjZTogZnVuY3Rpb24gKGZpbGVQYXRoLCBjb250ZW50cywgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gdXBsb2FkRmlsZU9wdGlvbnMoZmlsZVBhdGgsIGNvbnRlbnRzLCBvcHRpb25zKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucHV0KGh0dHBPcHRpb25zLmRhdGEsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ3JlYXRlcyBhIGZpbGUgaW4gdGhlIGdpdmVuIGZpbGUgcGF0aC5cbiAgICAgICAgICogQHBhcmFtICB7U3RyaW5nfSBmaWxlUGF0aCBQYXRoIHRvIHRoZSBmaWxlXG4gICAgICAgICAqIEBwYXJhbSAge1N0cmluZ30gY29udGVudHMgQ29udGVudHMgdG8gd3JpdGUgdG8gZmlsZVxuICAgICAgICAgKiBAcGFyYW0gIHtCb29sZWFufSByZXBsYWNlRXhpc3RpbmcgUmVwbGFjZSBmaWxlIGlmIGl0IGFscmVhZHkgZXhpc3RzOyBkZWZhdWx0cyB0byBmYWxzZVxuICAgICAgICAgKiBAcGFyYW0gIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgY3JlYXRlOiBmdW5jdGlvbiAoZmlsZVBhdGgsIGNvbnRlbnRzLCByZXBsYWNlRXhpc3RpbmcsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9IHVwbG9hZEZpbGVPcHRpb25zKGZpbGVQYXRoLCBjb250ZW50cywgb3B0aW9ucyk7XG4gICAgICAgICAgICB2YXIgcHJvbSA9IGh0dHAucG9zdChodHRwT3B0aW9ucy5kYXRhLCBodHRwT3B0aW9ucyk7XG4gICAgICAgICAgICB2YXIgbWUgPSB0aGlzO1xuICAgICAgICAgICAgaWYgKHJlcGxhY2VFeGlzdGluZyA9PT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgIHByb20gPSBwcm9tLnRoZW4obnVsbCwgZnVuY3Rpb24gKHhocikge1xuICAgICAgICAgICAgICAgICAgICB2YXIgY29uZmxpY3RTdGF0dXMgPSA0MDk7XG4gICAgICAgICAgICAgICAgICAgIGlmICh4aHIuc3RhdHVzID09PSBjb25mbGljdFN0YXR1cykge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1lLnJlcGxhY2UoZmlsZVBhdGgsIGNvbnRlbnRzLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHByb207XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJlbW92ZXMgdGhlIGZpbGUuXG4gICAgICAgICAqIEBwYXJhbSAge1N0cmluZ30gZmlsZVBhdGggUGF0aCB0byB0aGUgZmlsZVxuICAgICAgICAgKiBAcGFyYW0gIHtPYmplY3R9IG9wdGlvbnMgIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnNcbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgIHJlbW92ZTogZnVuY3Rpb24gKGZpbGVQYXRoLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcGF0aCA9IHNlcnZpY2VPcHRpb25zLmZvbGRlclR5cGUgKyAnLycgKyBmaWxlUGF0aDtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucywge1xuICAgICAgICAgICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoJ2ZpbGUnKSArIHBhdGhcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZGVsZXRlKG51bGwsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmVuYW1lcyB0aGUgZmlsZS5cbiAgICAgICAgICogQHBhcmFtICB7U3RyaW5nfSBmaWxlUGF0aCBQYXRoIHRvIHRoZSBmaWxlXG4gICAgICAgICAqIEBwYXJhbSAge1N0cmluZ30gbmV3TmFtZSAgTmV3IG5hbWUgb2YgZmlsZVxuICAgICAgICAgKiBAcGFyYW0gIHtPYmplY3R9IG9wdGlvbnMgIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnNcbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgIHJlbmFtZTogZnVuY3Rpb24gKGZpbGVQYXRoLCBuZXdOYW1lLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcGF0aCA9IHNlcnZpY2VPcHRpb25zLmZvbGRlclR5cGUgKyAnLycgKyBmaWxlUGF0aDtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucywge1xuICAgICAgICAgICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoJ2ZpbGUnKSArIHBhdGhcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAucGF0Y2goeyBuYW1lOiBuZXdOYW1lIH0sIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBc3luY0FQSSk7XG59O1xuIiwiLyoqXG4gKiAjIyBBc3NldCBBUEkgQWRhcHRlclxuICpcbiAqIFRoZSBBc3NldCBBUEkgQWRhcHRlciBhbGxvd3MgeW91IHRvIHN0b3JlIGFzc2V0cyAtLSByZXNvdXJjZXMgb3IgZmlsZXMgb2YgYW55IGtpbmQgLS0gdXNlZCBieSBhIHByb2plY3Qgd2l0aCBhIHNjb3BlIHRoYXQgaXMgc3BlY2lmaWMgdG8gcHJvamVjdCwgZ3JvdXAsIG9yIGVuZCB1c2VyLlxuICpcbiAqIEFzc2V0cyBhcmUgdXNlZCB3aXRoIFt0ZWFtIHByb2plY3RzXSguLi8uLi8uLi9wcm9qZWN0X2FkbWluLyN0ZWFtKS4gT25lIGNvbW1vbiB1c2UgY2FzZSBpcyBoYXZpbmcgZW5kIHVzZXJzIGluIGEgW2dyb3VwXSguLi8uLi8uLi9nbG9zc2FyeS8jZ3JvdXBzKSBvciBpbiBhIFttdWx0aXBsYXllciB3b3JsZF0oLi4vLi4vLi4vZ2xvc3NhcnkvI3dvcmxkKSB1cGxvYWQgZGF0YSAtLSB2aWRlb3MgY3JlYXRlZCBkdXJpbmcgZ2FtZSBwbGF5LCBwcm9maWxlIHBpY3R1cmVzIGZvciBjdXN0b21pemluZyB0aGVpciBleHBlcmllbmNlLCBldGMuIC0tIGFzIHBhcnQgb2YgcGxheWluZyB0aHJvdWdoIHRoZSBwcm9qZWN0LlxuICpcbiAqIFJlc291cmNlcyBjcmVhdGVkIHVzaW5nIHRoZSBBc3NldCBBZGFwdGVyIGFyZSBzY29wZWQ6XG4gKlxuICogICogUHJvamVjdCBhc3NldHMgYXJlIHdyaXRhYmxlIG9ubHkgYnkgW3RlYW0gbWVtYmVyc10oLi4vLi4vLi4vZ2xvc3NhcnkvI3RlYW0pLCB0aGF0IGlzLCBFcGljZW50ZXIgYXV0aG9ycy5cbiAqICAqIEdyb3VwIGFzc2V0cyBhcmUgd3JpdGFibGUgYnkgYW55b25lIHdpdGggYWNjZXNzIHRvIHRoZSBwcm9qZWN0IHRoYXQgaXMgcGFydCBvZiB0aGF0IHBhcnRpY3VsYXIgW2dyb3VwXSguLi8uLi8uLi9nbG9zc2FyeS8jZ3JvdXBzKS4gVGhpcyBpbmNsdWRlcyBhbGwgW3RlYW0gbWVtYmVyc10oLi4vLi4vLi4vZ2xvc3NhcnkvI3RlYW0pIChFcGljZW50ZXIgYXV0aG9ycykgYW5kIGFueSBbZW5kIHVzZXJzXSguLi8uLi8uLi9nbG9zc2FyeS8jdXNlcnMpIHdobyBhcmUgbWVtYmVycyBvZiB0aGUgZ3JvdXAgLS0gYm90aCBmYWNpbGl0YXRvcnMgYW5kIHN0YW5kYXJkIGVuZCB1c2Vycy5cbiAqICAqIFVzZXIgYXNzZXRzIGFyZSB3cml0YWJsZSBieSB0aGUgc3BlY2lmaWMgZW5kIHVzZXIsIGFuZCBieSB0aGUgZmFjaWxpdGF0b3Igb2YgdGhlIGdyb3VwLlxuICogICogQWxsIGFzc2V0cyBhcmUgcmVhZGFibGUgYnkgYW55b25lIHdpdGggdGhlIGV4YWN0IFVSSS5cbiAqXG4gKiBUbyB1c2UgdGhlIEFzc2V0IEFkYXB0ZXIsIGluc3RhbnRpYXRlIGl0IGFuZCB0aGVuIGFjY2VzcyB0aGUgbWV0aG9kcyBwcm92aWRlZC4gSW5zdGFudGlhdGluZyByZXF1aXJlcyB0aGUgYWNjb3VudCBpZCAoKipUZWFtIElEKiogaW4gdGhlIEVwaWNlbnRlciB1c2VyIGludGVyZmFjZSkgYW5kIHByb2plY3QgaWQgKCoqUHJvamVjdCBJRCoqKS4gVGhlIGdyb3VwIG5hbWUgaXMgcmVxdWlyZWQgZm9yIGFzc2V0cyB3aXRoIGEgZ3JvdXAgc2NvcGUsIGFuZCB0aGUgZ3JvdXAgbmFtZSBhbmQgdXNlcklkIGFyZSByZXF1aXJlZCBmb3IgYXNzZXRzIHdpdGggYSB1c2VyIHNjb3BlLiBJZiBub3QgaW5jbHVkZWQsIHRoZXkgYXJlIHRha2VuIGZyb20gdGhlIGxvZ2dlZCBpbiB1c2VyJ3Mgc2Vzc2lvbiBpbmZvcm1hdGlvbiBpZiBuZWVkZWQuXG4gKlxuICogV2hlbiBjcmVhdGluZyBhbiBhc3NldCwgeW91IGNhbiBwYXNzIGluIHRleHQgKGVuY29kZWQgZGF0YSkgdG8gdGhlIGBjcmVhdGUoKWAgY2FsbC4gQWx0ZXJuYXRpdmVseSwgeW91IGNhbiBtYWtlIHRoZSBgY3JlYXRlKClgIGNhbGwgYXMgcGFydCBvZiBhbiBIVE1MIGZvcm0gYW5kIHBhc3MgaW4gYSBmaWxlIHVwbG9hZGVkIHZpYSB0aGUgZm9ybS5cbiAqXG4gKiAgICAgICAvLyBpbnN0YW50aWF0ZSB0aGUgQXNzZXQgQWRhcHRlclxuICogICAgICAgdmFyIGFhID0gbmV3IEYuc2VydmljZS5Bc3NldCh7XG4gKiAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gKiAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICogICAgICAgICAgZ3JvdXA6ICd0ZWFtMScsXG4gKiAgICAgICAgICB1c2VySWQ6ICcxMjM0NSdcbiAqICAgICAgIH0pO1xuICpcbiAqICAgICAgIC8vIGNyZWF0ZSBhIG5ldyBhc3NldCB1c2luZyBlbmNvZGVkIHRleHRcbiAqICAgICAgIGFhLmNyZWF0ZSgndGVzdC50eHQnLCB7XG4gKiAgICAgICAgICAgZW5jb2Rpbmc6ICdCQVNFXzY0JyxcbiAqICAgICAgICAgICBkYXRhOiAnVkdocGN5QnBjeUJoSUhSbGMzUWdabWxzWlM0PScsXG4gKiAgICAgICAgICAgY29udGVudFR5cGU6ICd0ZXh0L3BsYWluJ1xuICogICAgICAgfSwgeyBzY29wZTogJ3VzZXInIH0pO1xuICpcbiAqICAgICAgIC8vIGFsdGVybmF0aXZlbHksIGNyZWF0ZSBhIG5ldyBhc3NldCB1c2luZyBhIGZpbGUgdXBsb2FkZWQgdGhyb3VnaCBhIGZvcm1cbiAqICAgICAgIC8vIHRoaXMgc2FtcGxlIGNvZGUgZ29lcyB3aXRoIGFuIGh0bWwgZm9ybSB0aGF0IGxvb2tzIGxpa2UgdGhpczpcbiAqICAgICAgIC8vXG4gKiAgICAgICAvLyA8Zm9ybSBpZD1cInVwbG9hZC1maWxlXCI+XG4gKiAgICAgICAvLyAgIDxpbnB1dCBpZD1cImZpbGVcIiB0eXBlPVwiZmlsZVwiPlxuICogICAgICAgLy8gICA8aW5wdXQgaWQ9XCJmaWxlbmFtZVwiIHR5cGU9XCJ0ZXh0XCIgdmFsdWU9XCJteUZpbGUudHh0XCI+XG4gKiAgICAgICAvLyAgIDxidXR0b24gdHlwZT1cInN1Ym1pdFwiPlVwbG9hZCBteUZpbGU8L2J1dHRvbj5cbiAqICAgICAgIC8vIDwvZm9ybT5cbiAqICAgICAgIC8vXG4gKiAgICAgICAkKCcjdXBsb2FkLWZpbGUnKS5vbignc3VibWl0JywgZnVuY3Rpb24gKGUpIHtcbiAqICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAqICAgICAgICAgIHZhciBmaWxlbmFtZSA9ICQoJyNmaWxlbmFtZScpLnZhbCgpO1xuICogICAgICAgICAgdmFyIGRhdGEgPSBuZXcgRm9ybURhdGEoKTtcbiAqICAgICAgICAgIHZhciBpbnB1dENvbnRyb2wgPSAkKCcjZmlsZScpWzBdO1xuICogICAgICAgICAgZGF0YS5hcHBlbmQoJ2ZpbGUnLCBpbnB1dENvbnRyb2wuZmlsZXNbMF0sIGZpbGVuYW1lKTtcbiAqXG4gKiAgICAgICAgICBhYS5jcmVhdGUoZmlsZW5hbWUsIGRhdGEsIHsgc2NvcGU6ICd1c2VyJyB9KTtcbiAqICAgICAgIH0pO1xuICpcbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbnZhciBfcGljayA9IHJlcXVpcmUoJy4uL3V0aWwvb2JqZWN0LXV0aWwnKS5fcGljaztcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xuXG52YXIgYXBpRW5kcG9pbnQgPSAnYXNzZXQnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBGb3IgcHJvamVjdHMgdGhhdCByZXF1aXJlIGF1dGhlbnRpY2F0aW9uLCBwYXNzIGluIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiAoZGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nKS4gSWYgdGhlIHVzZXIgaXMgYWxyZWFkeSBsb2dnZWQgaW4gdG8gRXBpY2VudGVyLCB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gaXMgYWxyZWFkeSBzZXQgaW4gYSBjb29raWUgYW5kIGF1dG9tYXRpY2FsbHkgbG9hZGVkIGZyb20gdGhlcmUuIChTZWUgW21vcmUgYmFja2dyb3VuZCBvbiBhY2Nlc3MgdG9rZW5zXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pKS5cbiAgICAgICAgICogQHNlZSBbQXV0aGVudGljYXRpb24gQVBJIFNlcnZpY2VdKC4uL2F1dGgtYXBpLXNlcnZpY2UvKSBmb3IgZ2V0dGluZyB0b2tlbnMuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICB0b2tlbjogdW5kZWZpbmVkLFxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFjY291bnQgaWQuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBVUkwuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBhY2NvdW50OiB1bmRlZmluZWQsXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgcHJvamVjdCBpZC4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHByb2plY3Q6IHVuZGVmaW5lZCxcbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBncm91cCBuYW1lLiBEZWZhdWx0cyB0byBzZXNzaW9uJ3MgYGdyb3VwTmFtZWAuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBncm91cDogdW5kZWZpbmVkLFxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHVzZXIgaWQuIERlZmF1bHRzIHRvIHNlc3Npb24ncyBgdXNlcklkYC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHVzZXJJZDogdW5kZWZpbmVkLFxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHNjb3BlIGZvciB0aGUgYXNzZXQuIFZhbGlkIHZhbHVlcyBhcmU6IGB1c2VyYCwgYGdyb3VwYCwgYW5kIGBwcm9qZWN0YC4gU2VlIGFib3ZlIGZvciB0aGUgcmVxdWlyZWQgcGVybWlzc2lvbnMgdG8gd3JpdGUgdG8gZWFjaCBzY29wZS4gRGVmYXVsdHMgdG8gYHVzZXJgLCBtZWFuaW5nIHRoZSBjdXJyZW50IGVuZCB1c2VyIG9yIGEgZmFjaWxpdGF0b3IgaW4gdGhlIGVuZCB1c2VyJ3MgZ3JvdXAgY2FuIGVkaXQgdGhlIGFzc2V0LlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgc2NvcGU6ICd1c2VyJyxcbiAgICAgICAgLyoqXG4gICAgICAgICAqIERldGVybWluZXMgaWYgYSByZXF1ZXN0IHRvIGxpc3QgdGhlIGFzc2V0cyBpbiBhIHNjb3BlIGluY2x1ZGVzIHRoZSBjb21wbGV0ZSBVUkwgZm9yIGVhY2ggYXNzZXQgKGB0cnVlYCksIG9yIG9ubHkgdGhlIGZpbGUgbmFtZXMgb2YgdGhlIGFzc2V0cyAoYGZhbHNlYCkuIERlZmF1bHRzIHRvIGB0cnVlYC5cbiAgICAgICAgICogQHR5cGUge2Jvb2xlYW59XG4gICAgICAgICAqL1xuICAgICAgICBmdWxsVXJsOiB0cnVlLFxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHRyYW5zcG9ydCBvYmplY3QgY29udGFpbnMgdGhlIG9wdGlvbnMgcGFzc2VkIHRvIHRoZSBYSFIgcmVxdWVzdC5cbiAgICAgICAgICogQHR5cGUge29iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIHRyYW5zcG9ydDoge1xuICAgICAgICAgICAgcHJvY2Vzc0RhdGE6IGZhbHNlXG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICB2YXIgc2VydmljZU9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMoZGVmYXVsdHMsIGNvbmZpZyk7XG4gICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuXG4gICAgaWYgKCFzZXJ2aWNlT3B0aW9ucy5hY2NvdW50KSB7XG4gICAgICAgIHNlcnZpY2VPcHRpb25zLmFjY291bnQgPSB1cmxDb25maWcuYWNjb3VudFBhdGg7XG4gICAgfVxuXG4gICAgaWYgKCFzZXJ2aWNlT3B0aW9ucy5wcm9qZWN0KSB7XG4gICAgICAgIHNlcnZpY2VPcHRpb25zLnByb2plY3QgPSB1cmxDb25maWcucHJvamVjdFBhdGg7XG4gICAgfVxuXG4gICAgdmFyIHRyYW5zcG9ydE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LCB7XG4gICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpXG4gICAgfSk7XG5cbiAgICBpZiAoc2VydmljZU9wdGlvbnMudG9rZW4pIHtcbiAgICAgICAgdHJhbnNwb3J0T3B0aW9ucy5oZWFkZXJzID0ge1xuICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogJ0JlYXJlciAnICsgc2VydmljZU9wdGlvbnMudG9rZW5cbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KHRyYW5zcG9ydE9wdGlvbnMpO1xuXG4gICAgdmFyIGFzc2V0QXBpUGFyYW1zID0gWydlbmNvZGluZycsICdkYXRhJywgJ2NvbnRlbnRUeXBlJ107XG4gICAgdmFyIHNjb3BlQ29uZmlnID0ge1xuICAgICAgICB1c2VyOiBbJ3Njb3BlJywgJ2FjY291bnQnLCAncHJvamVjdCcsICdncm91cCcsICd1c2VySWQnXSxcbiAgICAgICAgZ3JvdXA6IFsnc2NvcGUnLCAnYWNjb3VudCcsICdwcm9qZWN0JywgJ2dyb3VwJ10sXG4gICAgICAgIHByb2plY3Q6IFsnc2NvcGUnLCAnYWNjb3VudCcsICdwcm9qZWN0J10sXG4gICAgfTtcblxuICAgIHZhciB2YWxpZGF0ZUZpbGVuYW1lID0gZnVuY3Rpb24gKGZpbGVuYW1lKSB7XG4gICAgICAgIGlmICghZmlsZW5hbWUpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignZmlsZW5hbWUgaXMgbmVlZGVkLicpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHZhciB2YWxpZGF0ZVVybFBhcmFtcyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIHZhciBwYXJ0S2V5cyA9IHNjb3BlQ29uZmlnW29wdGlvbnMuc2NvcGVdO1xuICAgICAgICBpZiAoIXBhcnRLZXlzKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3Njb3BlIHBhcmFtZXRlciBpcyBuZWVkZWQuJyk7XG4gICAgICAgIH1cblxuICAgICAgICAkLmVhY2gocGFydEtleXMsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGlmICghb3B0aW9uc1t0aGlzXSkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcih0aGlzICsgJyBwYXJhbWV0ZXIgaXMgbmVlZGVkLicpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgdmFyIGJ1aWxkVXJsID0gZnVuY3Rpb24gKGZpbGVuYW1lLCBvcHRpb25zKSB7XG4gICAgICAgIHZhbGlkYXRlVXJsUGFyYW1zKG9wdGlvbnMpO1xuICAgICAgICB2YXIgcGFydEtleXMgPSBzY29wZUNvbmZpZ1tvcHRpb25zLnNjb3BlXTtcbiAgICAgICAgdmFyIHBhcnRzID0gJC5tYXAocGFydEtleXMsIGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgICAgIHJldHVybiBvcHRpb25zW2tleV07XG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoZmlsZW5hbWUpIHtcbiAgICAgICAgICAgIC8vIFRoaXMgcHJldmVudHMgYWRkaW5nIGEgdHJhaWxpbmcgLyBpbiB0aGUgVVJMIGFzIHRoZSBBc3NldCBBUElcbiAgICAgICAgICAgIC8vIGRvZXMgbm90IHdvcmsgY29ycmVjdGx5IHdpdGggaXRcbiAgICAgICAgICAgIGZpbGVuYW1lID0gJy8nICsgZmlsZW5hbWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHBhcnRzLmpvaW4oJy8nKSArIGZpbGVuYW1lO1xuICAgIH07XG5cbiAgICAvLyBQcml2YXRlIGZ1bmN0aW9uLCBhbGwgcmVxdWVzdHMgZm9sbG93IGEgbW9yZSBvciBsZXNzIHNhbWUgYXBwcm9hY2ggdG9cbiAgICAvLyB1c2UgdGhlIEFzc2V0IEFQSSBhbmQgdGhlIGRpZmZlcmVuY2UgaXMgdGhlIEhUVFAgdmVyYlxuICAgIC8vXG4gICAgLy8gQHBhcmFtIHtzdHJpbmd9IG1ldGhvZGAgKFJlcXVpcmVkKSBIVFRQIHZlcmJcbiAgICAvLyBAcGFyYW0ge3N0cmluZ30gZmlsZW5hbWVgIChSZXF1aXJlZCkgTmFtZSBvZiB0aGUgZmlsZSB0byBkZWxldGUvcmVwbGFjZS9jcmVhdGVcbiAgICAvLyBAcGFyYW0ge29iamVjdH0gcGFyYW1zYCAoT3B0aW9uYWwpIEJvZHkgcGFyYW1ldGVycyB0byBzZW5kIHRvIHRoZSBBc3NldCBBUElcbiAgICAvLyBAcGFyYW0ge29iamVjdH0gb3B0aW9uc2AgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICB2YXIgdXBsb2FkID0gZnVuY3Rpb24gKG1ldGhvZCwgZmlsZW5hbWUsIHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICB2YWxpZGF0ZUZpbGVuYW1lKGZpbGVuYW1lKTtcbiAgICAgICAgLy8gbWFrZSBzdXJlIHRoZSBwYXJhbWV0ZXIgaXMgY2xlYW5cbiAgICAgICAgbWV0aG9kID0gbWV0aG9kLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIHZhciBjb250ZW50VHlwZSA9IHBhcmFtcyBpbnN0YW5jZW9mIEZvcm1EYXRhID09PSB0cnVlID8gZmFsc2UgOiAnYXBwbGljYXRpb24vanNvbic7XG4gICAgICAgIGlmIChjb250ZW50VHlwZSA9PT0gJ2FwcGxpY2F0aW9uL2pzb24nKSB7XG4gICAgICAgICAgICAvLyB3aGl0ZWxpc3QgdGhlIGZpZWxkcyB0aGF0IHdlIGFjdHVhbGx5IGNhbiBzZW5kIHRvIHRoZSBhcGlcbiAgICAgICAgICAgIHBhcmFtcyA9IF9waWNrKHBhcmFtcywgYXNzZXRBcGlQYXJhbXMpO1xuICAgICAgICB9IGVsc2UgeyAvLyBlbHNlIHdlJ3JlIHNlbmRpbmcgZm9ybSBkYXRhIHdoaWNoIGdvZXMgZGlyZWN0bHkgaW4gcmVxdWVzdCBib2R5XG4gICAgICAgICAgICAvLyBGb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSB1cGxvYWRzIHRoZSBmaWxlbmFtZSBpcyBub3Qgc2V0IGluIHRoZSBVUkwsXG4gICAgICAgICAgICAvLyBpdCdzIGdldHRpbmcgcGlja2VkIGJ5IHRoZSBGb3JtRGF0YSBmaWVsZCBmaWxlbmFtZS5cbiAgICAgICAgICAgIGZpbGVuYW1lID0gbWV0aG9kID09PSAncG9zdCcgfHwgbWV0aG9kID09PSAncHV0JyA/ICcnIDogZmlsZW5hbWU7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIHVybE9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICB2YXIgdXJsID0gYnVpbGRVcmwoZmlsZW5hbWUsIHVybE9wdGlvbnMpO1xuICAgICAgICB2YXIgY3JlYXRlT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCB1cmxPcHRpb25zLCB7IHVybDogdXJsLCBjb250ZW50VHlwZTogY29udGVudFR5cGUgfSk7XG5cbiAgICAgICAgcmV0dXJuIGh0dHBbbWV0aG9kXShwYXJhbXMsIGNyZWF0ZU9wdGlvbnMpO1xuICAgIH07XG5cbiAgICB2YXIgcHVibGljQVBJID0ge1xuICAgICAgICAvKipcbiAgICAgICAgKiBDcmVhdGVzIGEgZmlsZSBpbiB0aGUgQXNzZXQgQVBJLiBUaGUgc2VydmVyIHJldHVybnMgYW4gZXJyb3IgKHN0YXR1cyBjb2RlIGA0MDlgLCBjb25mbGljdCkgaWYgdGhlIGZpbGUgYWxyZWFkeSBleGlzdHMsIHNvXG4gICAgICAgICogY2hlY2sgZmlyc3Qgd2l0aCBhIGBsaXN0KClgIG9yIGEgYGdldCgpYC5cbiAgICAgICAgKlxuICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgdmFyIGFhID0gbmV3IEYuc2VydmljZS5Bc3NldCh7XG4gICAgICAgICogICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICAgICogICAgICAgICAgZ3JvdXA6ICd0ZWFtMScsXG4gICAgICAgICogICAgICAgICAgdXNlcklkOiAnJ1xuICAgICAgICAqICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICAgICAgLy8gY3JlYXRlIGEgbmV3IGFzc2V0IHVzaW5nIGVuY29kZWQgdGV4dFxuICAgICAgICAqICAgICAgIGFhLmNyZWF0ZSgndGVzdC50eHQnLCB7XG4gICAgICAgICogICAgICAgICAgIGVuY29kaW5nOiAnQkFTRV82NCcsXG4gICAgICAgICogICAgICAgICAgIGRhdGE6ICdWR2hwY3lCcGN5QmhJSFJsYzNRZ1ptbHNaUzQ9JyxcbiAgICAgICAgKiAgICAgICAgICAgY29udGVudFR5cGU6ICd0ZXh0L3BsYWluJ1xuICAgICAgICAqICAgICAgIH0sIHsgc2NvcGU6ICd1c2VyJyB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgIC8vIGFsdGVybmF0aXZlbHksIGNyZWF0ZSBhIG5ldyBhc3NldCB1c2luZyBhIGZpbGUgdXBsb2FkZWQgdGhyb3VnaCBhIGZvcm1cbiAgICAgICAgKiAgICAgICAvLyB0aGlzIHNhbXBsZSBjb2RlIGdvZXMgd2l0aCBhbiBodG1sIGZvcm0gdGhhdCBsb29rcyBsaWtlIHRoaXM6XG4gICAgICAgICogICAgICAgLy9cbiAgICAgICAgKiAgICAgICAvLyA8Zm9ybSBpZD1cInVwbG9hZC1maWxlXCI+XG4gICAgICAgICogICAgICAgLy8gICA8aW5wdXQgaWQ9XCJmaWxlXCIgdHlwZT1cImZpbGVcIj5cbiAgICAgICAgKiAgICAgICAvLyAgIDxpbnB1dCBpZD1cImZpbGVuYW1lXCIgdHlwZT1cInRleHRcIiB2YWx1ZT1cIm15RmlsZS50eHRcIj5cbiAgICAgICAgKiAgICAgICAvLyAgIDxidXR0b24gdHlwZT1cInN1Ym1pdFwiPlVwbG9hZCBteUZpbGU8L2J1dHRvbj5cbiAgICAgICAgKiAgICAgICAvLyA8L2Zvcm0+XG4gICAgICAgICogICAgICAgLy9cbiAgICAgICAgKiAgICAgICAkKCcjdXBsb2FkLWZpbGUnKS5vbignc3VibWl0JywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgKiAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICogICAgICAgICAgdmFyIGZpbGVuYW1lID0gJCgnI2ZpbGVuYW1lJykudmFsKCk7XG4gICAgICAgICogICAgICAgICAgdmFyIGRhdGEgPSBuZXcgRm9ybURhdGEoKTtcbiAgICAgICAgKiAgICAgICAgICB2YXIgaW5wdXRDb250cm9sID0gJCgnI2ZpbGUnKVswXTtcbiAgICAgICAgKiAgICAgICAgICBkYXRhLmFwcGVuZCgnZmlsZScsIGlucHV0Q29udHJvbC5maWxlc1swXSwgZmlsZW5hbWUpO1xuICAgICAgICAqXG4gICAgICAgICogICAgICAgICAgYWEuY3JlYXRlKGZpbGVuYW1lLCBkYXRhLCB7IHNjb3BlOiAndXNlcicgfSk7XG4gICAgICAgICogICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKlxuICAgICAgICAqICAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlbmFtZSAoUmVxdWlyZWQpIE5hbWUgb2YgdGhlIGZpbGUgdG8gY3JlYXRlLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgKE9wdGlvbmFsKSBCb2R5IHBhcmFtZXRlcnMgdG8gc2VuZCB0byB0aGUgQXNzZXQgQVBJLiBSZXF1aXJlZCBpZiB0aGUgYG9wdGlvbnMudHJhbnNwb3J0LmNvbnRlbnRUeXBlYCBpcyBgYXBwbGljYXRpb24vanNvbmAsIG90aGVyd2lzZSBpZ25vcmVkLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuZW5jb2RpbmcgRWl0aGVyIGBIRVhgIG9yIGBCQVNFXzY0YC4gUmVxdWlyZWQgaWYgYG9wdGlvbnMudHJhbnNwb3J0LmNvbnRlbnRUeXBlYCBpcyBgYXBwbGljYXRpb24vanNvbmAuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5kYXRhIFRoZSBlbmNvZGVkIGRhdGEgZm9yIHRoZSBmaWxlLiBSZXF1aXJlZCBpZiBgb3B0aW9ucy50cmFuc3BvcnQuY29udGVudFR5cGVgIGlzIGBhcHBsaWNhdGlvbi9qc29uYC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmNvbnRlbnRUeXBlIFRoZSBtaW1lIHR5cGUgb2YgdGhlIGZpbGUuIE9wdGlvbmFsLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgY3JlYXRlOiBmdW5jdGlvbiAoZmlsZW5hbWUsIHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIHVwbG9hZCgncG9zdCcsIGZpbGVuYW1lLCBwYXJhbXMsIG9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIEdldHMgYSBmaWxlIGZyb20gdGhlIEFzc2V0IEFQSSwgZmV0Y2hpbmcgdGhlIGFzc2V0IGNvbnRlbnQuIChUbyBnZXQgYSBsaXN0XG4gICAgICAgICogb2YgdGhlIGFzc2V0cyBpbiBhIHNjb3BlLCB1c2UgYGxpc3QoKWAuKVxuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGZpbGVuYW1lIChSZXF1aXJlZCkgTmFtZSBvZiB0aGUgZmlsZSB0byByZXRyaWV2ZS5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldDogZnVuY3Rpb24gKGZpbGVuYW1lLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgZ2V0U2VydmljZU9wdGlvbnMgPSBfcGljayhzZXJ2aWNlT3B0aW9ucywgWydzY29wZScsICdhY2NvdW50JywgJ3Byb2plY3QnLCAnZ3JvdXAnLCAndXNlcklkJ10pO1xuICAgICAgICAgICAgdmFyIHVybE9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgZ2V0U2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgdmFyIHVybCA9IGJ1aWxkVXJsKGZpbGVuYW1lLCB1cmxPcHRpb25zKTtcbiAgICAgICAgICAgIHZhciBnZXRPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHVybE9wdGlvbnMsIHsgdXJsOiB1cmwgfSk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldCh7fSwgZ2V0T3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogR2V0cyB0aGUgbGlzdCBvZiB0aGUgYXNzZXRzIGluIGEgc2NvcGUuXG4gICAgICAgICpcbiAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgYWEubGlzdCh7IGZ1bGxVcmw6IHRydWUgfSkudGhlbihmdW5jdGlvbihmaWxlTGlzdCl7XG4gICAgICAgICogICAgICAgICAgIGNvbnNvbGUubG9nKCdhcnJheSBvZiBmaWxlcyA9ICcsIGZpbGVMaXN0KTtcbiAgICAgICAgKiAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IG9wdGlvbnMuc2NvcGUgKE9wdGlvbmFsKSBUaGUgc2NvcGUgKGB1c2VyYCwgYGdyb3VwYCwgYHByb2plY3RgKS5cbiAgICAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IG9wdGlvbnMuZnVsbFVybCAoT3B0aW9uYWwpIERldGVybWluZXMgaWYgdGhlIGxpc3Qgb2YgYXNzZXRzIGluIGEgc2NvcGUgaW5jbHVkZXMgdGhlIGNvbXBsZXRlIFVSTCBmb3IgZWFjaCBhc3NldCAoYHRydWVgKSwgb3Igb25seSB0aGUgZmlsZSBuYW1lcyBvZiB0aGUgYXNzZXRzIChgZmFsc2VgKS5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBsaXN0OiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIGR0ZCA9ICQuRGVmZXJyZWQoKTtcbiAgICAgICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgICAgICB2YXIgdXJsT3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICB2YXIgdXJsID0gYnVpbGRVcmwoJycsIHVybE9wdGlvbnMpO1xuICAgICAgICAgICAgdmFyIGdldE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgdXJsT3B0aW9ucywgeyB1cmw6IHVybCB9KTtcbiAgICAgICAgICAgIHZhciBmdWxsVXJsID0gZ2V0T3B0aW9ucy5mdWxsVXJsO1xuXG4gICAgICAgICAgICBpZiAoIWZ1bGxVcmwpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQoe30sIGdldE9wdGlvbnMpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBodHRwLmdldCh7fSwgZ2V0T3B0aW9ucylcbiAgICAgICAgICAgICAgICAudGhlbihmdW5jdGlvbiAoZmlsZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGZ1bGxQYXRoRmlsZXMgPSAkLm1hcChmaWxlcywgZnVuY3Rpb24gKGZpbGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBidWlsZFVybChmaWxlLCB1cmxPcHRpb25zKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIGR0ZC5yZXNvbHZlV2l0aChtZSwgW2Z1bGxQYXRoRmlsZXNdKTtcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC5mYWlsKGR0ZC5yZWplY3QpO1xuXG4gICAgICAgICAgICByZXR1cm4gZHRkLnByb21pc2UoKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBSZXBsYWNlcyBhbiBleGlzdGluZyBmaWxlIGluIHRoZSBBc3NldCBBUEkuXG4gICAgICAgICpcbiAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgLy8gcmVwbGFjZSBhbiBhc3NldCB1c2luZyBlbmNvZGVkIHRleHRcbiAgICAgICAgKiAgICAgICBhYS5yZXBsYWNlKCd0ZXN0LnR4dCcsIHtcbiAgICAgICAgKiAgICAgICAgICAgZW5jb2Rpbmc6ICdCQVNFXzY0JyxcbiAgICAgICAgKiAgICAgICAgICAgZGF0YTogJ1ZHaHBjeUJwY3lCaElITmxZMjl1WkNCMFpYTjBJR1pwYkdVdScsXG4gICAgICAgICogICAgICAgICAgIGNvbnRlbnRUeXBlOiAndGV4dC9wbGFpbidcbiAgICAgICAgKiAgICAgICB9LCB7IHNjb3BlOiAndXNlcicgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICAvLyBhbHRlcm5hdGl2ZWx5LCByZXBsYWNlIGFuIGFzc2V0IHVzaW5nIGEgZmlsZSB1cGxvYWRlZCB0aHJvdWdoIGEgZm9ybVxuICAgICAgICAqICAgICAgIC8vIHRoaXMgc2FtcGxlIGNvZGUgZ29lcyB3aXRoIGFuIGh0bWwgZm9ybSB0aGF0IGxvb2tzIGxpa2UgdGhpczpcbiAgICAgICAgKiAgICAgICAvL1xuICAgICAgICAqICAgICAgIC8vIDxmb3JtIGlkPVwicmVwbGFjZS1maWxlXCI+XG4gICAgICAgICogICAgICAgLy8gICA8aW5wdXQgaWQ9XCJmaWxlXCIgdHlwZT1cImZpbGVcIj5cbiAgICAgICAgKiAgICAgICAvLyAgIDxpbnB1dCBpZD1cInJlcGxhY2UtZmlsZW5hbWVcIiB0eXBlPVwidGV4dFwiIHZhbHVlPVwibXlGaWxlLnR4dFwiPlxuICAgICAgICAqICAgICAgIC8vICAgPGJ1dHRvbiB0eXBlPVwic3VibWl0XCI+UmVwbGFjZSBteUZpbGU8L2J1dHRvbj5cbiAgICAgICAgKiAgICAgICAvLyA8L2Zvcm0+XG4gICAgICAgICogICAgICAgLy9cbiAgICAgICAgKiAgICAgICAkKCcjcmVwbGFjZS1maWxlJykub24oJ3N1Ym1pdCcsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICogICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAqICAgICAgICAgIHZhciBmaWxlbmFtZSA9ICQoJyNyZXBsYWNlLWZpbGVuYW1lJykudmFsKCk7XG4gICAgICAgICogICAgICAgICAgdmFyIGRhdGEgPSBuZXcgRm9ybURhdGEoKTtcbiAgICAgICAgKiAgICAgICAgICB2YXIgaW5wdXRDb250cm9sID0gJCgnI2ZpbGUnKVswXTtcbiAgICAgICAgKiAgICAgICAgICBkYXRhLmFwcGVuZCgnZmlsZScsIGlucHV0Q29udHJvbC5maWxlc1swXSwgZmlsZW5hbWUpO1xuICAgICAgICAqXG4gICAgICAgICogICAgICAgICAgYWEucmVwbGFjZShmaWxlbmFtZSwgZGF0YSwgeyBzY29wZTogJ3VzZXInIH0pO1xuICAgICAgICAqICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGZpbGVuYW1lIChSZXF1aXJlZCkgTmFtZSBvZiB0aGUgZmlsZSBiZWluZyByZXBsYWNlZC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zIChPcHRpb25hbCkgQm9keSBwYXJhbWV0ZXJzIHRvIHNlbmQgdG8gdGhlIEFzc2V0IEFQSS4gUmVxdWlyZWQgaWYgdGhlIGBvcHRpb25zLnRyYW5zcG9ydC5jb250ZW50VHlwZWAgaXMgYGFwcGxpY2F0aW9uL2pzb25gLCBvdGhlcndpc2UgaWdub3JlZC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmVuY29kaW5nIEVpdGhlciBgSEVYYCBvciBgQkFTRV82NGAuIFJlcXVpcmVkIGlmIGBvcHRpb25zLnRyYW5zcG9ydC5jb250ZW50VHlwZWAgaXMgYGFwcGxpY2F0aW9uL2pzb25gLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuZGF0YSBUaGUgZW5jb2RlZCBkYXRhIGZvciB0aGUgZmlsZS4gUmVxdWlyZWQgaWYgYG9wdGlvbnMudHJhbnNwb3J0LmNvbnRlbnRUeXBlYCBpcyBgYXBwbGljYXRpb24vanNvbmAuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5jb250ZW50VHlwZSBUaGUgbWltZSB0eXBlIG9mIHRoZSBmaWxlLiBPcHRpb25hbC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIHJlcGxhY2U6IGZ1bmN0aW9uIChmaWxlbmFtZSwgcGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICByZXR1cm4gdXBsb2FkKCdwdXQnLCBmaWxlbmFtZSwgcGFyYW1zLCBvcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBEZWxldGVzIGEgZmlsZSBmcm9tIHRoZSBBc3NldCBBUEkuXG4gICAgICAgICpcbiAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgYWEuZGVsZXRlKHNhbXBsZUZpbGVOYW1lKTtcbiAgICAgICAgKlxuICAgICAgICAqICAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlbmFtZSAoUmVxdWlyZWQpIE5hbWUgb2YgdGhlIGZpbGUgdG8gZGVsZXRlLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZGVsZXRlOiBmdW5jdGlvbiAoZmlsZW5hbWUsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHJldHVybiB1cGxvYWQoJ2RlbGV0ZScsIGZpbGVuYW1lLCB7fSwgb3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgYXNzZXRVcmw6IGZ1bmN0aW9uIChmaWxlbmFtZSwgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIHVybE9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgcmV0dXJuIGJ1aWxkVXJsKGZpbGVuYW1lLCB1cmxPcHRpb25zKTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgJC5leHRlbmQodGhpcywgcHVibGljQVBJKTtcbn07XG4iLCIvKipcbiAqXG4gKiAjIyBBdXRoZW50aWNhdGlvbiBBUEkgU2VydmljZVxuICpcbiAqIFRoZSBBdXRoZW50aWNhdGlvbiBBUEkgU2VydmljZSBwcm92aWRlcyBhIG1ldGhvZCBmb3IgbG9nZ2luZyBpbiwgd2hpY2ggY3JlYXRlcyBhbmQgcmV0dXJucyBhIHVzZXIgYWNjZXNzIHRva2VuLlxuICpcbiAqIFVzZXIgYWNjZXNzIHRva2VucyBhcmUgcmVxdWlyZWQgZm9yIGVhY2ggY2FsbCB0byBFcGljZW50ZXIuIChTZWUgW1Byb2plY3QgQWNjZXNzXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pIGZvciBtb3JlIGluZm9ybWF0aW9uLilcbiAqXG4gKiBJZiB5b3UgbmVlZCBhZGRpdGlvbmFsIGZ1bmN0aW9uYWxpdHkgLS0gc3VjaCBhcyB0cmFja2luZyBzZXNzaW9uIGluZm9ybWF0aW9uLCBlYXNpbHkgcmV0cmlldmluZyB0aGUgdXNlciB0b2tlbiwgb3IgZ2V0dGluZyB0aGUgZ3JvdXBzIHRvIHdoaWNoIGFuIGVuZCB1c2VyIGJlbG9uZ3MgLS0gY29uc2lkZXIgdXNpbmcgdGhlIFtBdXRob3JpemF0aW9uIE1hbmFnZXJdKC4uL2F1dGgtbWFuYWdlci8pIGluc3RlYWQuXG4gKlxuICogICAgICB2YXIgYXV0aCA9IG5ldyBGLnNlcnZpY2UuQXV0aCgpO1xuICogICAgICBhdXRoLmxvZ2luKHsgdXNlck5hbWU6ICdqc21pdGhAYWNtZXNpbXVsYXRpb25zLmNvbScsXG4gKiAgICAgICAgICAgICAgICAgIHBhc3N3b3JkOiAncGFzc3cwcmQnIH0pO1xuICovXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBFbWFpbCBvciB1c2VybmFtZSB0byB1c2UgZm9yIGxvZ2dpbmcgaW4uIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHVzZXJOYW1lOiAnJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUGFzc3dvcmQgZm9yIHNwZWNpZmllZCBgdXNlck5hbWVgLiBEZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBwYXNzd29yZDogJycsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBhY2NvdW50IGlkIGZvciB0aGlzIGB1c2VyTmFtZWAuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykgb3IgdGhlICoqVXNlciBJRCoqIChmb3IgcGVyc29uYWwgcHJvamVjdHMpLiBSZXF1aXJlZCBpZiB0aGUgYHVzZXJOYW1lYCBpcyBmb3IgYW4gW2VuZCB1c2VyXSguLi8uLi8uLi9nbG9zc2FyeS8jdXNlcnMpLiBEZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBhY2NvdW50OiAnJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT3B0aW9ucyB0byBwYXNzIG9uIHRvIHRoZSB1bmRlcmx5aW5nIHRyYW5zcG9ydCBsYXllci4gQWxsIGpxdWVyeS5hamF4IG9wdGlvbnMgYXQgaHR0cDovL2FwaS5qcXVlcnkuY29tL2pRdWVyeS5hamF4LyBhcmUgYXZhaWxhYmxlLiBEZWZhdWx0cyB0byBlbXB0eSBvYmplY3QuXG4gICAgICAgICAqIEB0eXBlIHtPYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICB0cmFuc3BvcnQ6IHt9XG4gICAgfTtcbiAgICB2YXIgc2VydmljZU9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgZGVmYXVsdHMsIGNvbmZpZyk7XG4gICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuXG4gICAgdmFyIHRyYW5zcG9ydE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LCB7XG4gICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoJ2F1dGhlbnRpY2F0aW9uJylcbiAgICB9KTtcbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KHRyYW5zcG9ydE9wdGlvbnMpO1xuXG4gICAgdmFyIHB1YmxpY0FQSSA9IHtcblxuICAgICAgICAvKipcbiAgICAgICAgICogTG9ncyB1c2VyIGluLCByZXR1cm5pbmcgdGhlIHVzZXIgYWNjZXNzIHRva2VuLlxuICAgICAgICAgKlxuICAgICAgICAgKiBJZiBubyBgdXNlck5hbWVgIG9yIGBwYXNzd29yZGAgd2VyZSBwcm92aWRlZCBpbiB0aGUgaW5pdGlhbCBjb25maWd1cmF0aW9uIG9wdGlvbnMsIHRoZXkgYXJlIHJlcXVpcmVkIGluIHRoZSBgb3B0aW9uc2AgaGVyZS4gSWYgbm8gYGFjY291bnRgIHdhcyBwcm92aWRlZCBpbiB0aGUgaW5pdGlhbCBjb25maWd1cmF0aW9uIG9wdGlvbnMgYW5kIHRoZSBgdXNlck5hbWVgIGlzIGZvciBhbiBbZW5kIHVzZXJdKC4uLy4uLy4uL2dsb3NzYXJ5LyN1c2VycyksIHRoZSBgYWNjb3VudGAgaXMgcmVxdWlyZWQgYXMgd2VsbC5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICBhdXRoLmxvZ2luKHtcbiAgICAgICAgICogICAgICAgICAgdXNlck5hbWU6ICdqc21pdGgnLFxuICAgICAgICAgKiAgICAgICAgICBwYXNzd29yZDogJ3Bhc3N3MHJkJyxcbiAgICAgICAgICogICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnIH0pXG4gICAgICAgICAqICAgICAgLnRoZW4oZnVuY3Rpb24gKHRva2VuKSB7XG4gICAgICAgICAqICAgICAgICAgIGNvbnNvbGUubG9nKFwidXNlciBhY2Nlc3MgdG9rZW4gaXM6IFwiLCB0b2tlbi5hY2Nlc3NfdG9rZW4pO1xuICAgICAgICAgKiAgICAgIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgbG9naW46IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7IHN1Y2Nlc3M6ICQubm9vcCB9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICBpZiAoIWh0dHBPcHRpb25zLnVzZXJOYW1lIHx8ICFodHRwT3B0aW9ucy5wYXNzd29yZCkge1xuICAgICAgICAgICAgICAgIHZhciByZXNwID0geyBzdGF0dXM6IDQwMSwgc3RhdHVzTWVzc2FnZTogJ05vIHVzZXJuYW1lIG9yIHBhc3N3b3JkIHNwZWNpZmllZC4nIH07XG4gICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMuZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5lcnJvci5jYWxsKHRoaXMsIHJlc3ApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybiAkLkRlZmVycmVkKCkucmVqZWN0KHJlc3ApLnByb21pc2UoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHBvc3RQYXJhbXMgPSB7XG4gICAgICAgICAgICAgICAgdXNlck5hbWU6IGh0dHBPcHRpb25zLnVzZXJOYW1lLFxuICAgICAgICAgICAgICAgIHBhc3N3b3JkOiBodHRwT3B0aW9ucy5wYXNzd29yZCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBpZiAoaHR0cE9wdGlvbnMuYWNjb3VudCkge1xuICAgICAgICAgICAgICAgIC8vcGFzcyBpbiBudWxsIGZvciBhY2NvdW50IHVuZGVyIG9wdGlvbnMgaWYgeW91IGRvbid0IHdhbnQgaXQgdG8gYmUgc2VudFxuICAgICAgICAgICAgICAgIHBvc3RQYXJhbXMuYWNjb3VudCA9IGh0dHBPcHRpb25zLmFjY291bnQ7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLnBvc3QocG9zdFBhcmFtcywgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8vIChyZXBsYWNlIHdpdGggLyogKi8gY29tbWVudCBibG9jaywgdG8gbWFrZSB2aXNpYmxlIGluIGRvY3MsIG9uY2UgdGhpcyBpcyBtb3JlIHRoYW4gYSBub29wKVxuICAgICAgICAvL1xuICAgICAgICAvLyBMb2dzIHVzZXIgb3V0IGZyb20gc3BlY2lmaWVkIGFjY291bnRzLlxuICAgICAgICAvL1xuICAgICAgICAvLyBFcGljZW50ZXIgbG9nb3V0IGlzIG5vdCBpbXBsZW1lbnRlZCB5ZXQsIHNvIGZvciBub3cgdGhpcyBpcyBhIGR1bW15IHByb21pc2UgdGhhdCBnZXRzIGF1dG9tYXRpY2FsbHkgcmVzb2x2ZWQuXG4gICAgICAgIC8vXG4gICAgICAgIC8vICoqRXhhbXBsZSoqXG4gICAgICAgIC8vXG4gICAgICAgIC8vICAgICAgYXV0aC5sb2dvdXQoKTtcbiAgICAgICAgLy9cbiAgICAgICAgLy8gKipQYXJhbWV0ZXJzKipcbiAgICAgICAgLy8gQHBhcmFtIHtPYmplY3R9IGBvcHRpb25zYCAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAvL1xuICAgICAgICBsb2dvdXQ6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgZHRkID0gJC5EZWZlcnJlZCgpO1xuICAgICAgICAgICAgZHRkLnJlc29sdmUoKTtcbiAgICAgICAgICAgIHJldHVybiBkdGQucHJvbWlzZSgpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FQSSk7XG59O1xuIiwiLyoqXG4gKiAjIyBDaGFubmVsIFNlcnZpY2VcbiAqXG4gKiBUaGUgRXBpY2VudGVyIHBsYXRmb3JtIHByb3ZpZGVzIGEgcHVzaCBjaGFubmVsLCB3aGljaCBhbGxvd3MgeW91IHRvIHB1Ymxpc2ggYW5kIHN1YnNjcmliZSB0byBtZXNzYWdlcyB3aXRoaW4gYSBbcHJvamVjdF0oLi4vLi4vLi4vZ2xvc3NhcnkvI3Byb2plY3RzKSwgW2dyb3VwXSguLi8uLi8uLi9nbG9zc2FyeS8jZ3JvdXBzKSwgb3IgW211bHRpcGxheWVyIHdvcmxkXSguLi8uLi8uLi9nbG9zc2FyeS8jd29ybGQpLiBUaGVyZSBhcmUgdHdvIG1haW4gdXNlIGNhc2VzIGZvciB0aGUgY2hhbm5lbDogZXZlbnQgbm90aWZpY2F0aW9ucyBhbmQgY2hhdCBtZXNzYWdlcy5cbiAqXG4gKiBUaGUgQ2hhbm5lbCBTZXJ2aWNlIGlzIGEgYnVpbGRpbmcgYmxvY2sgZm9yIHRoaXMgZnVuY3Rpb25hbGl0eS4gSXQgY3JlYXRlcyBhIHB1Ymxpc2gtc3Vic2NyaWJlIG9iamVjdCwgYWxsb3dpbmcgeW91IHRvIHB1Ymxpc2ggbWVzc2FnZXMsIHN1YnNjcmliZSB0byBtZXNzYWdlcywgb3IgdW5zdWJzY3JpYmUgZnJvbSBtZXNzYWdlcyBmb3IgYSBnaXZlbiAndG9waWMnIG9uIGEgYCQuY29tZXRkYCB0cmFuc3BvcnQgaW5zdGFuY2UuXG4gKlxuICogVHlwaWNhbGx5LCB5b3UgdXNlIHRoZSBbRXBpY2VudGVyIENoYW5uZWwgTWFuYWdlcl0oLi4vZXBpY2VudGVyLWNoYW5uZWwtbWFuYWdlci8pIHRvIGNyZWF0ZSBvciByZXRyaWV2ZSBjaGFubmVscywgdGhlbiB1c2UgdGhlIENoYW5uZWwgU2VydmljZSBgc3Vic2NyaWJlKClgIGFuZCBgcHVibGlzaCgpYCBtZXRob2RzIHRvIGxpc3RlbiB0byBvciB1cGRhdGUgZGF0YS4gKEZvciBhZGRpdGlvbmFsIGJhY2tncm91bmQgb24gRXBpY2VudGVyJ3MgcHVzaCBjaGFubmVsLCBzZWUgdGhlIGludHJvZHVjdG9yeSBub3RlcyBvbiB0aGUgW1B1c2ggQ2hhbm5lbCBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9tdWx0aXBsYXllci9jaGFubmVsLykgcGFnZS4pXG4gKlxuICogWW91J2xsIG5lZWQgdG8gaW5jbHVkZSB0aGUgYGVwaWNlbnRlci1tdWx0aXBsYXllci1kZXBlbmRlbmNpZXMuanNgIGxpYnJhcnkgaW4gYWRkaXRpb24gdG8gdGhlIGBlcGljZW50ZXIuanNgIGxpYnJhcnkgaW4geW91ciBwcm9qZWN0IHRvIHVzZSB0aGUgQ2hhbm5lbCBTZXJ2aWNlLiBTZWUgW0luY2x1ZGluZyBFcGljZW50ZXIuanNdKC4uLy4uLyNpbmNsdWRlKS5cbiAqXG4gKiBUbyB1c2UgdGhlIENoYW5uZWwgU2VydmljZSwgaW5zdGFudGlhdGUgaXQsIHRoZW4gbWFrZSBjYWxscyB0byBhbnkgb2YgdGhlIG1ldGhvZHMgeW91IG5lZWQuXG4gKlxuICogICAgICAgIHZhciBjcyA9IG5ldyBGLnNlcnZpY2UuQ2hhbm5lbCgpO1xuICogICAgICAgIGNzLnB1Ymxpc2goJy9hY21lLXNpbXVsYXRpb25zL3N1cHBseS1jaGFpbi1nYW1lL2ZhbGwtc2VtaW5hci9ydW4vdmFyaWFibGVzJywgeyBwcmljZTogNTAgfSk7XG4gKlxuICogVGhlIHBhcmFtZXRlcnMgZm9yIGluc3RhbnRpYXRpbmcgYSBDaGFubmVsIFNlcnZpY2UgaW5jbHVkZTpcbiAqXG4gKiAqIGBvcHRpb25zYCBUaGUgb3B0aW9ucyBvYmplY3QgdG8gY29uZmlndXJlIHRoZSBDaGFubmVsIFNlcnZpY2UuXG4gKiAqIGBvcHRpb25zLmJhc2VgIFRoZSBiYXNlIHRvcGljLiBUaGlzIGlzIGFkZGVkIGFzIGEgcHJlZml4IHRvIGFsbCBmdXJ0aGVyIHRvcGljcyB5b3UgcHVibGlzaCBvciBzdWJzY3JpYmUgdG8gd2hpbGUgd29ya2luZyB3aXRoIHRoaXMgQ2hhbm5lbCBTZXJ2aWNlLlxuICogKiBgb3B0aW9ucy50b3BpY1Jlc29sdmVyYCBBIGZ1bmN0aW9uIHRoYXQgcHJvY2Vzc2VzIGFsbCAndG9waWNzJyBwYXNzZWQgaW50byB0aGUgYHB1Ymxpc2hgIGFuZCBgc3Vic2NyaWJlYCBtZXRob2RzLiBUaGlzIGlzIHVzZWZ1bCBpZiB5b3Ugd2FudCB0byBpbXBsZW1lbnQgeW91ciBvd24gc2VyaWFsaXplIGZ1bmN0aW9ucyBmb3IgY29udmVydGluZyBjdXN0b20gb2JqZWN0cyB0byB0b3BpYyBuYW1lcy4gUmV0dXJucyBhIFN0cmluZy4gQnkgZGVmYXVsdCwgaXQganVzdCBlY2hvZXMgdGhlIHRvcGljLlxuICogKiBgb3B0aW9ucy50cmFuc3BvcnRgIFRoZSBpbnN0YW5jZSBvZiBgJC5jb21ldGRgIHRvIGhvb2sgb250by4gU2VlIGh0dHA6Ly9kb2NzLmNvbWV0ZC5vcmcvcmVmZXJlbmNlL2phdmFzY3JpcHQuaHRtbCBmb3IgYWRkaXRpb25hbCBiYWNrZ3JvdW5kIG9uIGNvbWV0ZC5cbiAqL1xuXG4ndXNlIHN0cmljdCc7XG52YXIgQ2hhbm5lbCA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgdmFyIGRlZmF1bHRzID0ge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgYmFzZSB0b3BpYy4gVGhpcyBpcyBhZGRlZCBhcyBhIHByZWZpeCB0byBhbGwgZnVydGhlciB0b3BpY3MgeW91IHB1Ymxpc2ggb3Igc3Vic2NyaWJlIHRvIHdoaWxlIHdvcmtpbmcgd2l0aCB0aGlzIENoYW5uZWwgU2VydmljZS5cbiAgICAgICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGJhc2U6ICcnLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBBIGZ1bmN0aW9uIHRoYXQgcHJvY2Vzc2VzIGFsbCAndG9waWNzJyBwYXNzZWQgaW50byB0aGUgYHB1Ymxpc2hgIGFuZCBgc3Vic2NyaWJlYCBtZXRob2RzLiBUaGlzIGlzIHVzZWZ1bCBpZiB5b3Ugd2FudCB0byBpbXBsZW1lbnQgeW91ciBvd24gc2VyaWFsaXplIGZ1bmN0aW9ucyBmb3IgY29udmVydGluZyBjdXN0b20gb2JqZWN0cyB0byB0b3BpYyBuYW1lcy4gQnkgZGVmYXVsdCwgaXQganVzdCBlY2hvZXMgdGhlIHRvcGljLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAqIGB0b3BpY2AgVG9waWMgdG8gcGFyc2UuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqUmV0dXJuIFZhbHVlKipcbiAgICAgICAgICpcbiAgICAgICAgICogKiAqU3RyaW5nKjogVGhpcyBmdW5jdGlvbiBzaG91bGQgcmV0dXJuIGEgc3RyaW5nIHRvcGljLlxuICAgICAgICAgKlxuICAgICAgICAgKiBAdHlwZSB7ZnVuY3Rpb259XG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSB0b3BpYyB0b3BpYyB0byByZXNvbHZlXG4gICAgICAgICAqIEByZXR1cm4ge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHRvcGljUmVzb2x2ZXI6IGZ1bmN0aW9uICh0b3BpYykge1xuICAgICAgICAgICAgcmV0dXJuIHRvcGljO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgaW5zdGFuY2Ugb2YgYCQuY29tZXRkYCB0byBob29rIG9udG8uXG4gICAgICAgICAqIEB0eXBlIHtvYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICB0cmFuc3BvcnQ6IG51bGxcbiAgICB9O1xuICAgIHRoaXMuY2hhbm5lbE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgZGVmYXVsdHMsIG9wdGlvbnMpO1xufTtcblxudmFyIG1ha2VOYW1lID0gZnVuY3Rpb24gKGNoYW5uZWxOYW1lLCB0b3BpYykge1xuICAgIC8vUmVwbGFjZSB0cmFpbGluZy9kb3VibGUgc2xhc2hlc1xuICAgIHZhciBuZXdOYW1lID0gKGNoYW5uZWxOYW1lID8gKGNoYW5uZWxOYW1lICsgJy8nICsgdG9waWMpIDogdG9waWMpLnJlcGxhY2UoL1xcL1xcLy9nLCAnLycpLnJlcGxhY2UoL1xcLyQvLCAnJyk7XG4gICAgcmV0dXJuIG5ld05hbWU7XG59O1xuXG5cbkNoYW5uZWwucHJvdG90eXBlID0gJC5leHRlbmQoQ2hhbm5lbC5wcm90b3R5cGUsIHtcblxuICAgIC8vIGZ1dHVyZSBmdW5jdGlvbmFsaXR5OlxuICAgIC8vICAgICAgLy8gU2V0IHRoZSBjb250ZXh0IGZvciB0aGUgY2FsbGJhY2tcbiAgICAvLyAgICAgIGNzLnN1YnNjcmliZSgncnVuJywgZnVuY3Rpb24gKCkgeyB0aGlzLmlubmVySFRNTCA9ICdUcmlnZ2VyZWQnfSwgZG9jdW1lbnQuYm9keSk7XG4gICAgIC8vXG4gICAgIC8vICAgICAgLy8gQ29udHJvbCB0aGUgb3JkZXIgb2Ygb3BlcmF0aW9ucyBieSBzZXR0aW5nIHRoZSBgcHJpb3JpdHlgXG4gICAgIC8vICAgICAgY3Muc3Vic2NyaWJlKCdydW4nLCBjYiwgdGhpcywge3ByaW9yaXR5OiA5fSk7XG4gICAgIC8vXG4gICAgIC8vICAgICAgLy8gT25seSBleGVjdXRlIHRoZSBjYWxsYmFjaywgYGNiYCwgaWYgdGhlIHZhbHVlIG9mIHRoZSBgcHJpY2VgIHZhcmlhYmxlIGlzIDUwXG4gICAgIC8vICAgICAgY3Muc3Vic2NyaWJlKCdydW4vdmFyaWFibGVzL3ByaWNlJywgY2IsIHRoaXMsIHtwcmlvcml0eTogMzAsIHZhbHVlOiA1MH0pO1xuICAgICAvL1xuICAgICAvLyAgICAgIC8vIE9ubHkgZXhlY3V0ZSB0aGUgY2FsbGJhY2ssIGBjYmAsIGlmIHRoZSB2YWx1ZSBvZiB0aGUgYHByaWNlYCB2YXJpYWJsZSBpcyBncmVhdGVyIHRoYW4gNTBcbiAgICAgLy8gICAgICBzdWJzY3JpYmUoJ3J1bi92YXJpYWJsZXMvcHJpY2UnLCBjYiwgdGhpcywge3ByaW9yaXR5OiAzMCwgdmFsdWU6ICc+NTAnfSk7XG4gICAgIC8vXG4gICAgIC8vICAgICAgLy8gT25seSBleGVjdXRlIHRoZSBjYWxsYmFjaywgYGNiYCwgaWYgdGhlIHZhbHVlIG9mIHRoZSBgcHJpY2VgIHZhcmlhYmxlIGlzIGV2ZW5cbiAgICAgLy8gICAgICBzdWJzY3JpYmUoJ3J1bi92YXJpYWJsZXMvcHJpY2UnLCBjYiwgdGhpcywge3ByaW9yaXR5OiAzMCwgdmFsdWU6IGZ1bmN0aW9uICh2YWwpIHtyZXR1cm4gdmFsICUgMiA9PT0gMH19KTtcblxuXG4gICAgLyoqXG4gICAgICogU3Vic2NyaWJlIHRvIGNoYW5nZXMgb24gYSB0b3BpYy5cbiAgICAgKlxuICAgICAqIFRoZSB0b3BpYyBzaG91bGQgaW5jbHVkZSB0aGUgZnVsbCBwYXRoIG9mIHRoZSBhY2NvdW50IGlkICgqKlRlYW0gSUQqKiBmb3IgdGVhbSBwcm9qZWN0cyksIHByb2plY3QgaWQsIGFuZCBncm91cCBuYW1lLiAoSW4gbW9zdCBjYXNlcywgaXQgaXMgc2ltcGxlciB0byB1c2UgdGhlIFtFcGljZW50ZXIgQ2hhbm5lbCBNYW5hZ2VyXSguLi9lcGljZW50ZXItY2hhbm5lbC1tYW5hZ2VyLykgaW5zdGVhZCwgaW4gd2hpY2ggY2FzZSB0aGlzIGlzIGNvbmZpZ3VyZWQgZm9yIHlvdS4pXG4gICAgICpcbiAgICAgKiAgKipFeGFtcGxlcyoqXG4gICAgICpcbiAgICAgKiAgICAgIHZhciBjYiA9IGZ1bmN0aW9uKHZhbCkgeyBjb25zb2xlLmxvZyh2YWwuZGF0YSk7IH07XG4gICAgICpcbiAgICAgKiAgICAgIC8vIFN1YnNjcmliZSB0byBjaGFuZ2VzIG9uIGEgdG9wLWxldmVsICdydW4nIHRvcGljXG4gICAgICogICAgICBjcy5zdWJzY3JpYmUoJy9hY21lLXNpbXVsYXRpb25zL3N1cHBseS1jaGFpbi1nYW1lL2ZhbGwtc2VtaW5hci9ydW4nLCBjYik7XG4gICAgICpcbiAgICAgKiAgICAgIC8vIFN1YnNjcmliZSB0byBjaGFuZ2VzIG9uIGNoaWxkcmVuIG9mIHRoZSAncnVuJyB0b3BpYy4gTm90ZSB0aGlzIHdpbGwgYWxzbyBiZSB0cmlnZ2VyZWQgZm9yIGNoYW5nZXMgdG8gcnVuLngueS56LlxuICAgICAqICAgICAgY3Muc3Vic2NyaWJlKCcvYWNtZS1zaW11bGF0aW9ucy9zdXBwbHktY2hhaW4tZ2FtZS9mYWxsLXNlbWluYXIvcnVuLyonLCBjYik7XG4gICAgICpcbiAgICAgKiAgICAgIC8vIFN1YnNjcmliZSB0byBjaGFuZ2VzIG9uIGJvdGggdGhlIHRvcC1sZXZlbCAncnVuJyB0b3BpYyBhbmQgaXRzIGNoaWxkcmVuXG4gICAgICogICAgICBjcy5zdWJzY3JpYmUoWycvYWNtZS1zaW11bGF0aW9ucy9zdXBwbHktY2hhaW4tZ2FtZS9mYWxsLXNlbWluYXIvcnVuJyxcbiAgICAgKiAgICAgICAgICAnL2FjbWUtc2ltdWxhdGlvbnMvc3VwcGx5LWNoYWluLWdhbWUvZmFsbC1zZW1pbmFyL3J1bi8qJ10sIGNiKTtcbiAgICAgKlxuICAgICAqICAgICAgLy8gU3Vic2NyaWJlIHRvIGNoYW5nZXMgb24gYSBwYXJ0aWN1bGFyIHZhcmlhYmxlXG4gICAgICogICAgICBzdWJzY3JpYmUoJy9hY21lLXNpbXVsYXRpb25zL3N1cHBseS1jaGFpbi1nYW1lL2ZhbGwtc2VtaW5hci9ydW4vdmFyaWFibGVzL3ByaWNlJywgY2IpO1xuICAgICAqXG4gICAgICpcbiAgICAgKiAqKlJldHVybiBWYWx1ZSoqXG4gICAgICpcbiAgICAgKiAqICpTdHJpbmcqIFJldHVybnMgYSB0b2tlbiB5b3UgY2FuIGxhdGVyIHVzZSB0byB1bnN1YnNjcmliZS5cbiAgICAgKlxuICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICogQHBhcmFtICB7U3RyaW5nfEFycmF5fSAgIHRvcGljICAgIExpc3Qgb2YgdG9waWNzIHRvIGxpc3RlbiBmb3IgY2hhbmdlcyBvbi5cbiAgICAgKiBAcGFyYW0gIHtGdW5jdGlvbn0gY2FsbGJhY2sgQ2FsbGJhY2sgZnVuY3Rpb24gdG8gZXhlY3V0ZS4gQ2FsbGJhY2sgaXMgY2FsbGVkIHdpdGggc2lnbmF0dXJlIGAoZXZ0LCBwYXlsb2FkLCBtZXRhZGF0YSlgLlxuICAgICAqIEBwYXJhbSAge09iamVjdH0gICBjb250ZXh0ICBDb250ZXh0IGluIHdoaWNoIHRoZSBgY2FsbGJhY2tgIGlzIGV4ZWN1dGVkLlxuICAgICAqIEBwYXJhbSAge09iamVjdH0gICBvcHRpb25zICAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAqIEBwYXJhbSAge051bWJlcn0gICBvcHRpb25zLnByaW9yaXR5ICBVc2VkIHRvIGNvbnRyb2wgb3JkZXIgb2Ygb3BlcmF0aW9ucy4gRGVmYXVsdHMgdG8gMC4gQ2FuIGJlIGFueSArdmUgb3IgLXZlIG51bWJlci5cbiAgICAgKiBAcGFyYW0gIHtTdHJpbmd8TnVtYmVyfEZ1bmN0aW9ufSAgIG9wdGlvbnMudmFsdWUgVGhlIGBjYWxsYmFja2AgaXMgb25seSB0cmlnZ2VyZWQgaWYgdGhpcyBjb25kaXRpb24gbWF0Y2hlcy4gU2VlIGV4YW1wbGVzIGZvciBkZXRhaWxzLlxuICAgICAqIEByZXR1cm4ge3N0cmluZ30gU3Vic2NyaXB0aW9uIElEXG4gICAgICovXG4gICAgc3Vic2NyaWJlOiBmdW5jdGlvbiAodG9waWMsIGNhbGxiYWNrLCBjb250ZXh0LCBvcHRpb25zKSB7XG5cbiAgICAgICAgdmFyIHRvcGljcyA9IFtdLmNvbmNhdCh0b3BpYyk7XG4gICAgICAgIHZhciBtZSA9IHRoaXM7XG4gICAgICAgIHZhciBzdWJzY3JpcHRpb25JZHMgPSBbXTtcbiAgICAgICAgdmFyIG9wdHMgPSBtZS5jaGFubmVsT3B0aW9ucztcblxuICAgICAgICBvcHRzLnRyYW5zcG9ydC5iYXRjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAkLmVhY2godG9waWNzLCBmdW5jdGlvbiAoaW5kZXgsIHRvcGljKSB7XG4gICAgICAgICAgICAgICAgdG9waWMgPSBtYWtlTmFtZShvcHRzLmJhc2UsIG9wdHMudG9waWNSZXNvbHZlcih0b3BpYykpO1xuICAgICAgICAgICAgICAgIHN1YnNjcmlwdGlvbklkcy5wdXNoKG9wdHMudHJhbnNwb3J0LnN1YnNjcmliZSh0b3BpYywgY2FsbGJhY2spKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIChzdWJzY3JpcHRpb25JZHNbMV0gPyBzdWJzY3JpcHRpb25JZHMgOiBzdWJzY3JpcHRpb25JZHNbMF0pO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBQdWJsaXNoIGRhdGEgdG8gYSB0b3BpYy5cbiAgICAgKlxuICAgICAqICoqRXhhbXBsZXMqKlxuICAgICAqXG4gICAgICogICAgICAvLyBTZW5kIGRhdGEgdG8gYWxsIHN1YnNjcmliZXJzIG9mIHRoZSAncnVuJyB0b3BpY1xuICAgICAqICAgICAgY3MucHVibGlzaCgnL2FjbWUtc2ltdWxhdGlvbnMvc3VwcGx5LWNoYWluLWdhbWUvZmFsbC1zZW1pbmFyL3J1bicsIHsgY29tcGxldGVkOiBmYWxzZSB9KTtcbiAgICAgKlxuICAgICAqICAgICAgLy8gU2VuZCBkYXRhIHRvIGFsbCBzdWJzY3JpYmVycyBvZiB0aGUgJ3J1bi92YXJpYWJsZXMnIHRvcGljXG4gICAgICogICAgICBjcy5wdWJsaXNoKCcvYWNtZS1zaW11bGF0aW9ucy9zdXBwbHktY2hhaW4tZ2FtZS9mYWxsLXNlbWluYXIvcnVuL3ZhcmlhYmxlcycsIHsgcHJpY2U6IDUwIH0pO1xuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSAge1N0cmluZ30gdG9waWMgVG9waWMgdG8gcHVibGlzaCB0by5cbiAgICAgKiBAcGFyYW0gIHsqfSBkYXRhICBEYXRhIHRvIHB1Ymxpc2ggdG8gdG9waWMuXG4gICAgICogQHJldHVybiB7QXJyYXkgfCBPYmplY3R9IFJlc3BvbnNlcyB0byBwdWJsaXNoZWQgZGF0YVxuICAgICAqXG4gICAgICovXG4gICAgcHVibGlzaDogZnVuY3Rpb24gKHRvcGljLCBkYXRhKSB7XG4gICAgICAgIHZhciB0b3BpY3MgPSBbXS5jb25jYXQodG9waWMpO1xuICAgICAgICB2YXIgbWUgPSB0aGlzO1xuICAgICAgICB2YXIgcmV0dXJuT2JqcyA9IFtdO1xuICAgICAgICB2YXIgb3B0cyA9IG1lLmNoYW5uZWxPcHRpb25zO1xuXG5cbiAgICAgICAgb3B0cy50cmFuc3BvcnQuYmF0Y2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgJC5lYWNoKHRvcGljcywgZnVuY3Rpb24gKGluZGV4LCB0b3BpYykge1xuICAgICAgICAgICAgICAgIHRvcGljID0gbWFrZU5hbWUob3B0cy5iYXNlLCBvcHRzLnRvcGljUmVzb2x2ZXIodG9waWMpKTtcbiAgICAgICAgICAgICAgICBpZiAodG9waWMuY2hhckF0KHRvcGljLmxlbmd0aCAtIDEpID09PSAnKicpIHtcbiAgICAgICAgICAgICAgICAgICAgdG9waWMgPSB0b3BpYy5yZXBsYWNlKC9cXCorJC8sICcnKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdZb3UgY2FuIGNhbm5vdCBwdWJsaXNoIHRvIGNoYW5uZWxzIHdpdGggd2lsZGNhcmRzLiBQdWJsaXNoaW5nIHRvICcsIHRvcGljLCAnaW5zdGVhZCcpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm5PYmpzLnB1c2gob3B0cy50cmFuc3BvcnQucHVibGlzaCh0b3BpYywgZGF0YSkpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gKHJldHVybk9ianNbMV0gPyByZXR1cm5PYmpzIDogcmV0dXJuT2Jqc1swXSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFVuc3Vic2NyaWJlIGZyb20gY2hhbmdlcyB0byBhIHRvcGljLlxuICAgICAqXG4gICAgICogKipFeGFtcGxlKipcbiAgICAgKlxuICAgICAqICAgICAgY3MudW5zdWJzY3JpYmUoJ3NhbXBsZVRva2VuJyk7XG4gICAgICpcbiAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAqIEBwYXJhbSAge1N0cmluZ30gdG9rZW4gVGhlIHRva2VuIGZvciB0b3BpYyBpcyByZXR1cm5lZCB3aGVuIHlvdSBpbml0aWFsbHkgc3Vic2NyaWJlLiBQYXNzIGl0IGhlcmUgdG8gdW5zdWJzY3JpYmUgZnJvbSB0aGF0IHRvcGljLlxuICAgICAqIEByZXR1cm4ge09iamVjdH0gcmVmZXJlbmNlIHRvIGN1cnJlbnQgaW5zdGFuY2VcbiAgICAgKi9cbiAgICB1bnN1YnNjcmliZTogZnVuY3Rpb24gKHRva2VuKSB7XG4gICAgICAgIHRoaXMuY2hhbm5lbE9wdGlvbnMudHJhbnNwb3J0LnVuc3Vic2NyaWJlKHRva2VuKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFN0YXJ0IGxpc3RlbmluZyBmb3IgZXZlbnRzIG9uIHRoaXMgaW5zdGFuY2UuIFNpZ25hdHVyZSBpcyBzYW1lIGFzIGZvciBqUXVlcnkgRXZlbnRzOiBodHRwOi8vYXBpLmpxdWVyeS5jb20vb24vLlxuICAgICAqXG4gICAgICogU3VwcG9ydGVkIGV2ZW50cyBhcmU6IGBjb25uZWN0YCwgYGRpc2Nvbm5lY3RgLCBgc3Vic2NyaWJlYCwgYHVuc3Vic2NyaWJlYCwgYHB1Ymxpc2hgLCBgZXJyb3JgLlxuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudCBUaGUgZXZlbnQgdHlwZS4gU2VlIG1vcmUgZGV0YWlsIGF0IGpRdWVyeSBFdmVudHM6IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS9vbi8uXG4gICAgICovXG4gICAgb246IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICAkKHRoaXMpLm9uLmFwcGx5KCQodGhpcyksIGFyZ3VtZW50cyk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFN0b3AgbGlzdGVuaW5nIGZvciBldmVudHMgb24gdGhpcyBpbnN0YW5jZS4gU2lnbmF0dXJlIGlzIHNhbWUgYXMgZm9yIGpRdWVyeSBFdmVudHM6IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS9vZmYvLlxuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudCBUaGUgZXZlbnQgdHlwZS4gU2VlIG1vcmUgZGV0YWlsIGF0IGpRdWVyeSBFdmVudHM6IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS9vZmYvLlxuICAgICAqL1xuICAgIG9mZjogZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICQodGhpcykub2ZmLmFwcGx5KCQodGhpcyksIGFyZ3VtZW50cyk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFRyaWdnZXIgZXZlbnRzIGFuZCBleGVjdXRlIGhhbmRsZXJzLiBTaWduYXR1cmUgaXMgc2FtZSBhcyBmb3IgalF1ZXJ5IEV2ZW50czogaHR0cDovL2FwaS5qcXVlcnkuY29tL3RyaWdnZXIvLlxuICAgICAqXG4gICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudCBUaGUgZXZlbnQgdHlwZS4gU2VlIG1vcmUgZGV0YWlsIGF0IGpRdWVyeSBFdmVudHM6IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS90cmlnZ2VyLy5cbiAgICAgKi9cbiAgICB0cmlnZ2VyOiBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgJCh0aGlzKS50cmlnZ2VyLmFwcGx5KCQodGhpcyksIGFyZ3VtZW50cyk7XG4gICAgfVxuXG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBDaGFubmVsO1xuIiwiLyoqXG4gKiBAY2xhc3MgQ29uZmlndXJhdGlvblNlcnZpY2VcbiAqXG4gKiBBbGwgc2VydmljZXMgdGFrZSBpbiBhIGNvbmZpZ3VyYXRpb24gc2V0dGluZ3Mgb2JqZWN0IHRvIGNvbmZpZ3VyZSB0aGVtc2VsdmVzLiBBIEpTIGhhc2gge30gaXMgYSB2YWxpZCBjb25maWd1cmF0aW9uIG9iamVjdCwgYnV0IG9wdGlvbmFsbHkgeW91IGNhbiB1c2UgdGhlIGNvbmZpZ3VyYXRpb24gc2VydmljZSB0byB0b2dnbGUgY29uZmlncyBiYXNlZCBvbiB0aGUgZW52aXJvbm1lbnRcbiAqXG4gKiBAZXhhbXBsZVxuICogICAgIHZhciBjcyA9IHJlcXVpcmUoJ2NvbmZpZ3VyYXRpb24tc2VydmljZScpKHtcbiAqICAgICAgICAgIGRldjogeyAvL2Vudmlyb25tZW50XG4gICAgICAgICAgICAgICAgcG9ydDogMzAwMCxcbiAgICAgICAgICAgICAgICBob3N0OiAnbG9jYWxob3N0JyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBwcm9kOiB7XG4gICAgICAgICAgICAgICAgcG9ydDogODA4MCxcbiAgICAgICAgICAgICAgICBob3N0OiAnYXBpLmZvcmlvLmNvbScsXG4gICAgICAgICAgICAgICAgbG9nTGV2ZWw6ICdub25lJ1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGxvZ0xldmVsOiAnREVCVUcnIC8vZ2xvYmFsXG4gKiAgICAgfSk7XG4gKlxuICogICAgICBjcy5nZXQoJ2xvZ0xldmVsJyk7IC8vcmV0dXJucyAnREVCVUcnXG4gKlxuICogICAgICBjcy5zZXRFbnYoJ2RldicpO1xuICogICAgICBjcy5nZXQoJ2xvZ0xldmVsJyk7IC8vcmV0dXJucyAnREVCVUcnXG4gKlxuICogICAgICBjcy5zZXRFbnYoJ3Byb2QnKTtcbiAqICAgICAgY3MuZ2V0KCdsb2dMZXZlbCcpOyAvL3JldHVybnMgJ25vbmUnXG4gKlxuICovXG5cbid1c2Ugc3RyaWN0JztcbnZhciB1cmxTZXJ2aWNlID0gcmVxdWlyZSgnLi91cmwtY29uZmlnLXNlcnZpY2UnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgLy9UT0RPOiBFbnZpcm9ubWVudHNcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIGxvZ0xldmVsOiAnTk9ORSdcbiAgICB9O1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBkZWZhdWx0cywgY29uZmlnKTtcbiAgICBzZXJ2aWNlT3B0aW9ucy5zZXJ2ZXIgPSB1cmxTZXJ2aWNlKHNlcnZpY2VPcHRpb25zLnNlcnZlcik7XG5cbiAgICByZXR1cm4ge1xuXG4gICAgICAgIGRhdGE6IHNlcnZpY2VPcHRpb25zLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBTZXQgdGhlIGVudmlyb25tZW50IGtleSB0byBnZXQgY29uZmlndXJhdGlvbiBvcHRpb25zIGZyb21cbiAgICAgICAgICogQHBhcmFtIHsgc3RyaW5nfSBlbnZcbiAgICAgICAgICovXG4gICAgICAgIHNldEVudjogZnVuY3Rpb24gKGVudikge1xuXG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCBjb25maWd1cmF0aW9uLlxuICAgICAgICAgKiBAcGFyYW0gIHsgc3RyaW5nfSBwcm9wZXJ0eSBvcHRpb25hbFxuICAgICAgICAgKiBAcmV0dXJuIHsqfSAgICAgICAgICBWYWx1ZSBvZiBwcm9wZXJ0eSBpZiBzcGVjaWZpZWQsIHRoZSBlbnRpcmUgY29uZmlnIG9iamVjdCBvdGhlcndpc2VcbiAgICAgICAgICovXG4gICAgICAgIGdldDogZnVuY3Rpb24gKHByb3BlcnR5KSB7XG4gICAgICAgICAgICByZXR1cm4gc2VydmljZU9wdGlvbnNbcHJvcGVydHldO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBTZXQgY29uZmlndXJhdGlvbi5cbiAgICAgICAgICogQHBhcmFtICB7IHN0cmluZ3xPYmplY3R9IGtleSBpZiBhIGtleSBpcyBwcm92aWRlZCwgc2V0IGEga2V5IHRvIHRoYXQgdmFsdWUuIE90aGVyd2lzZSBtZXJnZSBvYmplY3Qgd2l0aCBjdXJyZW50IGNvbmZpZ1xuICAgICAgICAgKiBAcGFyYW0gIHsqfSB2YWx1ZSAgdmFsdWUgZm9yIHByb3ZpZGVkIGtleVxuICAgICAgICAgKi9cbiAgICAgICAgc2V0OiBmdW5jdGlvbiAoa2V5LCB2YWx1ZSkge1xuICAgICAgICAgICAgc2VydmljZU9wdGlvbnNba2V5XSA9IHZhbHVlO1xuICAgICAgICB9XG4gICAgfTtcbn07XG5cbiIsIi8qKlxuICogIyMgRGF0YSBBUEkgU2VydmljZVxuICpcbiAqIFRoZSBEYXRhIEFQSSBTZXJ2aWNlIGFsbG93cyB5b3UgdG8gY3JlYXRlLCBhY2Nlc3MsIGFuZCBtYW5pcHVsYXRlIGRhdGEgcmVsYXRlZCB0byBhbnkgb2YgeW91ciBwcm9qZWN0cy4gRGF0YSBhcmUgb3JnYW5pemVkIGluIGNvbGxlY3Rpb25zLiBFYWNoIGNvbGxlY3Rpb24gY29udGFpbnMgYSBkb2N1bWVudDsgZWFjaCBlbGVtZW50IG9mIHRoaXMgdG9wLWxldmVsIGRvY3VtZW50IGlzIGEgSlNPTiBvYmplY3QuIChTZWUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBvbiB0aGUgdW5kZXJseWluZyBbRGF0YSBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9kYXRhX2FwaS8pLilcbiAqXG4gKiBBbGwgQVBJIGNhbGxzIHRha2UgaW4gYW4gXCJvcHRpb25zXCIgb2JqZWN0IGFzIHRoZSBsYXN0IHBhcmFtZXRlci4gVGhlIG9wdGlvbnMgY2FuIGJlIHVzZWQgdG8gZXh0ZW5kL292ZXJyaWRlIHRoZSBEYXRhIEFQSSBTZXJ2aWNlIGRlZmF1bHRzLiBJbiBwYXJ0aWN1bGFyLCB0aGVyZSBhcmUgdGhyZWUgcmVxdWlyZWQgcGFyYW1ldGVycyB3aGVuIHlvdSBpbnN0YW50aWF0ZSB0aGUgRGF0YSBTZXJ2aWNlOlxuICpcbiAqICogYGFjY291bnRgOiBFcGljZW50ZXIgYWNjb3VudCBpZCAoKipUZWFtIElEKiogZm9yIHRlYW0gcHJvamVjdHMsICoqVXNlciBJRCoqIGZvciBwZXJzb25hbCBwcm9qZWN0cykuXG4gKiAqIGBwcm9qZWN0YDogRXBpY2VudGVyIHByb2plY3QgaWQuXG4gKiAqIGByb290YDogVGhlIHRoZSBuYW1lIG9mIHRoZSBjb2xsZWN0aW9uLiBJZiB5b3UgaGF2ZSBtdWx0aXBsZSBjb2xsZWN0aW9ucyB3aXRoaW4gZWFjaCBvZiB5b3VyIHByb2plY3RzLCB5b3UgY2FuIGFsc28gcGFzcyB0aGUgY29sbGVjdGlvbiBuYW1lIGFzIGFuIG9wdGlvbiBmb3IgZWFjaCBjYWxsLlxuICpcbiAqICAgICAgIHZhciBkcyA9IG5ldyBGLnNlcnZpY2UuRGF0YSh7XG4gKiAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gKiAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICogICAgICAgICAgcm9vdDogJ3N1cnZleS1yZXNwb25zZXMnLFxuICogICAgICAgICAgc2VydmVyOiB7IGhvc3Q6ICdhcGkuZm9yaW8uY29tJyB9XG4gKiAgICAgICB9KTtcbiAqICAgICAgIGRzLnNhdmVBcygndXNlcjEnLFxuICogICAgICAgICAgeyAncXVlc3Rpb24xJzogMiwgJ3F1ZXN0aW9uMic6IDEwLFxuICogICAgICAgICAgJ3F1ZXN0aW9uMyc6IGZhbHNlLCAncXVlc3Rpb240JzogJ3NvbWV0aW1lcycgfSApO1xuICogICAgICAgZHMuc2F2ZUFzKCd1c2VyMicsXG4gKiAgICAgICAgICB7ICdxdWVzdGlvbjEnOiAzLCAncXVlc3Rpb24yJzogOCxcbiAqICAgICAgICAgICdxdWVzdGlvbjMnOiB0cnVlLCAncXVlc3Rpb240JzogJ2Fsd2F5cycgfSApO1xuICogICAgICAgZHMucXVlcnkoJycseyAncXVlc3Rpb24yJzogeyAnJGd0JzogOX0gfSk7XG4gKlxuICogTm90ZSB0aGF0IGluIGFkZGl0aW9uIHRvIHRoZSBgYWNjb3VudGAsIGBwcm9qZWN0YCwgYW5kIGByb290YCwgdGhlIERhdGEgU2VydmljZSBwYXJhbWV0ZXJzIG9wdGlvbmFsbHkgaW5jbHVkZSBhIGBzZXJ2ZXJgIG9iamVjdCwgd2hvc2UgYGhvc3RgIGZpZWxkIGNvbnRhaW5zIHRoZSBVUkkgb2YgdGhlIEZvcmlvIHNlcnZlci4gVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHNldCwgYnV0IHlvdSBjYW4gcGFzcyBpdCBleHBsaWNpdGx5IGlmIGRlc2lyZWQuIEl0IGlzIG1vc3QgY29tbW9ubHkgdXNlZCBmb3IgY2xhcml0eSB3aGVuIHlvdSBhcmUgW2hvc3RpbmcgYW4gRXBpY2VudGVyIHByb2plY3Qgb24geW91ciBvd24gc2VydmVyXSguLi8uLi8uLi9ob3dfdG8vc2VsZl9ob3N0aW5nLykuXG4gKi9cblxuJ3VzZSBzdHJpY3QnO1xuXG52YXIgQ29uZmlnU2VydmljZSA9IHJlcXVpcmUoJy4vY29uZmlndXJhdGlvbi1zZXJ2aWNlJyk7XG52YXIgcXV0aWwgPSByZXF1aXJlKCcuLi91dGlsL3F1ZXJ5LXV0aWwnKTtcbnZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBOYW1lIG9mIGNvbGxlY3Rpb24uIFJlcXVpcmVkLiBEZWZhdWx0cyB0byBgL2AsIHRoYXQgaXMsIHRoZSByb290IGxldmVsIG9mIHlvdXIgcHJvamVjdCBhdCBgZm9yaW8uY29tL2FwcC95b3VyLWFjY291bnQtaWQveW91ci1wcm9qZWN0LWlkL2AsIGJ1dCBtdXN0IGJlIHNldCB0byBhIGNvbGxlY3Rpb24gbmFtZS5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHJvb3Q6ICcvJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFjY291bnQgaWQuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykgb3IgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFjY291bnQ6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHByb2plY3QgaWQuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHByb2plY3Q6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogRm9yIG9wZXJhdGlvbnMgdGhhdCByZXF1aXJlIGF1dGhlbnRpY2F0aW9uLCBwYXNzIGluIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiAoZGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nKS4gSWYgdGhlIHVzZXIgaXMgYWxyZWFkeSBsb2dnZWQgaW4gdG8gRXBpY2VudGVyLCB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gaXMgYWxyZWFkeSBzZXQgaW4gYSBjb29raWUgYW5kIGF1dG9tYXRpY2FsbHkgbG9hZGVkIGZyb20gdGhlcmUuIChTZWUgW21vcmUgYmFja2dyb3VuZCBvbiBhY2Nlc3MgdG9rZW5zXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pKS5cbiAgICAgICAgICogQHNlZSBbQXV0aGVudGljYXRpb24gQVBJIFNlcnZpY2VdKC4uL2F1dGgtYXBpLXNlcnZpY2UvKSBmb3IgZ2V0dGluZyB0b2tlbnMuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICB0b2tlbjogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8vT3B0aW9ucyB0byBwYXNzIG9uIHRvIHRoZSB1bmRlcmx5aW5nIHRyYW5zcG9ydCBsYXllclxuICAgICAgICB0cmFuc3BvcnQ6IHt9XG4gICAgfTtcbiAgICB0aGlzLnNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKCk7XG4gICAgdmFyIHNlcnZpY2VPcHRpb25zID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zKGRlZmF1bHRzLCBjb25maWcpO1xuXG4gICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5hY2NvdW50KSB7XG4gICAgICAgIHVybENvbmZpZy5hY2NvdW50UGF0aCA9IHNlcnZpY2VPcHRpb25zLmFjY291bnQ7XG4gICAgfVxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy5wcm9qZWN0KSB7XG4gICAgICAgIHVybENvbmZpZy5wcm9qZWN0UGF0aCA9IHNlcnZpY2VPcHRpb25zLnByb2plY3Q7XG4gICAgfVxuXG4gICAgdmFyIGdldFVSTCA9IGZ1bmN0aW9uIChrZXksIHJvb3QpIHtcbiAgICAgICAgaWYgKCFyb290KSB7XG4gICAgICAgICAgICByb290ID0gc2VydmljZU9wdGlvbnMucm9vdDtcbiAgICAgICAgfVxuICAgICAgICB2YXIgdXJsID0gdXJsQ29uZmlnLmdldEFQSVBhdGgoJ2RhdGEnKSArIHF1dGlsLmFkZFRyYWlsaW5nU2xhc2gocm9vdCk7XG4gICAgICAgIGlmIChrZXkpIHtcbiAgICAgICAgICAgIHVybCArPSBxdXRpbC5hZGRUcmFpbGluZ1NsYXNoKGtleSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHVybDtcbiAgICB9O1xuXG4gICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydCwge1xuICAgICAgICB1cmw6IGdldFVSTFxuICAgIH0pO1xuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICBodHRwT3B0aW9ucy5oZWFkZXJzID0ge1xuICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogJ0JlYXJlciAnICsgc2VydmljZU9wdGlvbnMudG9rZW5cbiAgICAgICAgfTtcbiAgICB9XG4gICAgdmFyIGh0dHAgPSBuZXcgVHJhbnNwb3J0RmFjdG9yeShodHRwT3B0aW9ucyk7XG5cbiAgICB2YXIgcHVibGljQVBJID0ge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBTZWFyY2ggZm9yIGRhdGEgd2l0aGluIGEgY29sbGVjdGlvbi5cbiAgICAgICAgICpcbiAgICAgICAgICogU2VhcmNoaW5nIHVzaW5nIGNvbXBhcmlzb24gb3IgbG9naWNhbCBvcGVyYXRvcnMgKGFzIG9wcG9zZWQgdG8gZXhhY3QgbWF0Y2hlcykgcmVxdWlyZXMgTW9uZ29EQiBzeW50YXguIFNlZSB0aGUgdW5kZXJseWluZyBbRGF0YSBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9kYXRhX2FwaS8jc2VhcmNoaW5nKSBmb3IgYWRkaXRpb25hbCBkZXRhaWxzLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGVzKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyByZXF1ZXN0IGFsbCBkYXRhIGFzc29jaWF0ZWQgd2l0aCBkb2N1bWVudCAndXNlcjEnXG4gICAgICAgICAqICAgICAgZHMucXVlcnkoJ3VzZXIxJyk7XG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gZXhhY3QgbWF0Y2hpbmc6XG4gICAgICAgICAqICAgICAgLy8gcmVxdWVzdCBhbGwgZG9jdW1lbnRzIGluIGNvbGxlY3Rpb24gd2hlcmUgJ3F1ZXN0aW9uMicgaXMgOVxuICAgICAgICAgKiAgICAgIGRzLnF1ZXJ5KCcnLCB7ICdxdWVzdGlvbjInOiA5fSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gY29tcGFyaXNvbiBvcGVyYXRvcnM6XG4gICAgICAgICAqICAgICAgLy8gcmVxdWVzdCBhbGwgZG9jdW1lbnRzIGluIGNvbGxlY3Rpb25cbiAgICAgICAgICogICAgICAvLyB3aGVyZSAncXVlc3Rpb24yJyBpcyBncmVhdGVyIHRoYW4gOVxuICAgICAgICAgKiAgICAgIGRzLnF1ZXJ5KCcnLCB7ICdxdWVzdGlvbjInOiB7ICckZ3QnOiA5fSB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBsb2dpY2FsIG9wZXJhdG9yczpcbiAgICAgICAgICogICAgICAvLyByZXF1ZXN0IGFsbCBkb2N1bWVudHMgaW4gY29sbGVjdGlvblxuICAgICAgICAgKiAgICAgIC8vIHdoZXJlICdxdWVzdGlvbjInIGlzIGxlc3MgdGhhbiAxMCwgYW5kICdxdWVzdGlvbjMnIGlzIGZhbHNlXG4gICAgICAgICAqICAgICAgZHMucXVlcnkoJycsIHsgJyRhbmQnOiBbIHsgJ3F1ZXN0aW9uMic6IHsgJyRsdCc6MTB9IH0sIHsgJ3F1ZXN0aW9uMyc6IGZhbHNlIH1dIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIHJlZ3VsYXIgZXhwcmVzc3Npb25zOiB1c2UgYW55IFBlcmwtY29tcGF0aWJsZSByZWd1bGFyIGV4cHJlc3Npb25zXG4gICAgICAgICAqICAgICAgLy8gcmVxdWVzdCBhbGwgZG9jdW1lbnRzIGluIGNvbGxlY3Rpb25cbiAgICAgICAgICogICAgICAvLyB3aGVyZSAncXVlc3Rpb241JyBjb250YWlucyB0aGUgc3RyaW5nICcuKmRheSdcbiAgICAgICAgICogICAgICBkcy5xdWVyeSgnJywgeyAncXVlc3Rpb241JzogeyAnJHJlZ2V4JzogJy4qZGF5JyB9IH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30ga2V5IFRoZSBuYW1lIG9mIHRoZSBkb2N1bWVudCB0byBzZWFyY2guIFBhc3MgdGhlIGVtcHR5IHN0cmluZyAoJycpIHRvIHNlYXJjaCB0aGUgZW50aXJlIGNvbGxlY3Rpb24uXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBxdWVyeSBUaGUgcXVlcnkgb2JqZWN0LiBGb3IgZXhhY3QgbWF0Y2hpbmcsIHRoaXMgb2JqZWN0IGNvbnRhaW5zIHRoZSBmaWVsZCBuYW1lIGFuZCBmaWVsZCB2YWx1ZSB0byBtYXRjaC4gRm9yIG1hdGNoaW5nIGJhc2VkIG9uIGNvbXBhcmlzb24sIHRoaXMgb2JqZWN0IGNvbnRhaW5zIHRoZSBmaWVsZCBuYW1lIGFuZCB0aGUgY29tcGFyaXNvbiBleHByZXNzaW9uLiBGb3IgbWF0Y2hpbmcgYmFzZWQgb24gbG9naWNhbCBvcGVyYXRvcnMsIHRoaXMgb2JqZWN0IGNvbnRhaW5zIGFuIGV4cHJlc3Npb24gdXNpbmcgTW9uZ29EQiBzeW50YXguIFNlZSB0aGUgdW5kZXJseWluZyBbRGF0YSBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9kYXRhX2FwaS8jc2VhcmNoaW5nKSBmb3IgYWRkaXRpb25hbCBleGFtcGxlcy5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG91dHB1dE1vZGlmaWVyIChPcHRpb25hbCkgQXZhaWxhYmxlIGZpZWxkcyBpbmNsdWRlOiBgc3RhcnRyZWNvcmRgLCBgZW5kcmVjb3JkYCwgYHNvcnRgLCBhbmQgYGRpcmVjdGlvbmAgKGBhc2NgIG9yIGBkZXNjYCkuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9IFxuICAgICAgICAgKi9cbiAgICAgICAgcXVlcnk6IGZ1bmN0aW9uIChrZXksIHF1ZXJ5LCBvdXRwdXRNb2RpZmllciwgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIHBhcmFtcyA9ICQuZXh0ZW5kKHRydWUsIHsgcTogcXVlcnkgfSwgb3V0cHV0TW9kaWZpZXIpO1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIGh0dHBPcHRpb25zLnVybCA9IGdldFVSTChrZXksIGh0dHBPcHRpb25zLnJvb3QpO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KHBhcmFtcywgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBTYXZlIGRhdGEgaW4gYW4gYW5vbnltb3VzIGRvY3VtZW50IHdpdGhpbiB0aGUgY29sbGVjdGlvbi5cbiAgICAgICAgICpcbiAgICAgICAgICogVGhlIGByb290YCBvZiB0aGUgY29sbGVjdGlvbiBtdXN0IGJlIHNwZWNpZmllZC4gQnkgZGVmYXVsdCB0aGUgYHJvb3RgIGlzIHRha2VuIGZyb20gdGhlIERhdGEgU2VydmljZSBjb25maWd1cmF0aW9uIG9wdGlvbnM7IHlvdSBjYW4gYWxzbyBwYXNzIHRoZSBgcm9vdGAgdG8gdGhlIGBzYXZlYCBjYWxsIGV4cGxpY2l0bHkgYnkgb3ZlcnJpZGluZyB0aGUgb3B0aW9ucyAodGhpcmQgcGFyYW1ldGVyKS5cbiAgICAgICAgICpcbiAgICAgICAgICogKEFkZGl0aW9uYWwgYmFja2dyb3VuZDogRG9jdW1lbnRzIGFyZSB0b3AtbGV2ZWwgZWxlbWVudHMgd2l0aGluIGEgY29sbGVjdGlvbi4gQ29sbGVjdGlvbnMgbXVzdCBiZSB1bmlxdWUgd2l0aGluIHRoaXMgYWNjb3VudCAodGVhbSBvciBwZXJzb25hbCBhY2NvdW50KSBhbmQgcHJvamVjdCBhbmQgYXJlIHNldCB3aXRoIHRoZSBgcm9vdGAgZmllbGQgaW4gdGhlIGBvcHRpb25gIHBhcmFtZXRlci4gU2VlIHRoZSB1bmRlcmx5aW5nIFtEYXRhIEFQSV0oLi4vLi4vLi4vcmVzdF9hcGlzL2RhdGFfYXBpLykgZm9yIG1vcmUgaW5mb3JtYXRpb24uIFRoZSBgc2F2ZWAgbWV0aG9kIGlzIG1ha2luZyBhIGBQT1NUYCByZXF1ZXN0LilcbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBDcmVhdGUgYSBuZXcgZG9jdW1lbnQsIHdpdGggb25lIGVsZW1lbnQsIGF0IHRoZSBkZWZhdWx0IHJvb3QgbGV2ZWxcbiAgICAgICAgICogICAgICBkcy5zYXZlKCdxdWVzdGlvbjEnLCAneWVzJyk7XG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gQ3JlYXRlIGEgbmV3IGRvY3VtZW50LCB3aXRoIHR3byBlbGVtZW50cywgYXQgdGhlIGRlZmF1bHQgcm9vdCBsZXZlbFxuICAgICAgICAgKiAgICAgIGRzLnNhdmUoeyBxdWVzdGlvbjE6J3llcycsIHF1ZXN0aW9uMjogMzIgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gQ3JlYXRlIGEgbmV3IGRvY3VtZW50LCB3aXRoIHR3byBlbGVtZW50cywgYXQgYC9zdHVkZW50cy9gXG4gICAgICAgICAqICAgICAgZHMuc2F2ZSh7IG5hbWU6J0pvaG4nLCBjbGFzc05hbWU6ICdDUzEwMScgfSwgeyByb290OiAnc3R1ZGVudHMnIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ3xPYmplY3R9IGtleSBJZiBga2V5YCBpcyBhIHN0cmluZywgaXQgaXMgdGhlIGlkIG9mIHRoZSBlbGVtZW50IHRvIHNhdmUgKGNyZWF0ZSkgaW4gdGhpcyBkb2N1bWVudC4gSWYgYGtleWAgaXMgYW4gb2JqZWN0LCB0aGUgb2JqZWN0IGlzIHRoZSBkYXRhIHRvIHNhdmUgKGNyZWF0ZSkgaW4gdGhpcyBkb2N1bWVudC4gSW4gYm90aCBjYXNlcywgdGhlIGlkIGZvciB0aGUgZG9jdW1lbnQgaXMgZ2VuZXJhdGVkIGF1dG9tYXRpY2FsbHkuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSB2YWx1ZSAoT3B0aW9uYWwpIFRoZSBkYXRhIHRvIHNhdmUuIElmIGBrZXlgIGlzIGEgc3RyaW5nLCB0aGlzIGlzIHRoZSB2YWx1ZSB0byBzYXZlLiBJZiBga2V5YCBpcyBhbiBvYmplY3QsIHRoZSB2YWx1ZShzKSB0byBzYXZlIGFyZSBhbHJlYWR5IHBhcnQgb2YgYGtleWAgYW5kIHRoaXMgYXJndW1lbnQgaXMgbm90IHJlcXVpcmVkLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLiBJZiB5b3Ugd2FudCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdCBgcm9vdGAgb2YgdGhlIGNvbGxlY3Rpb24sIGRvIHNvIGhlcmUuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9IFxuICAgICAgICAgKi9cbiAgICAgICAgc2F2ZTogZnVuY3Rpb24gKGtleSwgdmFsdWUsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBhdHRycztcbiAgICAgICAgICAgIGlmICh0eXBlb2Yga2V5ID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgICAgIGF0dHJzID0ga2V5O1xuICAgICAgICAgICAgICAgIG9wdGlvbnMgPSB2YWx1ZTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgKGF0dHJzID0ge30pW2tleV0gPSB2YWx1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICBodHRwT3B0aW9ucy51cmwgPSBnZXRVUkwoJycsIGh0dHBPcHRpb25zLnJvb3QpO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wb3N0KGF0dHJzLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNhdmUgKGNyZWF0ZSBvciByZXBsYWNlKSBkYXRhIGluIGEgbmFtZWQgZG9jdW1lbnQgb3IgZWxlbWVudCB3aXRoaW4gdGhlIGNvbGxlY3Rpb24uIFxuICAgICAgICAgKiBcbiAgICAgICAgICogVGhlIGByb290YCBvZiB0aGUgY29sbGVjdGlvbiBtdXN0IGJlIHNwZWNpZmllZC4gQnkgZGVmYXVsdCB0aGUgYHJvb3RgIGlzIHRha2VuIGZyb20gdGhlIERhdGEgU2VydmljZSBjb25maWd1cmF0aW9uIG9wdGlvbnM7IHlvdSBjYW4gYWxzbyBwYXNzIHRoZSBgcm9vdGAgdG8gdGhlIGBzYXZlQXNgIGNhbGwgZXhwbGljaXRseSBieSBvdmVycmlkaW5nIHRoZSBvcHRpb25zICh0aGlyZCBwYXJhbWV0ZXIpLlxuICAgICAgICAgKlxuICAgICAgICAgKiBPcHRpb25hbGx5LCB0aGUgbmFtZWQgZG9jdW1lbnQgb3IgZWxlbWVudCBjYW4gaW5jbHVkZSBwYXRoIGluZm9ybWF0aW9uLCBzbyB0aGF0IHlvdSBhcmUgc2F2aW5nIGp1c3QgcGFydCBvZiB0aGUgZG9jdW1lbnQuXG4gICAgICAgICAqXG4gICAgICAgICAqIChBZGRpdGlvbmFsIGJhY2tncm91bmQ6IERvY3VtZW50cyBhcmUgdG9wLWxldmVsIGVsZW1lbnRzIHdpdGhpbiBhIGNvbGxlY3Rpb24uIENvbGxlY3Rpb25zIG11c3QgYmUgdW5pcXVlIHdpdGhpbiB0aGlzIGFjY291bnQgKHRlYW0gb3IgcGVyc29uYWwgYWNjb3VudCkgYW5kIHByb2plY3QgYW5kIGFyZSBzZXQgd2l0aCB0aGUgYHJvb3RgIGZpZWxkIGluIHRoZSBgb3B0aW9uYCBwYXJhbWV0ZXIuIFNlZSB0aGUgdW5kZXJseWluZyBbRGF0YSBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9kYXRhX2FwaS8pIGZvciBtb3JlIGluZm9ybWF0aW9uLiBUaGUgYHNhdmVBc2AgbWV0aG9kIGlzIG1ha2luZyBhIGBQVVRgIHJlcXVlc3QuKVxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIENyZWF0ZSAob3IgcmVwbGFjZSkgdGhlIGB1c2VyMWAgZG9jdW1lbnQgYXQgdGhlIGRlZmF1bHQgcm9vdCBsZXZlbC5cbiAgICAgICAgICogICAgICAvLyBOb3RlIHRoYXQgdGhpcyByZXBsYWNlcyBhbnkgZXhpc3RpbmcgY29udGVudCBpbiB0aGUgYHVzZXIxYCBkb2N1bWVudC5cbiAgICAgICAgICogICAgICBkcy5zYXZlQXMoJ3VzZXIxJyxcbiAgICAgICAgICogICAgICAgICAgeyAncXVlc3Rpb24xJzogMiwgJ3F1ZXN0aW9uMic6IDEwLFxuICAgICAgICAgKiAgICAgICAgICAgJ3F1ZXN0aW9uMyc6IGZhbHNlLCAncXVlc3Rpb240JzogJ3NvbWV0aW1lcycgfSApO1xuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIENyZWF0ZSAob3IgcmVwbGFjZSkgdGhlIGBzdHVkZW50MWAgZG9jdW1lbnQgYXQgdGhlIGBzdHVkZW50c2Agcm9vdCwgXG4gICAgICAgICAqICAgICAgLy8gdGhhdCBpcywgdGhlIGRhdGEgYXQgYC9zdHVkZW50cy9zdHVkZW50MS9gLlxuICAgICAgICAgKiAgICAgIC8vIE5vdGUgdGhhdCB0aGlzIHJlcGxhY2VzIGFueSBleGlzdGluZyBjb250ZW50IGluIHRoZSBgL3N0dWRlbnRzL3N0dWRlbnQxL2AgZG9jdW1lbnQuXG4gICAgICAgICAqICAgICAgLy8gSG93ZXZlciwgdGhpcyB3aWxsIGtlZXAgZXhpc3RpbmcgY29udGVudCBpbiBvdGhlciBwYXRocyBvZiB0aGlzIGNvbGxlY3Rpb24uXG4gICAgICAgICAqICAgICAgLy8gRm9yIGV4YW1wbGUsIHRoZSBkYXRhIGF0IGAvc3R1ZGVudHMvc3R1ZGVudDIvYCBpcyB1bmNoYW5nZWQgYnkgdGhpcyBjYWxsLlxuICAgICAgICAgKiAgICAgIGRzLnNhdmVBcygnc3R1ZGVudDEnLFxuICAgICAgICAgKiAgICAgICAgICB7IGZpcnN0TmFtZTogJ2pvaG4nLCBsYXN0TmFtZTogJ3NtaXRoJyB9LFxuICAgICAgICAgKiAgICAgICAgICB7IHJvb3Q6ICdzdHVkZW50cycgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gQ3JlYXRlIChvciByZXBsYWNlKSB0aGUgYG1nbXQxMDAvZ3JvdXBCYCBkb2N1bWVudCBhdCB0aGUgYG15Y2xhc3Nlc2Agcm9vdCxcbiAgICAgICAgICogICAgICAvLyB0aGF0IGlzLCB0aGUgZGF0YSBhdCBgL215Y2xhc3Nlcy9tZ210MTAwL2dyb3VwQi9gLlxuICAgICAgICAgKiAgICAgIC8vIE5vdGUgdGhhdCB0aGlzIHJlcGxhY2VzIGFueSBleGlzdGluZyBjb250ZW50IGluIHRoZSBgL215Y2xhc3Nlcy9tZ210MTAwL2dyb3VwQi9gIGRvY3VtZW50LlxuICAgICAgICAgKiAgICAgIC8vIEhvd2V2ZXIsIHRoaXMgd2lsbCBrZWVwIGV4aXN0aW5nIGNvbnRlbnQgaW4gb3RoZXIgcGF0aHMgb2YgdGhpcyBjb2xsZWN0aW9uLlxuICAgICAgICAgKiAgICAgIC8vIEZvciBleGFtcGxlLCB0aGUgZGF0YSBhdCBgL215Y2xhc3Nlcy9tZ210MTAwL2dyb3VwQS9gIGlzIHVuY2hhbmdlZCBieSB0aGlzIGNhbGwuXG4gICAgICAgICAqICAgICAgZHMuc2F2ZUFzKCdtZ210MTAwL2dyb3VwQicsXG4gICAgICAgICAqICAgICAgICAgIHsgc2NlbmFyaW9ZZWFyOiAnMjAxNScgfSxcbiAgICAgICAgICogICAgICAgICAgeyByb290OiAnbXljbGFzc2VzJyB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IGtleSBJZCBvZiB0aGUgZG9jdW1lbnQuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSB2YWx1ZSAoT3B0aW9uYWwpIFRoZSBkYXRhIHRvIHNhdmUsIGluIGtleTp2YWx1ZSBwYWlycy5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy4gSWYgeW91IHdhbnQgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgYHJvb3RgIG9mIHRoZSBjb2xsZWN0aW9uLCBkbyBzbyBoZXJlLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfSBcbiAgICAgICAgICovXG4gICAgICAgIHNhdmVBczogZnVuY3Rpb24gKGtleSwgdmFsdWUsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICBodHRwT3B0aW9ucy51cmwgPSBnZXRVUkwoa2V5LCBodHRwT3B0aW9ucy5yb290KTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucHV0KHZhbHVlLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCBkYXRhIGZvciBhIHNwZWNpZmljIGRvY3VtZW50IG9yIGZpZWxkLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIGRzLmxvYWQoJ3VzZXIxJyk7XG4gICAgICAgICAqICAgICAgZHMubG9hZCgndXNlcjEvcXVlc3Rpb24zJyk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSAge1N0cmluZ3xPYmplY3R9IGtleSBUaGUgaWQgb2YgdGhlIGRhdGEgdG8gcmV0dXJuLiBDYW4gYmUgdGhlIGlkIG9mIGEgZG9jdW1lbnQsIG9yIGEgcGF0aCB0byBkYXRhIHdpdGhpbiB0aGF0IGRvY3VtZW50LlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3V0cHV0TW9kaWZpZXIgKE9wdGlvbmFsKSBBdmFpbGFibGUgZmllbGRzIGluY2x1ZGU6IGBzdGFydHJlY29yZGAsIGBlbmRyZWNvcmRgLCBgc29ydGAsIGFuZCBgZGlyZWN0aW9uYCAoYGFzY2Agb3IgYGRlc2NgKS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9IFxuICAgICAgICAgKi9cbiAgICAgICAgbG9hZDogZnVuY3Rpb24gKGtleSwgb3V0cHV0TW9kaWZpZXIsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICBodHRwT3B0aW9ucy51cmwgPSBnZXRVUkwoa2V5LCBodHRwT3B0aW9ucy5yb290KTtcbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldChvdXRwdXRNb2RpZmllciwgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZW1vdmVzIGRhdGEgZnJvbSBjb2xsZWN0aW9uLiBPbmx5IGRvY3VtZW50cyAodG9wLWxldmVsIGVsZW1lbnRzIGluIGVhY2ggY29sbGVjdGlvbikgY2FuIGJlIGRlbGV0ZWQuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICBkcy5yZW1vdmUoJ3VzZXIxJyk7XG4gICAgICAgICAqXG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfEFycmF5fSBrZXlzIFRoZSBpZCBvZiB0aGUgZG9jdW1lbnQgdG8gcmVtb3ZlIGZyb20gdGhpcyBjb2xsZWN0aW9uLCBvciBhbiBhcnJheSBvZiBzdWNoIGlkcy5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX0gXG4gICAgICAgICAqL1xuICAgICAgICByZW1vdmU6IGZ1bmN0aW9uIChrZXlzLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgdmFyIHBhcmFtcztcbiAgICAgICAgICAgIGlmICgkLmlzQXJyYXkoa2V5cykpIHtcbiAgICAgICAgICAgICAgICBwYXJhbXMgPSB7IGlkOiBrZXlzIH07XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHBhcmFtcyA9ICcnO1xuICAgICAgICAgICAgICAgIGh0dHBPcHRpb25zLnVybCA9IGdldFVSTChrZXlzLCBodHRwT3B0aW9ucy5yb290KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBodHRwLmRlbGV0ZShwYXJhbXMsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEVwaWNlbnRlciBkb2Vzbid0IGFsbG93IG51a2luZyBjb2xsZWN0aW9uc1xuICAgICAgICAvLyAgICAgLyoqXG4gICAgICAgIC8vICAgICAgKiBSZW1vdmVzIGNvbGxlY3Rpb24gYmVpbmcgcmVmZXJlbmNlZFxuICAgICAgICAvLyAgICAgICogQHJldHVybiBudWxsXG4gICAgICAgIC8vICAgICAgKi9cbiAgICAgICAgLy8gICAgIGRlc3Ryb3k6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIC8vICAgICAgICAgcmV0dXJuIHRoaXMucmVtb3ZlKCcnLCBvcHRpb25zKTtcbiAgICAgICAgLy8gICAgIH1cbiAgICB9O1xuXG4gICAgJC5leHRlbmQodGhpcywgcHVibGljQVBJKTtcbn07XG4iLCIvKipcbiAqXG4gKiAjIyBHcm91cCBBUEkgQWRhcHRlclxuICpcbiAqIFRoZSBHcm91cCBBUEkgQWRhcHRlciBwcm92aWRlcyBtZXRob2RzIHRvIGxvb2sgdXAsIGNyZWF0ZSwgY2hhbmdlIG9yIHJlbW92ZSBpbmZvcm1hdGlvbiBhYm91dCBncm91cHMgaW4gYSBwcm9qZWN0LiBJdCBpcyBiYXNlZCBvbiBxdWVyeSBjYXBhYmlsaXRpZXMgb2YgdGhlIHVuZGVybHlpbmcgUkVTVGZ1bCBbR3JvdXAgQVBJXSguLi8uLi8uLi9yZXN0X2FwaXMvdXNlcl9tYW5hZ2VtZW50L2dyb3VwLykuXG4gKlxuICogVGhpcyBpcyBvbmx5IG5lZWRlZCBmb3IgQXV0aGVudGljYXRlZCBwcm9qZWN0cywgdGhhdCBpcywgdGVhbSBwcm9qZWN0cyB3aXRoIFtlbmQgdXNlcnMgYW5kIGdyb3Vwc10oLi4vLi4vLi4vZ3JvdXBzX2FuZF9lbmRfdXNlcnMvKS5cbiAqXG4gKiAgICAgIHZhciBtYSA9IG5ldyBGLnNlcnZpY2UuR3JvdXAoeyB0b2tlbjogJ3VzZXItb3ItcHJvamVjdC1hY2Nlc3MtdG9rZW4nIH0pO1xuICogICAgICBtYS5nZXRHcm91cHNGb3JQcm9qZWN0KHsgYWNjb3VudDogJ2FjbWUnLCBwcm9qZWN0OiAnc2FtcGxlJyB9KTtcbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBzZXJ2aWNlVXRpbHMgPSByZXF1aXJlKCcuL3NlcnZpY2UtdXRpbHMnKTtcbnZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbnZhciBvYmplY3RBc3NpZ24gPSByZXF1aXJlKCdvYmplY3QtYXNzaWduJyk7XG5cbnZhciBhcGlFbmRwb2ludCA9ICdncm91cC9sb2NhbCc7XG5cbnZhciBHcm91cFNlcnZpY2UgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogRXBpY2VudGVyIGFjY291bnQgbmFtZS4gRGVmYXVsdHMgdG8gdW5kZWZpbmVkLlxuICAgICAgICAgKiBAdHlwZSB7c3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgYWNjb3VudDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBFcGljZW50ZXIgcHJvamVjdCBuYW1lLiBEZWZhdWx0cyB0byB1bmRlZmluZWQuXG4gICAgICAgICAqIEB0eXBlIHtzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBwcm9qZWN0OiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIE9wdGlvbnMgdG8gcGFzcyBvbiB0byB0aGUgdW5kZXJseWluZyB0cmFuc3BvcnQgbGF5ZXIuIEFsbCBqcXVlcnkuYWpheCBvcHRpb25zIGF0IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS9qUXVlcnkuYWpheC8gYXJlIGF2YWlsYWJsZS4gRGVmYXVsdHMgdG8gZW1wdHkgb2JqZWN0LlxuICAgICAgICAgKiBAdHlwZSB7b2JqZWN0fVxuICAgICAgICAgKi9cbiAgICAgICAgdHJhbnNwb3J0OiB7fVxuICAgIH07XG4gICAgdmFyIHNlcnZpY2VPcHRpb25zID0gc2VydmljZVV0aWxzLmdldERlZmF1bHRPcHRpb25zKGRlZmF1bHRzLCBjb25maWcsIHsgYXBpRW5kcG9pbnQ6IGFwaUVuZHBvaW50IH0pO1xuICAgIHZhciB0cmFuc3BvcnRPcHRpb25zID0gc2VydmljZU9wdGlvbnMudHJhbnNwb3J0O1xuICAgIGRlbGV0ZSBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQ7XG4gICAgdmFyIGh0dHAgPSBuZXcgVHJhbnNwb3J0RmFjdG9yeSh0cmFuc3BvcnRPcHRpb25zLCBzZXJ2aWNlT3B0aW9ucyk7XG4gICAgdmFyIHB1YmxpY0FQSSA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICogR2V0cyBpbmZvcm1hdGlvbiBmb3IgYSBncm91cCBvciBtdWx0aXBsZSBncm91cHMuXG4gICAgICAgICogQHBhcmFtIHtPYmplY3R9IHBhcmFtcyBvYmplY3Qgd2l0aCBxdWVyeSBwYXJhbWV0ZXJzXG4gICAgICAgICogQHBhdGFtIHtzdHJpbmd9IHBhcmFtcy5xIHBhcnRpYWwgbWF0Y2ggZm9yIG5hbWUsIG9yZ2FuaXphdGlvbiBvciBldmVudC5cbiAgICAgICAgKiBAcGF0YW0ge3N0cmluZ30gcGFyYW1zLmFjY291bnQgRXBpY2VudGVyJ3MgVGVhbSBJRFxuICAgICAgICAqIEBwYXRhbSB7c3RyaW5nfSBwYXJhbXMucHJvamVjdCBFcGljZW50ZXIncyBQcm9qZWN0IElEXG4gICAgICAgICogQHBhdGFtIHtzdHJpbmd9IHBhcmFtcy5uYW1lIEVwaWNlbnRlcidzIEdyb3VwIE5hbWVcbiAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldEdyb3VwczogZnVuY3Rpb24gKHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICAgICAgLy9ncm91cElEIGlzIHBhcnQgb2YgdGhlIFVSTFxuICAgICAgICAgICAgLy9xLCBhY2NvdW50IGFuZCBwcm9qZWN0IGFyZSBwYXJ0IG9mIHRoZSBxdWVyeSBzdHJpbmdcbiAgICAgICAgICAgIHZhciBmaW5hbE9wdHMgPSBvYmplY3RBc3NpZ24oe30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIHZhciBmaW5hbFBhcmFtcztcbiAgICAgICAgICAgIGlmICh0eXBlb2YgcGFyYW1zID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIGZpbmFsT3B0cy51cmwgPSBzZXJ2aWNlVXRpbHMuZ2V0QXBpVXJsKGFwaUVuZHBvaW50ICsgJy8nICsgcGFyYW1zLCBmaW5hbE9wdHMpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBmaW5hbFBhcmFtcyA9IHBhcmFtcztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldChmaW5hbFBhcmFtcywgZmluYWxPcHRzKTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgb2JqZWN0QXNzaWduKHRoaXMsIHB1YmxpY0FQSSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEdyb3VwU2VydmljZTtcbiIsIi8qKlxuICpcbiAqICMjIEludHJvc3BlY3Rpb24gQVBJIFNlcnZpY2VcbiAqXG4gKiBUaGUgSW50cm9zcGVjdGlvbiBBUEkgU2VydmljZSBhbGxvd3MgeW91IHRvIHZpZXcgYSBsaXN0IG9mIHRoZSB2YXJpYWJsZXMgYW5kIG9wZXJhdGlvbnMgaW4gYSBtb2RlbC4gVHlwaWNhbGx5IHVzZWQgaW4gY29uanVuY3Rpb24gd2l0aCB0aGUgW1J1biBBUEkgU2VydmljZV0oLi4vcnVuLWFwaS1zZXJ2aWNlLykuXG4gKlxuICogVGhlIEludHJvc3BlY3Rpb24gQVBJIFNlcnZpY2UgaXMgbm90IGF2YWlsYWJsZSBmb3IgRm9yaW8gU2ltTGFuZy5cbiAqXG4gKiAgICAgICB2YXIgaW50cm8gPSBuZXcgRi5zZXJ2aWNlLkludHJvc3BlY3Qoe1xuICogICAgICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gKiAgICAgICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZSdcbiAqICAgICAgIH0pO1xuICogICAgICAgaW50cm8uYnlNb2RlbCgnc3VwcGx5LWNoYWluLnB5JykudGhlbihmdW5jdGlvbihkYXRhKXsgLi4uIH0pO1xuICogICAgICAgaW50cm8uYnlSdW5JRCgnMmI0ZDhmNzEtNWMzNC00MzVhLThjMTYtOWRlNjc0YWI3MmU2JykudGhlbihmdW5jdGlvbihkYXRhKXsgLi4uIH0pO1xuICpcbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xuXG52YXIgYXBpRW5kcG9pbnQgPSAnbW9kZWwvaW50cm9zcGVjdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEZvciBwcm9qZWN0cyB0aGF0IHJlcXVpcmUgYXV0aGVudGljYXRpb24sIHBhc3MgaW4gdGhlIHVzZXIgYWNjZXNzIHRva2VuIChkZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcpLiBJZiB0aGUgdXNlciBpcyBhbHJlYWR5IGxvZ2dlZCBpbiB0byBFcGljZW50ZXIsIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiBpcyBhbHJlYWR5IHNldCBpbiBhIGNvb2tpZSBhbmQgYXV0b21hdGljYWxseSBsb2FkZWQgZnJvbSB0aGVyZS4gKFNlZSBbbW9yZSBiYWNrZ3JvdW5kIG9uIGFjY2VzcyB0b2tlbnNdKC4uLy4uLy4uL3Byb2plY3RfYWNjZXNzLykpLlxuICAgICAgICAgKiBAc2VlIFtBdXRoZW50aWNhdGlvbiBBUEkgU2VydmljZV0oLi4vYXV0aC1hcGktc2VydmljZS8pIGZvciBnZXR0aW5nIHRva2Vucy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHRva2VuOiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBhY2NvdW50IGlkLiBJbiB0aGUgRXBpY2VudGVyIFVJLCB0aGlzIGlzIHRoZSAqKlRlYW0gSUQqKiAoZm9yIHRlYW0gcHJvamVjdHMpIG9yICoqVXNlciBJRCoqIChmb3IgcGVyc29uYWwgcHJvamVjdHMpLiBEZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBVUkwuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBhY2NvdW50OiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBwcm9qZWN0IGlkLiBEZWZhdWx0cyB0byBlbXB0eSBzdHJpbmcuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBVUkwuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBwcm9qZWN0OiB1bmRlZmluZWQsXG5cbiAgICB9O1xuXG4gICAgdmFyIHNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKCk7XG4gICAgdmFyIHNlcnZpY2VPcHRpb25zID0gc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgY29uZmlnKTtcblxuICAgIHZhciB1cmxDb25maWcgPSBuZXcgQ29uZmlnU2VydmljZShzZXJ2aWNlT3B0aW9ucykuZ2V0KCdzZXJ2ZXInKTtcbiAgICBpZiAoc2VydmljZU9wdGlvbnMuYWNjb3VudCkge1xuICAgICAgICB1cmxDb25maWcuYWNjb3VudFBhdGggPSBzZXJ2aWNlT3B0aW9ucy5hY2NvdW50O1xuICAgIH1cbiAgICBpZiAoc2VydmljZU9wdGlvbnMucHJvamVjdCkge1xuICAgICAgICB1cmxDb25maWcucHJvamVjdFBhdGggPSBzZXJ2aWNlT3B0aW9ucy5wcm9qZWN0O1xuICAgIH1cblxuICAgIHZhciB0cmFuc3BvcnRPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydCwge1xuICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KVxuICAgIH0pO1xuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICB0cmFuc3BvcnRPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KHRyYW5zcG9ydE9wdGlvbnMpO1xuXG4gICAgdmFyIHB1YmxpY0FQSSA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCB0aGUgYXZhaWxhYmxlIHZhcmlhYmxlcyBhbmQgb3BlcmF0aW9ucyBmb3IgYSBnaXZlbiBtb2RlbCBmaWxlLlxuICAgICAgICAgKlxuICAgICAgICAgKiBOb3RlOiBUaGlzIGRvZXMgbm90IHdvcmsgZm9yIGFueSBtb2RlbCB3aGljaCByZXF1aXJlcyBhZGRpdGlvbmFsIHBhcmFtZXRlcnMsIHN1Y2ggYXMgYGZpbGVzYC5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICBpbnRyby5ieU1vZGVsKCdhYmMudm1mJylcbiAgICAgICAgICogICAgICAgICAgLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICAgKiAgICAgICAgICAgICAgLy8gZGF0YSBjb250YWlucyBhbiBvYmplY3Qgd2l0aCBhdmFpbGFibGUgZnVuY3Rpb25zICh1c2VkIHdpdGggb3BlcmF0aW9ucyBBUEkpIGFuZCBhdmFpbGFibGUgdmFyaWFibGVzICh1c2VkIHdpdGggdmFyaWFibGVzIEFQSSlcbiAgICAgICAgICogICAgICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEuZnVuY3Rpb25zKTtcbiAgICAgICAgICogICAgICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEudmFyaWFibGVzKTtcbiAgICAgICAgICogICAgICAgICAgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSAge1N0cmluZ30gbW9kZWxGaWxlIE5hbWUgb2YgdGhlIG1vZGVsIGZpbGUgdG8gaW50cm9zcGVjdC5cbiAgICAgICAgICogQHBhcmFtICB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9IFxuICAgICAgICAgKi9cbiAgICAgICAgYnlNb2RlbDogZnVuY3Rpb24gKG1vZGVsRmlsZSwgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIG9wdHMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgaWYgKCFvcHRzLmFjY291bnQgfHwgIW9wdHMucHJvamVjdCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQWNjb3VudCBhbmQgcHJvamVjdCBhcmUgcmVxdWlyZWQgd2hlbiB1c2luZyBpbnRyb3NwZWN0I2J5TW9kZWwnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICghbW9kZWxGaWxlKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdtb2RlbEZpbGUgaXMgcmVxdWlyZWQgd2hlbiB1c2luZyBpbnRyb3NwZWN0I2J5TW9kZWwnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciB1cmwgPSB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpICsgW29wdHMuYWNjb3VudCwgb3B0cy5wcm9qZWN0LCBtb2RlbEZpbGVdLmpvaW4oJy8nKSB9O1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zLCB1cmwpO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KCcnLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEdldCB0aGUgYXZhaWxhYmxlIHZhcmlhYmxlcyBhbmQgb3BlcmF0aW9ucyBmb3IgYSBnaXZlbiBtb2RlbCBmaWxlLlxuICAgICAgICAgKlxuICAgICAgICAgKiBOb3RlOiBUaGlzIGRvZXMgbm90IHdvcmsgZm9yIGFueSBtb2RlbCB3aGljaCByZXF1aXJlcyBhZGRpdGlvbmFsIHBhcmFtZXRlcnMgc3VjaCBhcyBgZmlsZXNgLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIGludHJvLmJ5UnVuSUQoJzJiNGQ4ZjcxLTVjMzQtNDM1YS04YzE2LTlkZTY3NGFiNzJlNicpXG4gICAgICAgICAqICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgICogICAgICAgICAgICAgIC8vIGRhdGEgY29udGFpbnMgYW4gb2JqZWN0IHdpdGggYXZhaWxhYmxlIGZ1bmN0aW9ucyAodXNlZCB3aXRoIG9wZXJhdGlvbnMgQVBJKSBhbmQgYXZhaWxhYmxlIHZhcmlhYmxlcyAodXNlZCB3aXRoIHZhcmlhYmxlcyBBUEkpXG4gICAgICAgICAqICAgICAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhLmZ1bmN0aW9ucyk7XG4gICAgICAgICAqICAgICAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhLnZhcmlhYmxlcyk7XG4gICAgICAgICAqICAgICAgICAgIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0gIHtTdHJpbmd9IHJ1bklEIElkIG9mIHRoZSBydW4gdG8gaW50cm9zcGVjdC5cbiAgICAgICAgICogQHBhcmFtICB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9IFxuICAgICAgICAgKi9cbiAgICAgICAgYnlSdW5JRDogZnVuY3Rpb24gKHJ1bklELCBvcHRpb25zKSB7XG4gICAgICAgICAgICBpZiAoIXJ1bklEKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdydW5JRCBpcyByZXF1aXJlZCB3aGVuIHVzaW5nIGludHJvc3BlY3QjYnlNb2RlbCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHVybCA9IHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBydW5JRCB9O1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zLCB1cmwpO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KCcnLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FQSSk7XG59O1xuIiwiLyoqXG4gKlxuICogIyMgTWVtYmVyIEFQSSBBZGFwdGVyXG4gKlxuICogVGhlIE1lbWJlciBBUEkgQWRhcHRlciBwcm92aWRlcyBtZXRob2RzIHRvIGxvb2sgdXAgaW5mb3JtYXRpb24gYWJvdXQgZW5kIHVzZXJzIGZvciB5b3VyIHByb2plY3QgYW5kIGhvdyB0aGV5IGFyZSBkaXZpZGVkIGFjcm9zcyBncm91cHMuIEl0IGlzIGJhc2VkIG9uIHF1ZXJ5IGNhcGFiaWxpdGllcyBvZiB0aGUgdW5kZXJseWluZyBSRVNUZnVsIFtNZW1iZXIgQVBJXSguLi8uLi8uLi9yZXN0X2FwaXMvdXNlcl9tYW5hZ2VtZW50L21lbWJlci8pLlxuICpcbiAqIFRoaXMgaXMgb25seSBuZWVkZWQgZm9yIEF1dGhlbnRpY2F0ZWQgcHJvamVjdHMsIHRoYXQgaXMsIHRlYW0gcHJvamVjdHMgd2l0aCBbZW5kIHVzZXJzIGFuZCBncm91cHNdKC4uLy4uLy4uL2dyb3Vwc19hbmRfZW5kX3VzZXJzLykuIEZvciBleGFtcGxlLCBpZiBzb21lIG9mIHlvdXIgZW5kIHVzZXJzIGFyZSBmYWNpbGl0YXRvcnMsIG9yIGlmIHlvdXIgZW5kIHVzZXJzIHNob3VsZCBiZSB0cmVhdGVkIGRpZmZlcmVudGx5IGJhc2VkIG9uIHdoaWNoIGdyb3VwIHRoZXkgYXJlIGluLCB1c2UgdGhlIE1lbWJlciBBUEkgdG8gZmluZCB0aGF0IGluZm9ybWF0aW9uLlxuICpcbiAqICAgICAgdmFyIG1hID0gbmV3IEYuc2VydmljZS5NZW1iZXIoeyB0b2tlbjogJ3VzZXItb3ItcHJvamVjdC1hY2Nlc3MtdG9rZW4nIH0pO1xuICogICAgICBtYS5nZXRHcm91cHNGb3JVc2VyKHsgdXNlcklkOiAnYjZiMzEzYTMtYWI4NC00NzljLWJhZWEtMjA2ZjZiZmYzMzcnIH0pO1xuICogICAgICBtYS5nZXRHcm91cERldGFpbHMoeyBncm91cElkOiAnMDBiNTMzMDgtOTgzMy00N2YyLWIyMWUtMTI3OGMwN2Q1M2I4JyB9KTtcbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xudmFyIF9waWNrID0gcmVxdWlyZSgnLi4vdXRpbC9vYmplY3QtdXRpbCcpLl9waWNrO1xudmFyIGFwaUVuZHBvaW50ID0gJ21lbWJlci9sb2NhbCc7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBkZWZhdWx0cyA9IHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEVwaWNlbnRlciB1c2VyIGlkLiBEZWZhdWx0cyB0byBhIGJsYW5rIHN0cmluZy5cbiAgICAgICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHVzZXJJZDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBFcGljZW50ZXIgZ3JvdXAgaWQuIERlZmF1bHRzIHRvIGEgYmxhbmsgc3RyaW5nLiBOb3RlIHRoYXQgdGhpcyBpcyB0aGUgZ3JvdXAgKmlkKiwgbm90IHRoZSBncm91cCAqbmFtZSouXG4gICAgICAgICAqIEB0eXBlIHtzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBncm91cElkOiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIE9wdGlvbnMgdG8gcGFzcyBvbiB0byB0aGUgdW5kZXJseWluZyB0cmFuc3BvcnQgbGF5ZXIuIEFsbCBqcXVlcnkuYWpheCBvcHRpb25zIGF0IGh0dHA6Ly9hcGkuanF1ZXJ5LmNvbS9qUXVlcnkuYWpheC8gYXJlIGF2YWlsYWJsZS4gRGVmYXVsdHMgdG8gZW1wdHkgb2JqZWN0LlxuICAgICAgICAgKiBAdHlwZSB7b2JqZWN0fVxuICAgICAgICAgKi9cbiAgICAgICAgdHJhbnNwb3J0OiB7fVxuICAgIH07XG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgY29uZmlnKTtcbiAgICB2YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2Uoc2VydmljZU9wdGlvbnMpLmdldCgnc2VydmVyJyk7XG5cbiAgICB2YXIgdHJhbnNwb3J0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQsIHtcbiAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludClcbiAgICB9KTtcblxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICB0cmFuc3BvcnRPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KHRyYW5zcG9ydE9wdGlvbnMsIHNlcnZpY2VPcHRpb25zKTtcblxuICAgIHZhciBnZXRGaW5hbFBhcmFtcyA9IGZ1bmN0aW9uIChwYXJhbXMpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBwYXJhbXMgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICByZXR1cm4gJC5leHRlbmQodHJ1ZSwgc2VydmljZU9wdGlvbnMsIHBhcmFtcyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHNlcnZpY2VPcHRpb25zO1xuICAgIH07XG5cbiAgICB2YXIgcGF0Y2hVc2VyQWN0aXZlRmllbGQgPSBmdW5jdGlvbiAocGFyYW1zLCBhY3RpdmUsIG9wdGlvbnMpIHtcbiAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMsIHtcbiAgICAgICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpICsgcGFyYW1zLmdyb3VwSWQgKyAnLycgKyBwYXJhbXMudXNlcklkXG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBodHRwLnBhdGNoKHsgYWN0aXZlOiBhY3RpdmUgfSwgaHR0cE9wdGlvbnMpO1xuICAgIH07XG5cbiAgICB2YXIgcHVibGljQVBJID0ge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAqIFJldHJpZXZlIGRldGFpbHMgYWJvdXQgYWxsIG9mIHRoZSBncm91cCBtZW1iZXJzaGlwcyBmb3Igb25lIGVuZCB1c2VyLiBUaGUgbWVtYmVyc2hpcCBkZXRhaWxzIGFyZSByZXR1cm5lZCBpbiBhbiBhcnJheSwgd2l0aCBvbmUgZWxlbWVudCAoZ3JvdXAgcmVjb3JkKSBmb3IgZWFjaCBncm91cCB0byB3aGljaCB0aGUgZW5kIHVzZXIgYmVsb25ncy5cbiAgICAgICAgKlxuICAgICAgICAqIEluIHRoZSBtZW1iZXJzaGlwIGFycmF5LCBlYWNoIGdyb3VwIHJlY29yZCBpbmNsdWRlcyB0aGUgZ3JvdXAgaWQsIHByb2plY3QgaWQsIGFjY291bnQgKHRlYW0pIGlkLCBhbmQgYW4gYXJyYXkgb2YgbWVtYmVycy4gSG93ZXZlciwgb25seSB0aGUgdXNlciB3aG9zZSB1c2VySWQgaXMgaW5jbHVkZWQgaW4gdGhlIGNhbGwgaXMgbGlzdGVkIGluIHRoZSBtZW1iZXJzIGFycmF5IChyZWdhcmRsZXNzIG9mIHdoZXRoZXIgdGhlcmUgYXJlIG90aGVyIG1lbWJlcnMgaW4gdGhpcyBncm91cCkuXG4gICAgICAgICpcbiAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgdmFyIG1hID0gbmV3IEYuc2VydmljZS5NZW1iZXIoeyB0b2tlbjogJ3VzZXItb3ItcHJvamVjdC1hY2Nlc3MtdG9rZW4nIH0pO1xuICAgICAgICAqICAgICAgIG1hLmdldEdyb3Vwc0ZvclVzZXIoJzQyODM2ZDRiLTViNjEtNGZlNC04MGViLTMxMzZlOTU2ZWU1YycpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uKG1lbWJlcnNoaXBzKXtcbiAgICAgICAgKiAgICAgICAgICAgICAgIGZvciAodmFyIGk9MDsgaTxtZW1iZXJzaGlwcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAqICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKG1lbWJlcnNoaXBzW2ldLmdyb3VwSWQpO1xuICAgICAgICAqICAgICAgICAgICAgICAgfVxuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgIG1hLmdldEdyb3Vwc0ZvclVzZXIoeyB1c2VySWQ6ICc0MjgzNmQ0Yi01YjYxLTRmZTQtODBlYi0zMTM2ZTk1NmVlNWMnIH0pO1xuICAgICAgICAqXG4gICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ3xvYmplY3R9IHBhcmFtcyBUaGUgdXNlciBpZCBmb3IgdGhlIGVuZCB1c2VyLiBBbHRlcm5hdGl2ZWx5LCBhbiBvYmplY3Qgd2l0aCBmaWVsZCBgdXNlcklkYCBhbmQgdmFsdWUgdGhlIHVzZXIgaWQuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgKi9cblxuICAgICAgICBnZXRHcm91cHNGb3JVc2VyOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIHZhciBpc1N0cmluZyA9IHR5cGVvZiBwYXJhbXMgPT09ICdzdHJpbmcnO1xuICAgICAgICAgICAgdmFyIG9ialBhcmFtcyA9IGdldEZpbmFsUGFyYW1zKHBhcmFtcyk7XG4gICAgICAgICAgICBpZiAoIWlzU3RyaW5nICYmICFvYmpQYXJhbXMudXNlcklkKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyB1c2VySWQgc3BlY2lmaWVkLicpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgZ2V0UGFybXMgPSBpc1N0cmluZyA/IHsgdXNlcklkOiBwYXJhbXMgfSA6IF9waWNrKG9ialBhcmFtcywgJ3VzZXJJZCcpO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KGdldFBhcm1zLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogUmV0cmlldmUgZGV0YWlscyBhYm91dCBvbmUgZ3JvdXAsIGluY2x1ZGluZyBhbiBhcnJheSBvZiBhbGwgaXRzIG1lbWJlcnMuXG4gICAgICAgICpcbiAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgdmFyIG1hID0gbmV3IEYuc2VydmljZS5NZW1iZXIoeyB0b2tlbjogJ3VzZXItb3ItcHJvamVjdC1hY2Nlc3MtdG9rZW4nIH0pO1xuICAgICAgICAqICAgICAgIG1hLmdldEdyb3VwRGV0YWlscygnODAyNTdhMjUtYWExMC00OTU5LTk2OGItZmQwNTM5MDFmNzJmJylcbiAgICAgICAgKiAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24oZ3JvdXApe1xuICAgICAgICAqICAgICAgICAgICAgICAgZm9yICh2YXIgaT0wOyBpPGdyb3VwLm1lbWJlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgKiAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhncm91cC5tZW1iZXJzW2ldLnVzZXJOYW1lKTtcbiAgICAgICAgKiAgICAgICAgICAgICAgIH1cbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICBtYS5nZXRHcm91cERldGFpbHMoeyBncm91cElkOiAnODAyNTdhMjUtYWExMC00OTU5LTk2OGItZmQwNTM5MDFmNzJmJyB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd8b2JqZWN0fSBwYXJhbXMgVGhlIGdyb3VwIGlkLiBBbHRlcm5hdGl2ZWx5LCBhbiBvYmplY3Qgd2l0aCBmaWVsZCBgZ3JvdXBJZGAgYW5kIHZhbHVlIHRoZSBncm91cCBpZC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldEdyb3VwRGV0YWlsczogZnVuY3Rpb24gKHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgICAgICAgICB2YXIgaXNTdHJpbmcgPSB0eXBlb2YgcGFyYW1zID09PSAnc3RyaW5nJztcbiAgICAgICAgICAgIHZhciBvYmpQYXJhbXMgPSBnZXRGaW5hbFBhcmFtcyhwYXJhbXMpO1xuICAgICAgICAgICAgaWYgKCFpc1N0cmluZyAmJiAhb2JqUGFyYW1zLmdyb3VwSWQpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIGdyb3VwSWQgc3BlY2lmaWVkLicpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgZ3JvdXBJZCA9IGlzU3RyaW5nID8gcGFyYW1zIDogb2JqUGFyYW1zLmdyb3VwSWQ7XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBncm91cElkIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldCh7fSwgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIFNldCBhIHBhcnRpY3VsYXIgZW5kIHVzZXIgYXMgYGFjdGl2ZWAuIEFjdGl2ZSBlbmQgdXNlcnMgY2FuIGJlIGFzc2lnbmVkIHRvIFt3b3JsZHNdKC4uL3dvcmxkLW1hbmFnZXIvKSBpbiBtdWx0aXBsYXllciBnYW1lcyBkdXJpbmcgYXV0b21hdGljIGFzc2lnbm1lbnQuXG4gICAgICAgICpcbiAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgdmFyIG1hID0gbmV3IEYuc2VydmljZS5NZW1iZXIoeyB0b2tlbjogJ3VzZXItb3ItcHJvamVjdC1hY2Nlc3MtdG9rZW4nIH0pO1xuICAgICAgICAqICAgICAgIG1hLm1ha2VVc2VyQWN0aXZlKHsgdXNlcklkOiAnNDI4MzZkNGItNWI2MS00ZmU0LTgwZWItMzEzNmU5NTZlZTVjJyxcbiAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwSWQ6ICc4MDI1N2EyNS1hYTEwLTQ5NTktOTY4Yi1mZDA1MzkwMWY3MmYnIH0pO1xuICAgICAgICAqXG4gICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zIFRoZSBlbmQgdXNlciBhbmQgZ3JvdXAgaW5mb3JtYXRpb24uXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy51c2VySWQgVGhlIGlkIG9mIHRoZSBlbmQgdXNlciB0byBtYWtlIGFjdGl2ZS5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLmdyb3VwSWQgVGhlIGlkIG9mIHRoZSBncm91cCB0byB3aGljaCB0aGlzIGVuZCB1c2VyIGJlbG9uZ3MsIGFuZCBpbiB3aGljaCB0aGUgZW5kIHVzZXIgc2hvdWxkIGJlY29tZSBhY3RpdmUuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBtYWtlVXNlckFjdGl2ZTogZnVuY3Rpb24gKHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIHBhdGNoVXNlckFjdGl2ZUZpZWxkKHBhcmFtcywgdHJ1ZSwgb3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogU2V0IGEgcGFydGljdWxhciBlbmQgdXNlciBhcyBgaW5hY3RpdmVgLiBJbmFjdGl2ZSBlbmQgdXNlcnMgYXJlIG5vdCBhc3NpZ25lZCB0byBbd29ybGRzXSguLi93b3JsZC1tYW5hZ2VyLykgaW4gbXVsdGlwbGF5ZXIgZ2FtZXMgZHVyaW5nIGF1dG9tYXRpYyBhc3NpZ25tZW50LlxuICAgICAgICAqXG4gICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgIHZhciBtYSA9IG5ldyBGLnNlcnZpY2UuTWVtYmVyKHsgdG9rZW46ICd1c2VyLW9yLXByb2plY3QtYWNjZXNzLXRva2VuJyB9KTtcbiAgICAgICAgKiAgICAgICBtYS5tYWtlVXNlckluYWN0aXZlKHsgdXNlcklkOiAnNDI4MzZkNGItNWI2MS00ZmU0LTgwZWItMzEzNmU5NTZlZTVjJyxcbiAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwSWQ6ICc4MDI1N2EyNS1hYTEwLTQ5NTktOTY4Yi1mZDA1MzkwMWY3MmYnIH0pO1xuICAgICAgICAqXG4gICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zIFRoZSBlbmQgdXNlciBhbmQgZ3JvdXAgaW5mb3JtYXRpb24uXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy51c2VySWQgVGhlIGlkIG9mIHRoZSBlbmQgdXNlciB0byBtYWtlIGluYWN0aXZlLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuZ3JvdXBJZCBUaGUgaWQgb2YgdGhlIGdyb3VwIHRvIHdoaWNoIHRoaXMgZW5kIHVzZXIgYmVsb25ncywgYW5kIGluIHdoaWNoIHRoZSBlbmQgdXNlciBzaG91bGQgYmVjb21lIGluYWN0aXZlLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgbWFrZVVzZXJJbmFjdGl2ZTogZnVuY3Rpb24gKHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIHBhdGNoVXNlckFjdGl2ZUZpZWxkKHBhcmFtcywgZmFsc2UsIG9wdGlvbnMpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FQSSk7XG59O1xuIiwiLyoqXG4gKlxuICogIyMgUnVuIEFQSSBTZXJ2aWNlXG4gKlxuICogVGhlIFJ1biBBUEkgU2VydmljZSBhbGxvd3MgeW91IHRvIHBlcmZvcm0gY29tbW9uIHRhc2tzIGFyb3VuZCBjcmVhdGluZyBhbmQgdXBkYXRpbmcgcnVucywgdmFyaWFibGVzLCBhbmQgZGF0YS5cbiAqXG4gKiBXaGVuIGJ1aWxkaW5nIGludGVyZmFjZXMgdG8gc2hvdyBydW4gb25lIGF0IGEgdGltZSAoYXMgZm9yIHN0YW5kYXJkIGVuZCB1c2VycyksIHR5cGljYWxseSB5b3UgZmlyc3QgaW5zdGFudGlhdGUgYSBbUnVuIE1hbmFnZXJdKC4uL3J1bi1tYW5hZ2VyLykgYW5kIHRoZW4gYWNjZXNzIHRoZSBSdW4gU2VydmljZSB0aGF0IGlzIGF1dG9tYXRpY2FsbHkgcGFydCBvZiB0aGUgbWFuYWdlciwgcmF0aGVyIHRoYW4gaW5zdGFudGlhdGluZyB0aGUgUnVuIFNlcnZpY2UgZGlyZWN0bHkuIFRoaXMgaXMgYmVjYXVzZSB0aGUgUnVuIE1hbmFnZXIgZ2l2ZXMgeW91IGNvbnRyb2wgb3ZlciBydW4gY3JlYXRpb24gZGVwZW5kaW5nIG9uIHJ1biBzdGF0ZXMuXG4gKlxuICogSG93ZXZlciwgbWFueSBvZiB0aGUgRXBpY2VudGVyIHNhbXBsZSBwcm9qZWN0cyB1c2UgYSBSdW4gU2VydmljZSwgYmVjYXVzZSBnZW5lcmFsbHkgdGhlIHNhbXBsZSBwcm9qZWN0cyBhcmUgcGxheWVkIGluIG9uZSBlbmQgdXNlciBzZXNzaW9uIGFuZCBkb24ndCBjYXJlIGFib3V0IHJ1biBzdGF0ZXMgb3IgW3J1biBzdHJhdGVnaWVzXSguLi9zdHJhdGVnaWVzLykuIFRoZSBSdW4gQVBJIFNlcnZpY2UgaXMgYWxzbyB1c2VmdWwgZm9yIGJ1aWxkaW5nIGFuIGludGVyZmFjZSBmb3IgYSBmYWNpbGl0YXRvciwgYmVjYXVzZSBpdCBtYWtlcyBpdCBlYXN5IHRvIGxpc3QgZGF0YSBhY3Jvc3MgbXVsdGlwbGUgcnVucyAodXNpbmcgdGhlIGBmaWx0ZXIoKWAgYW5kIGBxdWVyeSgpYCBtZXRob2RzKS5cbiAqXG4gKiBUbyB1c2UgdGhlIFJ1biBBUEkgU2VydmljZSwgaW5zdGFudGlhdGUgaXQgYnkgcGFzc2luZyBpbjpcbiAqXG4gKiAqIGBhY2NvdW50YDogRXBpY2VudGVyIGFjY291bnQgaWQgKCoqVGVhbSBJRCoqIGZvciB0ZWFtIHByb2plY3RzLCAqKlVzZXIgSUQqKiBmb3IgcGVyc29uYWwgcHJvamVjdHMpLlxuICogKiBgcHJvamVjdGA6IEVwaWNlbnRlciBwcm9qZWN0IGlkLlxuICpcbiAqIEZvciBleGFtcGxlLFxuICpcbiAqICAgICAgIHZhciBycyA9IG5ldyBGLnNlcnZpY2UuUnVuKHtcbiAqICAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICogICAgICAgICAgICBwcm9qZWN0OiAnc3VwcGx5LWNoYWluLWdhbWUnLFxuICogICAgICB9KTtcbiAqICAgICAgcnMuY3JlYXRlKCdzdXBwbHlfY2hhaW5fZ2FtZS5weScpLnRoZW4oZnVuY3Rpb24ocnVuKSB7XG4gKiAgICAgICAgICAgICBycy5kbygnc29tZU9wZXJhdGlvbicpO1xuICogICAgICB9KTtcbiAqXG4gKlxuICogQWRkaXRpb25hbGx5LCBhbGwgQVBJIGNhbGxzIHRha2UgaW4gYW4gXCJvcHRpb25zXCIgb2JqZWN0IGFzIHRoZSBsYXN0IHBhcmFtZXRlci4gVGhlIG9wdGlvbnMgY2FuIGJlIHVzZWQgdG8gZXh0ZW5kL292ZXJyaWRlIHRoZSBSdW4gQVBJIFNlcnZpY2UgZGVmYXVsdHMgbGlzdGVkIGJlbG93LlxuICpcbiAqIE5vdGUgdGhhdCBpbiBhZGRpdGlvbiB0byB0aGUgYGFjY291bnRgLCBgcHJvamVjdGAsIGFuZCBgbW9kZWxgLCB0aGUgUnVuIFNlcnZpY2UgcGFyYW1ldGVycyBvcHRpb25hbGx5IGluY2x1ZGUgYSBgc2VydmVyYCBvYmplY3QsIHdob3NlIGBob3N0YCBmaWVsZCBjb250YWlucyB0aGUgVVJJIG9mIHRoZSBGb3JpbyBzZXJ2ZXIuIFRoaXMgaXMgYXV0b21hdGljYWxseSBzZXQsIGJ1dCB5b3UgY2FuIHBhc3MgaXQgZXhwbGljaXRseSBpZiBkZXNpcmVkLiBJdCBpcyBtb3N0IGNvbW1vbmx5IHVzZWQgZm9yIGNsYXJpdHkgd2hlbiB5b3UgYXJlIFtob3N0aW5nIGFuIEVwaWNlbnRlciBwcm9qZWN0IG9uIHlvdXIgb3duIHNlcnZlcl0oLi4vLi4vLi4vaG93X3RvL3NlbGZfaG9zdGluZy8pLlxuICpcbiAqICAgICAgIHZhciBybSA9IG5ldyBGLm1hbmFnZXIuUnVuTWFuYWdlcih7XG4gKiAgICAgICAgICAgcnVuOiB7XG4gKiAgICAgICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAqICAgICAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAqICAgICAgICAgICAgICAgbW9kZWw6ICdzdXBwbHlfY2hhaW5fZ2FtZS5weScsXG4gKiAgICAgICAgICAgICAgIHNlcnZlcjogeyBob3N0OiAnYXBpLmZvcmlvLmNvbScgfVxuICogICAgICAgICAgIH1cbiAqICAgICAgIH0pO1xuICogICAgICAgcm0uZ2V0UnVuKClcbiAqICAgICAgICAgICAudGhlbihmdW5jdGlvbihydW4pIHtcbiAqICAgICAgICAgICAgICAgLy8gdGhlIFJ1bk1hbmFnZXIucnVuIGNvbnRhaW5zIHRoZSBpbnN0YW50aWF0ZWQgUnVuIFNlcnZpY2UsXG4gKiAgICAgICAgICAgICAgIC8vIHNvIGFueSBSdW4gU2VydmljZSBtZXRob2QgaXMgdmFsaWQgaGVyZVxuICogICAgICAgICAgICAgICB2YXIgcnMgPSBybS5ydW47XG4gKiAgICAgICAgICAgICAgIHJzLmRvKCdzb21lT3BlcmF0aW9uJyk7XG4gKiAgICAgICB9KVxuICpcbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBxdXRpbCA9IHJlcXVpcmUoJy4uL3V0aWwvcXVlcnktdXRpbCcpO1xudmFyIHJ1dGlsID0gcmVxdWlyZSgnLi4vdXRpbC9ydW4tdXRpbCcpO1xudmFyIF9waWNrID0gcmVxdWlyZSgnLi4vdXRpbC9vYmplY3QtdXRpbCcpLl9waWNrO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIFZhcmlhYmxlc1NlcnZpY2UgPSByZXF1aXJlKCcuL3ZhcmlhYmxlcy1hcGktc2VydmljZScpO1xudmFyIEludHJvc3BlY3Rpb25TZXJ2aWNlID0gcmVxdWlyZSgnLi9pbnRyb3NwZWN0aW9uLWFwaS1zZXJ2aWNlJyk7XG52YXIgU2Vzc2lvbk1hbmFnZXIgPSByZXF1aXJlKCcuLi9zdG9yZS9zZXNzaW9uLW1hbmFnZXInKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogRm9yIHByb2plY3RzIHRoYXQgcmVxdWlyZSBhdXRoZW50aWNhdGlvbiwgcGFzcyBpbiB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gKGRlZmF1bHRzIHRvIGVtcHR5IHN0cmluZykuIElmIHRoZSB1c2VyIGlzIGFscmVhZHkgbG9nZ2VkIGluIHRvIEVwaWNlbnRlciwgdGhlIHVzZXIgYWNjZXNzIHRva2VuIGlzIGFscmVhZHkgc2V0IGluIGEgY29va2llIGFuZCBhdXRvbWF0aWNhbGx5IGxvYWRlZCBmcm9tIHRoZXJlLiAoU2VlIFttb3JlIGJhY2tncm91bmQgb24gYWNjZXNzIHRva2Vuc10oLi4vLi4vLi4vcHJvamVjdF9hY2Nlc3MvKSkuXG4gICAgICAgICAqIEBzZWUgW0F1dGhlbnRpY2F0aW9uIEFQSSBTZXJ2aWNlXSguLi9hdXRoLWFwaS1zZXJ2aWNlLykgZm9yIGdldHRpbmcgdG9rZW5zLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdG9rZW46IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFjY291bnQgaWQuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykgb3IgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFjY291bnQ6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHByb2plY3QgaWQuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIHByb2plY3Q6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ3JpdGVyaWEgYnkgd2hpY2ggdG8gZmlsdGVyIHJ1bnMuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGZpbHRlcjogJycsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENvbnZlbmllbmNlIGFsaWFzIGZvciBmaWx0ZXIuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBpZDogJycsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEZsYWcgZGV0ZXJtaW5lcyBpZiBgWC1BdXRvUmVzdG9yZTogdHJ1ZWAgaGVhZGVyIGlzIHNlbnQgdG8gRXBpY2VudGVyLiBEZWZhdWx0cyB0byBgdHJ1ZWAuXG4gICAgICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAgICAgKi9cbiAgICAgICAgYXV0b1Jlc3RvcmU6IHRydWUsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENhbGxlZCB3aGVuIHRoZSBjYWxsIGNvbXBsZXRlcyBzdWNjZXNzZnVsbHkuIERlZmF1bHRzIHRvIGAkLm5vb3BgLlxuICAgICAgICAgKiBAdHlwZSB7ZnVuY3Rpb259XG4gICAgICAgICAqL1xuICAgICAgICBzdWNjZXNzOiAkLm5vb3AsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENhbGxlZCB3aGVuIHRoZSBjYWxsIGZhaWxzLiBEZWZhdWx0cyB0byBgJC5ub29wYC5cbiAgICAgICAgICogQHR5cGUge2Z1bmN0aW9ufVxuICAgICAgICAgKi9cbiAgICAgICAgZXJyb3I6ICQubm9vcCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT3B0aW9ucyB0byBwYXNzIG9uIHRvIHRoZSB1bmRlcmx5aW5nIHRyYW5zcG9ydCBsYXllci4gQWxsIGpxdWVyeS5hamF4IG9wdGlvbnMgYXQgaHR0cDovL2FwaS5qcXVlcnkuY29tL2pRdWVyeS5hamF4LyBhcmUgYXZhaWxhYmxlLiBEZWZhdWx0cyB0byBlbXB0eSBvYmplY3QuXG4gICAgICAgICAqIEB0eXBlIHtPYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICB0cmFuc3BvcnQ6IHt9XG4gICAgfTtcblxuICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICB2YXIgc2VydmljZU9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMoZGVmYXVsdHMsIGNvbmZpZyk7XG4gICAgaWYgKHNlcnZpY2VPcHRpb25zLmlkKSB7XG4gICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IHNlcnZpY2VPcHRpb25zLmlkO1xuICAgIH1cblxuICAgIHZhciB1cmxDb25maWcgPSBuZXcgQ29uZmlnU2VydmljZShzZXJ2aWNlT3B0aW9ucykuZ2V0KCdzZXJ2ZXInKTtcbiAgICBpZiAoc2VydmljZU9wdGlvbnMuYWNjb3VudCkge1xuICAgICAgICB1cmxDb25maWcuYWNjb3VudFBhdGggPSBzZXJ2aWNlT3B0aW9ucy5hY2NvdW50O1xuICAgIH1cbiAgICBpZiAoc2VydmljZU9wdGlvbnMucHJvamVjdCkge1xuICAgICAgICB1cmxDb25maWcucHJvamVjdFBhdGggPSBzZXJ2aWNlT3B0aW9ucy5wcm9qZWN0O1xuICAgIH1cblxuICAgIHVybENvbmZpZy5maWx0ZXIgPSAnOyc7XG4gICAgdXJsQ29uZmlnLmdldEZpbHRlclVSTCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHVybCA9IHVybENvbmZpZy5nZXRBUElQYXRoKCdydW4nKTtcbiAgICAgICAgdmFyIGZpbHRlciA9IHF1dGlsLnRvTWF0cml4Rm9ybWF0KHNlcnZpY2VPcHRpb25zLmZpbHRlcik7XG5cbiAgICAgICAgaWYgKGZpbHRlcikge1xuICAgICAgICAgICAgdXJsICs9IGZpbHRlciArICcvJztcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdXJsO1xuICAgIH07XG5cbiAgICB1cmxDb25maWcuYWRkQXV0b1Jlc3RvcmVIZWFkZXIgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICB2YXIgZmlsdGVyID0gc2VydmljZU9wdGlvbnMuZmlsdGVyO1xuICAgICAgICAvLyBUaGUgc2VtaWNvbG9uIHNlcGFyYXRlZCBmaWx0ZXIgaXMgdXNlZCB3aGVuIGZpbHRlciBpcyBhbiBvYmplY3RcbiAgICAgICAgdmFyIGlzRmlsdGVyUnVuSWQgPSBmaWx0ZXIgJiYgJC50eXBlKGZpbHRlcikgPT09ICdzdHJpbmcnO1xuICAgICAgICBpZiAoc2VydmljZU9wdGlvbnMuYXV0b1Jlc3RvcmUgJiYgaXNGaWx0ZXJSdW5JZCkge1xuICAgICAgICAgICAgLy8gQnkgZGVmYXVsdCBhdXRvcmVwbGF5IHRoZSBydW4gYnkgc2VuZGluZyB0aGlzIGhlYWRlciB0byBlcGljZW50ZXJcbiAgICAgICAgICAgIC8vIGh0dHBzOi8vZm9yaW8uY29tL2VwaWNlbnRlci9kb2NzL3B1YmxpYy9yZXN0X2FwaXMvYWdncmVnYXRlX3J1bl9hcGkvI3JldHJpZXZpbmdcbiAgICAgICAgICAgIHZhciBhdXRvcmVzdG9yZU9wdHMgPSB7XG4gICAgICAgICAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICAgICAgICAgICAnWC1BdXRvUmVzdG9yZSc6IHRydWVcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmV0dXJuICQuZXh0ZW5kKHRydWUsIGF1dG9yZXN0b3JlT3B0cywgb3B0aW9ucyk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gb3B0aW9ucztcbiAgICB9O1xuXG4gICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydCwge1xuICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRGaWx0ZXJVUkxcbiAgICB9KTtcblxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICBodHRwT3B0aW9ucy5oZWFkZXJzID0ge1xuICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogJ0JlYXJlciAnICsgc2VydmljZU9wdGlvbnMudG9rZW5cbiAgICAgICAgfTtcbiAgICB9XG4gICAgdmFyIGh0dHAgPSBuZXcgVHJhbnNwb3J0RmFjdG9yeShodHRwT3B0aW9ucyk7XG4gICAgaHR0cC5zcGxpdEdldCA9IHJ1dGlsLnNwbGl0R2V0RmFjdG9yeShodHRwT3B0aW9ucyk7XG5cbiAgICB2YXIgc2V0RmlsdGVyT3JUaHJvd0Vycm9yID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgaWYgKG9wdGlvbnMuaWQpIHtcbiAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IG9wdGlvbnMuaWQ7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9wdGlvbnMuZmlsdGVyKSB7XG4gICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgPSBvcHRpb25zLmZpbHRlcjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIXNlcnZpY2VPcHRpb25zLmZpbHRlcikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyBmaWx0ZXIgc3BlY2lmaWVkIHRvIGFwcGx5IG9wZXJhdGlvbnMgYWdhaW5zdCcpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHZhciBwdWJsaWNBc3luY0FQSSA9IHtcbiAgICAgICAgdXJsQ29uZmlnOiB1cmxDb25maWcsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENyZWF0ZSBhIG5ldyBydW4uXG4gICAgICAgICAqXG4gICAgICAgICAqIE5PVEU6IFR5cGljYWxseSB0aGlzIGlzIG5vdCB1c2VkISBVc2UgYFJ1bk1hbmFnZXIuZ2V0UnVuKClgIHdpdGggYSBgc3RyYXRlZ3lgIG9mIGBhbHdheXMtbmV3YCwgb3IgdXNlIGBSdW5NYW5hZ2VyLnJlc2V0KClgLiBTZWUgW1J1biBNYW5hZ2VyXSguLi9ydW4tbWFuYWdlci8pIGZvciBtb3JlIGRldGFpbHMuXG4gICAgICAgICAqXG4gICAgICAgICAqICAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIHJzLmNyZWF0ZSgnaGVsbG9fd29ybGQuamwnKTtcbiAgICAgICAgICpcbiAgICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfE9iamVjdH0gcGFyYW1zIElmIGEgc3RyaW5nLCB0aGUgbmFtZSBvZiB0aGUgcHJpbWFyeSBbbW9kZWwgZmlsZV0oLi4vLi4vLi4vd3JpdGluZ195b3VyX21vZGVsLykuIFRoaXMgaXMgdGhlIG9uZSBmaWxlIGluIHRoZSBwcm9qZWN0IHRoYXQgZXhwbGljaXRseSBleHBvc2VzIHZhcmlhYmxlcyBhbmQgbWV0aG9kcywgYW5kIGl0IG11c3QgYmUgc3RvcmVkIGluIHRoZSBNb2RlbCBmb2xkZXIgb2YgeW91ciBFcGljZW50ZXIgcHJvamVjdC4gSWYgYW4gb2JqZWN0LCBtYXkgaW5jbHVkZSBgbW9kZWxgLCBgc2NvcGVgLCBhbmQgYGZpbGVzYC4gKFNlZSB0aGUgW1J1biBNYW5hZ2VyXSguLi9ydW5fbWFuYWdlci8pIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGBzY29wZWAgYW5kIGBmaWxlc2AuKVxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgY3JlYXRlOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgY3JlYXRlT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucywgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKCdydW4nKSB9KTtcbiAgICAgICAgICAgIHZhciBydW5BcGlQYXJhbXMgPSBbJ21vZGVsJywgJ3Njb3BlJywgJ2ZpbGVzJ107XG4gICAgICAgICAgICBpZiAodHlwZW9mIHBhcmFtcyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICAvLyB0aGlzIGlzIGp1c3QgdGhlIG1vZGVsIG5hbWVcbiAgICAgICAgICAgICAgICBwYXJhbXMgPSB7IG1vZGVsOiBwYXJhbXMgfTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gd2hpdGVsaXN0IHRoZSBmaWVsZHMgdGhhdCB3ZSBhY3R1YWxseSBjYW4gc2VuZCB0byB0aGUgYXBpXG4gICAgICAgICAgICAgICAgcGFyYW1zID0gX3BpY2socGFyYW1zLCBydW5BcGlQYXJhbXMpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgb2xkU3VjY2VzcyA9IGNyZWF0ZU9wdGlvbnMuc3VjY2VzcztcbiAgICAgICAgICAgIGNyZWF0ZU9wdGlvbnMuc3VjY2VzcyA9IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IHJlc3BvbnNlLmlkOyAvL2FsbCBmdXR1cmUgY2hhaW5lZCBjYWxscyB0byBvcGVyYXRlIG9uIHRoaXMgaWRcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucy5pZCA9IHJlc3BvbnNlLmlkO1xuICAgICAgICAgICAgICAgIHJldHVybiBvbGRTdWNjZXNzLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wb3N0KHBhcmFtcywgY3JlYXRlT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJldHVybnMgcGFydGljdWxhciBydW5zLCBiYXNlZCBvbiBjb25kaXRpb25zIHNwZWNpZmllZCBpbiB0aGUgYHFzYCBvYmplY3QuXG4gICAgICAgICAqXG4gICAgICAgICAqIFRoZSBlbGVtZW50cyBvZiB0aGUgYHFzYCBvYmplY3QgYXJlIEFORGVkIHRvZ2V0aGVyIHdpdGhpbiBhIHNpbmdsZSBjYWxsIHRvIGAucXVlcnkoKWAuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgLy8gcmV0dXJucyBydW5zIHdpdGggc2F2ZWQgPSB0cnVlIGFuZCB2YXJpYWJsZXMucHJpY2UgPiAxLFxuICAgICAgICAgKiAgICAgIC8vIHdoZXJlIHZhcmlhYmxlcy5wcmljZSBoYXMgYmVlbiBwZXJzaXN0ZWQgKHJlY29yZGVkKVxuICAgICAgICAgKiAgICAgIC8vIGluIHRoZSBtb2RlbC5cbiAgICAgICAgICogICAgIHJzLnF1ZXJ5KHtcbiAgICAgICAgICogICAgICAgICAgJ3NhdmVkJzogJ3RydWUnLFxuICAgICAgICAgKiAgICAgICAgICAnLnByaWNlJzogJz4xJ1xuICAgICAgICAgKiAgICAgICB9LFxuICAgICAgICAgKiAgICAgICB7XG4gICAgICAgICAqICAgICAgICAgIHN0YXJ0cmVjb3JkOiAyLFxuICAgICAgICAgKiAgICAgICAgICBlbmRyZWNvcmQ6IDVcbiAgICAgICAgICogICAgICAgfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBxcyBRdWVyeSBvYmplY3QuIEVhY2gga2V5IGNhbiBiZSBhIHByb3BlcnR5IG9mIHRoZSBydW4gb3IgdGhlIG5hbWUgb2YgdmFyaWFibGUgdGhhdCBoYXMgYmVlbiBzYXZlZCBpbiB0aGUgcnVuIChwcmVmYWNlZCBieSBgdmFyaWFibGVzLmApLiBFYWNoIHZhbHVlIGNhbiBiZSBhIGxpdGVyYWwgdmFsdWUsIG9yIGEgY29tcGFyaXNvbiBvcGVyYXRvciBhbmQgdmFsdWUuIChTZWUgW21vcmUgb24gZmlsdGVyaW5nXSguLi8uLi8uLi9yZXN0X2FwaXMvYWdncmVnYXRlX3J1bl9hcGkvI2ZpbHRlcnMpIGFsbG93ZWQgaW4gdGhlIHVuZGVybHlpbmcgUnVuIEFQSS4pIFF1ZXJ5aW5nIGZvciB2YXJpYWJsZXMgaXMgYXZhaWxhYmxlIGZvciBydW5zIFtpbiBtZW1vcnldKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZS8jcnVucy1pbi1tZW1vcnkpIGFuZCBmb3IgcnVucyBbaW4gdGhlIGRhdGFiYXNlXSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvI3J1bnMtaW4tbWVtb3J5KSBpZiB0aGUgdmFyaWFibGVzIGFyZSBwZXJzaXN0ZWQgKGUuZy4gdGhhdCBoYXZlIGJlZW4gYHJlY29yZGBlZCBpbiB5b3VyIEp1bGlhIG1vZGVsKS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG91dHB1dE1vZGlmaWVyIChPcHRpb25hbCkgQXZhaWxhYmxlIGZpZWxkcyBpbmNsdWRlOiBgc3RhcnRyZWNvcmRgLCBgZW5kcmVjb3JkYCwgYHNvcnRgLCBhbmQgYGRpcmVjdGlvbmAgKGBhc2NgIG9yIGBkZXNjYCkuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBxdWVyeTogZnVuY3Rpb24gKHFzLCBvdXRwdXRNb2RpZmllciwgb3B0aW9ucykge1xuICAgICAgICAgICAgc2VydmljZU9wdGlvbnMuZmlsdGVyID0gcXM7IC8vc2hvdWxkbid0IGJlIGFibGUgdG8gb3Zlci1yaWRlXG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMpO1xuICAgICAgICAgICAgaHR0cE9wdGlvbnMgPSB1cmxDb25maWcuYWRkQXV0b1Jlc3RvcmVIZWFkZXIoaHR0cE9wdGlvbnMpO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5zcGxpdEdldChvdXRwdXRNb2RpZmllciwgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZXR1cm5zIHBhcnRpY3VsYXIgcnVucywgYmFzZWQgb24gY29uZGl0aW9ucyBzcGVjaWZpZWQgaW4gdGhlIGBxc2Agb2JqZWN0LlxuICAgICAgICAgKlxuICAgICAgICAgKiBTaW1pbGFyIHRvIGAucXVlcnkoKWAuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBmaWx0ZXIgRmlsdGVyIG9iamVjdC4gRWFjaCBrZXkgY2FuIGJlIGEgcHJvcGVydHkgb2YgdGhlIHJ1biBvciB0aGUgbmFtZSBvZiB2YXJpYWJsZSB0aGF0IGhhcyBiZWVuIHNhdmVkIGluIHRoZSBydW4gKHByZWZhY2VkIGJ5IGB2YXJpYWJsZXMuYCkuIEVhY2ggdmFsdWUgY2FuIGJlIGEgbGl0ZXJhbCB2YWx1ZSwgb3IgYSBjb21wYXJpc29uIG9wZXJhdG9yIGFuZCB2YWx1ZS4gKFNlZSBbbW9yZSBvbiBmaWx0ZXJpbmddKC4uLy4uLy4uL3Jlc3RfYXBpcy9hZ2dyZWdhdGVfcnVuX2FwaS8jZmlsdGVycykgYWxsb3dlZCBpbiB0aGUgdW5kZXJseWluZyBSdW4gQVBJLikgRmlsdGVyaW5nIGZvciB2YXJpYWJsZXMgaXMgYXZhaWxhYmxlIGZvciBydW5zIFtpbiBtZW1vcnldKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZS8jcnVucy1pbi1tZW1vcnkpIGFuZCBmb3IgcnVucyBbaW4gdGhlIGRhdGFiYXNlXSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvI3J1bnMtaW4tbWVtb3J5KSBpZiB0aGUgdmFyaWFibGVzIGFyZSBwZXJzaXN0ZWQgKGUuZy4gdGhhdCBoYXZlIGJlZW4gYHJlY29yZGBlZCBpbiB5b3VyIEp1bGlhIG1vZGVsKS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG91dHB1dE1vZGlmaWVyIChPcHRpb25hbCkgQXZhaWxhYmxlIGZpZWxkcyBpbmNsdWRlOiBgc3RhcnRyZWNvcmRgLCBgZW5kcmVjb3JkYCwgYHNvcnRgLCBhbmQgYGRpcmVjdGlvbmAgKGBhc2NgIG9yIGBkZXNjYCkuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBmaWx0ZXI6IGZ1bmN0aW9uIChmaWx0ZXIsIG91dHB1dE1vZGlmaWVyLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBpZiAoJC5pc1BsYWluT2JqZWN0KHNlcnZpY2VPcHRpb25zLmZpbHRlcikpIHtcbiAgICAgICAgICAgICAgICAkLmV4dGVuZChzZXJ2aWNlT3B0aW9ucy5maWx0ZXIsIGZpbHRlcik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IGZpbHRlcjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICBodHRwT3B0aW9ucyA9IHVybENvbmZpZy5hZGRBdXRvUmVzdG9yZUhlYWRlcihodHRwT3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5zcGxpdEdldChvdXRwdXRNb2RpZmllciwgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBHZXQgZGF0YSBmb3IgYSBzcGVjaWZpYyBydW4uIFRoaXMgaW5jbHVkZXMgc3RhbmRhcmQgcnVuIGRhdGEgc3VjaCBhcyB0aGUgYWNjb3VudCwgbW9kZWwsIHByb2plY3QsIGFuZCBjcmVhdGVkIGFuZCBsYXN0IG1vZGlmaWVkIGRhdGVzLiBUbyByZXF1ZXN0IHNwZWNpZmljIG1vZGVsIHZhcmlhYmxlcywgcGFzcyB0aGVtIGFzIHBhcnQgb2YgdGhlIGBmaWx0ZXJzYCBwYXJhbWV0ZXIuXG4gICAgICAgICAqXG4gICAgICAgICAqIE5vdGUgdGhhdCBpZiB0aGUgcnVuIGlzIFtpbiBtZW1vcnldKC4uLy4uLy4uL3J1bl9wZXJzaXN0ZW5jZS8jcnVucy1pbi1tZW1vcnkpLCBhbnkgbW9kZWwgdmFyaWFibGVzIGFyZSBhdmFpbGFibGU7IGlmIHRoZSBydW4gaXMgW2luIHRoZSBkYXRhYmFzZV0oLi4vLi4vLi4vcnVuX3BlcnNpc3RlbmNlLyNydW5zLWluLWRiKSwgb25seSBtb2RlbCB2YXJpYWJsZXMgdGhhdCBoYXZlIGJlZW4gcGVyc2lzdGVkICZtZGFzaDsgdGhhdCBpcywgYHJlY29yZGBlZCBpbiB5b3VyIEp1bGlhIG1vZGVsICZtZGFzaDsgYXJlIGF2YWlsYWJsZS5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgIHJzLmxvYWQoJ2JiNTg5Njc3LWQ0NzYtNDk3MS1hNjhlLTBjNThkMTkxZTQ1MCcsIHsgaW5jbHVkZTogWycucHJpY2UnLCAnLnNhbGVzJ10gfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSBydW5JRCBUaGUgcnVuIGlkLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gZmlsdGVycyAoT3B0aW9uYWwpIE9iamVjdCBjb250YWluaW5nIGZpbHRlcnMgYW5kIG9wZXJhdGlvbiBtb2RpZmllcnMuIFVzZSBrZXkgYGluY2x1ZGVgIHRvIGxpc3QgbW9kZWwgdmFyaWFibGVzIHRoYXQgeW91IHdhbnQgdG8gaW5jbHVkZSBpbiB0aGUgcmVzcG9uc2UuIE90aGVyIGF2YWlsYWJsZSBmaWVsZHMgaW5jbHVkZTogYHN0YXJ0cmVjb3JkYCwgYGVuZHJlY29yZGAsIGBzb3J0YCwgYW5kIGBkaXJlY3Rpb25gIChgYXNjYCBvciBgZGVzY2ApLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgbG9hZDogZnVuY3Rpb24gKHJ1bklELCBmaWx0ZXJzLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBpZiAocnVuSUQpIHtcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgPSBydW5JRDsgLy9zaG91bGRuJ3QgYmUgYWJsZSB0byBvdmVyLXJpZGVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICBodHRwT3B0aW9ucyA9IHVybENvbmZpZy5hZGRBdXRvUmVzdG9yZUhlYWRlcihodHRwT3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQoZmlsdGVycywgaHR0cE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNhdmUgYXR0cmlidXRlcyAoZGF0YSwgbW9kZWwgdmFyaWFibGVzKSBvZiB0aGUgcnVuLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGVzKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgIC8vIGFkZCAnY29tcGxldGVkJyBmaWVsZCB0byBydW4gcmVjb3JkXG4gICAgICAgICAqICAgICBycy5zYXZlKHsgY29tcGxldGVkOiB0cnVlIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgLy8gdXBkYXRlICdzYXZlZCcgZmllbGQgb2YgcnVuIHJlY29yZCwgYW5kIHVwZGF0ZSB2YWx1ZXMgb2YgbW9kZWwgdmFyaWFibGVzIGZvciB0aGlzIHJ1blxuICAgICAgICAgKiAgICAgcnMuc2F2ZSh7IHNhdmVkOiB0cnVlLCB2YXJpYWJsZXM6IHsgYTogMjMsIGI6IDIzIH0gfSk7XG4gICAgICAgICAqXG4gICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBhdHRyaWJ1dGVzIFRoZSBydW4gZGF0YSBhbmQgdmFyaWFibGVzIHRvIHNhdmUuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBhdHRyaWJ1dGVzLnZhcmlhYmxlcyBNb2RlbCB2YXJpYWJsZXMgbXVzdCBiZSBpbmNsdWRlZCBpbiBhIGB2YXJpYWJsZXNgIGZpZWxkIHdpdGhpbiB0aGUgYGF0dHJpYnV0ZXNgIG9iamVjdC4gKE90aGVyd2lzZSB0aGV5IGFyZSB0cmVhdGVkIGFzIHJ1biBkYXRhIGFuZCBhZGRlZCB0byB0aGUgcnVuIHJlY29yZCBkaXJlY3RseS4pXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBzYXZlOiBmdW5jdGlvbiAoYXR0cmlidXRlcywgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgIHNldEZpbHRlck9yVGhyb3dFcnJvcihodHRwT3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wYXRjaChhdHRyaWJ1dGVzLCBodHRwT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENhbGwgYSBtZXRob2QgZnJvbSB0aGUgbW9kZWwuXG4gICAgICAgICAqXG4gICAgICAgICAqIERlcGVuZGluZyBvbiB0aGUgbGFuZ3VhZ2UgaW4gd2hpY2ggeW91IGhhdmUgd3JpdHRlbiB5b3VyIG1vZGVsLCB0aGUgbWV0aG9kIG1heSBuZWVkIHRvIGJlIGV4cG9zZWQgKGUuZy4gYGV4cG9ydGAgZm9yIGEgSnVsaWEgbW9kZWwpIGluIHRoZSBtb2RlbCBmaWxlIGluIG9yZGVyIHRvIGJlIGNhbGxlZCB0aHJvdWdoIHRoZSBBUEkuIFNlZSBbV3JpdGluZyB5b3VyIE1vZGVsXSguLi8uLi8uLi93cml0aW5nX3lvdXJfbW9kZWwvKSkuXG4gICAgICAgICAqXG4gICAgICAgICAqIFRoZSBgcGFyYW1zYCBhcmd1bWVudCBpcyBub3JtYWxseSBhbiBhcnJheSBvZiBhcmd1bWVudHMgdG8gdGhlIGBvcGVyYXRpb25gLiBJbiB0aGUgc3BlY2lhbCBjYXNlIHdoZXJlIGBvcGVyYXRpb25gIG9ubHkgdGFrZXMgb25lIGFyZ3VtZW50LCB5b3UgYXJlIG5vdCByZXF1aXJlZCB0byBwdXQgdGhhdCBhcmd1bWVudCBpbnRvIGFuIGFycmF5LlxuICAgICAgICAgKlxuICAgICAgICAgKiBOb3RlIHRoYXQgeW91IGNhbiBjb21iaW5lIHRoZSBgb3BlcmF0aW9uYCBhbmQgYHBhcmFtc2AgYXJndW1lbnRzIGludG8gYSBzaW5nbGUgb2JqZWN0IGlmIHlvdSBwcmVmZXIsIGFzIGluIHRoZSBsYXN0IGV4YW1wbGUuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZXMqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIC8vIG1ldGhvZCBcInNvbHZlXCIgdGFrZXMgbm8gYXJndW1lbnRzXG4gICAgICAgICAqICAgICBycy5kbygnc29sdmUnKTtcbiAgICAgICAgICogICAgICAvLyBtZXRob2QgXCJlY2hvXCIgdGFrZXMgb25lIGFyZ3VtZW50LCBhIHN0cmluZ1xuICAgICAgICAgKiAgICAgcnMuZG8oJ2VjaG8nLCBbJ2hlbGxvJ10pO1xuICAgICAgICAgKiAgICAgIC8vIG1ldGhvZCBcImVjaG9cIiB0YWtlcyBvbmUgYXJndW1lbnQsIGEgc3RyaW5nXG4gICAgICAgICAqICAgICBycy5kbygnZWNobycsICdoZWxsbycpO1xuICAgICAgICAgKiAgICAgIC8vIG1ldGhvZCBcInN1bUFycmF5XCIgdGFrZXMgb25lIGFyZ3VtZW50LCBhbiBhcnJheVxuICAgICAgICAgKiAgICAgcnMuZG8oJ3N1bUFycmF5JywgW1s0LDIsMV1dKTtcbiAgICAgICAgICogICAgICAvLyBtZXRob2QgXCJhZGRcIiB0YWtlcyB0d28gYXJndW1lbnRzLCBib3RoIGludGVnZXJzXG4gICAgICAgICAqICAgICBycy5kbyh7IG5hbWU6J2FkZCcsIHBhcmFtczpbMiw0XSB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IG9wZXJhdGlvbiBOYW1lIG9mIG1ldGhvZC5cbiAgICAgICAgICogQHBhcmFtIHtBcnJheX0gcGFyYW1zIChPcHRpb25hbCkgQW55IHBhcmFtZXRlcnMgdGhlIG9wZXJhdGlvbiB0YWtlcywgcGFzc2VkIGFzIGFuIGFycmF5LiBJbiB0aGUgc3BlY2lhbCBjYXNlIHdoZXJlIGBvcGVyYXRpb25gIG9ubHkgdGFrZXMgb25lIGFyZ3VtZW50LCB5b3UgYXJlIG5vdCByZXF1aXJlZCB0byBwdXQgdGhhdCBhcmd1bWVudCBpbnRvIGFuIGFycmF5LCBhbmQgY2FuIGp1c3QgcGFzcyBpdCBkaXJlY3RseS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgIGRvOiBmdW5jdGlvbiAob3BlcmF0aW9uLCBwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCdkbycsIG9wZXJhdGlvbiwgcGFyYW1zKTtcbiAgICAgICAgICAgIHZhciBvcHNBcmdzO1xuICAgICAgICAgICAgdmFyIHBvc3RPcHRpb25zO1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMpIHtcbiAgICAgICAgICAgICAgICBvcHNBcmdzID0gcGFyYW1zO1xuICAgICAgICAgICAgICAgIHBvc3RPcHRpb25zID0gb3B0aW9ucztcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoJC5pc1BsYWluT2JqZWN0KHBhcmFtcykpIHtcbiAgICAgICAgICAgICAgICBvcHNBcmdzID0gbnVsbDtcbiAgICAgICAgICAgICAgICBwb3N0T3B0aW9ucyA9IHBhcmFtcztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgb3BzQXJncyA9IHBhcmFtcztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciByZXN1bHQgPSBydXRpbC5ub3JtYWxpemVPcGVyYXRpb25zKG9wZXJhdGlvbiwgb3BzQXJncyk7XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIHBvc3RPcHRpb25zKTtcblxuICAgICAgICAgICAgc2V0RmlsdGVyT3JUaHJvd0Vycm9yKGh0dHBPcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIHBybXMgPSAocmVzdWx0LmFyZ3NbMF0ubGVuZ3RoICYmIChyZXN1bHQuYXJnc1swXSAhPT0gbnVsbCAmJiByZXN1bHQuYXJnc1swXSAhPT0gdW5kZWZpbmVkKSkgPyByZXN1bHQuYXJnc1swXSA6IFtdO1xuICAgICAgICAgICAgcmV0dXJuIGh0dHAucG9zdCh7IGFyZ3VtZW50czogcHJtcyB9LCAkLmV4dGVuZCh0cnVlLCB7fSwgaHR0cE9wdGlvbnMsIHtcbiAgICAgICAgICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRGaWx0ZXJVUkwoKSArICdvcGVyYXRpb25zLycgKyByZXN1bHQub3BzWzBdICsgJy8nXG4gICAgICAgICAgICB9KSk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENhbGwgc2V2ZXJhbCBtZXRob2RzIGZyb20gdGhlIG1vZGVsLCBzZXF1ZW50aWFsbHkuXG4gICAgICAgICAqXG4gICAgICAgICAqIERlcGVuZGluZyBvbiB0aGUgbGFuZ3VhZ2UgaW4gd2hpY2ggeW91IGhhdmUgd3JpdHRlbiB5b3VyIG1vZGVsLCB0aGUgbWV0aG9kcyBtYXkgbmVlZCB0byBiZSBleHBvc2VkIChlLmcuIGBleHBvcnRgIGZvciBhIEp1bGlhIG1vZGVsKSBpbiB0aGUgbW9kZWwgZmlsZSBpbiBvcmRlciB0byBiZSBjYWxsZWQgdGhyb3VnaCB0aGUgQVBJLiBTZWUgW1dyaXRpbmcgeW91ciBNb2RlbF0oLi4vLi4vLi4vd3JpdGluZ195b3VyX21vZGVsLykpLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGVzKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBtZXRob2RzIFwiaW5pdGlhbGl6ZVwiIGFuZCBcInNvbHZlXCIgZG8gbm90IHRha2UgYW55IGFyZ3VtZW50c1xuICAgICAgICAgKiAgICAgcnMuc2VyaWFsKFsnaW5pdGlhbGl6ZScsICdzb2x2ZSddKTtcbiAgICAgICAgICogICAgICAvLyBtZXRob2RzIFwiaW5pdFwiIGFuZCBcInJlc2V0XCIgdGFrZSB0d28gYXJndW1lbnRzIGVhY2hcbiAgICAgICAgICogICAgIHJzLnNlcmlhbChbICB7IG5hbWU6ICdpbml0JywgcGFyYW1zOiBbMSwyXSB9LFxuICAgICAgICAgKiAgICAgICAgICAgICAgICAgIHsgbmFtZTogJ3Jlc2V0JywgcGFyYW1zOiBbMiwzXSB9XSk7XG4gICAgICAgICAqICAgICAgLy8gbWV0aG9kIFwiaW5pdFwiIHRha2VzIHR3byBhcmd1bWVudHMsXG4gICAgICAgICAqICAgICAgLy8gbWV0aG9kIFwicnVubW9kZWxcIiB0YWtlcyBub25lXG4gICAgICAgICAqICAgICBycy5zZXJpYWwoWyAgeyBuYW1lOiAnaW5pdCcsIHBhcmFtczogWzEsMl0gfSxcbiAgICAgICAgICogICAgICAgICAgICAgICAgICB7IG5hbWU6ICdydW5tb2RlbCcsIHBhcmFtczogW10gfV0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSBvcGVyYXRpb25zIElmIG5vbmUgb2YgdGhlIG1ldGhvZHMgdGFrZSBwYXJhbWV0ZXJzLCBwYXNzIGFuIGFycmF5IG9mIHRoZSBtZXRob2QgbmFtZXMgKHN0cmluZ3MpLiBJZiBhbnkgb2YgdGhlIG1ldGhvZHMgZG8gdGFrZSBwYXJhbWV0ZXJzLCBwYXNzIGFuIGFycmF5IG9mIG9iamVjdHMsIGVhY2ggb2Ygd2hpY2ggY29udGFpbnMgYSBtZXRob2QgbmFtZSBhbmQgaXRzIG93biAocG9zc2libHkgZW1wdHkpIGFycmF5IG9mIHBhcmFtZXRlcnMuXG4gICAgICAgICAqIEBwYXJhbSB7Kn0gcGFyYW1zIFBhcmFtZXRlcnMgdG8gcGFzcyB0byBvcGVyYXRpb25zLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgc2VyaWFsOiBmdW5jdGlvbiAob3BlcmF0aW9ucywgcGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgb3BQYXJhbXMgPSBydXRpbC5ub3JtYWxpemVPcGVyYXRpb25zKG9wZXJhdGlvbnMsIHBhcmFtcyk7XG4gICAgICAgICAgICB2YXIgb3BzID0gb3BQYXJhbXMub3BzO1xuICAgICAgICAgICAgdmFyIGFyZ3MgPSBvcFBhcmFtcy5hcmdzO1xuICAgICAgICAgICAgdmFyIG1lID0gdGhpcztcblxuICAgICAgICAgICAgdmFyICRkID0gJC5EZWZlcnJlZCgpO1xuICAgICAgICAgICAgdmFyIHBvc3RPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIGRvU2luZ2xlT3AgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgdmFyIG9wID0gb3BzLnNoaWZ0KCk7XG4gICAgICAgICAgICAgICAgdmFyIGFyZyA9IGFyZ3Muc2hpZnQoKTtcblxuICAgICAgICAgICAgICAgIG1lLmRvKG9wLCBhcmcsIHtcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG9wcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb1NpbmdsZU9wKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICRkLnJlc29sdmUuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3N0T3B0aW9ucy5zdWNjZXNzLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAkZC5yZWplY3QuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBvc3RPcHRpb25zLmVycm9yLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIGRvU2luZ2xlT3AoKTtcblxuICAgICAgICAgICAgcmV0dXJuICRkLnByb21pc2UoKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ2FsbCBzZXZlcmFsIG1ldGhvZHMgZnJvbSB0aGUgbW9kZWwsIGV4ZWN1dGluZyB0aGVtIGluIHBhcmFsbGVsLlxuICAgICAgICAgKlxuICAgICAgICAgKiBEZXBlbmRpbmcgb24gdGhlIGxhbmd1YWdlIGluIHdoaWNoIHlvdSBoYXZlIHdyaXR0ZW4geW91ciBtb2RlbCwgdGhlIG1ldGhvZHMgbWF5IG5lZWQgdG8gYmUgZXhwb3NlZCAoZS5nLiBgZXhwb3J0YCBmb3IgYSBKdWxpYSBtb2RlbCkgaW4gdGhlIG1vZGVsIGZpbGUgaW4gb3JkZXIgdG8gYmUgY2FsbGVkIHRocm91Z2ggdGhlIEFQSS4gU2VlIFtXcml0aW5nIHlvdXIgTW9kZWxdKC4uLy4uLy4uL3dyaXRpbmdfeW91cl9tb2RlbC8pKS5cbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICAvLyBtZXRob2RzIFwic29sdmVcIiBhbmQgXCJyZXNldFwiIGRvIG5vdCB0YWtlIGFueSBhcmd1bWVudHNcbiAgICAgICAgICogICAgIHJzLnBhcmFsbGVsKFsnc29sdmUnLCAncmVzZXQnXSk7XG4gICAgICAgICAqICAgICAgLy8gbWV0aG9kcyBcImFkZFwiIGFuZCBcInN1YnRyYWN0XCIgdGFrZSB0d28gYXJndW1lbnRzIGVhY2hcbiAgICAgICAgICogICAgIHJzLnBhcmFsbGVsKFsgeyBuYW1lOiAnYWRkJywgcGFyYW1zOiBbMSwyXSB9LFxuICAgICAgICAgKiAgICAgICAgICAgICAgICAgICB7IG5hbWU6ICdzdWJ0cmFjdCcsIHBhcmFtczpbMiwzXSB9XSk7XG4gICAgICAgICAqICAgICAgLy8gbWV0aG9kcyBcImFkZFwiIGFuZCBcInN1YnRyYWN0XCIgdGFrZSB0d28gYXJndW1lbnRzIGVhY2hcbiAgICAgICAgICogICAgIHJzLnBhcmFsbGVsKHsgYWRkOiBbMSwyXSwgc3VidHJhY3Q6IFsyLDRdIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge0FycmF5fE9iamVjdH0gb3BlcmF0aW9ucyBJZiBub25lIG9mIHRoZSBtZXRob2RzIHRha2UgcGFyYW1ldGVycywgcGFzcyBhbiBhcnJheSBvZiB0aGUgbWV0aG9kIG5hbWVzIChhcyBzdHJpbmdzKS4gSWYgYW55IG9mIHRoZSBtZXRob2RzIGRvIHRha2UgcGFyYW1ldGVycywgeW91IGhhdmUgdHdvIG9wdGlvbnMuIFlvdSBjYW4gcGFzcyBhbiBhcnJheSBvZiBvYmplY3RzLCBlYWNoIG9mIHdoaWNoIGNvbnRhaW5zIGEgbWV0aG9kIG5hbWUgYW5kIGl0cyBvd24gKHBvc3NpYmx5IGVtcHR5KSBhcnJheSBvZiBwYXJhbWV0ZXJzLiBBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIHBhc3MgYSBzaW5nbGUgb2JqZWN0IHdpdGggdGhlIG1ldGhvZCBuYW1lIGFuZCBhIChwb3NzaWJseSBlbXB0eSkgYXJyYXkgb2YgcGFyYW1ldGVycy5cbiAgICAgICAgICogQHBhcmFtIHsqfSBwYXJhbXMgUGFyYW1ldGVycyB0byBwYXNzIHRvIG9wZXJhdGlvbnMuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBwYXJhbGxlbDogZnVuY3Rpb24gKG9wZXJhdGlvbnMsIHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyICRkID0gJC5EZWZlcnJlZCgpO1xuXG4gICAgICAgICAgICB2YXIgb3BQYXJhbXMgPSBydXRpbC5ub3JtYWxpemVPcGVyYXRpb25zKG9wZXJhdGlvbnMsIHBhcmFtcyk7XG4gICAgICAgICAgICB2YXIgb3BzID0gb3BQYXJhbXMub3BzO1xuICAgICAgICAgICAgdmFyIGFyZ3MgPSBvcFBhcmFtcy5hcmdzO1xuICAgICAgICAgICAgdmFyIHBvc3RPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIHF1ZXVlID0gW107XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG9wcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHF1ZXVlLnB1c2goXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZG8ob3BzW2ldLCBhcmdzW2ldKVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAkLndoZW4uYXBwbHkodGhpcywgcXVldWUpXG4gICAgICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAkZC5yZXNvbHZlLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICAgICAgICAgIHBvc3RPcHRpb25zLnN1Y2Nlc3MuYXBwbHkodGhpcy5hcmd1bWVudHMpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZhaWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAkZC5yZWplY3QuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgcG9zdE9wdGlvbnMuZXJyb3IuYXBwbHkodGhpcy5hcmd1bWVudHMpO1xuICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gJGQucHJvbWlzZSgpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBTaG9ydGN1dCB0byB1c2luZyB0aGUgW0ludHJvc3BlY3Rpb24gQVBJIFNlcnZpY2VdKC4uL2ludHJvc3BlY3Rpb24tYXBpLXNlcnZpY2UvKS4gQWxsb3dzIHlvdSB0byB2aWV3IGEgbGlzdCBvZiB0aGUgdmFyaWFibGVzIGFuZCBvcGVyYXRpb25zIGluIGEgbW9kZWwuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICBycy5pbnRyb3NwZWN0KHsgcnVuSUQ6ICdjYmY4NTQzNy1iNTM5LTQ5NzctYTFmYy0yMzUxNWNmMDcxYmInIH0pLnRoZW4oZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgICAgICogICAgICAgICAgY29uc29sZS5sb2coZGF0YS5mdW5jdGlvbnMpO1xuICAgICAgICAgKiAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhLnZhcmlhYmxlcyk7XG4gICAgICAgICAqICAgICB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgICogQHBhcmFtICB7T2JqZWN0fSBvcHRpb25zIE9wdGlvbnMgY2FuIGVpdGhlciBiZSBvZiB0aGUgZm9ybSBgeyBydW5JRDogPHJ1bmlkPiB9YCBvciBgeyBtb2RlbDogPG1vZGVsRmlsZU5hbWU+IH1gLlxuICAgICAgICAgKiBAcGFyYW0gIHtPYmplY3R9IGludHJvc3BlY3Rpb25Db25maWcgKE9wdGlvbmFsKSBTZXJ2aWNlIG9wdGlvbnMgZm9yIEludHJvc3BlY3Rpb24gU2VydmljZVxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgaW50cm9zcGVjdDogZnVuY3Rpb24gKG9wdGlvbnMsIGludHJvc3BlY3Rpb25Db25maWcpIHtcbiAgICAgICAgICAgIHZhciBpbnRyb3NwZWN0aW9uID0gbmV3IEludHJvc3BlY3Rpb25TZXJ2aWNlKCQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgaW50cm9zcGVjdGlvbkNvbmZpZykpO1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMpIHtcbiAgICAgICAgICAgICAgICBpZiAob3B0aW9ucy5ydW5JRCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaW50cm9zcGVjdGlvbi5ieVJ1bklEKG9wdGlvbnMucnVuSUQpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAob3B0aW9ucy5tb2RlbCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaW50cm9zcGVjdGlvbi5ieU1vZGVsKG9wdGlvbnMubW9kZWwpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoc2VydmljZU9wdGlvbnMuaWQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaW50cm9zcGVjdGlvbi5ieVJ1bklEKHNlcnZpY2VPcHRpb25zLmlkKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQbGVhc2Ugc3BlY2lmeSBlaXRoZXIgdGhlIG1vZGVsIG9yIHJ1bmlkIHRvIGludHJvc3BlY3QnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICB2YXIgcHVibGljU3luY0FQSSA9IHtcbiAgICAgICAgZ2V0Q3VycmVudENvbmZpZzogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIHNlcnZpY2VPcHRpb25zO1xuICAgICAgICB9LFxuICAgICAgICAvKipcbiAgICAgICAgICAqIFJldHVybnMgYSBWYXJpYWJsZXMgU2VydmljZSBpbnN0YW5jZS4gVXNlIHRoZSB2YXJpYWJsZXMgaW5zdGFuY2UgdG8gbG9hZCwgc2F2ZSwgYW5kIHF1ZXJ5IGZvciBzcGVjaWZpYyBtb2RlbCB2YXJpYWJsZXMuIFNlZSB0aGUgW1ZhcmlhYmxlIEFQSSBTZXJ2aWNlXSguLi92YXJpYWJsZXMtYXBpLXNlcnZpY2UvKSBmb3IgbW9yZSBpbmZvcm1hdGlvbi5cbiAgICAgICAgICAqXG4gICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgICpcbiAgICAgICAgICAqICAgICAgdmFyIHZzID0gcnMudmFyaWFibGVzKCk7XG4gICAgICAgICAgKiAgICAgIHZzLnNhdmUoeyBzYW1wbGVfaW50OiA0IH0pO1xuICAgICAgICAgICpcbiAgICAgICAgICAqICoqUGFyYW1ldGVycyoqXG4gICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIChPcHRpb25hbCkgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAgICAgICAgKiBAcmV0dXJuIHtPYmplY3R9IHZhcmlhYmxlc1NlcnZpY2UgSW5zdGFuY2VcbiAgICAgICAgICAqL1xuICAgICAgICB2YXJpYWJsZXM6IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICAgICAgICAgIHZhciB2cyA9IG5ldyBWYXJpYWJsZXNTZXJ2aWNlKCQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgY29uZmlnLCB7XG4gICAgICAgICAgICAgICAgcnVuU2VydmljZTogdGhpc1xuICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgcmV0dXJuIHZzO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FzeW5jQVBJKTtcbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNTeW5jQVBJKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xudmFyIG9iamVjdEFzc2lnbiA9IHJlcXVpcmUoJ29iamVjdC1hc3NpZ24nKTtcblxudmFyIHNlcnZpY2VVdGlscyA9IHtcbiAgICAvKlxuICAgICogR2V0cyB0aGUgZGVmYXVsdCBvcHRpb25zIGZvciBhIGFwaSBzZXJ2aWNlLlxuICAgICogSXQgd2lsbCBtZXJnZTpcbiAgICAqIC0gVGhlIFNlc3Npb24gb3B0aW9ucyAoVXNpbmcgdGhlIFNlc3Npb24gTWFuYWdlcilcbiAgICAqIC0gVGhlIEF1dGhvcml6YXRpb24gSGVhZGVyIGZyb20gdGhlIHRva2VuIG9wdGlvblxuICAgICogLSBUaGUgZnVsbCB1cmwgZnJvbSB0aGUgZW5kcG9pbnQgb3B0aW9uXG4gICAgKiBXaXRoIHRoZSBzdXBwbGllZCBvdmVycmlkZXMgYW5kIGRlZmF1bHRzXG4gICAgKlxuICAgICovXG4gICAgZ2V0RGVmYXVsdE9wdGlvbnM6IGZ1bmN0aW9uIChkZWZhdWx0cykge1xuICAgICAgICB2YXIgcmVzdCA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSk7XG4gICAgICAgIHZhciBzZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgICAgICB2YXIgc2VydmljZU9wdGlvbnMgPSBzZXNzaW9uTWFuYWdlci5nZXRNZXJnZWRPcHRpb25zLmFwcGx5KHNlc3Npb25NYW5hZ2VyLCBbZGVmYXVsdHNdLmNvbmNhdChyZXN0KSk7XG5cbiAgICAgICAgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0ID0gb2JqZWN0QXNzaWduKHt9LCBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQsIHtcbiAgICAgICAgICAgIHVybDogdGhpcy5nZXRBcGlVcmwoc2VydmljZU9wdGlvbnMuYXBpRW5kcG9pbnQsIHNlcnZpY2VPcHRpb25zKVxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoc2VydmljZU9wdGlvbnMudG9rZW4pIHtcbiAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydC5oZWFkZXJzID0ge1xuICAgICAgICAgICAgICAgIEF1dGhvcml6YXRpb246ICdCZWFyZXIgJyArIHNlcnZpY2VPcHRpb25zLnRva2VuXG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzZXJ2aWNlT3B0aW9ucztcbiAgICB9LFxuXG4gICAgZ2V0QXBpVXJsOiBmdW5jdGlvbiAoYXBpRW5kcG9pbnQsIHNlcnZpY2VPcHRpb25zKSB7XG4gICAgICAgIHZhciB1cmxDb25maWcgPSBuZXcgQ29uZmlnU2VydmljZShzZXJ2aWNlT3B0aW9ucykuZ2V0KCdzZXJ2ZXInKTtcbiAgICAgICAgcmV0dXJuIHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KTtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHNlcnZpY2VVdGlsczsiLCIndXNlIHN0cmljdCc7XG4vKipcbiAqICMjIFN0YXRlIEFQSSBBZGFwdGVyXG4gKlxuICogVGhlIFN0YXRlIEFQSSBBZGFwdGVyIGFsbG93cyB5b3UgdG8gcmVwbGF5IG9yIGNsb25lIHJ1bnMuIEl0IGJyaW5ncyBleGlzdGluZywgcGVyc2lzdGVkIHJ1biBkYXRhIGZyb20gdGhlIGRhdGFiYXNlIGJhY2sgaW50byBtZW1vcnksIHVzaW5nIHRoZSBzYW1lIHJ1biBpZCAoYHJlcGxheWApIG9yIGEgbmV3IHJ1biBpZCAoYGNsb25lYCkuIFJ1bnMgbXVzdCBiZSBpbiBtZW1vcnkgaW4gb3JkZXIgZm9yIHlvdSB0byB1cGRhdGUgdmFyaWFibGVzIG9yIGNhbGwgb3BlcmF0aW9ucyBvbiB0aGVtLlxuICpcbiAqIFNwZWNpZmljYWxseSwgdGhlIFN0YXRlIEFQSSBBZGFwdGVyIHdvcmtzIGJ5IFwicmUtcnVubmluZ1wiIHRoZSBydW4gKHVzZXIgaW50ZXJhY3Rpb25zKSBmcm9tIHRoZSBjcmVhdGlvbiBvZiB0aGUgcnVuIHVwIHRvIHRoZSB0aW1lIGl0IHdhcyBsYXN0IHBlcnNpc3RlZCBpbiB0aGUgZGF0YWJhc2UuIFRoaXMgcHJvY2VzcyB1c2VzIHRoZSBjdXJyZW50IHZlcnNpb24gb2YgdGhlIHJ1bidzIG1vZGVsLiBUaGVyZWZvcmUsIGlmIHRoZSBtb2RlbCBoYXMgY2hhbmdlZCBzaW5jZSB0aGUgb3JpZ2luYWwgcnVuIHdhcyBjcmVhdGVkLCB0aGUgcmV0cmlldmVkIHJ1biB3aWxsIHVzZSB0aGUgbmV3IG1vZGVsIOKAlCBhbmQgbWF5IGVuZCB1cCBoYXZpbmcgZGlmZmVyZW50IHZhbHVlcyBvciBiZWhhdmlvciBhcyBhIHJlc3VsdC4gVXNlIHdpdGggY2FyZSFcbiAqXG4gKiBUbyB1c2UgdGhlIFN0YXRlIEFQSSBBZGFwdGVyLCBpbnN0YW50aWF0ZSBpdCBhbmQgdGhlbiBjYWxsIGl0cyBtZXRob2RzOlxuICpcbiAqICAgICAgdmFyIHNhID0gbmV3IEYuc2VydmljZS5TdGF0ZSgpO1xuICogICAgICBzYS5yZXBsYXkoe3J1bklkOiAnMTg0MmJiNWMtODNhZC00YmE4LWE5NTUtYmQxM2NjMmZkYjRmJ30pO1xuICpcbiAqIFRoZSBjb25zdHJ1Y3RvciB0YWtlcyBhbiBvcHRpb25hbCBgb3B0aW9uc2AgcGFyYW1ldGVyIGluIHdoaWNoIHlvdSBjYW4gc3BlY2lmeSB0aGUgYGFjY291bnRgIGFuZCBgcHJvamVjdGAgaWYgdGhleSBhcmUgbm90IGFscmVhZHkgYXZhaWxhYmxlIGluIHRoZSBjdXJyZW50IGNvbnRleHQuXG4gKlxuICovXG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbnZhciBfcGljayA9IHJlcXVpcmUoJy4uL3V0aWwvb2JqZWN0LXV0aWwnKS5fcGljaztcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xudmFyIGFwaUVuZHBvaW50ID0gJ21vZGVsL3N0YXRlJztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG5cbiAgICB2YXIgZGVmYXVsdHMgPSB7XG5cbiAgICB9O1xuXG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgY29uZmlnKTtcbiAgICB2YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2Uoc2VydmljZU9wdGlvbnMpLmdldCgnc2VydmVyJyk7XG5cbiAgICB2YXIgdHJhbnNwb3J0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucy50cmFuc3BvcnQsIHtcbiAgICAgICAgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludClcbiAgICB9KTtcblxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICB0cmFuc3BvcnRPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHZhciBodHRwID0gbmV3IFRyYW5zcG9ydEZhY3RvcnkodHJhbnNwb3J0T3B0aW9ucyk7XG4gICAgdmFyIHBhcnNlUnVuSWRPckVycm9yID0gZnVuY3Rpb24gKHBhcmFtcykge1xuICAgICAgICBpZiAoJC5pc1BsYWluT2JqZWN0KHBhcmFtcykgJiYgcGFyYW1zLnJ1bklkKSB7XG4gICAgICAgICAgICByZXR1cm4gcGFyYW1zLnJ1bklkO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQbGVhc2UgcGFzcyBpbiBhIHJ1biBpZCcpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHZhciBwdWJsaWNBUEkgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAqIFJlcGxheSBhIHJ1bi4gQWZ0ZXIgdGhpcyBjYWxsLCB0aGUgcnVuLCB3aXRoIGl0cyBvcmlnaW5hbCBydW4gaWQsIGlzIG5vdyBhdmFpbGFibGUgW2luIG1lbW9yeV0oLi4vLi4vLi4vcnVuX3BlcnNpc3RlbmNlLyNydW5zLWluLW1lbW9yeSkuIChJdCBjb250aW51ZXMgdG8gYmUgcGVyc2lzdGVkIGludG8gdGhlIEVwaWNlbnRlciBkYXRhYmFzZSBhdCByZWd1bGFyIGludGVydmFscy4pXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHNhID0gbmV3IEYuc2VydmljZS5TdGF0ZSgpO1xuICAgICAgICAqICAgICAgc2EucmVwbGF5KHtydW5JZDogJzE4NDJiYjVjLTgzYWQtNGJhOC1hOTU1LWJkMTNjYzJmZGI0ZicsIHN0b3BCZWZvcmU6ICdjYWxjdWxhdGVTY29yZSd9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgUGFyYW1ldGVycyBvYmplY3QuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5ydW5JZCBUaGUgaWQgb2YgdGhlIHJ1biB0byBicmluZyBiYWNrIHRvIG1lbW9yeS5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1zLnN0b3BCZWZvcmUgKE9wdGlvbmFsKSBUaGUgcnVuIGlzIGFkdmFuY2VkIG9ubHkgdXAgdG8gdGhlIGZpcnN0IG9jY3VycmVuY2Ugb2YgdGhpcyBtZXRob2QuXG4gICAgICAgICogQHBhcmFtIHthcnJheX0gcGFyYW1zLmV4Y2x1ZGUgKE9wdGlvbmFsKSBBcnJheSBvZiBtZXRob2RzIHRvIGV4Y2x1ZGUgd2hlbiBhZHZhbmNpbmcgdGhlIHJ1bi5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIHJlcGxheTogZnVuY3Rpb24gKHBhcmFtcywgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIHJ1bklkID0gcGFyc2VSdW5JZE9yRXJyb3IocGFyYW1zKTtcblxuICAgICAgICAgICAgdmFyIHJlcGxheU9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBydW5JZCB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICBwYXJhbXMgPSAkLmV4dGVuZCh0cnVlLCB7IGFjdGlvbjogJ3JlcGxheScgfSwgX3BpY2socGFyYW1zLCBbJ3N0b3BCZWZvcmUnLCAnZXhjbHVkZSddKSk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLnBvc3QocGFyYW1zLCByZXBsYXlPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBDbG9uZSBhIGdpdmVuIHJ1biBhbmQgcmV0dXJuIGEgbmV3IHJ1biBpbiB0aGUgc2FtZSBzdGF0ZSBhcyB0aGUgZ2l2ZW4gcnVuLlxuICAgICAgICAqXG4gICAgICAgICogVGhlIG5ldyBydW4gaWQgaXMgbm93IGF2YWlsYWJsZSBbaW4gbWVtb3J5XSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvI3J1bnMtaW4tbWVtb3J5KS4gVGhlIG5ldyBydW4gaW5jbHVkZXMgYSBjb3B5IG9mIGFsbCBvZiB0aGUgZGF0YSBmcm9tIHRoZSBvcmlnaW5hbCBydW4sIEVYQ0VQVDpcbiAgICAgICAgKlxuICAgICAgICAqICogVGhlIGBzYXZlZGAgZmllbGQgaW4gdGhlIG5ldyBydW4gcmVjb3JkIGlzIG5vdCBjb3BpZWQgZnJvbSB0aGUgb3JpZ2luYWwgcnVuIHJlY29yZC4gSXQgZGVmYXVsdHMgdG8gYGZhbHNlYC5cbiAgICAgICAgKiAqIFRoZSBgaW5pdGlhbGl6ZWRgIGZpZWxkIGluIHRoZSBuZXcgcnVuIHJlY29yZCBpcyBub3QgY29waWVkIGZyb20gdGhlIG9yaWdpbmFsIHJ1biByZWNvcmQuIEl0IGRlZmF1bHRzIHRvIGBmYWxzZWAgYnV0IG1heSBjaGFuZ2UgdG8gYHRydWVgIGFzIHRoZSBuZXcgcnVuIGlzIGFkdmFuY2VkLiBGb3IgZXhhbXBsZSwgaWYgdGhlcmUgaGFzIGJlZW4gYSBjYWxsIHRvIHRoZSBgc3RlcGAgZnVuY3Rpb24gKGZvciBWZW5zaW0gbW9kZWxzKSwgdGhlIGBpbml0aWFsaXplZGAgZmllbGQgaXMgc2V0IHRvIGB0cnVlYC5cbiAgICAgICAgKiAqIFRoZSBgY3JlYXRlZGAgZmllbGQgaW4gdGhlIG5ldyBydW4gcmVjb3JkIGlzIHRoZSBkYXRlIGFuZCB0aW1lIGF0IHdoaWNoIHRoZSBjbG9uZSB3YXMgY3JlYXRlZCAobm90IHRoZSB0aW1lIHRoYXQgdGhlIG9yaWdpbmFsIHJ1biB3YXMgY3JlYXRlZC4pXG4gICAgICAgICpcbiAgICAgICAgKiBUaGUgb3JpZ2luYWwgcnVuIHJlbWFpbnMgb25seSBbaW4gdGhlIGRhdGFiYXNlXSguLi8uLi8uLi9ydW5fcGVyc2lzdGVuY2UvI3J1bnMtaW4tZGIpLlxuICAgICAgICAqXG4gICAgICAgICogICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHZhciBzYSA9IG5ldyBGLnNlcnZpY2UuU3RhdGUoKTtcbiAgICAgICAgKiAgICAgIHNhLmNsb25lKHtydW5JZDogJzE4NDJiYjVjLTgzYWQtNGJhOC1hOTU1LWJkMTNjYzJmZGI0ZicsIHN0b3BCZWZvcmU6ICdjYWxjdWxhdGVTY29yZScsIGV4Y2x1ZGU6IFsnaW50ZXJpbUNhbGN1bGF0aW9uJ10gfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zIFBhcmFtZXRlcnMgb2JqZWN0LlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMucnVuSWQgVGhlIGlkIG9mIHRoZSBydW4gdG8gY2xvbmUgZnJvbSBtZW1vcnkuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5zdG9wQmVmb3JlIChPcHRpb25hbCkgVGhlIG5ld2x5IGNsb25lZCBydW4gaXMgYWR2YW5jZWQgb25seSB1cCB0byB0aGUgZmlyc3Qgb2NjdXJyZW5jZSBvZiB0aGlzIG1ldGhvZC5cbiAgICAgICAgKiBAcGFyYW0ge2FycmF5fSBwYXJhbXMuZXhjbHVkZSAoT3B0aW9uYWwpIEFycmF5IG9mIG1ldGhvZHMgdG8gZXhjbHVkZSB3aGVuIGFkdmFuY2luZyB0aGUgbmV3bHkgY2xvbmVkIHJ1bi5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGNsb25lOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcnVuSWQgPSBwYXJzZVJ1bklkT3JFcnJvcihwYXJhbXMpO1xuXG4gICAgICAgICAgICB2YXIgcmVwbGF5T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHJ1bklkIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHBhcmFtcyA9ICQuZXh0ZW5kKHRydWUsIHsgYWN0aW9uOiAnY2xvbmUnIH0sIF9waWNrKHBhcmFtcywgWydzdG9wQmVmb3JlJywgJ2V4Y2x1ZGUnXSkpO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5wb3N0KHBhcmFtcywgcmVwbGF5T3B0aW9ucyk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgJC5leHRlbmQodGhpcywgcHVibGljQVBJKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBlcGlWZXJzaW9uID0gcmVxdWlyZSgnLi4vYXBpLXZlcnNpb24uanNvbicpO1xuXG4vL1RPRE86IHVybHV0aWxzIHRvIGdldCBob3N0LCBzaW5jZSBubyB3aW5kb3cgb24gbm9kZVxudmFyIGRlZmF1bHRzID0ge1xuICAgIGhvc3Q6IHdpbmRvdy5sb2NhdGlvbi5ob3N0LFxuICAgIHBhdGhuYW1lOiB3aW5kb3cubG9jYXRpb24ucGF0aG5hbWVcbn07XG5cbmZ1bmN0aW9uIGdldExvY2FsSG9zdChleGlzdGluZ0ZuLCBob3N0KSB7XG4gICAgdmFyIGxvY2FsSG9zdEZuO1xuICAgIGlmIChleGlzdGluZ0ZuICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKCEkLmlzRnVuY3Rpb24oZXhpc3RpbmdGbikpIHtcbiAgICAgICAgICAgIGxvY2FsSG9zdEZuID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gZXhpc3RpbmdGbjsgfTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGxvY2FsSG9zdEZuID0gZXhpc3RpbmdGbjtcbiAgICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAgIGxvY2FsSG9zdEZuID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGlzTG9jYWwgPSAhaG9zdCB8fCAvL3BoYW50b21qc1xuICAgICAgICAgICAgICAgIGhvc3QgPT09ICcxMjcuMC4wLjEnIHx8IFxuICAgICAgICAgICAgICAgIGhvc3QuaW5kZXhPZignbG9jYWwuJykgPT09IDAgfHwgXG4gICAgICAgICAgICAgICAgaG9zdC5pbmRleE9mKCdsb2NhbGhvc3QnKSA9PT0gMDtcbiAgICAgICAgICAgIHJldHVybiBpc0xvY2FsO1xuICAgICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gbG9jYWxIb3N0Rm47XG59XG5cbnZhciBVcmxDb25maWdTZXJ2aWNlID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBlbnZDb25mID0gVXJsQ29uZmlnU2VydmljZS5kZWZhdWx0cztcblxuICAgIGlmICghY29uZmlnKSB7XG4gICAgICAgIGNvbmZpZyA9IHt9O1xuICAgIH1cbiAgICAvLyBjb25zb2xlLmxvZyh0aGlzLmRlZmF1bHRzKTtcbiAgICB2YXIgb3ZlcnJpZGVzID0gJC5leHRlbmQoe30sIGVudkNvbmYsIGNvbmZpZyk7XG4gICAgdmFyIG9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgZGVmYXVsdHMsIG92ZXJyaWRlcyk7XG5cbiAgICBvdmVycmlkZXMuaXNMb2NhbGhvc3QgPSBvcHRpb25zLmlzTG9jYWxob3N0ID0gZ2V0TG9jYWxIb3N0KG9wdGlvbnMuaXNMb2NhbGhvc3QsIG9wdGlvbnMuaG9zdCk7XG4gICAgXG4gICAgLy8gY29uc29sZS5sb2coaXNMb2NhbGhvc3QoKSwgJ19fX19fX19fX19fJyk7XG4gICAgdmFyIGFjdGluZ0hvc3QgPSBjb25maWcgJiYgY29uZmlnLmhvc3Q7XG4gICAgaWYgKCFhY3RpbmdIb3N0ICYmIG9wdGlvbnMuaXNMb2NhbGhvc3QoKSkge1xuICAgICAgICBhY3RpbmdIb3N0ID0gJ2ZvcmlvLmNvbSc7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgYWN0aW5nSG9zdCA9IG9wdGlvbnMuaG9zdDtcbiAgICB9XG5cbiAgICB2YXIgQVBJX1BST1RPQ09MID0gJ2h0dHBzJztcbiAgICB2YXIgSE9TVF9BUElfTUFQUElORyA9IHtcbiAgICAgICAgJ2ZvcmlvLmNvbSc6ICdhcGkuZm9yaW8uY29tJyxcbiAgICAgICAgJ2ZvcmlvZGV2LmNvbSc6ICdhcGkuZXBpY2VudGVyLmZvcmlvZGV2LmNvbSdcbiAgICB9O1xuXG4gICAgdmFyIHB1YmxpY0V4cG9ydHMgPSB7XG4gICAgICAgIHByb3RvY29sOiBBUElfUFJPVE9DT0wsXG5cbiAgICAgICAgYXBpOiAnJyxcblxuICAgICAgICAvL1RPRE86IHRoaXMgc2hvdWxkIHJlYWxseSBiZSBjYWxsZWQgJ2FwaWhvc3QnLCBidXQgY2FuJ3QgYmVjYXVzZSB0aGF0IHdvdWxkIGJyZWFrIHRvbyBtYW55IHRoaW5nc1xuICAgICAgICBob3N0OiAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGFwaUhvc3QgPSAoSE9TVF9BUElfTUFQUElOR1thY3RpbmdIb3N0XSkgPyBIT1NUX0FQSV9NQVBQSU5HW2FjdGluZ0hvc3RdIDogYWN0aW5nSG9zdDtcbiAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKGFjdGluZ0hvc3QsIGNvbmZpZywgYXBpSG9zdCk7XG4gICAgICAgICAgICByZXR1cm4gYXBpSG9zdDtcbiAgICAgICAgfSgpKSxcblxuICAgICAgICBpc0N1c3RvbURvbWFpbjogKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBwYXRoID0gb3B0aW9ucy5wYXRobmFtZS5zcGxpdCgnXFwvJyk7XG4gICAgICAgICAgICB2YXIgcGF0aEhhc0FwcCA9IHBhdGggJiYgcGF0aFsxXSA9PT0gJ2FwcCc7XG4gICAgICAgICAgICByZXR1cm4gKCFvcHRpb25zLmlzTG9jYWxob3N0KCkgJiYgIXBhdGhIYXNBcHApO1xuICAgICAgICB9KCkpLFxuXG4gICAgICAgIGFwcFBhdGg6IChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgcGF0aCA9IG9wdGlvbnMucGF0aG5hbWUuc3BsaXQoJ1xcLycpO1xuXG4gICAgICAgICAgICByZXR1cm4gcGF0aCAmJiBwYXRoWzFdIHx8ICcnO1xuICAgICAgICB9KCkpLFxuXG4gICAgICAgIGFjY291bnRQYXRoOiAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGFjY250ID0gJyc7XG4gICAgICAgICAgICB2YXIgcGF0aCA9IG9wdGlvbnMucGF0aG5hbWUuc3BsaXQoJ1xcLycpO1xuICAgICAgICAgICAgaWYgKHBhdGggJiYgcGF0aFsxXSA9PT0gJ2FwcCcpIHtcbiAgICAgICAgICAgICAgICBhY2NudCA9IHBhdGhbMl07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gYWNjbnQ7XG4gICAgICAgIH0oKSksXG5cbiAgICAgICAgcHJvamVjdFBhdGg6IChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgcHJqID0gJyc7XG4gICAgICAgICAgICB2YXIgcGF0aCA9IG9wdGlvbnMucGF0aG5hbWUuc3BsaXQoJ1xcLycpO1xuICAgICAgICAgICAgaWYgKHBhdGggJiYgcGF0aFsxXSA9PT0gJ2FwcCcpIHtcbiAgICAgICAgICAgICAgICBwcmogPSBwYXRoWzNdOyAvL2VzbGludC1kaXNhYmxlLWxpbmUgbm8tbWFnaWMtbnVtYmVyc1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHByajtcbiAgICAgICAgfSgpKSxcblxuICAgICAgICB2ZXJzaW9uUGF0aDogKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciB2ZXJzaW9uID0gZXBpVmVyc2lvbi52ZXJzaW9uID8gZXBpVmVyc2lvbi52ZXJzaW9uICsgJy8nIDogJyc7XG4gICAgICAgICAgICByZXR1cm4gdmVyc2lvbjtcbiAgICAgICAgfSgpKSxcblxuICAgICAgICBnZXRBUElQYXRoOiBmdW5jdGlvbiAoYXBpKSB7XG4gICAgICAgICAgICB2YXIgUFJPSkVDVF9BUElTID0gWydydW4nLCAnZGF0YScsICdmaWxlJ107XG5cbiAgICAgICAgICAgIGlmIChhcGkgPT09ICdjb25maWcnKSB7XG4gICAgICAgICAgICAgICAgdmFyIGFjdHVhbFByb3RvY29sID0gd2luZG93LmxvY2F0aW9uLnByb3RvY29sLnJlcGxhY2UoJzonLCAnJyk7XG4gICAgICAgICAgICAgICAgdmFyIGNvbmZpZ1Byb3RvY29sID0gKG9wdGlvbnMuaXNMb2NhbGhvc3QoKSkgPyB0aGlzLnByb3RvY29sIDogYWN0dWFsUHJvdG9jb2w7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNvbmZpZ1Byb3RvY29sICsgJzovLycgKyBhY3RpbmdIb3N0ICsgJy9lcGljZW50ZXIvJyArIHRoaXMudmVyc2lvblBhdGggKyAnY29uZmlnJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBhcGlQYXRoID0gdGhpcy5wcm90b2NvbCArICc6Ly8nICsgdGhpcy5ob3N0ICsgJy8nICsgdGhpcy52ZXJzaW9uUGF0aCArIGFwaSArICcvJztcblxuICAgICAgICAgICAgaWYgKCQuaW5BcnJheShhcGksIFBST0pFQ1RfQVBJUykgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgYXBpUGF0aCArPSB0aGlzLmFjY291bnRQYXRoICsgJy8nICsgdGhpcy5wcm9qZWN0UGF0aCArICcvJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBhcGlQYXRoO1xuICAgICAgICB9XG4gICAgfTtcblxuXG4gICAgJC5leHRlbmQocHVibGljRXhwb3J0cywgb3ZlcnJpZGVzKTtcbiAgICByZXR1cm4gcHVibGljRXhwb3J0cztcbn07XG4vLyBUaGlzIGRhdGEgY2FuIGJlIHNldCBieSBleHRlcm5hbCBzY3JpcHRzLCBmb3IgbG9hZGluZyBmcm9tIGFuIGVudiBzZXJ2ZXIgZm9yIGVnO1xuVXJsQ29uZmlnU2VydmljZS5kZWZhdWx0cyA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFVybENvbmZpZ1NlcnZpY2U7XG4iLCIndXNlIHN0cmljdCc7XG4vKipcbiogIyMgVXNlciBBUEkgQWRhcHRlclxuKlxuKiBUaGUgVXNlciBBUEkgQWRhcHRlciBhbGxvd3MgeW91IHRvIHJldHJpZXZlIGRldGFpbHMgYWJvdXQgZW5kIHVzZXJzIGluIHlvdXIgdGVhbSAoYWNjb3VudCkuIEl0IGlzIGJhc2VkIG9uIHRoZSBxdWVyeWluZyBjYXBhYmlsaXRpZXMgb2YgdGhlIHVuZGVybHlpbmcgUkVTVGZ1bCBbVXNlciBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy91c2VyX21hbmFnZW1lbnQvdXNlci8pLlxuKlxuKiBUbyB1c2UgdGhlIFVzZXIgQVBJIEFkYXB0ZXIsIGluc3RhbnRpYXRlIGl0IGFuZCB0aGVuIGNhbGwgaXRzIG1ldGhvZHMuXG4qXG4qICAgICAgIHZhciB1YSA9IG5ldyBGLnNlcnZpY2UuVXNlcih7XG4qICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4qICAgICAgICAgICB0b2tlbjogJ3VzZXItb3ItcHJvamVjdC1hY2Nlc3MtdG9rZW4nXG4qICAgICAgIH0pO1xuKiAgICAgICB1YS5nZXRCeUlkKCc0MjgzNmQ0Yi01YjYxLTRmZTQtODBlYi0zMTM2ZTk1NmVlNWMnKTtcbiogICAgICAgdWEuZ2V0KHsgdXNlck5hbWU6ICdqc21pdGgnIH0pO1xuKiAgICAgICB1YS5nZXQoeyBpZDogWyc0MjgzNmQ0Yi01YjYxLTRmZTQtODBlYi0zMTM2ZTk1NmVlNWMnLFxuKiAgICAgICAgICAgICAgICAgICAnNGVhNzU2MzEtNGM4ZC00ODcyLTlkODAtYjQ2MDAxNDY0NzhlJ10gfSk7XG4qXG4qIFRoZSBjb25zdHJ1Y3RvciB0YWtlcyBhbiBvcHRpb25hbCBvcHRpb25zIHBhcmFtZXRlciBpbiB3aGljaCB5b3UgY2FuIHNwZWNpZnkgdGhlIGBhY2NvdW50YCBhbmQgYHRva2VuYCBpZiB0aGV5IGFyZSBub3QgYWxyZWFkeSBhdmFpbGFibGUgaW4gdGhlIGN1cnJlbnQgY29udGV4dC5cbiovXG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbnZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbnZhciBTZXNzaW9uTWFuYWdlciA9IHJlcXVpcmUoJy4uL3N0b3JlL3Nlc3Npb24tbWFuYWdlcicpO1xudmFyIHF1dGlsID0gcmVxdWlyZSgnLi4vdXRpbC9xdWVyeS11dGlsJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBkZWZhdWx0cyA9IHtcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFjY291bnQgaWQuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykgb3IgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGFjY291bnQ6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFjY2VzcyB0b2tlbiB0byB1c2Ugd2hlbiBzZWFyY2hpbmcgZm9yIGVuZCB1c2Vycy4gKFNlZSBbbW9yZSBiYWNrZ3JvdW5kIG9uIGFjY2VzcyB0b2tlbnNdKC4uLy4uLy4uL3Byb2plY3RfYWNjZXNzLykpLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdG9rZW46IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT3B0aW9ucyB0byBwYXNzIG9uIHRvIHRoZSB1bmRlcmx5aW5nIHRyYW5zcG9ydCBsYXllci4gQWxsIGpxdWVyeS5hamF4IG9wdGlvbnMgYXQgaHR0cDovL2FwaS5qcXVlcnkuY29tL2pRdWVyeS5hamF4LyBhcmUgYXZhaWxhYmxlLiBEZWZhdWx0cyB0byBlbXB0eSBvYmplY3QuXG4gICAgICAgICAqIEB0eXBlIHtPYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICB0cmFuc3BvcnQ6IHt9XG4gICAgfTtcblxuICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICB2YXIgc2VydmljZU9wdGlvbnMgPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldE1lcmdlZE9wdGlvbnMoZGVmYXVsdHMsIGNvbmZpZyk7XG4gICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuICAgIHZhciB0cmFuc3BvcnRPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLnRyYW5zcG9ydCwge1xuICAgICAgICB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKCd1c2VyJylcbiAgICB9KTtcblxuICAgIGlmIChzZXJ2aWNlT3B0aW9ucy50b2tlbikge1xuICAgICAgICB0cmFuc3BvcnRPcHRpb25zLmhlYWRlcnMgPSB7XG4gICAgICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBzZXJ2aWNlT3B0aW9ucy50b2tlblxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHZhciBodHRwID0gbmV3IFRyYW5zcG9ydEZhY3RvcnkodHJhbnNwb3J0T3B0aW9ucyk7XG5cbiAgICB2YXIgcHVibGljQVBJID0ge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAqIFJldHJpZXZlIGRldGFpbHMgYWJvdXQgcGFydGljdWxhciBlbmQgdXNlcnMgaW4geW91ciB0ZWFtLCBiYXNlZCBvbiB1c2VyIG5hbWUgb3IgdXNlciBpZC5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICB2YXIgdWEgPSBuZXcgRi5zZXJ2aWNlLlVzZXIoe1xuICAgICAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgIHRva2VuOiAndXNlci1vci1wcm9qZWN0LWFjY2Vzcy10b2tlbidcbiAgICAgICAgKiAgICAgICB9KTtcbiAgICAgICAgKiAgICAgICB1YS5nZXQoeyB1c2VyTmFtZTogJ2pzbWl0aCcgfSk7XG4gICAgICAgICogICAgICAgdWEuZ2V0KHsgaWQ6IFsnNDI4MzZkNGItNWI2MS00ZmU0LTgwZWItMzEzNmU5NTZlZTVjJyxcbiAgICAgICAgKiAgICAgICAgICAgICAgICAgICAnNGVhNzU2MzEtNGM4ZC00ODcyLTlkODAtYjQ2MDAxNDY0NzhlJ10gfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBmaWx0ZXIgT2JqZWN0IHdpdGggZmllbGQgYHVzZXJOYW1lYCBhbmQgdmFsdWUgb2YgdGhlIHVzZXJuYW1lLiBBbHRlcm5hdGl2ZWx5LCBvYmplY3Qgd2l0aCBmaWVsZCBgaWRgIGFuZCB2YWx1ZSBvZiBhbiBhcnJheSBvZiB1c2VyIGlkcy5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG5cbiAgICAgICAgZ2V0OiBmdW5jdGlvbiAoZmlsdGVyLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICAgICAgICAgIGZpbHRlciA9IGZpbHRlciB8fCB7fTtcblxuICAgICAgICAgICAgdmFyIGdldE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICB2YXIgdG9RRmlsdGVyID0gZnVuY3Rpb24gKGZpbHRlcikge1xuICAgICAgICAgICAgICAgIHZhciByZXMgPSB7fTtcblxuICAgICAgICAgICAgICAgIC8vIEFQSSBvbmx5IHN1cHBvcnRzIGZpbHRlcmluZyBieSB1c2VybmFtZSBmb3Igbm93XG4gICAgICAgICAgICAgICAgaWYgKGZpbHRlci51c2VyTmFtZSkge1xuICAgICAgICAgICAgICAgICAgICByZXMucSA9IGZpbHRlci51c2VyTmFtZTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXR1cm4gcmVzO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgdmFyIHRvSWRGaWx0ZXJzID0gZnVuY3Rpb24gKGlkKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFpZCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gJyc7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWQgPSAkLmlzQXJyYXkoaWQpID8gaWQgOiBbaWRdO1xuICAgICAgICAgICAgICAgIHJldHVybiAnaWQ9JyArIGlkLmpvaW4oJyZpZD0nKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHZhciBnZXRGaWx0ZXJzID0gW1xuICAgICAgICAgICAgICAgICdhY2NvdW50PScgKyBnZXRPcHRpb25zLmFjY291bnQsXG4gICAgICAgICAgICAgICAgdG9JZEZpbHRlcnMoZmlsdGVyLmlkKSxcbiAgICAgICAgICAgICAgICBxdXRpbC50b1F1ZXJ5Rm9ybWF0KHRvUUZpbHRlcihmaWx0ZXIpKVxuICAgICAgICAgICAgXS5qb2luKCcmJyk7XG5cbiAgICAgICAgICAgIC8vIHNwZWNpYWwgY2FzZSBmb3IgcXVlcmllcyB3aXRoIGxhcmdlIG51bWJlciBvZiBpZHNcbiAgICAgICAgICAgIC8vIG1ha2UgaXQgYXMgYSBwb3N0IHdpdGggR0VUIHNlbWFudGljc1xuICAgICAgICAgICAgdmFyIHRocmVzaG9sZCA9IDMwO1xuICAgICAgICAgICAgaWYgKGZpbHRlci5pZCAmJiAkLmlzQXJyYXkoZmlsdGVyLmlkKSAmJiBmaWx0ZXIuaWQubGVuZ3RoID49IHRocmVzaG9sZCkge1xuICAgICAgICAgICAgICAgIGdldE9wdGlvbnMudXJsID0gdXJsQ29uZmlnLmdldEFQSVBhdGgoJ3VzZXInKSArICc/X21ldGhvZD1HRVQnO1xuICAgICAgICAgICAgICAgIHJldHVybiBodHRwLnBvc3QoeyBpZDogZmlsdGVyLmlkIH0sIGdldE9wdGlvbnMpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQoZ2V0RmlsdGVycywgZ2V0T3B0aW9ucyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogUmV0cmlldmUgZGV0YWlscyBhYm91dCBhIHNpbmdsZSBlbmQgdXNlciBpbiB5b3VyIHRlYW0sIGJhc2VkIG9uIHVzZXIgaWQuXG4gICAgICAgICpcbiAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAqXG4gICAgICAgICogICAgICAgdmFyIHVhID0gbmV3IEYuc2VydmljZS5Vc2VyKHtcbiAgICAgICAgKiAgICAgICAgICAgYWNjb3VudDogJ2FjbWUtc2ltdWxhdGlvbnMnLFxuICAgICAgICAqICAgICAgICAgICB0b2tlbjogJ3VzZXItb3ItcHJvamVjdC1hY2Nlc3MtdG9rZW4nXG4gICAgICAgICogICAgICAgfSk7XG4gICAgICAgICogICAgICAgdWEuZ2V0QnlJZCgnNDI4MzZkNGItNWI2MS00ZmU0LTgwZWItMzEzNmU5NTZlZTVjJyk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB1c2VySWQgVGhlIHVzZXIgaWQgZm9yIHRoZSBlbmQgdXNlciBpbiB5b3VyIHRlYW0uXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuXG4gICAgICAgIGdldEJ5SWQ6IGZ1bmN0aW9uICh1c2VySWQsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHJldHVybiBwdWJsaWNBUEkuZ2V0KHsgaWQ6IHVzZXJJZCB9LCBvcHRpb25zKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xufTtcblxuXG4iLCIvKipcbiAqXG4gKiAjIyBWYXJpYWJsZXMgQVBJIFNlcnZpY2VcbiAqXG4gKiBVc2VkIGluIGNvbmp1bmN0aW9uIHdpdGggdGhlIFtSdW4gQVBJIFNlcnZpY2VdKC4uL3J1bi1hcGktc2VydmljZS8pIHRvIHJlYWQsIHdyaXRlLCBhbmQgc2VhcmNoIGZvciBzcGVjaWZpYyBtb2RlbCB2YXJpYWJsZXMuXG4gKlxuICogICAgIHZhciBybSA9IG5ldyBGLm1hbmFnZXIuUnVuTWFuYWdlcih7XG4gKiAgICAgICAgICAgcnVuOiB7XG4gKiAgICAgICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAqICAgICAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAqICAgICAgICAgICAgICAgbW9kZWw6ICdzdXBwbHktY2hhaW4tbW9kZWwuamwnXG4gKiAgICAgICAgICAgfVxuICogICAgICB9KTtcbiAqICAgICBybS5nZXRSdW4oKVxuICogICAgICAgLnRoZW4oZnVuY3Rpb24oKSB7XG4gKiAgICAgICAgICB2YXIgdnMgPSBybS5ydW4udmFyaWFibGVzKCk7XG4gKiAgICAgICAgICB2cy5zYXZlKHtzYW1wbGVfaW50OiA0fSk7XG4gKiAgICAgICAgfSk7XG4gKlxuICovXG5cblxuICd1c2Ugc3RyaWN0JztcblxuIHZhciBUcmFuc3BvcnRGYWN0b3J5ID0gcmVxdWlyZSgnLi4vdHJhbnNwb3J0L2h0dHAtdHJhbnNwb3J0LWZhY3RvcnknKTtcbiB2YXIgcnV0aWwgPSByZXF1aXJlKCcuLi91dGlsL3J1bi11dGlsJyk7XG5cbiBtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHJ1bnMgb2JqZWN0IHRvIHdoaWNoIHRoZSB2YXJpYWJsZSBmaWx0ZXJzIGFwcGx5LiBEZWZhdWx0cyB0byBudWxsLlxuICAgICAgICAgKiBAdHlwZSB7cnVuU2VydmljZX1cbiAgICAgICAgICovXG4gICAgICAgICBydW5TZXJ2aWNlOiBudWxsXG4gICAgIH07XG4gICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBkZWZhdWx0cywgY29uZmlnKTtcblxuICAgICB2YXIgZ2V0VVJMID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgcmV0dXJuIHNlcnZpY2VPcHRpb25zLnJ1blNlcnZpY2UudXJsQ29uZmlnLmdldEZpbHRlclVSTCgpICsgJ3ZhcmlhYmxlcy8nO1xuICAgICB9O1xuXG4gICAgIHZhciBhZGRBdXRvUmVzdG9yZUhlYWRlciA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgICByZXR1cm4gc2VydmljZU9wdGlvbnMucnVuU2VydmljZS51cmxDb25maWcuYWRkQXV0b1Jlc3RvcmVIZWFkZXIob3B0aW9ucyk7XG4gICAgIH07XG5cbiAgICAgdmFyIGh0dHBPcHRpb25zID0ge1xuICAgICAgICAgdXJsOiBnZXRVUkxcbiAgICAgfTtcbiAgICAgaWYgKHNlcnZpY2VPcHRpb25zLnRva2VuKSB7XG4gICAgICAgICBodHRwT3B0aW9ucy5oZWFkZXJzID0ge1xuICAgICAgICAgICAgIEF1dGhvcml6YXRpb246ICdCZWFyZXIgJyArIHNlcnZpY2VPcHRpb25zLnRva2VuXG4gICAgICAgICB9O1xuICAgICB9XG4gICAgIHZhciBodHRwID0gbmV3IFRyYW5zcG9ydEZhY3RvcnkoaHR0cE9wdGlvbnMpO1xuICAgICBodHRwLnNwbGl0R2V0ID0gcnV0aWwuc3BsaXRHZXRGYWN0b3J5KGh0dHBPcHRpb25zKTtcblxuICAgICB2YXIgcHVibGljQVBJID0ge1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBHZXQgdmFsdWVzIGZvciBhIHZhcmlhYmxlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKkV4YW1wbGUqKlxuICAgICAgICAgKlxuICAgICAgICAgKiAgICAgIHZzLmxvYWQoJ3NhbXBsZV9pbnQnKVxuICAgICAgICAgKiAgICAgICAgICAudGhlbihmdW5jdGlvbih2YWwpe1xuICAgICAgICAgKiAgICAgICAgICAgICAgLy8gdmFsIGNvbnRhaW5zIHRoZSB2YWx1ZSBvZiBzYW1wbGVfaW50XG4gICAgICAgICAqICAgICAgICAgIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gdmFyaWFibGUgTmFtZSBvZiB2YXJpYWJsZSB0byBsb2FkLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3V0cHV0TW9kaWZpZXIgKE9wdGlvbmFsKSBBdmFpbGFibGUgZmllbGRzIGluY2x1ZGU6IGBzdGFydHJlY29yZGAsIGBlbmRyZWNvcmRgLCBgc29ydGAsIGFuZCBgZGlyZWN0aW9uYCAoYGFzY2Agb3IgYGRlc2NgKS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgICBsb2FkOiBmdW5jdGlvbiAodmFyaWFibGUsIG91dHB1dE1vZGlmaWVyLCBvcHRpb25zKSB7XG4gICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcbiAgICAgICAgICAgICBodHRwT3B0aW9ucyA9IGFkZEF1dG9SZXN0b3JlSGVhZGVyKGh0dHBPcHRpb25zKTtcbiAgICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQob3V0cHV0TW9kaWZpZXIsICQuZXh0ZW5kKHt9LCBodHRwT3B0aW9ucywge1xuICAgICAgICAgICAgICAgICB1cmw6IGdldFVSTCgpICsgdmFyaWFibGUgKyAnLydcbiAgICAgICAgICAgICB9KSk7XG4gICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZXR1cm5zIHBhcnRpY3VsYXIgdmFyaWFibGVzLCBiYXNlZCBvbiBjb25kaXRpb25zIHNwZWNpZmllZCBpbiB0aGUgYHF1ZXJ5YCBvYmplY3QuXG4gICAgICAgICAqXG4gICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICAqXG4gICAgICAgICAqICAgICAgdnMucXVlcnkoWydwcmljZScsICdzYWxlcyddKVxuICAgICAgICAgKiAgICAgICAgICAudGhlbihmdW5jdGlvbih2YWwpIHtcbiAgICAgICAgICogICAgICAgICAgICAgIC8vIHZhbCBpcyBhbiBvYmplY3Qgd2l0aCB0aGUgdmFsdWVzIG9mIHRoZSByZXF1ZXN0ZWQgdmFyaWFibGVzOiB2YWwucHJpY2UsIHZhbC5zYWxlc1xuICAgICAgICAgKiAgICAgICAgICB9KTtcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICB2cy5xdWVyeSh7IGluY2x1ZGU6WydwcmljZScsICdzYWxlcyddIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdHxBcnJheX0gcXVlcnkgVGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgcmVxdWVzdGVkLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3V0cHV0TW9kaWZpZXIgKE9wdGlvbmFsKSBBdmFpbGFibGUgZmllbGRzIGluY2x1ZGU6IGBzdGFydHJlY29yZGAsIGBlbmRyZWNvcmRgLCBgc29ydGAsIGFuZCBgZGlyZWN0aW9uYCAoYGFzY2Agb3IgYGRlc2NgKS5cbiAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPdmVycmlkZXMgZm9yIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAgICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgICovXG4gICAgICAgICBxdWVyeTogZnVuY3Rpb24gKHF1ZXJ5LCBvdXRwdXRNb2RpZmllciwgb3B0aW9ucykge1xuICAgICAgICAgICAgLy9RdWVyeSBhbmQgb3V0cHV0TW9kaWZpZXIgYXJlIGJvdGggcXVlcnlzdHJpbmdzIGluIHRoZSB1cmw7IG9ubHkgY2FsbGluZyB0aGVtIG91dCBzZXBhcmF0ZWx5IGhlcmUgdG8gYmUgY29uc2lzdGVudCB3aXRoIHRoZSBvdGhlciBjYWxsc1xuICAgICAgICAgICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICAgICAgICAgaHR0cE9wdGlvbnMgPSBhZGRBdXRvUmVzdG9yZUhlYWRlcihodHRwT3B0aW9ucyk7XG5cbiAgICAgICAgICAgICBpZiAoJC5pc0FycmF5KHF1ZXJ5KSkge1xuICAgICAgICAgICAgICAgICBxdWVyeSA9IHsgaW5jbHVkZTogcXVlcnkgfTtcbiAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgJC5leHRlbmQocXVlcnksIG91dHB1dE1vZGlmaWVyKTtcbiAgICAgICAgICAgICByZXR1cm4gaHR0cC5zcGxpdEdldChxdWVyeSwgaHR0cE9wdGlvbnMpO1xuICAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogU2F2ZSB2YWx1ZXMgdG8gbW9kZWwgdmFyaWFibGVzLiBPdmVyd3JpdGVzIGV4aXN0aW5nIHZhbHVlcy4gTm90ZSB0aGF0IHlvdSBjYW4gb25seSB1cGRhdGUgbW9kZWwgdmFyaWFibGVzIGlmIHRoZSBydW4gaXMgW2luIG1lbW9yeV0oLi4vLi4vLi4vcnVuX3BlcnNpc3RlbmNlLyNydW5zLWluLW1lbW9yeSkuIChBbiBhbHRlcm5hdGUgd2F5IHRvIHVwZGF0ZSBtb2RlbCB2YXJpYWJsZXMgaXMgdG8gY2FsbCBhIG1ldGhvZCBmcm9tIHRoZSBtb2RlbCBhbmQgbWFrZSBzdXJlIHRoYXQgdGhlIG1ldGhvZCBwZXJzaXN0cyB0aGUgdmFyaWFibGVzLiBTZWUgYGRvYCwgYHNlcmlhbGAsIGFuZCBgcGFyYWxsZWxgIGluIHRoZSBbUnVuIEFQSSBTZXJ2aWNlXSguLi9ydW4tYXBpLXNlcnZpY2UvKSBmb3IgY2FsbGluZyBtZXRob2RzIGZyb20gdGhlIG1vZGVsLilcbiAgICAgICAgICpcbiAgICAgICAgICogKipFeGFtcGxlKipcbiAgICAgICAgICpcbiAgICAgICAgICogICAgICB2cy5zYXZlKCdwcmljZScsIDQpO1xuICAgICAgICAgKiAgICAgIHZzLnNhdmUoeyBwcmljZTogNCwgcXVhbnRpdHk6IDUsIHByb2R1Y3RzOiBbMiwzLDRdIH0pO1xuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdHxTdHJpbmd9IHZhcmlhYmxlIEFuIG9iamVjdCBjb21wb3NlZCBvZiB0aGUgbW9kZWwgdmFyaWFibGVzIGFuZCB0aGUgdmFsdWVzIHRvIHNhdmUuIEFsdGVybmF0aXZlbHksIGEgc3RyaW5nIHdpdGggdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gdmFsIChPcHRpb25hbCkgSWYgcGFzc2luZyBhIHN0cmluZyBmb3IgYHZhcmlhYmxlYCwgdXNlIHRoaXMgYXJndW1lbnQgZm9yIHRoZSB2YWx1ZSB0byBzYXZlLlxuICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE92ZXJyaWRlcyBmb3IgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAgKi9cbiAgICAgICAgIHNhdmU6IGZ1bmN0aW9uICh2YXJpYWJsZSwgdmFsLCBvcHRpb25zKSB7XG4gICAgICAgICAgICAgdmFyIGF0dHJzO1xuICAgICAgICAgICAgIGlmICh0eXBlb2YgdmFyaWFibGUgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICAgICAgIGF0dHJzID0gdmFyaWFibGU7XG4gICAgICAgICAgICAgICAgIG9wdGlvbnMgPSB2YWw7XG4gICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgKGF0dHJzID0ge30pW3ZhcmlhYmxlXSA9IHZhbDtcbiAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgdmFyIGh0dHBPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcblxuICAgICAgICAgICAgIHJldHVybiBodHRwLnBhdGNoLmNhbGwodGhpcywgYXR0cnMsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgIH1cblxuICAgICAgICAvLyBOb3QgQXZhaWxhYmxlIHVudGlsIHVuZGVybHlpbmcgQVBJIHN1cHBvcnRzIFBVVC4gT3RoZXJ3aXNlIHNhdmUgd291bGQgYmUgUFVUIGFuZCBtZXJnZSB3b3VsZCBiZSBQQVRDSFxuICAgICAgICAvLyAqXG4gICAgICAgIC8vICAqIFNhdmUgdmFsdWVzIHRvIHRoZSBhcGkuIE1lcmdlcyBhcnJheXMsIGJ1dCBvdGhlcndpc2Ugc2FtZSBhcyBzYXZlXG4gICAgICAgIC8vICAqIEBwYXJhbSB7T2JqZWN0fFN0cmluZ30gdmFyaWFibGUgT2JqZWN0IHdpdGggYXR0cmlidXRlcywgb3Igc3RyaW5nIGtleVxuICAgICAgICAvLyAgKiBAcGFyYW0ge09iamVjdH0gdmFsIE9wdGlvbmFsIGlmIHByZXYgcGFyYW1ldGVyIHdhcyBhIHN0cmluZywgc2V0IHZhbHVlIGhlcmVcbiAgICAgICAgLy8gICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgT3ZlcnJpZGVzIGZvciBjb25maWd1cmF0aW9uIG9wdGlvbnNcbiAgICAgICAgLy8gICpcbiAgICAgICAgLy8gICogQGV4YW1wbGVcbiAgICAgICAgLy8gICogICAgIHZzLm1lcmdlKHsgcHJpY2U6IDQsIHF1YW50aXR5OiA1LCBwcm9kdWN0czogWzIsMyw0XSB9KVxuICAgICAgICAvLyAgKiAgICAgdnMubWVyZ2UoJ3ByaWNlJywgNCk7XG5cbiAgICAgICAgLy8gbWVyZ2U6IGZ1bmN0aW9uICh2YXJpYWJsZSwgdmFsLCBvcHRpb25zKSB7XG4gICAgICAgIC8vICAgICB2YXIgYXR0cnM7XG4gICAgICAgIC8vICAgICBpZiAodHlwZW9mIHZhcmlhYmxlID09PSAnb2JqZWN0Jykge1xuICAgICAgICAvLyAgICAgICBhdHRycyA9IHZhcmlhYmxlO1xuICAgICAgICAvLyAgICAgICBvcHRpb25zID0gdmFsO1xuICAgICAgICAvLyAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gICAgICAgKGF0dHJzID0ge30pW3ZhcmlhYmxlXSA9IHZhbDtcbiAgICAgICAgLy8gICAgIH1cbiAgICAgICAgLy8gICAgIHZhciBodHRwT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucyk7XG5cbiAgICAgICAgLy8gICAgIHJldHVybiBodHRwLnBhdGNoLmNhbGwodGhpcywgYXR0cnMsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgLy8gfVxuICAgICB9O1xuICAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xuIH07XG4iLCIvKipcbiAqICMjIFdvcmxkIEFQSSBBZGFwdGVyXG4gKlxuICogQSBbcnVuXSguLi8uLi8uLi9nbG9zc2FyeS8jcnVuKSBpcyBhIGNvbGxlY3Rpb24gb2YgZW5kIHVzZXIgaW50ZXJhY3Rpb25zIHdpdGggYSBwcm9qZWN0IGFuZCBpdHMgbW9kZWwgLS0gaW5jbHVkaW5nIHNldHRpbmcgdmFyaWFibGVzLCBtYWtpbmcgZGVjaXNpb25zLCBhbmQgY2FsbGluZyBvcGVyYXRpb25zLiBGb3IgYnVpbGRpbmcgbXVsdGlwbGF5ZXIgc2ltdWxhdGlvbnMgeW91IHR5cGljYWxseSB3YW50IG11bHRpcGxlIGVuZCB1c2VycyB0byBzaGFyZSB0aGUgc2FtZSBzZXQgb2YgaW50ZXJhY3Rpb25zLCBhbmQgd29yayB3aXRoaW4gYSBjb21tb24gc3RhdGUuIEVwaWNlbnRlciBhbGxvd3MgeW91IHRvIGNyZWF0ZSBcIndvcmxkc1wiIHRvIGhhbmRsZSBzdWNoIGNhc2VzLiBPbmx5IFt0ZWFtIHByb2plY3RzXSguLi8uLi8uLi9nbG9zc2FyeS8jdGVhbSkgY2FuIGJlIG11bHRpcGxheWVyLlxuICpcbiAqIFRoZSBXb3JsZCBBUEkgQWRhcHRlciBhbGxvd3MgeW91IHRvIGNyZWF0ZSwgYWNjZXNzLCBhbmQgbWFuaXB1bGF0ZSBtdWx0aXBsYXllciB3b3JsZHMgd2l0aGluIHlvdXIgRXBpY2VudGVyIHByb2plY3QuIFlvdSBjYW4gdXNlIHRoaXMgdG8gYWRkIGFuZCByZW1vdmUgZW5kIHVzZXJzIGZyb20gdGhlIHdvcmxkLCBhbmQgdG8gY3JlYXRlLCBhY2Nlc3MsIGFuZCByZW1vdmUgdGhlaXIgcnVucy4gQmVjYXVzZSBvZiB0aGlzLCB0eXBpY2FsbHkgdGhlIFdvcmxkIEFkYXB0ZXIgaXMgdXNlZCBmb3IgZmFjaWxpdGF0b3IgcGFnZXMgaW4geW91ciBwcm9qZWN0LiAoVGhlIHJlbGF0ZWQgW1dvcmxkIE1hbmFnZXJdKC4uL3dvcmxkLW1hbmFnZXIvKSBwcm92aWRlcyBhbiBlYXN5IHdheSB0byBhY2Nlc3MgcnVucyBhbmQgd29ybGRzIGZvciBwYXJ0aWN1bGFyIGVuZCB1c2Vycywgc28gaXMgdHlwaWNhbGx5IHVzZWQgaW4gcGFnZXMgdGhhdCBlbmQgdXNlcnMgd2lsbCBpbnRlcmFjdCB3aXRoLilcbiAqXG4gKiBBcyB3aXRoIGFsbCB0aGUgb3RoZXIgW0FQSSBBZGFwdGVyc10oLi4vLi4vKSwgYWxsIG1ldGhvZHMgdGFrZSBpbiBhbiBcIm9wdGlvbnNcIiBvYmplY3QgYXMgdGhlIGxhc3QgcGFyYW1ldGVyLiBUaGUgb3B0aW9ucyBjYW4gYmUgdXNlZCB0byBleHRlbmQvb3ZlcnJpZGUgdGhlIFdvcmxkIEFQSSBTZXJ2aWNlIGRlZmF1bHRzLlxuICpcbiAqIFRvIHVzZSB0aGUgV29ybGQgQWRhcHRlciwgaW5zdGFudGlhdGUgaXQgYW5kIHRoZW4gYWNjZXNzIHRoZSBtZXRob2RzIHByb3ZpZGVkLiBJbnN0YW50aWF0aW5nIHJlcXVpcmVzIHRoZSBhY2NvdW50IGlkICgqKlRlYW0gSUQqKiBpbiB0aGUgRXBpY2VudGVyIHVzZXIgaW50ZXJmYWNlKSwgcHJvamVjdCBpZCAoKipQcm9qZWN0IElEKiopLCBhbmQgZ3JvdXAgKCoqR3JvdXAgTmFtZSoqKS5cbiAqXG4gKiAgICAgICB2YXIgd2EgPSBuZXcgRi5zZXJ2aWNlLldvcmxkKHtcbiAqICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAqICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gKiAgICAgICAgICBncm91cDogJ3RlYW0xJyB9KTtcbiAqICAgICAgIHdhLmNyZWF0ZSgpXG4gKiAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICogICAgICAgICAgICAgIC8vIGNhbGwgbWV0aG9kcywgZS5nLiB3YS5hZGRVc2VycygpXG4gKiAgICAgICAgICB9KTtcbiAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbnZhciBDb25maWdTZXJ2aWNlID0gcmVxdWlyZSgnLi9jb25maWd1cmF0aW9uLXNlcnZpY2UnKTtcbi8vIHZhciBxdXRpbCA9IHJlcXVpcmUoJy4uL3V0aWwvcXVlcnktdXRpbCcpO1xudmFyIFRyYW5zcG9ydEZhY3RvcnkgPSByZXF1aXJlKCcuLi90cmFuc3BvcnQvaHR0cC10cmFuc3BvcnQtZmFjdG9yeScpO1xudmFyIFNlc3Npb25NYW5hZ2VyID0gcmVxdWlyZSgnLi4vc3RvcmUvc2Vzc2lvbi1tYW5hZ2VyJyk7XG52YXIgX3BpY2sgPSByZXF1aXJlKCcuLi91dGlsL29iamVjdC11dGlsJykuX3BpY2s7XG5cbnZhciBhcGlCYXNlID0gJ211bHRpcGxheWVyLyc7XG52YXIgYXNzaWdubWVudEVuZHBvaW50ID0gYXBpQmFzZSArICdhc3NpZ24nO1xudmFyIGFwaUVuZHBvaW50ID0gYXBpQmFzZSArICd3b3JsZCc7XG52YXIgcHJvamVjdEVuZHBvaW50ID0gYXBpQmFzZSArICdwcm9qZWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICAvKipcbiAgICAgICAgICogRm9yIHByb2plY3RzIHRoYXQgcmVxdWlyZSBhdXRoZW50aWNhdGlvbiwgcGFzcyBpbiB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gKGRlZmF1bHRzIHRvIGVtcHR5IHN0cmluZykuIElmIHRoZSB1c2VyIGlzIGFscmVhZHkgbG9nZ2VkIGluIHRvIEVwaWNlbnRlciwgdGhlIHVzZXIgYWNjZXNzIHRva2VuIGlzIGFscmVhZHkgc2V0IGluIGEgY29va2llIGFuZCBhdXRvbWF0aWNhbGx5IGxvYWRlZCBmcm9tIHRoZXJlLiAoU2VlIFttb3JlIGJhY2tncm91bmQgb24gYWNjZXNzIHRva2Vuc10oLi4vLi4vLi4vcHJvamVjdF9hY2Nlc3MvKSkuXG4gICAgICAgICAqIEBzZWUgW0F1dGhlbnRpY2F0aW9uIEFQSSBTZXJ2aWNlXSguLi9hdXRoLWFwaS1zZXJ2aWNlLykgZm9yIGdldHRpbmcgdG9rZW5zLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgdG9rZW46IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHByb2plY3QgaWQuIElmIGxlZnQgdW5kZWZpbmVkLCB0YWtlbiBmcm9tIHRoZSBVUkwuXG4gICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICBwcm9qZWN0OiB1bmRlZmluZWQsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBhY2NvdW50IGlkLiBJbiB0aGUgRXBpY2VudGVyIFVJLCB0aGlzIGlzIHRoZSAqKlRlYW0gSUQqKiAoZm9yIHRlYW0gcHJvamVjdHMpLiBJZiBsZWZ0IHVuZGVmaW5lZCwgdGFrZW4gZnJvbSB0aGUgVVJMLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgYWNjb3VudDogdW5kZWZpbmVkLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgZ3JvdXAgbmFtZS4gRGVmYXVsdHMgdG8gdW5kZWZpbmVkLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgZ3JvdXA6IHVuZGVmaW5lZCxcblxuICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgbW9kZWwgZmlsZSB0byB1c2UgdG8gY3JlYXRlIHJ1bnMgaW4gdGhpcyB3b3JsZC4gRGVmYXVsdHMgdG8gdW5kZWZpbmVkLlxuICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgKi9cbiAgICAgICAgbW9kZWw6IHVuZGVmaW5lZCxcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ3JpdGVyaWEgYnkgd2hpY2ggdG8gZmlsdGVyIHdvcmxkLiBDdXJyZW50bHkgb25seSBzdXBwb3J0cyB3b3JsZC1pZHMgYXMgZmlsdGVycy5cbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGZpbHRlcjogJycsXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENvbnZlbmllbmNlIGFsaWFzIGZvciBmaWx0ZXJcbiAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICovXG4gICAgICAgIGlkOiAnJyxcblxuICAgICAgICAvKipcbiAgICAgICAgICogT3B0aW9ucyB0byBwYXNzIG9uIHRvIHRoZSB1bmRlcmx5aW5nIHRyYW5zcG9ydCBsYXllci4gQWxsIGpxdWVyeS5hamF4IG9wdGlvbnMgYXQgaHR0cDovL2FwaS5qcXVlcnkuY29tL2pRdWVyeS5hamF4LyBhcmUgYXZhaWxhYmxlLiBEZWZhdWx0cyB0byBlbXB0eSBvYmplY3QuXG4gICAgICAgICAqIEB0eXBlIHtPYmplY3R9XG4gICAgICAgICAqL1xuICAgICAgICB0cmFuc3BvcnQ6IHt9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDYWxsZWQgd2hlbiB0aGUgY2FsbCBjb21wbGV0ZXMgc3VjY2Vzc2Z1bGx5LiBEZWZhdWx0cyB0byBgJC5ub29wYC5cbiAgICAgICAgICogQHR5cGUge2Z1bmN0aW9ufVxuICAgICAgICAgKi9cbiAgICAgICAgc3VjY2VzczogJC5ub29wLFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDYWxsZWQgd2hlbiB0aGUgY2FsbCBmYWlscy4gRGVmYXVsdHMgdG8gYCQubm9vcGAuXG4gICAgICAgICAqIEB0eXBlIHtmdW5jdGlvbn1cbiAgICAgICAgICovXG4gICAgICAgIGVycm9yOiAkLm5vb3BcbiAgICB9O1xuXG4gICAgdGhpcy5zZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHZhciBzZXJ2aWNlT3B0aW9ucyA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0TWVyZ2VkT3B0aW9ucyhkZWZhdWx0cywgY29uZmlnKTtcbiAgICBpZiAoc2VydmljZU9wdGlvbnMuaWQpIHtcbiAgICAgICAgc2VydmljZU9wdGlvbnMuZmlsdGVyID0gc2VydmljZU9wdGlvbnMuaWQ7XG4gICAgfVxuXG4gICAgdmFyIHVybENvbmZpZyA9IG5ldyBDb25maWdTZXJ2aWNlKHNlcnZpY2VPcHRpb25zKS5nZXQoJ3NlcnZlcicpO1xuXG4gICAgaWYgKCFzZXJ2aWNlT3B0aW9ucy5hY2NvdW50KSB7XG4gICAgICAgIHNlcnZpY2VPcHRpb25zLmFjY291bnQgPSB1cmxDb25maWcuYWNjb3VudFBhdGg7XG4gICAgfVxuXG4gICAgaWYgKCFzZXJ2aWNlT3B0aW9ucy5wcm9qZWN0KSB7XG4gICAgICAgIHNlcnZpY2VPcHRpb25zLnByb2plY3QgPSB1cmxDb25maWcucHJvamVjdFBhdGg7XG4gICAgfVxuXG4gICAgdmFyIHRyYW5zcG9ydE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMudHJhbnNwb3J0LCB7XG4gICAgICAgIHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpXG4gICAgfSk7XG5cbiAgICBpZiAoc2VydmljZU9wdGlvbnMudG9rZW4pIHtcbiAgICAgICAgdHJhbnNwb3J0T3B0aW9ucy5oZWFkZXJzID0ge1xuICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogJ0JlYXJlciAnICsgc2VydmljZU9wdGlvbnMudG9rZW5cbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICB2YXIgaHR0cCA9IG5ldyBUcmFuc3BvcnRGYWN0b3J5KHRyYW5zcG9ydE9wdGlvbnMpO1xuXG4gICAgdmFyIHNldElkRmlsdGVyT3JUaHJvd0Vycm9yID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgaWYgKG9wdGlvbnMuaWQpIHtcbiAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IG9wdGlvbnMuaWQ7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9wdGlvbnMuZmlsdGVyKSB7XG4gICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgPSBvcHRpb25zLmZpbHRlcjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIXNlcnZpY2VPcHRpb25zLmZpbHRlcikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyB3b3JsZCBpZCBzcGVjaWZpZWQgdG8gYXBwbHkgb3BlcmF0aW9ucyBhZ2FpbnN0LiBUaGlzIGNvdWxkIGhhcHBlbiBpZiB0aGUgdXNlciBpcyBub3QgYXNzaWduZWQgdG8gYSB3b3JsZCBhbmQgaXMgdHJ5aW5nIHRvIHdvcmsgd2l0aCBydW5zIGZyb20gdGhhdCB3b3JsZC4nKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICB2YXIgdmFsaWRhdGVNb2RlbE9yVGhyb3dFcnJvciA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICAgIGlmICghb3B0aW9ucy5tb2RlbCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyBtb2RlbCBzcGVjaWZpZWQgdG8gZ2V0IHRoZSBjdXJyZW50IHJ1bicpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHZhciBwdWJsaWNBUEkgPSB7XG5cbiAgICAgICAgLyoqXG4gICAgICAgICogQ3JlYXRlcyBhIG5ldyBXb3JsZC5cbiAgICAgICAgKlxuICAgICAgICAqIFVzaW5nIHRoaXMgbWV0aG9kIGlzIHJhcmUuIEl0IGlzIG1vcmUgY29tbW9uIHRvIGNyZWF0ZSB3b3JsZHMgYXV0b21hdGljYWxseSB3aGlsZSB5b3UgYGF1dG9Bc3NpZ24oKWAgZW5kIHVzZXJzIHRvIHdvcmxkcy4gKEluIHRoaXMgY2FzZSwgY29uZmlndXJhdGlvbiBkYXRhIGZvciB0aGUgd29ybGQsIHN1Y2ggYXMgdGhlIHJvbGVzLCBhcmUgcmVhZCBmcm9tIHRoZSBwcm9qZWN0LWxldmVsIHdvcmxkIGNvbmZpZ3VyYXRpb24gaW5mb3JtYXRpb24sIGZvciBleGFtcGxlIGJ5IGBnZXRQcm9qZWN0U2V0dGluZ3MoKWAuKVxuICAgICAgICAqXG4gICAgICAgICogICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHZhciB3YSA9IG5ldyBGLnNlcnZpY2UuV29ybGQoe1xuICAgICAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICAgICogICAgICAgICAgIGdyb3VwOiAndGVhbTEnIH0pO1xuICAgICAgICAqICAgICAgd2EuY3JlYXRlKHtcbiAgICAgICAgKiAgICAgICAgICAgcm9sZXM6IFsnVlAgTWFya2V0aW5nJywgJ1ZQIFNhbGVzJywgJ1ZQIEVuZ2luZWVyaW5nJ11cbiAgICAgICAgKiAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwYXJhbXMgUGFyYW1ldGVycyB0byBjcmVhdGUgdGhlIHdvcmxkLlxuICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbXMuZ3JvdXAgKE9wdGlvbmFsKSBUaGUgKipHcm91cCBOYW1lKiogdG8gY3JlYXRlIHRoaXMgd29ybGQgdW5kZXIuIE9ubHkgZW5kIHVzZXJzIGluIHRoaXMgZ3JvdXAgYXJlIGVsaWdpYmxlIHRvIGpvaW4gdGhlIHdvcmxkLiBPcHRpb25hbCBoZXJlOyByZXF1aXJlZCB3aGVuIGluc3RhbnRpYXRpbmcgdGhlIHNlcnZpY2UgKGBuZXcgRi5zZXJ2aWNlLldvcmxkKClgKS5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zLnJvbGVzIChPcHRpb25hbCkgVGhlIGxpc3Qgb2Ygcm9sZXMgKHN0cmluZ3MpIGZvciB0aGlzIHdvcmxkLiBTb21lIHdvcmxkcyBoYXZlIHNwZWNpZmljIHJvbGVzIHRoYXQgKiptdXN0KiogYmUgZmlsbGVkIGJ5IGVuZCB1c2Vycy4gTGlzdGluZyB0aGUgcm9sZXMgYWxsb3dzIHlvdSB0byBhdXRvYXNzaWduIHVzZXJzIHRvIHdvcmxkcyBhbmQgZW5zdXJlIHRoYXQgYWxsIHJvbGVzIGFyZSBmaWxsZWQgaW4gZWFjaCB3b3JsZC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zLm9wdGlvbmFsUm9sZXMgKE9wdGlvbmFsKSBUaGUgbGlzdCBvZiBvcHRpb25hbCByb2xlcyAoc3RyaW5ncykgZm9yIHRoaXMgd29ybGQuIFNvbWUgd29ybGRzIGhhdmUgc3BlY2lmaWMgcm9sZXMgdGhhdCAqKm1heSoqIGJlIGZpbGxlZCBieSBlbmQgdXNlcnMuIExpc3RpbmcgdGhlIG9wdGlvbmFsIHJvbGVzIGFzIHBhcnQgb2YgdGhlIHdvcmxkIG9iamVjdCBhbGxvd3MgeW91IHRvIGF1dG9hc3NpZ24gdXNlcnMgdG8gd29ybGRzIGFuZCBlbnN1cmUgdGhhdCBhbGwgcm9sZXMgYXJlIGZpbGxlZCBpbiBlYWNoIHdvcmxkLlxuICAgICAgICAqIEBwYXJhbSB7aW50ZWdlcn0gcGFyYW1zLm1pblVzZXJzIChPcHRpb25hbCkgVGhlIG1pbmltdW0gbnVtYmVyIG9mIHVzZXJzIGZvciB0aGUgd29ybGQuIEluY2x1ZGluZyB0aGlzIG51bWJlciBhbGxvd3MgeW91IHRvIGF1dG9hc3NpZ24gZW5kIHVzZXJzIHRvIHdvcmxkcyBhbmQgZW5zdXJlIHRoYXQgdGhlIGNvcnJlY3QgbnVtYmVyIG9mIHVzZXJzIGFyZSBpbiBlYWNoIHdvcmxkLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgY3JlYXRlOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgY3JlYXRlT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBzZXJ2aWNlT3B0aW9ucywgb3B0aW9ucywgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSB9KTtcbiAgICAgICAgICAgIHZhciB3b3JsZEFwaVBhcmFtcyA9IFsnc2NvcGUnLCAnZmlsZXMnLCAncm9sZXMnLCAnb3B0aW9uYWxSb2xlcycsICdtaW5Vc2VycycsICdncm91cCcsICduYW1lJ107XG4gICAgICAgICAgICB2YXIgdmFsaWRQYXJhbXMgPSBfcGljayhzZXJ2aWNlT3B0aW9ucywgWydhY2NvdW50JywgJ3Byb2plY3QnLCAnZ3JvdXAnXSk7XG4gICAgICAgICAgICAvLyB3aGl0ZWxpc3QgdGhlIGZpZWxkcyB0aGF0IHdlIGFjdHVhbGx5IGNhbiBzZW5kIHRvIHRoZSBhcGlcbiAgICAgICAgICAgIHBhcmFtcyA9IF9waWNrKHBhcmFtcywgd29ybGRBcGlQYXJhbXMpO1xuXG4gICAgICAgICAgICAvLyBhY2NvdW50IGFuZCBwcm9qZWN0IGdvIGluIHRoZSBib2R5LCBub3QgaW4gdGhlIHVybFxuICAgICAgICAgICAgcGFyYW1zID0gJC5leHRlbmQoe30sIHZhbGlkUGFyYW1zLCBwYXJhbXMpO1xuXG4gICAgICAgICAgICB2YXIgb2xkU3VjY2VzcyA9IGNyZWF0ZU9wdGlvbnMuc3VjY2VzcztcbiAgICAgICAgICAgIGNyZWF0ZU9wdGlvbnMuc3VjY2VzcyA9IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLmZpbHRlciA9IHJlc3BvbnNlLmlkOyAvL2FsbCBmdXR1cmUgY2hhaW5lZCBjYWxscyB0byBvcGVyYXRlIG9uIHRoaXMgaWRcbiAgICAgICAgICAgICAgICByZXR1cm4gb2xkU3VjY2Vzcy5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucG9zdChwYXJhbXMsIGNyZWF0ZU9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIFVwZGF0ZXMgYSBXb3JsZCwgZm9yIGV4YW1wbGUgdG8gcmVwbGFjZSB0aGUgcm9sZXMgaW4gdGhlIHdvcmxkLlxuICAgICAgICAqXG4gICAgICAgICogVHlwaWNhbGx5LCB5b3UgY29tcGxldGUgd29ybGQgY29uZmlndXJhdGlvbiBhdCB0aGUgcHJvamVjdCBsZXZlbCwgcmF0aGVyIHRoYW4gYXQgdGhlIHdvcmxkIGxldmVsLiBGb3IgZXhhbXBsZSwgZWFjaCB3b3JsZCBpbiB5b3VyIHByb2plY3QgcHJvYmFibHkgaGFzIHRoZSBzYW1lIHJvbGVzIGZvciBlbmQgdXNlcnMuIEFuZCB5b3VyIHByb2plY3QgaXMgcHJvYmFibHkgZWl0aGVyIGNvbmZpZ3VyZWQgc28gdGhhdCBhbGwgZW5kIHVzZXJzIHNoYXJlIHRoZSBzYW1lIHdvcmxkIChhbmQgcnVuKSwgb3Igc21hbGxlciBzZXRzIG9mIGVuZCB1c2VycyBzaGFyZSB3b3JsZHMg4oCUIGJ1dCBub3QgYm90aC4gSG93ZXZlciwgdGhpcyBtZXRob2QgaXMgYXZhaWxhYmxlIGlmIHlvdSBuZWVkIHRvIHVwZGF0ZSB0aGUgY29uZmlndXJhdGlvbiBvZiBhIHBhcnRpY3VsYXIgd29ybGQuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5jcmVhdGUoKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EudXBkYXRlKHsgcm9sZXM6IFsnVlAgTWFya2V0aW5nJywgJ1ZQIFNhbGVzJywgJ1ZQIEVuZ2luZWVyaW5nJ10gfSk7XG4gICAgICAgICogICAgICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IHBhcmFtcyBQYXJhbWV0ZXJzIHRvIHVwZGF0ZSB0aGUgd29ybGQuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmFtcy5uYW1lIEEgc3RyaW5nIGlkZW50aWZpZXIgZm9yIHRoZSBsaW5rZWQgZW5kIHVzZXJzLCBmb3IgZXhhbXBsZSwgXCJuYW1lXCI6IFwiT3VyIFRlYW1cIi5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zLnJvbGVzIChPcHRpb25hbCkgVGhlIGxpc3Qgb2Ygcm9sZXMgKHN0cmluZ3MpIGZvciB0aGlzIHdvcmxkLiBTb21lIHdvcmxkcyBoYXZlIHNwZWNpZmljIHJvbGVzIHRoYXQgKiptdXN0KiogYmUgZmlsbGVkIGJ5IGVuZCB1c2Vycy4gTGlzdGluZyB0aGUgcm9sZXMgYWxsb3dzIHlvdSB0byBhdXRvYXNzaWduIHVzZXJzIHRvIHdvcmxkcyBhbmQgZW5zdXJlIHRoYXQgYWxsIHJvbGVzIGFyZSBmaWxsZWQgaW4gZWFjaCB3b3JsZC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gcGFyYW1zLm9wdGlvbmFsUm9sZXMgKE9wdGlvbmFsKSBUaGUgbGlzdCBvZiBvcHRpb25hbCByb2xlcyAoc3RyaW5ncykgZm9yIHRoaXMgd29ybGQuIFNvbWUgd29ybGRzIGhhdmUgc3BlY2lmaWMgcm9sZXMgdGhhdCAqKm1heSoqIGJlIGZpbGxlZCBieSBlbmQgdXNlcnMuIExpc3RpbmcgdGhlIG9wdGlvbmFsIHJvbGVzIGFzIHBhcnQgb2YgdGhlIHdvcmxkIG9iamVjdCBhbGxvd3MgeW91IHRvIGF1dG9hc3NpZ24gdXNlcnMgdG8gd29ybGRzIGFuZCBlbnN1cmUgdGhhdCBhbGwgcm9sZXMgYXJlIGZpbGxlZCBpbiBlYWNoIHdvcmxkLlxuICAgICAgICAqIEBwYXJhbSB7aW50ZWdlcn0gcGFyYW1zLm1pblVzZXJzIChPcHRpb25hbCkgVGhlIG1pbmltdW0gbnVtYmVyIG9mIHVzZXJzIGZvciB0aGUgd29ybGQuIEluY2x1ZGluZyB0aGlzIG51bWJlciBhbGxvd3MgeW91IHRvIGF1dG9hc3NpZ24gZW5kIHVzZXJzIHRvIHdvcmxkcyBhbmQgZW5zdXJlIHRoYXQgdGhlIGNvcnJlY3QgbnVtYmVyIG9mIHVzZXJzIGFyZSBpbiBlYWNoIHdvcmxkLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgdXBkYXRlOiBmdW5jdGlvbiAocGFyYW1zLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgd2hpdGVsaXN0ID0gWydyb2xlcycsICdvcHRpb25hbFJvbGVzJywgJ21pblVzZXJzJ107XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICAgICAgICAgIHNldElkRmlsdGVyT3JUaHJvd0Vycm9yKG9wdGlvbnMpO1xuXG4gICAgICAgICAgICB2YXIgdXBkYXRlT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHNlcnZpY2VPcHRpb25zLmZpbHRlciB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICBwYXJhbXMgPSBfcGljayhwYXJhbXMgfHwge30sIHdoaXRlbGlzdCk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLnBhdGNoKHBhcmFtcywgdXBkYXRlT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogRGVsZXRlcyBhbiBleGlzdGluZyB3b3JsZC5cbiAgICAgICAgKlxuICAgICAgICAqIFRoaXMgZnVuY3Rpb24gb3B0aW9uYWxseSB0YWtlcyBvbmUgYXJndW1lbnQuIElmIHRoZSBhcmd1bWVudCBpcyBhIHN0cmluZywgaXQgaXMgdGhlIGlkIG9mIHRoZSB3b3JsZCB0byBkZWxldGUuIElmIHRoZSBhcmd1bWVudCBpcyBhbiBvYmplY3QsIGl0IGlzIHRoZSBvdmVycmlkZSBmb3IgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5jcmVhdGUoKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EuZGVsZXRlKCk7XG4gICAgICAgICogICAgICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtTdHJpbmd8T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgVGhlIGlkIG9mIHRoZSB3b3JsZCB0byBkZWxldGUsIG9yIG9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGRlbGV0ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSAob3B0aW9ucyAmJiAodHlwZW9mIG9wdGlvbnMgPT09ICdzdHJpbmcnKSkgPyB7IGZpbHRlcjogb3B0aW9ucyB9IDoge307XG4gICAgICAgICAgICBzZXRJZEZpbHRlck9yVGhyb3dFcnJvcihvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIGRlbGV0ZU9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZGVsZXRlKG51bGwsIGRlbGV0ZU9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIFVwZGF0ZXMgdGhlIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBjdXJyZW50IGluc3RhbmNlIG9mIHRoZSBXb3JsZCBBUEkgQWRhcHRlciAoaW5jbHVkaW5nIGFsbCBzdWJzZXF1ZW50IGZ1bmN0aW9uIGNhbGxzLCB1bnRpbCB0aGUgY29uZmlndXJhdGlvbiBpcyB1cGRhdGVkIGFnYWluKS5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHZhciB3YSA9IG5ldyBGLnNlcnZpY2UuV29ybGQoey4uLn0pLnVwZGF0ZUNvbmZpZyh7IGZpbHRlcjogJzEyMycgfSkuYWRkVXNlcih7IHVzZXJJZDogJzEyMycgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBjb25maWcgVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIHVzZSBpbiB1cGRhdGluZyBleGlzdGluZyBjb25maWd1cmF0aW9uLlxuICAgICAgICAqIEByZXR1cm4ge09iamVjdH0gcmVmZXJlbmNlIHRvIGN1cnJlbnQgaW5zdGFuY2VcbiAgICAgICAgKi9cbiAgICAgICAgdXBkYXRlQ29uZmlnOiBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgICAgICAgICAkLmV4dGVuZChzZXJ2aWNlT3B0aW9ucywgY29uZmlnKTtcblxuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogTGlzdHMgYWxsIHdvcmxkcyBmb3IgYSBnaXZlbiBhY2NvdW50LCBwcm9qZWN0LCBhbmQgZ3JvdXAuIEFsbCB0aHJlZSBhcmUgcmVxdWlyZWQsIGFuZCBpZiBub3Qgc3BlY2lmaWVkIGFzIHBhcmFtZXRlcnMsIGFyZSByZWFkIGZyb20gdGhlIHNlcnZpY2UuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5jcmVhdGUoKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgLy8gbGlzdHMgYWxsIHdvcmxkcyBpbiBncm91cCBcInRlYW0xXCJcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdhLmxpc3QoKTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgICAgICAgICAgLy8gbGlzdHMgYWxsIHdvcmxkcyBpbiBncm91cCBcIm90aGVyLWdyb3VwLW5hbWVcIlxuICAgICAgICAqICAgICAgICAgICAgICAgd2EubGlzdCh7IGdyb3VwOiAnb3RoZXItZ3JvdXAtbmFtZScgfSk7XG4gICAgICAgICogICAgICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICoqUGFyYW1ldGVycyoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBsaXN0OiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAgICAgICAgIHZhciBnZXRPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sXG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMsXG4gICAgICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgICAgICB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHZhciBmaWx0ZXJzID0gX3BpY2soZ2V0T3B0aW9ucywgWydhY2NvdW50JywgJ3Byb2plY3QnLCAnZ3JvdXAnXSk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldChmaWx0ZXJzLCBnZXRPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBHZXRzIGFsbCB3b3JsZHMgdGhhdCBhbiBlbmQgdXNlciBiZWxvbmdzIHRvIGZvciBhIGdpdmVuIGFjY291bnQgKHRlYW0pLCBwcm9qZWN0LCBhbmQgZ3JvdXAuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5jcmVhdGUoKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EuZ2V0V29ybGRzRm9yVXNlcignYjFjMTlkZGEtMmQyZS00Nzc3LWFkNWQtMzkyOWYxN2U4NmQzJylcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKiBQYXJhbWV0ZXJzICoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHVzZXJJZCBUaGUgYHVzZXJJZGAgb2YgdGhlIHVzZXIgd2hvc2Ugd29ybGRzIGFyZSBiZWluZyByZXRyaWV2ZWQuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBnZXRXb3JsZHNGb3JVc2VyOiBmdW5jdGlvbiAodXNlcklkLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgICAgICAgICAgdmFyIGdldE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgdmFyIGZpbHRlcnMgPSAkLmV4dGVuZChcbiAgICAgICAgICAgICAgICBfcGljayhnZXRPcHRpb25zLCBbJ2FjY291bnQnLCAncHJvamVjdCcsICdncm91cCddKSxcbiAgICAgICAgICAgICAgICB7IHVzZXJJZDogdXNlcklkIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldChmaWx0ZXJzLCBnZXRPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogTG9hZCBpbmZvcm1hdGlvbiBmb3IgYSBzcGVjaWZpYyB3b3JsZC4gQWxsIGZ1cnRoZXIgY2FsbHMgdG8gdGhlIHdvcmxkIHNlcnZpY2Ugd2lsbCB1c2UgdGhlIGlkIHByb3ZpZGVkLlxuICAgICAgICAgKlxuICAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gd29ybGRJZCBUaGUgaWQgb2YgdGhlIHdvcmxkIHRvIGxvYWQuXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICAqL1xuICAgICAgICBsb2FkOiBmdW5jdGlvbiAod29ybGRJZCwgb3B0aW9ucykge1xuICAgICAgICAgICAgaWYgKHdvcmxkSWQpIHtcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgPSB3b3JsZElkO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCFzZXJ2aWNlT3B0aW9ucy5maWx0ZXIpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsZWFzZSBwcm92aWRlIGEgd29ybGRpZCB0byBsb2FkJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgaHR0cE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSwgc2VydmljZU9wdGlvbnMsIG9wdGlvbnMsIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgKyAnLycgfSk7XG4gICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQoJycsIGh0dHBPcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBBZGRzIGFuIGVuZCB1c2VyIG9yIGxpc3Qgb2YgZW5kIHVzZXJzIHRvIGEgZ2l2ZW4gd29ybGQuIFRoZSBlbmQgdXNlciBtdXN0IGJlIGEgbWVtYmVyIG9mIHRoZSBgZ3JvdXBgIHRoYXQgaXMgYXNzb2NpYXRlZCB3aXRoIHRoaXMgd29ybGQuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5jcmVhdGUoKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgLy8gYWRkIG9uZSB1c2VyXG4gICAgICAgICogICAgICAgICAgICAgICB3YS5hZGRVc2VycygnYjFjMTlkZGEtMmQyZS00Nzc3LWFkNWQtMzkyOWYxN2U4NmQzJyk7XG4gICAgICAgICogICAgICAgICAgICAgICB3YS5hZGRVc2VycyhbJ2IxYzE5ZGRhLTJkMmUtNDc3Ny1hZDVkLTM5MjlmMTdlODZkMyddKTtcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdhLmFkZFVzZXJzKHsgdXNlcklkOiAnYjFjMTlkZGEtMmQyZS00Nzc3LWFkNWQtMzkyOWYxN2U4NmQzJywgcm9sZTogJ1ZQIFNhbGVzJyB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgICAgICAgICAgLy8gYWRkIHNldmVyYWwgdXNlcnNcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdhLmFkZFVzZXJzKFtcbiAgICAgICAgKiAgICAgICAgICAgICAgICAgICB7IHVzZXJJZDogJ2E2ZmUwYzFlLWY0YjgtNGYwMS05ZjVmLTAxY2NmNGMyZWQ0NCcsXG4gICAgICAgICogICAgICAgICAgICAgICAgICAgICByb2xlOiAnVlAgTWFya2V0aW5nJyB9LFxuICAgICAgICAqICAgICAgICAgICAgICAgICAgIHsgdXNlcklkOiAnOGYyNjA0Y2YtOTZjZC00NDlmLTgyZmEtZTMzMTUzMDczNGVlJyxcbiAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgIHJvbGU6ICdWUCBFbmdpbmVlcmluZycgfVxuICAgICAgICAqICAgICAgICAgICAgICAgXSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgICAgICAgICAgIC8vIGFkZCBvbmUgdXNlciB0byBhIHNwZWNpZmljIHdvcmxkXG4gICAgICAgICogICAgICAgICAgICAgICB3YS5hZGRVc2VycygnYjFjMTlkZGEtMmQyZS00Nzc3LWFkNWQtMzkyOWYxN2U4NmQzJywgd29ybGQuaWQpO1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EuYWRkVXNlcnMoJ2IxYzE5ZGRhLTJkMmUtNDc3Ny1hZDVkLTM5MjlmMTdlODZkMycsIHsgZmlsdGVyOiB3b3JsZC5pZCB9KTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKiBQYXJhbWV0ZXJzICoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd8b2JqZWN0fGFycmF5fSB1c2VycyBVc2VyIGlkLCBhcnJheSBvZiB1c2VyIGlkcywgb2JqZWN0LCBvciBhcnJheSBvZiBvYmplY3RzIG9mIHRoZSB1c2VycyB0byBhZGQgdG8gdGhpcyB3b3JsZC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdXNlcnMucm9sZSBUaGUgYHJvbGVgIHRoZSB1c2VyIHNob3VsZCBoYXZlIGluIHRoZSB3b3JsZC4gSXQgaXMgdXAgdG8gdGhlIGNhbGxlciB0byBlbnN1cmUsIGlmIG5lZWRlZCwgdGhhdCB0aGUgYHJvbGVgIHBhc3NlZCBpbiBpcyBvbmUgb2YgdGhlIGByb2xlc2Agb3IgYG9wdGlvbmFsUm9sZXNgIG9mIHRoaXMgd29ybGQuXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHdvcmxkSWQgVGhlIHdvcmxkIHRvIHdoaWNoIHRoZSB1c2VycyBzaG91bGQgYmUgYWRkZWQuIElmIG5vdCBzcGVjaWZpZWQsIHRoZSBmaWx0ZXIgcGFyYW1ldGVyIG9mIHRoZSBgb3B0aW9uc2Agb2JqZWN0IGlzIHVzZWQuXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKE9wdGlvbmFsKSBPcHRpb25zIG9iamVjdCB0byBvdmVycmlkZSBnbG9iYWwgb3B0aW9ucy5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBhZGRVc2VyczogZnVuY3Rpb24gKHVzZXJzLCB3b3JsZElkLCBvcHRpb25zKSB7XG5cbiAgICAgICAgICAgIGlmICghdXNlcnMpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsZWFzZSBwcm92aWRlIGEgbGlzdCBvZiB1c2VycyB0byBhZGQgdG8gdGhlIHdvcmxkJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIG5vcm1hbGl6ZSB0aGUgbGlzdCBvZiB1c2VycyB0byBhbiBhcnJheSBvZiB1c2VyIG9iamVjdHNcbiAgICAgICAgICAgIHVzZXJzID0gJC5tYXAoW10uY29uY2F0KHVzZXJzKSwgZnVuY3Rpb24gKHUpIHtcbiAgICAgICAgICAgICAgICB2YXIgaXNPYmplY3QgPSAkLmlzUGxhaW5PYmplY3QodSk7XG5cbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHUgIT09ICdzdHJpbmcnICYmICFpc09iamVjdCkge1xuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NvbWUgb2YgdGhlIHVzZXJzIGluIHRoZSBsaXN0IGFyZSBub3QgaW4gdGhlIHZhbGlkIGZvcm1hdDogJyArIHUpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybiBpc09iamVjdCA/IHUgOiB7IHVzZXJJZDogdSB9O1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIC8vIGNoZWNrIGlmIG9wdGlvbnMgd2VyZSBwYXNzZWQgYXMgdGhlIHNlY29uZCBwYXJhbWV0ZXJcbiAgICAgICAgICAgIGlmICgkLmlzUGxhaW5PYmplY3Qod29ybGRJZCkgJiYgIW9wdGlvbnMpIHtcbiAgICAgICAgICAgICAgICBvcHRpb25zID0gd29ybGRJZDtcbiAgICAgICAgICAgICAgICB3b3JsZElkID0gbnVsbDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAgICAgICAgIC8vIHdlIG11c3QgaGF2ZSBvcHRpb25zIGJ5IG5vd1xuICAgICAgICAgICAgaWYgKHR5cGVvZiB3b3JsZElkID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMuZmlsdGVyID0gd29ybGRJZDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc2V0SWRGaWx0ZXJPclRocm93RXJyb3Iob3B0aW9ucyk7XG5cbiAgICAgICAgICAgIHZhciB1cGRhdGVPcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sXG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMsXG4gICAgICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgICAgICB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgoYXBpRW5kcG9pbnQpICsgc2VydmljZU9wdGlvbnMuZmlsdGVyICsgJy91c2VycycgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucG9zdCh1c2VycywgdXBkYXRlT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogVXBkYXRlcyB0aGUgcm9sZSBvZiBhbiBlbmQgdXNlciBpbiBhIGdpdmVuIHdvcmxkLiAoWW91IGNhbiBvbmx5IHVwZGF0ZSBvbmUgZW5kIHVzZXIgYXQgYSB0aW1lLilcbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHZhciB3YSA9IG5ldyBGLnNlcnZpY2UuV29ybGQoe1xuICAgICAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICAgICogICAgICAgICAgIGdyb3VwOiAndGVhbTEnIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICAgICB3YS5jcmVhdGUoKS50aGVuKGZ1bmN0aW9uKHdvcmxkKSB7XG4gICAgICAgICogICAgICAgICAgIHdhLmFkZFVzZXJzKCdiMWMxOWRkYS0yZDJlLTQ3NzctYWQ1ZC0zOTI5ZjE3ZTg2ZDMnKTtcbiAgICAgICAgKiAgICAgICAgICAgd2EudXBkYXRlVXNlcih7IHVzZXJJZDogJ2IxYzE5ZGRhLTJkMmUtNDc3Ny1hZDVkLTM5MjlmMTdlODZkMycsIHJvbGU6ICdsZWFkZXInIH0pO1xuICAgICAgICAqICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSB1c2VyIFVzZXIgb2JqZWN0IHdpdGggYHVzZXJJZGAgYW5kIHRoZSBuZXcgYHJvbGVgLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgdXBkYXRlVXNlcjogZnVuY3Rpb24gKHVzZXIsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgICAgICBpZiAoIXVzZXIgfHwgIXVzZXIudXNlcklkKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdZb3UgbmVlZCB0byBwYXNzIGEgdXNlcklkIHRvIHVwZGF0ZSBmcm9tIHRoZSB3b3JsZCcpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzZXRJZEZpbHRlck9yVGhyb3dFcnJvcihvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIHBhdGNoT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LFxuICAgICAgICAgICAgICAgIHNlcnZpY2VPcHRpb25zLFxuICAgICAgICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgICAgICAgeyB1cmw6IHVybENvbmZpZy5nZXRBUElQYXRoKGFwaUVuZHBvaW50KSArIHNlcnZpY2VPcHRpb25zLmZpbHRlciArICcvdXNlcnMvJyArIHVzZXIudXNlcklkIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHJldHVybiBodHRwLnBhdGNoKF9waWNrKHVzZXIsICdyb2xlJyksIHBhdGNoT3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogUmVtb3ZlcyBhbiBlbmQgdXNlciBmcm9tIGEgZ2l2ZW4gd29ybGQuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICogICAgICB3YS5jcmVhdGUoKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbih3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EuYWRkVXNlcnMoWydhNmZlMGMxZS1mNGI4LTRmMDEtOWY1Zi0wMWNjZjRjMmVkNDQnLCAnOGYyNjA0Y2YtOTZjZC00NDlmLTgyZmEtZTMzMTUzMDczNGVlJ10pO1xuICAgICAgICAqICAgICAgICAgICAgICAgd2EucmVtb3ZlVXNlcignYTZmZTBjMWUtZjRiOC00ZjAxLTlmNWYtMDFjY2Y0YzJlZDQ0Jyk7XG4gICAgICAgICogICAgICAgICAgICAgICB3YS5yZW1vdmVVc2VyKHsgdXNlcklkOiAnOGYyNjA0Y2YtOTZjZC00NDlmLTgyZmEtZTMzMTUzMDczNGVlJyB9KTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKiBQYXJhbWV0ZXJzICoqXG4gICAgICAgICogQHBhcmFtIHtvYmplY3R8c3RyaW5nfSB1c2VyIFRoZSBgdXNlcklkYCBvZiB0aGUgdXNlciB0byByZW1vdmUgZnJvbSB0aGUgd29ybGQsIG9yIGFuIG9iamVjdCBjb250YWluaW5nIHRoZSBgdXNlcklkYCBmaWVsZC5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIHJlbW92ZVVzZXI6IGZ1bmN0aW9uICh1c2VyLCBvcHRpb25zKSB7XG4gICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgICAgICAgICAgaWYgKHR5cGVvZiB1c2VyID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIHVzZXIgPSB7IHVzZXJJZDogdXNlciB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIXVzZXIudXNlcklkKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdZb3UgbmVlZCB0byBwYXNzIGEgdXNlcklkIHRvIHJlbW92ZSBmcm9tIHRoZSB3b3JsZCcpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzZXRJZEZpbHRlck9yVGhyb3dFcnJvcihvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIGdldE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgKyAnL3VzZXJzLycgKyB1c2VyLnVzZXJJZCB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICByZXR1cm4gaHR0cC5kZWxldGUobnVsbCwgZ2V0T3B0aW9ucyk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogR2V0cyB0aGUgcnVuIGlkIG9mIGN1cnJlbnQgcnVuIGZvciB0aGUgZ2l2ZW4gd29ybGQuIElmIHRoZSB3b3JsZCBkb2VzIG5vdCBoYXZlIGEgcnVuLCBjcmVhdGVzIGEgbmV3IG9uZSBhbmQgcmV0dXJucyB0aGUgcnVuIGlkLlxuICAgICAgICAqXG4gICAgICAgICogUmVtZW1iZXIgdGhhdCBhIFtydW5dKC4uLy4uL2dsb3NzYXJ5LyNydW4pIGlzIGEgY29sbGVjdGlvbiBvZiBpbnRlcmFjdGlvbnMgd2l0aCBhIHByb2plY3QgYW5kIGl0cyBtb2RlbC4gSW4gdGhlIGNhc2Ugb2YgbXVsdGlwbGF5ZXIgcHJvamVjdHMsIHRoZSBydW4gaXMgc2hhcmVkIGJ5IGFsbCBlbmQgdXNlcnMgaW4gdGhlIHdvcmxkLlxuICAgICAgICAqXG4gICAgICAgICogICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHZhciB3YSA9IG5ldyBGLnNlcnZpY2UuV29ybGQoe1xuICAgICAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICAgICogICAgICAgICAgIGdyb3VwOiAndGVhbTEnIH0pO1xuICAgICAgICAqICAgICAgd2EuY3JlYXRlKClcbiAgICAgICAgKiAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24od29ybGQpIHtcbiAgICAgICAgKiAgICAgICAgICAgICAgIHdhLmdldEN1cnJlbnRSdW5JZCh7IG1vZGVsOiAnbW9kZWwucHknIH0pO1xuICAgICAgICAqICAgICAgICAgICB9KTtcbiAgICAgICAgKlxuICAgICAgICAqICoqIFBhcmFtZXRlcnMgKipcbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zLm1vZGVsIFRoZSBtb2RlbCBmaWxlIHRvIHVzZSB0byBjcmVhdGUgYSBydW4gaWYgbmVlZGVkLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldEN1cnJlbnRSdW5JZDogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgICAgICBzZXRJZEZpbHRlck9yVGhyb3dFcnJvcihvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIGdldE9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgKyAnL3J1bicgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgdmFsaWRhdGVNb2RlbE9yVGhyb3dFcnJvcihnZXRPcHRpb25zKTtcbiAgICAgICAgICAgIHJldHVybiBodHRwLnBvc3QoX3BpY2soZ2V0T3B0aW9ucywgJ21vZGVsJyksIGdldE9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIEdldHMgdGhlIGN1cnJlbnQgKG1vc3QgcmVjZW50KSB3b3JsZCBmb3IgdGhlIGdpdmVuIGVuZCB1c2VyIGluIHRoZSBnaXZlbiBncm91cC4gQnJpbmdzIHRoaXMgbW9zdCByZWNlbnQgd29ybGQgaW50byBtZW1vcnkgaWYgbmVlZGVkLlxuICAgICAgICAqXG4gICAgICAgICogICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHZhciB3YSA9IG5ldyBGLnNlcnZpY2UuV29ybGQoe1xuICAgICAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICAgICogICAgICAgICAgIGdyb3VwOiAndGVhbTEnIH0pO1xuICAgICAgICAqICAgICAgd2EuZ2V0Q3VycmVudFdvcmxkRm9yVXNlcignOGYyNjA0Y2YtOTZjZC00NDlmLTgyZmEtZTMzMTUzMDczNGVlJylcbiAgICAgICAgKiAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24od29ybGQpIHtcbiAgICAgICAgKiAgICAgICAgICAgICAgIC8vIHVzZSBkYXRhIGZyb20gd29ybGRcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKiBQYXJhbWV0ZXJzICoqXG4gICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHVzZXJJZCBUaGUgYHVzZXJJZGAgb2YgdGhlIHVzZXIgd2hvc2UgY3VycmVudCAobW9zdCByZWNlbnQpIHdvcmxkIGlzIGJlaW5nIHJldHJpZXZlZC5cbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gZ3JvdXBOYW1lIChPcHRpb25hbCkgVGhlIG5hbWUgb2YgdGhlIGdyb3VwLiBJZiBub3QgcHJvdmlkZWQsIGRlZmF1bHRzIHRvIHRoZSBncm91cCB1c2VkIHRvIGNyZWF0ZSB0aGUgc2VydmljZS5cbiAgICAgICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgICAgICAqL1xuICAgICAgICBnZXRDdXJyZW50V29ybGRGb3JVc2VyOiBmdW5jdGlvbiAodXNlcklkLCBncm91cE5hbWUpIHtcbiAgICAgICAgICAgIHZhciBkdGQgPSAkLkRlZmVycmVkKCk7XG4gICAgICAgICAgICB2YXIgbWUgPSB0aGlzO1xuICAgICAgICAgICAgdGhpcy5nZXRXb3JsZHNGb3JVc2VyKHVzZXJJZCwgeyBncm91cDogZ3JvdXBOYW1lIH0pXG4gICAgICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKHdvcmxkcykge1xuICAgICAgICAgICAgICAgICAgICAvLyBhc3N1bWUgdGhlIG1vc3QgcmVjZW50IHdvcmxkIGFzIHRoZSAnYWN0aXZlJyB3b3JsZFxuICAgICAgICAgICAgICAgICAgICB3b3JsZHMuc29ydChmdW5jdGlvbiAoYSwgYikgeyByZXR1cm4gbmV3IERhdGUoYi5sYXN0TW9kaWZpZWQpIC0gbmV3IERhdGUoYS5sYXN0TW9kaWZpZWQpOyB9KTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGN1cnJlbnRXb3JsZCA9IHdvcmxkc1swXTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoY3VycmVudFdvcmxkKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgPSBjdXJyZW50V29ybGQuaWQ7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBkdGQucmVzb2x2ZVdpdGgobWUsIFtjdXJyZW50V29ybGRdKTtcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC5mYWlsKGR0ZC5yZWplY3QpO1xuXG4gICAgICAgICAgICByZXR1cm4gZHRkLnByb21pc2UoKTtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgKiBEZWxldGVzIHRoZSBjdXJyZW50IHJ1biBmcm9tIHRoZSB3b3JsZC5cbiAgICAgICAgKlxuICAgICAgICAqIChOb3RlIHRoYXQgdGhlIHdvcmxkIGlkIHJlbWFpbnMgcGFydCBvZiB0aGUgcnVuIHJlY29yZCwgaW5kaWNhdGluZyB0aGF0IHRoZSBydW4gd2FzIGZvcm1lcmx5IGFuIGFjdGl2ZSBydW4gZm9yIHRoZSB3b3JsZC4pXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHdhLmRlbGV0ZVJ1bignc2FtcGxlLXdvcmxkLWlkJyk7XG4gICAgICAgICpcbiAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gd29ybGRJZCBUaGUgYHdvcmxkSWRgIG9mIHRoZSB3b3JsZCBmcm9tIHdoaWNoIHRoZSBjdXJyZW50IHJ1biBpcyBiZWluZyBkZWxldGVkLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKi9cbiAgICAgICAgZGVsZXRlUnVuOiBmdW5jdGlvbiAod29ybGRJZCwgb3B0aW9ucykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAgICAgICAgIGlmICh3b3JsZElkKSB7XG4gICAgICAgICAgICAgICAgb3B0aW9ucy5maWx0ZXIgPSB3b3JsZElkO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzZXRJZEZpbHRlck9yVGhyb3dFcnJvcihvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIGRlbGV0ZU9wdGlvbnMgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhcGlFbmRwb2ludCkgKyBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgKyAnL3J1bicgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAuZGVsZXRlKG51bGwsIGRlbGV0ZU9wdGlvbnMpO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIENyZWF0ZXMgYSBuZXcgcnVuIGZvciB0aGUgd29ybGQuXG4gICAgICAgICpcbiAgICAgICAgKiAgKipFeGFtcGxlKipcbiAgICAgICAgKlxuICAgICAgICAqICAgICAgdmFyIHdhID0gbmV3IEYuc2VydmljZS5Xb3JsZCh7XG4gICAgICAgICogICAgICAgICAgIGFjY291bnQ6ICdhY21lLXNpbXVsYXRpb25zJyxcbiAgICAgICAgKiAgICAgICAgICAgcHJvamVjdDogJ3N1cHBseS1jaGFpbi1nYW1lJyxcbiAgICAgICAgKiAgICAgICAgICAgZ3JvdXA6ICd0ZWFtMScgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHdhLmdldEN1cnJlbnRXb3JsZEZvclVzZXIoJzhmMjYwNGNmLTk2Y2QtNDQ5Zi04MmZhLWUzMzE1MzA3MzRlZScpXG4gICAgICAgICogICAgICAgICAgIC50aGVuKGZ1bmN0aW9uICh3b3JsZCkge1xuICAgICAgICAqICAgICAgICAgICAgICAgICAgIHdhLm5ld1J1bkZvcldvcmxkKHdvcmxkLmlkKTtcbiAgICAgICAgKiAgICAgICAgICAgfSk7XG4gICAgICAgICpcbiAgICAgICAgKiAgKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gd29ybGRJZCB3b3JsZElkIGluIHdoaWNoIHdlIGNyZWF0ZSB0aGUgbmV3IHJ1bi5cbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zLm1vZGVsIFRoZSBtb2RlbCBmaWxlIHRvIHVzZSB0byBjcmVhdGUgYSBydW4gaWYgbmVlZGVkLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIG5ld1J1bkZvcldvcmxkOiBmdW5jdGlvbiAod29ybGRJZCwgb3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIGN1cnJlbnRSdW5PcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sXG4gICAgICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgICAgICB7IGZpbHRlcjogd29ybGRJZCB8fCBzZXJ2aWNlT3B0aW9ucy5maWx0ZXIgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHZhciBtZSA9IHRoaXM7XG5cbiAgICAgICAgICAgIHZhbGlkYXRlTW9kZWxPclRocm93RXJyb3IoY3VycmVudFJ1bk9wdGlvbnMpO1xuXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5kZWxldGVSdW4od29ybGRJZCwgb3B0aW9ucylcbiAgICAgICAgICAgICAgICAudGhlbihmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBtZS5nZXRDdXJyZW50UnVuSWQoY3VycmVudFJ1bk9wdGlvbnMpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAqIEFzc2lnbnMgZW5kIHVzZXJzIHRvIHdvcmxkcywgY3JlYXRpbmcgbmV3IHdvcmxkcyBhcyBhcHByb3ByaWF0ZSwgYXV0b21hdGljYWxseS4gQXNzaWducyBhbGwgZW5kIHVzZXJzIGluIHRoZSBncm91cCwgYW5kIGNyZWF0ZXMgbmV3IHdvcmxkcyBhcyBuZWVkZWQgYmFzZWQgb24gdGhlIHByb2plY3QtbGV2ZWwgd29ybGQgY29uZmlndXJhdGlvbiAocm9sZXMsIG9wdGlvbmFsIHJvbGVzLCBhbmQgbWluaW11bSBlbmQgdXNlcnMgcGVyIHdvcmxkKS5cbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHZhciB3YSA9IG5ldyBGLnNlcnZpY2UuV29ybGQoe1xuICAgICAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICAgICogICAgICAgICAgIGdyb3VwOiAndGVhbTEnIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICAgICB3YS5hdXRvQXNzaWduKCk7XG4gICAgICAgICpcbiAgICAgICAgKiAqKlBhcmFtZXRlcnMqKlxuICAgICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIChPcHRpb25hbCkgT3B0aW9ucyBvYmplY3QgdG8gb3ZlcnJpZGUgZ2xvYmFsIG9wdGlvbnMuXG4gICAgICAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICAgICAgKlxuICAgICAgICAqL1xuICAgICAgICBhdXRvQXNzaWduOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAgICAgICAgIHZhciBvcHQgPSAkLmV4dGVuZCh0cnVlLCB7fSxcbiAgICAgICAgICAgICAgICBzZXJ2aWNlT3B0aW9ucyxcbiAgICAgICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgICAgIHsgdXJsOiB1cmxDb25maWcuZ2V0QVBJUGF0aChhc3NpZ25tZW50RW5kcG9pbnQpIH1cbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHZhciBwYXJhbXMgPSB7XG4gICAgICAgICAgICAgICAgYWNjb3VudDogb3B0LmFjY291bnQsXG4gICAgICAgICAgICAgICAgcHJvamVjdDogb3B0LnByb2plY3QsXG4gICAgICAgICAgICAgICAgZ3JvdXA6IG9wdC5ncm91cFxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgaWYgKG9wdC5tYXhVc2Vycykge1xuICAgICAgICAgICAgICAgIHBhcmFtcy5tYXhVc2VycyA9IG9wdC5tYXhVc2VycztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIGh0dHAucG9zdChwYXJhbXMsIG9wdCk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICogR2V0cyB0aGUgcHJvamVjdCdzIHdvcmxkIGNvbmZpZ3VyYXRpb24uXG4gICAgICAgICpcbiAgICAgICAgKiBUeXBpY2FsbHksIGV2ZXJ5IGludGVyYWN0aW9uIHdpdGggeW91ciBwcm9qZWN0IHVzZXMgdGhlIHNhbWUgY29uZmlndXJhdGlvbiBvZiBlYWNoIHdvcmxkLiBGb3IgZXhhbXBsZSwgZWFjaCB3b3JsZCBpbiB5b3VyIHByb2plY3QgcHJvYmFibHkgaGFzIHRoZSBzYW1lIHJvbGVzIGZvciBlbmQgdXNlcnMuIEFuZCB5b3VyIHByb2plY3QgaXMgcHJvYmFibHkgZWl0aGVyIGNvbmZpZ3VyZWQgc28gdGhhdCBhbGwgZW5kIHVzZXJzIHNoYXJlIHRoZSBzYW1lIHdvcmxkIChhbmQgcnVuKSwgb3Igc21hbGxlciBzZXRzIG9mIGVuZCB1c2VycyBzaGFyZSB3b3JsZHMg4oCUIGJ1dCBub3QgYm90aC5cbiAgICAgICAgKlxuICAgICAgICAqIChUaGUgW011bHRpcGxheWVyIFByb2plY3QgUkVTVCBBUEldKC4uLy4uLy4uL3Jlc3RfYXBpcy9tdWx0aXBsYXllci9tdWx0aXBsYXllcl9wcm9qZWN0LykgYWxsb3dzIHlvdSB0byBzZXQgdGhlc2UgcHJvamVjdC1sZXZlbCB3b3JsZCBjb25maWd1cmF0aW9ucy4gVGhlIFdvcmxkIEFkYXB0ZXIgc2ltcGx5IHJldHJpZXZlcyB0aGVtLCBmb3IgZXhhbXBsZSBzbyB0aGV5IGNhbiBiZSB1c2VkIGluIGF1dG8tYXNzaWdubWVudCBvZiBlbmQgdXNlcnMgdG8gd29ybGRzLilcbiAgICAgICAgKlxuICAgICAgICAqICoqRXhhbXBsZSoqXG4gICAgICAgICpcbiAgICAgICAgKiAgICAgIHZhciB3YSA9IG5ldyBGLnNlcnZpY2UuV29ybGQoe1xuICAgICAgICAqICAgICAgICAgICBhY2NvdW50OiAnYWNtZS1zaW11bGF0aW9ucycsXG4gICAgICAgICogICAgICAgICAgIHByb2plY3Q6ICdzdXBwbHktY2hhaW4tZ2FtZScsXG4gICAgICAgICogICAgICAgICAgIGdyb3VwOiAndGVhbTEnIH0pO1xuICAgICAgICAqXG4gICAgICAgICogICAgICB3YS5nZXRQcm9qZWN0U2V0dGluZ3MoKVxuICAgICAgICAqICAgICAgICAgICAudGhlbihmdW5jdGlvbihzZXR0aW5ncykge1xuICAgICAgICAqICAgICAgICAgICAgICAgY29uc29sZS5sb2coc2V0dGluZ3Mucm9sZXMpO1xuICAgICAgICAqICAgICAgICAgICAgICAgY29uc29sZS5sb2coc2V0dGluZ3Mub3B0aW9uYWxSb2xlcyk7XG4gICAgICAgICogICAgICAgICAgIH0pO1xuICAgICAgICAqXG4gICAgICAgICogKipQYXJhbWV0ZXJzKipcbiAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAoT3B0aW9uYWwpIE9wdGlvbnMgb2JqZWN0IHRvIG92ZXJyaWRlIGdsb2JhbCBvcHRpb25zLlxuICAgICAgICAqIEByZXR1cm4ge1Byb21pc2V9XG4gICAgICAgICovXG4gICAgICAgIGdldFByb2plY3RTZXR0aW5nczogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgICAgICAgICB2YXIgb3B0ID0gJC5leHRlbmQodHJ1ZSwge30sXG4gICAgICAgICAgICAgICAgc2VydmljZU9wdGlvbnMsXG4gICAgICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgICAgICB7IHVybDogdXJsQ29uZmlnLmdldEFQSVBhdGgocHJvamVjdEVuZHBvaW50KSB9XG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICBvcHQudXJsICs9IFtvcHQuYWNjb3VudCwgb3B0LnByb2plY3RdLmpvaW4oJy8nKTtcbiAgICAgICAgICAgIHJldHVybiBodHRwLmdldChudWxsLCBvcHQpO1xuICAgICAgICB9XG5cbiAgICB9O1xuXG4gICAgJC5leHRlbmQodGhpcywgcHVibGljQVBJKTtcbn07XG4iLCIvKipcbiAqIEBjbGFzcyBDb29raWUgU3RvcmFnZSBTZXJ2aWNlXG4gKlxuICogQGV4YW1wbGVcbiAqICAgICAgdmFyIHBlb3BsZSA9IHJlcXVpcmUoJ2Nvb2tpZS1zdG9yZScpKHsgcm9vdDogJ3Blb3BsZScgfSk7XG4gICAgICAgIHBlb3BsZVxuICAgICAgICAgICAgLnNhdmUoe2xhc3ROYW1lOiAnc21pdGgnIH0pXG5cbiAqL1xuXG5cbid1c2Ugc3RyaWN0JztcblxuLy8gVGhpbiBkb2N1bWVudC5jb29raWUgd3JhcHBlciB0byBhbGxvdyB1bml0IHRlc3RpbmdcbnZhciBDb29raWUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5nZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBkb2N1bWVudC5jb29raWU7XG4gICAgfTtcblxuICAgIHRoaXMuc2V0ID0gZnVuY3Rpb24gKG5ld0Nvb2tpZSkge1xuICAgICAgICBkb2N1bWVudC5jb29raWUgPSBuZXdDb29raWU7XG4gICAgfTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuICAgIHZhciBob3N0ID0gd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lO1xuICAgIHZhciB2YWxpZEhvc3QgPSBob3N0LnNwbGl0KCcuJykubGVuZ3RoID4gMTtcbiAgICB2YXIgZG9tYWluID0gdmFsaWRIb3N0ID8gJy4nICsgaG9zdCA6IG51bGw7XG5cbiAgICB2YXIgZGVmYXVsdHMgPSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBOYW1lIG9mIGNvbGxlY3Rpb25cbiAgICAgICAgICogQHR5cGUgeyBzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICByb290OiAnLycsXG5cbiAgICAgICAgZG9tYWluOiBkb21haW4sXG4gICAgICAgIGNvb2tpZTogbmV3IENvb2tpZSgpXG4gICAgfTtcbiAgICB0aGlzLnNlcnZpY2VPcHRpb25zID0gJC5leHRlbmQoe30sIGRlZmF1bHRzLCBjb25maWcpO1xuXG4gICAgdmFyIHB1YmxpY0FQSSA9IHtcbiAgICAgICAgLy8gKiBUQkRcbiAgICAgICAgLy8gICogUXVlcnkgY29sbGVjdGlvbjsgdXNlcyBNb25nb0RCIHN5bnRheFxuICAgICAgICAvLyAgKiBAc2VlICA8VEJEOiBEYXRhIEFQSSBVUkw+XG4gICAgICAgIC8vICAqXG4gICAgICAgIC8vICAqIEBwYXJhbSB7IHN0cmluZ30gcXMgUXVlcnkgRmlsdGVyXG4gICAgICAgIC8vICAqIEBwYXJhbSB7IHN0cmluZ30gbGltaXRlcnMgQHNlZSA8VEJEOiB1cmwgZm9yIGxpbWl0cywgcGFnaW5nIGV0Yz5cbiAgICAgICAgLy8gICpcbiAgICAgICAgLy8gICogQGV4YW1wbGVcbiAgICAgICAgLy8gICogICAgIGNzLnF1ZXJ5KFxuICAgICAgICAvLyAgKiAgICAgIHsgbmFtZTogJ0pvaG4nLCBjbGFzc05hbWU6ICdDU0MxMDEnIH0sXG4gICAgICAgIC8vICAqICAgICAge2xpbWl0OiAxMH1cbiAgICAgICAgLy8gICogICAgIClcblxuICAgICAgICAvLyBxdWVyeTogZnVuY3Rpb24gKHFzLCBsaW1pdGVycykge1xuXG4gICAgICAgIC8vIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNhdmUgY29va2llIHZhbHVlXG4gICAgICAgICAqIEBwYXJhbSAgeyBzdHJpbmd8T2JqZWN0fSBrZXkgICBJZiBnaXZlbiBhIGtleSBzYXZlIHZhbHVlcyB1bmRlciBpdCwgaWYgZ2l2ZW4gYW4gb2JqZWN0IGRpcmVjdGx5LCBzYXZlIHRvIHRvcC1sZXZlbCBhcGlcbiAgICAgICAgICogQHBhcmFtICB7T2JqZWN0fSB2YWx1ZSAoT3B0aW9uYWwpXG4gICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIE92ZXJyaWRlcyBmb3Igc2VydmljZSBvcHRpb25zXG4gICAgICAgICAqXG4gICAgICAgICAqIEByZXR1cm4geyp9IFRoZSBzYXZlZCB2YWx1ZVxuICAgICAgICAgKlxuICAgICAgICAgKiBAZXhhbXBsZVxuICAgICAgICAgKiAgICAgY3Muc2V0KCdwZXJzb24nLCB7IGZpcnN0TmFtZTogJ2pvaG4nLCBsYXN0TmFtZTogJ3NtaXRoJyB9KTtcbiAgICAgICAgICogICAgIGNzLnNldCh7IG5hbWU6J3NtaXRoJywgYWdlOiczMicgfSk7XG4gICAgICAgICAqL1xuICAgICAgICBzZXQ6IGZ1bmN0aW9uIChrZXksIHZhbHVlLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgc2V0T3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCB0aGlzLnNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIGRvbWFpbiA9IHNldE9wdGlvbnMuZG9tYWluO1xuICAgICAgICAgICAgdmFyIHBhdGggPSBzZXRPcHRpb25zLnJvb3Q7XG4gICAgICAgICAgICB2YXIgY29va2llID0gc2V0T3B0aW9ucy5jb29raWU7XG5cbiAgICAgICAgICAgIGNvb2tpZS5zZXQoZW5jb2RlVVJJQ29tcG9uZW50KGtleSkgKyAnPScgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmNvZGVVUklDb21wb25lbnQodmFsdWUpICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGRvbWFpbiA/ICc7IGRvbWFpbj0nICsgZG9tYWluIDogJycpICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHBhdGggPyAnOyBwYXRoPScgKyBwYXRoIDogJycpXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIExvYWQgY29va2llIHZhbHVlXG4gICAgICAgICAqIEBwYXJhbSAgeyBzdHJpbmd8T2JqZWN0fSBrZXkgICBJZiBnaXZlbiBhIGtleSBzYXZlIHZhbHVlcyB1bmRlciBpdCwgaWYgZ2l2ZW4gYW4gb2JqZWN0IGRpcmVjdGx5LCBzYXZlIHRvIHRvcC1sZXZlbCBhcGlcbiAgICAgICAgICogQHJldHVybiB7Kn0gVGhlIHZhbHVlIHN0b3JlZFxuICAgICAgICAgKlxuICAgICAgICAgKiBAZXhhbXBsZVxuICAgICAgICAgKiAgICAgY3MuZ2V0KCdwZXJzb24nKTtcbiAgICAgICAgICovXG4gICAgICAgIGdldDogZnVuY3Rpb24gKGtleSkge1xuICAgICAgICAgICAgdmFyIGNvb2tpZSA9IHRoaXMuc2VydmljZU9wdGlvbnMuY29va2llO1xuICAgICAgICAgICAgdmFyIGNvb2tpZVJlZyA9IG5ldyBSZWdFeHAoJyg/Ol58OylcXFxccyonICsgZW5jb2RlVVJJQ29tcG9uZW50KGtleSkucmVwbGFjZSgvW1xcLVxcLlxcK1xcKl0vZywgJ1xcXFwkJicpICsgJ1xcXFxzKlxcXFw9XFxcXHMqKFteO10qKS4qJCcpO1xuICAgICAgICAgICAgdmFyIHJlcyA9IGNvb2tpZVJlZy5leGVjKGNvb2tpZS5nZXQoKSk7XG4gICAgICAgICAgICB2YXIgdmFsID0gcmVzID8gZGVjb2RlVVJJQ29tcG9uZW50KHJlc1sxXSkgOiBudWxsO1xuICAgICAgICAgICAgcmV0dXJuIHZhbDtcbiAgICAgICAgfSxcblxuICAgICAgICAvKipcbiAgICAgICAgICogUmVtb3ZlcyBrZXkgZnJvbSBjb2xsZWN0aW9uXG4gICAgICAgICAqIEBwYXJhbSB7IHN0cmluZ30ga2V5IGtleSB0byByZW1vdmVcbiAgICAgICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgKG9wdGlvbmFsKSBvdmVycmlkZXMgZm9yIHNlcnZpY2Ugb3B0aW9uc1xuICAgICAgICAgKiBAcmV0dXJuIHsgc3RyaW5nfSBrZXkgVGhlIGtleSByZW1vdmVkXG4gICAgICAgICAqXG4gICAgICAgICAqIEBleGFtcGxlXG4gICAgICAgICAqICAgICBjcy5yZW1vdmUoJ3BlcnNvbicpO1xuICAgICAgICAgKi9cbiAgICAgICAgcmVtb3ZlOiBmdW5jdGlvbiAoa2V5LCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgcmVtT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCB0aGlzLnNlcnZpY2VPcHRpb25zLCBvcHRpb25zKTtcblxuICAgICAgICAgICAgdmFyIGRvbWFpbiA9IHJlbU9wdGlvbnMuZG9tYWluO1xuICAgICAgICAgICAgdmFyIHBhdGggPSByZW1PcHRpb25zLnJvb3Q7XG4gICAgICAgICAgICB2YXIgY29va2llID0gcmVtT3B0aW9ucy5jb29raWU7XG5cbiAgICAgICAgICAgIGNvb2tpZS5zZXQoZW5jb2RlVVJJQ29tcG9uZW50KGtleSkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICc9OyBleHBpcmVzPVRodSwgMDEgSmFuIDE5NzAgMDA6MDA6MDAgR01UJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKGRvbWFpbiA/ICc7IGRvbWFpbj0nICsgZG9tYWluIDogJycpICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAocGF0aCA/ICc7IHBhdGg9JyArIHBhdGggOiAnJylcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm4ga2V5O1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZW1vdmVzIGNvbGxlY3Rpb24gYmVpbmcgcmVmZXJlbmNlZFxuICAgICAgICAgKiBAcmV0dXJuIHsgYXJyYXl9IGtleXMgQWxsIHRoZSBrZXlzIHJlbW92ZWRcbiAgICAgICAgICovXG4gICAgICAgIGRlc3Ryb3k6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBjb29raWUgPSB0aGlzLnNlcnZpY2VPcHRpb25zLmNvb2tpZTtcbiAgICAgICAgICAgIHZhciBhS2V5cyA9IGNvb2tpZS5nZXQoKS5yZXBsYWNlKC8oKD86XnxcXHMqOylbXlxcPV0rKSg/PTt8JCl8Xlxccyp8XFxzKig/OlxcPVteO10qKT8oPzpcXDF8JCkvZywgJycpLnNwbGl0KC9cXHMqKD86XFw9W147XSopPztcXHMqLyk7XG4gICAgICAgICAgICBmb3IgKHZhciBuSWR4ID0gMDsgbklkeCA8IGFLZXlzLmxlbmd0aDsgbklkeCsrKSB7XG4gICAgICAgICAgICAgICAgdmFyIGNvb2tpZUtleSA9IGRlY29kZVVSSUNvbXBvbmVudChhS2V5c1tuSWR4XSk7XG4gICAgICAgICAgICAgICAgdGhpcy5yZW1vdmUoY29va2llS2V5KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBhS2V5cztcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGtleU5hbWVzID0gcmVxdWlyZSgnLi4vbWFuYWdlcnMva2V5LW5hbWVzJyk7XG52YXIgU3RvcmFnZUZhY3RvcnkgPSByZXF1aXJlKCcuL3N0b3JlLWZhY3RvcnknKTtcbnZhciBvcHRpb25VdGlscyA9IHJlcXVpcmUoJy4uL3V0aWwvb3B0aW9uLXV0aWxzJyk7XG5cbnZhciBFUElfU0VTU0lPTl9LRVkgPSBrZXlOYW1lcy5FUElfU0VTU0lPTl9LRVk7XG52YXIgRVBJX01BTkFHRVJfS0VZID0gJ2VwaWNlbnRlci50b2tlbic7IC8vY2FuJ3QgYmUgdW5kZXIga2V5LW5hbWVzLCBvciBsb2dvdXQgd2lsbCBjbGVhciB0aGlzIHRvb1xuXG52YXIgZGVmYXVsdHMgPSB7XG4gICAgLyoqXG4gICAgICogV2hlcmUgdG8gc3RvcmUgdXNlciBhY2Nlc3MgdG9rZW5zIGZvciB0ZW1wb3JhcnkgYWNjZXNzLiBEZWZhdWx0cyB0byBzdG9yaW5nIGluIGEgY29va2llIGluIHRoZSBicm93c2VyLlxuICAgICAqIEB0eXBlIHtzdHJpbmd9XG4gICAgICovXG4gICAgc3RvcmU6IHsgc3luY2hyb25vdXM6IHRydWUgfVxufTtcblxudmFyIFNlc3Npb25NYW5hZ2VyID0gZnVuY3Rpb24gKG1hbmFnZXJPcHRpb25zKSB7XG4gICAgbWFuYWdlck9wdGlvbnMgPSBtYW5hZ2VyT3B0aW9ucyB8fCB7fTtcbiAgICBmdW5jdGlvbiBnZXRCYXNlT3B0aW9ucyhvdmVycmlkZXMpIHtcbiAgICAgICAgb3ZlcnJpZGVzID0gb3ZlcnJpZGVzIHx8IHt9O1xuICAgICAgICB2YXIgbGliT3B0aW9ucyA9IG9wdGlvblV0aWxzLmdldE9wdGlvbnMoKTtcbiAgICAgICAgdmFyIGZpbmFsT3B0aW9ucyA9ICQuZXh0ZW5kKHRydWUsIHt9LCBkZWZhdWx0cywgbGliT3B0aW9ucywgbWFuYWdlck9wdGlvbnMsIG92ZXJyaWRlcyk7XG4gICAgICAgIHJldHVybiBmaW5hbE9wdGlvbnM7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZ2V0U3RvcmUob3ZlcnJpZGVzKSB7XG4gICAgICAgIHZhciBiYXNlT3B0aW9ucyA9IGdldEJhc2VPcHRpb25zKG92ZXJyaWRlcyk7XG4gICAgICAgIHZhciBzdG9yZU9wdHMgPSBiYXNlT3B0aW9ucy5zdG9yZSB8fCB7fTtcbiAgICAgICAgdmFyIGlzRXBpY2VudGVyRG9tYWluID0gIWJhc2VPcHRpb25zLmlzTG9jYWwgJiYgIWJhc2VPcHRpb25zLmlzQ3VzdG9tRG9tYWluO1xuICAgICAgICBpZiAoc3RvcmVPcHRzLnJvb3QgPT09IHVuZGVmaW5lZCAmJiBiYXNlT3B0aW9ucy5hY2NvdW50ICYmIGJhc2VPcHRpb25zLnByb2plY3QgJiYgaXNFcGljZW50ZXJEb21haW4pIHtcbiAgICAgICAgICAgIHN0b3JlT3B0cy5yb290ID0gJy9hcHAvJyArIGJhc2VPcHRpb25zLmFjY291bnQgKyAnLycgKyBiYXNlT3B0aW9ucy5wcm9qZWN0O1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuZXcgU3RvcmFnZUZhY3Rvcnkoc3RvcmVPcHRzKTtcbiAgICB9XG5cbiAgICB2YXIgcHVibGljQVBJID0ge1xuICAgICAgICBzYXZlU2Vzc2lvbjogZnVuY3Rpb24gKHVzZXJJbmZvLCBvcHRpb25zKSB7XG4gICAgICAgICAgICB2YXIgc2VyaWFsaXplZCA9IEpTT04uc3RyaW5naWZ5KHVzZXJJbmZvKTtcbiAgICAgICAgICAgIGdldFN0b3JlKG9wdGlvbnMpLnNldChFUElfU0VTU0lPTl9LRVksIHNlcmlhbGl6ZWQpO1xuICAgICAgICB9LFxuICAgICAgICBnZXRTZXNzaW9uOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICAgICAgLy8gdmFyIHNlc3Npb24gPSBnZXRTdG9yZShvcHRpb25zKS5nZXQoRVBJX1NFU1NJT05fS0VZKSB8fCAne30nO1xuICAgICAgICAgICAgLy8gcmV0dXJuIEpTT04ucGFyc2Uoc2Vzc2lvbik7XG4gICAgICAgICAgICB2YXIgc3RvcmUgPSBnZXRTdG9yZShvcHRpb25zKTtcbiAgICAgICAgICAgIHZhciBmaW5hbE9wdHMgPSBzdG9yZS5zZXJ2aWNlT3B0aW9ucztcbiAgICAgICAgICAgIHZhciBzZXJpYWxpemVkID0gc3RvcmUuZ2V0KEVQSV9TRVNTSU9OX0tFWSkgfHwgJ3t9JztcbiAgICAgICAgICAgIHZhciBzZXNzaW9uID0gSlNPTi5wYXJzZShzZXJpYWxpemVkKTtcbiAgICAgICAgICAgIC8vIElmIHRoZSB1cmwgY29udGFpbnMgdGhlIHByb2plY3QgYW5kIGFjY291bnRcbiAgICAgICAgICAgIC8vIHZhbGlkYXRlIHRoZSBhY2NvdW50IGFuZCBwcm9qZWN0IGluIHRoZSBzZXNzaW9uXG4gICAgICAgICAgICAvLyBhbmQgb3ZlcnJpZGUgcHJvamVjdCwgZ3JvdXBOYW1lLCBncm91cElkIGFuZCBpc0ZhY1xuICAgICAgICAgICAgLy8gT3RoZXJ3aXNlIChpLmUuIGxvY2FsaG9zdCkgdXNlIHRoZSBzYXZlZCBzZXNzaW9uIHZhbHVlc1xuICAgICAgICAgICAgdmFyIGFjY291bnQgPSBmaW5hbE9wdHMuYWNjb3VudDtcbiAgICAgICAgICAgIHZhciBwcm9qZWN0ID0gZmluYWxPcHRzLnByb2plY3Q7XG4gICAgICAgICAgICBpZiAoYWNjb3VudCAmJiBzZXNzaW9uLmFjY291bnQgIT09IGFjY291bnQpIHtcbiAgICAgICAgICAgICAgICAvLyBUaGlzIG1lYW5zIHRoYXQgdGhlIHRva2VuIHdhcyBub3QgdXNlZCB0byBsb2dpbiB0byB0aGUgc2FtZSBhY2NvdW50XG4gICAgICAgICAgICAgICAgcmV0dXJuIHt9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHNlc3Npb24uZ3JvdXBzICYmIGFjY291bnQgJiYgcHJvamVjdCkge1xuICAgICAgICAgICAgICAgIHZhciBncm91cCA9IHNlc3Npb24uZ3JvdXBzW3Byb2plY3RdIHx8IHsgZ3JvdXBJZDogJycsIGdyb3VwTmFtZTogJycsIGlzRmFjOiBmYWxzZSB9O1xuICAgICAgICAgICAgICAgICQuZXh0ZW5kKHNlc3Npb24sIHsgcHJvamVjdDogcHJvamVjdCB9LCBncm91cCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gc2Vzc2lvbjtcbiAgICAgICAgfSxcbiAgICAgICAgcmVtb3ZlU2Vzc2lvbjogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHZhciBzdG9yZSA9IGdldFN0b3JlKG9wdGlvbnMpO1xuICAgICAgICAgICAgT2JqZWN0LmtleXMoa2V5TmFtZXMpLmZvckVhY2goZnVuY3Rpb24gKGNvb2tpZUtleSkge1xuICAgICAgICAgICAgICAgIHZhciBjb29raWVOYW1lID0ga2V5TmFtZXNbY29va2llS2V5XTtcbiAgICAgICAgICAgICAgICBzdG9yZS5yZW1vdmUoY29va2llTmFtZSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBnZXRTdG9yZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICAgICAgICAgIHJldHVybiBnZXRTdG9yZShvcHRpb25zKTtcbiAgICAgICAgfSxcblxuICAgICAgICBnZXRNZXJnZWRPcHRpb25zOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB2YXIgb3ZlcnJpZGVzID0gJC5leHRlbmQuYXBwbHkoJCwgW3RydWUsIHt9XS5jb25jYXQoYXJncykpO1xuICAgICAgICAgICAgdmFyIGJhc2VPcHRpb25zID0gZ2V0QmFzZU9wdGlvbnMob3ZlcnJpZGVzKTtcbiAgICAgICAgICAgIHZhciBzZXNzaW9uID0gdGhpcy5nZXRTZXNzaW9uKG92ZXJyaWRlcyk7XG5cbiAgICAgICAgICAgIHZhciB0b2tlbiA9IHNlc3Npb24uYXV0aF90b2tlbjtcbiAgICAgICAgICAgIGlmICghdG9rZW4pIHtcbiAgICAgICAgICAgICAgICB2YXIgZmFjdG9yeSA9IG5ldyBTdG9yYWdlRmFjdG9yeSgpO1xuICAgICAgICAgICAgICAgIHRva2VuID0gZmFjdG9yeS5nZXQoRVBJX01BTkFHRVJfS0VZKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHNlc3Npb25EZWZhdWx0cyA9IHtcbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBGb3IgcHJvamVjdHMgdGhhdCByZXF1aXJlIGF1dGhlbnRpY2F0aW9uLCBwYXNzIGluIHRoZSB1c2VyIGFjY2VzcyB0b2tlbiAoZGVmYXVsdHMgdG8gZW1wdHkgc3RyaW5nKS4gSWYgdGhlIHVzZXIgaXMgYWxyZWFkeSBsb2dnZWQgaW4gdG8gRXBpY2VudGVyLCB0aGUgdXNlciBhY2Nlc3MgdG9rZW4gaXMgYWxyZWFkeSBzZXQgaW4gYSBjb29raWUgYW5kIGF1dG9tYXRpY2FsbHkgbG9hZGVkIGZyb20gdGhlcmUuIChTZWUgW21vcmUgYmFja2dyb3VuZCBvbiBhY2Nlc3MgdG9rZW5zXSguLi8uLi8uLi9wcm9qZWN0X2FjY2Vzcy8pKS5cbiAgICAgICAgICAgICAgICAgKiBAc2VlIFtBdXRoZW50aWNhdGlvbiBBUEkgU2VydmljZV0oLi4vYXV0aC1hcGktc2VydmljZS8pIGZvciBnZXR0aW5nIHRva2Vucy5cbiAgICAgICAgICAgICAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIHRva2VuOiB0b2tlbixcblxuICAgICAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICAgICAqIFRoZSBhY2NvdW50LiBJZiBsZWZ0IHVuZGVmaW5lZCwgdGFrZW4gZnJvbSB0aGUgY29va2llIHNlc3Npb24uXG4gICAgICAgICAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICAgICAgICAgKi9cbiAgICAgICAgICAgICAgICBhY2NvdW50OiBzZXNzaW9uLmFjY291bnQsXG5cbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBUaGUgcHJvamVjdC4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIGNvb2tpZSBzZXNzaW9uLlxuICAgICAgICAgICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgcHJvamVjdDogc2Vzc2lvbi5wcm9qZWN0LFxuXG5cbiAgICAgICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAgICAgKiBUaGUgZ3JvdXAgbmFtZS4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIGNvb2tpZSBzZXNzaW9uLlxuICAgICAgICAgICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgZ3JvdXA6IHNlc3Npb24uZ3JvdXBOYW1lLFxuICAgICAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICAgICAqIEFsaWFzIGZvciBncm91cC4gXG4gICAgICAgICAgICAgICAgICogQHR5cGUge1N0cmluZ31cbiAgICAgICAgICAgICAgICAgKi9cbiAgICAgICAgICAgICAgICBncm91cE5hbWU6IHNlc3Npb24uZ3JvdXBOYW1lLCAvL0l0J3MgYSBsaXR0bGUgd2VpcmQgdGhhdCBpdCdzIGNhbGxlZCBncm91cE5hbWUgaW4gdGhlIGNvb2tpZSwgYnV0ICdncm91cCcgaW4gYWxsIHRoZSBzZXJ2aWNlIG9wdGlvbnMsIHNvIG5vcm1hbGl6ZSBmb3IgYm90aFxuICAgICAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICAgICAqIFRoZSBncm91cCBpZC4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIGNvb2tpZSBzZXNzaW9uLlxuICAgICAgICAgICAgICAgICAqIEB0eXBlIHtTdHJpbmd9XG4gICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgZ3JvdXBJZDogc2Vzc2lvbi5ncm91cElkLFxuICAgICAgICAgICAgICAgIHVzZXJJZDogc2Vzc2lvbi51c2VySWRcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXR1cm4gJC5leHRlbmQodHJ1ZSwgc2Vzc2lvbkRlZmF1bHRzLCBiYXNlT3B0aW9ucyk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgICQuZXh0ZW5kKHRoaXMsIHB1YmxpY0FQSSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNlc3Npb25NYW5hZ2VyOyIsIi8qKlxuICAgIERlY2lkZXMgdHlwZSBvZiBzdG9yZSB0byBwcm92aWRlXG4qL1xuXG4ndXNlIHN0cmljdCc7XG4vLyB2YXIgaXNOb2RlID0gZmFsc2U7IEZJWE1FOiBCcm93c2VyaWZ5L21pbmlmeWlmeSBoYXMgaXNzdWVzIHdpdGggdGhlIG5leHQgbGlua1xuLy8gdmFyIHN0b3JlID0gKGlzTm9kZSkgPyByZXF1aXJlKCcuL3Nlc3Npb24tc3RvcmUnKSA6IHJlcXVpcmUoJy4vY29va2llLXN0b3JlJyk7XG52YXIgc3RvcmUgPSByZXF1aXJlKCcuL2Nvb2tpZS1zdG9yZScpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHN0b3JlO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgcXV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbC9xdWVyeS11dGlsJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGNvbmZpZykge1xuXG4gICAgdmFyIGRlZmF1bHRzID0ge1xuICAgICAgICB1cmw6ICcnLFxuXG4gICAgICAgIGNvbnRlbnRUeXBlOiAnYXBwbGljYXRpb24vanNvbicsXG4gICAgICAgIGhlYWRlcnM6IHt9LFxuICAgICAgICBzdGF0dXNDb2RlOiB7XG4gICAgICAgICAgICA0MDQ6ICQubm9vcFxuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBPTkxZIGZvciBzdHJpbmdzIGluIHRoZSB1cmwuIEFsbCBHRVQgJiBERUxFVEUgcGFyYW1zIGFyZSBydW4gdGhyb3VnaCB0aGlzXG4gICAgICAgICAqIEB0eXBlIHtbdHlwZV0gfVxuICAgICAgICAgKi9cbiAgICAgICAgcGFyYW1ldGVyUGFyc2VyOiBxdXRpbHMudG9RdWVyeUZvcm1hdCxcblxuICAgICAgICAvLyBUbyBhbGxvdyBlcGljZW50ZXIudG9rZW4gYW5kIG90aGVyIHNlc3Npb24gY29va2llcyB0byBiZSBwYXNzZWRcbiAgICAgICAgLy8gd2l0aCB0aGUgcmVxdWVzdHNcbiAgICAgICAgeGhyRmllbGRzOiB7XG4gICAgICAgICAgICB3aXRoQ3JlZGVudGlhbHM6IHRydWVcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICB2YXIgdHJhbnNwb3J0T3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCBkZWZhdWx0cywgY29uZmlnKTtcblxuICAgIHZhciByZXN1bHQgPSBmdW5jdGlvbiAoZCkge1xuICAgICAgICByZXR1cm4gKCQuaXNGdW5jdGlvbihkKSkgPyBkKCkgOiBkO1xuICAgIH07XG5cbiAgICB2YXIgY29ubmVjdCA9IGZ1bmN0aW9uIChtZXRob2QsIHBhcmFtcywgY29ubmVjdE9wdGlvbnMpIHtcbiAgICAgICAgcGFyYW1zID0gcmVzdWx0KHBhcmFtcyk7XG4gICAgICAgIHBhcmFtcyA9ICgkLmlzUGxhaW5PYmplY3QocGFyYW1zKSB8fCAkLmlzQXJyYXkocGFyYW1zKSkgPyBKU09OLnN0cmluZ2lmeShwYXJhbXMpIDogcGFyYW1zO1xuXG4gICAgICAgIHZhciBvcHRpb25zID0gJC5leHRlbmQodHJ1ZSwge30sIHRyYW5zcG9ydE9wdGlvbnMsIGNvbm5lY3RPcHRpb25zLCB7XG4gICAgICAgICAgICB0eXBlOiBtZXRob2QsXG4gICAgICAgICAgICBkYXRhOiBwYXJhbXNcbiAgICAgICAgfSk7XG4gICAgICAgIHZhciBBTExPV0VEX1RPX0JFX0ZVTkNUSU9OUyA9IFsnZGF0YScsICd1cmwnXTtcbiAgICAgICAgJC5lYWNoKG9wdGlvbnMsIGZ1bmN0aW9uIChrZXksIHZhbHVlKSB7XG4gICAgICAgICAgICBpZiAoJC5pc0Z1bmN0aW9uKHZhbHVlKSAmJiAkLmluQXJyYXkoa2V5LCBBTExPV0VEX1RPX0JFX0ZVTkNUSU9OUykgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgb3B0aW9uc1trZXldID0gdmFsdWUoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKG9wdGlvbnMubG9nTGV2ZWwgJiYgb3B0aW9ucy5sb2dMZXZlbCA9PT0gJ0RFQlVHJykge1xuICAgICAgICAgICAgY29uc29sZS5sb2cob3B0aW9ucy51cmwpO1xuICAgICAgICAgICAgdmFyIG9sZFN1Y2Nlc3NGbiA9IG9wdGlvbnMuc3VjY2VzcyB8fCAkLm5vb3A7XG4gICAgICAgICAgICBvcHRpb25zLnN1Y2Nlc3MgPSBmdW5jdGlvbiAocmVzcG9uc2UsIGFqYXhTdGF0dXMsIGFqYXhSZXEpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhyZXNwb25zZSk7XG4gICAgICAgICAgICAgICAgb2xkU3VjY2Vzc0ZuLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGJlZm9yZVNlbmQgPSBvcHRpb25zLmJlZm9yZVNlbmQ7XG4gICAgICAgIG9wdGlvbnMuYmVmb3JlU2VuZCA9IGZ1bmN0aW9uICh4aHIsIHNldHRpbmdzKSB7XG4gICAgICAgICAgICB4aHIucmVxdWVzdFVybCA9IChjb25uZWN0T3B0aW9ucyB8fCB7fSkudXJsO1xuICAgICAgICAgICAgaWYgKGJlZm9yZVNlbmQpIHtcbiAgICAgICAgICAgICAgICBiZWZvcmVTZW5kLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgcmV0dXJuICQuYWpheChvcHRpb25zKTtcbiAgICB9O1xuXG4gICAgdmFyIHB1YmxpY0FQSSA9IHtcbiAgICAgICAgZ2V0OiBmdW5jdGlvbiAocGFyYW1zLCBhamF4T3B0aW9ucykge1xuICAgICAgICAgICAgdmFyIG9wdGlvbnMgPSAkLmV4dGVuZCh7fSwgdHJhbnNwb3J0T3B0aW9ucywgYWpheE9wdGlvbnMpO1xuICAgICAgICAgICAgcGFyYW1zID0gb3B0aW9ucy5wYXJhbWV0ZXJQYXJzZXIocmVzdWx0KHBhcmFtcykpO1xuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3QuY2FsbCh0aGlzLCAnR0VUJywgcGFyYW1zLCBvcHRpb25zKTtcbiAgICAgICAgfSxcbiAgICAgICAgc3BsaXRHZXQ6IGZ1bmN0aW9uICgpIHtcblxuICAgICAgICB9LFxuICAgICAgICBwb3N0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gY29ubmVjdC5hcHBseSh0aGlzLCBbJ3Bvc3QnXS5jb25jYXQoW10uc2xpY2UuY2FsbChhcmd1bWVudHMpKSk7XG4gICAgICAgIH0sXG4gICAgICAgIHBhdGNoOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gY29ubmVjdC5hcHBseSh0aGlzLCBbJ3BhdGNoJ10uY29uY2F0KFtdLnNsaWNlLmNhbGwoYXJndW1lbnRzKSkpO1xuICAgICAgICB9LFxuICAgICAgICBwdXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiBjb25uZWN0LmFwcGx5KHRoaXMsIFsncHV0J10uY29uY2F0KFtdLnNsaWNlLmNhbGwoYXJndW1lbnRzKSkpO1xuICAgICAgICB9LFxuICAgICAgICBkZWxldGU6IGZ1bmN0aW9uIChwYXJhbXMsIGFqYXhPcHRpb25zKSB7XG4gICAgICAgICAgICAvL0RFTEVURSBkb2Vzbid0IHN1cHBvcnQgYm9keSBwYXJhbXMsIGJ1dCBqUXVlcnkgdGhpbmtzIGl0IGRvZXMuXG4gICAgICAgICAgICB2YXIgb3B0aW9ucyA9ICQuZXh0ZW5kKHt9LCB0cmFuc3BvcnRPcHRpb25zLCBhamF4T3B0aW9ucyk7XG4gICAgICAgICAgICBwYXJhbXMgPSBvcHRpb25zLnBhcmFtZXRlclBhcnNlcihyZXN1bHQocGFyYW1zKSk7XG4gICAgICAgICAgICBpZiAoJC50cmltKHBhcmFtcykpIHtcbiAgICAgICAgICAgICAgICB2YXIgZGVsaW1pdGVyID0gKHJlc3VsdChvcHRpb25zLnVybCkuaW5kZXhPZignPycpID09PSAtMSkgPyAnPycgOiAnJic7XG4gICAgICAgICAgICAgICAgb3B0aW9ucy51cmwgPSByZXN1bHQob3B0aW9ucy51cmwpICsgZGVsaW1pdGVyICsgcGFyYW1zO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3QuY2FsbCh0aGlzLCAnREVMRVRFJywgbnVsbCwgb3B0aW9ucyk7XG4gICAgICAgIH0sXG4gICAgICAgIGhlYWQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiBjb25uZWN0LmFwcGx5KHRoaXMsIFsnaGVhZCddLmNvbmNhdChbXS5zbGljZS5jYWxsKGFyZ3VtZW50cykpKTtcbiAgICAgICAgfSxcbiAgICAgICAgb3B0aW9uczogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3QuYXBwbHkodGhpcywgWydvcHRpb25zJ10uY29uY2F0KFtdLnNsaWNlLmNhbGwoYXJndW1lbnRzKSkpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHJldHVybiAkLmV4dGVuZCh0aGlzLCBwdWJsaWNBUEkpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxuLy8gdmFyIGlzTm9kZSA9IGZhbHNlOyBGSVhNRTogQnJvd3NlcmlmeS9taW5pZnlpZnkgaGFzIGlzc3VlcyB3aXRoIHRoZSBuZXh0IGxpbmtcbi8vIHZhciB0cmFuc3BvcnQgPSAoaXNOb2RlKSA/IHJlcXVpcmUoJy4vbm9kZS1odHRwLXRyYW5zcG9ydCcpIDogcmVxdWlyZSgnLi9hamF4LWh0dHAtdHJhbnNwb3J0Jyk7XG52YXIgdHJhbnNwb3J0ID0gcmVxdWlyZSgnLi9hamF4LWh0dHAtdHJhbnNwb3J0Jyk7XG5tb2R1bGUuZXhwb3J0cyA9IHRyYW5zcG9ydDtcbiIsIi8qKlxuLyogSW5oZXJpdCBmcm9tIGEgY2xhc3MgKHVzaW5nIHByb3RvdHlwZSBib3Jyb3dpbmcpXG4qL1xuJ3VzZSBzdHJpY3QnO1xuXG5mdW5jdGlvbiBpbmhlcml0KEMsIFApIHtcbiAgICB2YXIgRiA9IGZ1bmN0aW9uICgpIHt9O1xuICAgIEYucHJvdG90eXBlID0gUC5wcm90b3R5cGU7XG4gICAgQy5wcm90b3R5cGUgPSBuZXcgRigpO1xuICAgIEMuX19zdXBlciA9IFAucHJvdG90eXBlO1xuICAgIEMucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gQztcbn1cblxuLyoqXG4qIFNoYWxsb3cgY29weSBvZiBhbiBvYmplY3RcbiogQHBhcmFtIHtPYmplY3R9IGRlc3Qgb2JqZWN0IHRvIGV4dGVuZFxuKiBAcmV0dXJuIHtPYmplY3R9IGV4dGVuZGVkIG9iamVjdFxuKi9cbnZhciBleHRlbmQgPSBmdW5jdGlvbiAoZGVzdCAvKiwgdmFyX2FyZ3MqLykge1xuICAgIHZhciBvYmogPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMsIDEpO1xuICAgIHZhciBjdXJyZW50O1xuICAgIGZvciAodmFyIGogPSAwOyBqIDwgb2JqLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIGlmICghKGN1cnJlbnQgPSBvYmpbal0pKSB7IC8vZXNsaW50LWRpc2FibGUtbGluZVxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBkbyBub3Qgd3JhcCBpbm5lciBpbiBkZXN0Lmhhc093blByb3BlcnR5IG9yIGJhZCB0aGluZ3Mgd2lsbCBoYXBwZW5cbiAgICAgICAgZm9yICh2YXIga2V5IGluIGN1cnJlbnQpIHsgLy9lc2xpbnQtZGlzYWJsZS1saW5lXG4gICAgICAgICAgICBkZXN0W2tleV0gPSBjdXJyZW50W2tleV07XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZGVzdDtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGJhc2UsIHByb3BzLCBzdGF0aWNQcm9wcykge1xuICAgIHZhciBwYXJlbnQgPSBiYXNlO1xuICAgIHZhciBjaGlsZDtcblxuICAgIGNoaWxkID0gcHJvcHMgJiYgcHJvcHMuaGFzT3duUHJvcGVydHkoJ2NvbnN0cnVjdG9yJykgPyBwcm9wcy5jb25zdHJ1Y3RvciA6IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHBhcmVudC5hcHBseSh0aGlzLCBhcmd1bWVudHMpOyB9O1xuXG4gICAgLy8gYWRkIHN0YXRpYyBwcm9wZXJ0aWVzIHRvIHRoZSBjaGlsZCBjb25zdHJ1Y3RvciBmdW5jdGlvblxuICAgIGV4dGVuZChjaGlsZCwgcGFyZW50LCBzdGF0aWNQcm9wcyk7XG5cbiAgICAvLyBhc3NvY2lhdGUgcHJvdG90eXBlIGNoYWluXG4gICAgaW5oZXJpdChjaGlsZCwgcGFyZW50KTtcblxuICAgIC8vIGFkZCBpbnN0YW5jZSBwcm9wZXJ0aWVzXG4gICAgaWYgKHByb3BzKSB7XG4gICAgICAgIGV4dGVuZChjaGlsZC5wcm90b3R5cGUsIHByb3BzKTtcbiAgICB9XG5cbiAgICAvLyBkb25lXG4gICAgcmV0dXJuIGNoaWxkO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgX3BpY2s6IGZ1bmN0aW9uIChvYmosIHByb3BzKSB7XG4gICAgICAgIHZhciByZXMgPSB7fTtcbiAgICAgICAgZm9yICh2YXIgcCBpbiBvYmopIHtcbiAgICAgICAgICAgIGlmIChwcm9wcy5pbmRleE9mKHApICE9PSAtMSkge1xuICAgICAgICAgICAgICAgIHJlc1twXSA9IG9ialtwXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiByZXM7XG4gICAgfVxufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIENvbmZpZ1NlcnZpY2UgPSByZXF1aXJlKCcuLi9zZXJ2aWNlL2NvbmZpZ3VyYXRpb24tc2VydmljZScpO1xuXG52YXIgdXJsQ29uZmlnID0gbmV3IENvbmZpZ1NlcnZpY2UoKS5nZXQoJ3NlcnZlcicpO1xudmFyIGN1c3RvbURlZmF1bHRzID0ge307XG52YXIgbGliRGVmYXVsdHMgPSB7XG4gICAgLyoqXG4gICAgICogVGhlIGFjY291bnQgaWQuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykgb3IgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAqL1xuICAgIGFjY291bnQ6IHVybENvbmZpZy5hY2NvdW50UGF0aCB8fCB1bmRlZmluZWQsXG4gICAgLyoqXG4gICAgICogVGhlIGFjY291bnQgaWQuIEluIHRoZSBFcGljZW50ZXIgVUksIHRoaXMgaXMgdGhlICoqVGVhbSBJRCoqIChmb3IgdGVhbSBwcm9qZWN0cykgb3IgKipVc2VyIElEKiogKGZvciBwZXJzb25hbCBwcm9qZWN0cykuIERlZmF1bHRzIHRvIGVtcHR5IHN0cmluZy4gSWYgbGVmdCB1bmRlZmluZWQsIHRha2VuIGZyb20gdGhlIFVSTC5cbiAgICAgKiBAdHlwZSB7U3RyaW5nfVxuICAgICAqL1xuICAgIHByb2plY3Q6IHVybENvbmZpZy5wcm9qZWN0UGF0aCB8fCB1bmRlZmluZWQsXG4gICAgaXNMb2NhbDogdXJsQ29uZmlnLmlzTG9jYWxob3N0KCksXG4gICAgaXNDdXN0b21Eb21haW46IHVybENvbmZpZy5pc0N1c3RvbURvbWFpbixcbiAgICBzdG9yZToge31cbn07XG5cbnZhciBvcHRpb25VdGlscyA9IHtcbiAgICAvKipcbiAgICAgKiBHZXRzIHRoZSBmaW5hbCBvcHRpb25zIGJ5IG92ZXJyaWRpbmcgdGhlIGdsb2JhbCBvcHRpb25zIHNldCB3aXRoXG4gICAgICogb3B0aW9uVXRpbHMjc2V0RGVmYXVsdHMoKSBhbmQgdGhlIGxpYiBkZWZhdWx0cy5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyBUaGUgZmluYWwgb3B0aW9ucyBvYmplY3QuXG4gICAgICogQHJldHVybiB7b2JqZWN0fSBFeHRlbmRlZCBvYmplY3RcbiAgICAgKi9cbiAgICBnZXRPcHRpb25zOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgICByZXR1cm4gJC5leHRlbmQodHJ1ZSwge30sIGxpYkRlZmF1bHRzLCBjdXN0b21EZWZhdWx0cywgb3B0aW9ucyk7XG4gICAgfSxcbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBnbG9iYWwgZGVmYXVsdHMgZm9yIHRoZSBvcHRpb25VdGlscyNnZXRPcHRpb25zKCkgbWV0aG9kLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBkZWZhdWx0cyBUaGUgZGVmYXVsdHMgb2JqZWN0LlxuICAgICAqL1xuICAgIHNldERlZmF1bHRzOiBmdW5jdGlvbiAoZGVmYXVsdHMpIHtcbiAgICAgICAgY3VzdG9tRGVmYXVsdHMgPSBkZWZhdWx0cztcbiAgICB9XG59O1xubW9kdWxlLmV4cG9ydHMgPSBvcHRpb25VdGlscztcbiIsIi8qKlxuICogVXRpbGl0aWVzIGZvciB3b3JraW5nIHdpdGggcXVlcnkgc3RyaW5nc1xuKi9cbid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSAoZnVuY3Rpb24gKCkge1xuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIENvbnZlcnRzIHRvIG1hdHJpeCBmb3JtYXRcbiAgICAgICAgICogQHBhcmFtICB7T2JqZWN0fSBxcyBPYmplY3QgdG8gY29udmVydCB0byBxdWVyeSBzdHJpbmdcbiAgICAgICAgICogQHJldHVybiB7IHN0cmluZ30gICAgTWF0cml4LWZvcm1hdCBxdWVyeSBwYXJhbWV0ZXJzXG4gICAgICAgICAqL1xuICAgICAgICB0b01hdHJpeEZvcm1hdDogZnVuY3Rpb24gKHFzKSB7XG4gICAgICAgICAgICBpZiAocXMgPT09IG51bGwgfHwgcXMgPT09IHVuZGVmaW5lZCB8fCBxcyA9PT0gJycpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gJzsnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHR5cGVvZiBxcyA9PT0gJ3N0cmluZycgfHwgcXMgaW5zdGFuY2VvZiBTdHJpbmcpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcXM7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciByZXR1cm5BcnJheSA9IFtdO1xuICAgICAgICAgICAgdmFyIE9QRVJBVE9SUyA9IFsnPCcsICc+JywgJyEnXTtcbiAgICAgICAgICAgICQuZWFjaChxcywgZnVuY3Rpb24gKGtleSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlICE9PSAnc3RyaW5nJyB8fCAkLmluQXJyYXkoJC50cmltKHZhbHVlKS5jaGFyQXQoMCksIE9QRVJBVE9SUykgPT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gJz0nICsgdmFsdWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybkFycmF5LnB1c2goa2V5ICsgdmFsdWUpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHZhciBtdHJ4ID0gJzsnICsgcmV0dXJuQXJyYXkuam9pbignOycpO1xuICAgICAgICAgICAgcmV0dXJuIG10cng7XG4gICAgICAgIH0sXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIENvbnZlcnRzIHN0cmluZ3MvYXJyYXlzL29iamVjdHMgdG8gdHlwZSAnYT1iJmI9YydcbiAgICAgICAgICogQHBhcmFtICB7IHN0cmluZ3xBcnJheXxPYmplY3R9IHFzXG4gICAgICAgICAqIEByZXR1cm4geyBzdHJpbmd9XG4gICAgICAgICAqL1xuICAgICAgICB0b1F1ZXJ5Rm9ybWF0OiBmdW5jdGlvbiAocXMpIHtcbiAgICAgICAgICAgIGlmIChxcyA9PT0gbnVsbCB8fCBxcyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuICcnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHR5cGVvZiBxcyA9PT0gJ3N0cmluZycgfHwgcXMgaW5zdGFuY2VvZiBTdHJpbmcpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcXM7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciByZXR1cm5BcnJheSA9IFtdO1xuICAgICAgICAgICAgJC5lYWNoKHFzLCBmdW5jdGlvbiAoa2V5LCB2YWx1ZSkge1xuICAgICAgICAgICAgICAgIGlmICgkLmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlID0gdmFsdWUuam9pbignLCcpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoJC5pc1BsYWluT2JqZWN0KHZhbHVlKSkge1xuICAgICAgICAgICAgICAgICAgICAvL01vc3RseSBmb3IgZGF0YSBhcGlcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBKU09OLnN0cmluZ2lmeSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybkFycmF5LnB1c2goa2V5ICsgJz0nICsgdmFsdWUpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHZhciByZXN1bHQgPSByZXR1cm5BcnJheS5qb2luKCcmJyk7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDb252ZXJ0cyBzdHJpbmdzIG9mIHR5cGUgJ2E9YiZiPWMnIHRvIHsgYTpiLCBiOmN9XG4gICAgICAgICAqIEBwYXJhbSAgeyBzdHJpbmd9IHFzXG4gICAgICAgICAqIEByZXR1cm4ge29iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIHFzVG9PYmplY3Q6IGZ1bmN0aW9uIChxcykge1xuICAgICAgICAgICAgaWYgKHFzID09PSBudWxsIHx8IHFzID09PSB1bmRlZmluZWQgfHwgcXMgPT09ICcnKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHt9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcXNBcnJheSA9IHFzLnNwbGl0KCcmJyk7XG4gICAgICAgICAgICB2YXIgcmV0dXJuT2JqID0ge307XG4gICAgICAgICAgICAkLmVhY2gocXNBcnJheSwgZnVuY3Rpb24gKGluZGV4LCB2YWx1ZSkge1xuICAgICAgICAgICAgICAgIHZhciBxS2V5ID0gdmFsdWUuc3BsaXQoJz0nKVswXTtcbiAgICAgICAgICAgICAgICB2YXIgcVZhbCA9IHZhbHVlLnNwbGl0KCc9JylbMV07XG5cbiAgICAgICAgICAgICAgICBpZiAocVZhbC5pbmRleE9mKCcsJykgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIHFWYWwgPSBxVmFsLnNwbGl0KCcsJyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuT2JqW3FLZXldID0gcVZhbDtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gcmV0dXJuT2JqO1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBOb3JtYWxpemVzIGFuZCBtZXJnZXMgc3RyaW5ncyBvZiB0eXBlICdhPWInLCB7IGI6Y30gdG8geyBhOmIsIGI6Y31cbiAgICAgICAgICogQHBhcmFtICB7IHN0cmluZ3xBcnJheXxPYmplY3R9IHFzMVxuICAgICAgICAgKiBAcGFyYW0gIHsgc3RyaW5nfEFycmF5fE9iamVjdH0gcXMyXG4gICAgICAgICAqIEByZXR1cm4ge09iamVjdH1cbiAgICAgICAgICovXG4gICAgICAgIG1lcmdlUVM6IGZ1bmN0aW9uIChxczEsIHFzMikge1xuICAgICAgICAgICAgdmFyIG9iajEgPSB0aGlzLnFzVG9PYmplY3QodGhpcy50b1F1ZXJ5Rm9ybWF0KHFzMSkpO1xuICAgICAgICAgICAgdmFyIG9iajIgPSB0aGlzLnFzVG9PYmplY3QodGhpcy50b1F1ZXJ5Rm9ybWF0KHFzMikpO1xuICAgICAgICAgICAgcmV0dXJuICQuZXh0ZW5kKHRydWUsIHt9LCBvYmoxLCBvYmoyKTtcbiAgICAgICAgfSxcblxuICAgICAgICBhZGRUcmFpbGluZ1NsYXNoOiBmdW5jdGlvbiAodXJsKSB7XG4gICAgICAgICAgICBpZiAoIXVybCkge1xuICAgICAgICAgICAgICAgIHJldHVybiAnJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiAodXJsLmNoYXJBdCh1cmwubGVuZ3RoIC0gMSkgPT09ICcvJykgPyB1cmwgOiAodXJsICsgJy8nKTtcbiAgICAgICAgfVxuICAgIH07XG59KCkpO1xuXG5cbiIsIi8qKlxuICogVXRpbGl0aWVzIGZvciB3b3JraW5nIHdpdGggdGhlIHJ1biBzZXJ2aWNlXG4qL1xuJ3VzZSBzdHJpY3QnO1xudmFyIHF1dGlsID0gcmVxdWlyZSgnLi9xdWVyeS11dGlsJyk7XG52YXIgTUFYX1VSTF9MRU5HVEggPSAyMDQ4O1xuXG5tb2R1bGUuZXhwb3J0cyA9IChmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIHJldHVybnMgb3BlcmF0aW9ucyBvZiB0aGUgZm9ybSBgW1tvcDEsb3AyXSwgW2FyZzEsIGFyZzJdXWBcbiAgICAgICAgICogQHBhcmFtICB7T2JqZWN0fEFycmF5fFN0cmluZ30gb3BlcmF0aW9ucyBvcGVyYXRpb25zIHRvIHBlcmZvcm1cbiAgICAgICAgICogQHBhcmFtICB7QXJyYXl9IGFyZ3MgYXJndW1lbnRzIGZvciBvcGVyYXRpb25cbiAgICAgICAgICogQHJldHVybiB7U3RyaW5nfSAgICBNYXRyaXgtZm9ybWF0IHF1ZXJ5IHBhcmFtZXRlcnNcbiAgICAgICAgICovXG4gICAgICAgIG5vcm1hbGl6ZU9wZXJhdGlvbnM6IGZ1bmN0aW9uIChvcGVyYXRpb25zLCBhcmdzKSB7XG4gICAgICAgICAgICBpZiAoIWFyZ3MpIHtcbiAgICAgICAgICAgICAgICBhcmdzID0gW107XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgcmV0dXJuTGlzdCA9IHtcbiAgICAgICAgICAgICAgICBvcHM6IFtdLFxuICAgICAgICAgICAgICAgIGFyZ3M6IFtdXG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICB2YXIgX2NvbmNhdCA9IGZ1bmN0aW9uIChhcnIpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gKGFyciAhPT0gbnVsbCAmJiBhcnIgIT09IHVuZGVmaW5lZCkgPyBbXS5jb25jYXQoYXJyKSA6IFtdO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgLy97IGFkZDogWzEsMl0sIHN1YnRyYWN0OiBbMiw0XSB9XG4gICAgICAgICAgICB2YXIgX25vcm1hbGl6ZVBsYWluT2JqZWN0cyA9IGZ1bmN0aW9uIChvcGVyYXRpb25zLCByZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgaWYgKCFyZXR1cm5MaXN0KSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybkxpc3QgPSB7IG9wczogW10sIGFyZ3M6IFtdIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICQuZWFjaChvcGVyYXRpb25zLCBmdW5jdGlvbiAob3BuLCBhcmcpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuTGlzdC5vcHMucHVzaChvcG4pO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm5MaXN0LmFyZ3MucHVzaChfY29uY2F0KGFyZykpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJldHVybiByZXR1cm5MaXN0O1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIC8veyBuYW1lOiAnYWRkJywgcGFyYW1zOiBbMV0gfVxuICAgICAgICAgICAgdmFyIF9ub3JtYWxpemVTdHJ1Y3R1cmVkT2JqZWN0cyA9IGZ1bmN0aW9uIChvcGVyYXRpb24sIHJldHVybkxpc3QpIHtcbiAgICAgICAgICAgICAgICBpZiAoIXJldHVybkxpc3QpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuTGlzdCA9IHsgb3BzOiBbXSwgYXJnczogW10gfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuTGlzdC5vcHMucHVzaChvcGVyYXRpb24ubmFtZSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuTGlzdC5hcmdzLnB1c2goX2NvbmNhdChvcGVyYXRpb24ucGFyYW1zKSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHJldHVybkxpc3Q7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICB2YXIgX25vcm1hbGl6ZU9iamVjdCA9IGZ1bmN0aW9uIChvcGVyYXRpb24sIHJldHVybkxpc3QpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gKChvcGVyYXRpb24ubmFtZSkgPyBfbm9ybWFsaXplU3RydWN0dXJlZE9iamVjdHMgOiBfbm9ybWFsaXplUGxhaW5PYmplY3RzKShvcGVyYXRpb24sIHJldHVybkxpc3QpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgdmFyIF9ub3JtYWxpemVMaXRlcmFscyA9IGZ1bmN0aW9uIChvcGVyYXRpb24sIGFyZ3MsIHJldHVybkxpc3QpIHtcbiAgICAgICAgICAgICAgICBpZiAoIXJldHVybkxpc3QpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuTGlzdCA9IHsgb3BzOiBbXSwgYXJnczogW10gfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuTGlzdC5vcHMucHVzaChvcGVyYXRpb24pO1xuICAgICAgICAgICAgICAgIHJldHVybkxpc3QuYXJncy5wdXNoKF9jb25jYXQoYXJncykpO1xuICAgICAgICAgICAgICAgIHJldHVybiByZXR1cm5MaXN0O1xuICAgICAgICAgICAgfTtcblxuXG4gICAgICAgICAgICB2YXIgX25vcm1hbGl6ZUFycmF5cyA9IGZ1bmN0aW9uIChvcGVyYXRpb25zLCBhcmcsIHJldHVybkxpc3QpIHtcbiAgICAgICAgICAgICAgICBpZiAoIXJldHVybkxpc3QpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuTGlzdCA9IHsgb3BzOiBbXSwgYXJnczogW10gfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgJC5lYWNoKG9wZXJhdGlvbnMsIGZ1bmN0aW9uIChpbmRleCwgb3BuKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICgkLmlzUGxhaW5PYmplY3Qob3BuKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgX25vcm1hbGl6ZU9iamVjdChvcG4sIHJldHVybkxpc3QpO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgX25vcm1hbGl6ZUxpdGVyYWxzKG9wbiwgYXJnc1tpbmRleF0sIHJldHVybkxpc3QpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHJldHVybkxpc3Q7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBpZiAoJC5pc1BsYWluT2JqZWN0KG9wZXJhdGlvbnMpKSB7XG4gICAgICAgICAgICAgICAgX25vcm1hbGl6ZU9iamVjdChvcGVyYXRpb25zLCByZXR1cm5MaXN0KTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoJC5pc0FycmF5KG9wZXJhdGlvbnMpKSB7XG4gICAgICAgICAgICAgICAgX25vcm1hbGl6ZUFycmF5cyhvcGVyYXRpb25zLCBhcmdzLCByZXR1cm5MaXN0KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgX25vcm1hbGl6ZUxpdGVyYWxzKG9wZXJhdGlvbnMsIGFyZ3MsIHJldHVybkxpc3QpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gcmV0dXJuTGlzdDtcbiAgICAgICAgfSxcblxuICAgICAgICBzcGxpdEdldEZhY3Rvcnk6IGZ1bmN0aW9uIChodHRwT3B0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uIChwYXJhbXMsIG9wdGlvbnMpIHtcbiAgICAgICAgICAgICAgICB2YXIgaHR0cCA9IHRoaXM7IC8vZXNsaW50LWRpc2FibGUtbGluZVxuICAgICAgICAgICAgICAgIHZhciBnZXRWYWx1ZSA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciB2YWx1ZSA9IG9wdGlvbnNbbmFtZV0gfHwgaHR0cE9wdGlvbnNbbmFtZV07XG4gICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gdmFsdWUoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB2YXIgZ2V0RmluYWxVcmwgPSBmdW5jdGlvbiAocGFyYW1zKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciB1cmwgPSBnZXRWYWx1ZSgndXJsJywgb3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBkYXRhID0gcGFyYW1zO1xuICAgICAgICAgICAgICAgICAgICAvLyBUaGVyZSBpcyBlYXN5IChvciBrbm93bikgd2F5IHRvIGdldCB0aGUgZmluYWwgVVJMIGpxdWVyeSBpcyBnb2luZyB0byBzZW5kIHNvXG4gICAgICAgICAgICAgICAgICAgIC8vIHdlJ3JlIHJlcGxpY2F0aW5nIGl0LiBUaGUgcHJvY2VzcyBtaWdodCBjaGFuZ2UgYXQgc29tZSBwb2ludCBidXQgaXQgcHJvYmFibHkgd2lsbCBub3QuXG4gICAgICAgICAgICAgICAgICAgIC8vIDEuIFJlbW92ZSBoYXNoXG4gICAgICAgICAgICAgICAgICAgIHVybCA9IHVybC5yZXBsYWNlKC8jLiokLywgJycpO1xuICAgICAgICAgICAgICAgICAgICAvLyAxLiBBcHBlbmQgcXVlcnkgc3RyaW5nXG4gICAgICAgICAgICAgICAgICAgIHZhciBxdWVyeVBhcmFtcyA9IHF1dGlsLnRvUXVlcnlGb3JtYXQoZGF0YSk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBxdWVzdGlvbklkeCA9IHVybC5pbmRleE9mKCc/Jyk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChxdWVyeVBhcmFtcyAmJiBxdWVzdGlvbklkeCA+IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gdXJsICsgJyYnICsgcXVlcnlQYXJhbXM7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAocXVlcnlQYXJhbXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB1cmwgKyAnPycgKyBxdWVyeVBhcmFtcztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdXJsO1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgdmFyIHVybCA9IGdldEZpbmFsVXJsKHBhcmFtcyk7XG4gICAgICAgICAgICAgICAgLy8gV2UgbXVzdCBzcGxpdCB0aGUgR0VUIGluIG11bHRpcGxlIHNob3J0IFVSTCdzXG4gICAgICAgICAgICAgICAgLy8gVGhlIG9ubHkgcHJvcGVydHkgYWxsb3dlZCB0byBiZSBzcGxpdCBpcyBcImluY2x1ZGVcIlxuICAgICAgICAgICAgICAgIGlmIChwYXJhbXMgJiYgcGFyYW1zLmluY2x1ZGUgJiYgZW5jb2RlVVJJKHVybCkubGVuZ3RoID4gTUFYX1VSTF9MRU5HVEgpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGR0ZCA9ICQuRGVmZXJyZWQoKTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcmFtc0NvcHkgPSAkLmV4dGVuZCh0cnVlLCB7fSwgcGFyYW1zKTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHBhcmFtc0NvcHkuaW5jbHVkZTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHVybE5vSW5jbHVkZXMgPSBnZXRGaW5hbFVybChwYXJhbXNDb3B5KTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGRpZmYgPSBNQVhfVVJMX0xFTkdUSCAtIHVybE5vSW5jbHVkZXMubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICB2YXIgb2xkU3VjY2VzcyA9IG9wdGlvbnMuc3VjY2VzcyB8fCBodHRwT3B0aW9ucy5zdWNjZXNzIHx8ICQubm9vcDtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG9sZEVycm9yID0gb3B0aW9ucy5lcnJvciB8fCBodHRwT3B0aW9ucy5lcnJvciB8fCAkLm5vb3A7XG4gICAgICAgICAgICAgICAgICAgIC8vIHJlbW92ZSB0aGUgb3JpZ2luYWwgc3VjY2VzcyBhbmQgZXJyb3IgY2FsbGJhY2tzXG4gICAgICAgICAgICAgICAgICAgIG9wdGlvbnMuc3VjY2VzcyA9ICQubm9vcDtcbiAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5lcnJvciA9ICQubm9vcDtcblxuICAgICAgICAgICAgICAgICAgICB2YXIgaW5jbHVkZSA9IHBhcmFtcy5pbmNsdWRlO1xuICAgICAgICAgICAgICAgICAgICB2YXIgY3VyckluY2x1ZGVzID0gW107XG4gICAgICAgICAgICAgICAgICAgIHZhciBpbmNsdWRlT3B0cyA9IFtjdXJySW5jbHVkZXNdO1xuICAgICAgICAgICAgICAgICAgICB2YXIgY3Vyckxlbmd0aCA9IGVuY29kZVVSSUNvbXBvbmVudCgnP2luY2x1ZGU9JykubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICB2YXIgdmFyaWFibGUgPSBpbmNsdWRlLnBvcCgpO1xuICAgICAgICAgICAgICAgICAgICB3aGlsZSAodmFyaWFibGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciB2YXJMZW5naHQgPSBlbmNvZGVVUklDb21wb25lbnQodmFyaWFibGUpLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFVzZSBhIGdyZWVkeSBhcHByb2FjaCBmb3Igbm93LCBjYW4gYmUgb3B0aW1pemVkIHRvIGJlIHNvbHZlZCBpbiBhIG1vcmVcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGVmZmljaWVudCB3YXlcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vICsgMSBpcyB0aGUgY29tbWFcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjdXJyTGVuZ3RoICsgdmFyTGVuZ2h0ICsgMSA8IGRpZmYpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJySW5jbHVkZXMucHVzaCh2YXJpYWJsZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY3Vyckxlbmd0aCArPSB2YXJMZW5naHQgKyAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJySW5jbHVkZXMgPSBbdmFyaWFibGVdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVPcHRzLnB1c2goY3VyckluY2x1ZGVzKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyTGVuZ3RoID0gJz9pbmNsdWRlPScubGVuZ3RoICsgdmFyTGVuZ2h0O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSBpbmNsdWRlLnBvcCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHZhciByZXFzID0gJC5tYXAoaW5jbHVkZU9wdHMsIGZ1bmN0aW9uIChpbmNsdWRlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVxUGFyYW1zID0gJC5leHRlbmQoe30sIHBhcmFtcywgeyBpbmNsdWRlOiBpbmNsdWRlIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGh0dHAuZ2V0KHJlcVBhcmFtcywgb3B0aW9ucyk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAkLndoZW4uYXBwbHkoJCwgcmVxcykudGhlbihmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBFYWNoIGFyZ3VtZW50IGFyZSBhcnJheXMgb2YgdGhlIGFyZ3VtZW50cyBvZiBlYWNoIGRvbmUgcmVxdWVzdFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gU28gdGhlIGZpcnN0IGFyZ3VtZW50IG9mIHRoZSBmaXJzdCBhcnJheSBvZiBhcmd1bWVudHMgaXMgdGhlIGRhdGFcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBpc1ZhbGlkID0gYXJndW1lbnRzWzBdICYmIGFyZ3VtZW50c1swXVswXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghaXNWYWxpZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNob3VsZCBuZXZlciBoYXBwZW4uLi5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbGRFcnJvcigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBkdGQucmVqZWN0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZmlyc3RSZXNwb25zZSA9IGFyZ3VtZW50c1swXVswXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBpc09iamVjdCA9ICQuaXNQbGFpbk9iamVjdChmaXJzdFJlc3BvbnNlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBpc1J1bkFQSSA9IChpc09iamVjdCAmJiAkLmlzUGxhaW5PYmplY3QoZmlyc3RSZXNwb25zZS52YXJpYWJsZXMpKSB8fCAhaXNPYmplY3Q7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXNSdW5BUEkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXNPYmplY3QpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gYWdncmVnYXRlIHRoZSB2YXJpYWJsZXMgcHJvcGVydHkgb25seVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgYWdncmVnYXRlUnVuID0gYXJndW1lbnRzWzBdWzBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLmVhY2goYXJndW1lbnRzLCBmdW5jdGlvbiAoaWR4LCBhcmdzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgcnVuID0gYXJnc1swXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICQuZXh0ZW5kKHRydWUsIGFnZ3JlZ2F0ZVJ1bi52YXJpYWJsZXMsIHJ1bi52YXJpYWJsZXMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2xkU3VjY2VzcyhhZ2dyZWdhdGVSdW4sIGFyZ3VtZW50c1swXVsxXSwgYXJndW1lbnRzWzBdWzJdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHRkLnJlc29sdmUoYWdncmVnYXRlUnVuLCBhcmd1bWVudHNbMF1bMV0sIGFyZ3VtZW50c1swXVsyXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gYXJyYXkgb2YgcnVuc1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBZ3JlZ2F0ZSB2YXJpYWJsZXMgaW4gZWFjaCBydW5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFnZ3JlZ2F0ZWRSdW5zID0ge307XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICQuZWFjaChhcmd1bWVudHMsIGZ1bmN0aW9uIChpZHgsIGFyZ3MpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBydW5zID0gYXJnc1swXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghJC5pc0FycmF5KHJ1bnMpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5lYWNoKHJ1bnMsIGZ1bmN0aW9uIChpZHhSdW4sIHJ1bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChydW4uaWQgJiYgIWFnZ3JlZ2F0ZWRSdW5zW3J1bi5pZF0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcnVuLnZhcmlhYmxlcyA9IHJ1bi52YXJpYWJsZXMgfHwge307XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZ3JlZ2F0ZWRSdW5zW3J1bi5pZF0gPSBydW47XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChydW4uaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5leHRlbmQodHJ1ZSwgYWdncmVnYXRlZFJ1bnNbcnVuLmlkXS52YXJpYWJsZXMsIHJ1bi52YXJpYWJsZXMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdHVybiBpdCBpbnRvIGFuIGFycmF5XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZ3JlZ2F0ZWRSdW5zID0gJC5tYXAoYWdncmVnYXRlZFJ1bnMsIGZ1bmN0aW9uIChydW4pIHsgcmV0dXJuIHJ1bjsgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9sZFN1Y2Nlc3MoYWdncmVnYXRlZFJ1bnMsIGFyZ3VtZW50c1swXVsxXSwgYXJndW1lbnRzWzBdWzJdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHRkLnJlc29sdmUoYWdncmVnYXRlZFJ1bnMsIGFyZ3VtZW50c1swXVsxXSwgYXJndW1lbnRzWzBdWzJdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGlzIHZhcmlhYmxlcyBBUElcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBhZ2dyZWdhdGUgdGhlIHJlc3BvbnNlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFnZ3JlZ2F0ZWRWYXJpYWJsZXMgPSB7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkLmVhY2goYXJndW1lbnRzLCBmdW5jdGlvbiAoaWR4LCBhcmdzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciB2YXJzID0gYXJnc1swXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5leHRlbmQodHJ1ZSwgYWdncmVnYXRlZFZhcmlhYmxlcywgdmFycyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb2xkU3VjY2VzcyhhZ2dyZWdhdGVkVmFyaWFibGVzLCBhcmd1bWVudHNbMF1bMV0sIGFyZ3VtZW50c1swXVsyXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZHRkLnJlc29sdmUoYWdncmVnYXRlZFZhcmlhYmxlcywgYXJndW1lbnRzWzBdWzFdLCBhcmd1bWVudHNbMF1bMl0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBvbGRFcnJvci5hcHBseShodHRwLCBhcmd1bWVudHMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZHRkLnJlamVjdC5hcHBseShkdGQsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZHRkLnByb21pc2UoKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaHR0cC5nZXQocGFyYW1zLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgfTtcbn0oKSk7XG4iXX0= diff --git a/dist/epicenter.min.js b/dist/epicenter.min.js index 01dbbeb9..9aa0c098 100644 --- a/dist/epicenter.min.js +++ b/dist/epicenter.min.js @@ -80,7 +80,7 @@ module.exports={"new-if-initialized":require("./new-if-initialized-strategy"),"n },{"../store/session-manager":40,"../transport/http-transport-factory":43,"../util/object-util":45,"./configuration-service":27}],39:[function(require,module,exports){ "use strict";var Cookie=function(){this.get=function(){return document.cookie},this.set=function(newCookie){document.cookie=newCookie}};module.exports=function(config){var host=window.location.hostname;var validHost=host.split(".").length>1;var domain=validHost?"."+host:null;var defaults={root:"/",domain:domain,cookie:new Cookie};this.serviceOptions=$.extend({},defaults,config);var publicAPI={set:function(key,value,options){var setOptions=$.extend(!0,{},this.serviceOptions,options);var domain=setOptions.domain;var path=setOptions.root;var cookie=setOptions.cookie;return cookie.set(encodeURIComponent(key)+"="+encodeURIComponent(value)+(domain?"; domain="+domain:"")+(path?"; path="+path:"")),value},get:function(key){var cookie=this.serviceOptions.cookie;var cookieReg=new RegExp("(?:^|;)\\s*"+encodeURIComponent(key).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=\\s*([^;]*).*$");var res=cookieReg.exec(cookie.get());var val=res?decodeURIComponent(res[1]):null;return val},remove:function(key,options){var remOptions=$.extend(!0,{},this.serviceOptions,options);var domain=remOptions.domain;var path=remOptions.root;var cookie=remOptions.cookie;return cookie.set(encodeURIComponent(key)+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT"+(domain?"; domain="+domain:"")+(path?"; path="+path:"")),key},destroy:function(){var cookie=this.serviceOptions.cookie;var aKeys=cookie.get().replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g,"").split(/\s*(?:\=[^;]*)?;\s*/);for(var nIdx=0;nIdx> 8 - idx % 1 * 8)\n ) {\n charCode = str.charCodeAt(idx += 3/4);\n if (charCode > 0xFF) {\n throw new InvalidCharacterError(\"'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.\");\n }\n block = block << 8 | charCode;\n }\n return output;\n });\n\n // decoder\n // [https://gist.github.com/1020396] by [https://github.com/atk]\n object.atob || (\n object.atob = function (input) {\n var str = String(input).replace(/=+$/, '');\n if (str.length % 4 == 1) {\n throw new InvalidCharacterError(\"'atob' failed: The string to be decoded is not correctly encoded.\");\n }\n for (\n // initialize result and counters\n var bc = 0, bs, buffer, idx = 0, output = '';\n // get next character\n buffer = str.charAt(idx++);\n // character found in table? initialize bit storage and add its ascii value;\n ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,\n // and if not first of each 4 characters,\n // convert the first 8 bits to one ascii character\n bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0\n ) {\n // try to find character in table (0-63, not found => -1)\n buffer = chars.indexOf(buffer);\n }\n return output;\n });\n\n}());\n","'use strict';\n/* eslint-disable no-unused-vars */\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nvar propIsEnumerable = Object.prototype.propertyIsEnumerable;\n\nfunction toObject(val) {\n\tif (val === null || val === undefined) {\n\t\tthrow new TypeError('Object.assign cannot be called with null or undefined');\n\t}\n\n\treturn Object(val);\n}\n\nfunction shouldUseNative() {\n\ttry {\n\t\tif (!Object.assign) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Detect buggy property enumeration order in older V8 versions.\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=4118\n\t\tvar test1 = new String('abc'); // eslint-disable-line\n\t\ttest1[5] = 'de';\n\t\tif (Object.getOwnPropertyNames(test1)[0] === '5') {\n\t\t\treturn false;\n\t\t}\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=3056\n\t\tvar test2 = {};\n\t\tfor (var i = 0; i < 10; i++) {\n\t\t\ttest2['_' + String.fromCharCode(i)] = i;\n\t\t}\n\t\tvar order2 = Object.getOwnPropertyNames(test2).map(function (n) {\n\t\t\treturn test2[n];\n\t\t});\n\t\tif (order2.join('') !== '0123456789') {\n\t\t\treturn false;\n\t\t}\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=3056\n\t\tvar test3 = {};\n\t\t'abcdefghijklmnopqrst'.split('').forEach(function (letter) {\n\t\t\ttest3[letter] = letter;\n\t\t});\n\t\tif (Object.keys(Object.assign({}, test3)).join('') !==\n\t\t\t\t'abcdefghijklmnopqrst') {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t} catch (e) {\n\t\t// We don't expect any of the above to throw, but better to be safe.\n\t\treturn false;\n\t}\n}\n\nmodule.exports = shouldUseNative() ? Object.assign : function (target, source) {\n\tvar from;\n\tvar to = toObject(target);\n\tvar symbols;\n\n\tfor (var s = 1; s < arguments.length; s++) {\n\t\tfrom = Object(arguments[s]);\n\n\t\tfor (var key in from) {\n\t\t\tif (hasOwnProperty.call(from, key)) {\n\t\t\t\tto[key] = from[key];\n\t\t\t}\n\t\t}\n\n\t\tif (Object.getOwnPropertySymbols) {\n\t\t\tsymbols = Object.getOwnPropertySymbols(from);\n\t\t\tfor (var i = 0; i < symbols.length; i++) {\n\t\t\t\tif (propIsEnumerable.call(from, symbols[i])) {\n\t\t\t\t\tto[symbols[i]] = from[symbols[i]];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn to;\n};\n","/**\n * Epicenter Javascript libraries\n * v<%= version %>\n * https://github.com/forio/epicenter-js-libs\n */\n\nvar F = {\n util: {},\n factory: {},\n transport: {},\n store: {},\n service: {},\n manager: {\n strategy: {}\n },\n\n};\n\nF.load = require('./env-load');\n\nif (!global.SKIP_ENV_LOAD) {\n F.load();\n}\n\nF.util.query = require('./util/query-util');\nF.util.run = require('./util/run-util');\nF.util.classFrom = require('./util/inherit');\n\nF.factory.Transport = require('./transport/http-transport-factory');\nF.transport.Ajax = require('./transport/ajax-http-transport');\n\nF.service.URL = require('./service/url-config-service');\nF.service.Config = require('./service/configuration-service');\nF.service.Run = require('./service/run-api-service');\nF.service.File = require('./service/admin-file-service');\nF.service.Variables = require('./service/variables-api-service');\nF.service.Data = require('./service/data-api-service');\nF.service.Auth = require('./service/auth-api-service');\nF.service.World = require('./service/world-api-adapter');\nF.service.State = require('./service/state-api-adapter');\nF.service.User = require('./service/user-api-adapter');\nF.service.Member = require('./service/member-api-adapter');\nF.service.Asset = require('./service/asset-api-adapter');\nF.service.Group = require('./service/group-api-service');\nF.service.Introspect = require('./service/introspection-api-service');\n\nF.store.Cookie = require('./store/cookie-store');\nF.factory.Store = require('./store/store-factory');\n\nF.manager.ScenarioManager = require('./managers/scenario-manager');\nF.manager.RunManager = require('./managers/run-manager');\nF.manager.AuthManager = require('./managers/auth-manager');\nF.manager.WorldManager = require('./managers/world-manager');\n\nF.manager.strategy['always-new'] = require('./managers/run-strategies/always-new-strategy');\nF.manager.strategy['conditional-creation'] = require('./managers/run-strategies/conditional-creation-strategy');\nF.manager.strategy.identity = require('./managers/run-strategies/none-strategy');\nF.manager.strategy['new-if-missing'] = require('./managers/run-strategies/new-if-missing-strategy');\nF.manager.strategy['new-if-missing'] = require('./managers/run-strategies/new-if-missing-strategy');\nF.manager.strategy['new-if-persisted'] = require('./managers/run-strategies/new-if-persisted-strategy');\nF.manager.strategy['new-if-initialized'] = require('./managers/run-strategies/new-if-initialized-strategy');\n\nF.manager.ChannelManager = require('./managers/epicenter-channel-manager');\nF.service.Channel = require('./service/channel-service');\n\nF.version = '<%= version %>';\nF.api = require('./api-version.json');\n\nglobal.F = F;\nmodule.exports = F;\n","'use strict';\n\nvar URLConfigService = require('./service/url-config-service');\n\nvar envLoad = function (callback) {\n var urlService = new URLConfigService();\n var infoUrl = urlService.getAPIPath('config');\n var envPromise = $.ajax({ url: infoUrl, async: false });\n envPromise = envPromise.then(function (res) {\n var overrides = res.api;\n URLConfigService.defaults = $.extend(URLConfigService.defaults, overrides);\n });\n return envPromise.then(callback).fail(callback);\n};\n\nmodule.exports = envLoad;\n","/**\n* ## Authorization Manager\n*\n* The Authorization Manager provides an easy way to manage user authentication (logging in and out) and authorization (keeping track of tokens, sessions, and groups) for projects.\n*\n* The Authorization Manager is most useful for [team projects](../../../glossary/#team) with an access level of [Authenticated](../../../glossary/#access). These projects are accessed by [end users](../../../glossary/#users) who are members of one or more [groups](../../../glossary/#groups).\n*\n* #### Using the Authorization Manager\n*\n* To use the Authorization Manager, instantiate it. Then, make calls to any of the methods you need:\n*\n* var authMgr = new F.manager.AuthManager({\n* account: 'acme-simulations',\n* userName: 'enduser1',\n* password: 'passw0rd'\n* });\n* authMgr.login().then(function () {\n* authMgr.getCurrentUserSessionInfo();\n* });\n*\n*\n* The `options` object passed to the `F.manager.AuthManager()` call can include:\n*\n* * `account`: The account id for this `userName`. In the Epicenter UI, this is the **Team ID** (for team projects) or the **User ID** (for personal projects).\n* * `userName`: Email or username to use for logging in.\n* * `password`: Password for specified `userName`.\n* * `project`: The **Project ID** for the project to log this user into. Optional.\n* * `groupId`: Id of the group to which `userName` belongs. Required for end users if the `project` is specified.\n*\n* If you prefer starting from a template, the Epicenter JS Libs [Login Component](../../#components) uses the Authorization Manager as well. This sample HTML page (and associated CSS and JS files) provides a login form for team members and end users of your project. It also includes a group selector for end users that are members of multiple groups.\n*/\n\n'use strict';\nvar AuthAdapter = require('../service/auth-api-service');\nvar MemberAdapter = require('../service/member-api-adapter');\nvar GroupService = require('../service/group-api-service');\nvar SessionManager = require('../store/session-manager');\nvar _pick = require('../util/object-util')._pick;\nvar objectAssign = require('object-assign');\n\nvar atob = window.atob || require('Base64').atob;\n\nvar defaults = {\n requiresGroup: true\n};\n\nfunction AuthManager(options) {\n options = $.extend(true, {}, defaults, options);\n this.sessionManager = new SessionManager(options);\n this.options = this.sessionManager.getMergedOptions();\n\n this.authAdapter = new AuthAdapter(this.options);\n}\n\nvar _findUserInGroup = function (members, id) {\n for (var j = 0; j < members.length; j++) {\n if (members[j].userId === id) {\n return members[j];\n }\n }\n return null;\n};\n\nAuthManager.prototype = $.extend(AuthManager.prototype, {\n\n /**\n * Logs user in.\n *\n * **Example**\n *\n * authMgr.login({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * userName: 'enduser1',\n * password: 'passw0rd'\n * })\n * .then(function(statusObj) {\n * // if enduser1 belongs to exactly one group\n * // (or if the login() call is modified to include the group id)\n * // continue here\n * })\n * .fail(function(statusObj) {\n * // if enduser1 belongs to multiple groups,\n * // the login() call fails\n * // and returns all groups of which the user is a member\n * for (var i=0; i < statusObj.userGroups.length; i++) {\n * console.log(statusObj.userGroups[i].name, statusObj.userGroups[i].groupId);\n * }\n * });\n *\n * **Parameters**\n *\n * @param {Object} options (Optional) Overrides for configuration options. If not passed in when creating an instance of the manager (`F.manager.AuthManager()`), these options should include:\n * @param {string} options.account The account id for this `userName`. In the Epicenter UI, this is the **Team ID** (for team projects) or the **User ID** (for personal projects).\n * @param {string} options.userName Email or username to use for logging in.\n * @param {string} options.password Password for specified `userName`.\n * @param {string} options.project (Optional) The **Project ID** for the project to log this user into.\n * @param {string} options.groupId The id of the group to which `userName` belongs. Required for [end users](../../../glossary/#users) if the `project` is specified and if the end users are members of multiple [groups](../../../glossary/#groups), otherwise optional.\n * @return {Promise}\n */\n login: function (options) {\n var me = this;\n var $d = $.Deferred();\n var sessionManager = this.sessionManager;\n var adapterOptions = sessionManager.getMergedOptions({ success: $.noop, error: $.noop }, options);\n var outSuccess = adapterOptions.success;\n var outError = adapterOptions.error;\n var groupId = adapterOptions.groupId;\n\n var decodeToken = function (token) {\n var encoded = token.split('.')[1];\n while (encoded.length % 4 !== 0) { //eslint-disable-line\n encoded += '=';\n }\n return JSON.parse(atob(encoded));\n };\n\n var handleGroupError = function (message, statusCode, data) {\n // logout the user since it's in an invalid state with no group selected\n me.logout().then(function () {\n var error = $.extend(true, {}, data, { statusText: message, status: statusCode });\n $d.reject(error);\n });\n };\n\n var handleSuccess = function (response) {\n var token = response.access_token;\n var userInfo = decodeToken(token);\n var oldGroups = sessionManager.getSession(adapterOptions).groups || {};\n var userGroupOpts = $.extend(true, {}, adapterOptions, { success: $.noop });\n var data = { auth: response, user: userInfo };\n var project = adapterOptions.project;\n var isTeamMember = userInfo.parent_account_id === null;\n var requiresGroup = adapterOptions.requiresGroup && project;\n\n var sessionInfo = {\n auth_token: token,\n account: adapterOptions.account,\n project: project,\n userId: userInfo.user_id,\n groups: oldGroups,\n isTeamMember: isTeamMember\n };\n // The group is not required if the user is not logging into a project\n if (!requiresGroup) {\n sessionManager.saveSession(sessionInfo);\n outSuccess.apply(this, [data]);\n $d.resolve(data);\n return;\n }\n\n var handleGroupList = function (groupList) {\n data.userGroups = groupList;\n\n var group = null;\n if (groupList.length === 0) {\n handleGroupError('The user has no groups associated in this account', 401, data);\n return;\n } else if (groupList.length === 1) {\n // Select the only group\n group = groupList[0];\n } else if (groupList.length > 1) {\n if (groupId) {\n var filteredGroups = $.grep(groupList, function (resGroup) {\n return resGroup.groupId === groupId;\n });\n group = filteredGroups.length === 1 ? filteredGroups[0] : null;\n }\n }\n\n if (group) {\n // A team member does not get the group members because is calling the Group API\n // but it's automatically a fac user\n var isFac = isTeamMember ? true : _findUserInGroup(group.members, userInfo.user_id).role === 'facilitator';\n var groupData = {\n groupId: group.groupId,\n groupName: group.name,\n isFac: isFac\n };\n var sessionInfoWithGroup = objectAssign({}, sessionInfo, groupData);\n sessionInfo.groups[project] = groupData;\n me.sessionManager.saveSession(sessionInfoWithGroup, adapterOptions);\n outSuccess.apply(this, [data]);\n $d.resolve(data);\n } else {\n handleGroupError('This user is associated with more than one group. Please specify a group id to log into and try again', 403, data);\n }\n };\n\n if (!isTeamMember) {\n me.getUserGroups({ userId: userInfo.user_id, token: token }, userGroupOpts)\n .then(handleGroupList, $d.reject);\n } else {\n var opts = objectAssign({}, userGroupOpts, { token: token });\n var groupService = new GroupService(opts);\n groupService.getGroups({ account: adapterOptions.account, project: project })\n .then(function (groups) {\n // Group API returns id instead of groupId\n groups.forEach(function (group) {\n group.groupId = group.id;\n });\n handleGroupList(groups);\n }, $d.reject);\n }\n };\n\n adapterOptions.success = handleSuccess;\n adapterOptions.error = function (response) {\n if (adapterOptions.account) {\n // Try to login as a system user\n adapterOptions.account = null;\n adapterOptions.error = function () {\n outError.apply(this, arguments);\n $d.reject(response);\n };\n\n me.authAdapter.login(adapterOptions);\n return;\n }\n\n outError.apply(this, arguments);\n $d.reject(response);\n };\n\n this.authAdapter.login(adapterOptions);\n return $d.promise();\n },\n\n /**\n * Logs user out by clearing all session information.\n *\n * **Example**\n *\n * authMgr.logout();\n *\n * **Parameters**\n *\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n logout: function (options) {\n var me = this;\n var adapterOptions = this.sessionManager.getMergedOptions(options);\n\n var removeCookieFn = function (response) {\n me.sessionManager.removeSession();\n };\n\n return this.authAdapter.logout(adapterOptions).then(removeCookieFn);\n },\n\n /**\n * Returns the existing user access token if the user is already logged in. Otherwise, logs the user in, creating a new user access token, and returns the new token. (See [more background on access tokens](../../../project_access/)).\n *\n * **Example**\n *\n * authMgr.getToken()\n * .then(function (token) {\n * console.log('My token is ', token);\n * });\n *\n * **Parameters**\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n getToken: function (options) {\n var httpOptions = this.sessionManager.getMergedOptions(options);\n\n var session = this.sessionManager.getSession(httpOptions);\n var $d = $.Deferred();\n if (session.auth_token) {\n $d.resolve(session.auth_token);\n } else {\n this.login(httpOptions).then($d.resolve);\n }\n return $d.promise();\n },\n\n /**\n * Returns an array of group records, one for each group of which the current user is a member. Each group record includes the group `name`, `account`, `project`, and `groupId`.\n *\n * If some end users in your project are members of multiple groups, this is a useful method to call on your project's login page. When the user attempts to log in, you can use this to display the groups of which the user is member, and have the user select the correct group to log in to for this session.\n *\n * **Example**\n *\n * // get groups for current user\n * var sessionObj = authMgr.getCurrentUserSessionInfo();\n * authMgr.getUserGroups({ userId: sessionObj.userId, token: sessionObj.auth_token })\n * .then(function (groups) {\n * for (var i=0; i < groups.length; i++)\n * { console.log(groups[i].name); }\n * });\n *\n * // get groups for particular user\n * authMgr.getUserGroups({userId: 'b1c19dda-2d2e-4777-ad5d-3929f17e86d3', token: savedProjAccessToken });\n *\n * **Parameters**\n * @param {Object} params Object with a userId and token properties.\n * @param {String} params.userId The userId. If looking up groups for the currently logged in user, this is in the session information. Otherwise, pass a string.\n * @param {String} params.token The authorization credentials (access token) to use for checking the groups for this user. If looking up groups for the currently logged in user, this is in the session information. A team member's token or a project access token can access all the groups for all end users in the team or project.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n getUserGroups: function (params, options) {\n var adapterOptions = this.sessionManager.getMergedOptions({ success: $.noop }, options);\n var $d = $.Deferred();\n var outSuccess = adapterOptions.success;\n\n adapterOptions.success = function (memberInfo) {\n // The member API is at the account scope, we filter by project\n if (adapterOptions.project) {\n memberInfo = $.grep(memberInfo, function (group) {\n return group.project === adapterOptions.project;\n });\n }\n\n outSuccess.apply(this, [memberInfo]);\n $d.resolve(memberInfo);\n };\n\n var memberAdapter = new MemberAdapter({ token: params.token, server: adapterOptions.server });\n memberAdapter.getGroupsForUser(params, adapterOptions).fail($d.reject);\n return $d.promise();\n },\n\n /**\n * Returns session information for the current user, including the `userId`, `account`, `project`, `groupId`, `groupName`, `isFac` (whether the end user is a facilitator of this group), and `auth_token` (user access token).\n *\n * *Important*: This method is synchronous. The session information is returned immediately in an object; no callbacks or promises are needed.\n *\n * Session information is stored in a cookie in the browser.\n *\n * **Example**\n *\n * var sessionObj = authMgr.getCurrentUserSessionInfo();\n *\n * **Parameters**\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Object} session information\n */\n getCurrentUserSessionInfo: function (options) {\n var adapterOptions = this.sessionManager.getMergedOptions({ success: $.noop }, options);\n return this.sessionManager.getSession(adapterOptions);\n },\n\n /*\n * Adds one or more groups to the current session. \n *\n * This method assumes that the project and group exist and the user specified in the session is part of this project and group.\n *\n * Returns the new session object.\n *\n * **Example**\n *\n * authMgr.addGroups({ project: 'hello-world', groupName: 'groupName', groupId: 'groupId' });\n * authMgr.addGroups([{ project: 'hello-world', groupName: 'groupName', groupId: 'groupId' }, { project: 'hello-world', groupName: '...' }]);\n *\n * **Parameters**\n * @param {object|array} groups (Required) The group object must contain the `project` (**Project ID**) and `groupName` properties. If passing an array of such objects, all of the objects must contain *different* `project` (**Project ID**) values: although end users may be logged in to multiple projects at once, they may only be logged in to one group per project at a time.\n * @param {string} group.isFac (optional) Defaults to `false`. Set to `true` if the user in the session should be a facilitator in this group.\n * @param {string} group.groupId (optional) Defaults to undefined. Needed mostly for the Members API.\n * @return {Object} session information\n */\n addGroups: function (groups) {\n var session = this.getCurrentUserSessionInfo();\n var isArray = Array.isArray(groups);\n groups = isArray ? groups : [groups];\n\n $.each(groups, function (index, group) {\n var extendedGroup = $.extend({}, { isFac: false }, group);\n var project = extendedGroup.project;\n var validProps = ['groupName', 'groupId', 'isFac'];\n if (!project || !extendedGroup.groupName) {\n throw new Error('No project or groupName specified.');\n }\n // filter object\n extendedGroup = _pick(extendedGroup, validProps);\n session.groups[project] = extendedGroup;\n });\n this.sessionManager.saveSession(session);\n return session;\n }\n});\n\nmodule.exports = AuthManager;\n","'use strict';\n\n/**\n * ## Channel Manager\n *\n * There are two main use cases for the channel: event notifications and chat messages.\n *\n * The Channel Manager is a wrapper around the default [cometd JavaScript library](http://docs.cometd.org/2/reference/javascript.html), `$.cometd`. It provides a few nice features that `$.cometd` doesn't, including:\n *\n * * Automatic re-subscription to channels if you lose your connection\n * * Online / Offline notifications\n * * 'Events' for cometd notifications (instead of having to listen on specific meta channels)\n *\n * While you can work directly with the Channel Manager through Node.js (for example, `require('manager/channel-manager')`) -- or even work directly with `$.cometd` and Epicenter's underlying [Push Channel API](../../../rest_apis/multiplayer/channel/) -- most often it will be easiest to work with the [Epicenter Channel Manager](../epicenter-channel-manager/). The Epicenter Channel Manager is a wrapper that instantiates a Channel Manager with Epicenter-specific defaults.\n *\n * You'll need to include the `epicenter-multiplayer-dependencies.js` library in addition to the `epicenter.js` library in your project to use the Channel Manager. (See [Including Epicenter.js](../../#include).)\n *\n * To use the Channel Manager in client-side JavaScript, instantiate the [Epicenter Channel Manager](../epicenter-channel-manager/), get the channel, then use the channel's `subscribe()` and `publish()` methods to subscribe to topics or publish data to topics.\n *\n * var cm = new F.manager.ChannelManager();\n * var channel = cm.getChannel();\n *\n * channel.subscribe('topic', callback);\n * channel.publish('topic', { myData: 100 });\n *\n * The parameters for instantiating a Channel Manager include:\n *\n * * `options` The options object to configure the Channel Manager. Besides the common options listed here, see http://docs.cometd.org/reference/javascript.html for other supported options.\n * * `options.url` The Cometd endpoint URL.\n * * `options.websocketEnabled` Whether websocket support is active (boolean).\n * * `options.channel` Other defaults to pass on to instances of the underlying Channel Service. See [Channel Service](../channel-service/) for details.\n *\n */\n\nvar Channel = require('../service/channel-service');\nvar SessionManager = require('../store/session-manager');\n\nvar ChannelManager = function (options) {\n if (!$.cometd) {\n throw new Error('Cometd library not found. Please include epicenter-multiplayer-dependencies.js');\n }\n if (!options || !options.url) {\n throw new Error('Please provide an url for the cometd server');\n }\n\n var defaults = {\n /**\n * The Cometd endpoint URL.\n * @type {string}\n */\n url: '',\n\n /**\n * The log level for the channel (logs to console).\n * @type {string}\n */\n logLevel: 'info',\n\n /**\n * Whether websocket support is active. Defaults to `true`.\n * @type {boolean}\n */\n websocketEnabled: true,\n\n /**\n * Whether the ACK extension is enabled. See https://docs.cometd.org/current/reference/#_extensions_acknowledge for more info.\n * @type {boolean}\n */\n ackEnabled: true,\n\n /**\n * If false each instance of Channel will have a separate cometd connection to server, which could be noisy. Set to true to re-use the same connection across instances.\n * @type {boolean}\n */\n shareConnection: false,\n\n /**\n * Other defaults to pass on to instances of the underlying [Channel Service](../channel-service/), which are created through `getChannel()`.\n * @type {object}\n */\n channel: {\n\n },\n\n /**\n * Options to pass to the channel handshake.\n *\n * For example, the [Epicenter Channel Manager](../epicenter-channel-manager/) passes `ext` and authorization information. More information on possible options is in the details of the underlying [Push Channel API](../../../rest_apis/multiplayer/channel/).\n *\n * @type {object}\n */\n handshake: undefined\n };\n this.sessionManager = new SessionManager();\n var defaultCometOptions = this.sessionManager.getMergedOptions(defaults, options);\n this.currentSubscriptions = [];\n this.options = defaultCometOptions;\n\n if (defaultCometOptions.shareConnection && ChannelManager.prototype._cometd) {\n this.cometd = ChannelManager.prototype._cometd;\n return this;\n }\n var cometd = new $.CometD();\n ChannelManager.prototype._cometd = cometd;\n\n cometd.websocketEnabled = defaultCometOptions.websocketEnabled;\n cometd.ackEnabled = defaultCometOptions.ackEnabled;\n\n this.isConnected = false;\n var connectionBroken = function (message) {\n $(this).trigger('disconnect', message);\n };\n var connectionSucceeded = function (message) {\n $(this).trigger('connect', message);\n };\n var me = this;\n\n cometd.configure(defaultCometOptions);\n\n cometd.addListener('/meta/connect', function (message) {\n var wasConnected = this.isConnected;\n this.isConnected = (message.successful === true);\n if (!wasConnected && this.isConnected) { //Connecting for the first time\n connectionSucceeded.call(this, message);\n } else if (wasConnected && !this.isConnected) { //Only throw disconnected message fro the first disconnect, not once per try\n connectionBroken.call(this, message);\n }\n }.bind(this));\n\n cometd.addListener('/meta/disconnect', connectionBroken);\n\n cometd.addListener('/meta/handshake', function (message) {\n if (message.successful) {\n //http://docs.cometd.org/reference/javascript_subscribe.html#javascript_subscribe_meta_channels\n // ^ \"dynamic subscriptions are cleared (like any other subscription) and the application needs to figure out which dynamic subscription must be performed again\"\n cometd.batch(function () {\n $(me.currentSubscriptions).each(function (index, subs) {\n cometd.resubscribe(subs);\n });\n });\n }\n });\n\n //Other interesting events for reference\n cometd.addListener('/meta/subscribe', function (message) {\n $(me).trigger('subscribe', message);\n });\n cometd.addListener('/meta/unsubscribe', function (message) {\n $(me).trigger('unsubscribe', message);\n });\n cometd.addListener('/meta/publish', function (message) {\n $(me).trigger('publish', message);\n });\n cometd.addListener('/meta/unsuccessful', function (message) {\n $(me).trigger('error', message);\n });\n\n cometd.handshake(defaultCometOptions.handshake);\n\n this.cometd = cometd;\n};\n\n\nChannelManager.prototype = $.extend(ChannelManager.prototype, {\n\n /**\n * Creates and returns a channel, that is, an instance of a [Channel Service](../channel-service/).\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var channel = cm.getChannel();\n *\n * channel.subscribe('topic', callback);\n * channel.publish('topic', { myData: 100 });\n *\n * **Parameters**\n * @param {Object|String} options (Optional) If string, assumed to be the base channel url. If object, assumed to be configuration options for the constructor.\n * @return {Channel} Channel instance\n */\n getChannel: function (options) {\n //If you just want to pass in a string\n if (options && !$.isPlainObject(options)) {\n options = {\n base: options\n };\n }\n var defaults = {\n transport: this.cometd\n };\n var channel = new Channel($.extend(true, {}, this.options.channel, defaults, options));\n\n\n //Wrap subs and unsubs so we can use it to re-attach handlers after being disconnected\n var subs = channel.subscribe;\n channel.subscribe = function () {\n var subid = subs.apply(channel, arguments);\n this.currentSubscriptions = this.currentSubscriptions.concat(subid);\n return subid;\n }.bind(this);\n\n\n var unsubs = channel.unsubscribe;\n channel.unsubscribe = function () {\n var removed = unsubs.apply(channel, arguments);\n for (var i = 0; i < this.currentSubscriptions.length; i++) {\n if (this.currentSubscriptions[i].id === removed.id) {\n this.currentSubscriptions.splice(i, 1);\n }\n }\n return removed;\n }.bind(this);\n\n return channel;\n },\n\n /**\n * Start listening for events on this instance. Signature is same as for jQuery Events: http://api.jquery.com/on/.\n *\n * Supported events are: `connect`, `disconnect`, `subscribe`, `unsubscribe`, `publish`, `error`.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/on/.\n */\n on: function (event) {\n $(this).on.apply($(this), arguments);\n },\n\n /**\n * Stop listening for events on this instance. Signature is same as for jQuery Events: http://api.jquery.com/off/.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/off/.\n */\n off: function (event) {\n $(this).off.apply($(this), arguments);\n },\n\n /**\n * Trigger events and execute handlers. Signature is same as for jQuery Events: http://api.jquery.com/trigger/.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/trigger/.\n */\n trigger: function (event) {\n $(this).trigger.apply($(this), arguments);\n }\n});\n\nmodule.exports = ChannelManager;\n","'use strict';\n\n/**\n * ## Epicenter Channel Manager\n *\n * The Epicenter platform provides a push channel, which allows you to publish and subscribe to messages within a [project](../../../glossary/#projects), [group](../../../glossary/#groups), or [multiplayer world](../../../glossary/#world). There are two main use cases for the channel: event notifications and chat messages.\n *\n * The Epicenter Channel Manager is a wrapper around the (more generic) [Channel Manager](../channel-manager/), to instantiate it with Epicenter-specific defaults. If you are interested in including a notification or chat feature in your project, using an Epicenter Channel Manager is probably the easiest way to get started.\n *\n * You'll need to include the `epicenter-multiplayer-dependencies.js` library in addition to the `epicenter.js` library in your project to use the Epicenter Channel Manager. See [Including Epicenter.js](../../#include).\n *\n * To use the Epicenter Channel Manager: instantiate it, get the channel of the scope you want ([user](../../../glossary/#users), [world](../../../glossary/#world), or [group](../../../glossary/#groups)), then use the channel's `subscribe()` and `publish()` methods to subscribe to topics or publish data to topics.\n *\n * var cm = new F.manager.ChannelManager();\n * var gc = cm.getGroupChannel();\n * gc.subscribe('broadcasts', callback);\n *\n * For additional background on Epicenter's push channel, see the introductory notes on the [Push Channel API](../../../rest_apis/multiplayer/channel/) page.\n *\n * The parameters for instantiating an Epicenter Channel Manager include:\n *\n * * `options` Object with details about the Epicenter project for this Epicenter Channel Manager instance.\n * * `options.account` The Epicenter account id (**Team ID** for team projects, **User ID** for personal projects).\n * * `options.project` Epicenter project id.\n * * `options.userName` Epicenter userName used for authentication.\n * * `options.userId` Epicenter user id used for authentication. Optional; `options.userName` is preferred.\n * * `options.token` Epicenter token used for authentication. (You can retrieve this using `authManager.getToken()` from the [Authorization Manager](../auth-manager/).)\n * * `options.allowAllChannels` If not included or if set to `false`, all channel paths are validated; if your project requires [Push Channel Authorization](../../../updating_your_settings/), you should use this option. If you want to allow other channel paths, set to `true`; this is not common.\n */\n\nvar ChannelManager = require('./channel-manager');\nvar classFrom = require('../util/inherit');\nvar urlService = require('../service/url-config-service');\nvar SessionManager = require('../store/session-manager');\n\nvar validTypes = {\n project: true,\n group: true,\n world: true,\n user: true,\n data: true,\n general: true,\n chat: true\n};\nvar getFromSessionOrError = function (value, sessionKeyName, settings) {\n if (!value) {\n if (settings && settings[sessionKeyName]) {\n value = settings[sessionKeyName];\n } else {\n throw new Error(sessionKeyName + ' not found. Please log-in again, or specify ' + sessionKeyName + ' explicitly');\n }\n }\n return value;\n};\nvar __super = ChannelManager.prototype;\nvar EpicenterChannelManager = classFrom(ChannelManager, {\n constructor: function (options) {\n this.sessionManager = new SessionManager(options);\n var defaultCometOptions = this.sessionManager.getMergedOptions(options);\n\n var urlOpts = urlService(defaultCometOptions.server);\n if (!defaultCometOptions.url) {\n //Default epicenter cometd endpoint\n defaultCometOptions.url = urlOpts.protocol + '://' + urlOpts.host + '/channel/subscribe';\n }\n\n if (defaultCometOptions.handshake === undefined) {\n var userName = defaultCometOptions.userName;\n var userId = defaultCometOptions.userId;\n var token = defaultCometOptions.token;\n if ((userName || userId) && token) {\n var userProp = userName ? 'userName' : 'userId';\n var ext = {\n authorization: 'Bearer ' + token\n };\n ext[userProp] = userName ? userName : userId;\n\n defaultCometOptions.handshake = {\n ext: ext\n };\n }\n }\n\n this.options = defaultCometOptions;\n return __super.constructor.call(this, defaultCometOptions);\n },\n\n /**\n * Creates and returns a channel, that is, an instance of a [Channel Service](../channel-service/).\n *\n * This method enforces Epicenter-specific channel naming: all channels requested must be in the form `/{type}/{account id}/{project id}/{...}`, where `type` is one of `run`, `data`, `user`, `world`, or `chat`.\n *\n * **Example**\n *\n * var cm = new F.manager.EpicenterChannelManager();\n * var channel = cm.getChannel('/group/acme/supply-chain-game/');\n *\n * channel.subscribe('topic', callback);\n * channel.publish('topic', { myData: 100 });\n *\n * **Parameters**\n * @param {Object|String} options (Optional) If string, assumed to be the base channel url. If object, assumed to be configuration options for the constructor.\n * @return {Channel} Channel instance\n */\n getChannel: function (options) {\n if (options && typeof options !== 'object') {\n options = {\n base: options\n };\n }\n var channelOpts = $.extend({}, this.options, options);\n var base = channelOpts.base;\n if (!base) {\n throw new Error('No base topic was provided');\n }\n\n if (!channelOpts.allowAllChannels) {\n var baseParts = base.split('/');\n var channelType = baseParts[1];\n if (baseParts.length < 4) { //eslint-disable-line\n throw new Error('Invalid channel base name, it must be in the form /{type}/{account id}/{project id}/{...}');\n }\n if (!validTypes[channelType]) {\n throw new Error('Invalid channel type');\n }\n }\n return __super.getChannel.apply(this, arguments);\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) for the given [group](../../../glossary/#groups). The group must exist in the account (team) and project provided.\n *\n * There are no notifications from Epicenter on this channel; all messages are user-originated.\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var gc = cm.getGroupChannel();\n * gc.subscribe('broadcasts', callback);\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String} groupName (Optional) Group to broadcast to. If not provided, picks up group from current session if end user is logged in.\n * @return {Channel} Channel instance\n */\n getGroupChannel: function (groupName) {\n var session = this.sessionManager.getMergedOptions(this.options);\n groupName = getFromSessionOrError(groupName, 'groupName', session);\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n\n var baseTopic = ['/group', account, project, groupName].join('/');\n return __super.getChannel.call(this, { base: baseTopic });\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) for the given [world](../../../glossary/#world).\n *\n * This is typically used together with the [World Manager](../world-manager).\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var worldManager = new F.manager.WorldManager({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1',\n * run: { model: 'model.eqn' }\n * });\n * worldManager.getCurrentWorld().then(function (worldObject, worldAdapter) {\n * var worldChannel = cm.getWorldChannel(worldObject);\n * worldChannel.subscribe('', function (data) {\n * console.log(data);\n * });\n * });\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String|Object} world The world object or id.\n * @param {String} groupName (Optional) Group the world exists in. If not provided, picks up group from current session if end user is logged in.\n * @return {Channel} Channel instance\n */\n getWorldChannel: function (world, groupName) {\n var worldid = ($.isPlainObject(world) && world.id) ? world.id : world;\n if (!worldid) {\n throw new Error('Please specify a world id');\n }\n var session = this.sessionManager.getMergedOptions(this.options);\n\n groupName = getFromSessionOrError(groupName, 'groupName', session);\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n\n var baseTopic = ['/world', account, project, groupName, worldid].join('/');\n return __super.getChannel.call(this, { base: baseTopic });\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) for the current [end user](../../../glossary/#users) in that user's current [world](../../../glossary/#world).\n *\n * This is typically used together with the [World Manager](../world-manager). Note that this channel only gets notifications for worlds currently in memory. (See more background on [persistence](../../../run_persistence).)\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var worldManager = new F.manager.WorldManager({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1',\n * run: { model: 'model.eqn' }\n * });\n * worldManager.getCurrentWorld().then(function (worldObject, worldAdapter) {\n * var userChannel = cm.getUserChannel(worldObject);\n * userChannel.subscribe('', function (data) {\n * console.log(data);\n * });\n * });\n *\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String|Object} world World object or id.\n * @param {String|Object} user (Optional) User object or id. If not provided, picks up user id from current session if end user is logged in.\n * @param {String} groupName (Optional) Group the world exists in. If not provided, picks up group from current session if end user is logged in.\n * @return {Channel} Channel instance\n */\n getUserChannel: function (world, user, groupName) {\n var worldid = ($.isPlainObject(world) && world.id) ? world.id : world;\n if (!worldid) {\n throw new Error('Please specify a world id');\n }\n var session = this.sessionManager.getMergedOptions(this.options);\n\n var userid = ($.isPlainObject(user) && user.id) ? user.id : user;\n userid = getFromSessionOrError(userid, 'userId', session);\n groupName = getFromSessionOrError(groupName, 'groupName', session);\n\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n\n var baseTopic = ['/user', account, project, groupName, worldid, userid].join('/');\n return __super.getChannel.call(this, { base: baseTopic });\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) that automatically tracks the presence of an [end user](../../../glossary/#users), that is, whether the end user is currently online in this group and world. Notifications are automatically sent when the end user comes online, and when the end user goes offline (not present for more than 2 minutes). Useful in multiplayer games for letting each end user know whether other users in their shared world are also online.\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var worldManager = new F.manager.WorldManager({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * model: 'model.eqn'\n * });\n * worldManager.getCurrentWorld().then(function (worldObject, worldService) {\n * var presenceChannel = cm.getPresenceChannel(worldObject);\n * presenceChannel.on('presence', function (evt, notification) {\n * console.log(notification.online, notification.userId);\n * });\n * });\n *\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String|Object} world World object or id.\n * @param {String|Object} userid (Optional) User object or id. If not provided, picks up user id from current session if end user is logged in.\n * @param {String} groupName (Optional) Group the world exists in. If not provided, picks up group from current session if end user is logged in.\n * @return {Channel} Channel instance\n */\n getPresenceChannel: function (world, userid, groupName) {\n var worldid = ($.isPlainObject(world) && world.id) ? world.id : world;\n if (!worldid) {\n throw new Error('Please specify a world id');\n }\n\n var session = this.sessionManager.getMergedOptions(this.options);\n userid = getFromSessionOrError(userid, 'userId', session);\n groupName = getFromSessionOrError(groupName, 'groupName', session);\n\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n\n var baseTopic = ['/user', account, project, groupName, worldid].join('/');\n var channel = __super.getChannel.call(this, { base: baseTopic });\n\n var lastPingTime = { };\n\n var PING_INTERVAL = 6000;\n channel.subscribe('internal-ping-channel', function (notification) {\n var incomingUserId = notification.data.user;\n if (!lastPingTime[incomingUserId] && incomingUserId !== userid) {\n channel.trigger('presence', { userId: incomingUserId, online: true });\n }\n lastPingTime[incomingUserId] = (new Date()).valueOf();\n });\n\n setInterval(function () {\n channel.publish('internal-ping-channel', { user: userid });\n\n $.each(lastPingTime, function (key, value) {\n var now = (new Date()).valueOf();\n if (value && value + (PING_INTERVAL * 2) < now) {\n lastPingTime[key] = null;\n channel.trigger('presence', { userId: key, online: false });\n }\n });\n }, PING_INTERVAL);\n\n return channel;\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) for the given collection. (The collection name is specified in the `root` argument when the [Data Service](../data-api-service/) is instantiated.) Must be one of the collections in this account (team) and project.\n *\n * There are automatic notifications from Epicenter on this channel when data is created, updated, or deleted in this collection. See more on [automatic messages to the data channel](../../../rest_apis/multiplayer/channel/#data-messages).\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var dc = cm.getDataChannel('survey-responses');\n * dc.subscribe('', function(data, meta) {\n * console.log(data);\n *\n * // meta.date is time of change,\n * // meta.subType is the kind of change: new, update, or delete\n * // meta.path is the full path to the changed data\n * console.log(meta);\n * });\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String} collection Name of collection whose automatic notifications you want to receive.\n * @return {Channel} Channel instance\n */\n getDataChannel: function (collection) {\n if (!collection) {\n throw new Error('Please specify a collection to listen on.');\n }\n\n var session = this.sessionManager.getMergedOptions(this.options);\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n var baseTopic = ['/data', account, project, collection].join('/');\n var channel = __super.getChannel.call(this, { base: baseTopic });\n\n //TODO: Fix after Epicenter bug is resolved\n var oldsubs = channel.subscribe;\n channel.subscribe = function (topic, callback, context, options) {\n var callbackWithCleanData = function (payload) {\n var meta = {\n path: payload.channel,\n subType: payload.data.subType,\n date: payload.data.date\n };\n var actualData = payload.data.data;\n if (actualData.data) { //Delete notifications are one data-level behind of course\n actualData = actualData.data;\n }\n\n callback.call(context, actualData, meta);\n };\n return oldsubs.call(channel, topic, callbackWithCleanData, context, options);\n };\n\n return channel;\n }\n});\n\nmodule.exports = EpicenterChannelManager;\n","'use strict';\n\nmodule.exports = {\n EPI_SESSION_KEY: 'epicenterjs.session',\n STRATEGY_SESSION_KEY: 'epicenter-scenario'\n};","/**\n* ## Run Manager\n*\n* The Run Manager gives you access to runs for your project. This allows you to read and update variables, call operations, etc. Additionally, the Run Manager gives you control over run creation depending on run states. Specifically, you can select [run creation strategies (rules)](../strategies/) for which runs end users of your project work with when they log in to your project.\n*\n* There are many ways to create new runs, including the Epicenter.js [Run Service](../run-api-service/), the RESFTful [Run API](../../../rest_apis/aggregate_run_api) and the [Model Run API](../../../rest_apis/other_apis/model_apis/run/). However, for some projects it makes more sense to pick up where the user left off, using an existing run. And in some projects, whether to create a new run or use an existing one is conditional, for example based on characteristics of the existing run or your own knowledge about the model. The Run Manager provides this level of control: your call to `getRun()`, rather than always returning a new run, returns a run based on the strategy you've specified. (Note that many of the Epicenter sample projects use a Run Service directly, because generally the sample projects are played in one end user session and don't care about run states or run strategies.)\n*\n*\n* ### Using the Run Manager to create and access runs\n*\n* To use the Run Manager, instantiate it by passing in:\n*\n* * `run`: (required) Run object. Must contain:\n* * `account`: Epicenter account id (**Team ID** for team projects, **User ID** for personal projects).\n* * `project`: Epicenter project id.\n* * `model`: The name of your primary model file. (See more on [Writing your Model](../../../writing_your_model/).)\n* * `scope`: (optional) Scope object for the run, for example `scope.group` with value of the name of the group.\n* * `server`: (optional) An object with one field, `host`. The value of `host` is the string `api.forio.com`, the URI of the Forio server. This is automatically set, but you can pass it explicitly if desired. It is most commonly used for clarity when you are [hosting an Epicenter project on your own server](../../../how_to/self_hosting/).\n* * `files`: (optional) If and only if you are using a Vensim model and you have additional data to pass in to your model, you can pass a `files` object with the names of the files, for example: `\"files\": {\"data\": \"myExtraData.xls\"}`. (Note that you'll also need to add this same files object to your Vensim [configuration file](../../../model_code/vensim/).) See the [underlying Model Run API](../../../rest_apis/other_apis/model_apis/run/#post-creating-a-new-run-for-this-project) for additional information.\n*\n* * `strategy`: (optional) Run creation strategy for when to create a new run and when to reuse an end user's existing run. See [Run Manager Strategies](../strategies/) for details. Defaults to `new-if-initialized`.\n*\n* * `sessionKey`: (optional) Name of browser cookie in which to store run information, including run id. Many conditional strategies, including the provided strategies, rely on this browser cookie to store the run id and help make the decision of whether to create a new run or use an existing one. The name of this cookie defaults to `epicenter-scenario` and can be set with the `sessionKey` parameter.\n*\n*\n* After instantiating a Run Manager, make a call to `getRun()` whenever you need to access a run for this end user. The `RunManager.run` contains the instantiated [Run Service](../run-api-service/). The Run Service allows you to access variables, call operations, etc.\n*\n* **Example**\n*\n* var rm = new F.manager.RunManager({\n* run: {\n* account: 'acme-simulations',\n* project: 'supply-chain-game',\n* model: 'supply-chain-model.jl',\n* server: { host: 'api.forio.com' }\n* },\n* strategy: 'always-new',\n* sessionKey: 'epicenter-session'\n* });\n* rm.getRun()\n* .then(function(run) {\n* // the return value of getRun() is a run object\n* var thisRunId = run.id;\n* // the RunManager.run also contains the instantiated Run Service,\n* // so any Run Service method is valid here\n* rm.run.do('runModel');\n* })\n*\n*/\n\n'use strict';\nvar strategiesMap = require('./run-strategies/strategies-map');\nvar specialOperations = require('./special-operations');\nvar RunService = require('../service/run-api-service');\n\n\nfunction patchRunService(service, manager) {\n if (service.patched) {\n return service;\n }\n\n var orig = service.do;\n service.do = function (operation, params, options) {\n var reservedOps = Object.keys(specialOperations);\n if (reservedOps.indexOf(operation) === -1) {\n return orig.apply(service, arguments);\n } else {\n return specialOperations[operation].call(service, params, options, manager);\n }\n };\n\n service.patched = true;\n\n return service;\n}\n\n\nvar defaults = {\n /**\n * Run creation strategy for when to create a new run and when to reuse an end user's existing run. See [Run Manager Strategies](../strategies/) for details. Defaults to `new-if-initialized`.\n * @type {String}\n */\n\n strategy: 'new-if-initialized'\n};\n\nfunction RunManager(options) {\n this.options = $.extend(true, {}, defaults, options);\n\n if (this.options.run instanceof RunService) {\n this.run = this.options.run;\n } else {\n this.run = new RunService(this.options.run);\n }\n\n patchRunService(this.run, this);\n\n var StrategyCtor = typeof this.options.strategy === 'function' ? this.options.strategy : strategiesMap[this.options.strategy];\n\n if (!StrategyCtor) {\n throw new Error('Specified run creation strategy was invalid:', this.options.strategy);\n }\n\n this.strategy = new StrategyCtor(this.run, this.options);\n}\n\nRunManager.prototype = {\n /**\n * Returns the run object for a 'good' run.\n *\n * A good run is defined by the strategy. For example, if the strategy is `always-new`, the call\n * to `getRun()` always returns a newly created run; if the strategy is `new-if-persisted`,\n * `getRun()` creates a new run if the previous run is in a persisted state, otherwise\n * it returns the previous run. See [Run Manager Strategies](../strategies/) for more on strategies.\n *\n * **Example**\n *\n * rm.getRun().then(function (run) {\n * // use the run object\n * var thisRunId = run.id;\n *\n * // use the Run Service object\n * rm.run.do('runModel');\n * });\n *\n * @return {$promise} Promise to complete the call.\n */\n getRun: function () {\n return this.strategy\n .getRun();\n },\n\n /**\n * Returns the run object for a new run, regardless of strategy: force creation of a new run.\n *\n * **Example**\n *\n * rm.reset().then(function (run) {\n * // use the (new) run object\n * var thisRunId = run.id;\n *\n * // use the Run Service object\n * rm.run.do('runModel');\n * });\n *\n * **Parameters**\n * @param {Object} runServiceOptions The options object to configure the Run Service. See [Run API Service](../run-api-service/) for more.\n * @return {Promise}\n */\n reset: function (runServiceOptions) {\n return this.strategy.reset(runServiceOptions);\n }\n};\n\nmodule.exports = RunManager;\n","/**\n * The `always-new` strategy always creates a new run for this end user irrespective of current state. This is equivalent to calling `F.service.Run.create()` from the [Run Service](../run-api-service/) every time. \n * \n * This strategy means that every time your end users refresh their browsers, they get a new run. \n * \n * This strategy can be useful for basic, single-page projects. This strategy is also useful for prototyping or project development: it creates a new run each time you refresh the page, and you can easily check the outputs of the model. However, typically you will use one of the other strategies for a production project.\n */\n\n'use strict';\n\nvar classFrom = require('../../util/inherit');\nvar ConditionalStrategy = require('./conditional-creation-strategy');\n\nvar __super = ConditionalStrategy.prototype;\n\nvar Strategy = classFrom(ConditionalStrategy, {\n constructor: function (runService, options) {\n __super.constructor.call(this, runService, this.createIf, options);\n },\n\n createIf: function (run, headers) {\n // always create a new run!\n return true;\n }\n});\n\nmodule.exports = Strategy;\n","'use strict';\n\nvar Base = require('./none-strategy');\nvar SessionManager = require('../../store/session-manager');\nvar classFrom = require('../../util/inherit');\nvar AuthManager = require('../auth-manager');\n\nvar keyNames = require('../key-names');\n\nvar defaults = {\n sessionKey: keyNames.STRATEGY_SESSION_KEY,\n path: ''\n};\n\nfunction setRunInSession(sessionKey, run, sessionManager) {\n sessionManager.getStore().set(sessionKey, JSON.stringify({ runId: run.id }));\n}\n\n/**\n* Conditional Creation Strategy\n* This strategy will try to get the run stored in the cookie and\n* evaluate if needs to create a new run by calling the 'condition' function\n*/\n\nvar Strategy = classFrom(Base, {\n constructor: function Strategy(runService, condition, options) {\n if (condition == null) { //eslint-disable-line\n //TODO: not sure why this is explicitly ==\n throw new Error('Conditional strategy needs a condition to create a run');\n }\n\n this._auth = new AuthManager();\n this.run = runService;\n this.condition = typeof condition !== 'function' ? function () { return condition; } : condition;\n this.options = $.extend(true, {}, defaults, options);\n this.sessionManager = new SessionManager(options);\n this.runOptions = this.options.run;\n },\n\n runOptionsWithScope: function () {\n var userSession = this._auth.getCurrentUserSessionInfo();\n return $.extend({\n scope: { group: userSession.groupName }\n }, this.runOptions);\n },\n\n reset: function (runServiceOptions) {\n var me = this;\n var opt = this.runOptionsWithScope();\n\n return this.run\n .create(opt, runServiceOptions)\n .then(function (run) {\n setRunInSession(me.options.sessionKey, run, me.sessionManager);\n run.freshlyCreated = true;\n return run;\n });\n },\n\n getRun: function () {\n var sessionStore = this.sessionManager.getStore();\n var runSession = JSON.parse(sessionStore.get(this.options.sessionKey));\n var me = this;\n if (runSession && runSession.runId) {\n return this._loadAndCheck(runSession).fail(function () {\n return me.reset(); //if it got the wrong cookie for e.g.\n });\n } else {\n return this.reset();\n }\n },\n\n _loadAndCheck: function (runSession) {\n var shouldCreate = false;\n var me = this;\n\n return this.run\n .load(runSession.runId, null, {\n success: function (run, msg, headers) {\n shouldCreate = me.condition(run, headers);\n }\n })\n .then(function (run) {\n if (shouldCreate) {\n var opt = me.runOptionsWithScope();\n return me.run.create(opt)\n .then(function (run) {\n setRunInSession(me.options.sessionKey, run, me.sessionManager);\n run.freshlyCreated = true;\n return run;\n });\n }\n return run;\n });\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `multiplayer` strategy is for use with [multiplayer worlds](../../../glossary/#world). It checks the current world for this end user, and always returns the current run for that world. This is equivalent to calling `getCurrentWorldForUser()` and then `getCurrentRunId()` from the [World API Adapater](../world-api-adapter/).\n * \n * Using this strategy means that end users in projects with multiplayer worlds always see the most current run and world. This ensures that they are in sync with the other end users sharing their world and run. In turn, this allows for competitive or collaborative multiplayer projects.\n */\n'use strict';\n\nvar classFrom = require('../../util/inherit');\n\nvar IdentityStrategy = require('./none-strategy');\nvar WorldApiAdapter = require('../../service/world-api-adapter');\nvar AuthManager = require('../auth-manager');\n\nvar defaults = {\n store: {\n synchronous: true\n }\n};\n\nvar Strategy = classFrom(IdentityStrategy, {\n\n constructor: function (runService, options) {\n this.runService = runService;\n this.options = $.extend(true, {}, defaults, options);\n this._auth = new AuthManager();\n this._loadRun = this._loadRun.bind(this);\n this.worldApi = new WorldApiAdapter(this.options.run);\n },\n\n reset: function () {\n var session = this._auth.getCurrentUserSessionInfo();\n var curUserId = session.userId;\n var curGroupName = session.groupName;\n\n return this.worldApi\n .getCurrentWorldForUser(curUserId, curGroupName)\n .then(function (world) {\n return this.worldApi.newRunForWorld(world.id);\n }.bind(this));\n },\n\n getRun: function () {\n var session = this._auth.getCurrentUserSessionInfo();\n var curUserId = session.userId;\n var curGroupName = session.groupName;\n var worldApi = this.worldApi;\n var model = this.options.model;\n var me = this;\n var dtd = $.Deferred();\n\n if (!curUserId) {\n return dtd.reject({ statusCode: 400, error: 'We need an authenticated user to join a multiplayer world. (ERR: no userId in session)' }, session).promise();\n }\n\n var loadRunFromWorld = function (world) {\n if (!world) {\n return dtd.reject({ statusCode: 404, error: 'The user is not in any world.' }, { options: me.options, session: session });\n }\n\n return worldApi.getCurrentRunId({ model: model, filter: world.id })\n .then(me._loadRun)\n .then(dtd.resolve)\n .fail(dtd.reject);\n };\n\n var serverError = function (error) {\n // is this possible?\n dtd.reject(error, session, me.options);\n };\n\n this.worldApi\n .getCurrentWorldForUser(curUserId, curGroupName)\n .then(loadRunFromWorld)\n .fail(serverError);\n\n return dtd.promise();\n },\n\n _loadRun: function (id, options) {\n return this.runService.load(id, null, options);\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `new-if-initialized` strategy creates a new run if the current one is in memory or has its `initialized` field set to `true`. The `initialized` field in the run record is automatically set to `true` at run creation for Vensim models; it can be set manually for other models.\n * \n * This strategy is useful if your project is structured such that immediately after a run is created, the model is executed completely (for example, a Vensim model is stepped to the end). It is similar to the `new-if-missing` strategy, except that it checks a field of the run record.\n * \n * Specifically, the strategy is:\n *\n * * Check the `sessionKey` cookie. \n * * This cookie is set by the [Run Manager](../run-manager/) and configurable through its options.\n * * If the cookie exists, check whether the run is in memory or only persisted in the database. Additionally, check whether the run's `initialized` field is `true`. \n * * If the run is in memory, create a new run.\n * * If the run's `initialized` field is `true`, create a new run.\n * * Otherwise, use the existing run.\n * * If the cookie does not exist, create a new run for this end user.\n */\n\n'use strict';\nvar classFrom = require('../../util/inherit');\nvar ConditionalStrategy = require('./conditional-creation-strategy');\n\nvar __super = ConditionalStrategy.prototype;\n\nvar Strategy = classFrom(ConditionalStrategy, {\n constructor: function (runService, options) {\n __super.constructor.call(this, runService, this.createIf, options);\n },\n\n createIf: function (run, headers) {\n return headers.getResponseHeader('pragma') === 'persistent' || run.initialized;\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `new-if-missing` strategy creates a new run when the current one is not in the browser cookie.\n * \n * Using this strategy means that when end users navigate between pages in your project, or refresh their browsers, they will still be working with the same run.\n *\n * This strategy is useful if your project is structured such that immediately after a run is created, the model is executed completely (for example, a Vensim model that is stepped to the end as soon as it is created). In other words, you care whether you have a run, but as long as you have one, you are certain that this run is the one you are interested in. \n * \n * Specifically, the strategy is:\n *\n * * Check the `sessionKey` cookie.\n * * This cookie is set by the [Run Manager](../run-manager/) and configurable through its options. \n * * If the cookie exists, use the run id stored there. \n * * If the cookie does not exist, create a new run for this end user. \n */\n\n'use strict';\n\nvar classFrom = require('../../util/inherit');\nvar ConditionalStrategy = require('./conditional-creation-strategy');\n\nvar __super = ConditionalStrategy.prototype;\n\n/*\n* create a new run only if nothing is stored in the cookie\n* this is useful for baseRuns.\n*/\nvar Strategy = classFrom(ConditionalStrategy, {\n constructor: function (runService, options) {\n __super.constructor.call(this, runService, this.createIf, options);\n },\n\n createIf: function (run, headers) {\n // if we are here, it means that the run exists... so we don't need a new one\n return false;\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `new-if-persisted` strategy creates a new run when the current one becomes persisted (end user is idle for a set period), but otherwise uses the current one. \n * \n * Using this strategy means that when end users navigate between pages in your project, or refresh their browsers, they will still be working with the same run. \n * \n * However, if they are idle for longer than your project's **Model Session Timeout** (configured in your project's [Settings](../../../updating_your_settings/)), then their run is persisted; the next time they interact with the project, they will get a new run. (See more background on [Run Persistence](../../../run_persistence/).)\n * \n * This strategy is useful for multi-page projects where end users play through a simulation in one sitting, stepping through the model sequentially (for example, a Vensim model that uses the `step` operation) or calling specific functions until the model is \"complete.\" However, you will need to guarantee that your end users will remain engaged with the project from beginning to end — or at least, if they are idle for longer than the **Model Session Timeout**, that it is okay for them to start the project from scratch (with an uninitialized model). \n * \n * Specifically, the strategy is:\n *\n * * Check the `sessionKey` cookie.\n * * This cookie is set by the [Run Manager](../run-manager/) and configurable through its options.\n * * If the cookie exists, check whether the run is in memory or only persisted in the database. \n * * If the run is in memory, use the run.\n * * If the run is only persisted (and not still in memory), create a new run for this end user.\n * * If the cookie does not exist, create a new run for this end user.\n */\n\n'use strict';\nvar classFrom = require('../../util/inherit');\nvar ConditionalStrategy = require('./conditional-creation-strategy');\n\nvar __super = ConditionalStrategy.prototype;\n\nvar Strategy = classFrom(ConditionalStrategy, {\n constructor: function (runService, options) {\n __super.constructor.call(this, runService, this.createIf, options);\n },\n\n createIf: function (run, headers) {\n return headers.getResponseHeader('pragma') === 'persistent';\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `none` strategy never returns a run or tries to create a new run. It simply returns the contents of the current [Run Service instance](../run-api-service/).\n * \n * This strategy is useful if you want to manually decide how to create your own runs and don't want any automatic assistance. \n * \n * Also, this strategy is necessary if you are working with a multiplayer project and using the [World Manager](../world-manager/) — or other, similar situations where you do not have direct control over creating the [Run Service](../run-api-service/) instance.\n */\n\n'use strict';\n\nvar classFrom = require('../../util/inherit');\nvar Base = {};\n\n// Interface that all strategies need to implement\nmodule.exports = classFrom(Base, {\n constructor: function (runService, options) {\n this.runService = runService;\n },\n\n reset: function () {\n // return a newly created run\n return $.Deferred().resolve().promise();\n },\n\n getRun: function () {\n // return a usable run\n return $.Deferred().resolve(this.runService).promise();\n }\n});\n","/**\n * The `persistent-single-player` strategy returns the latest (most recent) run for this user, whether it is in memory or not. If there are no runs for this user, it creates a new one.\n *\n * This strategy is useful if your project executes your model step by step (as opposed to a project where the model is executed completely, for example, a Vensim model that is immediately stepped to the end). It is useful if end users play with your project for an extended period of time, possibly over several sessions.\n *\n * Specifically, the strategy is:\n * \n * * Check if there are any runs for this end user.\n * * If there are no runs (either in memory or in the database), create a new one.\n * * If there are runs, take the latest (most recent) one.\n * * If the most recent run is currently in the database, bring it back into memory so that the end user can continue working with it. (See more background on [Run Persistence](../../../run_persistence/), or read more on the underlying [State API](../../../rest_apis/other_apis/model_apis/state/) for bringing runs from the database back into memory.) \n */\n\n'use strict';\n\nvar classFrom = require('../../util/inherit');\nvar IdentityStrategy = require('./none-strategy');\nvar StorageFactory = require('../../store/store-factory');\nvar StateApi = require('../../service/state-api-adapter');\nvar AuthManager = require('../auth-manager');\n\nvar keyNames = require('../key-names');\n\nvar defaults = {\n store: {\n synchronous: true\n }\n};\n\nvar Strategy = classFrom(IdentityStrategy, {\n constructor: function Strategy(runService, options) {\n this.run = runService;\n this.options = $.extend(true, {}, defaults, options);\n this.runOptions = this.options.run;\n this._store = new StorageFactory(this.options.store);\n this.stateApi = new StateApi();\n this._auth = new AuthManager();\n\n this._loadAndCheck = this._loadAndCheck.bind(this);\n this._restoreRun = this._restoreRun.bind(this);\n this._getAllRuns = this._getAllRuns.bind(this);\n this._loadRun = this._loadRun.bind(this);\n },\n\n reset: function (runServiceOptions) {\n var session = this._auth.getCurrentUserSessionInfo();\n var opt = $.extend({\n scope: { group: session.groupName }\n }, this.runOptions);\n\n return this.run\n .create(opt, runServiceOptions)\n .then(function (run) {\n run.freshlyCreated = true;\n return run;\n });\n },\n\n getRun: function () {\n return this._getAllRuns()\n .then(this._loadAndCheck);\n },\n\n _getAllRuns: function () {\n var session = JSON.parse(this._store.get(keyNames.EPI_SESSION_KEY) || '{}');\n return this.run.query({\n 'user.id': session.userId || '0000',\n 'scope.group': session.groupName\n });\n },\n\n _loadAndCheck: function (runs) {\n if (!runs || !runs.length) {\n return this.reset();\n }\n\n var dateComp = function (a, b) { return new Date(b.date) - new Date(a.date); };\n var latestRun = runs.sort(dateComp)[0];\n var me = this;\n var shouldReplay = false;\n\n return this.run.load(latestRun.id, null, {\n success: function (run, msg, headers) {\n shouldReplay = headers.getResponseHeader('pragma') === 'persistent';\n }\n }).then(function (run) {\n return shouldReplay ? me._restoreRun(run.id) : run;\n });\n },\n\n _restoreRun: function (runId) {\n var me = this;\n return this.stateApi.replay({ runId: runId })\n .then(function (resp) {\n return me._loadRun(resp.run);\n });\n },\n\n _loadRun: function (id, options) {\n return this.run.load(id, null, options);\n }\n\n});\n\nmodule.exports = Strategy;\n","module.exports = {\n 'new-if-initialized': require('./new-if-initialized-strategy'),\n 'new-if-persisted': require('./new-if-persisted-strategy'),\n 'new-if-missing': require('./new-if-missing-strategy'),\n 'always-new': require('./always-new-strategy'),\n multiplayer: require('./multiplayer-strategy'),\n 'persistent-single-player': require('./persistent-single-player-strategy'),\n none: require('./none-strategy')\n};\n","'use strict';\nvar RunService = require('../service/run-api-service');\n\nvar defaults = {\n validFilter: { saved: true }\n};\n\nfunction ScenarioManager(options) {\n this.options = $.extend(true, {}, defaults, options);\n this.runService = this.options.run || new RunService(this.options);\n}\n\nScenarioManager.prototype = {\n getRuns: function (filter) {\n this.filter = $.extend(true, {}, this.options.validFilter, filter);\n return this.runService.query(this.filter);\n },\n\n loadVariables: function (vars) {\n return this.runService.query(this.filter, { include: vars });\n },\n\n save: function (run, meta) {\n return this._getService(run).save($.extend(true, {}, { saved: true }, meta));\n },\n\n archive: function (run) {\n return this._getService(run).save({ saved: false });\n },\n\n _getService: function (run) {\n if (typeof run === 'string') {\n return new RunService($.extend(true, {}, this.options, { filter: run }));\n }\n\n if (typeof run === 'object' && run instanceof RunService) {\n return run;\n }\n\n throw new Error('Save method requires a run service or a runId');\n },\n\n getRun: function (runId) {\n return new RunService($.extend(true, {}, this.options, { filter: runId }));\n }\n};\n\nmodule.exports = ScenarioManager;\n\n","'use strict';\n\n\nmodule.exports = {\n reset: function (params, options, manager) {\n return manager.reset(options);\n }\n};\n","/**\n* ## World Manager\n*\n* As discussed under the [World API Adapter](../world-api-adapter/), a [run](../../../glossary/#run) is a collection of end user interactions with a project and its model. For building multiplayer simulations you typically want multiple end users to share the same set of interactions, and work within a common state. Epicenter allows you to create \"worlds\" to handle such cases.\n*\n* The World Manager provides an easy way to track and access the current world and run for particular end users. It is typically used in pages that end users will interact with. (The related [World API Adapter](../world-api-adapter/) handles creating multiplayer worlds, and adding and removing end users and runs from a world. Because of this, typically the World Adapter is used for facilitator pages in your project.)\n*\n* ### Using the World Manager\n*\n* To use the World Manager, instantiate it. Then, make calls to any of the methods you need.\n*\n* When you instantiate a World Manager, the world's account id, project id, and group are automatically taken from the session (thanks to the [Authentication Service](../auth-api-service)).\n*\n* Note that the World Manager does *not* create worlds automatically. (This is different than the [Run Manager](../run-manager).) However, you can pass in specific options to any runs created by the manager, using a `run` object.\n*\n* The parameters for creating a World Manager are:\n*\n* * `account`: The **Team ID** in the Epicenter user interface for this project.\n* * `project`: The **Project ID** for this project.\n* * `group`: The **Group Name** for this world.\n* * `run`: Options to use when creating new runs with the manager, e.g. `run: { files: ['data.xls'] }`.\n* * `run.model`: The name of the primary model file for this project. Required if you have not already passed it in as part of the `options` parameter for an enclosing call.\n*\n* For example:\n*\n* var wMgr = new F.manager.WorldManager({\n* account: 'acme-simulations',\n* project: 'supply-chain-game',\n* run: { model: 'supply-chain.py' },\n* group: 'team1'\n* });\n*\n* wMgr.getCurrentRun();\n*/\n\n'use strict';\n\nvar WorldApi = require('../service/world-api-adapter');\nvar RunManager = require('./run-manager');\nvar AuthManager = require('./auth-manager');\nvar worldApi;\n\nfunction buildStrategy(worldId, dtd) {\n\n return function Ctor(runService, options) {\n this.runService = runService;\n this.options = options;\n\n $.extend(this, {\n reset: function () {\n throw new Error('not implementd. Need api changes');\n },\n\n getRun: function () {\n var me = this;\n //get or create!\n // Model is required in the options\n var model = this.options.run.model || this.options.model;\n return worldApi.getCurrentRunId({ model: model, filter: worldId })\n .then(function (runId) {\n return me.runService.load(runId);\n })\n .then(function (run) {\n dtd.resolveWith(me, [run]);\n })\n .fail(dtd.reject);\n }\n }\n );\n };\n}\n\n\nmodule.exports = function (options) {\n this.options = options || { run: {}, world: {} };\n\n $.extend(true, this.options, this.options.run);\n $.extend(true, this.options, this.options.world);\n\n worldApi = new WorldApi(this.options);\n this._auth = new AuthManager();\n var me = this;\n\n var api = {\n\n /**\n * Returns the current world (object) and an instance of the [World API Adapter](../world-api-adapter/).\n *\n * **Example**\n *\n * wMgr.getCurrentWorld()\n * .then(function(world, worldAdapter) {\n * console.log(world.id);\n * worldAdapter.getCurrentRunId();\n * });\n *\n * **Parameters**\n * @param {string} userId (Optional) The id of the user whose world is being accessed. Defaults to the user in the current session.\n * @param {string} groupName (Optional) The name of the group whose world is being accessed. Defaults to the group for the user in the current session.\n * @return {Promise}\n */\n getCurrentWorld: function (userId, groupName) {\n var session = this._auth.getCurrentUserSessionInfo();\n if (!userId) {\n userId = session.userId;\n }\n if (!groupName) {\n groupName = session.groupName;\n }\n return worldApi.getCurrentWorldForUser(userId, groupName);\n },\n\n /**\n * Returns the current run (object) and an instance of the [Run API Service](../run-api-service/).\n *\n * **Example**\n *\n * wMgr.getCurrentRun({model: 'myModel.py'})\n * .then(function(run, runService) {\n * console.log(run.id);\n * runService.do('startGame');\n * });\n *\n * **Parameters**\n * @param {string} model (Optional) The name of the model file. Required if not already passed in as `run.model` when the World Manager is created.\n * @return {Promise}\n */\n getCurrentRun: function (model) {\n var dtd = $.Deferred();\n var session = this._auth.getCurrentUserSessionInfo();\n var curUserId = session.userId;\n var curGroupName = session.groupName;\n\n function getAndRestoreLatestRun(world) {\n if (!world) {\n return dtd.reject({ error: 'The user is not part of any world!' });\n }\n\n var currentWorldId = world.id;\n var runOpts = $.extend(true, me.options, { model: model });\n var strategy = buildStrategy(currentWorldId, dtd);\n var opt = $.extend(true, {}, {\n strategy: strategy,\n run: runOpts\n });\n var rm = new RunManager(opt);\n\n return rm.getRun()\n .then(function (run) {\n dtd.resolve(run, rm.runService, rm);\n });\n }\n\n this.getCurrentWorld(curUserId, curGroupName)\n .then(getAndRestoreLatestRun);\n\n return dtd.promise();\n }\n };\n\n $.extend(this, api);\n};\n","/**\n * ## File API Service\n *\n * The File API Service allows you to upload and download files directly onto Epicenter, analogous to using the File Manager UI in Epicenter directly or SFTPing files in. It is based on the Epicenter File API.\n *\n * The Asset API Service (https://forio.com/epicenter/docs/public/api_adapters/generated/asset-api-adapter/) is typically used for all project use cases, and it's unlikely this File Service will be used directly except by Admin tools (e.g. Flow Inspector).\n *\n * Partially implemented.\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to undefined.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The project id. Defaults to undefined.\n * @type {String}\n */\n project: undefined,\n\n /**\n * The folder type. One of `model` | `static` | `node`.\n * @type {String}\n */\n folderType: 'static',\n\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {Object}\n */\n transport: {}\n };\n\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n if (serviceOptions.account) {\n urlConfig.accountPath = serviceOptions.account;\n }\n if (serviceOptions.project) {\n urlConfig.projectPath = serviceOptions.project;\n }\n\n var httpOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath('file')\n });\n\n if (serviceOptions.token) {\n httpOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(httpOptions);\n\n function uploadBody(fileName, contents) {\n var boundary = '---------------------------7da24f2e50046';\n\n return {\n body: '--' + boundary + '\\r\\n' +\n 'Content-Disposition: form-data; name=\"file\";' +\n 'filename=\"' + fileName + '\"\\r\\n' +\n 'Content-type: text/html\\r\\n\\r\\n' +\n contents + '\\r\\n' +\n '--' + boundary + '--',\n boundary: boundary\n };\n }\n\n function uploadFileOptions(filePath, contents, options) {\n filePath = filePath.split('/');\n var fileName = filePath.pop();\n filePath = filePath.join('/');\n var path = serviceOptions.folderType + '/' + filePath;\n var upload = uploadBody(fileName, contents);\n\n return $.extend(true, {}, serviceOptions, options, {\n url: urlConfig.getAPIPath('file') + path,\n data: upload.body,\n contentType: 'multipart/form-data; boundary=' + upload.boundary\n });\n }\n\n var publicAsyncAPI = {\n /**\n * Get a directory listing, or contents of a file.\n * @param {String} filePath Path to the file\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n getContents: function (filePath, options) {\n var path = serviceOptions.folderType + '/' + filePath;\n var httpOptions = $.extend(true, {}, serviceOptions, options, {\n url: urlConfig.getAPIPath('file') + path\n });\n return http.get('', httpOptions);\n },\n\n /**\n * Replaces the file at the given file path.\n * @param {String} filePath Path to the file\n * @param {String} contents Contents to write to file\n * @param {Object} options (Optional) Overrides for configuration options\n * @return {Promise}\n */\n replace: function (filePath, contents, options) {\n var httpOptions = uploadFileOptions(filePath, contents, options);\n\n return http.put(httpOptions.data, httpOptions);\n },\n\n /**\n * Creates a file in the given file path.\n * @param {String} filePath Path to the file\n * @param {String} contents Contents to write to file\n * @param {Boolean} replaceExisting Replace file if it already exists; defaults to false\n * @param {Object} options (Optional) Overrides for configuration options\n * @return {Promise}\n */\n create: function (filePath, contents, replaceExisting, options) {\n var httpOptions = uploadFileOptions(filePath, contents, options);\n var prom = http.post(httpOptions.data, httpOptions);\n var me = this;\n if (replaceExisting === true) {\n prom = prom.then(null, function (xhr) {\n var conflictStatus = 409;\n if (xhr.status === conflictStatus) {\n return me.replace(filePath, contents, options);\n }\n });\n }\n return prom;\n },\n\n /**\n * Removes the file.\n * @param {String} filePath Path to the file\n * @param {Object} options (Optional) Overrides for configuration options\n * @return {Promise}\n */\n remove: function (filePath, options) {\n var path = serviceOptions.folderType + '/' + filePath;\n var httpOptions = $.extend(true, {}, serviceOptions, options, {\n url: urlConfig.getAPIPath('file') + path\n });\n return http.delete(null, httpOptions);\n },\n\n /**\n * Renames the file.\n * @param {String} filePath Path to the file\n * @param {String} newName New name of file\n * @param {Object} options (Optional) Overrides for configuration options\n * @return {Promise}\n */\n rename: function (filePath, newName, options) {\n var path = serviceOptions.folderType + '/' + filePath;\n var httpOptions = $.extend(true, {}, serviceOptions, options, {\n url: urlConfig.getAPIPath('file') + path\n });\n return http.patch({ name: newName }, httpOptions);\n }\n };\n\n $.extend(this, publicAsyncAPI);\n};\n","/**\n * ## Asset API Adapter\n *\n * The Asset API Adapter allows you to store assets -- resources or files of any kind -- used by a project with a scope that is specific to project, group, or end user.\n *\n * Assets are used with [team projects](../../../project_admin/#team). One common use case is having end users in a [group](../../../glossary/#groups) or in a [multiplayer world](../../../glossary/#world) upload data -- videos created during game play, profile pictures for customizing their experience, etc. -- as part of playing through the project.\n *\n * Resources created using the Asset Adapter are scoped:\n *\n * * Project assets are writable only by [team members](../../../glossary/#team), that is, Epicenter authors.\n * * Group assets are writable by anyone with access to the project that is part of that particular [group](../../../glossary/#groups). This includes all [team members](../../../glossary/#team) (Epicenter authors) and any [end users](../../../glossary/#users) who are members of the group -- both facilitators and standard end users.\n * * User assets are writable by the specific end user, and by the facilitator of the group.\n * * All assets are readable by anyone with the exact URI.\n *\n * To use the Asset Adapter, instantiate it and then access the methods provided. Instantiating requires the account id (**Team ID** in the Epicenter user interface) and project id (**Project ID**). The group name is required for assets with a group scope, and the group name and userId are required for assets with a user scope. If not included, they are taken from the logged in user's session information if needed.\n *\n * When creating an asset, you can pass in text (encoded data) to the `create()` call. Alternatively, you can make the `create()` call as part of an HTML form and pass in a file uploaded via the form.\n *\n * // instantiate the Asset Adapter\n * var aa = new F.service.Asset({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1',\n * userId: '12345'\n * });\n *\n * // create a new asset using encoded text\n * aa.create('test.txt', {\n * encoding: 'BASE_64',\n * data: 'VGhpcyBpcyBhIHRlc3QgZmlsZS4=',\n * contentType: 'text/plain'\n * }, { scope: 'user' });\n *\n * // alternatively, create a new asset using a file uploaded through a form\n * // this sample code goes with an html form that looks like this:\n * //\n * //
\n * // \n * // \n * // \n * //
\n * //\n * $('#upload-file').on('submit', function (e) {\n * e.preventDefault();\n * var filename = $('#filename').val();\n * var data = new FormData();\n * var inputControl = $('#file')[0];\n * data.append('file', inputControl.files[0], filename);\n *\n * aa.create(filename, data, { scope: 'user' });\n * });\n *\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar _pick = require('../util/object-util')._pick;\nvar SessionManager = require('../store/session-manager');\n\nvar apiEndpoint = 'asset';\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects). If left undefined, taken from the URL.\n * @type {String}\n */\n account: undefined,\n /**\n * The project id. If left undefined, taken from the URL.\n * @type {String}\n */\n project: undefined,\n /**\n * The group name. Defaults to session's `groupName`.\n * @type {String}\n */\n group: undefined,\n /**\n * The user id. Defaults to session's `userId`.\n * @type {String}\n */\n userId: undefined,\n /**\n * The scope for the asset. Valid values are: `user`, `group`, and `project`. See above for the required permissions to write to each scope. Defaults to `user`, meaning the current end user or a facilitator in the end user's group can edit the asset.\n * @type {String}\n */\n scope: 'user',\n /**\n * Determines if a request to list the assets in a scope includes the complete URL for each asset (`true`), or only the file names of the assets (`false`). Defaults to `true`.\n * @type {boolean}\n */\n fullUrl: true,\n /**\n * The transport object contains the options passed to the XHR request.\n * @type {object}\n */\n transport: {\n processData: false\n }\n };\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n if (!serviceOptions.account) {\n serviceOptions.account = urlConfig.accountPath;\n }\n\n if (!serviceOptions.project) {\n serviceOptions.project = urlConfig.projectPath;\n }\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n\n var http = new TransportFactory(transportOptions);\n\n var assetApiParams = ['encoding', 'data', 'contentType'];\n var scopeConfig = {\n user: ['scope', 'account', 'project', 'group', 'userId'],\n group: ['scope', 'account', 'project', 'group'],\n project: ['scope', 'account', 'project'],\n };\n\n var validateFilename = function (filename) {\n if (!filename) {\n throw new Error('filename is needed.');\n }\n };\n\n var validateUrlParams = function (options) {\n var partKeys = scopeConfig[options.scope];\n if (!partKeys) {\n throw new Error('scope parameter is needed.');\n }\n\n $.each(partKeys, function () {\n if (!options[this]) {\n throw new Error(this + ' parameter is needed.');\n }\n });\n };\n\n var buildUrl = function (filename, options) {\n validateUrlParams(options);\n var partKeys = scopeConfig[options.scope];\n var parts = $.map(partKeys, function (key) {\n return options[key];\n });\n if (filename) {\n // This prevents adding a trailing / in the URL as the Asset API\n // does not work correctly with it\n filename = '/' + filename;\n }\n return urlConfig.getAPIPath(apiEndpoint) + parts.join('/') + filename;\n };\n\n // Private function, all requests follow a more or less same approach to\n // use the Asset API and the difference is the HTTP verb\n //\n // @param {string} method` (Required) HTTP verb\n // @param {string} filename` (Required) Name of the file to delete/replace/create\n // @param {object} params` (Optional) Body parameters to send to the Asset API\n // @param {object} options` (Optional) Options object to override global options.\n var upload = function (method, filename, params, options) {\n validateFilename(filename);\n // make sure the parameter is clean\n method = method.toLowerCase();\n var contentType = params instanceof FormData === true ? false : 'application/json';\n if (contentType === 'application/json') {\n // whitelist the fields that we actually can send to the api\n params = _pick(params, assetApiParams);\n } else { // else we're sending form data which goes directly in request body\n // For multipart/form-data uploads the filename is not set in the URL,\n // it's getting picked by the FormData field filename.\n filename = method === 'post' || method === 'put' ? '' : filename;\n }\n var urlOptions = $.extend({}, serviceOptions, options);\n var url = buildUrl(filename, urlOptions);\n var createOptions = $.extend(true, {}, urlOptions, { url: url, contentType: contentType });\n\n return http[method](params, createOptions);\n };\n\n var publicAPI = {\n /**\n * Creates a file in the Asset API. The server returns an error (status code `409`, conflict) if the file already exists, so\n * check first with a `list()` or a `get()`.\n *\n * **Example**\n *\n * var aa = new F.service.Asset({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1',\n * userId: ''\n * });\n *\n * // create a new asset using encoded text\n * aa.create('test.txt', {\n * encoding: 'BASE_64',\n * data: 'VGhpcyBpcyBhIHRlc3QgZmlsZS4=',\n * contentType: 'text/plain'\n * }, { scope: 'user' });\n *\n * // alternatively, create a new asset using a file uploaded through a form\n * // this sample code goes with an html form that looks like this:\n * //\n * //
\n * // \n * // \n * // \n * //
\n * //\n * $('#upload-file').on('submit', function (e) {\n * e.preventDefault();\n * var filename = $('#filename').val();\n * var data = new FormData();\n * var inputControl = $('#file')[0];\n * data.append('file', inputControl.files[0], filename);\n *\n * aa.create(filename, data, { scope: 'user' });\n * });\n *\n *\n * **Parameters**\n * @param {string} filename (Required) Name of the file to create.\n * @param {object} params (Optional) Body parameters to send to the Asset API. Required if the `options.transport.contentType` is `application/json`, otherwise ignored.\n * @param {string} params.encoding Either `HEX` or `BASE_64`. Required if `options.transport.contentType` is `application/json`.\n * @param {string} params.data The encoded data for the file. Required if `options.transport.contentType` is `application/json`.\n * @param {string} params.contentType The mime type of the file. Optional.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n create: function (filename, params, options) {\n return upload('post', filename, params, options);\n },\n\n /**\n * Gets a file from the Asset API, fetching the asset content. (To get a list\n * of the assets in a scope, use `list()`.)\n *\n * **Parameters**\n * @param {string} filename (Required) Name of the file to retrieve.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n get: function (filename, options) {\n var getServiceOptions = _pick(serviceOptions, ['scope', 'account', 'project', 'group', 'userId']);\n var urlOptions = $.extend({}, getServiceOptions, options);\n var url = buildUrl(filename, urlOptions);\n var getOptions = $.extend(true, {}, urlOptions, { url: url });\n\n return http.get({}, getOptions);\n },\n\n /**\n * Gets the list of the assets in a scope.\n *\n * **Example**\n *\n * aa.list({ fullUrl: true }).then(function(fileList){\n * console.log('array of files = ', fileList);\n * });\n *\n * **Parameters**\n * @param {object} options (Optional) Options object to override global options.\n * @param {string} options.scope (Optional) The scope (`user`, `group`, `project`).\n * @param {boolean} options.fullUrl (Optional) Determines if the list of assets in a scope includes the complete URL for each asset (`true`), or only the file names of the assets (`false`).\n * @return {Promise}\n */\n list: function (options) {\n var dtd = $.Deferred();\n var me = this;\n var urlOptions = $.extend({}, serviceOptions, options);\n var url = buildUrl('', urlOptions);\n var getOptions = $.extend(true, {}, urlOptions, { url: url });\n var fullUrl = getOptions.fullUrl;\n\n if (!fullUrl) {\n return http.get({}, getOptions);\n }\n\n http.get({}, getOptions)\n .then(function (files) {\n var fullPathFiles = $.map(files, function (file) {\n return buildUrl(file, urlOptions);\n });\n dtd.resolveWith(me, [fullPathFiles]);\n })\n .fail(dtd.reject);\n\n return dtd.promise();\n },\n\n /**\n * Replaces an existing file in the Asset API.\n *\n * **Example**\n *\n * // replace an asset using encoded text\n * aa.replace('test.txt', {\n * encoding: 'BASE_64',\n * data: 'VGhpcyBpcyBhIHNlY29uZCB0ZXN0IGZpbGUu',\n * contentType: 'text/plain'\n * }, { scope: 'user' });\n *\n * // alternatively, replace an asset using a file uploaded through a form\n * // this sample code goes with an html form that looks like this:\n * //\n * //
\n * // \n * // \n * // \n * //
\n * //\n * $('#replace-file').on('submit', function (e) {\n * e.preventDefault();\n * var filename = $('#replace-filename').val();\n * var data = new FormData();\n * var inputControl = $('#file')[0];\n * data.append('file', inputControl.files[0], filename);\n *\n * aa.replace(filename, data, { scope: 'user' });\n * });\n *\n * **Parameters**\n * @param {string} filename (Required) Name of the file being replaced.\n * @param {object} params (Optional) Body parameters to send to the Asset API. Required if the `options.transport.contentType` is `application/json`, otherwise ignored.\n * @param {string} params.encoding Either `HEX` or `BASE_64`. Required if `options.transport.contentType` is `application/json`.\n * @param {string} params.data The encoded data for the file. Required if `options.transport.contentType` is `application/json`.\n * @param {string} params.contentType The mime type of the file. Optional.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n replace: function (filename, params, options) {\n return upload('put', filename, params, options);\n },\n\n /**\n * Deletes a file from the Asset API.\n *\n * **Example**\n *\n * aa.delete(sampleFileName);\n *\n * **Parameters**\n * @param {string} filename (Required) Name of the file to delete.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n delete: function (filename, options) {\n return upload('delete', filename, {}, options);\n },\n\n assetUrl: function (filename, options) {\n var urlOptions = $.extend({}, serviceOptions, options);\n return buildUrl(filename, urlOptions);\n }\n };\n $.extend(this, publicAPI);\n};\n","/**\n *\n * ## Authentication API Service\n *\n * The Authentication API Service provides a method for logging in, which creates and returns a user access token.\n *\n * User access tokens are required for each call to Epicenter. (See [Project Access](../../../project_access/) for more information.)\n *\n * If you need additional functionality -- such as tracking session information, easily retrieving the user token, or getting the groups to which an end user belongs -- consider using the [Authorization Manager](../auth-manager/) instead.\n *\n * var auth = new F.service.Auth();\n * auth.login({ userName: 'jsmith@acmesimulations.com',\n * password: 'passw0rd' });\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * Email or username to use for logging in. Defaults to empty string.\n * @type {String}\n */\n userName: '',\n\n /**\n * Password for specified `userName`. Defaults to empty string.\n * @type {String}\n */\n password: '',\n\n /**\n * The account id for this `userName`. In the Epicenter UI, this is the **Team ID** (for team projects) or the **User ID** (for personal projects). Required if the `userName` is for an [end user](../../../glossary/#users). Defaults to empty string.\n * @type {String}\n */\n account: '',\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {Object}\n */\n transport: {}\n };\n var serviceOptions = $.extend({}, defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath('authentication')\n });\n var http = new TransportFactory(transportOptions);\n\n var publicAPI = {\n\n /**\n * Logs user in, returning the user access token.\n *\n * If no `userName` or `password` were provided in the initial configuration options, they are required in the `options` here. If no `account` was provided in the initial configuration options and the `userName` is for an [end user](../../../glossary/#users), the `account` is required as well.\n *\n * **Example**\n *\n * auth.login({\n * userName: 'jsmith',\n * password: 'passw0rd',\n * account: 'acme-simulations' })\n * .then(function (token) {\n * console.log(\"user access token is: \", token.access_token);\n * });\n *\n * **Parameters**\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n login: function (options) {\n var httpOptions = $.extend(true, { success: $.noop }, serviceOptions, options);\n if (!httpOptions.userName || !httpOptions.password) {\n var resp = { status: 401, statusMessage: 'No username or password specified.' };\n if (options.error) {\n options.error.call(this, resp);\n }\n\n return $.Deferred().reject(resp).promise();\n }\n\n var postParams = {\n userName: httpOptions.userName,\n password: httpOptions.password,\n };\n if (httpOptions.account) {\n //pass in null for account under options if you don't want it to be sent\n postParams.account = httpOptions.account;\n }\n\n return http.post(postParams, httpOptions);\n },\n\n // (replace with /* */ comment block, to make visible in docs, once this is more than a noop)\n //\n // Logs user out from specified accounts.\n //\n // Epicenter logout is not implemented yet, so for now this is a dummy promise that gets automatically resolved.\n //\n // **Example**\n //\n // auth.logout();\n //\n // **Parameters**\n // @param {Object} `options` (Optional) Overrides for configuration options.\n //\n logout: function (options) {\n var dtd = $.Deferred();\n dtd.resolve();\n return dtd.promise();\n }\n };\n\n $.extend(this, publicAPI);\n};\n","/**\n * ## Channel Service\n *\n * The Epicenter platform provides a push channel, which allows you to publish and subscribe to messages within a [project](../../../glossary/#projects), [group](../../../glossary/#groups), or [multiplayer world](../../../glossary/#world). There are two main use cases for the channel: event notifications and chat messages.\n *\n * The Channel Service is a building block for this functionality. It creates a publish-subscribe object, allowing you to publish messages, subscribe to messages, or unsubscribe from messages for a given 'topic' on a `$.cometd` transport instance.\n *\n * Typically, you use the [Epicenter Channel Manager](../epicenter-channel-manager/) to create or retrieve channels, then use the Channel Service `subscribe()` and `publish()` methods to listen to or update data. (For additional background on Epicenter's push channel, see the introductory notes on the [Push Channel API](../../../rest_apis/multiplayer/channel/) page.)\n *\n * You'll need to include the `epicenter-multiplayer-dependencies.js` library in addition to the `epicenter.js` library in your project to use the Channel Service. See [Including Epicenter.js](../../#include).\n *\n * To use the Channel Service, instantiate it, then make calls to any of the methods you need.\n *\n * var cs = new F.service.Channel();\n * cs.publish('/acme-simulations/supply-chain-game/fall-seminar/run/variables', { price: 50 });\n *\n * The parameters for instantiating a Channel Service include:\n *\n * * `options` The options object to configure the Channel Service.\n * * `options.base` The base topic. This is added as a prefix to all further topics you publish or subscribe to while working with this Channel Service.\n * * `options.topicResolver` A function that processes all 'topics' passed into the `publish` and `subscribe` methods. This is useful if you want to implement your own serialize functions for converting custom objects to topic names. Returns a String. By default, it just echoes the topic.\n * * `options.transport` The instance of `$.cometd` to hook onto. See http://docs.cometd.org/reference/javascript.html for additional background on cometd.\n */\n\n'use strict';\nvar Channel = function (options) {\n var defaults = {\n\n /**\n * The base topic. This is added as a prefix to all further topics you publish or subscribe to while working with this Channel Service.\n * @type {string}\n */\n base: '',\n\n /**\n * A function that processes all 'topics' passed into the `publish` and `subscribe` methods. This is useful if you want to implement your own serialize functions for converting custom objects to topic names. By default, it just echoes the topic.\n *\n * **Parameters**\n *\n * * `topic` Topic to parse.\n *\n * **Return Value**\n *\n * * *String*: This function should return a string topic.\n *\n * @type {function}\n * @param {String} topic topic to resolve\n * @return {String}\n */\n topicResolver: function (topic) {\n return topic;\n },\n\n /**\n * The instance of `$.cometd` to hook onto.\n * @type {object}\n */\n transport: null\n };\n this.channelOptions = $.extend(true, {}, defaults, options);\n};\n\nvar makeName = function (channelName, topic) {\n //Replace trailing/double slashes\n var newName = (channelName ? (channelName + '/' + topic) : topic).replace(/\\/\\//g, '/').replace(/\\/$/, '');\n return newName;\n};\n\n\nChannel.prototype = $.extend(Channel.prototype, {\n\n // future functionality:\n // // Set the context for the callback\n // cs.subscribe('run', function () { this.innerHTML = 'Triggered'}, document.body);\n //\n // // Control the order of operations by setting the `priority`\n // cs.subscribe('run', cb, this, {priority: 9});\n //\n // // Only execute the callback, `cb`, if the value of the `price` variable is 50\n // cs.subscribe('run/variables/price', cb, this, {priority: 30, value: 50});\n //\n // // Only execute the callback, `cb`, if the value of the `price` variable is greater than 50\n // subscribe('run/variables/price', cb, this, {priority: 30, value: '>50'});\n //\n // // Only execute the callback, `cb`, if the value of the `price` variable is even\n // subscribe('run/variables/price', cb, this, {priority: 30, value: function (val) {return val % 2 === 0}});\n\n\n /**\n * Subscribe to changes on a topic.\n *\n * The topic should include the full path of the account id (**Team ID** for team projects), project id, and group name. (In most cases, it is simpler to use the [Epicenter Channel Manager](../epicenter-channel-manager/) instead, in which case this is configured for you.)\n *\n * **Examples**\n *\n * var cb = function(val) { console.log(val.data); };\n *\n * // Subscribe to changes on a top-level 'run' topic\n * cs.subscribe('/acme-simulations/supply-chain-game/fall-seminar/run', cb);\n *\n * // Subscribe to changes on children of the 'run' topic. Note this will also be triggered for changes to run.x.y.z.\n * cs.subscribe('/acme-simulations/supply-chain-game/fall-seminar/run/*', cb);\n *\n * // Subscribe to changes on both the top-level 'run' topic and its children\n * cs.subscribe(['/acme-simulations/supply-chain-game/fall-seminar/run',\n * '/acme-simulations/supply-chain-game/fall-seminar/run/*'], cb);\n *\n * // Subscribe to changes on a particular variable\n * subscribe('/acme-simulations/supply-chain-game/fall-seminar/run/variables/price', cb);\n *\n *\n * **Return Value**\n *\n * * *String* Returns a token you can later use to unsubscribe.\n *\n * **Parameters**\n * @param {String|Array} topic List of topics to listen for changes on.\n * @param {Function} callback Callback function to execute. Callback is called with signature `(evt, payload, metadata)`.\n * @param {Object} context Context in which the `callback` is executed.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @param {Number} options.priority Used to control order of operations. Defaults to 0. Can be any +ve or -ve number.\n * @param {String|Number|Function} options.value The `callback` is only triggered if this condition matches. See examples for details.\n * @return {string} Subscription ID\n */\n subscribe: function (topic, callback, context, options) {\n\n var topics = [].concat(topic);\n var me = this;\n var subscriptionIds = [];\n var opts = me.channelOptions;\n\n opts.transport.batch(function () {\n $.each(topics, function (index, topic) {\n topic = makeName(opts.base, opts.topicResolver(topic));\n subscriptionIds.push(opts.transport.subscribe(topic, callback));\n });\n });\n return (subscriptionIds[1] ? subscriptionIds : subscriptionIds[0]);\n },\n\n /**\n * Publish data to a topic.\n *\n * **Examples**\n *\n * // Send data to all subscribers of the 'run' topic\n * cs.publish('/acme-simulations/supply-chain-game/fall-seminar/run', { completed: false });\n *\n * // Send data to all subscribers of the 'run/variables' topic\n * cs.publish('/acme-simulations/supply-chain-game/fall-seminar/run/variables', { price: 50 });\n *\n * **Parameters**\n *\n * @param {String} topic Topic to publish to.\n * @param {*} data Data to publish to topic.\n * @return {Array | Object} Responses to published data\n *\n */\n publish: function (topic, data) {\n var topics = [].concat(topic);\n var me = this;\n var returnObjs = [];\n var opts = me.channelOptions;\n\n\n opts.transport.batch(function () {\n $.each(topics, function (index, topic) {\n topic = makeName(opts.base, opts.topicResolver(topic));\n if (topic.charAt(topic.length - 1) === '*') {\n topic = topic.replace(/\\*+$/, '');\n console.warn('You can cannot publish to channels with wildcards. Publishing to ', topic, 'instead');\n }\n returnObjs.push(opts.transport.publish(topic, data));\n });\n });\n return (returnObjs[1] ? returnObjs : returnObjs[0]);\n },\n\n /**\n * Unsubscribe from changes to a topic.\n *\n * **Example**\n *\n * cs.unsubscribe('sampleToken');\n *\n * **Parameters**\n * @param {String} token The token for topic is returned when you initially subscribe. Pass it here to unsubscribe from that topic.\n * @return {Object} reference to current instance\n */\n unsubscribe: function (token) {\n this.channelOptions.transport.unsubscribe(token);\n return this;\n },\n\n /**\n * Start listening for events on this instance. Signature is same as for jQuery Events: http://api.jquery.com/on/.\n *\n * Supported events are: `connect`, `disconnect`, `subscribe`, `unsubscribe`, `publish`, `error`.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/on/.\n */\n on: function (event) {\n $(this).on.apply($(this), arguments);\n },\n\n /**\n * Stop listening for events on this instance. Signature is same as for jQuery Events: http://api.jquery.com/off/.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/off/.\n */\n off: function (event) {\n $(this).off.apply($(this), arguments);\n },\n\n /**\n * Trigger events and execute handlers. Signature is same as for jQuery Events: http://api.jquery.com/trigger/.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/trigger/.\n */\n trigger: function (event) {\n $(this).trigger.apply($(this), arguments);\n }\n\n});\n\nmodule.exports = Channel;\n","/**\n * @class ConfigurationService\n *\n * All services take in a configuration settings object to configure themselves. A JS hash {} is a valid configuration object, but optionally you can use the configuration service to toggle configs based on the environment\n *\n * @example\n * var cs = require('configuration-service')({\n * dev: { //environment\n port: 3000,\n host: 'localhost',\n },\n prod: {\n port: 8080,\n host: 'api.forio.com',\n logLevel: 'none'\n },\n logLevel: 'DEBUG' //global\n * });\n *\n * cs.get('logLevel'); //returns 'DEBUG'\n *\n * cs.setEnv('dev');\n * cs.get('logLevel'); //returns 'DEBUG'\n *\n * cs.setEnv('prod');\n * cs.get('logLevel'); //returns 'none'\n *\n */\n\n'use strict';\nvar urlService = require('./url-config-service');\n\nmodule.exports = function (config) {\n //TODO: Environments\n var defaults = {\n logLevel: 'NONE'\n };\n var serviceOptions = $.extend({}, defaults, config);\n serviceOptions.server = urlService(serviceOptions.server);\n\n return {\n\n data: serviceOptions,\n\n /**\n * Set the environment key to get configuration options from\n * @param { string} env\n */\n setEnv: function (env) {\n\n },\n\n /**\n * Get configuration.\n * @param { string} property optional\n * @return {*} Value of property if specified, the entire config object otherwise\n */\n get: function (property) {\n return serviceOptions[property];\n },\n\n /**\n * Set configuration.\n * @param { string|Object} key if a key is provided, set a key to that value. Otherwise merge object with current config\n * @param {*} value value for provided key\n */\n set: function (key, value) {\n serviceOptions[key] = value;\n }\n };\n};\n\n","/**\n * ## Data API Service\n *\n * The Data API Service allows you to create, access, and manipulate data related to any of your projects. Data are organized in collections. Each collection contains a document; each element of this top-level document is a JSON object. (See additional information on the underlying [Data API](../../../rest_apis/data_api/).)\n *\n * All API calls take in an \"options\" object as the last parameter. The options can be used to extend/override the Data API Service defaults. In particular, there are three required parameters when you instantiate the Data Service:\n *\n * * `account`: Epicenter account id (**Team ID** for team projects, **User ID** for personal projects).\n * * `project`: Epicenter project id.\n * * `root`: The the name of the collection. If you have multiple collections within each of your projects, you can also pass the collection name as an option for each call.\n *\n * var ds = new F.service.Data({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * root: 'survey-responses',\n * server: { host: 'api.forio.com' }\n * });\n * ds.saveAs('user1',\n * { 'question1': 2, 'question2': 10,\n * 'question3': false, 'question4': 'sometimes' } );\n * ds.saveAs('user2',\n * { 'question1': 3, 'question2': 8,\n * 'question3': true, 'question4': 'always' } );\n * ds.query('',{ 'question2': { '$gt': 9} });\n *\n * Note that in addition to the `account`, `project`, and `root`, the Data Service parameters optionally include a `server` object, whose `host` field contains the URI of the Forio server. This is automatically set, but you can pass it explicitly if desired. It is most commonly used for clarity when you are [hosting an Epicenter project on your own server](../../../how_to/self_hosting/).\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar qutil = require('../util/query-util');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * Name of collection. Required. Defaults to `/`, that is, the root level of your project at `forio.com/app/your-account-id/your-project-id/`, but must be set to a collection name.\n * @type {String}\n */\n root: '/',\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The project id. Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n project: undefined,\n\n /**\n * For operations that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n\n //Options to pass on to the underlying transport layer\n transport: {}\n };\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n\n var urlConfig = new ConfigService(serviceOptions).get('server');\n if (serviceOptions.account) {\n urlConfig.accountPath = serviceOptions.account;\n }\n if (serviceOptions.project) {\n urlConfig.projectPath = serviceOptions.project;\n }\n\n var getURL = function (key, root) {\n if (!root) {\n root = serviceOptions.root;\n }\n var url = urlConfig.getAPIPath('data') + qutil.addTrailingSlash(root);\n if (key) {\n url += qutil.addTrailingSlash(key);\n }\n return url;\n };\n\n var httpOptions = $.extend(true, {}, serviceOptions.transport, {\n url: getURL\n });\n if (serviceOptions.token) {\n httpOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(httpOptions);\n\n var publicAPI = {\n\n /**\n * Search for data within a collection.\n *\n * Searching using comparison or logical operators (as opposed to exact matches) requires MongoDB syntax. See the underlying [Data API](../../../rest_apis/data_api/#searching) for additional details.\n *\n * **Examples**\n *\n * // request all data associated with document 'user1'\n * ds.query('user1');\n *\n * // exact matching:\n * // request all documents in collection where 'question2' is 9\n * ds.query('', { 'question2': 9});\n *\n * // comparison operators:\n * // request all documents in collection\n * // where 'question2' is greater than 9\n * ds.query('', { 'question2': { '$gt': 9} });\n *\n * // logical operators:\n * // request all documents in collection\n * // where 'question2' is less than 10, and 'question3' is false\n * ds.query('', { '$and': [ { 'question2': { '$lt':10} }, { 'question3': false }] });\n *\n * // regular expresssions: use any Perl-compatible regular expressions\n * // request all documents in collection\n * // where 'question5' contains the string '.*day'\n * ds.query('', { 'question5': { '$regex': '.*day' } });\n *\n * **Parameters**\n * @param {String} key The name of the document to search. Pass the empty string ('') to search the entire collection.\n * @param {Object} query The query object. For exact matching, this object contains the field name and field value to match. For matching based on comparison, this object contains the field name and the comparison expression. For matching based on logical operators, this object contains an expression using MongoDB syntax. See the underlying [Data API](../../../rest_apis/data_api/#searching) for additional examples.\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise} \n */\n query: function (key, query, outputModifier, options) {\n var params = $.extend(true, { q: query }, outputModifier);\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions.url = getURL(key, httpOptions.root);\n return http.get(params, httpOptions);\n },\n\n /**\n * Save data in an anonymous document within the collection.\n *\n * The `root` of the collection must be specified. By default the `root` is taken from the Data Service configuration options; you can also pass the `root` to the `save` call explicitly by overriding the options (third parameter).\n *\n * (Additional background: Documents are top-level elements within a collection. Collections must be unique within this account (team or personal account) and project and are set with the `root` field in the `option` parameter. See the underlying [Data API](../../../rest_apis/data_api/) for more information. The `save` method is making a `POST` request.)\n *\n * **Example**\n *\n * // Create a new document, with one element, at the default root level\n * ds.save('question1', 'yes');\n *\n * // Create a new document, with two elements, at the default root level\n * ds.save({ question1:'yes', question2: 32 });\n *\n * // Create a new document, with two elements, at `/students/`\n * ds.save({ name:'John', className: 'CS101' }, { root: 'students' });\n *\n * **Parameters**\n *\n * @param {String|Object} key If `key` is a string, it is the id of the element to save (create) in this document. If `key` is an object, the object is the data to save (create) in this document. In both cases, the id for the document is generated automatically.\n * @param {Object} value (Optional) The data to save. If `key` is a string, this is the value to save. If `key` is an object, the value(s) to save are already part of `key` and this argument is not required.\n * @param {Object} options (Optional) Overrides for configuration options. If you want to override the default `root` of the collection, do so here.\n * @return {Promise} \n */\n save: function (key, value, options) {\n var attrs;\n if (typeof key === 'object') {\n attrs = key;\n options = value;\n } else {\n (attrs = {})[key] = value;\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions.url = getURL('', httpOptions.root);\n\n return http.post(attrs, httpOptions);\n },\n\n /**\n * Save (create or replace) data in a named document or element within the collection. \n * \n * The `root` of the collection must be specified. By default the `root` is taken from the Data Service configuration options; you can also pass the `root` to the `saveAs` call explicitly by overriding the options (third parameter).\n *\n * Optionally, the named document or element can include path information, so that you are saving just part of the document.\n *\n * (Additional background: Documents are top-level elements within a collection. Collections must be unique within this account (team or personal account) and project and are set with the `root` field in the `option` parameter. See the underlying [Data API](../../../rest_apis/data_api/) for more information. The `saveAs` method is making a `PUT` request.)\n *\n * **Example**\n *\n * // Create (or replace) the `user1` document at the default root level.\n * // Note that this replaces any existing content in the `user1` document.\n * ds.saveAs('user1',\n * { 'question1': 2, 'question2': 10,\n * 'question3': false, 'question4': 'sometimes' } );\n *\n * // Create (or replace) the `student1` document at the `students` root, \n * // that is, the data at `/students/student1/`.\n * // Note that this replaces any existing content in the `/students/student1/` document.\n * // However, this will keep existing content in other paths of this collection.\n * // For example, the data at `/students/student2/` is unchanged by this call.\n * ds.saveAs('student1',\n * { firstName: 'john', lastName: 'smith' },\n * { root: 'students' });\n *\n * // Create (or replace) the `mgmt100/groupB` document at the `myclasses` root,\n * // that is, the data at `/myclasses/mgmt100/groupB/`.\n * // Note that this replaces any existing content in the `/myclasses/mgmt100/groupB/` document.\n * // However, this will keep existing content in other paths of this collection.\n * // For example, the data at `/myclasses/mgmt100/groupA/` is unchanged by this call.\n * ds.saveAs('mgmt100/groupB',\n * { scenarioYear: '2015' },\n * { root: 'myclasses' });\n *\n * **Parameters**\n *\n * @param {String} key Id of the document.\n * @param {Object} value (Optional) The data to save, in key:value pairs.\n * @param {Object} options (Optional) Overrides for configuration options. If you want to override the default `root` of the collection, do so here.\n * @return {Promise} \n */\n saveAs: function (key, value, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions.url = getURL(key, httpOptions.root);\n\n return http.put(value, httpOptions);\n },\n\n /**\n * Get data for a specific document or field.\n *\n * **Example**\n *\n * ds.load('user1');\n * ds.load('user1/question3');\n *\n * **Parameters**\n * @param {String|Object} key The id of the data to return. Can be the id of a document, or a path to data within that document.\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options Overrides for configuration options.\n * @return {Promise} \n */\n load: function (key, outputModifier, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions.url = getURL(key, httpOptions.root);\n return http.get(outputModifier, httpOptions);\n },\n\n /**\n * Removes data from collection. Only documents (top-level elements in each collection) can be deleted.\n *\n * **Example**\n *\n * ds.remove('user1');\n *\n *\n * **Parameters**\n *\n * @param {String|Array} keys The id of the document to remove from this collection, or an array of such ids.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise} \n */\n remove: function (keys, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n var params;\n if ($.isArray(keys)) {\n params = { id: keys };\n } else {\n params = '';\n httpOptions.url = getURL(keys, httpOptions.root);\n }\n return http.delete(params, httpOptions);\n }\n\n // Epicenter doesn't allow nuking collections\n // /**\n // * Removes collection being referenced\n // * @return null\n // */\n // destroy: function (options) {\n // return this.remove('', options);\n // }\n };\n\n $.extend(this, publicAPI);\n};\n","/**\n *\n * ## Group API Adapter\n *\n * The Group API Adapter provides methods to look up, create, change or remove information about groups in a project. It is based on query capabilities of the underlying RESTful [Group API](../../../rest_apis/user_management/group/).\n *\n * This is only needed for Authenticated projects, that is, team projects with [end users and groups](../../../groups_and_end_users/).\n *\n * var ma = new F.service.Group({ token: 'user-or-project-access-token' });\n * ma.getGroupsForProject({ account: 'acme', project: 'sample' });\n */\n\n'use strict';\n\nvar serviceUtils = require('./service-utils');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar objectAssign = require('object-assign');\n\nvar apiEndpoint = 'group/local';\n\nvar GroupService = function (config) {\n var defaults = {\n /**\n * Epicenter account name. Defaults to undefined.\n * @type {string}\n */\n account: undefined,\n\n /**\n * Epicenter project name. Defaults to undefined.\n * @type {string}\n */\n project: undefined,\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {object}\n */\n transport: {}\n };\n var serviceOptions = serviceUtils.getDefaultOptions(defaults, config, { apiEndpoint: apiEndpoint });\n var transportOptions = serviceOptions.transport;\n delete serviceOptions.transport;\n var http = new TransportFactory(transportOptions, serviceOptions);\n var publicAPI = {\n /**\n * Gets information for a group or multiple groups.\n * @param {Object} params object with query parameters\n * @patam {string} params.q partial match for name, organization or event.\n * @patam {string} params.account Epicenter's Team ID\n * @patam {string} params.project Epicenter's Project ID\n * @patam {string} params.name Epicenter's Group Name\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n getGroups: function (params, options) {\n //groupID is part of the URL\n //q, account and project are part of the query string\n var finalOpts = objectAssign({}, serviceOptions, options);\n var finalParams;\n if (typeof params === 'string') {\n finalOpts.url = serviceUtils.getApiUrl(apiEndpoint + '/' + params, finalOpts);\n } else {\n finalParams = params;\n }\n return http.get(finalParams, finalOpts);\n }\n };\n objectAssign(this, publicAPI);\n};\n\nmodule.exports = GroupService;\n","/**\n *\n * ## Introspection API Service\n *\n * The Introspection API Service allows you to view a list of the variables and operations in a model. Typically used in conjunction with the [Run API Service](../run-api-service/).\n *\n * The Introspection API Service is not available for Forio SimLang.\n *\n * var intro = new F.service.Introspect({\n * account: 'acme-simulations',\n * project: 'supply-chain-game'\n * });\n * intro.byModel('supply-chain.py').then(function(data){ ... });\n * intro.byRunID('2b4d8f71-5c34-435a-8c16-9de674ab72e6').then(function(data){ ... });\n *\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\n\nvar apiEndpoint = 'model/introspect';\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The project id. Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n project: undefined,\n\n };\n\n var sessionManager = new SessionManager();\n var serviceOptions = sessionManager.getMergedOptions(defaults, config);\n\n var urlConfig = new ConfigService(serviceOptions).get('server');\n if (serviceOptions.account) {\n urlConfig.accountPath = serviceOptions.account;\n }\n if (serviceOptions.project) {\n urlConfig.projectPath = serviceOptions.project;\n }\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(transportOptions);\n\n var publicAPI = {\n /**\n * Get the available variables and operations for a given model file.\n *\n * Note: This does not work for any model which requires additional parameters, such as `files`.\n *\n * **Example**\n *\n * intro.byModel('abc.vmf')\n * .then(function(data) {\n * // data contains an object with available functions (used with operations API) and available variables (used with variables API)\n * console.log(data.functions);\n * console.log(data.variables);\n * });\n *\n * **Parameters**\n * @param {String} modelFile Name of the model file to introspect.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise} \n */\n byModel: function (modelFile, options) {\n var opts = $.extend(true, {}, serviceOptions, options);\n if (!opts.account || !opts.project) {\n throw new Error('Account and project are required when using introspect#byModel');\n }\n if (!modelFile) {\n throw new Error('modelFile is required when using introspect#byModel');\n }\n var url = { url: urlConfig.getAPIPath(apiEndpoint) + [opts.account, opts.project, modelFile].join('/') };\n var httpOptions = $.extend(true, {}, serviceOptions, options, url);\n return http.get('', httpOptions);\n },\n\n /**\n * Get the available variables and operations for a given model file.\n *\n * Note: This does not work for any model which requires additional parameters such as `files`.\n *\n * **Example**\n *\n * intro.byRunID('2b4d8f71-5c34-435a-8c16-9de674ab72e6')\n * .then(function(data) {\n * // data contains an object with available functions (used with operations API) and available variables (used with variables API)\n * console.log(data.functions);\n * console.log(data.variables);\n * });\n *\n * **Parameters**\n * @param {String} runID Id of the run to introspect.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise} \n */\n byRunID: function (runID, options) {\n if (!runID) {\n throw new Error('runID is required when using introspect#byModel');\n }\n var url = { url: urlConfig.getAPIPath(apiEndpoint) + runID };\n var httpOptions = $.extend(true, {}, serviceOptions, options, url);\n return http.get('', httpOptions);\n }\n };\n $.extend(this, publicAPI);\n};\n","/**\n *\n * ## Member API Adapter\n *\n * The Member API Adapter provides methods to look up information about end users for your project and how they are divided across groups. It is based on query capabilities of the underlying RESTful [Member API](../../../rest_apis/user_management/member/).\n *\n * This is only needed for Authenticated projects, that is, team projects with [end users and groups](../../../groups_and_end_users/). For example, if some of your end users are facilitators, or if your end users should be treated differently based on which group they are in, use the Member API to find that information.\n *\n * var ma = new F.service.Member({ token: 'user-or-project-access-token' });\n * ma.getGroupsForUser({ userId: 'b6b313a3-ab84-479c-baea-206f6bff337' });\n * ma.getGroupDetails({ groupId: '00b53308-9833-47f2-b21e-1278c07d53b8' });\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\nvar _pick = require('../util/object-util')._pick;\nvar apiEndpoint = 'member/local';\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * Epicenter user id. Defaults to a blank string.\n * @type {string}\n */\n userId: undefined,\n\n /**\n * Epicenter group id. Defaults to a blank string. Note that this is the group *id*, not the group *name*.\n * @type {string}\n */\n groupId: undefined,\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {object}\n */\n transport: {}\n };\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(transportOptions, serviceOptions);\n\n var getFinalParams = function (params) {\n if (typeof params === 'object') {\n return $.extend(true, serviceOptions, params);\n }\n return serviceOptions;\n };\n\n var patchUserActiveField = function (params, active, options) {\n var httpOptions = $.extend(true, serviceOptions, options, {\n url: urlConfig.getAPIPath(apiEndpoint) + params.groupId + '/' + params.userId\n });\n\n return http.patch({ active: active }, httpOptions);\n };\n\n var publicAPI = {\n\n /**\n * Retrieve details about all of the group memberships for one end user. The membership details are returned in an array, with one element (group record) for each group to which the end user belongs.\n *\n * In the membership array, each group record includes the group id, project id, account (team) id, and an array of members. However, only the user whose userId is included in the call is listed in the members array (regardless of whether there are other members in this group).\n *\n * **Example**\n *\n * var ma = new F.service.Member({ token: 'user-or-project-access-token' });\n * ma.getGroupsForUser('42836d4b-5b61-4fe4-80eb-3136e956ee5c')\n * .then(function(memberships){\n * for (var i=0; i 1,\n * // where variables.price has been persisted (recorded)\n * // in the model.\n * rs.query({\n * 'saved': 'true',\n * '.price': '>1'\n * },\n * {\n * startrecord: 2,\n * endrecord: 5\n * });\n *\n * **Parameters**\n * @param {Object} qs Query object. Each key can be a property of the run or the name of variable that has been saved in the run (prefaced by `variables.`). Each value can be a literal value, or a comparison operator and value. (See [more on filtering](../../../rest_apis/aggregate_run_api/#filters) allowed in the underlying Run API.) Querying for variables is available for runs [in memory](../../../run_persistence/#runs-in-memory) and for runs [in the database](../../../run_persistence/#runs-in-memory) if the variables are persisted (e.g. that have been `record`ed in your Julia model).\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n query: function (qs, outputModifier, options) {\n serviceOptions.filter = qs; //shouldn't be able to over-ride\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = urlConfig.addAutoRestoreHeader(httpOptions);\n\n return http.splitGet(outputModifier, httpOptions);\n },\n\n /**\n * Returns particular runs, based on conditions specified in the `qs` object.\n *\n * Similar to `.query()`.\n *\n * **Parameters**\n * @param {Object} filter Filter object. Each key can be a property of the run or the name of variable that has been saved in the run (prefaced by `variables.`). Each value can be a literal value, or a comparison operator and value. (See [more on filtering](../../../rest_apis/aggregate_run_api/#filters) allowed in the underlying Run API.) Filtering for variables is available for runs [in memory](../../../run_persistence/#runs-in-memory) and for runs [in the database](../../../run_persistence/#runs-in-memory) if the variables are persisted (e.g. that have been `record`ed in your Julia model).\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n filter: function (filter, outputModifier, options) {\n if ($.isPlainObject(serviceOptions.filter)) {\n $.extend(serviceOptions.filter, filter);\n } else {\n serviceOptions.filter = filter;\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = urlConfig.addAutoRestoreHeader(httpOptions);\n return http.splitGet(outputModifier, httpOptions);\n },\n\n /**\n * Get data for a specific run. This includes standard run data such as the account, model, project, and created and last modified dates. To request specific model variables, pass them as part of the `filters` parameter.\n *\n * Note that if the run is [in memory](../../../run_persistence/#runs-in-memory), any model variables are available; if the run is [in the database](../../../run_persistence/#runs-in-db), only model variables that have been persisted — that is, `record`ed in your Julia model — are available.\n *\n * **Example**\n *\n * rs.load('bb589677-d476-4971-a68e-0c58d191e450', { include: ['.price', '.sales'] });\n *\n * **Parameters**\n * @param {String} runID The run id.\n * @param {Object} filters (Optional) Object containing filters and operation modifiers. Use key `include` to list model variables that you want to include in the response. Other available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n load: function (runID, filters, options) {\n if (runID) {\n serviceOptions.filter = runID; //shouldn't be able to over-ride\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = urlConfig.addAutoRestoreHeader(httpOptions);\n return http.get(filters, httpOptions);\n },\n\n\n /**\n * Save attributes (data, model variables) of the run.\n *\n * **Examples**\n *\n * // add 'completed' field to run record\n * rs.save({ completed: true });\n *\n * // update 'saved' field of run record, and update values of model variables for this run\n * rs.save({ saved: true, variables: { a: 23, b: 23 } });\n *\n * **Parameters**\n * @param {Object} attributes The run data and variables to save.\n * @param {Object} attributes.variables Model variables must be included in a `variables` field within the `attributes` object. (Otherwise they are treated as run data and added to the run record directly.)\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n save: function (attributes, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n setFilterOrThrowError(httpOptions);\n return http.patch(attributes, httpOptions);\n },\n\n /**\n * Call a method from the model.\n *\n * Depending on the language in which you have written your model, the method may need to be exposed (e.g. `export` for a Julia model) in the model file in order to be called through the API. See [Writing your Model](../../../writing_your_model/)).\n *\n * The `params` argument is normally an array of arguments to the `operation`. In the special case where `operation` only takes one argument, you are not required to put that argument into an array.\n *\n * Note that you can combine the `operation` and `params` arguments into a single object if you prefer, as in the last example.\n *\n * **Examples**\n *\n * // method \"solve\" takes no arguments\n * rs.do('solve');\n * // method \"echo\" takes one argument, a string\n * rs.do('echo', ['hello']);\n * // method \"echo\" takes one argument, a string\n * rs.do('echo', 'hello');\n * // method \"sumArray\" takes one argument, an array\n * rs.do('sumArray', [[4,2,1]]);\n * // method \"add\" takes two arguments, both integers\n * rs.do({ name:'add', params:[2,4] });\n *\n * **Parameters**\n * @param {String} operation Name of method.\n * @param {Array} params (Optional) Any parameters the operation takes, passed as an array. In the special case where `operation` only takes one argument, you are not required to put that argument into an array, and can just pass it directly.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n do: function (operation, params, options) {\n // console.log('do', operation, params);\n var opsArgs;\n var postOptions;\n if (options) {\n opsArgs = params;\n postOptions = options;\n } else if ($.isPlainObject(params)) {\n opsArgs = null;\n postOptions = params;\n } else {\n opsArgs = params;\n }\n var result = rutil.normalizeOperations(operation, opsArgs);\n var httpOptions = $.extend(true, {}, serviceOptions, postOptions);\n\n setFilterOrThrowError(httpOptions);\n\n var prms = (result.args[0].length && (result.args[0] !== null && result.args[0] !== undefined)) ? result.args[0] : [];\n return http.post({ arguments: prms }, $.extend(true, {}, httpOptions, {\n url: urlConfig.getFilterURL() + 'operations/' + result.ops[0] + '/'\n }));\n },\n\n /**\n * Call several methods from the model, sequentially.\n *\n * Depending on the language in which you have written your model, the methods may need to be exposed (e.g. `export` for a Julia model) in the model file in order to be called through the API. See [Writing your Model](../../../writing_your_model/)).\n *\n * **Examples**\n *\n * // methods \"initialize\" and \"solve\" do not take any arguments\n * rs.serial(['initialize', 'solve']);\n * // methods \"init\" and \"reset\" take two arguments each\n * rs.serial([ { name: 'init', params: [1,2] },\n * { name: 'reset', params: [2,3] }]);\n * // method \"init\" takes two arguments,\n * // method \"runmodel\" takes none\n * rs.serial([ { name: 'init', params: [1,2] },\n * { name: 'runmodel', params: [] }]);\n *\n * **Parameters**\n * @param {Array} operations If none of the methods take parameters, pass an array of the method names (strings). If any of the methods do take parameters, pass an array of objects, each of which contains a method name and its own (possibly empty) array of parameters.\n * @param {*} params Parameters to pass to operations.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n serial: function (operations, params, options) {\n var opParams = rutil.normalizeOperations(operations, params);\n var ops = opParams.ops;\n var args = opParams.args;\n var me = this;\n\n var $d = $.Deferred();\n var postOptions = $.extend(true, {}, serviceOptions, options);\n\n var doSingleOp = function () {\n var op = ops.shift();\n var arg = args.shift();\n\n me.do(op, arg, {\n success: function () {\n if (ops.length) {\n doSingleOp();\n } else {\n $d.resolve.apply(this, arguments);\n postOptions.success.apply(this, arguments);\n }\n },\n error: function () {\n $d.reject.apply(this, arguments);\n postOptions.error.apply(this, arguments);\n }\n });\n };\n\n doSingleOp();\n\n return $d.promise();\n },\n\n /**\n * Call several methods from the model, executing them in parallel.\n *\n * Depending on the language in which you have written your model, the methods may need to be exposed (e.g. `export` for a Julia model) in the model file in order to be called through the API. See [Writing your Model](../../../writing_your_model/)).\n *\n * **Example**\n *\n * // methods \"solve\" and \"reset\" do not take any arguments\n * rs.parallel(['solve', 'reset']);\n * // methods \"add\" and \"subtract\" take two arguments each\n * rs.parallel([ { name: 'add', params: [1,2] },\n * { name: 'subtract', params:[2,3] }]);\n * // methods \"add\" and \"subtract\" take two arguments each\n * rs.parallel({ add: [1,2], subtract: [2,4] });\n *\n * **Parameters**\n * @param {Array|Object} operations If none of the methods take parameters, pass an array of the method names (as strings). If any of the methods do take parameters, you have two options. You can pass an array of objects, each of which contains a method name and its own (possibly empty) array of parameters. Alternatively, you can pass a single object with the method name and a (possibly empty) array of parameters.\n * @param {*} params Parameters to pass to operations.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n parallel: function (operations, params, options) {\n var $d = $.Deferred();\n\n var opParams = rutil.normalizeOperations(operations, params);\n var ops = opParams.ops;\n var args = opParams.args;\n var postOptions = $.extend(true, {}, serviceOptions, options);\n\n var queue = [];\n for (var i = 0; i < ops.length; i++) {\n queue.push(\n this.do(ops[i], args[i])\n );\n }\n $.when.apply(this, queue)\n .then(function () {\n $d.resolve.apply(this, arguments);\n postOptions.success.apply(this.arguments);\n })\n .fail(function () {\n $d.reject.apply(this, arguments);\n postOptions.error.apply(this.arguments);\n });\n\n return $d.promise();\n },\n\n /**\n * Shortcut to using the [Introspection API Service](../introspection-api-service/). Allows you to view a list of the variables and operations in a model.\n *\n * **Example**\n *\n * rs.introspect({ runID: 'cbf85437-b539-4977-a1fc-23515cf071bb' }).then(function (data) {\n * console.log(data.functions);\n * console.log(data.variables);\n * });\n *\n * **Parameters**\n * @param {Object} options Options can either be of the form `{ runID: }` or `{ model: }`.\n * @param {Object} introspectionConfig (Optional) Service options for Introspection Service\n * @return {Promise}\n */\n introspect: function (options, introspectionConfig) {\n var introspection = new IntrospectionService($.extend(true, {}, serviceOptions, introspectionConfig));\n if (options) {\n if (options.runID) {\n return introspection.byRunID(options.runID);\n } else if (options.model) {\n return introspection.byModel(options.model);\n }\n } else if (serviceOptions.id) {\n return introspection.byRunID(serviceOptions.id);\n } else {\n throw new Error('Please specify either the model or runid to introspect');\n }\n }\n };\n\n var publicSyncAPI = {\n getCurrentConfig: function () {\n return serviceOptions;\n },\n /**\n * Returns a Variables Service instance. Use the variables instance to load, save, and query for specific model variables. See the [Variable API Service](../variables-api-service/) for more information.\n *\n * **Example**\n *\n * var vs = rs.variables();\n * vs.save({ sample_int: 4 });\n *\n * **Parameters**\n * @param {Object} config (Optional) Overrides for configuration options.\n * @return {Object} variablesService Instance\n */\n variables: function (config) {\n var vs = new VariablesService($.extend(true, {}, serviceOptions, config, {\n runService: this\n }));\n return vs;\n }\n };\n\n $.extend(this, publicAsyncAPI);\n $.extend(this, publicSyncAPI);\n};\n","'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar SessionManager = require('../store/session-manager');\nvar objectAssign = require('object-assign');\n\nvar serviceUtils = {\n /*\n * Gets the default options for a api service.\n * It will merge:\n * - The Session options (Using the Session Manager)\n * - The Authorization Header from the token option\n * - The full url from the endpoint option\n * With the supplied overrides and defaults\n *\n */\n getDefaultOptions: function (defaults) {\n var rest = Array.prototype.slice.call(arguments, 1);\n var sessionManager = new SessionManager();\n var serviceOptions = sessionManager.getMergedOptions.apply(sessionManager, [defaults].concat(rest));\n\n serviceOptions.transport = objectAssign({}, serviceOptions.transport, {\n url: this.getApiUrl(serviceOptions.apiEndpoint, serviceOptions)\n });\n\n if (serviceOptions.token) {\n serviceOptions.transport.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n return serviceOptions;\n },\n\n getApiUrl: function (apiEndpoint, serviceOptions) {\n var urlConfig = new ConfigService(serviceOptions).get('server');\n return urlConfig.getAPIPath(apiEndpoint);\n }\n};\n\nmodule.exports = serviceUtils;","'use strict';\n/**\n * ## State API Adapter\n *\n * The State API Adapter allows you to replay or clone runs. It brings existing, persisted run data from the database back into memory, using the same run id (`replay`) or a new run id (`clone`). Runs must be in memory in order for you to update variables or call operations on them.\n *\n * Specifically, the State API Adapter works by \"re-running\" the run (user interactions) from the creation of the run up to the time it was last persisted in the database. This process uses the current version of the run's model. Therefore, if the model has changed since the original run was created, the retrieved run will use the new model — and may end up having different values or behavior as a result. Use with care!\n *\n * To use the State API Adapter, instantiate it and then call its methods:\n *\n * var sa = new F.service.State();\n * sa.replay({runId: '1842bb5c-83ad-4ba8-a955-bd13cc2fdb4f'});\n *\n * The constructor takes an optional `options` parameter in which you can specify the `account` and `project` if they are not already available in the current context.\n *\n */\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar _pick = require('../util/object-util')._pick;\nvar SessionManager = require('../store/session-manager');\nvar apiEndpoint = 'model/state';\n\nmodule.exports = function (config) {\n\n var defaults = {\n\n };\n\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n\n var http = new TransportFactory(transportOptions);\n var parseRunIdOrError = function (params) {\n if ($.isPlainObject(params) && params.runId) {\n return params.runId;\n } else {\n throw new Error('Please pass in a run id');\n }\n };\n\n var publicAPI = {\n /**\n * Replay a run. After this call, the run, with its original run id, is now available [in memory](../../../run_persistence/#runs-in-memory). (It continues to be persisted into the Epicenter database at regular intervals.)\n *\n * **Example**\n *\n * var sa = new F.service.State();\n * sa.replay({runId: '1842bb5c-83ad-4ba8-a955-bd13cc2fdb4f', stopBefore: 'calculateScore'});\n *\n * **Parameters**\n * @param {object} params Parameters object.\n * @param {string} params.runId The id of the run to bring back to memory.\n * @param {string} params.stopBefore (Optional) The run is advanced only up to the first occurrence of this method.\n * @param {array} params.exclude (Optional) Array of methods to exclude when advancing the run.\n * @param {object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n replay: function (params, options) {\n var runId = parseRunIdOrError(params);\n\n var replayOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + runId }\n );\n\n params = $.extend(true, { action: 'replay' }, _pick(params, ['stopBefore', 'exclude']));\n\n return http.post(params, replayOptions);\n },\n\n /**\n * Clone a given run and return a new run in the same state as the given run.\n *\n * The new run id is now available [in memory](../../../run_persistence/#runs-in-memory). The new run includes a copy of all of the data from the original run, EXCEPT:\n *\n * * The `saved` field in the new run record is not copied from the original run record. It defaults to `false`.\n * * The `initialized` field in the new run record is not copied from the original run record. It defaults to `false` but may change to `true` as the new run is advanced. For example, if there has been a call to the `step` function (for Vensim models), the `initialized` field is set to `true`.\n * * The `created` field in the new run record is the date and time at which the clone was created (not the time that the original run was created.)\n *\n * The original run remains only [in the database](../../../run_persistence/#runs-in-db).\n *\n * **Example**\n *\n * var sa = new F.service.State();\n * sa.clone({runId: '1842bb5c-83ad-4ba8-a955-bd13cc2fdb4f', stopBefore: 'calculateScore', exclude: ['interimCalculation'] });\n *\n * **Parameters**\n * @param {object} params Parameters object.\n * @param {string} params.runId The id of the run to clone from memory.\n * @param {string} params.stopBefore (Optional) The newly cloned run is advanced only up to the first occurrence of this method.\n * @param {array} params.exclude (Optional) Array of methods to exclude when advancing the newly cloned run.\n * @param {object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n clone: function (params, options) {\n var runId = parseRunIdOrError(params);\n\n var replayOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + runId }\n );\n\n params = $.extend(true, { action: 'clone' }, _pick(params, ['stopBefore', 'exclude']));\n\n return http.post(params, replayOptions);\n }\n };\n\n $.extend(this, publicAPI);\n};\n","'use strict';\n\nvar epiVersion = require('../api-version.json');\n\n//TODO: urlutils to get host, since no window on node\nvar defaults = {\n host: window.location.host,\n pathname: window.location.pathname\n};\n\nfunction getLocalHost(existingFn, host) {\n var localHostFn;\n if (existingFn !== undefined) {\n if (!$.isFunction(existingFn)) {\n localHostFn = function () { return existingFn; };\n } else {\n localHostFn = existingFn;\n }\n } else {\n localHostFn = function () {\n var isLocal = !host || //phantomjs\n host === '127.0.0.1' || \n host.indexOf('local.') === 0 || \n host.indexOf('localhost') === 0;\n return isLocal;\n };\n }\n return localHostFn;\n}\n\nvar UrlConfigService = function (config) {\n var envConf = UrlConfigService.defaults;\n\n if (!config) {\n config = {};\n }\n // console.log(this.defaults);\n var overrides = $.extend({}, envConf, config);\n var options = $.extend({}, defaults, overrides);\n\n overrides.isLocalhost = options.isLocalhost = getLocalHost(options.isLocalhost, options.host);\n \n // console.log(isLocalhost(), '___________');\n var actingHost = config && config.host;\n if (!actingHost && options.isLocalhost()) {\n actingHost = 'forio.com';\n } else {\n actingHost = options.host;\n }\n\n var API_PROTOCOL = 'https';\n var HOST_API_MAPPING = {\n 'forio.com': 'api.forio.com',\n 'foriodev.com': 'api.epicenter.foriodev.com'\n };\n\n var publicExports = {\n protocol: API_PROTOCOL,\n\n api: '',\n\n //TODO: this should really be called 'apihost', but can't because that would break too many things\n host: (function () {\n var apiHost = (HOST_API_MAPPING[actingHost]) ? HOST_API_MAPPING[actingHost] : actingHost;\n // console.log(actingHost, config, apiHost);\n return apiHost;\n }()),\n\n isCustomDomain: (function () {\n var path = options.pathname.split('\\/');\n var pathHasApp = path && path[1] === 'app';\n return (!options.isLocalhost() && !pathHasApp);\n }()),\n\n appPath: (function () {\n var path = options.pathname.split('\\/');\n\n return path && path[1] || '';\n }()),\n\n accountPath: (function () {\n var accnt = '';\n var path = options.pathname.split('\\/');\n if (path && path[1] === 'app') {\n accnt = path[2];\n }\n return accnt;\n }()),\n\n projectPath: (function () {\n var prj = '';\n var path = options.pathname.split('\\/');\n if (path && path[1] === 'app') {\n prj = path[3]; //eslint-disable-line no-magic-numbers\n }\n return prj;\n }()),\n\n versionPath: (function () {\n var version = epiVersion.version ? epiVersion.version + '/' : '';\n return version;\n }()),\n\n getAPIPath: function (api) {\n var PROJECT_APIS = ['run', 'data', 'file'];\n\n if (api === 'config') {\n var actualProtocol = window.location.protocol.replace(':', '');\n var configProtocol = (options.isLocalhost()) ? this.protocol : actualProtocol;\n return configProtocol + '://' + actingHost + '/epicenter/' + this.versionPath + 'config';\n }\n var apiPath = this.protocol + '://' + this.host + '/' + this.versionPath + api + '/';\n\n if ($.inArray(api, PROJECT_APIS) !== -1) {\n apiPath += this.accountPath + '/' + this.projectPath + '/';\n }\n return apiPath;\n }\n };\n\n\n $.extend(publicExports, overrides);\n return publicExports;\n};\n// This data can be set by external scripts, for loading from an env server for eg;\nUrlConfigService.defaults = {};\n\nmodule.exports = UrlConfigService;\n","'use strict';\n/**\n* ## User API Adapter\n*\n* The User API Adapter allows you to retrieve details about end users in your team (account). It is based on the querying capabilities of the underlying RESTful [User API](../../../rest_apis/user_management/user/).\n*\n* To use the User API Adapter, instantiate it and then call its methods.\n*\n* var ua = new F.service.User({\n* account: 'acme-simulations',\n* token: 'user-or-project-access-token'\n* });\n* ua.getById('42836d4b-5b61-4fe4-80eb-3136e956ee5c');\n* ua.get({ userName: 'jsmith' });\n* ua.get({ id: ['42836d4b-5b61-4fe4-80eb-3136e956ee5c',\n* '4ea75631-4c8d-4872-9d80-b4600146478e'] });\n*\n* The constructor takes an optional options parameter in which you can specify the `account` and `token` if they are not already available in the current context.\n*/\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\nvar qutil = require('../util/query-util');\n\nmodule.exports = function (config) {\n var defaults = {\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The access token to use when searching for end users. (See [more background on access tokens](../../../project_access/)).\n * @type {String}\n */\n token: undefined,\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {Object}\n */\n transport: {}\n };\n\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath('user')\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n\n var http = new TransportFactory(transportOptions);\n\n var publicAPI = {\n\n /**\n * Retrieve details about particular end users in your team, based on user name or user id.\n *\n * **Example**\n *\n * var ua = new F.service.User({\n * account: 'acme-simulations',\n * token: 'user-or-project-access-token'\n * });\n * ua.get({ userName: 'jsmith' });\n * ua.get({ id: ['42836d4b-5b61-4fe4-80eb-3136e956ee5c',\n * '4ea75631-4c8d-4872-9d80-b4600146478e'] });\n *\n * **Parameters**\n * @param {object} filter Object with field `userName` and value of the username. Alternatively, object with field `id` and value of an array of user ids.\n * @param {object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n\n get: function (filter, options) {\n options = options || {};\n filter = filter || {};\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options\n );\n\n var toQFilter = function (filter) {\n var res = {};\n\n // API only supports filtering by username for now\n if (filter.userName) {\n res.q = filter.userName;\n }\n\n return res;\n };\n\n var toIdFilters = function (id) {\n if (!id) {\n return '';\n }\n\n id = $.isArray(id) ? id : [id];\n return 'id=' + id.join('&id=');\n };\n\n var getFilters = [\n 'account=' + getOptions.account,\n toIdFilters(filter.id),\n qutil.toQueryFormat(toQFilter(filter))\n ].join('&');\n\n // special case for queries with large number of ids\n // make it as a post with GET semantics\n var threshold = 30;\n if (filter.id && $.isArray(filter.id) && filter.id.length >= threshold) {\n getOptions.url = urlConfig.getAPIPath('user') + '?_method=GET';\n return http.post({ id: filter.id }, getOptions);\n } else {\n return http.get(getFilters, getOptions);\n }\n },\n\n /**\n * Retrieve details about a single end user in your team, based on user id.\n *\n * **Example**\n *\n * var ua = new F.service.User({\n * account: 'acme-simulations',\n * token: 'user-or-project-access-token'\n * });\n * ua.getById('42836d4b-5b61-4fe4-80eb-3136e956ee5c');\n *\n * **Parameters**\n * @param {string} userId The user id for the end user in your team.\n * @param {object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n\n getById: function (userId, options) {\n return publicAPI.get({ id: userId }, options);\n }\n };\n\n $.extend(this, publicAPI);\n};\n\n\n","/**\n *\n * ## Variables API Service\n *\n * Used in conjunction with the [Run API Service](../run-api-service/) to read, write, and search for specific model variables.\n *\n * var rm = new F.manager.RunManager({\n * run: {\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * model: 'supply-chain-model.jl'\n * }\n * });\n * rm.getRun()\n * .then(function() {\n * var vs = rm.run.variables();\n * vs.save({sample_int: 4});\n * });\n *\n */\n\n\n 'use strict';\n\n var TransportFactory = require('../transport/http-transport-factory');\n var rutil = require('../util/run-util');\n\n module.exports = function (config) {\n var defaults = {\n /**\n * The runs object to which the variable filters apply. Defaults to null.\n * @type {runService}\n */\n runService: null\n };\n var serviceOptions = $.extend({}, defaults, config);\n\n var getURL = function () {\n return serviceOptions.runService.urlConfig.getFilterURL() + 'variables/';\n };\n\n var addAutoRestoreHeader = function (options) {\n return serviceOptions.runService.urlConfig.addAutoRestoreHeader(options);\n };\n\n var httpOptions = {\n url: getURL\n };\n if (serviceOptions.token) {\n httpOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(httpOptions);\n http.splitGet = rutil.splitGetFactory(httpOptions);\n\n var publicAPI = {\n\n /**\n * Get values for a variable.\n *\n * **Example**\n *\n * vs.load('sample_int')\n * .then(function(val){\n * // val contains the value of sample_int\n * });\n *\n * **Parameters**\n * @param {String} variable Name of variable to load.\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n load: function (variable, outputModifier, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = addAutoRestoreHeader(httpOptions);\n return http.get(outputModifier, $.extend({}, httpOptions, {\n url: getURL() + variable + '/'\n }));\n },\n\n /**\n * Returns particular variables, based on conditions specified in the `query` object.\n *\n * **Example**\n *\n * vs.query(['price', 'sales'])\n * .then(function(val) {\n * // val is an object with the values of the requested variables: val.price, val.sales\n * });\n *\n * vs.query({ include:['price', 'sales'] });\n *\n * **Parameters**\n * @param {Object|Array} query The names of the variables requested.\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n query: function (query, outputModifier, options) {\n //Query and outputModifier are both querystrings in the url; only calling them out separately here to be consistent with the other calls\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = addAutoRestoreHeader(httpOptions);\n\n if ($.isArray(query)) {\n query = { include: query };\n }\n $.extend(query, outputModifier);\n return http.splitGet(query, httpOptions);\n },\n\n /**\n * Save values to model variables. Overwrites existing values. Note that you can only update model variables if the run is [in memory](../../../run_persistence/#runs-in-memory). (An alternate way to update model variables is to call a method from the model and make sure that the method persists the variables. See `do`, `serial`, and `parallel` in the [Run API Service](../run-api-service/) for calling methods from the model.)\n *\n * **Example**\n *\n * vs.save('price', 4);\n * vs.save({ price: 4, quantity: 5, products: [2,3,4] });\n *\n * **Parameters**\n * @param {Object|String} variable An object composed of the model variables and the values to save. Alternatively, a string with the name of the variable.\n * @param {Object} val (Optional) If passing a string for `variable`, use this argument for the value to save.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n save: function (variable, val, options) {\n var attrs;\n if (typeof variable === 'object') {\n attrs = variable;\n options = val;\n } else {\n (attrs = {})[variable] = val;\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n\n return http.patch.call(this, attrs, httpOptions);\n }\n\n // Not Available until underlying API supports PUT. Otherwise save would be PUT and merge would be PATCH\n // *\n // * Save values to the api. Merges arrays, but otherwise same as save\n // * @param {Object|String} variable Object with attributes, or string key\n // * @param {Object} val Optional if prev parameter was a string, set value here\n // * @param {Object} options Overrides for configuration options\n // *\n // * @example\n // * vs.merge({ price: 4, quantity: 5, products: [2,3,4] })\n // * vs.merge('price', 4);\n\n // merge: function (variable, val, options) {\n // var attrs;\n // if (typeof variable === 'object') {\n // attrs = variable;\n // options = val;\n // } else {\n // (attrs = {})[variable] = val;\n // }\n // var httpOptions = $.extend(true, {}, serviceOptions, options);\n\n // return http.patch.call(this, attrs, httpOptions);\n // }\n };\n $.extend(this, publicAPI);\n };\n","/**\n * ## World API Adapter\n *\n * A [run](../../../glossary/#run) is a collection of end user interactions with a project and its model -- including setting variables, making decisions, and calling operations. For building multiplayer simulations you typically want multiple end users to share the same set of interactions, and work within a common state. Epicenter allows you to create \"worlds\" to handle such cases. Only [team projects](../../../glossary/#team) can be multiplayer.\n *\n * The World API Adapter allows you to create, access, and manipulate multiplayer worlds within your Epicenter project. You can use this to add and remove end users from the world, and to create, access, and remove their runs. Because of this, typically the World Adapter is used for facilitator pages in your project. (The related [World Manager](../world-manager/) provides an easy way to access runs and worlds for particular end users, so is typically used in pages that end users will interact with.)\n *\n * As with all the other [API Adapters](../../), all methods take in an \"options\" object as the last parameter. The options can be used to extend/override the World API Service defaults.\n *\n * To use the World Adapter, instantiate it and then access the methods provided. Instantiating requires the account id (**Team ID** in the Epicenter user interface), project id (**Project ID**), and group (**Group Name**).\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * // call methods, e.g. wa.addUsers()\n * });\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\n// var qutil = require('../util/query-util');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\nvar _pick = require('../util/object-util')._pick;\n\nvar apiBase = 'multiplayer/';\nvar assignmentEndpoint = apiBase + 'assign';\nvar apiEndpoint = apiBase + 'world';\nvar projectEndpoint = apiBase + 'project';\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n\n /**\n * The project id. If left undefined, taken from the URL.\n * @type {String}\n */\n project: undefined,\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects). If left undefined, taken from the URL.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The group name. Defaults to undefined.\n * @type {String}\n */\n group: undefined,\n\n /**\n * The model file to use to create runs in this world. Defaults to undefined.\n * @type {String}\n */\n model: undefined,\n\n /**\n * Criteria by which to filter world. Currently only supports world-ids as filters.\n * @type {String}\n */\n filter: '',\n\n /**\n * Convenience alias for filter\n * @type {String}\n */\n id: '',\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {Object}\n */\n transport: {},\n\n /**\n * Called when the call completes successfully. Defaults to `$.noop`.\n * @type {function}\n */\n success: $.noop,\n\n /**\n * Called when the call fails. Defaults to `$.noop`.\n * @type {function}\n */\n error: $.noop\n };\n\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n if (serviceOptions.id) {\n serviceOptions.filter = serviceOptions.id;\n }\n\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n if (!serviceOptions.account) {\n serviceOptions.account = urlConfig.accountPath;\n }\n\n if (!serviceOptions.project) {\n serviceOptions.project = urlConfig.projectPath;\n }\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n\n var http = new TransportFactory(transportOptions);\n\n var setIdFilterOrThrowError = function (options) {\n if (options.id) {\n serviceOptions.filter = options.id;\n }\n if (options.filter) {\n serviceOptions.filter = options.filter;\n }\n if (!serviceOptions.filter) {\n throw new Error('No world id specified to apply operations against. This could happen if the user is not assigned to a world and is trying to work with runs from that world.');\n }\n };\n\n var validateModelOrThrowError = function (options) {\n if (!options.model) {\n throw new Error('No model specified to get the current run');\n }\n };\n\n var publicAPI = {\n\n /**\n * Creates a new World.\n *\n * Using this method is rare. It is more common to create worlds automatically while you `autoAssign()` end users to worlds. (In this case, configuration data for the world, such as the roles, are read from the project-level world configuration information, for example by `getProjectSettings()`.)\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create({\n * roles: ['VP Marketing', 'VP Sales', 'VP Engineering']\n * });\n *\n * **Parameters**\n * @param {object} params Parameters to create the world.\n * @param {string} params.group (Optional) The **Group Name** to create this world under. Only end users in this group are eligible to join the world. Optional here; required when instantiating the service (`new F.service.World()`).\n * @param {object} params.roles (Optional) The list of roles (strings) for this world. Some worlds have specific roles that **must** be filled by end users. Listing the roles allows you to autoassign users to worlds and ensure that all roles are filled in each world.\n * @param {object} params.optionalRoles (Optional) The list of optional roles (strings) for this world. Some worlds have specific roles that **may** be filled by end users. Listing the optional roles as part of the world object allows you to autoassign users to worlds and ensure that all roles are filled in each world.\n * @param {integer} params.minUsers (Optional) The minimum number of users for the world. Including this number allows you to autoassign end users to worlds and ensure that the correct number of users are in each world.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n create: function (params, options) {\n var createOptions = $.extend(true, {}, serviceOptions, options, { url: urlConfig.getAPIPath(apiEndpoint) });\n var worldApiParams = ['scope', 'files', 'roles', 'optionalRoles', 'minUsers', 'group', 'name'];\n var validParams = _pick(serviceOptions, ['account', 'project', 'group']);\n // whitelist the fields that we actually can send to the api\n params = _pick(params, worldApiParams);\n\n // account and project go in the body, not in the url\n params = $.extend({}, validParams, params);\n\n var oldSuccess = createOptions.success;\n createOptions.success = function (response) {\n serviceOptions.filter = response.id; //all future chained calls to operate on this id\n return oldSuccess.apply(this, arguments);\n };\n\n return http.post(params, createOptions);\n },\n\n /**\n * Updates a World, for example to replace the roles in the world.\n *\n * Typically, you complete world configuration at the project level, rather than at the world level. For example, each world in your project probably has the same roles for end users. And your project is probably either configured so that all end users share the same world (and run), or smaller sets of end users share worlds — but not both. However, this method is available if you need to update the configuration of a particular world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.update({ roles: ['VP Marketing', 'VP Sales', 'VP Engineering'] });\n * });\n *\n * **Parameters**\n * @param {object} params Parameters to update the world.\n * @param {string} params.name A string identifier for the linked end users, for example, \"name\": \"Our Team\".\n * @param {object} params.roles (Optional) The list of roles (strings) for this world. Some worlds have specific roles that **must** be filled by end users. Listing the roles allows you to autoassign users to worlds and ensure that all roles are filled in each world.\n * @param {object} params.optionalRoles (Optional) The list of optional roles (strings) for this world. Some worlds have specific roles that **may** be filled by end users. Listing the optional roles as part of the world object allows you to autoassign users to worlds and ensure that all roles are filled in each world.\n * @param {integer} params.minUsers (Optional) The minimum number of users for the world. Including this number allows you to autoassign end users to worlds and ensure that the correct number of users are in each world.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n update: function (params, options) {\n var whitelist = ['roles', 'optionalRoles', 'minUsers'];\n options = options || {};\n setIdFilterOrThrowError(options);\n\n var updateOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter }\n );\n\n params = _pick(params || {}, whitelist);\n\n return http.patch(params, updateOptions);\n },\n\n /**\n * Deletes an existing world.\n *\n * This function optionally takes one argument. If the argument is a string, it is the id of the world to delete. If the argument is an object, it is the override for global options.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.delete();\n * });\n *\n * **Parameters**\n * @param {String|Object} options (Optional) The id of the world to delete, or options object to override global options.\n * @return {Promise}\n */\n delete: function (options) {\n options = (options && (typeof options === 'string')) ? { filter: options } : {};\n setIdFilterOrThrowError(options);\n\n var deleteOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter }\n );\n\n return http.delete(null, deleteOptions);\n },\n\n /**\n * Updates the configuration for the current instance of the World API Adapter (including all subsequent function calls, until the configuration is updated again).\n *\n * **Example**\n *\n * var wa = new F.service.World({...}).updateConfig({ filter: '123' }).addUser({ userId: '123' });\n *\n * **Parameters**\n * @param {object} config The configuration object to use in updating existing configuration.\n * @return {Object} reference to current instance\n */\n updateConfig: function (config) {\n $.extend(serviceOptions, config);\n\n return this;\n },\n\n /**\n * Lists all worlds for a given account, project, and group. All three are required, and if not specified as parameters, are read from the service.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * // lists all worlds in group \"team1\"\n * wa.list();\n *\n * // lists all worlds in group \"other-group-name\"\n * wa.list({ group: 'other-group-name' });\n * });\n *\n * **Parameters**\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n list: function (options) {\n options = options || {};\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) }\n );\n\n var filters = _pick(getOptions, ['account', 'project', 'group']);\n\n return http.get(filters, getOptions);\n },\n\n /**\n * Gets all worlds that an end user belongs to for a given account (team), project, and group.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.getWorldsForUser('b1c19dda-2d2e-4777-ad5d-3929f17e86d3')\n * });\n *\n * ** Parameters **\n * @param {string} userId The `userId` of the user whose worlds are being retrieved.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n getWorldsForUser: function (userId, options) {\n options = options || {};\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) }\n );\n\n var filters = $.extend(\n _pick(getOptions, ['account', 'project', 'group']),\n { userId: userId }\n );\n\n return http.get(filters, getOptions);\n },\n\n /**\n * Load information for a specific world. All further calls to the world service will use the id provided.\n *\n * **Parameters**\n * @param {String} worldId The id of the world to load.\n * @param {Object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n load: function (worldId, options) {\n if (worldId) {\n serviceOptions.filter = worldId;\n }\n if (!serviceOptions.filter) {\n throw new Error('Please provide a worldid to load');\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options, { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/' });\n return http.get('', httpOptions);\n },\n\n /**\n * Adds an end user or list of end users to a given world. The end user must be a member of the `group` that is associated with this world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * // add one user\n * wa.addUsers('b1c19dda-2d2e-4777-ad5d-3929f17e86d3');\n * wa.addUsers(['b1c19dda-2d2e-4777-ad5d-3929f17e86d3']);\n * wa.addUsers({ userId: 'b1c19dda-2d2e-4777-ad5d-3929f17e86d3', role: 'VP Sales' });\n *\n * // add several users\n * wa.addUsers([\n * { userId: 'a6fe0c1e-f4b8-4f01-9f5f-01ccf4c2ed44',\n * role: 'VP Marketing' },\n * { userId: '8f2604cf-96cd-449f-82fa-e331530734ee',\n * role: 'VP Engineering' }\n * ]);\n *\n * // add one user to a specific world\n * wa.addUsers('b1c19dda-2d2e-4777-ad5d-3929f17e86d3', world.id);\n * wa.addUsers('b1c19dda-2d2e-4777-ad5d-3929f17e86d3', { filter: world.id });\n * });\n *\n * ** Parameters **\n * @param {string|object|array} users User id, array of user ids, object, or array of objects of the users to add to this world.\n * @param {string} users.role The `role` the user should have in the world. It is up to the caller to ensure, if needed, that the `role` passed in is one of the `roles` or `optionalRoles` of this world.\n * @param {string} worldId The world to which the users should be added. If not specified, the filter parameter of the `options` object is used.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n addUsers: function (users, worldId, options) {\n\n if (!users) {\n throw new Error('Please provide a list of users to add to the world');\n }\n\n // normalize the list of users to an array of user objects\n users = $.map([].concat(users), function (u) {\n var isObject = $.isPlainObject(u);\n\n if (typeof u !== 'string' && !isObject) {\n throw new Error('Some of the users in the list are not in the valid format: ' + u);\n }\n\n return isObject ? u : { userId: u };\n });\n\n // check if options were passed as the second parameter\n if ($.isPlainObject(worldId) && !options) {\n options = worldId;\n worldId = null;\n }\n\n options = options || {};\n\n // we must have options by now\n if (typeof worldId === 'string') {\n options.filter = worldId;\n }\n\n setIdFilterOrThrowError(options);\n\n var updateOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/users' }\n );\n\n return http.post(users, updateOptions);\n },\n\n /**\n * Updates the role of an end user in a given world. (You can only update one end user at a time.)\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.create().then(function(world) {\n * wa.addUsers('b1c19dda-2d2e-4777-ad5d-3929f17e86d3');\n * wa.updateUser({ userId: 'b1c19dda-2d2e-4777-ad5d-3929f17e86d3', role: 'leader' });\n * });\n *\n * **Parameters**\n * @param {object} user User object with `userId` and the new `role`.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n updateUser: function (user, options) {\n options = options || {};\n\n if (!user || !user.userId) {\n throw new Error('You need to pass a userId to update from the world');\n }\n\n setIdFilterOrThrowError(options);\n\n var patchOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/users/' + user.userId }\n );\n\n return http.patch(_pick(user, 'role'), patchOptions);\n },\n\n /**\n * Removes an end user from a given world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.addUsers(['a6fe0c1e-f4b8-4f01-9f5f-01ccf4c2ed44', '8f2604cf-96cd-449f-82fa-e331530734ee']);\n * wa.removeUser('a6fe0c1e-f4b8-4f01-9f5f-01ccf4c2ed44');\n * wa.removeUser({ userId: '8f2604cf-96cd-449f-82fa-e331530734ee' });\n * });\n *\n * ** Parameters **\n * @param {object|string} user The `userId` of the user to remove from the world, or an object containing the `userId` field.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n removeUser: function (user, options) {\n options = options || {};\n\n if (typeof user === 'string') {\n user = { userId: user };\n }\n\n if (!user.userId) {\n throw new Error('You need to pass a userId to remove from the world');\n }\n\n setIdFilterOrThrowError(options);\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/users/' + user.userId }\n );\n\n return http.delete(null, getOptions);\n },\n\n /**\n * Gets the run id of current run for the given world. If the world does not have a run, creates a new one and returns the run id.\n *\n * Remember that a [run](../../glossary/#run) is a collection of interactions with a project and its model. In the case of multiplayer projects, the run is shared by all end users in the world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.getCurrentRunId({ model: 'model.py' });\n * });\n *\n * ** Parameters **\n * @param {object} options (Optional) Options object to override global options.\n * @param {object} options.model The model file to use to create a run if needed.\n * @return {Promise}\n */\n getCurrentRunId: function (options) {\n options = options || {};\n\n setIdFilterOrThrowError(options);\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/run' }\n );\n\n validateModelOrThrowError(getOptions);\n return http.post(_pick(getOptions, 'model'), getOptions);\n },\n\n /**\n * Gets the current (most recent) world for the given end user in the given group. Brings this most recent world into memory if needed.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.getCurrentWorldForUser('8f2604cf-96cd-449f-82fa-e331530734ee')\n * .then(function(world) {\n * // use data from world\n * });\n *\n * ** Parameters **\n * @param {string} userId The `userId` of the user whose current (most recent) world is being retrieved.\n * @param {string} groupName (Optional) The name of the group. If not provided, defaults to the group used to create the service.\n * @return {Promise}\n */\n getCurrentWorldForUser: function (userId, groupName) {\n var dtd = $.Deferred();\n var me = this;\n this.getWorldsForUser(userId, { group: groupName })\n .then(function (worlds) {\n // assume the most recent world as the 'active' world\n worlds.sort(function (a, b) { return new Date(b.lastModified) - new Date(a.lastModified); });\n var currentWorld = worlds[0];\n\n if (currentWorld) {\n serviceOptions.filter = currentWorld.id;\n }\n\n dtd.resolveWith(me, [currentWorld]);\n })\n .fail(dtd.reject);\n\n return dtd.promise();\n },\n\n /**\n * Deletes the current run from the world.\n *\n * (Note that the world id remains part of the run record, indicating that the run was formerly an active run for the world.)\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.deleteRun('sample-world-id');\n *\n * **Parameters**\n * @param {string} worldId The `worldId` of the world from which the current run is being deleted.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n deleteRun: function (worldId, options) {\n options = options || {};\n\n if (worldId) {\n options.filter = worldId;\n }\n\n setIdFilterOrThrowError(options);\n\n var deleteOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/run' }\n );\n\n return http.delete(null, deleteOptions);\n },\n\n /**\n * Creates a new run for the world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.getCurrentWorldForUser('8f2604cf-96cd-449f-82fa-e331530734ee')\n * .then(function (world) {\n * wa.newRunForWorld(world.id);\n * });\n *\n * **Parameters**\n * @param {string} worldId worldId in which we create the new run.\n * @param {object} options (Optional) Options object to override global options.\n * @param {object} options.model The model file to use to create a run if needed.\n * @return {Promise}\n */\n newRunForWorld: function (worldId, options) {\n var currentRunOptions = $.extend(true, {},\n options,\n { filter: worldId || serviceOptions.filter }\n );\n var me = this;\n\n validateModelOrThrowError(currentRunOptions);\n\n return this.deleteRun(worldId, options)\n .then(function () {\n return me.getCurrentRunId(currentRunOptions);\n });\n },\n\n /**\n * Assigns end users to worlds, creating new worlds as appropriate, automatically. Assigns all end users in the group, and creates new worlds as needed based on the project-level world configuration (roles, optional roles, and minimum end users per world).\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.autoAssign();\n *\n * **Parameters**\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n *\n */\n autoAssign: function (options) {\n options = options || {};\n\n var opt = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(assignmentEndpoint) }\n );\n\n var params = {\n account: opt.account,\n project: opt.project,\n group: opt.group\n };\n\n if (opt.maxUsers) {\n params.maxUsers = opt.maxUsers;\n }\n\n return http.post(params, opt);\n },\n\n /**\n * Gets the project's world configuration.\n *\n * Typically, every interaction with your project uses the same configuration of each world. For example, each world in your project probably has the same roles for end users. And your project is probably either configured so that all end users share the same world (and run), or smaller sets of end users share worlds — but not both.\n *\n * (The [Multiplayer Project REST API](../../../rest_apis/multiplayer/multiplayer_project/) allows you to set these project-level world configurations. The World Adapter simply retrieves them, for example so they can be used in auto-assignment of end users to worlds.)\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.getProjectSettings()\n * .then(function(settings) {\n * console.log(settings.roles);\n * console.log(settings.optionalRoles);\n * });\n *\n * **Parameters**\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n getProjectSettings: function (options) {\n options = options || {};\n\n var opt = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(projectEndpoint) }\n );\n\n opt.url += [opt.account, opt.project].join('/');\n return http.get(null, opt);\n }\n\n };\n\n $.extend(this, publicAPI);\n};\n","/**\n * @class Cookie Storage Service\n *\n * @example\n * var people = require('cookie-store')({ root: 'people' });\n people\n .save({lastName: 'smith' })\n\n */\n\n\n'use strict';\n\n// Thin document.cookie wrapper to allow unit testing\nvar Cookie = function () {\n this.get = function () {\n return document.cookie;\n };\n\n this.set = function (newCookie) {\n document.cookie = newCookie;\n };\n};\n\nmodule.exports = function (config) {\n var host = window.location.hostname;\n var validHost = host.split('.').length > 1;\n var domain = validHost ? '.' + host : null;\n\n var defaults = {\n /**\n * Name of collection\n * @type { string}\n */\n root: '/',\n\n domain: domain,\n cookie: new Cookie()\n };\n this.serviceOptions = $.extend({}, defaults, config);\n\n var publicAPI = {\n // * TBD\n // * Query collection; uses MongoDB syntax\n // * @see \n // *\n // * @param { string} qs Query Filter\n // * @param { string} limiters @see \n // *\n // * @example\n // * cs.query(\n // * { name: 'John', className: 'CSC101' },\n // * {limit: 10}\n // * )\n\n // query: function (qs, limiters) {\n\n // },\n\n /**\n * Save cookie value\n * @param { string|Object} key If given a key save values under it, if given an object directly, save to top-level api\n * @param {Object} value (Optional)\n * @param {Object} options Overrides for service options\n *\n * @return {*} The saved value\n *\n * @example\n * cs.set('person', { firstName: 'john', lastName: 'smith' });\n * cs.set({ name:'smith', age:'32' });\n */\n set: function (key, value, options) {\n var setOptions = $.extend(true, {}, this.serviceOptions, options);\n\n var domain = setOptions.domain;\n var path = setOptions.root;\n var cookie = setOptions.cookie;\n\n cookie.set(encodeURIComponent(key) + '=' +\n encodeURIComponent(value) +\n (domain ? '; domain=' + domain : '') +\n (path ? '; path=' + path : '')\n );\n\n return value;\n },\n\n /**\n * Load cookie value\n * @param { string|Object} key If given a key save values under it, if given an object directly, save to top-level api\n * @return {*} The value stored\n *\n * @example\n * cs.get('person');\n */\n get: function (key) {\n var cookie = this.serviceOptions.cookie;\n var cookieReg = new RegExp('(?:^|;)\\\\s*' + encodeURIComponent(key).replace(/[\\-\\.\\+\\*]/g, '\\\\$&') + '\\\\s*\\\\=\\\\s*([^;]*).*$');\n var res = cookieReg.exec(cookie.get());\n var val = res ? decodeURIComponent(res[1]) : null;\n return val;\n },\n\n /**\n * Removes key from collection\n * @param { string} key key to remove\n * @param {object} options (optional) overrides for service options\n * @return { string} key The key removed\n *\n * @example\n * cs.remove('person');\n */\n remove: function (key, options) {\n var remOptions = $.extend(true, {}, this.serviceOptions, options);\n\n var domain = remOptions.domain;\n var path = remOptions.root;\n var cookie = remOptions.cookie;\n\n cookie.set(encodeURIComponent(key) +\n '=; expires=Thu, 01 Jan 1970 00:00:00 GMT' +\n (domain ? '; domain=' + domain : '') +\n (path ? '; path=' + path : '')\n );\n return key;\n },\n\n /**\n * Removes collection being referenced\n * @return { array} keys All the keys removed\n */\n destroy: function () {\n var cookie = this.serviceOptions.cookie;\n var aKeys = cookie.get().replace(/((?:^|\\s*;)[^\\=]+)(?=;|$)|^\\s*|\\s*(?:\\=[^;]*)?(?:\\1|$)/g, '').split(/\\s*(?:\\=[^;]*)?;\\s*/);\n for (var nIdx = 0; nIdx < aKeys.length; nIdx++) {\n var cookieKey = decodeURIComponent(aKeys[nIdx]);\n this.remove(cookieKey);\n }\n return aKeys;\n }\n };\n\n $.extend(this, publicAPI);\n};\n","'use strict';\n\nvar keyNames = require('../managers/key-names');\nvar StorageFactory = require('./store-factory');\nvar optionUtils = require('../util/option-utils');\n\nvar EPI_SESSION_KEY = keyNames.EPI_SESSION_KEY;\nvar defaults = {\n /**\n * Where to store user access tokens for temporary access. Defaults to storing in a cookie in the browser.\n * @type {string}\n */\n store: { synchronous: true }\n};\n\nvar SessionManager = function (managerOptions) {\n managerOptions = managerOptions || {};\n function getBaseOptions(overrides) {\n overrides = overrides || {};\n var libOptions = optionUtils.getOptions();\n var finalOptions = $.extend(true, {}, defaults, libOptions, managerOptions, overrides);\n return finalOptions;\n }\n\n function getStore(overrides) {\n var baseOptions = getBaseOptions(overrides);\n var storeOpts = baseOptions.store || {};\n var isEpicenterDomain = !baseOptions.isLocal && !baseOptions.isCustomDomain;\n if (storeOpts.root === undefined && baseOptions.account && baseOptions.project && isEpicenterDomain) {\n storeOpts.root = '/app/' + baseOptions.account + '/' + baseOptions.project;\n }\n return new StorageFactory(storeOpts);\n }\n\n var publicAPI = {\n saveSession: function (userInfo, options) {\n var serialized = JSON.stringify(userInfo);\n getStore(options).set(EPI_SESSION_KEY, serialized);\n },\n getSession: function (options) {\n // var session = getStore(options).get(EPI_SESSION_KEY) || '{}';\n // return JSON.parse(session);\n var store = getStore(options);\n var finalOpts = store.serviceOptions;\n var serialized = store.get(EPI_SESSION_KEY) || '{}';\n var session = JSON.parse(serialized);\n // If the url contains the project and account\n // validate the account and project in the session\n // and override project, groupName, groupId and isFac\n // Otherwise (i.e. localhost) use the saved session values\n var account = finalOpts.account;\n var project = finalOpts.project;\n if (account && session.account !== account) {\n // This means that the token was not used to login to the same account\n return {};\n }\n if (session.groups && account && project) {\n var group = session.groups[project] || { groupId: '', groupName: '', isFac: false };\n $.extend(session, { project: project }, group);\n }\n return session;\n },\n removeSession: function (options) {\n var store = getStore(options);\n Object.keys(keyNames).forEach(function (cookieKey) {\n var cookieName = keyNames[cookieKey];\n store.remove(cookieName);\n });\n return true;\n },\n getStore: function (options) {\n return getStore(options);\n },\n\n getMergedOptions: function () {\n var args = Array.prototype.slice.call(arguments);\n var overrides = $.extend.apply($, [true, {}].concat(args));\n var baseOptions = getBaseOptions(overrides);\n var session = this.getSession(overrides);\n\n var sessionDefaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: session.auth_token,\n\n /**\n * The account. If left undefined, taken from the cookie session.\n * @type {String}\n */\n account: session.account,\n\n /**\n * The project. If left undefined, taken from the cookie session.\n * @type {String}\n */\n project: session.project,\n\n\n /**\n * The group name. If left undefined, taken from the cookie session.\n * @type {String}\n */\n group: session.groupName,\n /**\n * Alias for group. \n * @type {String}\n */\n groupName: session.groupName, //It's a little weird that it's called groupName in the cookie, but 'group' in all the service options, so normalize for both\n /**\n * The group id. If left undefined, taken from the cookie session.\n * @type {String}\n */\n groupId: session.groupId,\n userId: session.userId\n };\n return $.extend(true, sessionDefaults, baseOptions);\n }\n };\n $.extend(this, publicAPI);\n};\n\nmodule.exports = SessionManager;","/**\n Decides type of store to provide\n*/\n\n'use strict';\n// var isNode = false; FIXME: Browserify/minifyify has issues with the next link\n// var store = (isNode) ? require('./session-store') : require('./cookie-store');\nvar store = require('./cookie-store');\n\nmodule.exports = store;\n","'use strict';\n\nvar qutils = require('../util/query-util');\n\nmodule.exports = function (config) {\n\n var defaults = {\n url: '',\n\n contentType: 'application/json',\n headers: {},\n statusCode: {\n 404: $.noop\n },\n\n /**\n * ONLY for strings in the url. All GET & DELETE params are run through this\n * @type {[type] }\n */\n parameterParser: qutils.toQueryFormat,\n\n // To allow epicenter.token and other session cookies to be passed\n // with the requests\n xhrFields: {\n withCredentials: true\n }\n };\n\n var transportOptions = $.extend({}, defaults, config);\n\n var result = function (d) {\n return ($.isFunction(d)) ? d() : d;\n };\n\n var connect = function (method, params, connectOptions) {\n params = result(params);\n params = ($.isPlainObject(params) || $.isArray(params)) ? JSON.stringify(params) : params;\n\n var options = $.extend(true, {}, transportOptions, connectOptions, {\n type: method,\n data: params\n });\n var ALLOWED_TO_BE_FUNCTIONS = ['data', 'url'];\n $.each(options, function (key, value) {\n if ($.isFunction(value) && $.inArray(key, ALLOWED_TO_BE_FUNCTIONS) !== -1) {\n options[key] = value();\n }\n });\n\n if (options.logLevel && options.logLevel === 'DEBUG') {\n console.log(options.url);\n var oldSuccessFn = options.success || $.noop;\n options.success = function (response, ajaxStatus, ajaxReq) {\n console.log(response);\n oldSuccessFn.apply(this, arguments);\n };\n }\n\n var beforeSend = options.beforeSend;\n options.beforeSend = function (xhr, settings) {\n xhr.requestUrl = (connectOptions || {}).url;\n if (beforeSend) {\n beforeSend.apply(this, arguments);\n }\n };\n\n return $.ajax(options);\n };\n\n var publicAPI = {\n get: function (params, ajaxOptions) {\n var options = $.extend({}, transportOptions, ajaxOptions);\n params = options.parameterParser(result(params));\n return connect.call(this, 'GET', params, options);\n },\n splitGet: function () {\n\n },\n post: function () {\n return connect.apply(this, ['post'].concat([].slice.call(arguments)));\n },\n patch: function () {\n return connect.apply(this, ['patch'].concat([].slice.call(arguments)));\n },\n put: function () {\n return connect.apply(this, ['put'].concat([].slice.call(arguments)));\n },\n delete: function (params, ajaxOptions) {\n //DELETE doesn't support body params, but jQuery thinks it does.\n var options = $.extend({}, transportOptions, ajaxOptions);\n params = options.parameterParser(result(params));\n if ($.trim(params)) {\n var delimiter = (result(options.url).indexOf('?') === -1) ? '?' : '&';\n options.url = result(options.url) + delimiter + params;\n }\n return connect.call(this, 'DELETE', null, options);\n },\n head: function () {\n return connect.apply(this, ['head'].concat([].slice.call(arguments)));\n },\n options: function () {\n return connect.apply(this, ['options'].concat([].slice.call(arguments)));\n }\n };\n\n return $.extend(this, publicAPI);\n};\n","'use strict';\n\n// var isNode = false; FIXME: Browserify/minifyify has issues with the next link\n// var transport = (isNode) ? require('./node-http-transport') : require('./ajax-http-transport');\nvar transport = require('./ajax-http-transport');\nmodule.exports = transport;\n","/**\n/* Inherit from a class (using prototype borrowing)\n*/\n'use strict';\n\nfunction inherit(C, P) {\n var F = function () {};\n F.prototype = P.prototype;\n C.prototype = new F();\n C.__super = P.prototype;\n C.prototype.constructor = C;\n}\n\n/**\n* Shallow copy of an object\n* @param {Object} dest object to extend\n* @return {Object} extended object\n*/\nvar extend = function (dest /*, var_args*/) {\n var obj = Array.prototype.slice.call(arguments, 1);\n var current;\n for (var j = 0; j < obj.length; j++) {\n if (!(current = obj[j])) { //eslint-disable-line\n continue;\n }\n\n // do not wrap inner in dest.hasOwnProperty or bad things will happen\n for (var key in current) { //eslint-disable-line\n dest[key] = current[key];\n }\n }\n\n return dest;\n};\n\nmodule.exports = function (base, props, staticProps) {\n var parent = base;\n var child;\n\n child = props && props.hasOwnProperty('constructor') ? props.constructor : function () { return parent.apply(this, arguments); };\n\n // add static properties to the child constructor function\n extend(child, parent, staticProps);\n\n // associate prototype chain\n inherit(child, parent);\n\n // add instance properties\n if (props) {\n extend(child.prototype, props);\n }\n\n // done\n return child;\n};\n","'use strict';\n\nmodule.exports = {\n _pick: function (obj, props) {\n var res = {};\n for (var p in obj) {\n if (props.indexOf(p) !== -1) {\n res[p] = obj[p];\n }\n }\n\n return res;\n }\n};\n","'use strict';\n\nvar ConfigService = require('../service/configuration-service');\n\nvar urlConfig = new ConfigService().get('server');\nvar customDefaults = {};\nvar libDefaults = {\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n account: urlConfig.accountPath || undefined,\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n project: urlConfig.projectPath || undefined,\n isLocal: urlConfig.isLocalhost(),\n isCustomDomain: urlConfig.isCustomDomain,\n store: {}\n};\n\nvar optionUtils = {\n /**\n * Gets the final options by overriding the global options set with\n * optionUtils#setDefaults() and the lib defaults.\n * @param {object} options The final options object.\n * @return {object} Extended object\n */\n getOptions: function (options) {\n return $.extend(true, {}, libDefaults, customDefaults, options);\n },\n /**\n * Sets the global defaults for the optionUtils#getOptions() method.\n * @param {object} defaults The defaults object.\n */\n setDefaults: function (defaults) {\n customDefaults = defaults;\n }\n};\nmodule.exports = optionUtils;\n","/**\n * Utilities for working with query strings\n*/\n'use strict';\n\nmodule.exports = (function () {\n\n return {\n /**\n * Converts to matrix format\n * @param {Object} qs Object to convert to query string\n * @return { string} Matrix-format query parameters\n */\n toMatrixFormat: function (qs) {\n if (qs === null || qs === undefined || qs === '') {\n return ';';\n }\n if (typeof qs === 'string' || qs instanceof String) {\n return qs;\n }\n\n var returnArray = [];\n var OPERATORS = ['<', '>', '!'];\n $.each(qs, function (key, value) {\n if (typeof value !== 'string' || $.inArray($.trim(value).charAt(0), OPERATORS) === -1) {\n value = '=' + value;\n }\n returnArray.push(key + value);\n });\n\n var mtrx = ';' + returnArray.join(';');\n return mtrx;\n },\n\n /**\n * Converts strings/arrays/objects to type 'a=b&b=c'\n * @param { string|Array|Object} qs\n * @return { string}\n */\n toQueryFormat: function (qs) {\n if (qs === null || qs === undefined) {\n return '';\n }\n if (typeof qs === 'string' || qs instanceof String) {\n return qs;\n }\n\n var returnArray = [];\n $.each(qs, function (key, value) {\n if ($.isArray(value)) {\n value = value.join(',');\n }\n if ($.isPlainObject(value)) {\n //Mostly for data api\n value = JSON.stringify(value);\n }\n returnArray.push(key + '=' + value);\n });\n\n var result = returnArray.join('&');\n return result;\n },\n\n /**\n * Converts strings of type 'a=b&b=c' to { a:b, b:c}\n * @param { string} qs\n * @return {object}\n */\n qsToObject: function (qs) {\n if (qs === null || qs === undefined || qs === '') {\n return {};\n }\n\n var qsArray = qs.split('&');\n var returnObj = {};\n $.each(qsArray, function (index, value) {\n var qKey = value.split('=')[0];\n var qVal = value.split('=')[1];\n\n if (qVal.indexOf(',') !== -1) {\n qVal = qVal.split(',');\n }\n\n returnObj[qKey] = qVal;\n });\n\n return returnObj;\n },\n\n /**\n * Normalizes and merges strings of type 'a=b', { b:c} to { a:b, b:c}\n * @param { string|Array|Object} qs1\n * @param { string|Array|Object} qs2\n * @return {Object}\n */\n mergeQS: function (qs1, qs2) {\n var obj1 = this.qsToObject(this.toQueryFormat(qs1));\n var obj2 = this.qsToObject(this.toQueryFormat(qs2));\n return $.extend(true, {}, obj1, obj2);\n },\n\n addTrailingSlash: function (url) {\n if (!url) {\n return '';\n }\n return (url.charAt(url.length - 1) === '/') ? url : (url + '/');\n }\n };\n}());\n\n\n","/**\n * Utilities for working with the run service\n*/\n'use strict';\nvar qutil = require('./query-util');\nvar MAX_URL_LENGTH = 2048;\n\nmodule.exports = (function () {\n return {\n /**\n * returns operations of the form `[[op1,op2], [arg1, arg2]]`\n * @param {Object|Array|String} operations operations to perform\n * @param {Array} args arguments for operation\n * @return {String} Matrix-format query parameters\n */\n normalizeOperations: function (operations, args) {\n if (!args) {\n args = [];\n }\n var returnList = {\n ops: [],\n args: []\n };\n\n var _concat = function (arr) {\n return (arr !== null && arr !== undefined) ? [].concat(arr) : [];\n };\n\n //{ add: [1,2], subtract: [2,4] }\n var _normalizePlainObjects = function (operations, returnList) {\n if (!returnList) {\n returnList = { ops: [], args: [] };\n }\n $.each(operations, function (opn, arg) {\n returnList.ops.push(opn);\n returnList.args.push(_concat(arg));\n });\n return returnList;\n };\n //{ name: 'add', params: [1] }\n var _normalizeStructuredObjects = function (operation, returnList) {\n if (!returnList) {\n returnList = { ops: [], args: [] };\n }\n returnList.ops.push(operation.name);\n returnList.args.push(_concat(operation.params));\n return returnList;\n };\n\n var _normalizeObject = function (operation, returnList) {\n return ((operation.name) ? _normalizeStructuredObjects : _normalizePlainObjects)(operation, returnList);\n };\n\n var _normalizeLiterals = function (operation, args, returnList) {\n if (!returnList) {\n returnList = { ops: [], args: [] };\n }\n returnList.ops.push(operation);\n returnList.args.push(_concat(args));\n return returnList;\n };\n\n\n var _normalizeArrays = function (operations, arg, returnList) {\n if (!returnList) {\n returnList = { ops: [], args: [] };\n }\n $.each(operations, function (index, opn) {\n if ($.isPlainObject(opn)) {\n _normalizeObject(opn, returnList);\n } else {\n _normalizeLiterals(opn, args[index], returnList);\n }\n });\n return returnList;\n };\n\n if ($.isPlainObject(operations)) {\n _normalizeObject(operations, returnList);\n } else if ($.isArray(operations)) {\n _normalizeArrays(operations, args, returnList);\n } else {\n _normalizeLiterals(operations, args, returnList);\n }\n\n return returnList;\n },\n\n splitGetFactory: function (httpOptions) {\n return function (params, options) {\n var http = this; //eslint-disable-line\n var getValue = function (name) {\n var value = options[name] || httpOptions[name];\n if (typeof value === 'function') {\n value = value();\n }\n return value;\n };\n var getFinalUrl = function (params) {\n var url = getValue('url', options);\n var data = params;\n // There is easy (or known) way to get the final URL jquery is going to send so\n // we're replicating it. The process might change at some point but it probably will not.\n // 1. Remove hash\n url = url.replace(/#.*$/, '');\n // 1. Append query string\n var queryParams = qutil.toQueryFormat(data);\n var questionIdx = url.indexOf('?');\n if (queryParams && questionIdx > -1) {\n return url + '&' + queryParams;\n } else if (queryParams) {\n return url + '?' + queryParams;\n }\n return url;\n };\n var url = getFinalUrl(params);\n // We must split the GET in multiple short URL's\n // The only property allowed to be split is \"include\"\n if (params && params.include && encodeURI(url).length > MAX_URL_LENGTH) {\n var dtd = $.Deferred();\n var paramsCopy = $.extend(true, {}, params);\n delete paramsCopy.include;\n var urlNoIncludes = getFinalUrl(paramsCopy);\n var diff = MAX_URL_LENGTH - urlNoIncludes.length;\n var oldSuccess = options.success || httpOptions.success || $.noop;\n var oldError = options.error || httpOptions.error || $.noop;\n // remove the original success and error callbacks\n options.success = $.noop;\n options.error = $.noop;\n\n var include = params.include;\n var currIncludes = [];\n var includeOpts = [currIncludes];\n var currLength = encodeURIComponent('?include=').length;\n var variable = include.pop();\n while (variable) {\n var varLenght = encodeURIComponent(variable).length;\n // Use a greedy approach for now, can be optimized to be solved in a more\n // efficient way\n // + 1 is the comma\n if (currLength + varLenght + 1 < diff) {\n currIncludes.push(variable);\n currLength += varLenght + 1;\n } else {\n currIncludes = [variable];\n includeOpts.push(currIncludes);\n currLength = '?include='.length + varLenght;\n }\n variable = include.pop();\n }\n var reqs = $.map(includeOpts, function (include) {\n var reqParams = $.extend({}, params, { include: include });\n return http.get(reqParams, options);\n });\n $.when.apply($, reqs).then(function () {\n // Each argument are arrays of the arguments of each done request\n // So the first argument of the first array of arguments is the data\n var isValid = arguments[0] && arguments[0][0];\n if (!isValid) {\n // Should never happen...\n oldError();\n return dtd.reject();\n }\n var firstResponse = arguments[0][0];\n var isObject = $.isPlainObject(firstResponse);\n var isRunAPI = (isObject && $.isPlainObject(firstResponse.variables)) || !isObject;\n if (isRunAPI) {\n if (isObject) {\n // aggregate the variables property only\n var aggregateRun = arguments[0][0];\n $.each(arguments, function (idx, args) {\n var run = args[0];\n $.extend(true, aggregateRun.variables, run.variables);\n });\n oldSuccess(aggregateRun, arguments[0][1], arguments[0][2]);\n dtd.resolve(aggregateRun, arguments[0][1], arguments[0][2]);\n } else {\n // array of runs\n // Agregate variables in each run\n var aggregatedRuns = {};\n $.each(arguments, function (idx, args) {\n var runs = args[0];\n if (!$.isArray(runs)) {\n return;\n }\n $.each(runs, function (idxRun, run) {\n if (run.id && !aggregatedRuns[run.id]) {\n run.variables = run.variables || {};\n aggregatedRuns[run.id] = run;\n } else if (run.id) {\n $.extend(true, aggregatedRuns[run.id].variables, run.variables);\n }\n });\n });\n // turn it into an array\n aggregatedRuns = $.map(aggregatedRuns, function (run) { return run; });\n oldSuccess(aggregatedRuns, arguments[0][1], arguments[0][2]);\n dtd.resolve(aggregatedRuns, arguments[0][1], arguments[0][2]);\n }\n } else {\n // is variables API\n // aggregate the response\n var aggregatedVariables = {};\n $.each(arguments, function (idx, args) {\n var vars = args[0];\n $.extend(true, aggregatedVariables, vars);\n });\n oldSuccess(aggregatedVariables, arguments[0][1], arguments[0][2]);\n dtd.resolve(aggregatedVariables, arguments[0][1], arguments[0][2]);\n }\n }, function () {\n oldError.apply(http, arguments);\n dtd.reject.apply(dtd, arguments);\n });\n return dtd.promise();\n } else {\n return http.get(params, options);\n }\n };\n }\n };\n}());\n"]} \ No newline at end of file +{"version":3,"sources":["node_modules/browser-pack/_prelude.js","src/api-version.json","node_modules/Base64/base64.js","node_modules/object-assign/index.js","src/app.js","src/env-load.js","src/managers/auth-manager.js","src/managers/channel-manager.js","src/managers/epicenter-channel-manager.js","src/managers/key-names.js","src/managers/run-manager.js","src/managers/run-strategies/always-new-strategy.js","src/managers/run-strategies/conditional-creation-strategy.js","src/managers/run-strategies/multiplayer-strategy.js","src/managers/run-strategies/new-if-initialized-strategy.js","src/managers/run-strategies/new-if-missing-strategy.js","src/managers/run-strategies/new-if-persisted-strategy.js","src/managers/run-strategies/none-strategy.js","src/managers/run-strategies/persistent-single-player-strategy.js","src/managers/run-strategies/strategies-map.js","src/managers/scenario-manager.js","src/managers/special-operations.js","src/managers/world-manager.js","src/service/admin-file-service.js","src/service/asset-api-adapter.js","src/service/auth-api-service.js","src/service/channel-service.js","src/service/configuration-service.js","src/service/data-api-service.js","src/service/group-api-service.js","src/service/introspection-api-service.js","src/service/member-api-adapter.js","src/service/run-api-service.js","src/service/service-utils.js","src/service/state-api-adapter.js","src/service/url-config-service.js","src/service/user-api-adapter.js","src/service/variables-api-service.js","src/service/world-api-adapter.js","src/store/cookie-store.js","src/store/session-manager.js","src/store/store-factory.js","src/transport/ajax-http-transport.js","src/transport/http-transport-factory.js","src/util/inherit.js","src/util/object-util.js","src/util/option-utils.js","src/util/query-util.js","src/util/run-util.js"],"names":["InvalidCharacterError","message","this","object","exports","self","chars","prototype","Error","name","btoa","input","str","String","block","charCode","idx","map","output","charAt","charCodeAt","atob","replace","length","bs","buffer","bc","fromCharCode","indexOf","toObject","val","undefined","TypeError","Object","shouldUseNative","assign","test1","getOwnPropertyNames","test2","i","order2","n","join","test3","split","forEach","letter","keys","e","hasOwnProperty","propIsEnumerable","propertyIsEnumerable","module","target","source","from","to","symbols","s","arguments","key","call","getOwnPropertySymbols","F","util","factory","transport","store","service","manager","strategy","load","require","global","SKIP_ENV_LOAD","query","run","classFrom","Transport","Ajax","URL","Config","Run","File","Variables","Data","Auth","World","State","User","Member","Asset","Group","Introspect","Cookie","Store","ScenarioManager","RunManager","AuthManager","WorldManager","identity","ChannelManager","Channel","version","api","URLConfigService","envLoad","callback","urlService","infoUrl","getAPIPath","envPromise","$","ajax","url","async","then","res","overrides","defaults","extend","fail","options","sessionManager","SessionManager","getMergedOptions","authAdapter","AuthAdapter","MemberAdapter","GroupService","_pick","objectAssign","window","requiresGroup","_findUserInGroup","members","id","j","userId","login","me","$d","Deferred","adapterOptions","success","noop","error","outSuccess","outError","groupId","decodeToken","token","encoded","JSON","parse","handleGroupError","statusCode","data","logout","statusText","status","reject","handleSuccess","response","access_token","userInfo","oldGroups","getSession","groups","userGroupOpts","auth","user","project","isTeamMember","parent_account_id","sessionInfo","auth_token","account","user_id","saveSession","apply","resolve","handleGroupList","groupList","userGroups","group","filteredGroups","grep","resGroup","isFac","role","groupData","groupName","sessionInfoWithGroup","opts","groupService","getGroups","getUserGroups","promise","removeCookieFn","removeSession","getToken","httpOptions","session","params","memberInfo","memberAdapter","server","getGroupsForUser","getCurrentUserSessionInfo","addGroups","isArray","Array","each","index","extendedGroup","validProps","cometd","logLevel","websocketEnabled","ackEnabled","shareConnection","channel","handshake","defaultCometOptions","currentSubscriptions","_cometd","CometD","isConnected","connectionBroken","trigger","connectionSucceeded","configure","addListener","wasConnected","successful","bind","batch","subs","resubscribe","getChannel","isPlainObject","base","subscribe","subid","concat","unsubs","unsubscribe","removed","splice","on","event","off","validTypes","world","general","chat","getFromSessionOrError","value","sessionKeyName","settings","__super","EpicenterChannelManager","constructor","urlOpts","protocol","host","userName","userProp","ext","authorization","channelOpts","allowAllChannels","baseParts","channelType","getGroupChannel","baseTopic","getWorldChannel","worldid","getUserChannel","userid","getPresenceChannel","lastPingTime","PING_INTERVAL","notification","incomingUserId","online","Date","valueOf","setInterval","publish","now","getDataChannel","collection","oldsubs","topic","context","callbackWithCleanData","payload","meta","path","subType","date","actualData","EPI_SESSION_KEY","STRATEGY_SESSION_KEY","patchRunService","patched","orig","do","operation","reservedOps","specialOperations","RunService","StrategyCtor","strategiesMap","getRun","reset","runServiceOptions","ConditionalStrategy","Strategy","runService","createIf","headers","setRunInSession","sessionKey","getStore","set","stringify","runId","Base","keyNames","condition","_auth","runOptions","runOptionsWithScope","userSession","scope","opt","create","freshlyCreated","sessionStore","runSession","get","_loadAndCheck","shouldCreate","msg","IdentityStrategy","WorldApiAdapter","synchronous","_loadRun","worldApi","curUserId","curGroupName","getCurrentWorldForUser","newRunForWorld","model","dtd","loadRunFromWorld","getCurrentRunId","filter","serverError","getResponseHeader","initialized","StorageFactory","StateApi","_store","stateApi","_restoreRun","_getAllRuns","user.id","scope.group","runs","dateComp","a","b","latestRun","sort","shouldReplay","replay","resp","new-if-initialized","new-if-persisted","new-if-missing","always-new","multiplayer","persistent-single-player","none","validFilter","saved","getRuns","loadVariables","vars","include","save","_getService","archive","buildStrategy","worldId","resolveWith","WorldApi","getCurrentWorld","getCurrentRun","getAndRestoreLatestRun","currentWorldId","runOpts","rm","ConfigService","TransportFactory","config","uploadBody","fileName","contents","boundary","body","uploadFileOptions","filePath","pop","serviceOptions","folderType","upload","urlConfig","contentType","accountPath","projectPath","Authorization","http","publicAsyncAPI","getContents","put","replaceExisting","prom","post","xhr","conflictStatus","remove","delete","rename","newName","patch","apiEndpoint","fullUrl","processData","transportOptions","assetApiParams","scopeConfig","validateFilename","filename","validateUrlParams","partKeys","buildUrl","parts","method","toLowerCase","FormData","urlOptions","createOptions","publicAPI","getServiceOptions","getOptions","list","files","fullPathFiles","file","assetUrl","password","statusMessage","postParams","topicResolver","channelOptions","makeName","channelName","topics","subscriptionIds","push","returnObjs","console","warn","setEnv","env","property","qutil","root","getURL","addTrailingSlash","outputModifier","q","attrs","saveAs","serviceUtils","getDefaultOptions","finalOpts","finalParams","getApiUrl","byModel","modelFile","byRunID","runID","getFinalParams","patchUserActiveField","active","isString","objParams","getParms","getGroupDetails","makeUserActive","makeUserInactive","rutil","VariablesService","IntrospectionService","autoRestore","getFilterURL","toMatrixFormat","addAutoRestoreHeader","isFilterRunId","type","autorestoreOpts","X-AutoRestore","splitGet","splitGetFactory","setFilterOrThrowError","runApiParams","oldSuccess","qs","filters","attributes","opsArgs","postOptions","result","normalizeOperations","prms","args","ops","serial","operations","opParams","doSingleOp","op","shift","arg","parallel","queue","when","introspect","introspectionConfig","introspection","publicSyncAPI","getCurrentConfig","variables","vs","rest","slice","parseRunIdOrError","replayOptions","action","clone","getLocalHost","existingFn","localHostFn","isFunction","isLocal","epiVersion","location","pathname","UrlConfigService","envConf","isLocalhost","actingHost","API_PROTOCOL","HOST_API_MAPPING","forio.com","foriodev.com","publicExports","apiHost","isCustomDomain","pathHasApp","appPath","accnt","prj","versionPath","PROJECT_APIS","actualProtocol","configProtocol","apiPath","inArray","toQFilter","toIdFilters","getFilters","toQueryFormat","threshold","getById","variable","apiBase","assignmentEndpoint","projectEndpoint","setIdFilterOrThrowError","validateModelOrThrowError","worldApiParams","validParams","update","whitelist","updateOptions","deleteOptions","updateConfig","getWorldsForUser","addUsers","users","u","isObject","updateUser","patchOptions","removeUser","worlds","lastModified","currentWorld","deleteRun","currentRunOptions","autoAssign","maxUsers","getProjectSettings","document","cookie","newCookie","hostname","validHost","domain","setOptions","encodeURIComponent","cookieReg","RegExp","exec","decodeURIComponent","remOptions","destroy","aKeys","nIdx","cookieKey","optionUtils","EPI_MANAGER_KEY","managerOptions","getBaseOptions","libOptions","finalOptions","baseOptions","storeOpts","isEpicenterDomain","serialized","cookieName","sessionDefaults","qutils","404","parameterParser","xhrFields","withCredentials","d","connect","connectOptions","ALLOWED_TO_BE_FUNCTIONS","log","oldSuccessFn","ajaxStatus","ajaxReq","beforeSend","requestUrl","ajaxOptions","trim","delimiter","head","inherit","C","P","dest","obj","current","props","staticProps","parent","child","p","customDefaults","libDefaults","setDefaults","returnArray","OPERATORS","mtrx","qsToObject","qsArray","returnObj","qKey","qVal","mergeQS","qs1","qs2","obj1","obj2","MAX_URL_LENGTH","returnList","_concat","arr","_normalizePlainObjects","opn","_normalizeStructuredObjects","_normalizeObject","_normalizeLiterals","_normalizeArrays","getValue","getFinalUrl","queryParams","questionIdx","encodeURI","paramsCopy","urlNoIncludes","diff","oldError","currIncludes","includeOpts","currLength","varLenght","reqs","reqParams","isValid","firstResponse","isRunAPI","aggregateRun","aggregatedRuns","idxRun","aggregatedVariables"],"mappings":"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","file":"bundle.js","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o> 8 - idx % 1 * 8)\n ) {\n charCode = str.charCodeAt(idx += 3/4);\n if (charCode > 0xFF) {\n throw new InvalidCharacterError(\"'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.\");\n }\n block = block << 8 | charCode;\n }\n return output;\n });\n\n // decoder\n // [https://gist.github.com/1020396] by [https://github.com/atk]\n object.atob || (\n object.atob = function (input) {\n var str = String(input).replace(/=+$/, '');\n if (str.length % 4 == 1) {\n throw new InvalidCharacterError(\"'atob' failed: The string to be decoded is not correctly encoded.\");\n }\n for (\n // initialize result and counters\n var bc = 0, bs, buffer, idx = 0, output = '';\n // get next character\n buffer = str.charAt(idx++);\n // character found in table? initialize bit storage and add its ascii value;\n ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,\n // and if not first of each 4 characters,\n // convert the first 8 bits to one ascii character\n bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0\n ) {\n // try to find character in table (0-63, not found => -1)\n buffer = chars.indexOf(buffer);\n }\n return output;\n });\n\n}());\n","'use strict';\n/* eslint-disable no-unused-vars */\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nvar propIsEnumerable = Object.prototype.propertyIsEnumerable;\n\nfunction toObject(val) {\n\tif (val === null || val === undefined) {\n\t\tthrow new TypeError('Object.assign cannot be called with null or undefined');\n\t}\n\n\treturn Object(val);\n}\n\nfunction shouldUseNative() {\n\ttry {\n\t\tif (!Object.assign) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Detect buggy property enumeration order in older V8 versions.\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=4118\n\t\tvar test1 = new String('abc'); // eslint-disable-line\n\t\ttest1[5] = 'de';\n\t\tif (Object.getOwnPropertyNames(test1)[0] === '5') {\n\t\t\treturn false;\n\t\t}\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=3056\n\t\tvar test2 = {};\n\t\tfor (var i = 0; i < 10; i++) {\n\t\t\ttest2['_' + String.fromCharCode(i)] = i;\n\t\t}\n\t\tvar order2 = Object.getOwnPropertyNames(test2).map(function (n) {\n\t\t\treturn test2[n];\n\t\t});\n\t\tif (order2.join('') !== '0123456789') {\n\t\t\treturn false;\n\t\t}\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=3056\n\t\tvar test3 = {};\n\t\t'abcdefghijklmnopqrst'.split('').forEach(function (letter) {\n\t\t\ttest3[letter] = letter;\n\t\t});\n\t\tif (Object.keys(Object.assign({}, test3)).join('') !==\n\t\t\t\t'abcdefghijklmnopqrst') {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t} catch (e) {\n\t\t// We don't expect any of the above to throw, but better to be safe.\n\t\treturn false;\n\t}\n}\n\nmodule.exports = shouldUseNative() ? Object.assign : function (target, source) {\n\tvar from;\n\tvar to = toObject(target);\n\tvar symbols;\n\n\tfor (var s = 1; s < arguments.length; s++) {\n\t\tfrom = Object(arguments[s]);\n\n\t\tfor (var key in from) {\n\t\t\tif (hasOwnProperty.call(from, key)) {\n\t\t\t\tto[key] = from[key];\n\t\t\t}\n\t\t}\n\n\t\tif (Object.getOwnPropertySymbols) {\n\t\t\tsymbols = Object.getOwnPropertySymbols(from);\n\t\t\tfor (var i = 0; i < symbols.length; i++) {\n\t\t\t\tif (propIsEnumerable.call(from, symbols[i])) {\n\t\t\t\t\tto[symbols[i]] = from[symbols[i]];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn to;\n};\n","/**\n * Epicenter Javascript libraries\n * v<%= version %>\n * https://github.com/forio/epicenter-js-libs\n */\n\nvar F = {\n util: {},\n factory: {},\n transport: {},\n store: {},\n service: {},\n manager: {\n strategy: {}\n },\n\n};\n\nF.load = require('./env-load');\n\nif (!global.SKIP_ENV_LOAD) {\n F.load();\n}\n\nF.util.query = require('./util/query-util');\nF.util.run = require('./util/run-util');\nF.util.classFrom = require('./util/inherit');\n\nF.factory.Transport = require('./transport/http-transport-factory');\nF.transport.Ajax = require('./transport/ajax-http-transport');\n\nF.service.URL = require('./service/url-config-service');\nF.service.Config = require('./service/configuration-service');\nF.service.Run = require('./service/run-api-service');\nF.service.File = require('./service/admin-file-service');\nF.service.Variables = require('./service/variables-api-service');\nF.service.Data = require('./service/data-api-service');\nF.service.Auth = require('./service/auth-api-service');\nF.service.World = require('./service/world-api-adapter');\nF.service.State = require('./service/state-api-adapter');\nF.service.User = require('./service/user-api-adapter');\nF.service.Member = require('./service/member-api-adapter');\nF.service.Asset = require('./service/asset-api-adapter');\nF.service.Group = require('./service/group-api-service');\nF.service.Introspect = require('./service/introspection-api-service');\n\nF.store.Cookie = require('./store/cookie-store');\nF.factory.Store = require('./store/store-factory');\n\nF.manager.ScenarioManager = require('./managers/scenario-manager');\nF.manager.RunManager = require('./managers/run-manager');\nF.manager.AuthManager = require('./managers/auth-manager');\nF.manager.WorldManager = require('./managers/world-manager');\n\nF.manager.strategy['always-new'] = require('./managers/run-strategies/always-new-strategy');\nF.manager.strategy['conditional-creation'] = require('./managers/run-strategies/conditional-creation-strategy');\nF.manager.strategy.identity = require('./managers/run-strategies/none-strategy');\nF.manager.strategy['new-if-missing'] = require('./managers/run-strategies/new-if-missing-strategy');\nF.manager.strategy['new-if-missing'] = require('./managers/run-strategies/new-if-missing-strategy');\nF.manager.strategy['new-if-persisted'] = require('./managers/run-strategies/new-if-persisted-strategy');\nF.manager.strategy['new-if-initialized'] = require('./managers/run-strategies/new-if-initialized-strategy');\n\nF.manager.ChannelManager = require('./managers/epicenter-channel-manager');\nF.service.Channel = require('./service/channel-service');\n\nF.version = '<%= version %>';\nF.api = require('./api-version.json');\n\nglobal.F = F;\nmodule.exports = F;\n","'use strict';\n\nvar URLConfigService = require('./service/url-config-service');\n\nvar envLoad = function (callback) {\n var urlService = new URLConfigService();\n var infoUrl = urlService.getAPIPath('config');\n var envPromise = $.ajax({ url: infoUrl, async: false });\n envPromise = envPromise.then(function (res) {\n var overrides = res.api;\n URLConfigService.defaults = $.extend(URLConfigService.defaults, overrides);\n });\n return envPromise.then(callback).fail(callback);\n};\n\nmodule.exports = envLoad;\n","/**\n* ## Authorization Manager\n*\n* The Authorization Manager provides an easy way to manage user authentication (logging in and out) and authorization (keeping track of tokens, sessions, and groups) for projects.\n*\n* The Authorization Manager is most useful for [team projects](../../../glossary/#team) with an access level of [Authenticated](../../../glossary/#access). These projects are accessed by [end users](../../../glossary/#users) who are members of one or more [groups](../../../glossary/#groups).\n*\n* #### Using the Authorization Manager\n*\n* To use the Authorization Manager, instantiate it. Then, make calls to any of the methods you need:\n*\n* var authMgr = new F.manager.AuthManager({\n* account: 'acme-simulations',\n* userName: 'enduser1',\n* password: 'passw0rd'\n* });\n* authMgr.login().then(function () {\n* authMgr.getCurrentUserSessionInfo();\n* });\n*\n*\n* The `options` object passed to the `F.manager.AuthManager()` call can include:\n*\n* * `account`: The account id for this `userName`. In the Epicenter UI, this is the **Team ID** (for team projects) or the **User ID** (for personal projects).\n* * `userName`: Email or username to use for logging in.\n* * `password`: Password for specified `userName`.\n* * `project`: The **Project ID** for the project to log this user into. Optional.\n* * `groupId`: Id of the group to which `userName` belongs. Required for end users if the `project` is specified.\n*\n* If you prefer starting from a template, the Epicenter JS Libs [Login Component](../../#components) uses the Authorization Manager as well. This sample HTML page (and associated CSS and JS files) provides a login form for team members and end users of your project. It also includes a group selector for end users that are members of multiple groups.\n*/\n\n'use strict';\nvar AuthAdapter = require('../service/auth-api-service');\nvar MemberAdapter = require('../service/member-api-adapter');\nvar GroupService = require('../service/group-api-service');\nvar SessionManager = require('../store/session-manager');\nvar _pick = require('../util/object-util')._pick;\nvar objectAssign = require('object-assign');\n\nvar atob = window.atob || require('Base64').atob;\n\nvar defaults = {\n requiresGroup: true\n};\n\nfunction AuthManager(options) {\n options = $.extend(true, {}, defaults, options);\n this.sessionManager = new SessionManager(options);\n this.options = this.sessionManager.getMergedOptions();\n\n this.authAdapter = new AuthAdapter(this.options);\n}\n\nvar _findUserInGroup = function (members, id) {\n for (var j = 0; j < members.length; j++) {\n if (members[j].userId === id) {\n return members[j];\n }\n }\n return null;\n};\n\nAuthManager.prototype = $.extend(AuthManager.prototype, {\n\n /**\n * Logs user in.\n *\n * **Example**\n *\n * authMgr.login({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * userName: 'enduser1',\n * password: 'passw0rd'\n * })\n * .then(function(statusObj) {\n * // if enduser1 belongs to exactly one group\n * // (or if the login() call is modified to include the group id)\n * // continue here\n * })\n * .fail(function(statusObj) {\n * // if enduser1 belongs to multiple groups,\n * // the login() call fails\n * // and returns all groups of which the user is a member\n * for (var i=0; i < statusObj.userGroups.length; i++) {\n * console.log(statusObj.userGroups[i].name, statusObj.userGroups[i].groupId);\n * }\n * });\n *\n * **Parameters**\n *\n * @param {Object} options (Optional) Overrides for configuration options. If not passed in when creating an instance of the manager (`F.manager.AuthManager()`), these options should include:\n * @param {string} options.account The account id for this `userName`. In the Epicenter UI, this is the **Team ID** (for team projects) or the **User ID** (for personal projects).\n * @param {string} options.userName Email or username to use for logging in.\n * @param {string} options.password Password for specified `userName`.\n * @param {string} options.project (Optional) The **Project ID** for the project to log this user into.\n * @param {string} options.groupId The id of the group to which `userName` belongs. Required for [end users](../../../glossary/#users) if the `project` is specified and if the end users are members of multiple [groups](../../../glossary/#groups), otherwise optional.\n * @return {Promise}\n */\n login: function (options) {\n var me = this;\n var $d = $.Deferred();\n var sessionManager = this.sessionManager;\n var adapterOptions = sessionManager.getMergedOptions({ success: $.noop, error: $.noop }, options);\n var outSuccess = adapterOptions.success;\n var outError = adapterOptions.error;\n var groupId = adapterOptions.groupId;\n\n var decodeToken = function (token) {\n var encoded = token.split('.')[1];\n while (encoded.length % 4 !== 0) { //eslint-disable-line\n encoded += '=';\n }\n return JSON.parse(atob(encoded));\n };\n\n var handleGroupError = function (message, statusCode, data) {\n // logout the user since it's in an invalid state with no group selected\n me.logout().then(function () {\n var error = $.extend(true, {}, data, { statusText: message, status: statusCode });\n $d.reject(error);\n });\n };\n\n var handleSuccess = function (response) {\n var token = response.access_token;\n var userInfo = decodeToken(token);\n var oldGroups = sessionManager.getSession(adapterOptions).groups || {};\n var userGroupOpts = $.extend(true, {}, adapterOptions, { success: $.noop });\n var data = { auth: response, user: userInfo };\n var project = adapterOptions.project;\n var isTeamMember = userInfo.parent_account_id === null;\n var requiresGroup = adapterOptions.requiresGroup && project;\n\n var sessionInfo = {\n auth_token: token,\n account: adapterOptions.account,\n project: project,\n userId: userInfo.user_id,\n groups: oldGroups,\n isTeamMember: isTeamMember\n };\n // The group is not required if the user is not logging into a project\n if (!requiresGroup) {\n sessionManager.saveSession(sessionInfo);\n outSuccess.apply(this, [data]);\n $d.resolve(data);\n return;\n }\n\n var handleGroupList = function (groupList) {\n data.userGroups = groupList;\n\n var group = null;\n if (groupList.length === 0) {\n handleGroupError('The user has no groups associated in this account', 401, data);\n return;\n } else if (groupList.length === 1) {\n // Select the only group\n group = groupList[0];\n } else if (groupList.length > 1) {\n if (groupId) {\n var filteredGroups = $.grep(groupList, function (resGroup) {\n return resGroup.groupId === groupId;\n });\n group = filteredGroups.length === 1 ? filteredGroups[0] : null;\n }\n }\n\n if (group) {\n // A team member does not get the group members because is calling the Group API\n // but it's automatically a fac user\n var isFac = isTeamMember ? true : _findUserInGroup(group.members, userInfo.user_id).role === 'facilitator';\n var groupData = {\n groupId: group.groupId,\n groupName: group.name,\n isFac: isFac\n };\n var sessionInfoWithGroup = objectAssign({}, sessionInfo, groupData);\n sessionInfo.groups[project] = groupData;\n me.sessionManager.saveSession(sessionInfoWithGroup, adapterOptions);\n outSuccess.apply(this, [data]);\n $d.resolve(data);\n } else {\n handleGroupError('This user is associated with more than one group. Please specify a group id to log into and try again', 403, data);\n }\n };\n\n if (!isTeamMember) {\n me.getUserGroups({ userId: userInfo.user_id, token: token }, userGroupOpts)\n .then(handleGroupList, $d.reject);\n } else {\n var opts = objectAssign({}, userGroupOpts, { token: token });\n var groupService = new GroupService(opts);\n groupService.getGroups({ account: adapterOptions.account, project: project })\n .then(function (groups) {\n // Group API returns id instead of groupId\n groups.forEach(function (group) {\n group.groupId = group.id;\n });\n handleGroupList(groups);\n }, $d.reject);\n }\n };\n\n adapterOptions.success = handleSuccess;\n adapterOptions.error = function (response) {\n if (adapterOptions.account) {\n // Try to login as a system user\n adapterOptions.account = null;\n adapterOptions.error = function () {\n outError.apply(this, arguments);\n $d.reject(response);\n };\n\n me.authAdapter.login(adapterOptions);\n return;\n }\n\n outError.apply(this, arguments);\n $d.reject(response);\n };\n\n this.authAdapter.login(adapterOptions);\n return $d.promise();\n },\n\n /**\n * Logs user out by clearing all session information.\n *\n * **Example**\n *\n * authMgr.logout();\n *\n * **Parameters**\n *\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n logout: function (options) {\n var me = this;\n var adapterOptions = this.sessionManager.getMergedOptions(options);\n\n var removeCookieFn = function (response) {\n me.sessionManager.removeSession();\n };\n\n return this.authAdapter.logout(adapterOptions).then(removeCookieFn);\n },\n\n /**\n * Returns the existing user access token if the user is already logged in. Otherwise, logs the user in, creating a new user access token, and returns the new token. (See [more background on access tokens](../../../project_access/)).\n *\n * **Example**\n *\n * authMgr.getToken()\n * .then(function (token) {\n * console.log('My token is ', token);\n * });\n *\n * **Parameters**\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n getToken: function (options) {\n var httpOptions = this.sessionManager.getMergedOptions(options);\n\n var session = this.sessionManager.getSession(httpOptions);\n var $d = $.Deferred();\n if (session.auth_token) {\n $d.resolve(session.auth_token);\n } else {\n this.login(httpOptions).then($d.resolve);\n }\n return $d.promise();\n },\n\n /**\n * Returns an array of group records, one for each group of which the current user is a member. Each group record includes the group `name`, `account`, `project`, and `groupId`.\n *\n * If some end users in your project are members of multiple groups, this is a useful method to call on your project's login page. When the user attempts to log in, you can use this to display the groups of which the user is member, and have the user select the correct group to log in to for this session.\n *\n * **Example**\n *\n * // get groups for current user\n * var sessionObj = authMgr.getCurrentUserSessionInfo();\n * authMgr.getUserGroups({ userId: sessionObj.userId, token: sessionObj.auth_token })\n * .then(function (groups) {\n * for (var i=0; i < groups.length; i++)\n * { console.log(groups[i].name); }\n * });\n *\n * // get groups for particular user\n * authMgr.getUserGroups({userId: 'b1c19dda-2d2e-4777-ad5d-3929f17e86d3', token: savedProjAccessToken });\n *\n * **Parameters**\n * @param {Object} params Object with a userId and token properties.\n * @param {String} params.userId The userId. If looking up groups for the currently logged in user, this is in the session information. Otherwise, pass a string.\n * @param {String} params.token The authorization credentials (access token) to use for checking the groups for this user. If looking up groups for the currently logged in user, this is in the session information. A team member's token or a project access token can access all the groups for all end users in the team or project.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n getUserGroups: function (params, options) {\n var adapterOptions = this.sessionManager.getMergedOptions({ success: $.noop }, options);\n var $d = $.Deferred();\n var outSuccess = adapterOptions.success;\n\n adapterOptions.success = function (memberInfo) {\n // The member API is at the account scope, we filter by project\n if (adapterOptions.project) {\n memberInfo = $.grep(memberInfo, function (group) {\n return group.project === adapterOptions.project;\n });\n }\n\n outSuccess.apply(this, [memberInfo]);\n $d.resolve(memberInfo);\n };\n\n var memberAdapter = new MemberAdapter({ token: params.token, server: adapterOptions.server });\n memberAdapter.getGroupsForUser(params, adapterOptions).fail($d.reject);\n return $d.promise();\n },\n\n /**\n * Returns session information for the current user, including the `userId`, `account`, `project`, `groupId`, `groupName`, `isFac` (whether the end user is a facilitator of this group), and `auth_token` (user access token).\n *\n * *Important*: This method is synchronous. The session information is returned immediately in an object; no callbacks or promises are needed.\n *\n * Session information is stored in a cookie in the browser.\n *\n * **Example**\n *\n * var sessionObj = authMgr.getCurrentUserSessionInfo();\n *\n * **Parameters**\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Object} session information\n */\n getCurrentUserSessionInfo: function (options) {\n var adapterOptions = this.sessionManager.getMergedOptions({ success: $.noop }, options);\n return this.sessionManager.getSession(adapterOptions);\n },\n\n /*\n * Adds one or more groups to the current session. \n *\n * This method assumes that the project and group exist and the user specified in the session is part of this project and group.\n *\n * Returns the new session object.\n *\n * **Example**\n *\n * authMgr.addGroups({ project: 'hello-world', groupName: 'groupName', groupId: 'groupId' });\n * authMgr.addGroups([{ project: 'hello-world', groupName: 'groupName', groupId: 'groupId' }, { project: 'hello-world', groupName: '...' }]);\n *\n * **Parameters**\n * @param {object|array} groups (Required) The group object must contain the `project` (**Project ID**) and `groupName` properties. If passing an array of such objects, all of the objects must contain *different* `project` (**Project ID**) values: although end users may be logged in to multiple projects at once, they may only be logged in to one group per project at a time.\n * @param {string} group.isFac (optional) Defaults to `false`. Set to `true` if the user in the session should be a facilitator in this group.\n * @param {string} group.groupId (optional) Defaults to undefined. Needed mostly for the Members API.\n * @return {Object} session information\n */\n addGroups: function (groups) {\n var session = this.getCurrentUserSessionInfo();\n var isArray = Array.isArray(groups);\n groups = isArray ? groups : [groups];\n\n $.each(groups, function (index, group) {\n var extendedGroup = $.extend({}, { isFac: false }, group);\n var project = extendedGroup.project;\n var validProps = ['groupName', 'groupId', 'isFac'];\n if (!project || !extendedGroup.groupName) {\n throw new Error('No project or groupName specified.');\n }\n // filter object\n extendedGroup = _pick(extendedGroup, validProps);\n session.groups[project] = extendedGroup;\n });\n this.sessionManager.saveSession(session);\n return session;\n }\n});\n\nmodule.exports = AuthManager;\n","'use strict';\n\n/**\n * ## Channel Manager\n *\n * There are two main use cases for the channel: event notifications and chat messages.\n *\n * The Channel Manager is a wrapper around the default [cometd JavaScript library](http://docs.cometd.org/2/reference/javascript.html), `$.cometd`. It provides a few nice features that `$.cometd` doesn't, including:\n *\n * * Automatic re-subscription to channels if you lose your connection\n * * Online / Offline notifications\n * * 'Events' for cometd notifications (instead of having to listen on specific meta channels)\n *\n * While you can work directly with the Channel Manager through Node.js (for example, `require('manager/channel-manager')`) -- or even work directly with `$.cometd` and Epicenter's underlying [Push Channel API](../../../rest_apis/multiplayer/channel/) -- most often it will be easiest to work with the [Epicenter Channel Manager](../epicenter-channel-manager/). The Epicenter Channel Manager is a wrapper that instantiates a Channel Manager with Epicenter-specific defaults.\n *\n * You'll need to include the `epicenter-multiplayer-dependencies.js` library in addition to the `epicenter.js` library in your project to use the Channel Manager. (See [Including Epicenter.js](../../#include).)\n *\n * To use the Channel Manager in client-side JavaScript, instantiate the [Epicenter Channel Manager](../epicenter-channel-manager/), get the channel, then use the channel's `subscribe()` and `publish()` methods to subscribe to topics or publish data to topics.\n *\n * var cm = new F.manager.ChannelManager();\n * var channel = cm.getChannel();\n *\n * channel.subscribe('topic', callback);\n * channel.publish('topic', { myData: 100 });\n *\n * The parameters for instantiating a Channel Manager include:\n *\n * * `options` The options object to configure the Channel Manager. Besides the common options listed here, see http://docs.cometd.org/reference/javascript.html for other supported options.\n * * `options.url` The Cometd endpoint URL.\n * * `options.websocketEnabled` Whether websocket support is active (boolean).\n * * `options.channel` Other defaults to pass on to instances of the underlying Channel Service. See [Channel Service](../channel-service/) for details.\n *\n */\n\nvar Channel = require('../service/channel-service');\nvar SessionManager = require('../store/session-manager');\n\nvar ChannelManager = function (options) {\n if (!$.cometd) {\n throw new Error('Cometd library not found. Please include epicenter-multiplayer-dependencies.js');\n }\n if (!options || !options.url) {\n throw new Error('Please provide an url for the cometd server');\n }\n\n var defaults = {\n /**\n * The Cometd endpoint URL.\n * @type {string}\n */\n url: '',\n\n /**\n * The log level for the channel (logs to console).\n * @type {string}\n */\n logLevel: 'info',\n\n /**\n * Whether websocket support is active. Defaults to `true`.\n * @type {boolean}\n */\n websocketEnabled: true,\n\n /**\n * Whether the ACK extension is enabled. See https://docs.cometd.org/current/reference/#_extensions_acknowledge for more info.\n * @type {boolean}\n */\n ackEnabled: true,\n\n /**\n * If false each instance of Channel will have a separate cometd connection to server, which could be noisy. Set to true to re-use the same connection across instances.\n * @type {boolean}\n */\n shareConnection: false,\n\n /**\n * Other defaults to pass on to instances of the underlying [Channel Service](../channel-service/), which are created through `getChannel()`.\n * @type {object}\n */\n channel: {\n\n },\n\n /**\n * Options to pass to the channel handshake.\n *\n * For example, the [Epicenter Channel Manager](../epicenter-channel-manager/) passes `ext` and authorization information. More information on possible options is in the details of the underlying [Push Channel API](../../../rest_apis/multiplayer/channel/).\n *\n * @type {object}\n */\n handshake: undefined\n };\n this.sessionManager = new SessionManager();\n var defaultCometOptions = this.sessionManager.getMergedOptions(defaults, options);\n this.currentSubscriptions = [];\n this.options = defaultCometOptions;\n\n if (defaultCometOptions.shareConnection && ChannelManager.prototype._cometd) {\n this.cometd = ChannelManager.prototype._cometd;\n return this;\n }\n var cometd = new $.CometD();\n ChannelManager.prototype._cometd = cometd;\n\n cometd.websocketEnabled = defaultCometOptions.websocketEnabled;\n cometd.ackEnabled = defaultCometOptions.ackEnabled;\n\n this.isConnected = false;\n var connectionBroken = function (message) {\n $(this).trigger('disconnect', message);\n };\n var connectionSucceeded = function (message) {\n $(this).trigger('connect', message);\n };\n var me = this;\n\n cometd.configure(defaultCometOptions);\n\n cometd.addListener('/meta/connect', function (message) {\n var wasConnected = this.isConnected;\n this.isConnected = (message.successful === true);\n if (!wasConnected && this.isConnected) { //Connecting for the first time\n connectionSucceeded.call(this, message);\n } else if (wasConnected && !this.isConnected) { //Only throw disconnected message fro the first disconnect, not once per try\n connectionBroken.call(this, message);\n }\n }.bind(this));\n\n cometd.addListener('/meta/disconnect', connectionBroken);\n\n cometd.addListener('/meta/handshake', function (message) {\n if (message.successful) {\n //http://docs.cometd.org/reference/javascript_subscribe.html#javascript_subscribe_meta_channels\n // ^ \"dynamic subscriptions are cleared (like any other subscription) and the application needs to figure out which dynamic subscription must be performed again\"\n cometd.batch(function () {\n $(me.currentSubscriptions).each(function (index, subs) {\n cometd.resubscribe(subs);\n });\n });\n }\n });\n\n //Other interesting events for reference\n cometd.addListener('/meta/subscribe', function (message) {\n $(me).trigger('subscribe', message);\n });\n cometd.addListener('/meta/unsubscribe', function (message) {\n $(me).trigger('unsubscribe', message);\n });\n cometd.addListener('/meta/publish', function (message) {\n $(me).trigger('publish', message);\n });\n cometd.addListener('/meta/unsuccessful', function (message) {\n $(me).trigger('error', message);\n });\n\n cometd.handshake(defaultCometOptions.handshake);\n\n this.cometd = cometd;\n};\n\n\nChannelManager.prototype = $.extend(ChannelManager.prototype, {\n\n /**\n * Creates and returns a channel, that is, an instance of a [Channel Service](../channel-service/).\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var channel = cm.getChannel();\n *\n * channel.subscribe('topic', callback);\n * channel.publish('topic', { myData: 100 });\n *\n * **Parameters**\n * @param {Object|String} options (Optional) If string, assumed to be the base channel url. If object, assumed to be configuration options for the constructor.\n * @return {Channel} Channel instance\n */\n getChannel: function (options) {\n //If you just want to pass in a string\n if (options && !$.isPlainObject(options)) {\n options = {\n base: options\n };\n }\n var defaults = {\n transport: this.cometd\n };\n var channel = new Channel($.extend(true, {}, this.options.channel, defaults, options));\n\n\n //Wrap subs and unsubs so we can use it to re-attach handlers after being disconnected\n var subs = channel.subscribe;\n channel.subscribe = function () {\n var subid = subs.apply(channel, arguments);\n this.currentSubscriptions = this.currentSubscriptions.concat(subid);\n return subid;\n }.bind(this);\n\n\n var unsubs = channel.unsubscribe;\n channel.unsubscribe = function () {\n var removed = unsubs.apply(channel, arguments);\n for (var i = 0; i < this.currentSubscriptions.length; i++) {\n if (this.currentSubscriptions[i].id === removed.id) {\n this.currentSubscriptions.splice(i, 1);\n }\n }\n return removed;\n }.bind(this);\n\n return channel;\n },\n\n /**\n * Start listening for events on this instance. Signature is same as for jQuery Events: http://api.jquery.com/on/.\n *\n * Supported events are: `connect`, `disconnect`, `subscribe`, `unsubscribe`, `publish`, `error`.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/on/.\n */\n on: function (event) {\n $(this).on.apply($(this), arguments);\n },\n\n /**\n * Stop listening for events on this instance. Signature is same as for jQuery Events: http://api.jquery.com/off/.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/off/.\n */\n off: function (event) {\n $(this).off.apply($(this), arguments);\n },\n\n /**\n * Trigger events and execute handlers. Signature is same as for jQuery Events: http://api.jquery.com/trigger/.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/trigger/.\n */\n trigger: function (event) {\n $(this).trigger.apply($(this), arguments);\n }\n});\n\nmodule.exports = ChannelManager;\n","'use strict';\n\n/**\n * ## Epicenter Channel Manager\n *\n * The Epicenter platform provides a push channel, which allows you to publish and subscribe to messages within a [project](../../../glossary/#projects), [group](../../../glossary/#groups), or [multiplayer world](../../../glossary/#world). There are two main use cases for the channel: event notifications and chat messages.\n *\n * The Epicenter Channel Manager is a wrapper around the (more generic) [Channel Manager](../channel-manager/), to instantiate it with Epicenter-specific defaults. If you are interested in including a notification or chat feature in your project, using an Epicenter Channel Manager is probably the easiest way to get started.\n *\n * You'll need to include the `epicenter-multiplayer-dependencies.js` library in addition to the `epicenter.js` library in your project to use the Epicenter Channel Manager. See [Including Epicenter.js](../../#include).\n *\n * To use the Epicenter Channel Manager: instantiate it, get the channel of the scope you want ([user](../../../glossary/#users), [world](../../../glossary/#world), or [group](../../../glossary/#groups)), then use the channel's `subscribe()` and `publish()` methods to subscribe to topics or publish data to topics.\n *\n * var cm = new F.manager.ChannelManager();\n * var gc = cm.getGroupChannel();\n * gc.subscribe('broadcasts', callback);\n *\n * For additional background on Epicenter's push channel, see the introductory notes on the [Push Channel API](../../../rest_apis/multiplayer/channel/) page.\n *\n * The parameters for instantiating an Epicenter Channel Manager include:\n *\n * * `options` Object with details about the Epicenter project for this Epicenter Channel Manager instance.\n * * `options.account` The Epicenter account id (**Team ID** for team projects, **User ID** for personal projects).\n * * `options.project` Epicenter project id.\n * * `options.userName` Epicenter userName used for authentication.\n * * `options.userId` Epicenter user id used for authentication. Optional; `options.userName` is preferred.\n * * `options.token` Epicenter token used for authentication. (You can retrieve this using `authManager.getToken()` from the [Authorization Manager](../auth-manager/).)\n * * `options.allowAllChannels` If not included or if set to `false`, all channel paths are validated; if your project requires [Push Channel Authorization](../../../updating_your_settings/), you should use this option. If you want to allow other channel paths, set to `true`; this is not common.\n */\n\nvar ChannelManager = require('./channel-manager');\nvar classFrom = require('../util/inherit');\nvar urlService = require('../service/url-config-service');\nvar SessionManager = require('../store/session-manager');\n\nvar validTypes = {\n project: true,\n group: true,\n world: true,\n user: true,\n data: true,\n general: true,\n chat: true\n};\nvar getFromSessionOrError = function (value, sessionKeyName, settings) {\n if (!value) {\n if (settings && settings[sessionKeyName]) {\n value = settings[sessionKeyName];\n } else {\n throw new Error(sessionKeyName + ' not found. Please log-in again, or specify ' + sessionKeyName + ' explicitly');\n }\n }\n return value;\n};\nvar __super = ChannelManager.prototype;\nvar EpicenterChannelManager = classFrom(ChannelManager, {\n constructor: function (options) {\n this.sessionManager = new SessionManager(options);\n var defaultCometOptions = this.sessionManager.getMergedOptions(options);\n\n var urlOpts = urlService(defaultCometOptions.server);\n if (!defaultCometOptions.url) {\n //Default epicenter cometd endpoint\n defaultCometOptions.url = urlOpts.protocol + '://' + urlOpts.host + '/channel/subscribe';\n }\n\n if (defaultCometOptions.handshake === undefined) {\n var userName = defaultCometOptions.userName;\n var userId = defaultCometOptions.userId;\n var token = defaultCometOptions.token;\n if ((userName || userId) && token) {\n var userProp = userName ? 'userName' : 'userId';\n var ext = {\n authorization: 'Bearer ' + token\n };\n ext[userProp] = userName ? userName : userId;\n\n defaultCometOptions.handshake = {\n ext: ext\n };\n }\n }\n\n this.options = defaultCometOptions;\n return __super.constructor.call(this, defaultCometOptions);\n },\n\n /**\n * Creates and returns a channel, that is, an instance of a [Channel Service](../channel-service/).\n *\n * This method enforces Epicenter-specific channel naming: all channels requested must be in the form `/{type}/{account id}/{project id}/{...}`, where `type` is one of `run`, `data`, `user`, `world`, or `chat`.\n *\n * **Example**\n *\n * var cm = new F.manager.EpicenterChannelManager();\n * var channel = cm.getChannel('/group/acme/supply-chain-game/');\n *\n * channel.subscribe('topic', callback);\n * channel.publish('topic', { myData: 100 });\n *\n * **Parameters**\n * @param {Object|String} options (Optional) If string, assumed to be the base channel url. If object, assumed to be configuration options for the constructor.\n * @return {Channel} Channel instance\n */\n getChannel: function (options) {\n if (options && typeof options !== 'object') {\n options = {\n base: options\n };\n }\n var channelOpts = $.extend({}, this.options, options);\n var base = channelOpts.base;\n if (!base) {\n throw new Error('No base topic was provided');\n }\n\n if (!channelOpts.allowAllChannels) {\n var baseParts = base.split('/');\n var channelType = baseParts[1];\n if (baseParts.length < 4) { //eslint-disable-line\n throw new Error('Invalid channel base name, it must be in the form /{type}/{account id}/{project id}/{...}');\n }\n if (!validTypes[channelType]) {\n throw new Error('Invalid channel type');\n }\n }\n return __super.getChannel.apply(this, arguments);\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) for the given [group](../../../glossary/#groups). The group must exist in the account (team) and project provided.\n *\n * There are no notifications from Epicenter on this channel; all messages are user-originated.\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var gc = cm.getGroupChannel();\n * gc.subscribe('broadcasts', callback);\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String} groupName (Optional) Group to broadcast to. If not provided, picks up group from current session if end user is logged in.\n * @return {Channel} Channel instance\n */\n getGroupChannel: function (groupName) {\n var session = this.sessionManager.getMergedOptions(this.options);\n groupName = getFromSessionOrError(groupName, 'groupName', session);\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n\n var baseTopic = ['/group', account, project, groupName].join('/');\n return __super.getChannel.call(this, { base: baseTopic });\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) for the given [world](../../../glossary/#world).\n *\n * This is typically used together with the [World Manager](../world-manager).\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var worldManager = new F.manager.WorldManager({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1',\n * run: { model: 'model.eqn' }\n * });\n * worldManager.getCurrentWorld().then(function (worldObject, worldAdapter) {\n * var worldChannel = cm.getWorldChannel(worldObject);\n * worldChannel.subscribe('', function (data) {\n * console.log(data);\n * });\n * });\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String|Object} world The world object or id.\n * @param {String} groupName (Optional) Group the world exists in. If not provided, picks up group from current session if end user is logged in.\n * @return {Channel} Channel instance\n */\n getWorldChannel: function (world, groupName) {\n var worldid = ($.isPlainObject(world) && world.id) ? world.id : world;\n if (!worldid) {\n throw new Error('Please specify a world id');\n }\n var session = this.sessionManager.getMergedOptions(this.options);\n\n groupName = getFromSessionOrError(groupName, 'groupName', session);\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n\n var baseTopic = ['/world', account, project, groupName, worldid].join('/');\n return __super.getChannel.call(this, { base: baseTopic });\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) for the current [end user](../../../glossary/#users) in that user's current [world](../../../glossary/#world).\n *\n * This is typically used together with the [World Manager](../world-manager). Note that this channel only gets notifications for worlds currently in memory. (See more background on [persistence](../../../run_persistence).)\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var worldManager = new F.manager.WorldManager({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1',\n * run: { model: 'model.eqn' }\n * });\n * worldManager.getCurrentWorld().then(function (worldObject, worldAdapter) {\n * var userChannel = cm.getUserChannel(worldObject);\n * userChannel.subscribe('', function (data) {\n * console.log(data);\n * });\n * });\n *\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String|Object} world World object or id.\n * @param {String|Object} user (Optional) User object or id. If not provided, picks up user id from current session if end user is logged in.\n * @param {String} groupName (Optional) Group the world exists in. If not provided, picks up group from current session if end user is logged in.\n * @return {Channel} Channel instance\n */\n getUserChannel: function (world, user, groupName) {\n var worldid = ($.isPlainObject(world) && world.id) ? world.id : world;\n if (!worldid) {\n throw new Error('Please specify a world id');\n }\n var session = this.sessionManager.getMergedOptions(this.options);\n\n var userid = ($.isPlainObject(user) && user.id) ? user.id : user;\n userid = getFromSessionOrError(userid, 'userId', session);\n groupName = getFromSessionOrError(groupName, 'groupName', session);\n\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n\n var baseTopic = ['/user', account, project, groupName, worldid, userid].join('/');\n return __super.getChannel.call(this, { base: baseTopic });\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) that automatically tracks the presence of an [end user](../../../glossary/#users), that is, whether the end user is currently online in this group and world. Notifications are automatically sent when the end user comes online, and when the end user goes offline (not present for more than 2 minutes). Useful in multiplayer games for letting each end user know whether other users in their shared world are also online.\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var worldManager = new F.manager.WorldManager({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * model: 'model.eqn'\n * });\n * worldManager.getCurrentWorld().then(function (worldObject, worldService) {\n * var presenceChannel = cm.getPresenceChannel(worldObject);\n * presenceChannel.on('presence', function (evt, notification) {\n * console.log(notification.online, notification.userId);\n * });\n * });\n *\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String|Object} world World object or id.\n * @param {String|Object} userid (Optional) User object or id. If not provided, picks up user id from current session if end user is logged in.\n * @param {String} groupName (Optional) Group the world exists in. If not provided, picks up group from current session if end user is logged in.\n * @return {Channel} Channel instance\n */\n getPresenceChannel: function (world, userid, groupName) {\n var worldid = ($.isPlainObject(world) && world.id) ? world.id : world;\n if (!worldid) {\n throw new Error('Please specify a world id');\n }\n\n var session = this.sessionManager.getMergedOptions(this.options);\n userid = getFromSessionOrError(userid, 'userId', session);\n groupName = getFromSessionOrError(groupName, 'groupName', session);\n\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n\n var baseTopic = ['/user', account, project, groupName, worldid].join('/');\n var channel = __super.getChannel.call(this, { base: baseTopic });\n\n var lastPingTime = { };\n\n var PING_INTERVAL = 6000;\n channel.subscribe('internal-ping-channel', function (notification) {\n var incomingUserId = notification.data.user;\n if (!lastPingTime[incomingUserId] && incomingUserId !== userid) {\n channel.trigger('presence', { userId: incomingUserId, online: true });\n }\n lastPingTime[incomingUserId] = (new Date()).valueOf();\n });\n\n setInterval(function () {\n channel.publish('internal-ping-channel', { user: userid });\n\n $.each(lastPingTime, function (key, value) {\n var now = (new Date()).valueOf();\n if (value && value + (PING_INTERVAL * 2) < now) {\n lastPingTime[key] = null;\n channel.trigger('presence', { userId: key, online: false });\n }\n });\n }, PING_INTERVAL);\n\n return channel;\n },\n\n /**\n * Create and return a publish/subscribe channel (from the underlying [Channel Manager](../channel-manager/)) for the given collection. (The collection name is specified in the `root` argument when the [Data Service](../data-api-service/) is instantiated.) Must be one of the collections in this account (team) and project.\n *\n * There are automatic notifications from Epicenter on this channel when data is created, updated, or deleted in this collection. See more on [automatic messages to the data channel](../../../rest_apis/multiplayer/channel/#data-messages).\n *\n * **Example**\n *\n * var cm = new F.manager.ChannelManager();\n * var dc = cm.getDataChannel('survey-responses');\n * dc.subscribe('', function(data, meta) {\n * console.log(data);\n *\n * // meta.date is time of change,\n * // meta.subType is the kind of change: new, update, or delete\n * // meta.path is the full path to the changed data\n * console.log(meta);\n * });\n *\n * **Return Value**\n *\n * * *Channel* Returns the channel (an instance of the [Channel Service](../channel-service/)).\n *\n * **Parameters**\n *\n * @param {String} collection Name of collection whose automatic notifications you want to receive.\n * @return {Channel} Channel instance\n */\n getDataChannel: function (collection) {\n if (!collection) {\n throw new Error('Please specify a collection to listen on.');\n }\n\n var session = this.sessionManager.getMergedOptions(this.options);\n var account = getFromSessionOrError('', 'account', session);\n var project = getFromSessionOrError('', 'project', session);\n var baseTopic = ['/data', account, project, collection].join('/');\n var channel = __super.getChannel.call(this, { base: baseTopic });\n\n //TODO: Fix after Epicenter bug is resolved\n var oldsubs = channel.subscribe;\n channel.subscribe = function (topic, callback, context, options) {\n var callbackWithCleanData = function (payload) {\n var meta = {\n path: payload.channel,\n subType: payload.data.subType,\n date: payload.data.date\n };\n var actualData = payload.data.data;\n if (actualData.data) { //Delete notifications are one data-level behind of course\n actualData = actualData.data;\n }\n\n callback.call(context, actualData, meta);\n };\n return oldsubs.call(channel, topic, callbackWithCleanData, context, options);\n };\n\n return channel;\n }\n});\n\nmodule.exports = EpicenterChannelManager;\n","'use strict';\n\nmodule.exports = {\n EPI_SESSION_KEY: 'epicenterjs.session',\n STRATEGY_SESSION_KEY: 'epicenter-scenario'\n};","/**\n* ## Run Manager\n*\n* The Run Manager gives you access to runs for your project. This allows you to read and update variables, call operations, etc. Additionally, the Run Manager gives you control over run creation depending on run states. Specifically, you can select [run creation strategies (rules)](../strategies/) for which runs end users of your project work with when they log in to your project.\n*\n* There are many ways to create new runs, including the Epicenter.js [Run Service](../run-api-service/), the RESFTful [Run API](../../../rest_apis/aggregate_run_api) and the [Model Run API](../../../rest_apis/other_apis/model_apis/run/). However, for some projects it makes more sense to pick up where the user left off, using an existing run. And in some projects, whether to create a new run or use an existing one is conditional, for example based on characteristics of the existing run or your own knowledge about the model. The Run Manager provides this level of control: your call to `getRun()`, rather than always returning a new run, returns a run based on the strategy you've specified. (Note that many of the Epicenter sample projects use a Run Service directly, because generally the sample projects are played in one end user session and don't care about run states or run strategies.)\n*\n*\n* ### Using the Run Manager to create and access runs\n*\n* To use the Run Manager, instantiate it by passing in:\n*\n* * `run`: (required) Run object. Must contain:\n* * `account`: Epicenter account id (**Team ID** for team projects, **User ID** for personal projects).\n* * `project`: Epicenter project id.\n* * `model`: The name of your primary model file. (See more on [Writing your Model](../../../writing_your_model/).)\n* * `scope`: (optional) Scope object for the run, for example `scope.group` with value of the name of the group.\n* * `server`: (optional) An object with one field, `host`. The value of `host` is the string `api.forio.com`, the URI of the Forio server. This is automatically set, but you can pass it explicitly if desired. It is most commonly used for clarity when you are [hosting an Epicenter project on your own server](../../../how_to/self_hosting/).\n* * `files`: (optional) If and only if you are using a Vensim model and you have additional data to pass in to your model, you can pass a `files` object with the names of the files, for example: `\"files\": {\"data\": \"myExtraData.xls\"}`. (Note that you'll also need to add this same files object to your Vensim [configuration file](../../../model_code/vensim/).) See the [underlying Model Run API](../../../rest_apis/other_apis/model_apis/run/#post-creating-a-new-run-for-this-project) for additional information.\n*\n* * `strategy`: (optional) Run creation strategy for when to create a new run and when to reuse an end user's existing run. See [Run Manager Strategies](../strategies/) for details. Defaults to `new-if-initialized`.\n*\n* * `sessionKey`: (optional) Name of browser cookie in which to store run information, including run id. Many conditional strategies, including the provided strategies, rely on this browser cookie to store the run id and help make the decision of whether to create a new run or use an existing one. The name of this cookie defaults to `epicenter-scenario` and can be set with the `sessionKey` parameter.\n*\n*\n* After instantiating a Run Manager, make a call to `getRun()` whenever you need to access a run for this end user. The `RunManager.run` contains the instantiated [Run Service](../run-api-service/). The Run Service allows you to access variables, call operations, etc.\n*\n* **Example**\n*\n* var rm = new F.manager.RunManager({\n* run: {\n* account: 'acme-simulations',\n* project: 'supply-chain-game',\n* model: 'supply-chain-model.jl',\n* server: { host: 'api.forio.com' }\n* },\n* strategy: 'always-new',\n* sessionKey: 'epicenter-session'\n* });\n* rm.getRun()\n* .then(function(run) {\n* // the return value of getRun() is a run object\n* var thisRunId = run.id;\n* // the RunManager.run also contains the instantiated Run Service,\n* // so any Run Service method is valid here\n* rm.run.do('runModel');\n* })\n*\n*/\n\n'use strict';\nvar strategiesMap = require('./run-strategies/strategies-map');\nvar specialOperations = require('./special-operations');\nvar RunService = require('../service/run-api-service');\n\n\nfunction patchRunService(service, manager) {\n if (service.patched) {\n return service;\n }\n\n var orig = service.do;\n service.do = function (operation, params, options) {\n var reservedOps = Object.keys(specialOperations);\n if (reservedOps.indexOf(operation) === -1) {\n return orig.apply(service, arguments);\n } else {\n return specialOperations[operation].call(service, params, options, manager);\n }\n };\n\n service.patched = true;\n\n return service;\n}\n\n\nvar defaults = {\n /**\n * Run creation strategy for when to create a new run and when to reuse an end user's existing run. See [Run Manager Strategies](../strategies/) for details. Defaults to `new-if-initialized`.\n * @type {String}\n */\n\n strategy: 'new-if-initialized'\n};\n\nfunction RunManager(options) {\n this.options = $.extend(true, {}, defaults, options);\n\n if (this.options.run instanceof RunService) {\n this.run = this.options.run;\n } else {\n this.run = new RunService(this.options.run);\n }\n\n patchRunService(this.run, this);\n\n var StrategyCtor = typeof this.options.strategy === 'function' ? this.options.strategy : strategiesMap[this.options.strategy];\n\n if (!StrategyCtor) {\n throw new Error('Specified run creation strategy was invalid:', this.options.strategy);\n }\n\n this.strategy = new StrategyCtor(this.run, this.options);\n}\n\nRunManager.prototype = {\n /**\n * Returns the run object for a 'good' run.\n *\n * A good run is defined by the strategy. For example, if the strategy is `always-new`, the call\n * to `getRun()` always returns a newly created run; if the strategy is `new-if-persisted`,\n * `getRun()` creates a new run if the previous run is in a persisted state, otherwise\n * it returns the previous run. See [Run Manager Strategies](../strategies/) for more on strategies.\n *\n * **Example**\n *\n * rm.getRun().then(function (run) {\n * // use the run object\n * var thisRunId = run.id;\n *\n * // use the Run Service object\n * rm.run.do('runModel');\n * });\n *\n * @return {$promise} Promise to complete the call.\n */\n getRun: function () {\n return this.strategy\n .getRun();\n },\n\n /**\n * Returns the run object for a new run, regardless of strategy: force creation of a new run.\n *\n * **Example**\n *\n * rm.reset().then(function (run) {\n * // use the (new) run object\n * var thisRunId = run.id;\n *\n * // use the Run Service object\n * rm.run.do('runModel');\n * });\n *\n * **Parameters**\n * @param {Object} runServiceOptions The options object to configure the Run Service. See [Run API Service](../run-api-service/) for more.\n * @return {Promise}\n */\n reset: function (runServiceOptions) {\n return this.strategy.reset(runServiceOptions);\n }\n};\n\nmodule.exports = RunManager;\n","/**\n * The `always-new` strategy always creates a new run for this end user irrespective of current state. This is equivalent to calling `F.service.Run.create()` from the [Run Service](../run-api-service/) every time. \n * \n * This strategy means that every time your end users refresh their browsers, they get a new run. \n * \n * This strategy can be useful for basic, single-page projects. This strategy is also useful for prototyping or project development: it creates a new run each time you refresh the page, and you can easily check the outputs of the model. However, typically you will use one of the other strategies for a production project.\n */\n\n'use strict';\n\nvar classFrom = require('../../util/inherit');\nvar ConditionalStrategy = require('./conditional-creation-strategy');\n\nvar __super = ConditionalStrategy.prototype;\n\nvar Strategy = classFrom(ConditionalStrategy, {\n constructor: function (runService, options) {\n __super.constructor.call(this, runService, this.createIf, options);\n },\n\n createIf: function (run, headers) {\n // always create a new run!\n return true;\n }\n});\n\nmodule.exports = Strategy;\n","'use strict';\n\nvar Base = require('./none-strategy');\nvar SessionManager = require('../../store/session-manager');\nvar classFrom = require('../../util/inherit');\nvar AuthManager = require('../auth-manager');\n\nvar keyNames = require('../key-names');\n\nvar defaults = {\n sessionKey: keyNames.STRATEGY_SESSION_KEY,\n path: ''\n};\n\nfunction setRunInSession(sessionKey, run, sessionManager) {\n sessionManager.getStore().set(sessionKey, JSON.stringify({ runId: run.id }));\n}\n\n/**\n* Conditional Creation Strategy\n* This strategy will try to get the run stored in the cookie and\n* evaluate if needs to create a new run by calling the 'condition' function\n*/\n\nvar Strategy = classFrom(Base, {\n constructor: function Strategy(runService, condition, options) {\n if (condition == null) { //eslint-disable-line\n //TODO: not sure why this is explicitly ==\n throw new Error('Conditional strategy needs a condition to create a run');\n }\n\n this._auth = new AuthManager();\n this.run = runService;\n this.condition = typeof condition !== 'function' ? function () { return condition; } : condition;\n this.options = $.extend(true, {}, defaults, options);\n this.sessionManager = new SessionManager(options);\n this.runOptions = this.options.run;\n },\n\n runOptionsWithScope: function () {\n var userSession = this._auth.getCurrentUserSessionInfo();\n return $.extend({\n scope: { group: userSession.groupName }\n }, this.runOptions);\n },\n\n reset: function (runServiceOptions) {\n var me = this;\n var opt = this.runOptionsWithScope();\n\n return this.run\n .create(opt, runServiceOptions)\n .then(function (run) {\n setRunInSession(me.options.sessionKey, run, me.sessionManager);\n run.freshlyCreated = true;\n return run;\n });\n },\n\n getRun: function () {\n var sessionStore = this.sessionManager.getStore();\n var runSession = JSON.parse(sessionStore.get(this.options.sessionKey));\n var me = this;\n if (runSession && runSession.runId) {\n return this._loadAndCheck(runSession).fail(function () {\n return me.reset(); //if it got the wrong cookie for e.g.\n });\n } else {\n return this.reset();\n }\n },\n\n _loadAndCheck: function (runSession) {\n var shouldCreate = false;\n var me = this;\n\n return this.run\n .load(runSession.runId, null, {\n success: function (run, msg, headers) {\n shouldCreate = me.condition(run, headers);\n }\n })\n .then(function (run) {\n if (shouldCreate) {\n var opt = me.runOptionsWithScope();\n return me.run.create(opt)\n .then(function (run) {\n setRunInSession(me.options.sessionKey, run, me.sessionManager);\n run.freshlyCreated = true;\n return run;\n });\n }\n return run;\n });\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `multiplayer` strategy is for use with [multiplayer worlds](../../../glossary/#world). It checks the current world for this end user, and always returns the current run for that world. This is equivalent to calling `getCurrentWorldForUser()` and then `getCurrentRunId()` from the [World API Adapater](../world-api-adapter/).\n * \n * Using this strategy means that end users in projects with multiplayer worlds always see the most current run and world. This ensures that they are in sync with the other end users sharing their world and run. In turn, this allows for competitive or collaborative multiplayer projects.\n */\n'use strict';\n\nvar classFrom = require('../../util/inherit');\n\nvar IdentityStrategy = require('./none-strategy');\nvar WorldApiAdapter = require('../../service/world-api-adapter');\nvar AuthManager = require('../auth-manager');\n\nvar defaults = {\n store: {\n synchronous: true\n }\n};\n\nvar Strategy = classFrom(IdentityStrategy, {\n\n constructor: function (runService, options) {\n this.runService = runService;\n this.options = $.extend(true, {}, defaults, options);\n this._auth = new AuthManager();\n this._loadRun = this._loadRun.bind(this);\n this.worldApi = new WorldApiAdapter(this.options.run);\n },\n\n reset: function () {\n var session = this._auth.getCurrentUserSessionInfo();\n var curUserId = session.userId;\n var curGroupName = session.groupName;\n\n return this.worldApi\n .getCurrentWorldForUser(curUserId, curGroupName)\n .then(function (world) {\n return this.worldApi.newRunForWorld(world.id);\n }.bind(this));\n },\n\n getRun: function () {\n var session = this._auth.getCurrentUserSessionInfo();\n var curUserId = session.userId;\n var curGroupName = session.groupName;\n var worldApi = this.worldApi;\n var model = this.options.model;\n var me = this;\n var dtd = $.Deferred();\n\n if (!curUserId) {\n return dtd.reject({ statusCode: 400, error: 'We need an authenticated user to join a multiplayer world. (ERR: no userId in session)' }, session).promise();\n }\n\n var loadRunFromWorld = function (world) {\n if (!world) {\n return dtd.reject({ statusCode: 404, error: 'The user is not in any world.' }, { options: me.options, session: session });\n }\n\n return worldApi.getCurrentRunId({ model: model, filter: world.id })\n .then(me._loadRun)\n .then(dtd.resolve)\n .fail(dtd.reject);\n };\n\n var serverError = function (error) {\n // is this possible?\n dtd.reject(error, session, me.options);\n };\n\n this.worldApi\n .getCurrentWorldForUser(curUserId, curGroupName)\n .then(loadRunFromWorld)\n .fail(serverError);\n\n return dtd.promise();\n },\n\n _loadRun: function (id, options) {\n return this.runService.load(id, null, options);\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `new-if-initialized` strategy creates a new run if the current one is in memory or has its `initialized` field set to `true`. The `initialized` field in the run record is automatically set to `true` at run creation for Vensim models; it can be set manually for other models.\n * \n * This strategy is useful if your project is structured such that immediately after a run is created, the model is executed completely (for example, a Vensim model is stepped to the end). It is similar to the `new-if-missing` strategy, except that it checks a field of the run record.\n * \n * Specifically, the strategy is:\n *\n * * Check the `sessionKey` cookie. \n * * This cookie is set by the [Run Manager](../run-manager/) and configurable through its options.\n * * If the cookie exists, check whether the run is in memory or only persisted in the database. Additionally, check whether the run's `initialized` field is `true`. \n * * If the run is in memory, create a new run.\n * * If the run's `initialized` field is `true`, create a new run.\n * * Otherwise, use the existing run.\n * * If the cookie does not exist, create a new run for this end user.\n */\n\n'use strict';\nvar classFrom = require('../../util/inherit');\nvar ConditionalStrategy = require('./conditional-creation-strategy');\n\nvar __super = ConditionalStrategy.prototype;\n\nvar Strategy = classFrom(ConditionalStrategy, {\n constructor: function (runService, options) {\n __super.constructor.call(this, runService, this.createIf, options);\n },\n\n createIf: function (run, headers) {\n return headers.getResponseHeader('pragma') === 'persistent' || run.initialized;\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `new-if-missing` strategy creates a new run when the current one is not in the browser cookie.\n * \n * Using this strategy means that when end users navigate between pages in your project, or refresh their browsers, they will still be working with the same run.\n *\n * This strategy is useful if your project is structured such that immediately after a run is created, the model is executed completely (for example, a Vensim model that is stepped to the end as soon as it is created). In other words, you care whether you have a run, but as long as you have one, you are certain that this run is the one you are interested in. \n * \n * Specifically, the strategy is:\n *\n * * Check the `sessionKey` cookie.\n * * This cookie is set by the [Run Manager](../run-manager/) and configurable through its options. \n * * If the cookie exists, use the run id stored there. \n * * If the cookie does not exist, create a new run for this end user. \n */\n\n'use strict';\n\nvar classFrom = require('../../util/inherit');\nvar ConditionalStrategy = require('./conditional-creation-strategy');\n\nvar __super = ConditionalStrategy.prototype;\n\n/*\n* create a new run only if nothing is stored in the cookie\n* this is useful for baseRuns.\n*/\nvar Strategy = classFrom(ConditionalStrategy, {\n constructor: function (runService, options) {\n __super.constructor.call(this, runService, this.createIf, options);\n },\n\n createIf: function (run, headers) {\n // if we are here, it means that the run exists... so we don't need a new one\n return false;\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `new-if-persisted` strategy creates a new run when the current one becomes persisted (end user is idle for a set period), but otherwise uses the current one. \n * \n * Using this strategy means that when end users navigate between pages in your project, or refresh their browsers, they will still be working with the same run. \n * \n * However, if they are idle for longer than your project's **Model Session Timeout** (configured in your project's [Settings](../../../updating_your_settings/)), then their run is persisted; the next time they interact with the project, they will get a new run. (See more background on [Run Persistence](../../../run_persistence/).)\n * \n * This strategy is useful for multi-page projects where end users play through a simulation in one sitting, stepping through the model sequentially (for example, a Vensim model that uses the `step` operation) or calling specific functions until the model is \"complete.\" However, you will need to guarantee that your end users will remain engaged with the project from beginning to end — or at least, if they are idle for longer than the **Model Session Timeout**, that it is okay for them to start the project from scratch (with an uninitialized model). \n * \n * Specifically, the strategy is:\n *\n * * Check the `sessionKey` cookie.\n * * This cookie is set by the [Run Manager](../run-manager/) and configurable through its options.\n * * If the cookie exists, check whether the run is in memory or only persisted in the database. \n * * If the run is in memory, use the run.\n * * If the run is only persisted (and not still in memory), create a new run for this end user.\n * * If the cookie does not exist, create a new run for this end user.\n */\n\n'use strict';\nvar classFrom = require('../../util/inherit');\nvar ConditionalStrategy = require('./conditional-creation-strategy');\n\nvar __super = ConditionalStrategy.prototype;\n\nvar Strategy = classFrom(ConditionalStrategy, {\n constructor: function (runService, options) {\n __super.constructor.call(this, runService, this.createIf, options);\n },\n\n createIf: function (run, headers) {\n return headers.getResponseHeader('pragma') === 'persistent';\n }\n});\n\nmodule.exports = Strategy;\n","/**\n * The `none` strategy never returns a run or tries to create a new run. It simply returns the contents of the current [Run Service instance](../run-api-service/).\n * \n * This strategy is useful if you want to manually decide how to create your own runs and don't want any automatic assistance. \n * \n * Also, this strategy is necessary if you are working with a multiplayer project and using the [World Manager](../world-manager/) — or other, similar situations where you do not have direct control over creating the [Run Service](../run-api-service/) instance.\n */\n\n'use strict';\n\nvar classFrom = require('../../util/inherit');\nvar Base = {};\n\n// Interface that all strategies need to implement\nmodule.exports = classFrom(Base, {\n constructor: function (runService, options) {\n this.runService = runService;\n },\n\n reset: function () {\n // return a newly created run\n return $.Deferred().resolve().promise();\n },\n\n getRun: function () {\n // return a usable run\n return $.Deferred().resolve(this.runService).promise();\n }\n});\n","/**\n * The `persistent-single-player` strategy returns the latest (most recent) run for this user, whether it is in memory or not. If there are no runs for this user, it creates a new one.\n *\n * This strategy is useful if your project executes your model step by step (as opposed to a project where the model is executed completely, for example, a Vensim model that is immediately stepped to the end). It is useful if end users play with your project for an extended period of time, possibly over several sessions.\n *\n * Specifically, the strategy is:\n * \n * * Check if there are any runs for this end user.\n * * If there are no runs (either in memory or in the database), create a new one.\n * * If there are runs, take the latest (most recent) one.\n * * If the most recent run is currently in the database, bring it back into memory so that the end user can continue working with it. (See more background on [Run Persistence](../../../run_persistence/), or read more on the underlying [State API](../../../rest_apis/other_apis/model_apis/state/) for bringing runs from the database back into memory.) \n */\n\n'use strict';\n\nvar classFrom = require('../../util/inherit');\nvar IdentityStrategy = require('./none-strategy');\nvar StorageFactory = require('../../store/store-factory');\nvar StateApi = require('../../service/state-api-adapter');\nvar AuthManager = require('../auth-manager');\n\nvar keyNames = require('../key-names');\n\nvar defaults = {\n store: {\n synchronous: true\n }\n};\n\nvar Strategy = classFrom(IdentityStrategy, {\n constructor: function Strategy(runService, options) {\n this.run = runService;\n this.options = $.extend(true, {}, defaults, options);\n this.runOptions = this.options.run;\n this._store = new StorageFactory(this.options.store);\n this.stateApi = new StateApi();\n this._auth = new AuthManager();\n\n this._loadAndCheck = this._loadAndCheck.bind(this);\n this._restoreRun = this._restoreRun.bind(this);\n this._getAllRuns = this._getAllRuns.bind(this);\n this._loadRun = this._loadRun.bind(this);\n },\n\n reset: function (runServiceOptions) {\n var session = this._auth.getCurrentUserSessionInfo();\n var opt = $.extend({\n scope: { group: session.groupName }\n }, this.runOptions);\n\n return this.run\n .create(opt, runServiceOptions)\n .then(function (run) {\n run.freshlyCreated = true;\n return run;\n });\n },\n\n getRun: function () {\n return this._getAllRuns()\n .then(this._loadAndCheck);\n },\n\n _getAllRuns: function () {\n var session = JSON.parse(this._store.get(keyNames.EPI_SESSION_KEY) || '{}');\n return this.run.query({\n 'user.id': session.userId || '0000',\n 'scope.group': session.groupName\n });\n },\n\n _loadAndCheck: function (runs) {\n if (!runs || !runs.length) {\n return this.reset();\n }\n\n var dateComp = function (a, b) { return new Date(b.date) - new Date(a.date); };\n var latestRun = runs.sort(dateComp)[0];\n var me = this;\n var shouldReplay = false;\n\n return this.run.load(latestRun.id, null, {\n success: function (run, msg, headers) {\n shouldReplay = headers.getResponseHeader('pragma') === 'persistent';\n }\n }).then(function (run) {\n return shouldReplay ? me._restoreRun(run.id) : run;\n });\n },\n\n _restoreRun: function (runId) {\n var me = this;\n return this.stateApi.replay({ runId: runId })\n .then(function (resp) {\n return me._loadRun(resp.run);\n });\n },\n\n _loadRun: function (id, options) {\n return this.run.load(id, null, options);\n }\n\n});\n\nmodule.exports = Strategy;\n","module.exports = {\n 'new-if-initialized': require('./new-if-initialized-strategy'),\n 'new-if-persisted': require('./new-if-persisted-strategy'),\n 'new-if-missing': require('./new-if-missing-strategy'),\n 'always-new': require('./always-new-strategy'),\n multiplayer: require('./multiplayer-strategy'),\n 'persistent-single-player': require('./persistent-single-player-strategy'),\n none: require('./none-strategy')\n};\n","'use strict';\nvar RunService = require('../service/run-api-service');\n\nvar defaults = {\n validFilter: { saved: true }\n};\n\nfunction ScenarioManager(options) {\n this.options = $.extend(true, {}, defaults, options);\n this.runService = this.options.run || new RunService(this.options);\n}\n\nScenarioManager.prototype = {\n getRuns: function (filter) {\n this.filter = $.extend(true, {}, this.options.validFilter, filter);\n return this.runService.query(this.filter);\n },\n\n loadVariables: function (vars) {\n return this.runService.query(this.filter, { include: vars });\n },\n\n save: function (run, meta) {\n return this._getService(run).save($.extend(true, {}, { saved: true }, meta));\n },\n\n archive: function (run) {\n return this._getService(run).save({ saved: false });\n },\n\n _getService: function (run) {\n if (typeof run === 'string') {\n return new RunService($.extend(true, {}, this.options, { filter: run }));\n }\n\n if (typeof run === 'object' && run instanceof RunService) {\n return run;\n }\n\n throw new Error('Save method requires a run service or a runId');\n },\n\n getRun: function (runId) {\n return new RunService($.extend(true, {}, this.options, { filter: runId }));\n }\n};\n\nmodule.exports = ScenarioManager;\n\n","'use strict';\n\n\nmodule.exports = {\n reset: function (params, options, manager) {\n return manager.reset(options);\n }\n};\n","/**\n* ## World Manager\n*\n* As discussed under the [World API Adapter](../world-api-adapter/), a [run](../../../glossary/#run) is a collection of end user interactions with a project and its model. For building multiplayer simulations you typically want multiple end users to share the same set of interactions, and work within a common state. Epicenter allows you to create \"worlds\" to handle such cases.\n*\n* The World Manager provides an easy way to track and access the current world and run for particular end users. It is typically used in pages that end users will interact with. (The related [World API Adapter](../world-api-adapter/) handles creating multiplayer worlds, and adding and removing end users and runs from a world. Because of this, typically the World Adapter is used for facilitator pages in your project.)\n*\n* ### Using the World Manager\n*\n* To use the World Manager, instantiate it. Then, make calls to any of the methods you need.\n*\n* When you instantiate a World Manager, the world's account id, project id, and group are automatically taken from the session (thanks to the [Authentication Service](../auth-api-service)).\n*\n* Note that the World Manager does *not* create worlds automatically. (This is different than the [Run Manager](../run-manager).) However, you can pass in specific options to any runs created by the manager, using a `run` object.\n*\n* The parameters for creating a World Manager are:\n*\n* * `account`: The **Team ID** in the Epicenter user interface for this project.\n* * `project`: The **Project ID** for this project.\n* * `group`: The **Group Name** for this world.\n* * `run`: Options to use when creating new runs with the manager, e.g. `run: { files: ['data.xls'] }`.\n* * `run.model`: The name of the primary model file for this project. Required if you have not already passed it in as part of the `options` parameter for an enclosing call.\n*\n* For example:\n*\n* var wMgr = new F.manager.WorldManager({\n* account: 'acme-simulations',\n* project: 'supply-chain-game',\n* run: { model: 'supply-chain.py' },\n* group: 'team1'\n* });\n*\n* wMgr.getCurrentRun();\n*/\n\n'use strict';\n\nvar WorldApi = require('../service/world-api-adapter');\nvar RunManager = require('./run-manager');\nvar AuthManager = require('./auth-manager');\nvar worldApi;\n\nfunction buildStrategy(worldId, dtd) {\n\n return function Ctor(runService, options) {\n this.runService = runService;\n this.options = options;\n\n $.extend(this, {\n reset: function () {\n throw new Error('not implementd. Need api changes');\n },\n\n getRun: function () {\n var me = this;\n //get or create!\n // Model is required in the options\n var model = this.options.run.model || this.options.model;\n return worldApi.getCurrentRunId({ model: model, filter: worldId })\n .then(function (runId) {\n return me.runService.load(runId);\n })\n .then(function (run) {\n dtd.resolveWith(me, [run]);\n })\n .fail(dtd.reject);\n }\n }\n );\n };\n}\n\n\nmodule.exports = function (options) {\n this.options = options || { run: {}, world: {} };\n\n $.extend(true, this.options, this.options.run);\n $.extend(true, this.options, this.options.world);\n\n worldApi = new WorldApi(this.options);\n this._auth = new AuthManager();\n var me = this;\n\n var api = {\n\n /**\n * Returns the current world (object) and an instance of the [World API Adapter](../world-api-adapter/).\n *\n * **Example**\n *\n * wMgr.getCurrentWorld()\n * .then(function(world, worldAdapter) {\n * console.log(world.id);\n * worldAdapter.getCurrentRunId();\n * });\n *\n * **Parameters**\n * @param {string} userId (Optional) The id of the user whose world is being accessed. Defaults to the user in the current session.\n * @param {string} groupName (Optional) The name of the group whose world is being accessed. Defaults to the group for the user in the current session.\n * @return {Promise}\n */\n getCurrentWorld: function (userId, groupName) {\n var session = this._auth.getCurrentUserSessionInfo();\n if (!userId) {\n userId = session.userId;\n }\n if (!groupName) {\n groupName = session.groupName;\n }\n return worldApi.getCurrentWorldForUser(userId, groupName);\n },\n\n /**\n * Returns the current run (object) and an instance of the [Run API Service](../run-api-service/).\n *\n * **Example**\n *\n * wMgr.getCurrentRun({model: 'myModel.py'})\n * .then(function(run, runService) {\n * console.log(run.id);\n * runService.do('startGame');\n * });\n *\n * **Parameters**\n * @param {string} model (Optional) The name of the model file. Required if not already passed in as `run.model` when the World Manager is created.\n * @return {Promise}\n */\n getCurrentRun: function (model) {\n var dtd = $.Deferred();\n var session = this._auth.getCurrentUserSessionInfo();\n var curUserId = session.userId;\n var curGroupName = session.groupName;\n\n function getAndRestoreLatestRun(world) {\n if (!world) {\n return dtd.reject({ error: 'The user is not part of any world!' });\n }\n\n var currentWorldId = world.id;\n var runOpts = $.extend(true, me.options, { model: model });\n var strategy = buildStrategy(currentWorldId, dtd);\n var opt = $.extend(true, {}, {\n strategy: strategy,\n run: runOpts\n });\n var rm = new RunManager(opt);\n\n return rm.getRun()\n .then(function (run) {\n dtd.resolve(run, rm.runService, rm);\n });\n }\n\n this.getCurrentWorld(curUserId, curGroupName)\n .then(getAndRestoreLatestRun);\n\n return dtd.promise();\n }\n };\n\n $.extend(this, api);\n};\n","/**\n * ## File API Service\n *\n * The File API Service allows you to upload and download files directly onto Epicenter, analogous to using the File Manager UI in Epicenter directly or SFTPing files in. It is based on the Epicenter File API.\n *\n * The Asset API Service (https://forio.com/epicenter/docs/public/api_adapters/generated/asset-api-adapter/) is typically used for all project use cases, and it's unlikely this File Service will be used directly except by Admin tools (e.g. Flow Inspector).\n *\n * Partially implemented.\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to undefined.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The project id. Defaults to undefined.\n * @type {String}\n */\n project: undefined,\n\n /**\n * The folder type. One of `model` | `static` | `node`.\n * @type {String}\n */\n folderType: 'static',\n\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {Object}\n */\n transport: {}\n };\n\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n if (serviceOptions.account) {\n urlConfig.accountPath = serviceOptions.account;\n }\n if (serviceOptions.project) {\n urlConfig.projectPath = serviceOptions.project;\n }\n\n var httpOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath('file')\n });\n\n if (serviceOptions.token) {\n httpOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(httpOptions);\n\n function uploadBody(fileName, contents) {\n var boundary = '---------------------------7da24f2e50046';\n\n return {\n body: '--' + boundary + '\\r\\n' +\n 'Content-Disposition: form-data; name=\"file\";' +\n 'filename=\"' + fileName + '\"\\r\\n' +\n 'Content-type: text/html\\r\\n\\r\\n' +\n contents + '\\r\\n' +\n '--' + boundary + '--',\n boundary: boundary\n };\n }\n\n function uploadFileOptions(filePath, contents, options) {\n filePath = filePath.split('/');\n var fileName = filePath.pop();\n filePath = filePath.join('/');\n var path = serviceOptions.folderType + '/' + filePath;\n var upload = uploadBody(fileName, contents);\n\n return $.extend(true, {}, serviceOptions, options, {\n url: urlConfig.getAPIPath('file') + path,\n data: upload.body,\n contentType: 'multipart/form-data; boundary=' + upload.boundary\n });\n }\n\n var publicAsyncAPI = {\n /**\n * Get a directory listing, or contents of a file.\n * @param {String} filePath Path to the file\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n getContents: function (filePath, options) {\n var path = serviceOptions.folderType + '/' + filePath;\n var httpOptions = $.extend(true, {}, serviceOptions, options, {\n url: urlConfig.getAPIPath('file') + path\n });\n return http.get('', httpOptions);\n },\n\n /**\n * Replaces the file at the given file path.\n * @param {String} filePath Path to the file\n * @param {String} contents Contents to write to file\n * @param {Object} options (Optional) Overrides for configuration options\n * @return {Promise}\n */\n replace: function (filePath, contents, options) {\n var httpOptions = uploadFileOptions(filePath, contents, options);\n\n return http.put(httpOptions.data, httpOptions);\n },\n\n /**\n * Creates a file in the given file path.\n * @param {String} filePath Path to the file\n * @param {String} contents Contents to write to file\n * @param {Boolean} replaceExisting Replace file if it already exists; defaults to false\n * @param {Object} options (Optional) Overrides for configuration options\n * @return {Promise}\n */\n create: function (filePath, contents, replaceExisting, options) {\n var httpOptions = uploadFileOptions(filePath, contents, options);\n var prom = http.post(httpOptions.data, httpOptions);\n var me = this;\n if (replaceExisting === true) {\n prom = prom.then(null, function (xhr) {\n var conflictStatus = 409;\n if (xhr.status === conflictStatus) {\n return me.replace(filePath, contents, options);\n }\n });\n }\n return prom;\n },\n\n /**\n * Removes the file.\n * @param {String} filePath Path to the file\n * @param {Object} options (Optional) Overrides for configuration options\n * @return {Promise}\n */\n remove: function (filePath, options) {\n var path = serviceOptions.folderType + '/' + filePath;\n var httpOptions = $.extend(true, {}, serviceOptions, options, {\n url: urlConfig.getAPIPath('file') + path\n });\n return http.delete(null, httpOptions);\n },\n\n /**\n * Renames the file.\n * @param {String} filePath Path to the file\n * @param {String} newName New name of file\n * @param {Object} options (Optional) Overrides for configuration options\n * @return {Promise}\n */\n rename: function (filePath, newName, options) {\n var path = serviceOptions.folderType + '/' + filePath;\n var httpOptions = $.extend(true, {}, serviceOptions, options, {\n url: urlConfig.getAPIPath('file') + path\n });\n return http.patch({ name: newName }, httpOptions);\n }\n };\n\n $.extend(this, publicAsyncAPI);\n};\n","/**\n * ## Asset API Adapter\n *\n * The Asset API Adapter allows you to store assets -- resources or files of any kind -- used by a project with a scope that is specific to project, group, or end user.\n *\n * Assets are used with [team projects](../../../project_admin/#team). One common use case is having end users in a [group](../../../glossary/#groups) or in a [multiplayer world](../../../glossary/#world) upload data -- videos created during game play, profile pictures for customizing their experience, etc. -- as part of playing through the project.\n *\n * Resources created using the Asset Adapter are scoped:\n *\n * * Project assets are writable only by [team members](../../../glossary/#team), that is, Epicenter authors.\n * * Group assets are writable by anyone with access to the project that is part of that particular [group](../../../glossary/#groups). This includes all [team members](../../../glossary/#team) (Epicenter authors) and any [end users](../../../glossary/#users) who are members of the group -- both facilitators and standard end users.\n * * User assets are writable by the specific end user, and by the facilitator of the group.\n * * All assets are readable by anyone with the exact URI.\n *\n * To use the Asset Adapter, instantiate it and then access the methods provided. Instantiating requires the account id (**Team ID** in the Epicenter user interface) and project id (**Project ID**). The group name is required for assets with a group scope, and the group name and userId are required for assets with a user scope. If not included, they are taken from the logged in user's session information if needed.\n *\n * When creating an asset, you can pass in text (encoded data) to the `create()` call. Alternatively, you can make the `create()` call as part of an HTML form and pass in a file uploaded via the form.\n *\n * // instantiate the Asset Adapter\n * var aa = new F.service.Asset({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1',\n * userId: '12345'\n * });\n *\n * // create a new asset using encoded text\n * aa.create('test.txt', {\n * encoding: 'BASE_64',\n * data: 'VGhpcyBpcyBhIHRlc3QgZmlsZS4=',\n * contentType: 'text/plain'\n * }, { scope: 'user' });\n *\n * // alternatively, create a new asset using a file uploaded through a form\n * // this sample code goes with an html form that looks like this:\n * //\n * //
\n * // \n * // \n * // \n * //
\n * //\n * $('#upload-file').on('submit', function (e) {\n * e.preventDefault();\n * var filename = $('#filename').val();\n * var data = new FormData();\n * var inputControl = $('#file')[0];\n * data.append('file', inputControl.files[0], filename);\n *\n * aa.create(filename, data, { scope: 'user' });\n * });\n *\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar _pick = require('../util/object-util')._pick;\nvar SessionManager = require('../store/session-manager');\n\nvar apiEndpoint = 'asset';\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects). If left undefined, taken from the URL.\n * @type {String}\n */\n account: undefined,\n /**\n * The project id. If left undefined, taken from the URL.\n * @type {String}\n */\n project: undefined,\n /**\n * The group name. Defaults to session's `groupName`.\n * @type {String}\n */\n group: undefined,\n /**\n * The user id. Defaults to session's `userId`.\n * @type {String}\n */\n userId: undefined,\n /**\n * The scope for the asset. Valid values are: `user`, `group`, and `project`. See above for the required permissions to write to each scope. Defaults to `user`, meaning the current end user or a facilitator in the end user's group can edit the asset.\n * @type {String}\n */\n scope: 'user',\n /**\n * Determines if a request to list the assets in a scope includes the complete URL for each asset (`true`), or only the file names of the assets (`false`). Defaults to `true`.\n * @type {boolean}\n */\n fullUrl: true,\n /**\n * The transport object contains the options passed to the XHR request.\n * @type {object}\n */\n transport: {\n processData: false\n }\n };\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n if (!serviceOptions.account) {\n serviceOptions.account = urlConfig.accountPath;\n }\n\n if (!serviceOptions.project) {\n serviceOptions.project = urlConfig.projectPath;\n }\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n\n var http = new TransportFactory(transportOptions);\n\n var assetApiParams = ['encoding', 'data', 'contentType'];\n var scopeConfig = {\n user: ['scope', 'account', 'project', 'group', 'userId'],\n group: ['scope', 'account', 'project', 'group'],\n project: ['scope', 'account', 'project'],\n };\n\n var validateFilename = function (filename) {\n if (!filename) {\n throw new Error('filename is needed.');\n }\n };\n\n var validateUrlParams = function (options) {\n var partKeys = scopeConfig[options.scope];\n if (!partKeys) {\n throw new Error('scope parameter is needed.');\n }\n\n $.each(partKeys, function () {\n if (!options[this]) {\n throw new Error(this + ' parameter is needed.');\n }\n });\n };\n\n var buildUrl = function (filename, options) {\n validateUrlParams(options);\n var partKeys = scopeConfig[options.scope];\n var parts = $.map(partKeys, function (key) {\n return options[key];\n });\n if (filename) {\n // This prevents adding a trailing / in the URL as the Asset API\n // does not work correctly with it\n filename = '/' + filename;\n }\n return urlConfig.getAPIPath(apiEndpoint) + parts.join('/') + filename;\n };\n\n // Private function, all requests follow a more or less same approach to\n // use the Asset API and the difference is the HTTP verb\n //\n // @param {string} method` (Required) HTTP verb\n // @param {string} filename` (Required) Name of the file to delete/replace/create\n // @param {object} params` (Optional) Body parameters to send to the Asset API\n // @param {object} options` (Optional) Options object to override global options.\n var upload = function (method, filename, params, options) {\n validateFilename(filename);\n // make sure the parameter is clean\n method = method.toLowerCase();\n var contentType = params instanceof FormData === true ? false : 'application/json';\n if (contentType === 'application/json') {\n // whitelist the fields that we actually can send to the api\n params = _pick(params, assetApiParams);\n } else { // else we're sending form data which goes directly in request body\n // For multipart/form-data uploads the filename is not set in the URL,\n // it's getting picked by the FormData field filename.\n filename = method === 'post' || method === 'put' ? '' : filename;\n }\n var urlOptions = $.extend({}, serviceOptions, options);\n var url = buildUrl(filename, urlOptions);\n var createOptions = $.extend(true, {}, urlOptions, { url: url, contentType: contentType });\n\n return http[method](params, createOptions);\n };\n\n var publicAPI = {\n /**\n * Creates a file in the Asset API. The server returns an error (status code `409`, conflict) if the file already exists, so\n * check first with a `list()` or a `get()`.\n *\n * **Example**\n *\n * var aa = new F.service.Asset({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1',\n * userId: ''\n * });\n *\n * // create a new asset using encoded text\n * aa.create('test.txt', {\n * encoding: 'BASE_64',\n * data: 'VGhpcyBpcyBhIHRlc3QgZmlsZS4=',\n * contentType: 'text/plain'\n * }, { scope: 'user' });\n *\n * // alternatively, create a new asset using a file uploaded through a form\n * // this sample code goes with an html form that looks like this:\n * //\n * //
\n * // \n * // \n * // \n * //
\n * //\n * $('#upload-file').on('submit', function (e) {\n * e.preventDefault();\n * var filename = $('#filename').val();\n * var data = new FormData();\n * var inputControl = $('#file')[0];\n * data.append('file', inputControl.files[0], filename);\n *\n * aa.create(filename, data, { scope: 'user' });\n * });\n *\n *\n * **Parameters**\n * @param {string} filename (Required) Name of the file to create.\n * @param {object} params (Optional) Body parameters to send to the Asset API. Required if the `options.transport.contentType` is `application/json`, otherwise ignored.\n * @param {string} params.encoding Either `HEX` or `BASE_64`. Required if `options.transport.contentType` is `application/json`.\n * @param {string} params.data The encoded data for the file. Required if `options.transport.contentType` is `application/json`.\n * @param {string} params.contentType The mime type of the file. Optional.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n create: function (filename, params, options) {\n return upload('post', filename, params, options);\n },\n\n /**\n * Gets a file from the Asset API, fetching the asset content. (To get a list\n * of the assets in a scope, use `list()`.)\n *\n * **Parameters**\n * @param {string} filename (Required) Name of the file to retrieve.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n get: function (filename, options) {\n var getServiceOptions = _pick(serviceOptions, ['scope', 'account', 'project', 'group', 'userId']);\n var urlOptions = $.extend({}, getServiceOptions, options);\n var url = buildUrl(filename, urlOptions);\n var getOptions = $.extend(true, {}, urlOptions, { url: url });\n\n return http.get({}, getOptions);\n },\n\n /**\n * Gets the list of the assets in a scope.\n *\n * **Example**\n *\n * aa.list({ fullUrl: true }).then(function(fileList){\n * console.log('array of files = ', fileList);\n * });\n *\n * **Parameters**\n * @param {object} options (Optional) Options object to override global options.\n * @param {string} options.scope (Optional) The scope (`user`, `group`, `project`).\n * @param {boolean} options.fullUrl (Optional) Determines if the list of assets in a scope includes the complete URL for each asset (`true`), or only the file names of the assets (`false`).\n * @return {Promise}\n */\n list: function (options) {\n var dtd = $.Deferred();\n var me = this;\n var urlOptions = $.extend({}, serviceOptions, options);\n var url = buildUrl('', urlOptions);\n var getOptions = $.extend(true, {}, urlOptions, { url: url });\n var fullUrl = getOptions.fullUrl;\n\n if (!fullUrl) {\n return http.get({}, getOptions);\n }\n\n http.get({}, getOptions)\n .then(function (files) {\n var fullPathFiles = $.map(files, function (file) {\n return buildUrl(file, urlOptions);\n });\n dtd.resolveWith(me, [fullPathFiles]);\n })\n .fail(dtd.reject);\n\n return dtd.promise();\n },\n\n /**\n * Replaces an existing file in the Asset API.\n *\n * **Example**\n *\n * // replace an asset using encoded text\n * aa.replace('test.txt', {\n * encoding: 'BASE_64',\n * data: 'VGhpcyBpcyBhIHNlY29uZCB0ZXN0IGZpbGUu',\n * contentType: 'text/plain'\n * }, { scope: 'user' });\n *\n * // alternatively, replace an asset using a file uploaded through a form\n * // this sample code goes with an html form that looks like this:\n * //\n * //
\n * // \n * // \n * // \n * //
\n * //\n * $('#replace-file').on('submit', function (e) {\n * e.preventDefault();\n * var filename = $('#replace-filename').val();\n * var data = new FormData();\n * var inputControl = $('#file')[0];\n * data.append('file', inputControl.files[0], filename);\n *\n * aa.replace(filename, data, { scope: 'user' });\n * });\n *\n * **Parameters**\n * @param {string} filename (Required) Name of the file being replaced.\n * @param {object} params (Optional) Body parameters to send to the Asset API. Required if the `options.transport.contentType` is `application/json`, otherwise ignored.\n * @param {string} params.encoding Either `HEX` or `BASE_64`. Required if `options.transport.contentType` is `application/json`.\n * @param {string} params.data The encoded data for the file. Required if `options.transport.contentType` is `application/json`.\n * @param {string} params.contentType The mime type of the file. Optional.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n replace: function (filename, params, options) {\n return upload('put', filename, params, options);\n },\n\n /**\n * Deletes a file from the Asset API.\n *\n * **Example**\n *\n * aa.delete(sampleFileName);\n *\n * **Parameters**\n * @param {string} filename (Required) Name of the file to delete.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n delete: function (filename, options) {\n return upload('delete', filename, {}, options);\n },\n\n assetUrl: function (filename, options) {\n var urlOptions = $.extend({}, serviceOptions, options);\n return buildUrl(filename, urlOptions);\n }\n };\n $.extend(this, publicAPI);\n};\n","/**\n *\n * ## Authentication API Service\n *\n * The Authentication API Service provides a method for logging in, which creates and returns a user access token.\n *\n * User access tokens are required for each call to Epicenter. (See [Project Access](../../../project_access/) for more information.)\n *\n * If you need additional functionality -- such as tracking session information, easily retrieving the user token, or getting the groups to which an end user belongs -- consider using the [Authorization Manager](../auth-manager/) instead.\n *\n * var auth = new F.service.Auth();\n * auth.login({ userName: 'jsmith@acmesimulations.com',\n * password: 'passw0rd' });\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * Email or username to use for logging in. Defaults to empty string.\n * @type {String}\n */\n userName: '',\n\n /**\n * Password for specified `userName`. Defaults to empty string.\n * @type {String}\n */\n password: '',\n\n /**\n * The account id for this `userName`. In the Epicenter UI, this is the **Team ID** (for team projects) or the **User ID** (for personal projects). Required if the `userName` is for an [end user](../../../glossary/#users). Defaults to empty string.\n * @type {String}\n */\n account: '',\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {Object}\n */\n transport: {}\n };\n var serviceOptions = $.extend({}, defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath('authentication')\n });\n var http = new TransportFactory(transportOptions);\n\n var publicAPI = {\n\n /**\n * Logs user in, returning the user access token.\n *\n * If no `userName` or `password` were provided in the initial configuration options, they are required in the `options` here. If no `account` was provided in the initial configuration options and the `userName` is for an [end user](../../../glossary/#users), the `account` is required as well.\n *\n * **Example**\n *\n * auth.login({\n * userName: 'jsmith',\n * password: 'passw0rd',\n * account: 'acme-simulations' })\n * .then(function (token) {\n * console.log(\"user access token is: \", token.access_token);\n * });\n *\n * **Parameters**\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n login: function (options) {\n var httpOptions = $.extend(true, { success: $.noop }, serviceOptions, options);\n if (!httpOptions.userName || !httpOptions.password) {\n var resp = { status: 401, statusMessage: 'No username or password specified.' };\n if (options.error) {\n options.error.call(this, resp);\n }\n\n return $.Deferred().reject(resp).promise();\n }\n\n var postParams = {\n userName: httpOptions.userName,\n password: httpOptions.password,\n };\n if (httpOptions.account) {\n //pass in null for account under options if you don't want it to be sent\n postParams.account = httpOptions.account;\n }\n\n return http.post(postParams, httpOptions);\n },\n\n // (replace with /* */ comment block, to make visible in docs, once this is more than a noop)\n //\n // Logs user out from specified accounts.\n //\n // Epicenter logout is not implemented yet, so for now this is a dummy promise that gets automatically resolved.\n //\n // **Example**\n //\n // auth.logout();\n //\n // **Parameters**\n // @param {Object} `options` (Optional) Overrides for configuration options.\n //\n logout: function (options) {\n var dtd = $.Deferred();\n dtd.resolve();\n return dtd.promise();\n }\n };\n\n $.extend(this, publicAPI);\n};\n","/**\n * ## Channel Service\n *\n * The Epicenter platform provides a push channel, which allows you to publish and subscribe to messages within a [project](../../../glossary/#projects), [group](../../../glossary/#groups), or [multiplayer world](../../../glossary/#world). There are two main use cases for the channel: event notifications and chat messages.\n *\n * The Channel Service is a building block for this functionality. It creates a publish-subscribe object, allowing you to publish messages, subscribe to messages, or unsubscribe from messages for a given 'topic' on a `$.cometd` transport instance.\n *\n * Typically, you use the [Epicenter Channel Manager](../epicenter-channel-manager/) to create or retrieve channels, then use the Channel Service `subscribe()` and `publish()` methods to listen to or update data. (For additional background on Epicenter's push channel, see the introductory notes on the [Push Channel API](../../../rest_apis/multiplayer/channel/) page.)\n *\n * You'll need to include the `epicenter-multiplayer-dependencies.js` library in addition to the `epicenter.js` library in your project to use the Channel Service. See [Including Epicenter.js](../../#include).\n *\n * To use the Channel Service, instantiate it, then make calls to any of the methods you need.\n *\n * var cs = new F.service.Channel();\n * cs.publish('/acme-simulations/supply-chain-game/fall-seminar/run/variables', { price: 50 });\n *\n * The parameters for instantiating a Channel Service include:\n *\n * * `options` The options object to configure the Channel Service.\n * * `options.base` The base topic. This is added as a prefix to all further topics you publish or subscribe to while working with this Channel Service.\n * * `options.topicResolver` A function that processes all 'topics' passed into the `publish` and `subscribe` methods. This is useful if you want to implement your own serialize functions for converting custom objects to topic names. Returns a String. By default, it just echoes the topic.\n * * `options.transport` The instance of `$.cometd` to hook onto. See http://docs.cometd.org/reference/javascript.html for additional background on cometd.\n */\n\n'use strict';\nvar Channel = function (options) {\n var defaults = {\n\n /**\n * The base topic. This is added as a prefix to all further topics you publish or subscribe to while working with this Channel Service.\n * @type {string}\n */\n base: '',\n\n /**\n * A function that processes all 'topics' passed into the `publish` and `subscribe` methods. This is useful if you want to implement your own serialize functions for converting custom objects to topic names. By default, it just echoes the topic.\n *\n * **Parameters**\n *\n * * `topic` Topic to parse.\n *\n * **Return Value**\n *\n * * *String*: This function should return a string topic.\n *\n * @type {function}\n * @param {String} topic topic to resolve\n * @return {String}\n */\n topicResolver: function (topic) {\n return topic;\n },\n\n /**\n * The instance of `$.cometd` to hook onto.\n * @type {object}\n */\n transport: null\n };\n this.channelOptions = $.extend(true, {}, defaults, options);\n};\n\nvar makeName = function (channelName, topic) {\n //Replace trailing/double slashes\n var newName = (channelName ? (channelName + '/' + topic) : topic).replace(/\\/\\//g, '/').replace(/\\/$/, '');\n return newName;\n};\n\n\nChannel.prototype = $.extend(Channel.prototype, {\n\n // future functionality:\n // // Set the context for the callback\n // cs.subscribe('run', function () { this.innerHTML = 'Triggered'}, document.body);\n //\n // // Control the order of operations by setting the `priority`\n // cs.subscribe('run', cb, this, {priority: 9});\n //\n // // Only execute the callback, `cb`, if the value of the `price` variable is 50\n // cs.subscribe('run/variables/price', cb, this, {priority: 30, value: 50});\n //\n // // Only execute the callback, `cb`, if the value of the `price` variable is greater than 50\n // subscribe('run/variables/price', cb, this, {priority: 30, value: '>50'});\n //\n // // Only execute the callback, `cb`, if the value of the `price` variable is even\n // subscribe('run/variables/price', cb, this, {priority: 30, value: function (val) {return val % 2 === 0}});\n\n\n /**\n * Subscribe to changes on a topic.\n *\n * The topic should include the full path of the account id (**Team ID** for team projects), project id, and group name. (In most cases, it is simpler to use the [Epicenter Channel Manager](../epicenter-channel-manager/) instead, in which case this is configured for you.)\n *\n * **Examples**\n *\n * var cb = function(val) { console.log(val.data); };\n *\n * // Subscribe to changes on a top-level 'run' topic\n * cs.subscribe('/acme-simulations/supply-chain-game/fall-seminar/run', cb);\n *\n * // Subscribe to changes on children of the 'run' topic. Note this will also be triggered for changes to run.x.y.z.\n * cs.subscribe('/acme-simulations/supply-chain-game/fall-seminar/run/*', cb);\n *\n * // Subscribe to changes on both the top-level 'run' topic and its children\n * cs.subscribe(['/acme-simulations/supply-chain-game/fall-seminar/run',\n * '/acme-simulations/supply-chain-game/fall-seminar/run/*'], cb);\n *\n * // Subscribe to changes on a particular variable\n * subscribe('/acme-simulations/supply-chain-game/fall-seminar/run/variables/price', cb);\n *\n *\n * **Return Value**\n *\n * * *String* Returns a token you can later use to unsubscribe.\n *\n * **Parameters**\n * @param {String|Array} topic List of topics to listen for changes on.\n * @param {Function} callback Callback function to execute. Callback is called with signature `(evt, payload, metadata)`.\n * @param {Object} context Context in which the `callback` is executed.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @param {Number} options.priority Used to control order of operations. Defaults to 0. Can be any +ve or -ve number.\n * @param {String|Number|Function} options.value The `callback` is only triggered if this condition matches. See examples for details.\n * @return {string} Subscription ID\n */\n subscribe: function (topic, callback, context, options) {\n\n var topics = [].concat(topic);\n var me = this;\n var subscriptionIds = [];\n var opts = me.channelOptions;\n\n opts.transport.batch(function () {\n $.each(topics, function (index, topic) {\n topic = makeName(opts.base, opts.topicResolver(topic));\n subscriptionIds.push(opts.transport.subscribe(topic, callback));\n });\n });\n return (subscriptionIds[1] ? subscriptionIds : subscriptionIds[0]);\n },\n\n /**\n * Publish data to a topic.\n *\n * **Examples**\n *\n * // Send data to all subscribers of the 'run' topic\n * cs.publish('/acme-simulations/supply-chain-game/fall-seminar/run', { completed: false });\n *\n * // Send data to all subscribers of the 'run/variables' topic\n * cs.publish('/acme-simulations/supply-chain-game/fall-seminar/run/variables', { price: 50 });\n *\n * **Parameters**\n *\n * @param {String} topic Topic to publish to.\n * @param {*} data Data to publish to topic.\n * @return {Array | Object} Responses to published data\n *\n */\n publish: function (topic, data) {\n var topics = [].concat(topic);\n var me = this;\n var returnObjs = [];\n var opts = me.channelOptions;\n\n\n opts.transport.batch(function () {\n $.each(topics, function (index, topic) {\n topic = makeName(opts.base, opts.topicResolver(topic));\n if (topic.charAt(topic.length - 1) === '*') {\n topic = topic.replace(/\\*+$/, '');\n console.warn('You can cannot publish to channels with wildcards. Publishing to ', topic, 'instead');\n }\n returnObjs.push(opts.transport.publish(topic, data));\n });\n });\n return (returnObjs[1] ? returnObjs : returnObjs[0]);\n },\n\n /**\n * Unsubscribe from changes to a topic.\n *\n * **Example**\n *\n * cs.unsubscribe('sampleToken');\n *\n * **Parameters**\n * @param {String} token The token for topic is returned when you initially subscribe. Pass it here to unsubscribe from that topic.\n * @return {Object} reference to current instance\n */\n unsubscribe: function (token) {\n this.channelOptions.transport.unsubscribe(token);\n return this;\n },\n\n /**\n * Start listening for events on this instance. Signature is same as for jQuery Events: http://api.jquery.com/on/.\n *\n * Supported events are: `connect`, `disconnect`, `subscribe`, `unsubscribe`, `publish`, `error`.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/on/.\n */\n on: function (event) {\n $(this).on.apply($(this), arguments);\n },\n\n /**\n * Stop listening for events on this instance. Signature is same as for jQuery Events: http://api.jquery.com/off/.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/off/.\n */\n off: function (event) {\n $(this).off.apply($(this), arguments);\n },\n\n /**\n * Trigger events and execute handlers. Signature is same as for jQuery Events: http://api.jquery.com/trigger/.\n *\n * **Parameters**\n *\n * @param {string} event The event type. See more detail at jQuery Events: http://api.jquery.com/trigger/.\n */\n trigger: function (event) {\n $(this).trigger.apply($(this), arguments);\n }\n\n});\n\nmodule.exports = Channel;\n","/**\n * @class ConfigurationService\n *\n * All services take in a configuration settings object to configure themselves. A JS hash {} is a valid configuration object, but optionally you can use the configuration service to toggle configs based on the environment\n *\n * @example\n * var cs = require('configuration-service')({\n * dev: { //environment\n port: 3000,\n host: 'localhost',\n },\n prod: {\n port: 8080,\n host: 'api.forio.com',\n logLevel: 'none'\n },\n logLevel: 'DEBUG' //global\n * });\n *\n * cs.get('logLevel'); //returns 'DEBUG'\n *\n * cs.setEnv('dev');\n * cs.get('logLevel'); //returns 'DEBUG'\n *\n * cs.setEnv('prod');\n * cs.get('logLevel'); //returns 'none'\n *\n */\n\n'use strict';\nvar urlService = require('./url-config-service');\n\nmodule.exports = function (config) {\n //TODO: Environments\n var defaults = {\n logLevel: 'NONE'\n };\n var serviceOptions = $.extend({}, defaults, config);\n serviceOptions.server = urlService(serviceOptions.server);\n\n return {\n\n data: serviceOptions,\n\n /**\n * Set the environment key to get configuration options from\n * @param { string} env\n */\n setEnv: function (env) {\n\n },\n\n /**\n * Get configuration.\n * @param { string} property optional\n * @return {*} Value of property if specified, the entire config object otherwise\n */\n get: function (property) {\n return serviceOptions[property];\n },\n\n /**\n * Set configuration.\n * @param { string|Object} key if a key is provided, set a key to that value. Otherwise merge object with current config\n * @param {*} value value for provided key\n */\n set: function (key, value) {\n serviceOptions[key] = value;\n }\n };\n};\n\n","/**\n * ## Data API Service\n *\n * The Data API Service allows you to create, access, and manipulate data related to any of your projects. Data are organized in collections. Each collection contains a document; each element of this top-level document is a JSON object. (See additional information on the underlying [Data API](../../../rest_apis/data_api/).)\n *\n * All API calls take in an \"options\" object as the last parameter. The options can be used to extend/override the Data API Service defaults. In particular, there are three required parameters when you instantiate the Data Service:\n *\n * * `account`: Epicenter account id (**Team ID** for team projects, **User ID** for personal projects).\n * * `project`: Epicenter project id.\n * * `root`: The the name of the collection. If you have multiple collections within each of your projects, you can also pass the collection name as an option for each call.\n *\n * var ds = new F.service.Data({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * root: 'survey-responses',\n * server: { host: 'api.forio.com' }\n * });\n * ds.saveAs('user1',\n * { 'question1': 2, 'question2': 10,\n * 'question3': false, 'question4': 'sometimes' } );\n * ds.saveAs('user2',\n * { 'question1': 3, 'question2': 8,\n * 'question3': true, 'question4': 'always' } );\n * ds.query('',{ 'question2': { '$gt': 9} });\n *\n * Note that in addition to the `account`, `project`, and `root`, the Data Service parameters optionally include a `server` object, whose `host` field contains the URI of the Forio server. This is automatically set, but you can pass it explicitly if desired. It is most commonly used for clarity when you are [hosting an Epicenter project on your own server](../../../how_to/self_hosting/).\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar qutil = require('../util/query-util');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * Name of collection. Required. Defaults to `/`, that is, the root level of your project at `forio.com/app/your-account-id/your-project-id/`, but must be set to a collection name.\n * @type {String}\n */\n root: '/',\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The project id. Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n project: undefined,\n\n /**\n * For operations that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n\n //Options to pass on to the underlying transport layer\n transport: {}\n };\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n\n var urlConfig = new ConfigService(serviceOptions).get('server');\n if (serviceOptions.account) {\n urlConfig.accountPath = serviceOptions.account;\n }\n if (serviceOptions.project) {\n urlConfig.projectPath = serviceOptions.project;\n }\n\n var getURL = function (key, root) {\n if (!root) {\n root = serviceOptions.root;\n }\n var url = urlConfig.getAPIPath('data') + qutil.addTrailingSlash(root);\n if (key) {\n url += qutil.addTrailingSlash(key);\n }\n return url;\n };\n\n var httpOptions = $.extend(true, {}, serviceOptions.transport, {\n url: getURL\n });\n if (serviceOptions.token) {\n httpOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(httpOptions);\n\n var publicAPI = {\n\n /**\n * Search for data within a collection.\n *\n * Searching using comparison or logical operators (as opposed to exact matches) requires MongoDB syntax. See the underlying [Data API](../../../rest_apis/data_api/#searching) for additional details.\n *\n * **Examples**\n *\n * // request all data associated with document 'user1'\n * ds.query('user1');\n *\n * // exact matching:\n * // request all documents in collection where 'question2' is 9\n * ds.query('', { 'question2': 9});\n *\n * // comparison operators:\n * // request all documents in collection\n * // where 'question2' is greater than 9\n * ds.query('', { 'question2': { '$gt': 9} });\n *\n * // logical operators:\n * // request all documents in collection\n * // where 'question2' is less than 10, and 'question3' is false\n * ds.query('', { '$and': [ { 'question2': { '$lt':10} }, { 'question3': false }] });\n *\n * // regular expresssions: use any Perl-compatible regular expressions\n * // request all documents in collection\n * // where 'question5' contains the string '.*day'\n * ds.query('', { 'question5': { '$regex': '.*day' } });\n *\n * **Parameters**\n * @param {String} key The name of the document to search. Pass the empty string ('') to search the entire collection.\n * @param {Object} query The query object. For exact matching, this object contains the field name and field value to match. For matching based on comparison, this object contains the field name and the comparison expression. For matching based on logical operators, this object contains an expression using MongoDB syntax. See the underlying [Data API](../../../rest_apis/data_api/#searching) for additional examples.\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise} \n */\n query: function (key, query, outputModifier, options) {\n var params = $.extend(true, { q: query }, outputModifier);\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions.url = getURL(key, httpOptions.root);\n return http.get(params, httpOptions);\n },\n\n /**\n * Save data in an anonymous document within the collection.\n *\n * The `root` of the collection must be specified. By default the `root` is taken from the Data Service configuration options; you can also pass the `root` to the `save` call explicitly by overriding the options (third parameter).\n *\n * (Additional background: Documents are top-level elements within a collection. Collections must be unique within this account (team or personal account) and project and are set with the `root` field in the `option` parameter. See the underlying [Data API](../../../rest_apis/data_api/) for more information. The `save` method is making a `POST` request.)\n *\n * **Example**\n *\n * // Create a new document, with one element, at the default root level\n * ds.save('question1', 'yes');\n *\n * // Create a new document, with two elements, at the default root level\n * ds.save({ question1:'yes', question2: 32 });\n *\n * // Create a new document, with two elements, at `/students/`\n * ds.save({ name:'John', className: 'CS101' }, { root: 'students' });\n *\n * **Parameters**\n *\n * @param {String|Object} key If `key` is a string, it is the id of the element to save (create) in this document. If `key` is an object, the object is the data to save (create) in this document. In both cases, the id for the document is generated automatically.\n * @param {Object} value (Optional) The data to save. If `key` is a string, this is the value to save. If `key` is an object, the value(s) to save are already part of `key` and this argument is not required.\n * @param {Object} options (Optional) Overrides for configuration options. If you want to override the default `root` of the collection, do so here.\n * @return {Promise} \n */\n save: function (key, value, options) {\n var attrs;\n if (typeof key === 'object') {\n attrs = key;\n options = value;\n } else {\n (attrs = {})[key] = value;\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions.url = getURL('', httpOptions.root);\n\n return http.post(attrs, httpOptions);\n },\n\n /**\n * Save (create or replace) data in a named document or element within the collection. \n * \n * The `root` of the collection must be specified. By default the `root` is taken from the Data Service configuration options; you can also pass the `root` to the `saveAs` call explicitly by overriding the options (third parameter).\n *\n * Optionally, the named document or element can include path information, so that you are saving just part of the document.\n *\n * (Additional background: Documents are top-level elements within a collection. Collections must be unique within this account (team or personal account) and project and are set with the `root` field in the `option` parameter. See the underlying [Data API](../../../rest_apis/data_api/) for more information. The `saveAs` method is making a `PUT` request.)\n *\n * **Example**\n *\n * // Create (or replace) the `user1` document at the default root level.\n * // Note that this replaces any existing content in the `user1` document.\n * ds.saveAs('user1',\n * { 'question1': 2, 'question2': 10,\n * 'question3': false, 'question4': 'sometimes' } );\n *\n * // Create (or replace) the `student1` document at the `students` root, \n * // that is, the data at `/students/student1/`.\n * // Note that this replaces any existing content in the `/students/student1/` document.\n * // However, this will keep existing content in other paths of this collection.\n * // For example, the data at `/students/student2/` is unchanged by this call.\n * ds.saveAs('student1',\n * { firstName: 'john', lastName: 'smith' },\n * { root: 'students' });\n *\n * // Create (or replace) the `mgmt100/groupB` document at the `myclasses` root,\n * // that is, the data at `/myclasses/mgmt100/groupB/`.\n * // Note that this replaces any existing content in the `/myclasses/mgmt100/groupB/` document.\n * // However, this will keep existing content in other paths of this collection.\n * // For example, the data at `/myclasses/mgmt100/groupA/` is unchanged by this call.\n * ds.saveAs('mgmt100/groupB',\n * { scenarioYear: '2015' },\n * { root: 'myclasses' });\n *\n * **Parameters**\n *\n * @param {String} key Id of the document.\n * @param {Object} value (Optional) The data to save, in key:value pairs.\n * @param {Object} options (Optional) Overrides for configuration options. If you want to override the default `root` of the collection, do so here.\n * @return {Promise} \n */\n saveAs: function (key, value, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions.url = getURL(key, httpOptions.root);\n\n return http.put(value, httpOptions);\n },\n\n /**\n * Get data for a specific document or field.\n *\n * **Example**\n *\n * ds.load('user1');\n * ds.load('user1/question3');\n *\n * **Parameters**\n * @param {String|Object} key The id of the data to return. Can be the id of a document, or a path to data within that document.\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options Overrides for configuration options.\n * @return {Promise} \n */\n load: function (key, outputModifier, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions.url = getURL(key, httpOptions.root);\n return http.get(outputModifier, httpOptions);\n },\n\n /**\n * Removes data from collection. Only documents (top-level elements in each collection) can be deleted.\n *\n * **Example**\n *\n * ds.remove('user1');\n *\n *\n * **Parameters**\n *\n * @param {String|Array} keys The id of the document to remove from this collection, or an array of such ids.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise} \n */\n remove: function (keys, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n var params;\n if ($.isArray(keys)) {\n params = { id: keys };\n } else {\n params = '';\n httpOptions.url = getURL(keys, httpOptions.root);\n }\n return http.delete(params, httpOptions);\n }\n\n // Epicenter doesn't allow nuking collections\n // /**\n // * Removes collection being referenced\n // * @return null\n // */\n // destroy: function (options) {\n // return this.remove('', options);\n // }\n };\n\n $.extend(this, publicAPI);\n};\n","/**\n *\n * ## Group API Adapter\n *\n * The Group API Adapter provides methods to look up, create, change or remove information about groups in a project. It is based on query capabilities of the underlying RESTful [Group API](../../../rest_apis/user_management/group/).\n *\n * This is only needed for Authenticated projects, that is, team projects with [end users and groups](../../../groups_and_end_users/).\n *\n * var ma = new F.service.Group({ token: 'user-or-project-access-token' });\n * ma.getGroupsForProject({ account: 'acme', project: 'sample' });\n */\n\n'use strict';\n\nvar serviceUtils = require('./service-utils');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar objectAssign = require('object-assign');\n\nvar apiEndpoint = 'group/local';\n\nvar GroupService = function (config) {\n var defaults = {\n /**\n * Epicenter account name. Defaults to undefined.\n * @type {string}\n */\n account: undefined,\n\n /**\n * Epicenter project name. Defaults to undefined.\n * @type {string}\n */\n project: undefined,\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {object}\n */\n transport: {}\n };\n var serviceOptions = serviceUtils.getDefaultOptions(defaults, config, { apiEndpoint: apiEndpoint });\n var transportOptions = serviceOptions.transport;\n delete serviceOptions.transport;\n var http = new TransportFactory(transportOptions, serviceOptions);\n var publicAPI = {\n /**\n * Gets information for a group or multiple groups.\n * @param {Object} params object with query parameters\n * @patam {string} params.q partial match for name, organization or event.\n * @patam {string} params.account Epicenter's Team ID\n * @patam {string} params.project Epicenter's Project ID\n * @patam {string} params.name Epicenter's Group Name\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n getGroups: function (params, options) {\n //groupID is part of the URL\n //q, account and project are part of the query string\n var finalOpts = objectAssign({}, serviceOptions, options);\n var finalParams;\n if (typeof params === 'string') {\n finalOpts.url = serviceUtils.getApiUrl(apiEndpoint + '/' + params, finalOpts);\n } else {\n finalParams = params;\n }\n return http.get(finalParams, finalOpts);\n }\n };\n objectAssign(this, publicAPI);\n};\n\nmodule.exports = GroupService;\n","/**\n *\n * ## Introspection API Service\n *\n * The Introspection API Service allows you to view a list of the variables and operations in a model. Typically used in conjunction with the [Run API Service](../run-api-service/).\n *\n * The Introspection API Service is not available for Forio SimLang.\n *\n * var intro = new F.service.Introspect({\n * account: 'acme-simulations',\n * project: 'supply-chain-game'\n * });\n * intro.byModel('supply-chain.py').then(function(data){ ... });\n * intro.byRunID('2b4d8f71-5c34-435a-8c16-9de674ab72e6').then(function(data){ ... });\n *\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\n\nvar apiEndpoint = 'model/introspect';\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The project id. Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n project: undefined,\n\n };\n\n var sessionManager = new SessionManager();\n var serviceOptions = sessionManager.getMergedOptions(defaults, config);\n\n var urlConfig = new ConfigService(serviceOptions).get('server');\n if (serviceOptions.account) {\n urlConfig.accountPath = serviceOptions.account;\n }\n if (serviceOptions.project) {\n urlConfig.projectPath = serviceOptions.project;\n }\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(transportOptions);\n\n var publicAPI = {\n /**\n * Get the available variables and operations for a given model file.\n *\n * Note: This does not work for any model which requires additional parameters, such as `files`.\n *\n * **Example**\n *\n * intro.byModel('abc.vmf')\n * .then(function(data) {\n * // data contains an object with available functions (used with operations API) and available variables (used with variables API)\n * console.log(data.functions);\n * console.log(data.variables);\n * });\n *\n * **Parameters**\n * @param {String} modelFile Name of the model file to introspect.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise} \n */\n byModel: function (modelFile, options) {\n var opts = $.extend(true, {}, serviceOptions, options);\n if (!opts.account || !opts.project) {\n throw new Error('Account and project are required when using introspect#byModel');\n }\n if (!modelFile) {\n throw new Error('modelFile is required when using introspect#byModel');\n }\n var url = { url: urlConfig.getAPIPath(apiEndpoint) + [opts.account, opts.project, modelFile].join('/') };\n var httpOptions = $.extend(true, {}, serviceOptions, options, url);\n return http.get('', httpOptions);\n },\n\n /**\n * Get the available variables and operations for a given model file.\n *\n * Note: This does not work for any model which requires additional parameters such as `files`.\n *\n * **Example**\n *\n * intro.byRunID('2b4d8f71-5c34-435a-8c16-9de674ab72e6')\n * .then(function(data) {\n * // data contains an object with available functions (used with operations API) and available variables (used with variables API)\n * console.log(data.functions);\n * console.log(data.variables);\n * });\n *\n * **Parameters**\n * @param {String} runID Id of the run to introspect.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise} \n */\n byRunID: function (runID, options) {\n if (!runID) {\n throw new Error('runID is required when using introspect#byModel');\n }\n var url = { url: urlConfig.getAPIPath(apiEndpoint) + runID };\n var httpOptions = $.extend(true, {}, serviceOptions, options, url);\n return http.get('', httpOptions);\n }\n };\n $.extend(this, publicAPI);\n};\n","/**\n *\n * ## Member API Adapter\n *\n * The Member API Adapter provides methods to look up information about end users for your project and how they are divided across groups. It is based on query capabilities of the underlying RESTful [Member API](../../../rest_apis/user_management/member/).\n *\n * This is only needed for Authenticated projects, that is, team projects with [end users and groups](../../../groups_and_end_users/). For example, if some of your end users are facilitators, or if your end users should be treated differently based on which group they are in, use the Member API to find that information.\n *\n * var ma = new F.service.Member({ token: 'user-or-project-access-token' });\n * ma.getGroupsForUser({ userId: 'b6b313a3-ab84-479c-baea-206f6bff337' });\n * ma.getGroupDetails({ groupId: '00b53308-9833-47f2-b21e-1278c07d53b8' });\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\nvar _pick = require('../util/object-util')._pick;\nvar apiEndpoint = 'member/local';\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * Epicenter user id. Defaults to a blank string.\n * @type {string}\n */\n userId: undefined,\n\n /**\n * Epicenter group id. Defaults to a blank string. Note that this is the group *id*, not the group *name*.\n * @type {string}\n */\n groupId: undefined,\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {object}\n */\n transport: {}\n };\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(transportOptions, serviceOptions);\n\n var getFinalParams = function (params) {\n if (typeof params === 'object') {\n return $.extend(true, serviceOptions, params);\n }\n return serviceOptions;\n };\n\n var patchUserActiveField = function (params, active, options) {\n var httpOptions = $.extend(true, serviceOptions, options, {\n url: urlConfig.getAPIPath(apiEndpoint) + params.groupId + '/' + params.userId\n });\n\n return http.patch({ active: active }, httpOptions);\n };\n\n var publicAPI = {\n\n /**\n * Retrieve details about all of the group memberships for one end user. The membership details are returned in an array, with one element (group record) for each group to which the end user belongs.\n *\n * In the membership array, each group record includes the group id, project id, account (team) id, and an array of members. However, only the user whose userId is included in the call is listed in the members array (regardless of whether there are other members in this group).\n *\n * **Example**\n *\n * var ma = new F.service.Member({ token: 'user-or-project-access-token' });\n * ma.getGroupsForUser('42836d4b-5b61-4fe4-80eb-3136e956ee5c')\n * .then(function(memberships){\n * for (var i=0; i 1,\n * // where variables.price has been persisted (recorded)\n * // in the model.\n * rs.query({\n * 'saved': 'true',\n * '.price': '>1'\n * },\n * {\n * startrecord: 2,\n * endrecord: 5\n * });\n *\n * **Parameters**\n * @param {Object} qs Query object. Each key can be a property of the run or the name of variable that has been saved in the run (prefaced by `variables.`). Each value can be a literal value, or a comparison operator and value. (See [more on filtering](../../../rest_apis/aggregate_run_api/#filters) allowed in the underlying Run API.) Querying for variables is available for runs [in memory](../../../run_persistence/#runs-in-memory) and for runs [in the database](../../../run_persistence/#runs-in-memory) if the variables are persisted (e.g. that have been `record`ed in your Julia model).\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n query: function (qs, outputModifier, options) {\n serviceOptions.filter = qs; //shouldn't be able to over-ride\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = urlConfig.addAutoRestoreHeader(httpOptions);\n\n return http.splitGet(outputModifier, httpOptions);\n },\n\n /**\n * Returns particular runs, based on conditions specified in the `qs` object.\n *\n * Similar to `.query()`.\n *\n * **Parameters**\n * @param {Object} filter Filter object. Each key can be a property of the run or the name of variable that has been saved in the run (prefaced by `variables.`). Each value can be a literal value, or a comparison operator and value. (See [more on filtering](../../../rest_apis/aggregate_run_api/#filters) allowed in the underlying Run API.) Filtering for variables is available for runs [in memory](../../../run_persistence/#runs-in-memory) and for runs [in the database](../../../run_persistence/#runs-in-memory) if the variables are persisted (e.g. that have been `record`ed in your Julia model).\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n filter: function (filter, outputModifier, options) {\n if ($.isPlainObject(serviceOptions.filter)) {\n $.extend(serviceOptions.filter, filter);\n } else {\n serviceOptions.filter = filter;\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = urlConfig.addAutoRestoreHeader(httpOptions);\n return http.splitGet(outputModifier, httpOptions);\n },\n\n /**\n * Get data for a specific run. This includes standard run data such as the account, model, project, and created and last modified dates. To request specific model variables, pass them as part of the `filters` parameter.\n *\n * Note that if the run is [in memory](../../../run_persistence/#runs-in-memory), any model variables are available; if the run is [in the database](../../../run_persistence/#runs-in-db), only model variables that have been persisted — that is, `record`ed in your Julia model — are available.\n *\n * **Example**\n *\n * rs.load('bb589677-d476-4971-a68e-0c58d191e450', { include: ['.price', '.sales'] });\n *\n * **Parameters**\n * @param {String} runID The run id.\n * @param {Object} filters (Optional) Object containing filters and operation modifiers. Use key `include` to list model variables that you want to include in the response. Other available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n load: function (runID, filters, options) {\n if (runID) {\n serviceOptions.filter = runID; //shouldn't be able to over-ride\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = urlConfig.addAutoRestoreHeader(httpOptions);\n return http.get(filters, httpOptions);\n },\n\n\n /**\n * Save attributes (data, model variables) of the run.\n *\n * **Examples**\n *\n * // add 'completed' field to run record\n * rs.save({ completed: true });\n *\n * // update 'saved' field of run record, and update values of model variables for this run\n * rs.save({ saved: true, variables: { a: 23, b: 23 } });\n *\n * **Parameters**\n * @param {Object} attributes The run data and variables to save.\n * @param {Object} attributes.variables Model variables must be included in a `variables` field within the `attributes` object. (Otherwise they are treated as run data and added to the run record directly.)\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n save: function (attributes, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n setFilterOrThrowError(httpOptions);\n return http.patch(attributes, httpOptions);\n },\n\n /**\n * Call a method from the model.\n *\n * Depending on the language in which you have written your model, the method may need to be exposed (e.g. `export` for a Julia model) in the model file in order to be called through the API. See [Writing your Model](../../../writing_your_model/)).\n *\n * The `params` argument is normally an array of arguments to the `operation`. In the special case where `operation` only takes one argument, you are not required to put that argument into an array.\n *\n * Note that you can combine the `operation` and `params` arguments into a single object if you prefer, as in the last example.\n *\n * **Examples**\n *\n * // method \"solve\" takes no arguments\n * rs.do('solve');\n * // method \"echo\" takes one argument, a string\n * rs.do('echo', ['hello']);\n * // method \"echo\" takes one argument, a string\n * rs.do('echo', 'hello');\n * // method \"sumArray\" takes one argument, an array\n * rs.do('sumArray', [[4,2,1]]);\n * // method \"add\" takes two arguments, both integers\n * rs.do({ name:'add', params:[2,4] });\n *\n * **Parameters**\n * @param {String} operation Name of method.\n * @param {Array} params (Optional) Any parameters the operation takes, passed as an array. In the special case where `operation` only takes one argument, you are not required to put that argument into an array, and can just pass it directly.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n do: function (operation, params, options) {\n // console.log('do', operation, params);\n var opsArgs;\n var postOptions;\n if (options) {\n opsArgs = params;\n postOptions = options;\n } else if ($.isPlainObject(params)) {\n opsArgs = null;\n postOptions = params;\n } else {\n opsArgs = params;\n }\n var result = rutil.normalizeOperations(operation, opsArgs);\n var httpOptions = $.extend(true, {}, serviceOptions, postOptions);\n\n setFilterOrThrowError(httpOptions);\n\n var prms = (result.args[0].length && (result.args[0] !== null && result.args[0] !== undefined)) ? result.args[0] : [];\n return http.post({ arguments: prms }, $.extend(true, {}, httpOptions, {\n url: urlConfig.getFilterURL() + 'operations/' + result.ops[0] + '/'\n }));\n },\n\n /**\n * Call several methods from the model, sequentially.\n *\n * Depending on the language in which you have written your model, the methods may need to be exposed (e.g. `export` for a Julia model) in the model file in order to be called through the API. See [Writing your Model](../../../writing_your_model/)).\n *\n * **Examples**\n *\n * // methods \"initialize\" and \"solve\" do not take any arguments\n * rs.serial(['initialize', 'solve']);\n * // methods \"init\" and \"reset\" take two arguments each\n * rs.serial([ { name: 'init', params: [1,2] },\n * { name: 'reset', params: [2,3] }]);\n * // method \"init\" takes two arguments,\n * // method \"runmodel\" takes none\n * rs.serial([ { name: 'init', params: [1,2] },\n * { name: 'runmodel', params: [] }]);\n *\n * **Parameters**\n * @param {Array} operations If none of the methods take parameters, pass an array of the method names (strings). If any of the methods do take parameters, pass an array of objects, each of which contains a method name and its own (possibly empty) array of parameters.\n * @param {*} params Parameters to pass to operations.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n serial: function (operations, params, options) {\n var opParams = rutil.normalizeOperations(operations, params);\n var ops = opParams.ops;\n var args = opParams.args;\n var me = this;\n\n var $d = $.Deferred();\n var postOptions = $.extend(true, {}, serviceOptions, options);\n\n var doSingleOp = function () {\n var op = ops.shift();\n var arg = args.shift();\n\n me.do(op, arg, {\n success: function () {\n if (ops.length) {\n doSingleOp();\n } else {\n $d.resolve.apply(this, arguments);\n postOptions.success.apply(this, arguments);\n }\n },\n error: function () {\n $d.reject.apply(this, arguments);\n postOptions.error.apply(this, arguments);\n }\n });\n };\n\n doSingleOp();\n\n return $d.promise();\n },\n\n /**\n * Call several methods from the model, executing them in parallel.\n *\n * Depending on the language in which you have written your model, the methods may need to be exposed (e.g. `export` for a Julia model) in the model file in order to be called through the API. See [Writing your Model](../../../writing_your_model/)).\n *\n * **Example**\n *\n * // methods \"solve\" and \"reset\" do not take any arguments\n * rs.parallel(['solve', 'reset']);\n * // methods \"add\" and \"subtract\" take two arguments each\n * rs.parallel([ { name: 'add', params: [1,2] },\n * { name: 'subtract', params:[2,3] }]);\n * // methods \"add\" and \"subtract\" take two arguments each\n * rs.parallel({ add: [1,2], subtract: [2,4] });\n *\n * **Parameters**\n * @param {Array|Object} operations If none of the methods take parameters, pass an array of the method names (as strings). If any of the methods do take parameters, you have two options. You can pass an array of objects, each of which contains a method name and its own (possibly empty) array of parameters. Alternatively, you can pass a single object with the method name and a (possibly empty) array of parameters.\n * @param {*} params Parameters to pass to operations.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n parallel: function (operations, params, options) {\n var $d = $.Deferred();\n\n var opParams = rutil.normalizeOperations(operations, params);\n var ops = opParams.ops;\n var args = opParams.args;\n var postOptions = $.extend(true, {}, serviceOptions, options);\n\n var queue = [];\n for (var i = 0; i < ops.length; i++) {\n queue.push(\n this.do(ops[i], args[i])\n );\n }\n $.when.apply(this, queue)\n .then(function () {\n $d.resolve.apply(this, arguments);\n postOptions.success.apply(this.arguments);\n })\n .fail(function () {\n $d.reject.apply(this, arguments);\n postOptions.error.apply(this.arguments);\n });\n\n return $d.promise();\n },\n\n /**\n * Shortcut to using the [Introspection API Service](../introspection-api-service/). Allows you to view a list of the variables and operations in a model.\n *\n * **Example**\n *\n * rs.introspect({ runID: 'cbf85437-b539-4977-a1fc-23515cf071bb' }).then(function (data) {\n * console.log(data.functions);\n * console.log(data.variables);\n * });\n *\n * **Parameters**\n * @param {Object} options Options can either be of the form `{ runID: }` or `{ model: }`.\n * @param {Object} introspectionConfig (Optional) Service options for Introspection Service\n * @return {Promise}\n */\n introspect: function (options, introspectionConfig) {\n var introspection = new IntrospectionService($.extend(true, {}, serviceOptions, introspectionConfig));\n if (options) {\n if (options.runID) {\n return introspection.byRunID(options.runID);\n } else if (options.model) {\n return introspection.byModel(options.model);\n }\n } else if (serviceOptions.id) {\n return introspection.byRunID(serviceOptions.id);\n } else {\n throw new Error('Please specify either the model or runid to introspect');\n }\n }\n };\n\n var publicSyncAPI = {\n getCurrentConfig: function () {\n return serviceOptions;\n },\n /**\n * Returns a Variables Service instance. Use the variables instance to load, save, and query for specific model variables. See the [Variable API Service](../variables-api-service/) for more information.\n *\n * **Example**\n *\n * var vs = rs.variables();\n * vs.save({ sample_int: 4 });\n *\n * **Parameters**\n * @param {Object} config (Optional) Overrides for configuration options.\n * @return {Object} variablesService Instance\n */\n variables: function (config) {\n var vs = new VariablesService($.extend(true, {}, serviceOptions, config, {\n runService: this\n }));\n return vs;\n }\n };\n\n $.extend(this, publicAsyncAPI);\n $.extend(this, publicSyncAPI);\n};\n","'use strict';\n\nvar ConfigService = require('./configuration-service');\nvar SessionManager = require('../store/session-manager');\nvar objectAssign = require('object-assign');\n\nvar serviceUtils = {\n /*\n * Gets the default options for a api service.\n * It will merge:\n * - The Session options (Using the Session Manager)\n * - The Authorization Header from the token option\n * - The full url from the endpoint option\n * With the supplied overrides and defaults\n *\n */\n getDefaultOptions: function (defaults) {\n var rest = Array.prototype.slice.call(arguments, 1);\n var sessionManager = new SessionManager();\n var serviceOptions = sessionManager.getMergedOptions.apply(sessionManager, [defaults].concat(rest));\n\n serviceOptions.transport = objectAssign({}, serviceOptions.transport, {\n url: this.getApiUrl(serviceOptions.apiEndpoint, serviceOptions)\n });\n\n if (serviceOptions.token) {\n serviceOptions.transport.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n return serviceOptions;\n },\n\n getApiUrl: function (apiEndpoint, serviceOptions) {\n var urlConfig = new ConfigService(serviceOptions).get('server');\n return urlConfig.getAPIPath(apiEndpoint);\n }\n};\n\nmodule.exports = serviceUtils;","'use strict';\n/**\n * ## State API Adapter\n *\n * The State API Adapter allows you to replay or clone runs. It brings existing, persisted run data from the database back into memory, using the same run id (`replay`) or a new run id (`clone`). Runs must be in memory in order for you to update variables or call operations on them.\n *\n * Specifically, the State API Adapter works by \"re-running\" the run (user interactions) from the creation of the run up to the time it was last persisted in the database. This process uses the current version of the run's model. Therefore, if the model has changed since the original run was created, the retrieved run will use the new model — and may end up having different values or behavior as a result. Use with care!\n *\n * To use the State API Adapter, instantiate it and then call its methods:\n *\n * var sa = new F.service.State();\n * sa.replay({runId: '1842bb5c-83ad-4ba8-a955-bd13cc2fdb4f'});\n *\n * The constructor takes an optional `options` parameter in which you can specify the `account` and `project` if they are not already available in the current context.\n *\n */\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar _pick = require('../util/object-util')._pick;\nvar SessionManager = require('../store/session-manager');\nvar apiEndpoint = 'model/state';\n\nmodule.exports = function (config) {\n\n var defaults = {\n\n };\n\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n\n var http = new TransportFactory(transportOptions);\n var parseRunIdOrError = function (params) {\n if ($.isPlainObject(params) && params.runId) {\n return params.runId;\n } else {\n throw new Error('Please pass in a run id');\n }\n };\n\n var publicAPI = {\n /**\n * Replay a run. After this call, the run, with its original run id, is now available [in memory](../../../run_persistence/#runs-in-memory). (It continues to be persisted into the Epicenter database at regular intervals.)\n *\n * **Example**\n *\n * var sa = new F.service.State();\n * sa.replay({runId: '1842bb5c-83ad-4ba8-a955-bd13cc2fdb4f', stopBefore: 'calculateScore'});\n *\n * **Parameters**\n * @param {object} params Parameters object.\n * @param {string} params.runId The id of the run to bring back to memory.\n * @param {string} params.stopBefore (Optional) The run is advanced only up to the first occurrence of this method.\n * @param {array} params.exclude (Optional) Array of methods to exclude when advancing the run.\n * @param {object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n replay: function (params, options) {\n var runId = parseRunIdOrError(params);\n\n var replayOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + runId }\n );\n\n params = $.extend(true, { action: 'replay' }, _pick(params, ['stopBefore', 'exclude']));\n\n return http.post(params, replayOptions);\n },\n\n /**\n * Clone a given run and return a new run in the same state as the given run.\n *\n * The new run id is now available [in memory](../../../run_persistence/#runs-in-memory). The new run includes a copy of all of the data from the original run, EXCEPT:\n *\n * * The `saved` field in the new run record is not copied from the original run record. It defaults to `false`.\n * * The `initialized` field in the new run record is not copied from the original run record. It defaults to `false` but may change to `true` as the new run is advanced. For example, if there has been a call to the `step` function (for Vensim models), the `initialized` field is set to `true`.\n * * The `created` field in the new run record is the date and time at which the clone was created (not the time that the original run was created.)\n *\n * The original run remains only [in the database](../../../run_persistence/#runs-in-db).\n *\n * **Example**\n *\n * var sa = new F.service.State();\n * sa.clone({runId: '1842bb5c-83ad-4ba8-a955-bd13cc2fdb4f', stopBefore: 'calculateScore', exclude: ['interimCalculation'] });\n *\n * **Parameters**\n * @param {object} params Parameters object.\n * @param {string} params.runId The id of the run to clone from memory.\n * @param {string} params.stopBefore (Optional) The newly cloned run is advanced only up to the first occurrence of this method.\n * @param {array} params.exclude (Optional) Array of methods to exclude when advancing the newly cloned run.\n * @param {object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n clone: function (params, options) {\n var runId = parseRunIdOrError(params);\n\n var replayOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + runId }\n );\n\n params = $.extend(true, { action: 'clone' }, _pick(params, ['stopBefore', 'exclude']));\n\n return http.post(params, replayOptions);\n }\n };\n\n $.extend(this, publicAPI);\n};\n","'use strict';\n\nvar epiVersion = require('../api-version.json');\n\n//TODO: urlutils to get host, since no window on node\nvar defaults = {\n host: window.location.host,\n pathname: window.location.pathname\n};\n\nfunction getLocalHost(existingFn, host) {\n var localHostFn;\n if (existingFn !== undefined) {\n if (!$.isFunction(existingFn)) {\n localHostFn = function () { return existingFn; };\n } else {\n localHostFn = existingFn;\n }\n } else {\n localHostFn = function () {\n var isLocal = !host || //phantomjs\n host === '127.0.0.1' || \n host.indexOf('local.') === 0 || \n host.indexOf('localhost') === 0;\n return isLocal;\n };\n }\n return localHostFn;\n}\n\nvar UrlConfigService = function (config) {\n var envConf = UrlConfigService.defaults;\n\n if (!config) {\n config = {};\n }\n // console.log(this.defaults);\n var overrides = $.extend({}, envConf, config);\n var options = $.extend({}, defaults, overrides);\n\n overrides.isLocalhost = options.isLocalhost = getLocalHost(options.isLocalhost, options.host);\n \n // console.log(isLocalhost(), '___________');\n var actingHost = config && config.host;\n if (!actingHost && options.isLocalhost()) {\n actingHost = 'forio.com';\n } else {\n actingHost = options.host;\n }\n\n var API_PROTOCOL = 'https';\n var HOST_API_MAPPING = {\n 'forio.com': 'api.forio.com',\n 'foriodev.com': 'api.epicenter.foriodev.com'\n };\n\n var publicExports = {\n protocol: API_PROTOCOL,\n\n api: '',\n\n //TODO: this should really be called 'apihost', but can't because that would break too many things\n host: (function () {\n var apiHost = (HOST_API_MAPPING[actingHost]) ? HOST_API_MAPPING[actingHost] : actingHost;\n // console.log(actingHost, config, apiHost);\n return apiHost;\n }()),\n\n isCustomDomain: (function () {\n var path = options.pathname.split('\\/');\n var pathHasApp = path && path[1] === 'app';\n return (!options.isLocalhost() && !pathHasApp);\n }()),\n\n appPath: (function () {\n var path = options.pathname.split('\\/');\n\n return path && path[1] || '';\n }()),\n\n accountPath: (function () {\n var accnt = '';\n var path = options.pathname.split('\\/');\n if (path && path[1] === 'app') {\n accnt = path[2];\n }\n return accnt;\n }()),\n\n projectPath: (function () {\n var prj = '';\n var path = options.pathname.split('\\/');\n if (path && path[1] === 'app') {\n prj = path[3]; //eslint-disable-line no-magic-numbers\n }\n return prj;\n }()),\n\n versionPath: (function () {\n var version = epiVersion.version ? epiVersion.version + '/' : '';\n return version;\n }()),\n\n getAPIPath: function (api) {\n var PROJECT_APIS = ['run', 'data', 'file'];\n\n if (api === 'config') {\n var actualProtocol = window.location.protocol.replace(':', '');\n var configProtocol = (options.isLocalhost()) ? this.protocol : actualProtocol;\n return configProtocol + '://' + actingHost + '/epicenter/' + this.versionPath + 'config';\n }\n var apiPath = this.protocol + '://' + this.host + '/' + this.versionPath + api + '/';\n\n if ($.inArray(api, PROJECT_APIS) !== -1) {\n apiPath += this.accountPath + '/' + this.projectPath + '/';\n }\n return apiPath;\n }\n };\n\n\n $.extend(publicExports, overrides);\n return publicExports;\n};\n// This data can be set by external scripts, for loading from an env server for eg;\nUrlConfigService.defaults = {};\n\nmodule.exports = UrlConfigService;\n","'use strict';\n/**\n* ## User API Adapter\n*\n* The User API Adapter allows you to retrieve details about end users in your team (account). It is based on the querying capabilities of the underlying RESTful [User API](../../../rest_apis/user_management/user/).\n*\n* To use the User API Adapter, instantiate it and then call its methods.\n*\n* var ua = new F.service.User({\n* account: 'acme-simulations',\n* token: 'user-or-project-access-token'\n* });\n* ua.getById('42836d4b-5b61-4fe4-80eb-3136e956ee5c');\n* ua.get({ userName: 'jsmith' });\n* ua.get({ id: ['42836d4b-5b61-4fe4-80eb-3136e956ee5c',\n* '4ea75631-4c8d-4872-9d80-b4600146478e'] });\n*\n* The constructor takes an optional options parameter in which you can specify the `account` and `token` if they are not already available in the current context.\n*/\n\nvar ConfigService = require('./configuration-service');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\nvar qutil = require('../util/query-util');\n\nmodule.exports = function (config) {\n var defaults = {\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The access token to use when searching for end users. (See [more background on access tokens](../../../project_access/)).\n * @type {String}\n */\n token: undefined,\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {Object}\n */\n transport: {}\n };\n\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n var urlConfig = new ConfigService(serviceOptions).get('server');\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath('user')\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n\n var http = new TransportFactory(transportOptions);\n\n var publicAPI = {\n\n /**\n * Retrieve details about particular end users in your team, based on user name or user id.\n *\n * **Example**\n *\n * var ua = new F.service.User({\n * account: 'acme-simulations',\n * token: 'user-or-project-access-token'\n * });\n * ua.get({ userName: 'jsmith' });\n * ua.get({ id: ['42836d4b-5b61-4fe4-80eb-3136e956ee5c',\n * '4ea75631-4c8d-4872-9d80-b4600146478e'] });\n *\n * **Parameters**\n * @param {object} filter Object with field `userName` and value of the username. Alternatively, object with field `id` and value of an array of user ids.\n * @param {object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n\n get: function (filter, options) {\n options = options || {};\n filter = filter || {};\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options\n );\n\n var toQFilter = function (filter) {\n var res = {};\n\n // API only supports filtering by username for now\n if (filter.userName) {\n res.q = filter.userName;\n }\n\n return res;\n };\n\n var toIdFilters = function (id) {\n if (!id) {\n return '';\n }\n\n id = $.isArray(id) ? id : [id];\n return 'id=' + id.join('&id=');\n };\n\n var getFilters = [\n 'account=' + getOptions.account,\n toIdFilters(filter.id),\n qutil.toQueryFormat(toQFilter(filter))\n ].join('&');\n\n // special case for queries with large number of ids\n // make it as a post with GET semantics\n var threshold = 30;\n if (filter.id && $.isArray(filter.id) && filter.id.length >= threshold) {\n getOptions.url = urlConfig.getAPIPath('user') + '?_method=GET';\n return http.post({ id: filter.id }, getOptions);\n } else {\n return http.get(getFilters, getOptions);\n }\n },\n\n /**\n * Retrieve details about a single end user in your team, based on user id.\n *\n * **Example**\n *\n * var ua = new F.service.User({\n * account: 'acme-simulations',\n * token: 'user-or-project-access-token'\n * });\n * ua.getById('42836d4b-5b61-4fe4-80eb-3136e956ee5c');\n *\n * **Parameters**\n * @param {string} userId The user id for the end user in your team.\n * @param {object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n\n getById: function (userId, options) {\n return publicAPI.get({ id: userId }, options);\n }\n };\n\n $.extend(this, publicAPI);\n};\n\n\n","/**\n *\n * ## Variables API Service\n *\n * Used in conjunction with the [Run API Service](../run-api-service/) to read, write, and search for specific model variables.\n *\n * var rm = new F.manager.RunManager({\n * run: {\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * model: 'supply-chain-model.jl'\n * }\n * });\n * rm.getRun()\n * .then(function() {\n * var vs = rm.run.variables();\n * vs.save({sample_int: 4});\n * });\n *\n */\n\n\n 'use strict';\n\n var TransportFactory = require('../transport/http-transport-factory');\n var rutil = require('../util/run-util');\n\n module.exports = function (config) {\n var defaults = {\n /**\n * The runs object to which the variable filters apply. Defaults to null.\n * @type {runService}\n */\n runService: null\n };\n var serviceOptions = $.extend({}, defaults, config);\n\n var getURL = function () {\n return serviceOptions.runService.urlConfig.getFilterURL() + 'variables/';\n };\n\n var addAutoRestoreHeader = function (options) {\n return serviceOptions.runService.urlConfig.addAutoRestoreHeader(options);\n };\n\n var httpOptions = {\n url: getURL\n };\n if (serviceOptions.token) {\n httpOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n var http = new TransportFactory(httpOptions);\n http.splitGet = rutil.splitGetFactory(httpOptions);\n\n var publicAPI = {\n\n /**\n * Get values for a variable.\n *\n * **Example**\n *\n * vs.load('sample_int')\n * .then(function(val){\n * // val contains the value of sample_int\n * });\n *\n * **Parameters**\n * @param {String} variable Name of variable to load.\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n load: function (variable, outputModifier, options) {\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = addAutoRestoreHeader(httpOptions);\n return http.get(outputModifier, $.extend({}, httpOptions, {\n url: getURL() + variable + '/'\n }));\n },\n\n /**\n * Returns particular variables, based on conditions specified in the `query` object.\n *\n * **Example**\n *\n * vs.query(['price', 'sales'])\n * .then(function(val) {\n * // val is an object with the values of the requested variables: val.price, val.sales\n * });\n *\n * vs.query({ include:['price', 'sales'] });\n *\n * **Parameters**\n * @param {Object|Array} query The names of the variables requested.\n * @param {Object} outputModifier (Optional) Available fields include: `startrecord`, `endrecord`, `sort`, and `direction` (`asc` or `desc`).\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n query: function (query, outputModifier, options) {\n //Query and outputModifier are both querystrings in the url; only calling them out separately here to be consistent with the other calls\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n httpOptions = addAutoRestoreHeader(httpOptions);\n\n if ($.isArray(query)) {\n query = { include: query };\n }\n $.extend(query, outputModifier);\n return http.splitGet(query, httpOptions);\n },\n\n /**\n * Save values to model variables. Overwrites existing values. Note that you can only update model variables if the run is [in memory](../../../run_persistence/#runs-in-memory). (An alternate way to update model variables is to call a method from the model and make sure that the method persists the variables. See `do`, `serial`, and `parallel` in the [Run API Service](../run-api-service/) for calling methods from the model.)\n *\n * **Example**\n *\n * vs.save('price', 4);\n * vs.save({ price: 4, quantity: 5, products: [2,3,4] });\n *\n * **Parameters**\n * @param {Object|String} variable An object composed of the model variables and the values to save. Alternatively, a string with the name of the variable.\n * @param {Object} val (Optional) If passing a string for `variable`, use this argument for the value to save.\n * @param {Object} options (Optional) Overrides for configuration options.\n * @return {Promise}\n */\n save: function (variable, val, options) {\n var attrs;\n if (typeof variable === 'object') {\n attrs = variable;\n options = val;\n } else {\n (attrs = {})[variable] = val;\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options);\n\n return http.patch.call(this, attrs, httpOptions);\n }\n\n // Not Available until underlying API supports PUT. Otherwise save would be PUT and merge would be PATCH\n // *\n // * Save values to the api. Merges arrays, but otherwise same as save\n // * @param {Object|String} variable Object with attributes, or string key\n // * @param {Object} val Optional if prev parameter was a string, set value here\n // * @param {Object} options Overrides for configuration options\n // *\n // * @example\n // * vs.merge({ price: 4, quantity: 5, products: [2,3,4] })\n // * vs.merge('price', 4);\n\n // merge: function (variable, val, options) {\n // var attrs;\n // if (typeof variable === 'object') {\n // attrs = variable;\n // options = val;\n // } else {\n // (attrs = {})[variable] = val;\n // }\n // var httpOptions = $.extend(true, {}, serviceOptions, options);\n\n // return http.patch.call(this, attrs, httpOptions);\n // }\n };\n $.extend(this, publicAPI);\n };\n","/**\n * ## World API Adapter\n *\n * A [run](../../../glossary/#run) is a collection of end user interactions with a project and its model -- including setting variables, making decisions, and calling operations. For building multiplayer simulations you typically want multiple end users to share the same set of interactions, and work within a common state. Epicenter allows you to create \"worlds\" to handle such cases. Only [team projects](../../../glossary/#team) can be multiplayer.\n *\n * The World API Adapter allows you to create, access, and manipulate multiplayer worlds within your Epicenter project. You can use this to add and remove end users from the world, and to create, access, and remove their runs. Because of this, typically the World Adapter is used for facilitator pages in your project. (The related [World Manager](../world-manager/) provides an easy way to access runs and worlds for particular end users, so is typically used in pages that end users will interact with.)\n *\n * As with all the other [API Adapters](../../), all methods take in an \"options\" object as the last parameter. The options can be used to extend/override the World API Service defaults.\n *\n * To use the World Adapter, instantiate it and then access the methods provided. Instantiating requires the account id (**Team ID** in the Epicenter user interface), project id (**Project ID**), and group (**Group Name**).\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * // call methods, e.g. wa.addUsers()\n * });\n */\n\n'use strict';\n\nvar ConfigService = require('./configuration-service');\n// var qutil = require('../util/query-util');\nvar TransportFactory = require('../transport/http-transport-factory');\nvar SessionManager = require('../store/session-manager');\nvar _pick = require('../util/object-util')._pick;\n\nvar apiBase = 'multiplayer/';\nvar assignmentEndpoint = apiBase + 'assign';\nvar apiEndpoint = apiBase + 'world';\nvar projectEndpoint = apiBase + 'project';\n\nmodule.exports = function (config) {\n var defaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: undefined,\n\n /**\n * The project id. If left undefined, taken from the URL.\n * @type {String}\n */\n project: undefined,\n\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects). If left undefined, taken from the URL.\n * @type {String}\n */\n account: undefined,\n\n /**\n * The group name. Defaults to undefined.\n * @type {String}\n */\n group: undefined,\n\n /**\n * The model file to use to create runs in this world. Defaults to undefined.\n * @type {String}\n */\n model: undefined,\n\n /**\n * Criteria by which to filter world. Currently only supports world-ids as filters.\n * @type {String}\n */\n filter: '',\n\n /**\n * Convenience alias for filter\n * @type {String}\n */\n id: '',\n\n /**\n * Options to pass on to the underlying transport layer. All jquery.ajax options at http://api.jquery.com/jQuery.ajax/ are available. Defaults to empty object.\n * @type {Object}\n */\n transport: {},\n\n /**\n * Called when the call completes successfully. Defaults to `$.noop`.\n * @type {function}\n */\n success: $.noop,\n\n /**\n * Called when the call fails. Defaults to `$.noop`.\n * @type {function}\n */\n error: $.noop\n };\n\n this.sessionManager = new SessionManager();\n var serviceOptions = this.sessionManager.getMergedOptions(defaults, config);\n if (serviceOptions.id) {\n serviceOptions.filter = serviceOptions.id;\n }\n\n var urlConfig = new ConfigService(serviceOptions).get('server');\n\n if (!serviceOptions.account) {\n serviceOptions.account = urlConfig.accountPath;\n }\n\n if (!serviceOptions.project) {\n serviceOptions.project = urlConfig.projectPath;\n }\n\n var transportOptions = $.extend(true, {}, serviceOptions.transport, {\n url: urlConfig.getAPIPath(apiEndpoint)\n });\n\n if (serviceOptions.token) {\n transportOptions.headers = {\n Authorization: 'Bearer ' + serviceOptions.token\n };\n }\n\n var http = new TransportFactory(transportOptions);\n\n var setIdFilterOrThrowError = function (options) {\n if (options.id) {\n serviceOptions.filter = options.id;\n }\n if (options.filter) {\n serviceOptions.filter = options.filter;\n }\n if (!serviceOptions.filter) {\n throw new Error('No world id specified to apply operations against. This could happen if the user is not assigned to a world and is trying to work with runs from that world.');\n }\n };\n\n var validateModelOrThrowError = function (options) {\n if (!options.model) {\n throw new Error('No model specified to get the current run');\n }\n };\n\n var publicAPI = {\n\n /**\n * Creates a new World.\n *\n * Using this method is rare. It is more common to create worlds automatically while you `autoAssign()` end users to worlds. (In this case, configuration data for the world, such as the roles, are read from the project-level world configuration information, for example by `getProjectSettings()`.)\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create({\n * roles: ['VP Marketing', 'VP Sales', 'VP Engineering']\n * });\n *\n * **Parameters**\n * @param {object} params Parameters to create the world.\n * @param {string} params.group (Optional) The **Group Name** to create this world under. Only end users in this group are eligible to join the world. Optional here; required when instantiating the service (`new F.service.World()`).\n * @param {object} params.roles (Optional) The list of roles (strings) for this world. Some worlds have specific roles that **must** be filled by end users. Listing the roles allows you to autoassign users to worlds and ensure that all roles are filled in each world.\n * @param {object} params.optionalRoles (Optional) The list of optional roles (strings) for this world. Some worlds have specific roles that **may** be filled by end users. Listing the optional roles as part of the world object allows you to autoassign users to worlds and ensure that all roles are filled in each world.\n * @param {integer} params.minUsers (Optional) The minimum number of users for the world. Including this number allows you to autoassign end users to worlds and ensure that the correct number of users are in each world.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n create: function (params, options) {\n var createOptions = $.extend(true, {}, serviceOptions, options, { url: urlConfig.getAPIPath(apiEndpoint) });\n var worldApiParams = ['scope', 'files', 'roles', 'optionalRoles', 'minUsers', 'group', 'name'];\n var validParams = _pick(serviceOptions, ['account', 'project', 'group']);\n // whitelist the fields that we actually can send to the api\n params = _pick(params, worldApiParams);\n\n // account and project go in the body, not in the url\n params = $.extend({}, validParams, params);\n\n var oldSuccess = createOptions.success;\n createOptions.success = function (response) {\n serviceOptions.filter = response.id; //all future chained calls to operate on this id\n return oldSuccess.apply(this, arguments);\n };\n\n return http.post(params, createOptions);\n },\n\n /**\n * Updates a World, for example to replace the roles in the world.\n *\n * Typically, you complete world configuration at the project level, rather than at the world level. For example, each world in your project probably has the same roles for end users. And your project is probably either configured so that all end users share the same world (and run), or smaller sets of end users share worlds — but not both. However, this method is available if you need to update the configuration of a particular world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.update({ roles: ['VP Marketing', 'VP Sales', 'VP Engineering'] });\n * });\n *\n * **Parameters**\n * @param {object} params Parameters to update the world.\n * @param {string} params.name A string identifier for the linked end users, for example, \"name\": \"Our Team\".\n * @param {object} params.roles (Optional) The list of roles (strings) for this world. Some worlds have specific roles that **must** be filled by end users. Listing the roles allows you to autoassign users to worlds and ensure that all roles are filled in each world.\n * @param {object} params.optionalRoles (Optional) The list of optional roles (strings) for this world. Some worlds have specific roles that **may** be filled by end users. Listing the optional roles as part of the world object allows you to autoassign users to worlds and ensure that all roles are filled in each world.\n * @param {integer} params.minUsers (Optional) The minimum number of users for the world. Including this number allows you to autoassign end users to worlds and ensure that the correct number of users are in each world.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n update: function (params, options) {\n var whitelist = ['roles', 'optionalRoles', 'minUsers'];\n options = options || {};\n setIdFilterOrThrowError(options);\n\n var updateOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter }\n );\n\n params = _pick(params || {}, whitelist);\n\n return http.patch(params, updateOptions);\n },\n\n /**\n * Deletes an existing world.\n *\n * This function optionally takes one argument. If the argument is a string, it is the id of the world to delete. If the argument is an object, it is the override for global options.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.delete();\n * });\n *\n * **Parameters**\n * @param {String|Object} options (Optional) The id of the world to delete, or options object to override global options.\n * @return {Promise}\n */\n delete: function (options) {\n options = (options && (typeof options === 'string')) ? { filter: options } : {};\n setIdFilterOrThrowError(options);\n\n var deleteOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter }\n );\n\n return http.delete(null, deleteOptions);\n },\n\n /**\n * Updates the configuration for the current instance of the World API Adapter (including all subsequent function calls, until the configuration is updated again).\n *\n * **Example**\n *\n * var wa = new F.service.World({...}).updateConfig({ filter: '123' }).addUser({ userId: '123' });\n *\n * **Parameters**\n * @param {object} config The configuration object to use in updating existing configuration.\n * @return {Object} reference to current instance\n */\n updateConfig: function (config) {\n $.extend(serviceOptions, config);\n\n return this;\n },\n\n /**\n * Lists all worlds for a given account, project, and group. All three are required, and if not specified as parameters, are read from the service.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * // lists all worlds in group \"team1\"\n * wa.list();\n *\n * // lists all worlds in group \"other-group-name\"\n * wa.list({ group: 'other-group-name' });\n * });\n *\n * **Parameters**\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n list: function (options) {\n options = options || {};\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) }\n );\n\n var filters = _pick(getOptions, ['account', 'project', 'group']);\n\n return http.get(filters, getOptions);\n },\n\n /**\n * Gets all worlds that an end user belongs to for a given account (team), project, and group.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.getWorldsForUser('b1c19dda-2d2e-4777-ad5d-3929f17e86d3')\n * });\n *\n * ** Parameters **\n * @param {string} userId The `userId` of the user whose worlds are being retrieved.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n getWorldsForUser: function (userId, options) {\n options = options || {};\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) }\n );\n\n var filters = $.extend(\n _pick(getOptions, ['account', 'project', 'group']),\n { userId: userId }\n );\n\n return http.get(filters, getOptions);\n },\n\n /**\n * Load information for a specific world. All further calls to the world service will use the id provided.\n *\n * **Parameters**\n * @param {String} worldId The id of the world to load.\n * @param {Object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n load: function (worldId, options) {\n if (worldId) {\n serviceOptions.filter = worldId;\n }\n if (!serviceOptions.filter) {\n throw new Error('Please provide a worldid to load');\n }\n var httpOptions = $.extend(true, {}, serviceOptions, options, { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/' });\n return http.get('', httpOptions);\n },\n\n /**\n * Adds an end user or list of end users to a given world. The end user must be a member of the `group` that is associated with this world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * // add one user\n * wa.addUsers('b1c19dda-2d2e-4777-ad5d-3929f17e86d3');\n * wa.addUsers(['b1c19dda-2d2e-4777-ad5d-3929f17e86d3']);\n * wa.addUsers({ userId: 'b1c19dda-2d2e-4777-ad5d-3929f17e86d3', role: 'VP Sales' });\n *\n * // add several users\n * wa.addUsers([\n * { userId: 'a6fe0c1e-f4b8-4f01-9f5f-01ccf4c2ed44',\n * role: 'VP Marketing' },\n * { userId: '8f2604cf-96cd-449f-82fa-e331530734ee',\n * role: 'VP Engineering' }\n * ]);\n *\n * // add one user to a specific world\n * wa.addUsers('b1c19dda-2d2e-4777-ad5d-3929f17e86d3', world.id);\n * wa.addUsers('b1c19dda-2d2e-4777-ad5d-3929f17e86d3', { filter: world.id });\n * });\n *\n * ** Parameters **\n * @param {string|object|array} users User id, array of user ids, object, or array of objects of the users to add to this world.\n * @param {string} users.role The `role` the user should have in the world. It is up to the caller to ensure, if needed, that the `role` passed in is one of the `roles` or `optionalRoles` of this world.\n * @param {string} worldId The world to which the users should be added. If not specified, the filter parameter of the `options` object is used.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n addUsers: function (users, worldId, options) {\n\n if (!users) {\n throw new Error('Please provide a list of users to add to the world');\n }\n\n // normalize the list of users to an array of user objects\n users = $.map([].concat(users), function (u) {\n var isObject = $.isPlainObject(u);\n\n if (typeof u !== 'string' && !isObject) {\n throw new Error('Some of the users in the list are not in the valid format: ' + u);\n }\n\n return isObject ? u : { userId: u };\n });\n\n // check if options were passed as the second parameter\n if ($.isPlainObject(worldId) && !options) {\n options = worldId;\n worldId = null;\n }\n\n options = options || {};\n\n // we must have options by now\n if (typeof worldId === 'string') {\n options.filter = worldId;\n }\n\n setIdFilterOrThrowError(options);\n\n var updateOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/users' }\n );\n\n return http.post(users, updateOptions);\n },\n\n /**\n * Updates the role of an end user in a given world. (You can only update one end user at a time.)\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.create().then(function(world) {\n * wa.addUsers('b1c19dda-2d2e-4777-ad5d-3929f17e86d3');\n * wa.updateUser({ userId: 'b1c19dda-2d2e-4777-ad5d-3929f17e86d3', role: 'leader' });\n * });\n *\n * **Parameters**\n * @param {object} user User object with `userId` and the new `role`.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n updateUser: function (user, options) {\n options = options || {};\n\n if (!user || !user.userId) {\n throw new Error('You need to pass a userId to update from the world');\n }\n\n setIdFilterOrThrowError(options);\n\n var patchOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/users/' + user.userId }\n );\n\n return http.patch(_pick(user, 'role'), patchOptions);\n },\n\n /**\n * Removes an end user from a given world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.addUsers(['a6fe0c1e-f4b8-4f01-9f5f-01ccf4c2ed44', '8f2604cf-96cd-449f-82fa-e331530734ee']);\n * wa.removeUser('a6fe0c1e-f4b8-4f01-9f5f-01ccf4c2ed44');\n * wa.removeUser({ userId: '8f2604cf-96cd-449f-82fa-e331530734ee' });\n * });\n *\n * ** Parameters **\n * @param {object|string} user The `userId` of the user to remove from the world, or an object containing the `userId` field.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n removeUser: function (user, options) {\n options = options || {};\n\n if (typeof user === 'string') {\n user = { userId: user };\n }\n\n if (!user.userId) {\n throw new Error('You need to pass a userId to remove from the world');\n }\n\n setIdFilterOrThrowError(options);\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/users/' + user.userId }\n );\n\n return http.delete(null, getOptions);\n },\n\n /**\n * Gets the run id of current run for the given world. If the world does not have a run, creates a new one and returns the run id.\n *\n * Remember that a [run](../../glossary/#run) is a collection of interactions with a project and its model. In the case of multiplayer projects, the run is shared by all end users in the world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.create()\n * .then(function(world) {\n * wa.getCurrentRunId({ model: 'model.py' });\n * });\n *\n * ** Parameters **\n * @param {object} options (Optional) Options object to override global options.\n * @param {object} options.model The model file to use to create a run if needed.\n * @return {Promise}\n */\n getCurrentRunId: function (options) {\n options = options || {};\n\n setIdFilterOrThrowError(options);\n\n var getOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/run' }\n );\n\n validateModelOrThrowError(getOptions);\n return http.post(_pick(getOptions, 'model'), getOptions);\n },\n\n /**\n * Gets the current (most recent) world for the given end user in the given group. Brings this most recent world into memory if needed.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n * wa.getCurrentWorldForUser('8f2604cf-96cd-449f-82fa-e331530734ee')\n * .then(function(world) {\n * // use data from world\n * });\n *\n * ** Parameters **\n * @param {string} userId The `userId` of the user whose current (most recent) world is being retrieved.\n * @param {string} groupName (Optional) The name of the group. If not provided, defaults to the group used to create the service.\n * @return {Promise}\n */\n getCurrentWorldForUser: function (userId, groupName) {\n var dtd = $.Deferred();\n var me = this;\n this.getWorldsForUser(userId, { group: groupName })\n .then(function (worlds) {\n // assume the most recent world as the 'active' world\n worlds.sort(function (a, b) { return new Date(b.lastModified) - new Date(a.lastModified); });\n var currentWorld = worlds[0];\n\n if (currentWorld) {\n serviceOptions.filter = currentWorld.id;\n }\n\n dtd.resolveWith(me, [currentWorld]);\n })\n .fail(dtd.reject);\n\n return dtd.promise();\n },\n\n /**\n * Deletes the current run from the world.\n *\n * (Note that the world id remains part of the run record, indicating that the run was formerly an active run for the world.)\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.deleteRun('sample-world-id');\n *\n * **Parameters**\n * @param {string} worldId The `worldId` of the world from which the current run is being deleted.\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n deleteRun: function (worldId, options) {\n options = options || {};\n\n if (worldId) {\n options.filter = worldId;\n }\n\n setIdFilterOrThrowError(options);\n\n var deleteOptions = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(apiEndpoint) + serviceOptions.filter + '/run' }\n );\n\n return http.delete(null, deleteOptions);\n },\n\n /**\n * Creates a new run for the world.\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.getCurrentWorldForUser('8f2604cf-96cd-449f-82fa-e331530734ee')\n * .then(function (world) {\n * wa.newRunForWorld(world.id);\n * });\n *\n * **Parameters**\n * @param {string} worldId worldId in which we create the new run.\n * @param {object} options (Optional) Options object to override global options.\n * @param {object} options.model The model file to use to create a run if needed.\n * @return {Promise}\n */\n newRunForWorld: function (worldId, options) {\n var currentRunOptions = $.extend(true, {},\n options,\n { filter: worldId || serviceOptions.filter }\n );\n var me = this;\n\n validateModelOrThrowError(currentRunOptions);\n\n return this.deleteRun(worldId, options)\n .then(function () {\n return me.getCurrentRunId(currentRunOptions);\n });\n },\n\n /**\n * Assigns end users to worlds, creating new worlds as appropriate, automatically. Assigns all end users in the group, and creates new worlds as needed based on the project-level world configuration (roles, optional roles, and minimum end users per world).\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.autoAssign();\n *\n * **Parameters**\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n *\n */\n autoAssign: function (options) {\n options = options || {};\n\n var opt = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(assignmentEndpoint) }\n );\n\n var params = {\n account: opt.account,\n project: opt.project,\n group: opt.group\n };\n\n if (opt.maxUsers) {\n params.maxUsers = opt.maxUsers;\n }\n\n return http.post(params, opt);\n },\n\n /**\n * Gets the project's world configuration.\n *\n * Typically, every interaction with your project uses the same configuration of each world. For example, each world in your project probably has the same roles for end users. And your project is probably either configured so that all end users share the same world (and run), or smaller sets of end users share worlds — but not both.\n *\n * (The [Multiplayer Project REST API](../../../rest_apis/multiplayer/multiplayer_project/) allows you to set these project-level world configurations. The World Adapter simply retrieves them, for example so they can be used in auto-assignment of end users to worlds.)\n *\n * **Example**\n *\n * var wa = new F.service.World({\n * account: 'acme-simulations',\n * project: 'supply-chain-game',\n * group: 'team1' });\n *\n * wa.getProjectSettings()\n * .then(function(settings) {\n * console.log(settings.roles);\n * console.log(settings.optionalRoles);\n * });\n *\n * **Parameters**\n * @param {object} options (Optional) Options object to override global options.\n * @return {Promise}\n */\n getProjectSettings: function (options) {\n options = options || {};\n\n var opt = $.extend(true, {},\n serviceOptions,\n options,\n { url: urlConfig.getAPIPath(projectEndpoint) }\n );\n\n opt.url += [opt.account, opt.project].join('/');\n return http.get(null, opt);\n }\n\n };\n\n $.extend(this, publicAPI);\n};\n","/**\n * @class Cookie Storage Service\n *\n * @example\n * var people = require('cookie-store')({ root: 'people' });\n people\n .save({lastName: 'smith' })\n\n */\n\n\n'use strict';\n\n// Thin document.cookie wrapper to allow unit testing\nvar Cookie = function () {\n this.get = function () {\n return document.cookie;\n };\n\n this.set = function (newCookie) {\n document.cookie = newCookie;\n };\n};\n\nmodule.exports = function (config) {\n var host = window.location.hostname;\n var validHost = host.split('.').length > 1;\n var domain = validHost ? '.' + host : null;\n\n var defaults = {\n /**\n * Name of collection\n * @type { string}\n */\n root: '/',\n\n domain: domain,\n cookie: new Cookie()\n };\n this.serviceOptions = $.extend({}, defaults, config);\n\n var publicAPI = {\n // * TBD\n // * Query collection; uses MongoDB syntax\n // * @see \n // *\n // * @param { string} qs Query Filter\n // * @param { string} limiters @see \n // *\n // * @example\n // * cs.query(\n // * { name: 'John', className: 'CSC101' },\n // * {limit: 10}\n // * )\n\n // query: function (qs, limiters) {\n\n // },\n\n /**\n * Save cookie value\n * @param { string|Object} key If given a key save values under it, if given an object directly, save to top-level api\n * @param {Object} value (Optional)\n * @param {Object} options Overrides for service options\n *\n * @return {*} The saved value\n *\n * @example\n * cs.set('person', { firstName: 'john', lastName: 'smith' });\n * cs.set({ name:'smith', age:'32' });\n */\n set: function (key, value, options) {\n var setOptions = $.extend(true, {}, this.serviceOptions, options);\n\n var domain = setOptions.domain;\n var path = setOptions.root;\n var cookie = setOptions.cookie;\n\n cookie.set(encodeURIComponent(key) + '=' +\n encodeURIComponent(value) +\n (domain ? '; domain=' + domain : '') +\n (path ? '; path=' + path : '')\n );\n\n return value;\n },\n\n /**\n * Load cookie value\n * @param { string|Object} key If given a key save values under it, if given an object directly, save to top-level api\n * @return {*} The value stored\n *\n * @example\n * cs.get('person');\n */\n get: function (key) {\n var cookie = this.serviceOptions.cookie;\n var cookieReg = new RegExp('(?:^|;)\\\\s*' + encodeURIComponent(key).replace(/[\\-\\.\\+\\*]/g, '\\\\$&') + '\\\\s*\\\\=\\\\s*([^;]*).*$');\n var res = cookieReg.exec(cookie.get());\n var val = res ? decodeURIComponent(res[1]) : null;\n return val;\n },\n\n /**\n * Removes key from collection\n * @param { string} key key to remove\n * @param {object} options (optional) overrides for service options\n * @return { string} key The key removed\n *\n * @example\n * cs.remove('person');\n */\n remove: function (key, options) {\n var remOptions = $.extend(true, {}, this.serviceOptions, options);\n\n var domain = remOptions.domain;\n var path = remOptions.root;\n var cookie = remOptions.cookie;\n\n cookie.set(encodeURIComponent(key) +\n '=; expires=Thu, 01 Jan 1970 00:00:00 GMT' +\n (domain ? '; domain=' + domain : '') +\n (path ? '; path=' + path : '')\n );\n return key;\n },\n\n /**\n * Removes collection being referenced\n * @return { array} keys All the keys removed\n */\n destroy: function () {\n var cookie = this.serviceOptions.cookie;\n var aKeys = cookie.get().replace(/((?:^|\\s*;)[^\\=]+)(?=;|$)|^\\s*|\\s*(?:\\=[^;]*)?(?:\\1|$)/g, '').split(/\\s*(?:\\=[^;]*)?;\\s*/);\n for (var nIdx = 0; nIdx < aKeys.length; nIdx++) {\n var cookieKey = decodeURIComponent(aKeys[nIdx]);\n this.remove(cookieKey);\n }\n return aKeys;\n }\n };\n\n $.extend(this, publicAPI);\n};\n","'use strict';\n\nvar keyNames = require('../managers/key-names');\nvar StorageFactory = require('./store-factory');\nvar optionUtils = require('../util/option-utils');\n\nvar EPI_SESSION_KEY = keyNames.EPI_SESSION_KEY;\nvar EPI_MANAGER_KEY = 'epicenter.token'; //can't be under key-names, or logout will clear this too\n\nvar defaults = {\n /**\n * Where to store user access tokens for temporary access. Defaults to storing in a cookie in the browser.\n * @type {string}\n */\n store: { synchronous: true }\n};\n\nvar SessionManager = function (managerOptions) {\n managerOptions = managerOptions || {};\n function getBaseOptions(overrides) {\n overrides = overrides || {};\n var libOptions = optionUtils.getOptions();\n var finalOptions = $.extend(true, {}, defaults, libOptions, managerOptions, overrides);\n return finalOptions;\n }\n\n function getStore(overrides) {\n var baseOptions = getBaseOptions(overrides);\n var storeOpts = baseOptions.store || {};\n var isEpicenterDomain = !baseOptions.isLocal && !baseOptions.isCustomDomain;\n if (storeOpts.root === undefined && baseOptions.account && baseOptions.project && isEpicenterDomain) {\n storeOpts.root = '/app/' + baseOptions.account + '/' + baseOptions.project;\n }\n return new StorageFactory(storeOpts);\n }\n\n var publicAPI = {\n saveSession: function (userInfo, options) {\n var serialized = JSON.stringify(userInfo);\n getStore(options).set(EPI_SESSION_KEY, serialized);\n },\n getSession: function (options) {\n // var session = getStore(options).get(EPI_SESSION_KEY) || '{}';\n // return JSON.parse(session);\n var store = getStore(options);\n var finalOpts = store.serviceOptions;\n var serialized = store.get(EPI_SESSION_KEY) || '{}';\n var session = JSON.parse(serialized);\n // If the url contains the project and account\n // validate the account and project in the session\n // and override project, groupName, groupId and isFac\n // Otherwise (i.e. localhost) use the saved session values\n var account = finalOpts.account;\n var project = finalOpts.project;\n if (account && session.account !== account) {\n // This means that the token was not used to login to the same account\n return {};\n }\n if (session.groups && account && project) {\n var group = session.groups[project] || { groupId: '', groupName: '', isFac: false };\n $.extend(session, { project: project }, group);\n }\n return session;\n },\n removeSession: function (options) {\n var store = getStore(options);\n Object.keys(keyNames).forEach(function (cookieKey) {\n var cookieName = keyNames[cookieKey];\n store.remove(cookieName);\n });\n return true;\n },\n getStore: function (options) {\n return getStore(options);\n },\n\n getMergedOptions: function () {\n var args = Array.prototype.slice.call(arguments);\n var overrides = $.extend.apply($, [true, {}].concat(args));\n var baseOptions = getBaseOptions(overrides);\n var session = this.getSession(overrides);\n\n var token = session.auth_token;\n if (!token) {\n var factory = new StorageFactory();\n token = factory.get(EPI_MANAGER_KEY);\n }\n\n var sessionDefaults = {\n /**\n * For projects that require authentication, pass in the user access token (defaults to empty string). If the user is already logged in to Epicenter, the user access token is already set in a cookie and automatically loaded from there. (See [more background on access tokens](../../../project_access/)).\n * @see [Authentication API Service](../auth-api-service/) for getting tokens.\n * @type {String}\n */\n token: token,\n\n /**\n * The account. If left undefined, taken from the cookie session.\n * @type {String}\n */\n account: session.account,\n\n /**\n * The project. If left undefined, taken from the cookie session.\n * @type {String}\n */\n project: session.project,\n\n\n /**\n * The group name. If left undefined, taken from the cookie session.\n * @type {String}\n */\n group: session.groupName,\n /**\n * Alias for group. \n * @type {String}\n */\n groupName: session.groupName, //It's a little weird that it's called groupName in the cookie, but 'group' in all the service options, so normalize for both\n /**\n * The group id. If left undefined, taken from the cookie session.\n * @type {String}\n */\n groupId: session.groupId,\n userId: session.userId\n };\n return $.extend(true, sessionDefaults, baseOptions);\n }\n };\n $.extend(this, publicAPI);\n};\n\nmodule.exports = SessionManager;","/**\n Decides type of store to provide\n*/\n\n'use strict';\n// var isNode = false; FIXME: Browserify/minifyify has issues with the next link\n// var store = (isNode) ? require('./session-store') : require('./cookie-store');\nvar store = require('./cookie-store');\n\nmodule.exports = store;\n","'use strict';\n\nvar qutils = require('../util/query-util');\n\nmodule.exports = function (config) {\n\n var defaults = {\n url: '',\n\n contentType: 'application/json',\n headers: {},\n statusCode: {\n 404: $.noop\n },\n\n /**\n * ONLY for strings in the url. All GET & DELETE params are run through this\n * @type {[type] }\n */\n parameterParser: qutils.toQueryFormat,\n\n // To allow epicenter.token and other session cookies to be passed\n // with the requests\n xhrFields: {\n withCredentials: true\n }\n };\n\n var transportOptions = $.extend({}, defaults, config);\n\n var result = function (d) {\n return ($.isFunction(d)) ? d() : d;\n };\n\n var connect = function (method, params, connectOptions) {\n params = result(params);\n params = ($.isPlainObject(params) || $.isArray(params)) ? JSON.stringify(params) : params;\n\n var options = $.extend(true, {}, transportOptions, connectOptions, {\n type: method,\n data: params\n });\n var ALLOWED_TO_BE_FUNCTIONS = ['data', 'url'];\n $.each(options, function (key, value) {\n if ($.isFunction(value) && $.inArray(key, ALLOWED_TO_BE_FUNCTIONS) !== -1) {\n options[key] = value();\n }\n });\n\n if (options.logLevel && options.logLevel === 'DEBUG') {\n console.log(options.url);\n var oldSuccessFn = options.success || $.noop;\n options.success = function (response, ajaxStatus, ajaxReq) {\n console.log(response);\n oldSuccessFn.apply(this, arguments);\n };\n }\n\n var beforeSend = options.beforeSend;\n options.beforeSend = function (xhr, settings) {\n xhr.requestUrl = (connectOptions || {}).url;\n if (beforeSend) {\n beforeSend.apply(this, arguments);\n }\n };\n\n return $.ajax(options);\n };\n\n var publicAPI = {\n get: function (params, ajaxOptions) {\n var options = $.extend({}, transportOptions, ajaxOptions);\n params = options.parameterParser(result(params));\n return connect.call(this, 'GET', params, options);\n },\n splitGet: function () {\n\n },\n post: function () {\n return connect.apply(this, ['post'].concat([].slice.call(arguments)));\n },\n patch: function () {\n return connect.apply(this, ['patch'].concat([].slice.call(arguments)));\n },\n put: function () {\n return connect.apply(this, ['put'].concat([].slice.call(arguments)));\n },\n delete: function (params, ajaxOptions) {\n //DELETE doesn't support body params, but jQuery thinks it does.\n var options = $.extend({}, transportOptions, ajaxOptions);\n params = options.parameterParser(result(params));\n if ($.trim(params)) {\n var delimiter = (result(options.url).indexOf('?') === -1) ? '?' : '&';\n options.url = result(options.url) + delimiter + params;\n }\n return connect.call(this, 'DELETE', null, options);\n },\n head: function () {\n return connect.apply(this, ['head'].concat([].slice.call(arguments)));\n },\n options: function () {\n return connect.apply(this, ['options'].concat([].slice.call(arguments)));\n }\n };\n\n return $.extend(this, publicAPI);\n};\n","'use strict';\n\n// var isNode = false; FIXME: Browserify/minifyify has issues with the next link\n// var transport = (isNode) ? require('./node-http-transport') : require('./ajax-http-transport');\nvar transport = require('./ajax-http-transport');\nmodule.exports = transport;\n","/**\n/* Inherit from a class (using prototype borrowing)\n*/\n'use strict';\n\nfunction inherit(C, P) {\n var F = function () {};\n F.prototype = P.prototype;\n C.prototype = new F();\n C.__super = P.prototype;\n C.prototype.constructor = C;\n}\n\n/**\n* Shallow copy of an object\n* @param {Object} dest object to extend\n* @return {Object} extended object\n*/\nvar extend = function (dest /*, var_args*/) {\n var obj = Array.prototype.slice.call(arguments, 1);\n var current;\n for (var j = 0; j < obj.length; j++) {\n if (!(current = obj[j])) { //eslint-disable-line\n continue;\n }\n\n // do not wrap inner in dest.hasOwnProperty or bad things will happen\n for (var key in current) { //eslint-disable-line\n dest[key] = current[key];\n }\n }\n\n return dest;\n};\n\nmodule.exports = function (base, props, staticProps) {\n var parent = base;\n var child;\n\n child = props && props.hasOwnProperty('constructor') ? props.constructor : function () { return parent.apply(this, arguments); };\n\n // add static properties to the child constructor function\n extend(child, parent, staticProps);\n\n // associate prototype chain\n inherit(child, parent);\n\n // add instance properties\n if (props) {\n extend(child.prototype, props);\n }\n\n // done\n return child;\n};\n","'use strict';\n\nmodule.exports = {\n _pick: function (obj, props) {\n var res = {};\n for (var p in obj) {\n if (props.indexOf(p) !== -1) {\n res[p] = obj[p];\n }\n }\n\n return res;\n }\n};\n","'use strict';\n\nvar ConfigService = require('../service/configuration-service');\n\nvar urlConfig = new ConfigService().get('server');\nvar customDefaults = {};\nvar libDefaults = {\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n account: urlConfig.accountPath || undefined,\n /**\n * The account id. In the Epicenter UI, this is the **Team ID** (for team projects) or **User ID** (for personal projects). Defaults to empty string. If left undefined, taken from the URL.\n * @type {String}\n */\n project: urlConfig.projectPath || undefined,\n isLocal: urlConfig.isLocalhost(),\n isCustomDomain: urlConfig.isCustomDomain,\n store: {}\n};\n\nvar optionUtils = {\n /**\n * Gets the final options by overriding the global options set with\n * optionUtils#setDefaults() and the lib defaults.\n * @param {object} options The final options object.\n * @return {object} Extended object\n */\n getOptions: function (options) {\n return $.extend(true, {}, libDefaults, customDefaults, options);\n },\n /**\n * Sets the global defaults for the optionUtils#getOptions() method.\n * @param {object} defaults The defaults object.\n */\n setDefaults: function (defaults) {\n customDefaults = defaults;\n }\n};\nmodule.exports = optionUtils;\n","/**\n * Utilities for working with query strings\n*/\n'use strict';\n\nmodule.exports = (function () {\n\n return {\n /**\n * Converts to matrix format\n * @param {Object} qs Object to convert to query string\n * @return { string} Matrix-format query parameters\n */\n toMatrixFormat: function (qs) {\n if (qs === null || qs === undefined || qs === '') {\n return ';';\n }\n if (typeof qs === 'string' || qs instanceof String) {\n return qs;\n }\n\n var returnArray = [];\n var OPERATORS = ['<', '>', '!'];\n $.each(qs, function (key, value) {\n if (typeof value !== 'string' || $.inArray($.trim(value).charAt(0), OPERATORS) === -1) {\n value = '=' + value;\n }\n returnArray.push(key + value);\n });\n\n var mtrx = ';' + returnArray.join(';');\n return mtrx;\n },\n\n /**\n * Converts strings/arrays/objects to type 'a=b&b=c'\n * @param { string|Array|Object} qs\n * @return { string}\n */\n toQueryFormat: function (qs) {\n if (qs === null || qs === undefined) {\n return '';\n }\n if (typeof qs === 'string' || qs instanceof String) {\n return qs;\n }\n\n var returnArray = [];\n $.each(qs, function (key, value) {\n if ($.isArray(value)) {\n value = value.join(',');\n }\n if ($.isPlainObject(value)) {\n //Mostly for data api\n value = JSON.stringify(value);\n }\n returnArray.push(key + '=' + value);\n });\n\n var result = returnArray.join('&');\n return result;\n },\n\n /**\n * Converts strings of type 'a=b&b=c' to { a:b, b:c}\n * @param { string} qs\n * @return {object}\n */\n qsToObject: function (qs) {\n if (qs === null || qs === undefined || qs === '') {\n return {};\n }\n\n var qsArray = qs.split('&');\n var returnObj = {};\n $.each(qsArray, function (index, value) {\n var qKey = value.split('=')[0];\n var qVal = value.split('=')[1];\n\n if (qVal.indexOf(',') !== -1) {\n qVal = qVal.split(',');\n }\n\n returnObj[qKey] = qVal;\n });\n\n return returnObj;\n },\n\n /**\n * Normalizes and merges strings of type 'a=b', { b:c} to { a:b, b:c}\n * @param { string|Array|Object} qs1\n * @param { string|Array|Object} qs2\n * @return {Object}\n */\n mergeQS: function (qs1, qs2) {\n var obj1 = this.qsToObject(this.toQueryFormat(qs1));\n var obj2 = this.qsToObject(this.toQueryFormat(qs2));\n return $.extend(true, {}, obj1, obj2);\n },\n\n addTrailingSlash: function (url) {\n if (!url) {\n return '';\n }\n return (url.charAt(url.length - 1) === '/') ? url : (url + '/');\n }\n };\n}());\n\n\n","/**\n * Utilities for working with the run service\n*/\n'use strict';\nvar qutil = require('./query-util');\nvar MAX_URL_LENGTH = 2048;\n\nmodule.exports = (function () {\n return {\n /**\n * returns operations of the form `[[op1,op2], [arg1, arg2]]`\n * @param {Object|Array|String} operations operations to perform\n * @param {Array} args arguments for operation\n * @return {String} Matrix-format query parameters\n */\n normalizeOperations: function (operations, args) {\n if (!args) {\n args = [];\n }\n var returnList = {\n ops: [],\n args: []\n };\n\n var _concat = function (arr) {\n return (arr !== null && arr !== undefined) ? [].concat(arr) : [];\n };\n\n //{ add: [1,2], subtract: [2,4] }\n var _normalizePlainObjects = function (operations, returnList) {\n if (!returnList) {\n returnList = { ops: [], args: [] };\n }\n $.each(operations, function (opn, arg) {\n returnList.ops.push(opn);\n returnList.args.push(_concat(arg));\n });\n return returnList;\n };\n //{ name: 'add', params: [1] }\n var _normalizeStructuredObjects = function (operation, returnList) {\n if (!returnList) {\n returnList = { ops: [], args: [] };\n }\n returnList.ops.push(operation.name);\n returnList.args.push(_concat(operation.params));\n return returnList;\n };\n\n var _normalizeObject = function (operation, returnList) {\n return ((operation.name) ? _normalizeStructuredObjects : _normalizePlainObjects)(operation, returnList);\n };\n\n var _normalizeLiterals = function (operation, args, returnList) {\n if (!returnList) {\n returnList = { ops: [], args: [] };\n }\n returnList.ops.push(operation);\n returnList.args.push(_concat(args));\n return returnList;\n };\n\n\n var _normalizeArrays = function (operations, arg, returnList) {\n if (!returnList) {\n returnList = { ops: [], args: [] };\n }\n $.each(operations, function (index, opn) {\n if ($.isPlainObject(opn)) {\n _normalizeObject(opn, returnList);\n } else {\n _normalizeLiterals(opn, args[index], returnList);\n }\n });\n return returnList;\n };\n\n if ($.isPlainObject(operations)) {\n _normalizeObject(operations, returnList);\n } else if ($.isArray(operations)) {\n _normalizeArrays(operations, args, returnList);\n } else {\n _normalizeLiterals(operations, args, returnList);\n }\n\n return returnList;\n },\n\n splitGetFactory: function (httpOptions) {\n return function (params, options) {\n var http = this; //eslint-disable-line\n var getValue = function (name) {\n var value = options[name] || httpOptions[name];\n if (typeof value === 'function') {\n value = value();\n }\n return value;\n };\n var getFinalUrl = function (params) {\n var url = getValue('url', options);\n var data = params;\n // There is easy (or known) way to get the final URL jquery is going to send so\n // we're replicating it. The process might change at some point but it probably will not.\n // 1. Remove hash\n url = url.replace(/#.*$/, '');\n // 1. Append query string\n var queryParams = qutil.toQueryFormat(data);\n var questionIdx = url.indexOf('?');\n if (queryParams && questionIdx > -1) {\n return url + '&' + queryParams;\n } else if (queryParams) {\n return url + '?' + queryParams;\n }\n return url;\n };\n var url = getFinalUrl(params);\n // We must split the GET in multiple short URL's\n // The only property allowed to be split is \"include\"\n if (params && params.include && encodeURI(url).length > MAX_URL_LENGTH) {\n var dtd = $.Deferred();\n var paramsCopy = $.extend(true, {}, params);\n delete paramsCopy.include;\n var urlNoIncludes = getFinalUrl(paramsCopy);\n var diff = MAX_URL_LENGTH - urlNoIncludes.length;\n var oldSuccess = options.success || httpOptions.success || $.noop;\n var oldError = options.error || httpOptions.error || $.noop;\n // remove the original success and error callbacks\n options.success = $.noop;\n options.error = $.noop;\n\n var include = params.include;\n var currIncludes = [];\n var includeOpts = [currIncludes];\n var currLength = encodeURIComponent('?include=').length;\n var variable = include.pop();\n while (variable) {\n var varLenght = encodeURIComponent(variable).length;\n // Use a greedy approach for now, can be optimized to be solved in a more\n // efficient way\n // + 1 is the comma\n if (currLength + varLenght + 1 < diff) {\n currIncludes.push(variable);\n currLength += varLenght + 1;\n } else {\n currIncludes = [variable];\n includeOpts.push(currIncludes);\n currLength = '?include='.length + varLenght;\n }\n variable = include.pop();\n }\n var reqs = $.map(includeOpts, function (include) {\n var reqParams = $.extend({}, params, { include: include });\n return http.get(reqParams, options);\n });\n $.when.apply($, reqs).then(function () {\n // Each argument are arrays of the arguments of each done request\n // So the first argument of the first array of arguments is the data\n var isValid = arguments[0] && arguments[0][0];\n if (!isValid) {\n // Should never happen...\n oldError();\n return dtd.reject();\n }\n var firstResponse = arguments[0][0];\n var isObject = $.isPlainObject(firstResponse);\n var isRunAPI = (isObject && $.isPlainObject(firstResponse.variables)) || !isObject;\n if (isRunAPI) {\n if (isObject) {\n // aggregate the variables property only\n var aggregateRun = arguments[0][0];\n $.each(arguments, function (idx, args) {\n var run = args[0];\n $.extend(true, aggregateRun.variables, run.variables);\n });\n oldSuccess(aggregateRun, arguments[0][1], arguments[0][2]);\n dtd.resolve(aggregateRun, arguments[0][1], arguments[0][2]);\n } else {\n // array of runs\n // Agregate variables in each run\n var aggregatedRuns = {};\n $.each(arguments, function (idx, args) {\n var runs = args[0];\n if (!$.isArray(runs)) {\n return;\n }\n $.each(runs, function (idxRun, run) {\n if (run.id && !aggregatedRuns[run.id]) {\n run.variables = run.variables || {};\n aggregatedRuns[run.id] = run;\n } else if (run.id) {\n $.extend(true, aggregatedRuns[run.id].variables, run.variables);\n }\n });\n });\n // turn it into an array\n aggregatedRuns = $.map(aggregatedRuns, function (run) { return run; });\n oldSuccess(aggregatedRuns, arguments[0][1], arguments[0][2]);\n dtd.resolve(aggregatedRuns, arguments[0][1], arguments[0][2]);\n }\n } else {\n // is variables API\n // aggregate the response\n var aggregatedVariables = {};\n $.each(arguments, function (idx, args) {\n var vars = args[0];\n $.extend(true, aggregatedVariables, vars);\n });\n oldSuccess(aggregatedVariables, arguments[0][1], arguments[0][2]);\n dtd.resolve(aggregatedVariables, arguments[0][1], arguments[0][2]);\n }\n }, function () {\n oldError.apply(http, arguments);\n dtd.reject.apply(dtd, arguments);\n });\n return dtd.promise();\n } else {\n return http.get(params, options);\n }\n };\n }\n };\n}());\n"]} \ No newline at end of file diff --git a/package.json b/package.json index 3369a6f1..86f60abf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "epicenter-js", - "version": "2.0.0", + "version": "2.1.0", "repository": { "type": "git", "url": "https://github.com/forio/epicenter-js-libs"