UNPKG

ngx-endpoints

Version:

A library to dynamically load data from http endpoints / urls in angular

667 lines (652 loc) 69 kB
(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