ngx-endpoints
Version:
A library to dynamically load data from http endpoints / urls in angular
667 lines (652 loc) • 69 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common/http'), require('rxjs/operators'), require('rxjs'), require('query-string'), require('moment-relativism'), require('moment')) :
typeof define === 'function' && define.amd ? define('ngx-endpoints', ['exports', '@angular/core', '@angular/common/http', 'rxjs/operators', 'rxjs', 'query-string', 'moment-relativism', 'moment'], factory) :
(factory((global['ngx-endpoints'] = {}),global.ng.core,global.ng.common.http,global.rxjs.operators,global.rxjs,global.queryString,global.momentRelativism,global.moment_));
}(this, (function (exports,i0,i1,operators,rxjs,queryString,momentRelativism,moment_) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b)
if (b.hasOwnProperty(p))
d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try {
step(generator.next(value));
}
catch (e) {
reject(e);
} }
function rejected(value) { try {
step(generator["throw"](value));
}
catch (e) {
reject(e);
} }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function () { if (t[0] & 1)
throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f)
throw new TypeError("Generator is already executing.");
while (_)
try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done)
return t;
if (y = 0, t)
op = [op[0] & 2, t.value];
switch (op[0]) {
case 0:
case 1:
t = op;
break;
case 4:
_.label++;
return { value: op[1], done: false };
case 5:
_.label++;
y = op[1];
op = [0];
continue;
case 7:
op = _.ops.pop();
_.trys.pop();
continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
_ = 0;
continue;
}
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
_.label = op[1];
break;
}
if (op[0] === 6 && _.label < t[1]) {
_.label = t[1];
t = op;
break;
}
if (t && _.label < t[2]) {
_.label = t[2];
_.ops.push(op);
break;
}
if (t[2])
_.ops.pop();
_.trys.pop();
continue;
}
op = body.call(thisArg, _);
}
catch (e) {
op = [6, e];
y = 0;
}
finally {
f = t = 0;
}
if (op[0] & 5)
throw op[1];
return { value: op[0] ? op[1] : void 0, done: true };
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/** @type {?} */
var moment = moment_;
var NgxEndpointService = /** @class */ (function () {
function NgxEndpointService(_http) {
this._http = _http;
}
/**
* @return {?}
*/
NgxEndpointService.prototype.ngOnInit = /**
* @return {?}
*/
function () {
};
/**
* Gets data via http client
* @param endPointUrl url that will be requested
*/
/**
* Gets data via http client
* @template T
* @param {?} endPointUrl url that will be requested
* @param {?} httpheaders
* @return {?}
*/
NgxEndpointService.prototype.getData = /**
* Gets data via http client
* @template T
* @param {?} endPointUrl url that will be requested
* @param {?} httpheaders
* @return {?}
*/
function (endPointUrl, httpheaders) {
return __awaiter(this, void 0, void 0, function () {
var headers, key;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
headers = new i1.HttpHeaders();
// tslint:disable-next-line:forin
// tslint:disable-next-line:forin
for (key in httpheaders) {
headers = headers.append(key, httpheaders[key]);
}
return [4 /*yield*/, this._http.get(endPointUrl, { headers: headers }).pipe(operators.catchError(function (err) {
console.log('Cannot request data from ' + endPointUrl, err);
return rxjs.throwError(err);
})).toPromise()];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* Conversion method for relative dates in querystrings
* @param endPointUrl return endpointurl with converted dates in querystring
*/
/**
* Conversion method for relative dates in querystrings
* @param {?} endPointUrl return endpointurl with converted dates in querystring
* @param {?} momentjsformat
* @return {?}
*/
NgxEndpointService.prototype.convertDatesInURL = /**
* Conversion method for relative dates in querystrings
* @param {?} endPointUrl return endpointurl with converted dates in querystring
* @param {?} momentjsformat
* @return {?}
*/
function (endPointUrl, momentjsformat) {
/** @type {?} */
var urlObject = queryString.parseUrl(endPointUrl);
for (var propertyName in urlObject.query) {
if (urlObject.query[propertyName].startsWith('now')) {
/** @type {?} */
var dateResult = momentRelativism.relativism(urlObject.query[propertyName]);
urlObject.query[propertyName] = moment(dateResult).format(momentjsformat);
}
}
/** @type {?} */
var stringified = queryString.stringify(urlObject.query);
endPointUrl = urlObject.url + '?' + stringified;
return endPointUrl;
};
NgxEndpointService.decorators = [
{ type: i0.Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxEndpointService.ctorParameters = function () {
return [
{ type: i1.HttpClient }
];
};
/** @nocollapse */ NgxEndpointService.ngInjectableDef = i0.defineInjectable({ factory: function NgxEndpointService_Factory() { return new NgxEndpointService(i0.inject(i1.HttpClient)); }, token: NgxEndpointService, providedIn: "root" });
return NgxEndpointService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* Basic class that is used for the initial generation of the endpoints
*/
var /**
* Basic class that is used for the initial generation of the endpoints
*/ NgxEndPointData = /** @class */ (function () {
function NgxEndPointData() {
/**
* disable endpoint
*/
this.active = true;
/**
* Additional Headers (Api-Key, Basic Auth, Content-Type etc.)
*/
this.requestOptions = new Object();
/**
* Additional Headers (Api-Key, Basic Auth, Content-Type etc.)
*/
this.live = false;
/**
* Wait Interval between 2 live requests
*/
this.liveinterval = 10000;
/**
* If relative dates are used in querystring or specific query you can use this feature
*/
this.convertDates = false;
/**
* Replaces the relative date with the date in this output format in the querystring
*/
this.convertDatesOutputFormat = '';
}
return NgxEndPointData;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @template T
*/
var NgxEndPoint = /** @class */ (function () {
function NgxEndPoint(endpointservice, _endpoint) {
this.endpointservice = endpointservice;
this._endpoint = _endpoint;
/**
* data stores the last requested data as Behaviorsubject that can be subscribed.
*/
this.data = new rxjs.BehaviorSubject(null);
/**
* if a request is already pending no additional request should be made to the datasource
*/
this.requestPending = new rxjs.BehaviorSubject(false);
/**
* Is the endpoint alive and returns data ?
*/
this.isAlive = new rxjs.BehaviorSubject(false);
/**
* Delivering internal process information
*/
this.events = new rxjs.BehaviorSubject('');
/**
* for live sources this option will be used to stop the process
*/
this.running = true;
this.endpoint = _endpoint;
Object.assign(this.data, new Array());
}
/**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
*/
/**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
* @return {?}
*/
NgxEndPoint.prototype.requestData = /**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.requestInternal()];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* @return {?}
*/
NgxEndPoint.prototype.requestInternal = /**
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!this.requestPending.getValue())
return [3 /*break*/, 2];
this.addEvent('requesting data from endpoint', this.endpoint);
this.requestPending.next(true);
this.convertDates(this.endpoint.convertDates);
return [4 /*yield*/, this.request()];
case 1:
_a.sent();
this.requestPending.next(false);
return [3 /*break*/, 3];
case 2:
this.addEvent('a request is pending', this.endpoint);
_a.label = 3;
case 3: return [2 /*return*/];
}
});
});
};
/**
* @param {?} conversion
* @return {?}
*/
NgxEndPoint.prototype.convertDates = /**
* @param {?} conversion
* @return {?}
*/
function (conversion) {
if (conversion) {
this.endpoint.endPointUrl = this.endpointservice.convertDatesInURL(this.endpoint.endPointUrl, this.endpoint.convertDatesOutputFormat);
this.addEvent('converted endpointurl', this.endpoint);
}
};
/**
* @return {?}
*/
NgxEndPoint.prototype.request = /**
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
var currentData, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, this.endpointservice.getData(this.endpoint.endPointUrl, this.endpoint.requestOptions)];
case 1:
currentData = _a.sent();
if (!this.isAlive.getValue() && currentData != null) {
this.isAlive.next(true);
this.addEvent('endpoint is alive', this.endpoint);
}
this.data.next(currentData);
this.addEvent('updated data', this.endpoint);
return [3 /*break*/, 3];
case 2:
error_1 = _a.sent();
this.addEvent(error_1, this.endpoint);
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
});
};
/**
* @param {?} item
* @param {?} endpointdata
* @return {?}
*/
NgxEndPoint.prototype.addEvent = /**
* @param {?} item
* @param {?} endpointdata
* @return {?}
*/
function (item, endpointdata) {
this.events.next(JSON.stringify({ event: item, endpoint: endpointdata }));
};
NgxEndPoint.decorators = [
{ type: i0.Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxEndPoint.ctorParameters = function () {
return [
{ type: NgxEndpointService },
{ type: NgxEndPointData }
];
};
/** @nocollapse */ NgxEndPoint.ngInjectableDef = i0.defineInjectable({ factory: function NgxEndPoint_Factory() { return new NgxEndPoint(i0.inject(NgxEndpointService), i0.inject(NgxEndPointData)); }, token: NgxEndPoint, providedIn: "root" });
return NgxEndPoint;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @template T
*/
var NgxLiveEndPoint = /** @class */ (function (_super) {
__extends(NgxLiveEndPoint, _super);
function NgxLiveEndPoint(endpointservice, endpoint) {
var _this = _super.call(this, endpointservice, endpoint) || this;
_this.endpointservice = endpointservice;
_this.endpoint = endpoint;
return _this;
}
/**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
*/
/**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
* @return {?}
*/
NgxLiveEndPoint.prototype.requestData = /**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
this.running = true;
return [4 /*yield*/, this.refreshData()];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* internal function to wait before next live request will be made
*/
/**
* internal function to wait before next live request will be made
* @param {?} ms
* @return {?}
*/
NgxLiveEndPoint.prototype.sleep = /**
* internal function to wait before next live request will be made
* @param {?} ms
* @return {?}
*/
function (ms) {
return new Promise(function (resolve) { return setTimeout(resolve, ms); });
};
/**
* internal function to run live requests in loop
*/
/**
* internal function to run live requests in loop
* @return {?}
*/
NgxLiveEndPoint.prototype.refreshData = /**
* internal function to run live requests in loop
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.running)
return [3 /*break*/, 3];
return [4 /*yield*/, this.requestInternal()];
case 1:
_a.sent();
return [4 /*yield*/, this.sleep(this.endpoint.liveinterval)];
case 2:
_a.sent();
return [3 /*break*/, 0];
case 3: return [2 /*return*/];
}
});
});
};
NgxLiveEndPoint.decorators = [
{ type: i0.Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxLiveEndPoint.ctorParameters = function () {
return [
{ type: NgxEndpointService },
{ type: NgxEndPointData }
];
};
/** @nocollapse */ NgxLiveEndPoint.ngInjectableDef = i0.defineInjectable({ factory: function NgxLiveEndPoint_Factory() { return new NgxLiveEndPoint(i0.inject(NgxEndpointService), i0.inject(NgxEndPointData)); }, token: NgxLiveEndPoint, providedIn: "root" });
return NgxLiveEndPoint;
}(NgxEndPoint));
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @template T
*/
var NgxEndPointDataProviderService = /** @class */ (function () {
function NgxEndPointDataProviderService(endpointservice) {
this.endpointservice = endpointservice;
this.endpoints = new Array();
}
/**
* Creates an NgxEndPoint and stores it in an array for proceeding
* @param NgxEndPointData endpointitem (basic json object)
* @returns the created NgxEndPoint for further proceeding
*/
/**
* Creates an NgxEndPoint and stores it in an array for proceeding
* @param {?} endpointitem
* @return {?} the created NgxEndPoint for further proceeding
*/
NgxEndPointDataProviderService.prototype.addEndPoint = /**
* Creates an NgxEndPoint and stores it in an array for proceeding
* @param {?} endpointitem
* @return {?} the created NgxEndPoint for further proceeding
*/
function (endpointitem) {
if (!endpointitem.endPointId) {
console.log('No endPointId provided. Please attach an endpointId');
throw new Error('No endPointId provided. Please attach an endpointId');
}
if (this.endpoints.find(function (x) { return x.endpoint.endPointId === endpointitem.endPointId; })) {
throw new Error('An endpoint with id ' + endpointitem.endPointId + ' has been already added. Please choose another id.');
}
/** @type {?} */
var endpoint;
if (endpointitem.live) {
endpoint = new NgxLiveEndPoint(this.endpointservice, endpointitem);
}
else {
endpoint = new NgxEndPoint(this.endpointservice, endpointitem);
}
this.endpoints.push(endpoint);
return endpoint;
};
/**
* Deletes a certain NgxEndPoint from endpoints array
* Stops running live requests
* @param endPointId Id of the specific NgxEndPoint
*/
/**
* Deletes a certain NgxEndPoint from endpoints array
* Stops running live requests
* @param {?} endPointId Id of the specific NgxEndPoint
* @return {?}
*/
NgxEndPointDataProviderService.prototype.destroyEndPoint = /**
* Deletes a certain NgxEndPoint from endpoints array
* Stops running live requests
* @param {?} endPointId Id of the specific NgxEndPoint
* @return {?}
*/
function (endPointId) {
if (this.endpoints && this.endpoints.length > 0) {
/** @type {?} */
var index = this.endpoints.findIndex(function (x) { return x.endpoint.endPointId === endPointId; });
this.endpoints[index].running = false;
this.endpoints.splice(index, 1);
}
};
NgxEndPointDataProviderService.decorators = [
{ type: i0.Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxEndPointDataProviderService.ctorParameters = function () {
return [
{ type: NgxEndpointService }
];
};
/** @nocollapse */ NgxEndPointDataProviderService.ngInjectableDef = i0.defineInjectable({ factory: function NgxEndPointDataProviderService_Factory() { return new NgxEndPointDataProviderService(i0.inject(NgxEndpointService)); }, token: NgxEndPointDataProviderService, providedIn: "root" });
return NgxEndPointDataProviderService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
var NgxEndpointsModule = /** @class */ (function () {
function NgxEndpointsModule() {
}
NgxEndpointsModule.decorators = [
{ type: i0.NgModule, args: [{
declarations: [],
imports: [i1.HttpClientModule],
exports: [
i1.HttpClientModule
],
providers: [NgxEndpointService, NgxEndPointDataProviderService, NgxEndPoint, NgxLiveEndPoint]
},] }
];
return NgxEndpointsModule;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
exports.NgxEndpointsModule = NgxEndpointsModule;
exports.NgxEndPointDataProviderService = NgxEndPointDataProviderService;
exports.NgxLiveEndPoint = NgxLiveEndPoint;
exports.NgxEndPoint = NgxEndPoint;
exports.NgxEndpointService = NgxEndpointService;
exports.NgxEndPointData = NgxEndPointData;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWVuZHBvaW50cy51bWQuanMubWFwIiwic291cmNlcyI6WyJub2RlX21vZHVsZXMvdHNsaWIvdHNsaWIuZXM2LmpzIiwibmc6Ly9uZ3gtZW5kcG9pbnRzL2xpYi9uZ3gtZW5kcG9pbnRzL25neC1lbmRwb2ludHMuc2VydmljZS50cyIsIm5nOi8vbmd4LWVuZHBvaW50cy9saWIvbmd4LWVuZHBvaW50cy9uZ3gtZW5kcG9pbnQuZm9ybWF0LnRzIiwibmc6Ly9uZ3gtZW5kcG9pbnRzL2xpYi9uZ3gtZW5kcG9pbnRzL25neC1lbmRwb2ludHMudHMiLCJuZzovL25neC1lbmRwb2ludHMvbGliL25neC1lbmRwb2ludHMvbmd4LWxpdmUtZW5kLXBvaW50cy9uZ3gtbGl2ZS1lbmQtcG9pbnRzLnRzIiwibmc6Ly9uZ3gtZW5kcG9pbnRzL2xpYi9uZ3gtZW5kcG9pbnRzL25neC1lbmRwb2ludHMtZGF0YS1wcm92aWRlci5zZXJ2aWNlLnRzIiwibmc6Ly9uZ3gtZW5kcG9pbnRzL2xpYi9uZ3gtZW5kcG9pbnRzLm1vZHVsZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiEgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZVxyXG50aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS4gWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZVxyXG5MaWNlbnNlIGF0IGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxyXG5cclxuVEhJUyBDT0RFIElTIFBST1ZJREVEIE9OIEFOICpBUyBJUyogQkFTSVMsIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWVxyXG5LSU5ELCBFSVRIRVIgRVhQUkVTUyBPUiBJTVBMSUVELCBJTkNMVURJTkcgV0lUSE9VVCBMSU1JVEFUSU9OIEFOWSBJTVBMSUVEXHJcbldBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBUSVRMRSwgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UsXHJcbk1FUkNIQU5UQUJMSVRZIE9SIE5PTi1JTkZSSU5HRU1FTlQuXHJcblxyXG5TZWUgdGhlIEFwYWNoZSBWZXJzaW9uIDIuMCBMaWNlbnNlIGZvciBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnNcclxuYW5kIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiAqL1xyXG4vKiBnbG9iYWwgUmVmbGVjdCwgUHJvbWlzZSAqL1xyXG5cclxudmFyIGV4dGVuZFN0YXRpY3MgPSBmdW5jdGlvbihkLCBiKSB7XHJcbiAgICBleHRlbmRTdGF0aWNzID0gT2JqZWN0LnNldFByb3RvdHlwZU9mIHx8XHJcbiAgICAgICAgKHsgX19wcm90b19fOiBbXSB9IGluc3RhbmNlb2YgQXJyYXkgJiYgZnVuY3Rpb24gKGQsIGIpIHsgZC5fX3Byb3RvX18gPSBiOyB9KSB8fFxyXG4gICAgICAgIGZ1bmN0aW9uIChkLCBiKSB7IGZvciAodmFyIHAgaW4gYikgaWYgKGIuaGFzT3duUHJvcGVydHkocCkpIGRbcF0gPSBiW3BdOyB9O1xyXG4gICAgcmV0dXJuIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbn07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19leHRlbmRzKGQsIGIpIHtcclxuICAgIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbiAgICBmdW5jdGlvbiBfXygpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGQ7IH1cclxuICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTtcclxufVxyXG5cclxuZXhwb3J0IHZhciBfX2Fzc2lnbiA9IGZ1bmN0aW9uKCkge1xyXG4gICAgX19hc3NpZ24gPSBPYmplY3QuYXNzaWduIHx8IGZ1bmN0aW9uIF9fYXNzaWduKHQpIHtcclxuICAgICAgICBmb3IgKHZhciBzLCBpID0gMSwgbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGkgPCBuOyBpKyspIHtcclxuICAgICAgICAgICAgcyA9IGFyZ3VtZW50c1tpXTtcclxuICAgICAgICAgICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApKSB0W3BdID0gc1twXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHQ7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gX19hc3NpZ24uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcmVzdChzLCBlKSB7XHJcbiAgICB2YXIgdCA9IHt9O1xyXG4gICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApICYmIGUuaW5kZXhPZihwKSA8IDApXHJcbiAgICAgICAgdFtwXSA9IHNbcF07XHJcbiAgICBpZiAocyAhPSBudWxsICYmIHR5cGVvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzID09PSBcImZ1bmN0aW9uXCIpXHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIHAgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKHMpOyBpIDwgcC5sZW5ndGg7IGkrKykgaWYgKGUuaW5kZXhPZihwW2ldKSA8IDApXHJcbiAgICAgICAgICAgIHRbcFtpXV0gPSBzW3BbaV1dO1xyXG4gICAgcmV0dXJuIHQ7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2RlY29yYXRlKGRlY29yYXRvcnMsIHRhcmdldCwga2V5LCBkZXNjKSB7XHJcbiAgICB2YXIgYyA9IGFyZ3VtZW50cy5sZW5ndGgsIHIgPSBjIDwgMyA/IHRhcmdldCA6IGRlc2MgPT09IG51bGwgPyBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0YXJnZXQsIGtleSkgOiBkZXNjLCBkO1xyXG4gICAgaWYgKHR5cGVvZiBSZWZsZWN0ID09PSBcIm9iamVjdFwiICYmIHR5cGVvZiBSZWZsZWN0LmRlY29yYXRlID09PSBcImZ1bmN0aW9uXCIpIHIgPSBSZWZsZWN0LmRlY29yYXRlKGRlY29yYXRvcnMsIHRhcmdldCwga2V5LCBkZXNjKTtcclxuICAgIGVsc2UgZm9yICh2YXIgaSA9IGRlY29yYXRvcnMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIGlmIChkID0gZGVjb3JhdG9yc1tpXSkgciA9IChjIDwgMyA/IGQocikgOiBjID4gMyA/IGQodGFyZ2V0LCBrZXksIHIpIDogZCh0YXJnZXQsIGtleSkpIHx8IHI7XHJcbiAgICByZXR1cm4gYyA+IDMgJiYgciAmJiBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBrZXksIHIpLCByO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19wYXJhbShwYXJhbUluZGV4LCBkZWNvcmF0b3IpIHtcclxuICAgIHJldHVybiBmdW5jdGlvbiAodGFyZ2V0LCBrZXkpIHsgZGVjb3JhdG9yKHRhcmdldCwga2V5LCBwYXJhbUluZGV4KTsgfVxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19tZXRhZGF0YShtZXRhZGF0YUtleSwgbWV0YWRhdGFWYWx1ZSkge1xyXG4gICAgaWYgKHR5cGVvZiBSZWZsZWN0ID09PSBcIm9iamVjdFwiICYmIHR5cGVvZiBSZWZsZWN0Lm1ldGFkYXRhID09PSBcImZ1bmN0aW9uXCIpIHJldHVybiBSZWZsZWN0Lm1ldGFkYXRhKG1ldGFkYXRhS2V5LCBtZXRhZGF0YVZhbHVlKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fYXdhaXRlcih0aGlzQXJnLCBfYXJndW1lbnRzLCBQLCBnZW5lcmF0b3IpIHtcclxuICAgIHJldHVybiBuZXcgKFAgfHwgKFAgPSBQcm9taXNlKSkoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkge1xyXG4gICAgICAgIGZ1bmN0aW9uIGZ1bGZpbGxlZCh2YWx1ZSkgeyB0cnkgeyBzdGVwKGdlbmVyYXRvci5uZXh0KHZhbHVlKSk7IH0gY2F0Y2ggKGUpIHsgcmVqZWN0KGUpOyB9IH1cclxuICAgICAgICBmdW5jdGlvbiByZWplY3RlZCh2YWx1ZSkgeyB0cnkgeyBzdGVwKGdlbmVyYXRvcltcInRocm93XCJdKHZhbHVlKSk7IH0gY2F0Y2ggKGUpIHsgcmVqZWN0KGUpOyB9IH1cclxuICAgICAgICBmdW5jdGlvbiBzdGVwKHJlc3VsdCkgeyByZXN1bHQuZG9uZSA/IHJlc29sdmUocmVzdWx0LnZhbHVlKSA6IG5ldyBQKGZ1bmN0aW9uIChyZXNvbHZlKSB7IHJlc29sdmUocmVzdWx0LnZhbHVlKTsgfSkudGhlbihmdWxmaWxsZWQsIHJlamVjdGVkKTsgfVxyXG4gICAgICAgIHN0ZXAoKGdlbmVyYXRvciA9IGdlbmVyYXRvci5hcHBseSh0aGlzQXJnLCBfYXJndW1lbnRzIHx8IFtdKSkubmV4dCgpKTtcclxuICAgIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19nZW5lcmF0b3IodGhpc0FyZywgYm9keSkge1xyXG4gICAgdmFyIF8gPSB7IGxhYmVsOiAwLCBzZW50OiBmdW5jdGlvbigpIHsgaWYgKHRbMF0gJiAxKSB0aHJvdyB0WzFdOyByZXR1cm4gdFsxXTsgfSwgdHJ5czogW10sIG9wczogW10gfSwgZiwgeSwgdCwgZztcclxuICAgIHJldHVybiBnID0geyBuZXh0OiB2ZXJiKDApLCBcInRocm93XCI6IHZlcmIoMSksIFwicmV0dXJuXCI6IHZlcmIoMikgfSwgdHlwZW9mIFN5bWJvbCA9PT0gXCJmdW5jdGlvblwiICYmIChnW1N5bWJvbC5pdGVyYXRvcl0gPSBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXM7IH0pLCBnO1xyXG4gICAgZnVuY3Rpb24gdmVyYihuKSB7IHJldHVybiBmdW5jdGlvbiAodikgeyByZXR1cm4gc3RlcChbbiwgdl0pOyB9OyB9XHJcbiAgICBmdW5jdGlvbiBzdGVwKG9wKSB7XHJcbiAgICAgICAgaWYgKGYpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJHZW5lcmF0b3IgaXMgYWxyZWFkeSBleGVjdXRpbmcuXCIpO1xyXG4gICAgICAgIHdoaWxlIChfKSB0cnkge1xyXG4gICAgICAgICAgICBpZiAoZiA9IDEsIHkgJiYgKHQgPSBvcFswXSAmIDIgPyB5W1wicmV0dXJuXCJdIDogb3BbMF0gPyB5W1widGhyb3dcIl0gfHwgKCh0ID0geVtcInJldHVyblwiXSkgJiYgdC5jYWxsKHkpLCAwKSA6IHkubmV4dCkgJiYgISh0ID0gdC5jYWxsKHksIG9wWzFdKSkuZG9uZSkgcmV0dXJuIHQ7XHJcbiAgICAgICAgICAgIGlmICh5ID0gMCwgdCkgb3AgPSBbb3BbMF0gJiAyLCB0LnZhbHVlXTtcclxuICAgICAgICAgICAgc3dpdGNoIChvcFswXSkge1xyXG4gICAgICAgICAgICAgICAgY2FzZSAwOiBjYXNlIDE6IHQgPSBvcDsgYnJlYWs7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDQ6IF8ubGFiZWwrKzsgcmV0dXJuIHsgdmFsdWU6IG9wWzFdLCBkb25lOiBmYWxzZSB9O1xyXG4gICAgICAgICAgICAgICAgY2FzZSA1OiBfLmxhYmVsKys7IHkgPSBvcFsxXTsgb3AgPSBbMF07IGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgY2FzZSA3OiBvcCA9IF8ub3BzLnBvcCgpOyBfLnRyeXMucG9wKCk7IGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcclxuICAgICAgICAgICAgICAgICAgICBpZiAoISh0ID0gXy50cnlzLCB0ID0gdC5sZW5ndGggPiAwICYmIHRbdC5sZW5ndGggLSAxXSkgJiYgKG9wWzBdID09PSA2IHx8IG9wWzBdID09PSAyKSkgeyBfID0gMDsgY29udGludWU7IH1cclxuICAgICAgICAgICAgICAgICAgICBpZiAob3BbMF0gPT09IDMgJiYgKCF0IHx8IChvcFsxXSA+IHRbMF0gJiYgb3BbMV0gPCB0WzNdKSkpIHsgXy5sYWJlbCA9IG9wWzFdOyBicmVhazsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGlmIChvcFswXSA9PT0gNiAmJiBfLmxhYmVsIDwgdFsxXSkgeyBfLmxhYmVsID0gdFsxXTsgdCA9IG9wOyBicmVhazsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGlmICh0ICYmIF8ubGFiZWwgPCB0WzJdKSB7IF8ubGFiZWwgPSB0WzJdOyBfLm9wcy5wdXNoKG9wKTsgYnJlYWs7IH1cclxuICAgICAgICAgICAgICAgICAgICBpZiAodFsyXSkgXy5vcHMucG9wKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgXy50cnlzLnBvcCgpOyBjb250aW51ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBvcCA9IGJvZHkuY2FsbCh0aGlzQXJnLCBfKTtcclxuICAgICAgICB9IGNhdGNoIChlKSB7IG9wID0gWzYsIGVdOyB5ID0gMDsgfSBmaW5hbGx5IHsgZiA9IHQgPSAwOyB9XHJcbiAgICAgICAgaWYgKG9wWzBdICYgNSkgdGhyb3cgb3BbMV07IHJldHVybiB7IHZhbHVlOiBvcFswXSA/IG9wWzFdIDogdm9pZCAwLCBkb25lOiB0cnVlIH07XHJcbiAgICB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2V4cG9ydFN0YXIobSwgZXhwb3J0cykge1xyXG4gICAgZm9yICh2YXIgcCBpbiBtKSBpZiAoIWV4cG9ydHMuaGFzT3duUHJvcGVydHkocCkpIGV4cG9ydHNbcF0gPSBtW3BdO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX192YWx1ZXMobykge1xyXG4gICAgdmFyIG0gPSB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgb1tTeW1ib2wuaXRlcmF0b3JdLCBpID0gMDtcclxuICAgIGlmIChtKSByZXR1cm4gbS5jYWxsKG8pO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgICBuZXh0OiBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgIGlmIChvICYmIGkgPj0gby5sZW5ndGgpIG8gPSB2b2lkIDA7XHJcbiAgICAgICAgICAgIHJldHVybiB7IHZhbHVlOiBvICYmIG9baSsrXSwgZG9uZTogIW8gfTtcclxuICAgICAgICB9XHJcbiAgICB9O1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19yZWFkKG8sIG4pIHtcclxuICAgIHZhciBtID0gdHlwZW9mIFN5bWJvbCA9PT0gXCJmdW5jdGlvblwiICYmIG9bU3ltYm9sLml0ZXJhdG9yXTtcclxuICAgIGlmICghbSkgcmV0dXJuIG87XHJcbiAgICB2YXIgaSA9IG0uY2FsbChvKSwgciwgYXIgPSBbXSwgZTtcclxuICAgIHRyeSB7XHJcbiAgICAgICAgd2hpbGUgKChuID09PSB2b2lkIDAgfHwgbi0tID4gMCkgJiYgIShyID0gaS5uZXh0KCkpLmRvbmUpIGFyLnB1c2goci52YWx1ZSk7XHJcbiAgICB9XHJcbiAgICBjYXRjaCAoZXJyb3IpIHsgZSA9IHsgZXJyb3I6IGVycm9yIH07IH1cclxuICAgIGZpbmFsbHkge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGlmIChyICYmICFyLmRvbmUgJiYgKG0gPSBpW1wicmV0dXJuXCJdKSkgbS5jYWxsKGkpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBmaW5hbGx5IHsgaWYgKGUpIHRocm93IGUuZXJyb3I7IH1cclxuICAgIH1cclxuICAgIHJldHVybiBhcjtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fc3ByZWFkKCkge1xyXG4gICAgZm9yICh2YXIgYXIgPSBbXSwgaSA9IDA7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspXHJcbiAgICAgICAgYXIgPSBhci5jb25jYXQoX19yZWFkKGFyZ3VtZW50c1tpXSkpO1xyXG4gICAgcmV0dXJuIGFyO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hd2FpdCh2KSB7XHJcbiAgICByZXR1cm4gdGhpcyBpbnN0YW5jZW9mIF9fYXdhaXQgPyAodGhpcy52ID0gdiwgdGhpcykgOiBuZXcgX19hd2FpdCh2KTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fYXN5bmNHZW5lcmF0b3IodGhpc0FyZywgX2FyZ3VtZW50cywgZ2VuZXJhdG9yKSB7XHJcbiAgICBpZiAoIVN5bWJvbC5hc3luY0l0ZXJhdG9yKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiU3ltYm9sLmFzeW5jSXRlcmF0b3IgaXMgbm90IGRlZmluZWQuXCIpO1xyXG4gICAgdmFyIGcgPSBnZW5lcmF0b3IuYXBwbHkodGhpc0FyZywgX2FyZ3VtZW50cyB8fCBbXSksIGksIHEgPSBbXTtcclxuICAgIHJldHVybiBpID0ge30sIHZlcmIoXCJuZXh0XCIpLCB2ZXJiKFwidGhyb3dcIiksIHZlcmIoXCJyZXR1cm5cIiksIGlbU3ltYm9sLmFzeW5jSXRlcmF0b3JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfSwgaTtcclxuICAgIGZ1bmN0aW9uIHZlcmIobikgeyBpZiAoZ1tuXSkgaVtuXSA9IGZ1bmN0aW9uICh2KSB7IHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbiAoYSwgYikgeyBxLnB1c2goW24sIHYsIGEsIGJdKSA+IDEgfHwgcmVzdW1lKG4sIHYpOyB9KTsgfTsgfVxyXG4gICAgZnVuY3Rpb24gcmVzdW1lKG4sIHYpIHsgdHJ5IHsgc3RlcChnW25dKHYpKTsgfSBjYXRjaCAoZSkgeyBzZXR0bGUocVswXVszXSwgZSk7IH0gfVxyXG4gICAgZnVuY3Rpb24gc3RlcChyKSB7IHIudmFsdWUgaW5zdGFuY2VvZiBfX2F3YWl0ID8gUHJvbWlzZS5yZXNvbHZlKHIudmFsdWUudikudGhlbihmdWxmaWxsLCByZWplY3QpIDogc2V0dGxlKHFbMF1bMl0sIHIpOyB9XHJcbiAgICBmdW5jdGlvbiBmdWxmaWxsKHZhbHVlKSB7IHJlc3VtZShcIm5leHRcIiwgdmFsdWUpOyB9XHJcbiAgICBmdW5jdGlvbiByZWplY3QodmFsdWUpIHsgcmVzdW1lKFwidGhyb3dcIiwgdmFsdWUpOyB9XHJcbiAgICBmdW5jdGlvbiBzZXR0bGUoZiwgdikgeyBpZiAoZih2KSwgcS5zaGlmdCgpLCBxLmxlbmd0aCkgcmVzdW1lKHFbMF1bMF0sIHFbMF1bMV0pOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2FzeW5jRGVsZWdhdG9yKG8pIHtcclxuICAgIHZhciBpLCBwO1xyXG4gICAgcmV0dXJuIGkgPSB7fSwgdmVyYihcIm5leHRcIiksIHZlcmIoXCJ0aHJvd1wiLCBmdW5jdGlvbiAoZSkgeyB0aHJvdyBlOyB9KSwgdmVyYihcInJldHVyblwiKSwgaVtTeW1ib2wuaXRlcmF0b3JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfSwgaTtcclxuICAgIGZ1bmN0aW9uIHZlcmIobiwgZikgeyBpW25dID0gb1tuXSA/IGZ1bmN0aW9uICh2KSB7IHJldHVybiAocCA9ICFwKSA/IHsgdmFsdWU6IF9fYXdhaXQob1tuXSh2KSksIGRvbmU6IG4gPT09IFwicmV0dXJuXCIgfSA6IGYgPyBmKHYpIDogdjsgfSA6IGY7IH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fYXN5bmNWYWx1ZXMobykge1xyXG4gICAgaWYgKCFTeW1ib2wuYXN5bmNJdGVyYXRvcikgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN5bWJvbC5hc3luY0l0ZXJhdG9yIGlzIG5vdCBkZWZpbmVkLlwiKTtcclxuICAgIHZhciBtID0gb1tTeW1ib2wuYXN5bmNJdGVyYXRvcl0sIGk7XHJcbiAgICByZXR1cm4gbSA/IG0uY2FsbChvKSA6IChvID0gdHlwZW9mIF9fdmFsdWVzID09PSBcImZ1bmN0aW9uXCIgPyBfX3ZhbHVlcyhvKSA6IG9bU3ltYm9sLml0ZXJhdG9yXSgpLCBpID0ge30sIHZlcmIoXCJuZXh0XCIpLCB2ZXJiKFwidGhyb3dcIiksIHZlcmIoXCJyZXR1cm5cIiksIGlbU3ltYm9sLmFzeW5jSXRlcmF0b3JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfSwgaSk7XHJcbiAgICBmdW5jdGlvbiB2ZXJiKG4pIHsgaVtuXSA9IG9bbl0gJiYgZnVuY3Rpb24gKHYpIHsgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHsgdiA9IG9bbl0odiksIHNldHRsZShyZXNvbHZlLCByZWplY3QsIHYuZG9uZSwgdi52YWx1ZSk7IH0pOyB9OyB9XHJcbiAgICBmdW5jdGlvbiBzZXR0bGUocmVzb2x2ZSwgcmVqZWN0LCBkLCB2KSB7IFByb21pc2UucmVzb2x2ZSh2KS50aGVuKGZ1bmN0aW9uKHYpIHsgcmVzb2x2ZSh7IHZhbHVlOiB2LCBkb25lOiBkIH0pOyB9LCByZWplY3QpOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX21ha2VUZW1wbGF0ZU9iamVjdChjb29rZWQsIHJhdykge1xyXG4gICAgaWYgKE9iamVjdC5kZWZpbmVQcm9wZXJ0eSkgeyBPYmplY3QuZGVmaW5lUHJvcGVydHkoY29va2VkLCBcInJhd1wiLCB7IHZhbHVlOiByYXcgfSk7IH0gZWxzZSB7IGNvb2tlZC5yYXcgPSByYXc7IH1cclxuICAgIHJldHVybiBjb29rZWQ7XHJcbn07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19pbXBvcnRTdGFyKG1vZCkge1xyXG4gICAgaWYgKG1vZCAmJiBtb2QuX19lc01vZHVsZSkgcmV0dXJuIG1vZDtcclxuICAgIHZhciByZXN1bHQgPSB7fTtcclxuICAgIGlmIChtb2QgIT0gbnVsbCkgZm9yICh2YXIgayBpbiBtb2QpIGlmIChPYmplY3QuaGFzT3duUHJvcGVydHkuY2FsbChtb2QsIGspKSByZXN1bHRba10gPSBtb2Rba107XHJcbiAgICByZXN1bHQuZGVmYXVsdCA9IG1vZDtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2ltcG9ydERlZmF1bHQobW9kKSB7XHJcbiAgICByZXR1cm4gKG1vZCAmJiBtb2QuX19lc01vZHVsZSkgPyBtb2QgOiB7IGRlZmF1bHQ6IG1vZCB9O1xyXG59XHJcbiIsImltcG9ydCB7IEluamVjdGFibGUsIE9uSW5pdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSHR0cENsaWVudCwgSHR0cEhlYWRlcnMgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBjYXRjaEVycm9yIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgdGhyb3dFcnJvciwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCAqIGFzIHF1ZXJ5U3RyaW5nIGZyb20gJ3F1ZXJ5LXN0cmluZyc7XG5pbXBvcnQgKiBhcyBtb21lbnRSZWxhdGl2aXNtIGZyb20gJ21vbWVudC1yZWxhdGl2aXNtJztcbmltcG9ydCAqIGFzIG1vbWVudF8gZnJvbSAnbW9tZW50JztcbmNvbnN0IG1vbWVudCA9IG1vbWVudF87XG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBOZ3hFbmRwb2ludFNlcnZpY2UgaW1wbGVtZW50cyBPbkluaXQge1xuXG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBfaHR0cDogSHR0cENsaWVudCkgeyB9XG5cbiAgbmdPbkluaXQoKSB7XG4gIH1cbiAgLyoqXG4gICAqIEdldHMgZGF0YSB2aWEgaHR0cCBjbGllbnRcbiAgICogQHBhcmFtIGVuZFBvaW50VXJsIHVybCB0aGF0IHdpbGwgYmUgcmVxdWVzdGVkXG4gICAqL1xuICBhc3luYyBnZXREYXRhPFQ+KGVuZFBvaW50VXJsOiBzdHJpbmcsIGh0dHBoZWFkZXJzOiBPYmplY3QpOiBQcm9taXNlPFQ+IHtcbiAgICBsZXQgaGVhZGVycyA9IG5ldyBIdHRwSGVhZGVycygpO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpmb3JpblxuXG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOmZvcmluXG4gICAgZm9yIChjb25zdCBrZXkgaW4gaHR0cGhlYWRlcnMpIHtcbiAgICAgIGhlYWRlcnMgPSBoZWFkZXJzLmFwcGVuZChrZXksIGh0dHBoZWFkZXJzW2tleV0pO1xuICAgIH1cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5faHR0cC5nZXQ8VD4oZW5kUG9pbnRVcmwsIHsgaGVhZGVyczogaGVhZGVycyB9KS5waXBlPFQ+KFxuICAgICAgY2F0Y2hFcnJvcigoZXJyKSA9PiB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdDYW5ub3QgcmVxdWVzdCBkYXRhIGZyb20gJyArIGVuZFBvaW50VXJsLCBlcnIpO1xuICAgICAgICByZXR1cm4gdGhyb3dFcnJvcihlcnIpO1xuICAgIH0pXG4gICAgKS50b1Byb21pc2UoKTtcbiAgfVxuICAvKipcbiAgICogQ29udmVyc2lvbiBtZXRob2QgZm9yIHJlbGF0aXZlIGRhdGVzIGluIHF1ZXJ5c3RyaW5nc1xuICAgKiBAcGFyYW0gZW5kUG9pbnRVcmwgcmV0dXJuIGVuZHBvaW50dXJsIHdpdGggY29udmVydGVkIGRhdGVzIGluIHF1ZXJ5c3RyaW5nXG4gICAqL1xuICBjb252ZXJ0RGF0ZXNJblVSTChlbmRQb2ludFVybDogc3RyaW5nLCBtb21lbnRqc2Zvcm1hdDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCB1cmxPYmplY3QgPSBxdWVyeVN0cmluZy5wYXJzZVVybChlbmRQb2ludFVybCk7XG4gICAgZm9yIChjb25zdCBwcm9wZXJ0eU5hbWUgaW4gdXJsT2JqZWN0LnF1ZXJ5KSB7XG4gICAgICBpZiAodXJsT2JqZWN0LnF1ZXJ5W3Byb3BlcnR5TmFtZV0uc3RhcnRzV2l0aCgnbm93JykpIHtcbiAgICAgICAgY29uc3QgZGF0ZVJlc3VsdCA9IG1vbWVudFJlbGF0aXZpc20ucmVsYXRpdmlzbSh1cmxPYmplY3QucXVlcnlbcHJvcGVydHlOYW1lXSk7XG4gICAgICAgIHVybE9iamVjdC5xdWVyeVtwcm9wZXJ0eU5hbWVdID0gbW9tZW50KGRhdGVSZXN1bHQpLmZvcm1hdChtb21lbnRqc2Zvcm1hdCk7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IHN0cmluZ2lmaWVkID0gcXVlcnlTdHJpbmcuc3RyaW5naWZ5KHVybE9iamVjdC5xdWVyeSk7XG4gICAgZW5kUG9pbnRVcmwgPSB1cmxPYmplY3QudXJsICsgJz8nICsgc3RyaW5naWZpZWQ7XG4gICAgcmV0dXJuIGVuZFBvaW50VXJsO1xuICB9XG5cbn1cbiIsIiAgLyoqXG4gICogQmFzaWMgY2xhc3MgdGhhdCBpcyB1c2VkIGZvciB0aGUgaW5pdGlhbCBnZW5lcmF0aW9uIG9mIHRoZSBlbmRwb2ludHNcbiAgKi9cbmV4cG9ydCBjbGFzcyBOZ3hFbmRQb2ludERhdGEge1xuICAgLyoqXG4gICogVGl0bGUgb2YgdGhlIGVuZHBvaW50XG4gICovXG4gICAgdGl0bGU6IHN0cmluZztcbiAgLyoqXG4gICogSWQgZm9yIGxhdGVyIGlkZW50aWZpY2F0aW9uIG9mIHRoZSBlbmRwb2ludFxuICAqL1xuICAgIGVuZFBvaW50SWQ6IE5vbk51bGxhYmxlPG51bWJlcj47XG4gIC8qKlxuICAqIGRpc2FibGUgZW5kcG9pbnRcbiAgKi9cbiAgICBhY3RpdmUgPSB0cnVlO1xuICAvKipcbiAgKiBIdHRwIFVybCB0aGF0IHdpbGwgYmUgcmVxdWVzdGVkIGJ5IEVuZHBvaW50IChIdHRwLkdldClcbiAgKi9cbiAgICBlbmRQb2ludFVybDogc3RyaW5nO1xuICAvKipcbiAgKiBBZGRpdGlvbmFsIEhlYWRlcnMgKEFwaS1LZXksIEJhc2ljIEF1dGgsIENvbnRlbnQtVHlwZSBldGMuKVxuICAqL1xuICAgIHJlcXVlc3RPcHRpb25zID89IG5ldyBPYmplY3QoKTtcbiAgLyoqXG4gICogQWRkaXRpb25hbCBIZWFkZXJzIChBcGktS2V5LCBCYXNpYyBBdXRoLCBDb250ZW50LVR5cGUgZXRjLilcbiAgKi9cbiAgICBsaXZlID89IGZhbHNlO1xuICAvKipcbiAgKiBXYWl0IEludGVydmFsIGJldHdlZW4gMiBsaXZlIHJlcXVlc3RzXG4gICovXG4gICAgbGl2ZWludGVydmFsID89IDEwMDAwO1xuICAvKipcbiAgKiBJZiByZWxhdGl2ZSBkYXRlcyBhcmUgdXNlZCBpbiBxdWVyeXN0cmluZyBvciBzcGVjaWZpYyBxdWVyeSB5b3UgY2FuIHVzZSB0aGlzIGZlYXR1cmVcbiAgKi9cbiAgICBjb252ZXJ0RGF0ZXMgPz0gZmFsc2U7XG4gIC8qKlxuICAqIFJlcGxhY2VzIHRoZSByZWxhdGl2ZSBkYXRlIHdpdGggdGhlIGRhdGUgaW4gdGhpcyBvdXRwdXQgZm9ybWF0IGluIHRoZSBxdWVyeXN0cmluZ1xuICAqL1xuICAgIGNvbnZlcnREYXRlc091dHB1dEZvcm1hdCA/PSAnJztcbn1cbiIsImltcG9ydCB7IE5neEVuZHBvaW50U2VydmljZSB9IGZyb20gJy4vbmd4LWVuZHBvaW50cy5zZXJ2aWNlJztcbmltcG9ydCB7IE5neEVuZFBvaW50RGF0YSB9IGZyb20gJy4vbmd4LWVuZHBvaW50LmZvcm1hdCc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IENvbXBvbmVudCwgSW5qZWN0YWJsZSwgQ29tcG9uZW50UmVmIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIE5neEVuZFBvaW50PFQ+IHtcbiAgLyoqXG4gICogcmF3IGRhdGEgdGhhdCBpcyB1c2VkIGZvciBsYXRlciBpZGVudGlmaWNhdGlvbiBvZiB0aGUgZW5kcG9pbnRcbiAgKi9cbiAgcHVibGljIGVuZHBvaW50OiBOZ3hFbmRQb2ludERhdGE7XG4gIC8qKlxuICAqIGRhdGEgc3RvcmVzIHRoZSBsYXN0IHJlcXVlc3RlZCBkYXRhIGFzIEJlaGF2aW9yc3ViamVjdCB0aGF0IGNhbiBiZSBzdWJzY3JpYmVkLlxuICAqL1xuICBwdWJsaWMgZGF0YSA9IG5ldyBCZWhhdmlvclN1YmplY3Q8VD4obnVsbCk7XG4gIC8qKlxuICAqIGlmIGEgcmVxdWVzdCBpcyBhbHJlYWR5IHBlbmRpbmcgbm8gYWRkaXRpb25hbCByZXF1ZXN0IHNob3VsZCBiZSBtYWRlIHRvIHRoZSBkYXRhc291cmNlXG4gICovXG4gIHB1YmxpYyByZXF1ZXN0UGVuZGluZyA9IG5ldyBCZWhhdmlvclN1YmplY3Q8YW55PihmYWxzZSk7XG4gIC8qKlxuICAqIElzIHRoZSBlbmRwb2ludCBhbGl2ZSBhbmQgcmV0dXJucyBkYXRhID9cbiAgKi9cbiAgcHVibGljIGlzQWxpdmUgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGFueT4oZmFsc2UpO1xuICAvKipcbiAgKiBEZWxpdmVyaW5nIGludGVybmFsIHByb2Nlc3MgaW5mb3JtYXRpb25cbiAgKi9cbiAgcHVibGljIGV2ZW50cyA9IG5ldyBCZWhhdmlvclN1YmplY3Q8c3RyaW5nPignJyk7XG4gIC8qKlxuICAqIGZvciBsaXZlIHNvdXJjZXMgdGhpcyBvcHRpb24gd2lsbCBiZSB1c2VkIHRvIHN0b3AgdGhlIHByb2Nlc3NcbiAgKi9cbiAgcHVibGljIHJ1bm5pbmcgPSB0cnVlO1xuICBjb25zdHJ1Y3RvcihwdWJsaWMgZW5kcG9pbnRzZXJ2aWNlOiBOZ3hFbmRwb2ludFNlcnZpY2UsIHByb3RlY3RlZCBfZW5kcG9pbnQ6IE5neEVuZFBvaW50RGF0YSkge1xuICAgIHRoaXMuZW5kcG9pbnQgPSBfZW5kcG9pbnQ7XG4gICAgT2JqZWN0LmFzc2lnbih0aGlzLmRhdGEsIG5ldyBBcnJheTxhbnk+KCkpO1xuICB9XG5cbiAgLyoqXG4gICogUmVxdWVzdHMgZGF0YSBmcm9tIGFuIGh0dHAgc291cmNlXG4gICogT3B0aW9uYWxseSBjb252ZXJ0cyByZWxhdGl2ZSBkYXRlcyAobm93LTFkKSBpbnRvIHJlYWRhYmxlIHVybHMgYnkgRGF0ZXNPdXRwdXRGb3JtYXRcbiAgKiBBZGRzIHRoZSByZXN1bHQgb2YgdGhlIHJlcXVlc3QgdG8gQmVoYXZpb3JTdWJqZWN0IGRhdGEgdGhhdCBjYW4gYmUgc3Vic2NyaWJlZFxuICAqL1xuICBwdWJsaWMgYXN5bmMgcmVxdWVzdERhdGEoKSB7XG4gICAgYXdhaXQgdGhpcy5yZXF1ZXN0SW50ZXJuYWwoKTtcbiAgfVxuXG5cbiAgcHJvdGVjdGVkIGFzeW5jIHJlcXVlc3RJbnRlcm5hbCgpIHtcbiAgICBpZiAoIXRoaXMucmVxdWVzdFBlbmRpbmcuZ2V0VmFsdWUoKSkge1xuICAgICAgdGhpcy5hZGRFdmVudCgncmVxdWVzdGluZyBkYXRhIGZyb20gZW5kcG9pbnQnLCB0aGlzLmVuZHBvaW50KTtcbiAgICAgIHRoaXMucmVxdWVzdFBlbmRpbmcubmV4dCh0cnVlKTtcbiAgICAgIHRoaXMuY29udmVydERhdGVzKHRoaXMuZW5kcG9pbnQuY29udmVydERhdGVzKTtcbiAgICAgIGF3YWl0IHRoaXMucmVxdWVzdCgpO1xuICAgICAgdGhpcy5yZXF1ZXN0UGVuZGluZy5uZXh0KGZhbHNlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5hZGRFdmVudCgnYSByZXF1ZXN0IGlzIHBlbmRpbmcnLCB0aGlzLmVuZHBvaW50KTtcblxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY29udmVydERhdGVzKGNvbnZlcnNpb24pIHtcbiAgICBpZiAoY29udmVyc2lvbikge1xuICAgICAgdGhpcy5lbmRwb2ludC5lbmRQb2ludFVybCA9IHRoaXMuZW5kcG9pbnRzZXJ2aWNlLmNvbnZlcnREYXRlc0luVVJMKHRoaXMuZW5kcG9pbnQuZW5kUG9pbnRVcmwsIHRoaXMuZW5kcG9pbnQuY29udmVydERhdGVzT3V0cHV0Rm9ybWF0KTtcbiAgICAgIHRoaXMuYWRkRXZlbnQoJ2NvbnZlcnRlZCBlbmRwb2ludHVybCcsIHRoaXMuZW5kcG9pbnQpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVxdWVzdCgpIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY3VycmVudERhdGEgPSBhd2FpdCB0aGlzLmVuZHBvaW50c2VydmljZS5nZXREYXRhPFQ+KHRoaXMuZW5kcG9pbnQuZW5kUG9pbnRVcmwsIHRoaXMuZW5kcG9pbnQucmVxdWVzdE9wdGlvbnMpO1xuICAgICAgaWYgKCF0aGlzLmlzQWxpdmUuZ2V0VmFsdWUoKSAmJiBjdXJyZW50RGF0YSAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuaXNBbGl2ZS5uZXh0KHRydWUpO1xuICAgICAgICB0aGlzLmFkZEV2ZW50KCdlbmRwb2ludCBpcyBhbGl2ZScsIHRoaXMuZW5kcG9pbnQpO1xuICAgICAgfVxuICAgICAgdGhpcy5kYXRhLm5leHQoY3VycmVudERhdGEpO1xuICAgICAgdGhpcy5hZGRFdmVudCgndXBkYXRlZCBkYXRhJywgdGhpcy5lbmRwb2ludCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHRoaXMuYWRkRXZlbnQoZXJyb3IsIHRoaXMuZW5kcG9pbnQpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYWRkRXZlbnQoaXRlbTogYW55LCBlbmRwb2ludGRhdGE6IE5neEVuZFBvaW50RGF0YSkge1xuXG4gICAgdGhpcy5ldmVudHMubmV4dChKU09OLnN0cmluZ2lmeSh7ZXZlbnQ6IGl0ZW0sIGVuZHBvaW50OiBlbmRwb2ludGRhdGF9KSk7XG4gIH1cbn1cbiIsImltcG9ydCB7ICBPbkRlc3Ryb3ksIENvbXBvbmVudCwgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgIE5neEVuZHBvaW50U2VydmljZSB9IGZyb20gJy4uL25neC1lbmRwb2ludHMuc2VydmljZSc7XG5pbXBvcnQgeyAgTmd4RW5kUG9pbnQgfSBmcm9tICcuLi9uZ3gtZW5kcG9pbnRzJztcbmltcG9ydCB7ICBOZ3hFbmRQb2ludERhdGEgfSBmcm9tICcuLi9uZ3gtZW5kcG9pbnQuZm9ybWF0JztcblxuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xu