xng-breadcrumb
Version:
A declarative and reactive breadcrumb approach for Angular 6 and beyond https://www.npmjs.com/package/xng-breadcrumb
663 lines • 52.1 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import * as tslib_1 from "tslib";
import { Injectable } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { distinctUntilChanged, filter } from 'rxjs/operators';
import * as i0 from "@angular/core";
import * as i1 from "@angular/router";
var BreadcrumbService = /** @class */ (function () {
function BreadcrumbService(activatedRoute, router) {
this.activatedRoute = activatedRoute;
this.router = router;
this.baseHref = '/';
/**
* dynamicBreadcrumbStore holds information about dynamically updated breadcrumbs.
* Breadcrumbs can be set from anywhere (component, service) in the app.
* On every breadcrumb update check this store and use the info if available.
*/
this.dynamicBreadcrumbStore = [];
/**
* breadcrumbList for the current route
* When breadcrumb info is changed dynamically, check if the currentBreadcrumbs is effected
* If effected, update the change and emit a new stream
*/
this.currentBreadcrumbs = [];
/**
* Breadcrumbs observable to be subscribed by BreadcrumbComponent
* Emits on every route change OR dynamic update of breadcrumb
*/
this.breadcrumbs = new BehaviorSubject([]);
this.breadcrumbs$ = this.breadcrumbs.asObservable();
this.pathParamPrefix = ':';
this.pathParamRegexIdentifier = '/:[^/]+';
this.pathParamRegexReplacer = '/[^/]+';
this.setBaseBreadcrumb();
this.detectRouteChanges();
}
/**
* Update breadcrumb label or options for -
*
* route (complete route path). route can be passed the same way you define angular routes
* 1) update label Ex: set('/mentor', 'Mentor'), set('/mentor/:id', 'Mentor Details')
* 2) change visibility Ex: set('/mentor/:id/edit', { skip: true })
* 3) add info Ex: set('/mentor/:id/edit', { info: { icon: 'edit', iconColor: 'blue' } })
* ------------------------ OR -------------------------
*
* alias (prefixed with '@'). breadcrumb alias is unique for a route
* 1) update label Ex: set('@mentor', 'Enabler')
* 2) change visibility Ex: set('@mentorEdit', { skip: true })
* 3) add info Ex: set('@mentorEdit', { info: { icon: 'edit', iconColor: 'blue' } })
*/
/**
* Update breadcrumb label or options for -
*
* route (complete route path). route can be passed the same way you define angular routes
* 1) update label Ex: set('/mentor', 'Mentor'), set('/mentor/:id', 'Mentor Details')
* 2) change visibility Ex: set('/mentor/:id/edit', { skip: true })
* 3) add info Ex: set('/mentor/:id/edit', { info: { icon: 'edit', iconColor: 'blue' } })
* ------------------------ OR -------------------------
*
* alias (prefixed with '\@'). breadcrumb alias is unique for a route
* 1) update label Ex: set('\@mentor', 'Enabler')
* 2) change visibility Ex: set('\@mentorEdit', { skip: true })
* 3) add info Ex: set('\@mentorEdit', { info: { icon: 'edit', iconColor: 'blue' } })
* @param {?} pathOrAlias
* @param {?} breadcrumb
* @return {?}
*/
BreadcrumbService.prototype.set = /**
* Update breadcrumb label or options for -
*
* route (complete route path). route can be passed the same way you define angular routes
* 1) update label Ex: set('/mentor', 'Mentor'), set('/mentor/:id', 'Mentor Details')
* 2) change visibility Ex: set('/mentor/:id/edit', { skip: true })
* 3) add info Ex: set('/mentor/:id/edit', { info: { icon: 'edit', iconColor: 'blue' } })
* ------------------------ OR -------------------------
*
* alias (prefixed with '\@'). breadcrumb alias is unique for a route
* 1) update label Ex: set('\@mentor', 'Enabler')
* 2) change visibility Ex: set('\@mentorEdit', { skip: true })
* 3) add info Ex: set('\@mentorEdit', { info: { icon: 'edit', iconColor: 'blue' } })
* @param {?} pathOrAlias
* @param {?} breadcrumb
* @return {?}
*/
function (pathOrAlias, breadcrumb) {
if (!this.validateArguments(pathOrAlias, breadcrumb)) {
return;
}
if (typeof breadcrumb === 'string') {
breadcrumb = {
label: breadcrumb
};
}
if (pathOrAlias.startsWith('@')) {
this.updateStore(tslib_1.__assign({}, breadcrumb, { alias: pathOrAlias.slice(1) }));
}
else {
/** @type {?} */
var breadcrumbExtraProps = this.buildRouteRegExp(pathOrAlias);
this.updateStore(tslib_1.__assign({}, breadcrumb, breadcrumbExtraProps));
}
};
/**
* @private
* @return {?}
*/
BreadcrumbService.prototype.setBaseBreadcrumb = /**
* @private
* @return {?}
*/
function () {
/** @type {?} */
var baseConfig = this.router.config.find((/**
* @param {?} pathConfig
* @return {?}
*/
function (pathConfig) { return pathConfig.path === ''; }));
if (baseConfig && baseConfig.data) {
var _a = this.getBreadcrumbOptions(baseConfig.data), label = _a.label, alias = _a.alias, _b = _a.skip, skip = _b === void 0 ? false : _b, info = _a.info;
/** @type {?} */
var isAutoGeneratedLabel = false;
if (typeof label !== 'string' && !label) {
label = '';
isAutoGeneratedLabel = true;
}
this.baseBreadcrumb = {
label: label,
alias: alias,
skip: skip,
info: info,
routeLink: this.baseHref,
isAutoGeneratedLabel: isAutoGeneratedLabel
};
}
};
/**
* Whenever route changes build breadcrumb list again
*/
/**
* Whenever route changes build breadcrumb list again
* @private
* @return {?}
*/
BreadcrumbService.prototype.detectRouteChanges = /**
* Whenever route changes build breadcrumb list again
* @private
* @return {?}
*/
function () {
var _this = this;
this.router.events
.pipe(filter((/**
* @param {?} event
* @return {?}
*/
function (event) { return event instanceof NavigationEnd; })), distinctUntilChanged())
.subscribe((/**
* @param {?} event
* @return {?}
*/
function (event) {
_this.currentBreadcrumbs = _this.baseBreadcrumb ? [_this.baseBreadcrumb] : [];
_this.prepareBreadcrumbList(_this.activatedRoute.root, _this.baseHref);
}));
};
/**
* @private
* @param {?} activatedRoute
* @param {?} routeLinkPrefix
* @return {?}
*/
BreadcrumbService.prototype.prepareBreadcrumbList = /**
* @private
* @param {?} activatedRoute
* @param {?} routeLinkPrefix
* @return {?}
*/
function (activatedRoute, routeLinkPrefix) {
if (activatedRoute.routeConfig && activatedRoute.routeConfig.path) {
/** @type {?} */
var breadcrumbItem = this.prepareBreadcrumbItem(activatedRoute, routeLinkPrefix);
this.currentBreadcrumbs.push(breadcrumbItem);
if (activatedRoute.firstChild) {
return this.prepareBreadcrumbList(activatedRoute.firstChild, breadcrumbItem.routeLink + '/');
}
}
else if (activatedRoute.firstChild) {
return this.prepareBreadcrumbList(activatedRoute.firstChild, routeLinkPrefix);
}
// remove breadcrumb items that needs to be hidden or don't have a label
/** @type {?} */
var breacrumbsToShow = this.currentBreadcrumbs.filter((/**
* @param {?} item
* @return {?}
*/
function (item) { return !item.skip; }));
this.breadcrumbs.next(breacrumbsToShow);
};
/**
* @private
* @param {?} activatedRoute
* @param {?} routeLinkPrefix
* @return {?}
*/
BreadcrumbService.prototype.prepareBreadcrumbItem = /**
* @private
* @param {?} activatedRoute
* @param {?} routeLinkPrefix
* @return {?}
*/
function (activatedRoute, routeLinkPrefix) {
var _a = this.parseRouteData(activatedRoute.routeConfig), path = _a.path, breadcrumb = _a.breadcrumb;
// in case of path param get the resolved for param
/** @type {?} */
var resolvedPath = this.resolvePathParam(path, activatedRoute);
/** @type {?} */
var routeLink = "" + routeLinkPrefix + resolvedPath;
var _b = this.getFromStore(breadcrumb.alias, routeLink), label = _b.label, alias = _b.alias, skip = _b.skip, info = _b.info;
/** @type {?} */
var isAutoGeneratedLabel = false;
if (typeof label !== 'string') {
if (typeof breadcrumb.label === 'string') {
label = breadcrumb.label;
}
else {
label = resolvedPath;
isAutoGeneratedLabel = true;
}
}
return {
label: label,
alias: alias || breadcrumb.alias,
skip: skip || breadcrumb.skip,
info: info || breadcrumb.info,
routeLink: routeLink,
isAutoGeneratedLabel: isAutoGeneratedLabel
};
};
/**
* For a specific route, breadcrumb can be defined either on parent data OR it's child(which has empty path) data
* When both are defined, child takes precedence
*
* Ex: Below we are setting breadcrumb on both parent and child.
* So, child takes precedence and "Defined On Child" is displayed for the route 'home'
* { path: 'home', loadChildren: './home/home.module#HomeModule' , data: {breadcrumb: "Defined On Module"}}
* AND
* children: [
* { path: '', component: ShowUserComponent, data: {breadcrumb: "Defined On Child" }
* ]
*/
/**
* For a specific route, breadcrumb can be defined either on parent data OR it's child(which has empty path) data
* When both are defined, child takes precedence
*
* Ex: Below we are setting breadcrumb on both parent and child.
* So, child takes precedence and "Defined On Child" is displayed for the route 'home'
* { path: 'home', loadChildren: './home/home.module#HomeModule' , data: {breadcrumb: "Defined On Module"}}
* AND
* children: [
* { path: '', component: ShowUserComponent, data: {breadcrumb: "Defined On Child" }
* ]
* @private
* @param {?} routeConfig
* @return {?}
*/
BreadcrumbService.prototype.parseRouteData = /**
* For a specific route, breadcrumb can be defined either on parent data OR it's child(which has empty path) data
* When both are defined, child takes precedence
*
* Ex: Below we are setting breadcrumb on both parent and child.
* So, child takes precedence and "Defined On Child" is displayed for the route 'home'
* { path: 'home', loadChildren: './home/home.module#HomeModule' , data: {breadcrumb: "Defined On Module"}}
* AND
* children: [
* { path: '', component: ShowUserComponent, data: {breadcrumb: "Defined On Child" }
* ]
* @private
* @param {?} routeConfig
* @return {?}
*/
function (routeConfig) {
var path = routeConfig.path, _a = routeConfig.data, data = _a === void 0 ? {} : _a;
/** @type {?} */
var breadcrumb = this.mergeWithBaseChildData(routeConfig, tslib_1.__assign({}, data));
return { path: path, breadcrumb: breadcrumb };
};
/**
* @private
* @param {?} breadcrumbAlias
* @param {?} routeLink
* @return {?}
*/
BreadcrumbService.prototype.getFromStore = /**
* @private
* @param {?} breadcrumbAlias
* @param {?} routeLink
* @return {?}
*/
function (breadcrumbAlias, routeLink) {
/** @type {?} */
var matchingItem;
if (breadcrumbAlias) {
matchingItem = this.dynamicBreadcrumbStore.find((/**
* @param {?} item
* @return {?}
*/
function (item) { return item.alias === breadcrumbAlias; }));
}
if (!matchingItem && routeLink) {
matchingItem = this.dynamicBreadcrumbStore.find((/**
* @param {?} item
* @return {?}
*/
function (item) {
return (item.routeLink && item.routeLink === routeLink) || (item.routeRegex && new RegExp(item.routeRegex).test(routeLink + '/'));
}));
}
return matchingItem || {};
};
/**
* To update breadcrumb label for a route with path param, we need regex that matches route.
* Instead of user providing regex, we help in preparing regex dynamically
*
* Ex: route declaration - path: '/mentor/:id'
* breadcrumbService.set('/mentor/:id', 'Uday');
* '/mentor/2' OR 'mentor/adasd' we should use 'Uday' as label
*
* regex string is built, if route has path params(contains with ':')
*/
/**
* To update breadcrumb label for a route with path param, we need regex that matches route.
* Instead of user providing regex, we help in preparing regex dynamically
*
* Ex: route declaration - path: '/mentor/:id'
* breadcrumbService.set('/mentor/:id', 'Uday');
* '/mentor/2' OR 'mentor/adasd' we should use 'Uday' as label
*
* regex string is built, if route has path params(contains with ':')
* @private
* @param {?} path
* @return {?}
*/
BreadcrumbService.prototype.buildRouteRegExp = /**
* To update breadcrumb label for a route with path param, we need regex that matches route.
* Instead of user providing regex, we help in preparing regex dynamically
*
* Ex: route declaration - path: '/mentor/:id'
* breadcrumbService.set('/mentor/:id', 'Uday');
* '/mentor/2' OR 'mentor/adasd' we should use 'Uday' as label
*
* regex string is built, if route has path params(contains with ':')
* @private
* @param {?} path
* @return {?}
*/
function (path) {
// ensure leading slash is provided in the path
if (!path.startsWith('/')) {
path = '/' + path;
}
if (path.includes(this.pathParamPrefix)) {
// replace mathing path param with a regex
// '/mentor/:id' becomes '/mentor/[^/]', which further will be matched in updateStore
/** @type {?} */
var routeRegex = path.replace(new RegExp(this.pathParamRegexIdentifier, 'g'), this.pathParamRegexReplacer);
return { routeRegex: routeRegex };
}
else {
return { routeLink: path };
}
};
/**
* Update current breadcrumb definition and emit a new stream of breadcrumbs
* Also update the store to reuse dynamic declarations
*/
/**
* Update current breadcrumb definition and emit a new stream of breadcrumbs
* Also update the store to reuse dynamic declarations
* @private
* @param {?} breadcrumb
* @return {?}
*/
BreadcrumbService.prototype.updateStore = /**
* Update current breadcrumb definition and emit a new stream of breadcrumbs
* Also update the store to reuse dynamic declarations
* @private
* @param {?} breadcrumb
* @return {?}
*/
function (breadcrumb) {
var _a = this.getBreadcrumbIndexes(breadcrumb), breadcrumbItemIndex = _a.breadcrumbItemIndex, storeItemIndex = _a.storeItemIndex;
// if breadcrumb is present in current breadcrumbs update it and emit new stream
if (breadcrumbItemIndex > -1) {
this.currentBreadcrumbs[breadcrumbItemIndex] = tslib_1.__assign({}, this.currentBreadcrumbs[breadcrumbItemIndex], breadcrumb);
/** @type {?} */
var breacrumbsToShow = this.currentBreadcrumbs.filter((/**
* @param {?} item
* @return {?}
*/
function (item) { return !item.skip; }));
this.breadcrumbs.next(tslib_1.__spread(breacrumbsToShow));
}
// If the store already has this route definition update it, else add
if (storeItemIndex > -1) {
this.dynamicBreadcrumbStore[storeItemIndex] = tslib_1.__assign({}, this.dynamicBreadcrumbStore[storeItemIndex], breadcrumb);
}
else {
this.dynamicBreadcrumbStore.push(breadcrumb);
}
};
/**
* @private
* @param {?} breadcrumb
* @return {?}
*/
BreadcrumbService.prototype.getBreadcrumbIndexes = /**
* @private
* @param {?} breadcrumb
* @return {?}
*/
function (breadcrumb) {
var alias = breadcrumb.alias, routeLink = breadcrumb.routeLink, routeRegex = breadcrumb.routeRegex;
/** @type {?} */
var indexMap = {};
// identify macthing breadcrumb and store item
if (alias) {
indexMap = this.getBreadcrumbIndexesByType('alias', alias);
}
else if (routeLink) {
indexMap = this.getBreadcrumbIndexesByType('routeLink', routeLink);
}
else if (routeRegex) {
indexMap = this.getBreadcrumbIndexesByType('routeRegex', routeRegex);
}
return indexMap;
};
/**
* @private
* @param {?} key
* @param {?} value
* @return {?}
*/
BreadcrumbService.prototype.getBreadcrumbIndexesByType = /**
* @private
* @param {?} key
* @param {?} value
* @return {?}
*/
function (key, value) {
/** @type {?} */
var breadcrumbItemIndex = this.currentBreadcrumbs.findIndex((/**
* @param {?} item
* @return {?}
*/
function (item) { return value === item[key]; }));
/** @type {?} */
var storeItemIndex = this.dynamicBreadcrumbStore.findIndex((/**
* @param {?} item
* @return {?}
*/
function (item) { return value === item[key]; }));
return { breadcrumbItemIndex: breadcrumbItemIndex, storeItemIndex: storeItemIndex };
};
/**
* @private
* @param {?} path
* @param {?} activatedRoute
* @return {?}
*/
BreadcrumbService.prototype.resolvePathParam = /**
* @private
* @param {?} path
* @param {?} activatedRoute
* @return {?}
*/
function (path, activatedRoute) {
// if the path segment is a route param, read the param value from url
if (path.startsWith(this.pathParamPrefix)) {
return activatedRoute.snapshot.params[path.slice(1)];
}
return path;
};
/**
* get empty children of a module or Component. Empty child is the one with path: ''
* When parent and it's children (that has empty route path) define data
* merge them both with child taking precedence
*/
/**
* get empty children of a module or Component. Empty child is the one with path: ''
* When parent and it's children (that has empty route path) define data
* merge them both with child taking precedence
* @private
* @param {?} routeConfig
* @param {?} data
* @return {?}
*/
BreadcrumbService.prototype.mergeWithBaseChildData = /**
* get empty children of a module or Component. Empty child is the one with path: ''
* When parent and it's children (that has empty route path) define data
* merge them both with child taking precedence
* @private
* @param {?} routeConfig
* @param {?} data
* @return {?}
*/
function (routeConfig, data) {
if (!routeConfig) {
return this.getBreadcrumbOptions(data);
}
/** @type {?} */
var baseChild;
if (routeConfig.loadChildren) {
// To handle a module with empty child route
baseChild = routeConfig._loadedConfig.routes.find((/**
* @param {?} route
* @return {?}
*/
function (route) { return route.path === ''; }));
}
else if (routeConfig.children) {
// To handle a component with empty child route
baseChild = routeConfig.children.find((/**
* @param {?} route
* @return {?}
*/
function (route) { return route.path === ''; }));
}
return baseChild && baseChild.data
? this.mergeWithBaseChildData(baseChild, tslib_1.__assign({}, this.getBreadcrumbOptions(data), this.getBreadcrumbOptions(baseChild.data)))
: this.getBreadcrumbOptions(data);
};
/**
* @private
* @param {?} pathOrAlias
* @param {?} breadcrumb
* @return {?}
*/
BreadcrumbService.prototype.validateArguments = /**
* @private
* @param {?} pathOrAlias
* @param {?} breadcrumb
* @return {?}
*/
function (pathOrAlias, breadcrumb) {
if (pathOrAlias === null || pathOrAlias === undefined) {
console.error('Invalid first argument. Please pass a route path or a breadcrumb alias.');
return false;
}
else if (breadcrumb === null || breadcrumb === undefined) {
console.error('Invalid second argument. Please pass a string or an Object with breadcrumb options.');
return false;
}
return true;
};
/**
* breadcrumb can be passed a label or an options object
* If passed as a string convert to breadcrumb options object
*/
/**
* breadcrumb can be passed a label or an options object
* If passed as a string convert to breadcrumb options object
* @private
* @param {?} data
* @return {?}
*/
BreadcrumbService.prototype.getBreadcrumbOptions = /**
* breadcrumb can be passed a label or an options object
* If passed as a string convert to breadcrumb options object
* @private
* @param {?} data
* @return {?}
*/
function (data) {
var breadcrumb = data.breadcrumb;
if (typeof breadcrumb === 'string' || !breadcrumb) {
breadcrumb = {
label: breadcrumb
};
}
return breadcrumb;
};
BreadcrumbService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
BreadcrumbService.ctorParameters = function () { return [
{ type: ActivatedRoute },
{ type: Router }
]; };
/** @nocollapse */ BreadcrumbService.ngInjectableDef = i0.ɵɵdefineInjectable({ factory: function BreadcrumbService_Factory() { return new BreadcrumbService(i0.ɵɵinject(i1.ActivatedRoute), i0.ɵɵinject(i1.Router)); }, token: BreadcrumbService, providedIn: "root" });
return BreadcrumbService;
}());
export { BreadcrumbService };
if (false) {
/**
* breadcrumb label for base OR root path. Usually, this can be set as 'Home'
* @type {?}
* @private
*/
BreadcrumbService.prototype.baseBreadcrumb;
/**
* @type {?}
* @private
*/
BreadcrumbService.prototype.baseHref;
/**
* dynamicBreadcrumbStore holds information about dynamically updated breadcrumbs.
* Breadcrumbs can be set from anywhere (component, service) in the app.
* On every breadcrumb update check this store and use the info if available.
* @type {?}
* @private
*/
BreadcrumbService.prototype.dynamicBreadcrumbStore;
/**
* breadcrumbList for the current route
* When breadcrumb info is changed dynamically, check if the currentBreadcrumbs is effected
* If effected, update the change and emit a new stream
* @type {?}
* @private
*/
BreadcrumbService.prototype.currentBreadcrumbs;
/**
* Breadcrumbs observable to be subscribed by BreadcrumbComponent
* Emits on every route change OR dynamic update of breadcrumb
* @type {?}
* @private
*/
BreadcrumbService.prototype.breadcrumbs;
/** @type {?} */
BreadcrumbService.prototype.breadcrumbs$;
/**
* @type {?}
* @private
*/
BreadcrumbService.prototype.pathParamPrefix;
/**
* @type {?}
* @private
*/
BreadcrumbService.prototype.pathParamRegexIdentifier;
/**
* @type {?}
* @private
*/
BreadcrumbService.prototype.pathParamRegexReplacer;
/**
* @type {?}
* @private
*/
BreadcrumbService.prototype.activatedRoute;
/**
* @type {?}
* @private
*/
BreadcrumbService.prototype.router;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJlYWRjcnVtYi5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6Im5nOi8veG5nLWJyZWFkY3J1bWIvIiwic291cmNlcyI6WyJsaWIvYnJlYWRjcnVtYi5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQUUsY0FBYyxFQUFFLGFBQWEsRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN4RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7O0FBRzlEO0lBb0NFLDJCQUFvQixjQUE4QixFQUFVLE1BQWM7UUFBdEQsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBQVUsV0FBTSxHQUFOLE1BQU0sQ0FBUTtRQTNCbEUsYUFBUSxHQUFHLEdBQUcsQ0FBQzs7Ozs7O1FBT2YsMkJBQXNCLEdBQWlCLEVBQUUsQ0FBQzs7Ozs7O1FBTzFDLHVCQUFrQixHQUFpQixFQUFFLENBQUM7Ozs7O1FBTXRDLGdCQUFXLEdBQUcsSUFBSSxlQUFlLENBQWUsRUFBRSxDQUFDLENBQUM7UUFDckQsaUJBQVksR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLFlBQVksRUFBRSxDQUFDO1FBRTlDLG9CQUFlLEdBQUcsR0FBRyxDQUFDO1FBQ3RCLDZCQUF3QixHQUFHLFNBQVMsQ0FBQztRQUNyQywyQkFBc0IsR0FBRyxRQUFRLENBQUM7UUFHeEMsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDekIsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7SUFDNUIsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7O09BYUc7Ozs7Ozs7Ozs7Ozs7Ozs7OztJQUNILCtCQUFHOzs7Ozs7Ozs7Ozs7Ozs7OztJQUFILFVBQUksV0FBbUIsRUFBRSxVQUErQjtRQUN0RCxJQUFJLENB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dGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEFjdGl2YXRlZFJvdXRlLCBOYXZpZ2F0aW9uRW5kLCBSb3V0ZXIgfSBmcm9tICdAYW5ndWxhci9yb3V0ZXInO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBkaXN0aW5jdFVudGlsQ2hhbmdlZCwgZmlsdGVyIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgQnJlYWRjcnVtYiB9IGZyb20gJy4vYnJlYWRjcnVtYic7XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIEJyZWFkY3J1bWJTZXJ2aWNlIHtcbiAgLyoqXG4gICAqIGJyZWFkY3J1bWIgbGFiZWwgZm9yIGJhc2UgT1Igcm9vdCBwYXRoLiBVc3VhbGx5LCB0aGlzIGNhbiBiZSBzZXQgYXMgJ0hvbWUnXG4gICAqL1xuICBwcml2YXRlIGJhc2VCcmVhZGNydW1iOiBCcmVhZGNydW1iO1xuXG4gIHByaXZhdGUgYmFzZUhyZWYgPSAnLyc7XG5cbiAgLyoqXG4gICAqIGR5bmFtaWNCcmVhZGNydW1iU3RvcmUgaG9sZHMgaW5mb3JtYXRpb24gYWJvdXQgZHluYW1pY2FsbHkgdXBkYXRlZCBicmVhZGNydW1icy5cbiAgICogQnJlYWRjcnVtYnMgY2FuIGJlIHNldCBmcm9tIGFueXdoZXJlIChjb21wb25lbnQsIHNlcnZpY2UpIGluIHRoZSBhcHAuXG4gICAqIE9uIGV2ZXJ5IGJyZWFkY3J1bWIgdXBkYXRlIGNoZWNrIHRoaXMgc3RvcmUgYW5kIHVzZSB0aGUgaW5mbyBpZiBhdmFpbGFibGUuXG4gICAqL1xuICBwcml2YXRlIGR5bmFtaWNCcmVhZGNydW1iU3RvcmU6IEJyZWFkY3J1bWJbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBicmVhZGNydW1iTGlzdCBmb3IgdGhlIGN1cnJlbnQgcm91dGVcbiAgICogV2hlbiBicmVhZGNydW1iIGluZm8gaXMgY2hhbmdlZCBkeW5hbWljYWxseSwgY2hlY2sgaWYgdGhlIGN1cnJlbnRCcmVhZGNydW1icyBpcyBlZmZlY3RlZFxuICAgKiBJZiBlZmZlY3RlZCwgdXBkYXRlIHRoZSBjaGFuZ2UgYW5kIGVtaXQgYSBuZXcgc3RyZWFtXG4gICAqL1xuICBwcml2YXRlIGN1cnJlbnRCcmVhZGNydW1iczogQnJlYWRjcnVtYltdID0gW107XG5cbiAgLyoqXG4gICAqIEJyZWFkY3J1bWJzIG9ic2VydmFibGUgdG8gYmUgc3Vic2NyaWJlZCBieSBCcmVhZGNydW1iQ29tcG9uZW50XG4gICAqIEVtaXRzIG9uIGV2ZXJ5IHJvdXRlIGNoYW5nZSBPUiBkeW5hbWljIHVwZGF0ZSBvZiBicmVhZGNydW1iXG4gICAqL1xuICBwcml2YXRlIGJyZWFkY3J1bWJzID0gbmV3IEJlaGF2aW9yU3ViamVjdDxCcmVhZGNydW1iW10+KFtdKTtcbiAgcHVibGljIGJyZWFkY3J1bWJzJCA9IHRoaXMuYnJlYWRjcnVtYnMuYXNPYnNlcnZhYmxlKCk7XG5cbiAgcHJpdmF0ZSBwYXRoUGFyYW1QcmVmaXggPSAnOic7XG4gIHByaXZhdGUgcGF0aFBhcmFtUmVnZXhJZGVudGlmaWVyID0gJy86W14vXSsnO1xuICBwcml2YXRlIHBhdGhQYXJhbVJlZ2V4UmVwbGFjZXIgPSAnL1teL10rJztcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSwgcHJpdmF0ZSByb3V0ZXI6IFJvdXRlcikge1xuICAgIHRoaXMuc2V0QmFzZUJyZWFkY3J1bWIoKTtcbiAgICB0aGlzLmRldGVjdFJvdXRlQ2hhbmdlcygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSBicmVhZGNydW1iIGxhYmVsIG9yIG9wdGlvbnMgZm9yIC1cbiAgICpcbiAgICogcm91dGUgKGNvbXBsZXRlIHJvdXRlIHBhdGgpLiByb3V0ZSBjYW4gYmUgcGFzc2VkIHRoZSBzYW1lIHdheSB5b3UgZGVmaW5lIGFuZ3VsYXIgcm91dGVzXG4gICAqIDEpIHVwZGF0ZSBsYWJlbCBFeDogc2V0KCcvbWVudG9yJywgJ01lbnRvcicpLCBzZXQoJy9tZW50b3IvOmlkJywgJ01lbnRvciBEZXRhaWxzJylcbiAgICogMikgY2hhbmdlIHZpc2liaWxpdHkgRXg6IHNldCgnL21lbnRvci86aWQvZWRpdCcsIHsgc2tpcDogdHJ1ZSB9KVxuICAgKiAzKSBhZGQgaW5mbyBFeDogc2V0KCcvbWVudG9yLzppZC9lZGl0JywgeyBpbmZvOiB7IGljb246ICdlZGl0JywgaWNvbkNvbG9yOiAnYmx1ZScgfSB9KVxuICAgKiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gT1IgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgKlxuICAgKiBhbGlhcyAocHJlZml4ZWQgd2l0aCAnQCcpLiBicmVhZGNydW1iIGFsaWFzIGlzIHVuaXF1ZSBmb3IgYSByb3V0ZVxuICAgKiAxKSB1cGRhdGUgbGFiZWwgRXg6IHNldCgnQG1lbnRvcicsICdFbmFibGVyJylcbiAgICogMikgY2hhbmdlIHZpc2liaWxpdHkgRXg6IHNldCgnQG1lbnRvckVkaXQnLCB7IHNraXA6IHRydWUgfSlcbiAgICogMykgYWRkIGluZm8gRXg6IHNldCgnQG1lbnRvckVkaXQnLCB7IGluZm86IHsgaWNvbjogJ2VkaXQnLCBpY29uQ29sb3I6ICdibHVlJyB9IH0pXG4gICAqL1xuICBzZXQocGF0aE9yQWxpYXM6IHN0cmluZywgYnJlYWRjcnVtYjogc3RyaW5nIHwgQnJlYWRjcnVtYikge1xuICAgIGlmICghdGhpcy52YWxpZGF0ZUFyZ3VtZW50cyhwYXRoT3JBbGlhcywgYnJlYWRjcnVtYikpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGJyZWFkY3J1bWIgPT09ICdzdHJpbmcnKSB7XG4gICAgICBicmVhZGNydW1iID0ge1xuICAgICAgICBsYWJlbDogYnJlYWRjcnVtYlxuICAgICAgfTtcbiAgICB9XG5cbiAgICBpZiAocGF0aE9yQWxpYXMuc3RhcnRzV2l0aCgnQCcpKSB7XG4gICAgICB0aGlzLnVwZGF0ZVN0b3JlKHsgLi4uYnJlYWRjcnVtYiwgYWxpYXM6IHBhdGhPckFsaWFzLnNsaWNlKDEpIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBicmVhZGNydW1iRXh0cmFQcm9wcyA9IHRoaXMuYnVpbGRSb3V0ZVJlZ0V4cChwYXRoT3JBbGlhcyk7XG4gICAgICB0aGlzLnVwZGF0ZVN0b3JlKHsgLi4uYnJlYWRjcnVtYiwgLi4uYnJlYWRjcnVtYkV4dHJhUHJvcHMgfSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzZXRCYXNlQnJlYWRjcnVtYigpIHtcbiAgICBjb25zdCBiYXNlQ29uZmlnID0gdGhpcy5yb3V0ZXIuY29uZmlnLmZpbmQocGF0aENvbmZpZyA9PiBwYXRoQ29uZmlnLnBhdGggPT09ICcnKTtcbiAgICBpZiAoYmFzZUNvbmZpZyAmJiBiYXNlQ29uZmlnLmRhdGEpIHtcbiAgICAgIGxldCB7IGxhYmVsLCBhbGlhcywgc2tpcCA9IGZhbHNlLCBpbmZvIH0gPSB0aGlzLmdldEJyZWFkY3J1bWJPcHRpb25zKGJhc2VDb25maWcuZGF0YSk7XG5cbiAgICAgIGxldCBpc0F1dG9HZW5lcmF0ZWRMYWJlbCA9IGZhbHNlO1xuICAgICAgaWYgKHR5cGVvZiBsYWJlbCAhPT0gJ3N0cmluZycgJiYgIWxhYmVsKSB7XG4gICAgICAgIGxhYmVsID0gJyc7XG4gICAgICAgIGlzQXV0b0dlbmVyYXRlZExhYmVsID0gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5iYXNlQnJlYWRjcnVtYiA9IHtcbiAgICAgICAgbGFiZWwsXG4gICAgICAgIGFsaWFzLFxuICAgICAgICBza2lwLFxuICAgICAgICBpbmZvLFxuICAgICAgICByb3V0ZUxpbms6IHRoaXMuYmFzZUhyZWYsXG4gICAgICAgIGlzQXV0b0dlbmVyYXRlZExhYmVsXG4gICAgICB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBXaGVuZXZlciByb3V0ZSBjaGFuZ2VzIGJ1aWxkIGJyZWFkY3J1bWIgbGlzdCBhZ2FpblxuICAgKi9cbiAgcHJpdmF0ZSBkZXRlY3RSb3V0ZUNoYW5nZXMoKSB7XG4gICAgdGhpcy5yb3V0ZXIuZXZlbnRzXG4gICAgICAucGlwZShcbiAgICAgICAgZmlsdGVyKGV2ZW50ID0+IGV2ZW50IGluc3RhbmNlb2YgTmF2aWdhdGlvbkVuZCksXG4gICAgICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKClcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoZXZlbnQgPT4ge1xuICAgICAgICB0aGlzLmN1cnJlbnRCcmVhZGNydW1icyA9IHRoaXMuYmFzZUJyZWFkY3J1bWIgPyBbdGhpcy5iYXNlQnJlYWRjcnVtYl0gOiBbXTtcbiAgICAgICAgdGhpcy5wcmVwYXJlQnJlYWRjcnVtYkxpc3QodGhpcy5hY3RpdmF0ZWRSb3V0ZS5yb290LCB0aGlzLmJhc2VIcmVmKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBwcmVwYXJlQnJlYWRjcnVtYkxpc3QoYWN0aXZhdGVkUm91dGU6IEFjdGl2YXRlZFJvdXRlLCByb3V0ZUxpbmtQcmVmaXg6IHN0cmluZyk6IEJyZWFkY3J1bWJbXSB7XG4gICAgaWYgKGFjdGl2YXRlZFJvdXRlLnJvdXRlQ29uZmlnICYmIGFjdGl2YXRlZFJvdXRlLnJvdXRlQ29uZmlnLnBhdGgpIHtcbiAgICAgIGNvbnN0IGJyZWFkY3J1bWJJdGVtID0gdGhpcy5wcmVwYXJlQnJlYWRjcnVtYkl0ZW0oYWN0aXZhdGVkUm91dGUsIHJvdXRlTGlua1ByZWZpeCk7XG4gICAgICB0aGlzLmN1cnJlbnRCcmVhZGNydW1icy5wdXNoKGJyZWFkY3J1bWJJdGVtKTtcblxuICAgICAgaWYgKGFjdGl2YXRlZFJvdXRlLmZpcnN0Q2hpbGQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucHJlcGFyZUJyZWFkY3J1bWJMaXN0KGFjdGl2YXRlZFJvdXRlLmZpcnN0Q2hpbGQsIGJyZWFkY3J1bWJJdGVtLnJvdXRlTGluayArICcvJyk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChhY3RpdmF0ZWRSb3V0ZS5maXJzdENoaWxkKSB7XG4gICAgICByZXR1cm4gdGhpcy5wcmVwYXJlQnJlYWRjcnVtYkxpc3QoYWN0aXZhdGVkUm91dGUuZmlyc3RDaGlsZCwgcm91dGVMaW5rUHJlZml4KTtcbiAgICB9XG4gICAgLy8gcmVtb3ZlIGJyZWFkY3J1bWIgaXRlbXMgdGhhdCBuZWVkcyB0byBiZSBoaWRkZW4gb3IgZG9uJ3QgaGF2ZSBhIGxhYmVsXG4gICAgY29uc3QgYnJlYWNydW1ic1RvU2hvdyA9IHRoaXMuY3VycmVudEJyZWFkY3J1bWJzLmZpbHRlcihpdGVtID0+ICFpdGVtLnNraXApO1xuXG4gICAgdGhpcy5icmVhZGNydW1icy5uZXh0KGJyZWFjcnVtYnNUb1Nob3cpO1xuICB9XG5cbiAgcHJpdmF0ZSBwcmVwYXJlQnJlYWRjcnVtYkl0ZW0oYWN0aXZhdGVkUm91dGU6IEFjdGl2YXRlZFJvdXRlLCByb3V0ZUxpbmtQcmVmaXg6IHN0cmluZyk6IEJyZWFkY3J1bWIge1xuICAgIGNvbnN0IHsgcGF0aCwgYnJlYWRjcnVtYiB9ID0gdGhpcy5wYXJzZVJvdXRlRGF0YShhY3RpdmF0ZWRSb3V0ZS5yb3V0ZUNvbmZpZyk7XG5cbiAgICAvLyBpbiBjYXNlIG9mIHBhdGggcGFyYW0gZ2V0IHRoZSByZXNvbHZlZCBmb3IgcGFyYW1cbiAgICBjb25zdCByZXNvbHZlZFBhdGggPSB0aGlzLnJlc29sdmVQYXRoUGFyYW0ocGF0aCwgYWN0aXZhdGVkUm91dGUpO1xuICAgIGNvbnN0IHJvdXRlTGluayA9IGAke3JvdXRlTGlua1ByZWZpeH0ke3Jlc29sdmVkUGF0aH1gO1xuXG4gICAgbGV0IHsgbGFiZWwsIGFsaWFzLCBza2lwLCBpbmZvIH0gPSB0aGlzLmdldEZyb21TdG9yZShicmVhZGNydW1iLmFsaWFzLCByb3V0ZUxpbmspO1xuICAgIGxldCBpc0F1dG9HZW5lcmF0ZWRMYWJlbCA9IGZhbHNlO1xuXG4gICAgaWYgKHR5cGVvZiBsYWJlbCAhPT0gJ3N0cmluZycpIHtcbiAgICAgIGlmICh0eXBlb2YgYnJlYWRjcnVtYi5sYWJlbCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgbGFiZWwgPSBicmVhZGNydW1iLmxhYmVsO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbGFiZWwgPSByZXNvbHZlZFBhdGg7XG4gICAgICAgIGlzQXV0b0dlbmVyYXRlZExhYmVsID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgbGFiZWwsXG4gICAgICBhbGlhczogYWxpYXMgfHwgYnJlYWRjcnVtYi5hbGlhcyxcbiAgICAgIHNraXA6IHNraXAgfHwgYnJlYWRjcnVtYi5za2lwLFxuICAgICAgaW5mbzogaW5mbyB8fCBicmVhZGNydW1iLmluZm8sXG4gICAgICByb3V0ZUxpbmssXG4gICAgICBpc0F1dG9HZW5lcmF0ZWRMYWJlbFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogRm9yIGEgc3BlY2lmaWMgcm91dGUsIGJyZWFkY3J1bWIgY2FuIGJlIGRlZmluZWQgZWl0aGVyIG9uIHBhcmVudCBkYXRhIE9SIGl0J3MgY2hpbGQod2hpY2ggaGFzIGVtcHR5IHBhdGgpIGRhdGFcbiAgICogV2hlbiBib3RoIGFyZSBkZWZpbmVkLCBjaGlsZCB0YWtlcyBwcmVjZWRlbmNlXG4gICAqXG4gICAqIEV4OiBCZWxvdyB3ZSBhcmUgc2V0dGluZyBicmVhZGNydW1iIG9uIGJvdGggcGFyZW50IGFuZCBjaGlsZC5cbiAgICogU28sIGNoaWxkIHRha2VzIHByZWNlZGVuY2UgYW5kIFwiRGVmaW5lZCBPbiBDaGlsZFwiIGlzIGRpc3BsYXllZCBmb3IgdGhlIHJvdXRlICdob21lJ1xuICAgKiB7IHBhdGg6ICdob21lJywgbG9hZENoaWxkcmVuOiAnLi9ob21lL2hvbWUubW9kdWxlI0hvbWVNb2R1bGUnICwgZGF0YToge2JyZWFkY3J1bWI6IFwiRGVmaW5lZCBPbiBNb2R1bGVcIn19XG4gICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQU5EXG4gICAqIGNoaWxkcmVuOiBbXG4gICAqICAgeyBwYXRoOiAnJywgY29tcG9uZW50OiBTaG93VXNlckNvbXBvbmVudCwgZGF0YToge2JyZWFkY3J1bWI6IFwiRGVmaW5lZCBPbiBDaGlsZFwiIH1cbiAgICogXVxuICAgKi9cbiAgcHJpdmF0ZSBwYXJzZVJvdXRlRGF0YShyb3V0ZUNvbmZpZykge1xuICAgIGNvbnN0IHsgcGF0aCwgZGF0YSA9IHt9IH0gPSByb3V0ZUNvbmZpZztcbiAgICBjb25zdCBicmVhZGNydW1iID0gdGhpcy5tZXJnZVdpdGhCYXNlQ2hpbGREYXRhKHJvdXRlQ29uZmlnLCB7IC4uLmRhdGEgfSk7XG5cbiAgICByZXR1cm4geyBwYXRoLCBicmVhZGNydW1iIH07XG4gIH1cblxuICBwcml2YXRlIGdldEZyb21TdG9yZShicmVhZGNydW1iQWxpYXM6IHN0cmluZywgcm91dGVMaW5rOiBzdHJpbmcpOiBCcmVhZGNydW1iIHtcbiAgICBsZXQgbWF0Y2hpbmdJdGVtO1xuICAgIGlmIChicmVhZGNydW1iQWxpYXMpIHtcbiAgICAgIG1hdGNoaW5nSXRlbSA9IHRoaXMuZHluYW1pY0JyZWFkY3J1bWJTdG9yZS5maW5kKGl0ZW0gPT4gaXRlbS5hbGlhcyA9PT0gYnJlYWRjcnVtYkFsaWFzKTtcbiAgICB9XG5cbiAgICBpZiAoIW1hdGNoaW5nSXRlbSAmJiByb3V0ZUxpbmspIHtcbiAgICAgIG1hdGNoaW5nSXRlbSA9IHRoaXMuZHluYW1pY0JyZWFkY3J1bWJTdG9yZS5maW5kKGl0ZW0gPT4ge1xuICAgICAgICByZXR1cm4gKGl0ZW0ucm91dGVMaW5rICYmIGl0ZW0ucm91dGVMaW5rID09PSByb3V0ZUxpbmspIHx8IChpdGVtLnJvdXRlUmVnZXggJiYgbmV3IFJlZ0V4cChpdGVtLnJvdXRlUmVnZXgpLnRlc3Qocm91dGVMaW5rICsgJy8nKSk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gbWF0Y2hpbmdJdGVtIHx8IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIFRvIHVwZGF0ZSBicmVhZGNydW1iIGxhYmVsIGZvciBhIHJvdXRlIHdpdGggcGF0aCBwYXJhbSwgd2UgbmVlZCByZWdleCB0aGF0IG1hdGNoZXMgcm91dGUuXG4gICAqIEluc3RlYWQgb2YgdXNlciBwcm92aWRpbmcgcmVnZXgsIHdlIGhlbHAgaW4gcHJlcGFyaW5nIHJlZ2V4IGR5bmFtaWNhbGx5XG4gICAqXG4gICAqIEV4OiByb3V0ZSBkZWNsYXJhdGlvbiAtIHBhdGg6ICcvbWVudG9yLzppZCdcbiAgICogYnJlYWRjcnVtYlNlcnZpY2Uuc2V0KCcvbWVudG9yLzppZCcsICdVZGF5Jyk7XG4gICAqICcvbWVudG9yLzInIE9SICdtZW50b3IvYWRhc2QnIHdlIHNob3VsZCB1c2UgJ1VkYXknIGFzIGxhYmVsXG4gICAqXG4gICAqIHJlZ2V4IHN0cmluZyBpcyBidWlsdCwgaWYgcm91dGUgaGFzIHBhdGggcGFyYW1zKGNvbnRhaW5zIHdpdGggJzonKVxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFJvdXRlUmVnRXhwKHBhdGgpIHtcbiAgICAvLyBlbnN1cmUgbGVhZGluZyBzbGFzaCBpcyBwcm92aWRlZCBpbiB0aGUgcGF0aFxuICAgIGlmICghcGF0aC5zdGFydHNXaXRoKCcvJykpIHtcbiAgICAgIHBhdGggPSAnLycgKyBwYXRoO1xuICAgIH1cblxuICAgIGlmIChwYXRoLmluY2x1ZGVzKHRoaXMucGF0aFBhcmFtUHJlZml4KSkge1xuICAgICAgLy8gcmVwbGFjZSBtYXRoaW5nIHBhdGggcGFyYW0gd2l0aCBhIHJlZ2V4XG4gICAgICAvLyAnL21lbnRvci86aWQnIGJlY29tZXMgJy9tZW50b3IvW14vXScsIHdoaWNoIGZ1cnRoZXIgd2lsbCBiZSBtYXRjaGVkIGluIHVwZGF0ZVN0b3JlXG4gICAgICBjb25zdCByb3V0ZVJlZ2V4ID0gcGF0aC5yZXBsYWNlKG5ldyBSZWdFeHAodGhpcy5wYXRoUGFyYW1SZWdleElkZW50aWZpZXIsICdnJyksIHRoaXMucGF0aFBhcmFtUmVnZXhSZXBsYWNlcik7XG4gICAgICByZXR1cm4geyByb3V0ZVJlZ2V4IH07XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB7IHJvdXRlTGluazogcGF0aCB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgY3VycmVudCBicmVhZGNydW1iIGRlZmluaXRpb24gYW5kIGVtaXQgYSBuZXcgc3RyZWFtIG9mIGJyZWFkY3J1bWJzXG4gICAqIEFsc28gdXBkYXRlIHRoZSBzdG9yZSB0byByZXVzZSBkeW5hbWljIGRlY2xhcmF0aW9uc1xuICAgKi9cbiAgcHJpdmF0ZSB1cGRhdGVTdG9yZShicmVhZGNydW1iKSB7XG4gICAgY29uc3QgeyBicmVhZGNydW1iSXRlbUluZGV4LCBzdG9yZUl0ZW1JbmRleCB9ID0gdGhpcy5nZXRCcmVhZGNydW1iSW5kZXhlcyhicmVhZGNydW1iKTtcblxuICAgIC8vIGlmIGJyZWFkY3J1bWIgaXMgcHJlc2VudCBpbiBjdXJyZW50IGJyZWFkY3J1bWJzIHVwZGF0ZSBpdCBhbmQgZW1pdCBuZXcgc3RyZWFtXG4gICAgaWYgKGJyZWFkY3J1bWJJdGVtSW5kZXggPiAtMSkge1xuICAgICAgdGhpcy5jdXJyZW50QnJlYWRjcnVtYnNbYnJlYWRjcnVtYkl0ZW1JbmRleF0gPSB7IC4uLnRoaXMuY3VycmVudEJyZWFkY3J1bWJzW2JyZWFkY3J1bWJJdGVtSW5kZXhdLCAuLi5icmVhZGNydW1iIH07XG4gICAgICBjb25zdCBicmVhY3J1bWJzVG9TaG93ID0gdGhpcy5jdXJyZW50QnJlYWRjcnVtYnMuZmlsdGVyKGl0ZW0gPT4gIWl0ZW0uc2tpcCk7XG4gICAgICB0aGlzLmJyZWFkY3J1bWJzLm5leHQoWy4uLmJyZWFjcnVtYnNUb1Nob3ddKTtcbiAgICB9XG5cbiAgICAvLyBJZiB0aGUgc3RvcmUgYWxyZWFkeSBoYXMgdGhpcyByb3V0ZSBkZWZpbml0aW9uIHVwZGF0ZSBpdCwgZWxzZSBhZGRcbiAgICBpZiAoc3RvcmVJdGVtSW5kZXggPiAtMSkge1xuICAgICAgdGhpcy5keW5hbWljQnJlYWRjcnVtYlN0b3JlW3N0b3JlSXRlbUluZGV4XSA9IHsgLi4udGhpcy5keW5hbWljQnJlYWRjcnVtYlN0b3JlW3N0b3JlSXRlbUluZGV4XSwgLi4uYnJlYWRjcnVtYiB9O1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmR5bmFtaWNCcmVhZGNydW1iU3RvcmUucHVzaChicmVhZGNydW1iKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldEJyZWFkY3J1bWJJbmRleGVzKGJyZWFkY3J1bWIpOiBhbnkge1xuICAgIGNvbnN0IHsgYWxpYXMsIHJvdXRlTGluaywgcm91dGVSZWdleCB9ID0gYnJlYWRjcnVtYjtcbiAgICBsZXQgaW5kZXhNYXAgPSB7fTtcbiAgICAvLyBpZGVudGlmeSBtYWN0aGluZyBicmVhZGNydW1iIGFuZCBzdG9yZSBpdGVtXG4gICAgaWYgKGFsaWFzKSB7XG4gICAgICBpbmRleE1hcCA9IHRoaXMuZ2V0QnJlYWRjcnVtYkluZGV4ZXNCeVR5cGUoJ2FsaWFzJywgYWxpYXMpO1xuICAgIH0gZWxzZSBpZiAocm91dGVMaW5rKSB7XG4gICAgICBpbmRleE1hcCA9IHRoaXMuZ2V0QnJlYWRjcnVtYkluZGV4ZXNCeVR5cGUoJ3JvdXRlTGluaycsIHJvdXRlTGluayk7XG4gICAgfSBlbHNlIGlmIChyb3V0ZVJlZ2V4KSB7XG4gICAgICBpbmRleE1hcCA9IHRoaXMuZ2V0QnJlYWRjcnVtYkluZGV4ZXNCeVR5cGUoJ3JvdXRlUmVnZXgnLCByb3V0ZVJlZ2V4KTtcbiAgICB9XG4gICAgcmV0dXJuIGluZGV4TWFwO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRCcmVhZGNydW1iSW5kZXhlc0J5VHlwZShrZXk6IHN0cmluZywgdmFsdWU6IHN0cmluZykge1xuICAgIGNvbnN0IGJyZWFkY3J1bWJJdGVtSW5kZXggPSB0aGlzLmN1cnJlbnRCcmVhZGNydW1icy5maW5kSW5kZXgoaXRlbSA9PiB2YWx1ZSA9PT0gaXRlbVtrZXldKTtcbiAgICBjb25zdCBzdG9yZUl0ZW1JbmRleCA9IHRoaXMuZHluYW1pY0JyZWFkY3J1bWJTdG9yZS5maW5kSW5kZXgoaXRlbSA9PiB2YWx1ZSA9PT0gaXRlbVtrZXldKTtcbiAgICByZXR1cm4geyBicmVhZGNydW1iSXRlbUluZGV4LCBzdG9yZUl0ZW1JbmRleCB9O1xuICB9XG5cbiAgcHJpdmF0ZSByZXNvbHZlUGF0aFBhcmFtKHBhdGg6IHN0cmluZywgYWN0aXZhdGVkUm91dGU6IEFjdGl2YXRlZFJvdXRlKSB7XG4gICAgLy8gaWYgdGhlIHBhdGggc2VnbWVudCBpcyBhIHJvdXRlIHBhcmFtLCByZWFkIHRoZSBwYXJhbSB2YWx1ZSBmcm9tIHVybFxuICAgIGlmIChwYXRoLnN0YXJ0c1dpdGgodGhpcy5wYXRoUGFyYW1QcmVmaXgpKSB7XG4gICAgICByZXR1cm4gYWN0aXZhdGVkUm91dGUuc25hcHNob3QucGFyYW1zW3BhdGguc2xpY2UoMSldO1xuICAgIH1cbiAgICByZXR1cm4gcGF0aDtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXQgZW1wdHkgY2hpbGRyZW4gb2YgYSBtb2R1bGUgb3IgQ29tcG9uZW50LiBFbXB0eSBjaGlsZCBpcyB0aGUgb25lIHdpdGggcGF0aDogJydcbiAgICogV2hlbiBwYXJlbnQgYW5kIGl0J3MgY2hpbGRyZW4gKHRoYXQgaGFzIGVtcHR5IHJvdXRlIHBhdGgpIGRlZmluZSBkYXRhXG4gICAqIG1lcmdlIHRoZW0gYm90aCB3aXRoIGNoaWxkIHRha2luZyBwcmVjZWRlbmNlXG4gICAqL1xuICBwcml2YXRlIG1lcmdlV2l0aEJhc2VDaGlsZERhd