xng-breadcrumb
Version:
A declarative and reactive breadcrumb approach for Angular 6 and beyond https://www.npmjs.com/package/xng-breadcrumb
493 lines • 44.1 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
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";
export class BreadcrumbService {
/**
* @param {?} activatedRoute
* @param {?} router
*/
constructor(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' } })
* @param {?} pathOrAlias
* @param {?} breadcrumb
* @return {?}
*/
set(pathOrAlias, breadcrumb) {
if (!this.validateArguments(pathOrAlias, breadcrumb)) {
return;
}
if (typeof breadcrumb === 'string') {
breadcrumb = {
label: breadcrumb
};
}
if (pathOrAlias.startsWith('@')) {
this.updateStore(Object.assign({}, breadcrumb, { alias: pathOrAlias.slice(1) }));
}
else {
/** @type {?} */
const breadcrumbExtraProps = this.buildRouteRegExp(pathOrAlias);
this.updateStore(Object.assign({}, breadcrumb, breadcrumbExtraProps));
}
}
/**
* @private
* @return {?}
*/
setBaseBreadcrumb() {
/** @type {?} */
const baseConfig = this.router.config.find((/**
* @param {?} pathConfig
* @return {?}
*/
pathConfig => pathConfig.path === ''));
if (baseConfig && baseConfig.data) {
let { label, alias, skip = false, info } = this.getBreadcrumbOptions(baseConfig.data);
/** @type {?} */
let isAutoGeneratedLabel = false;
if (typeof label !== 'string' && !label) {
label = '';
isAutoGeneratedLabel = true;
}
this.baseBreadcrumb = {
label,
alias,
skip,
info,
routeLink: this.baseHref,
isAutoGeneratedLabel
};
}
}
/**
* Whenever route changes build breadcrumb list again
* @private
* @return {?}
*/
detectRouteChanges() {
this.router.events
.pipe(filter((/**
* @param {?} event
* @return {?}
*/
event => event instanceof NavigationEnd)), distinctUntilChanged())
.subscribe((/**
* @param {?} event
* @return {?}
*/
event => {
this.currentBreadcrumbs = this.baseBreadcrumb ? [this.baseBreadcrumb] : [];
this.prepareBreadcrumbList(this.activatedRoute.root, this.baseHref);
}));
}
/**
* @private
* @param {?} activatedRoute
* @param {?} routeLinkPrefix
* @return {?}
*/
prepareBreadcrumbList(activatedRoute, routeLinkPrefix) {
if (activatedRoute.routeConfig && activatedRoute.routeConfig.path) {
/** @type {?} */
const 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 {?} */
const breacrumbsToShow = this.currentBreadcrumbs.filter((/**
* @param {?} item
* @return {?}
*/
item => !item.skip));
this.breadcrumbs.next(breacrumbsToShow);
}
/**
* @private
* @param {?} activatedRoute
* @param {?} routeLinkPrefix
* @return {?}
*/
prepareBreadcrumbItem(activatedRoute, routeLinkPrefix) {
const { path, breadcrumb } = this.parseRouteData(activatedRoute.routeConfig);
// in case of path param get the resolved for param
/** @type {?} */
const resolvedPath = this.resolvePathParam(path, activatedRoute);
/** @type {?} */
const routeLink = `${routeLinkPrefix}${resolvedPath}`;
let { label, alias, skip, info } = this.getFromStore(breadcrumb.alias, routeLink);
/** @type {?} */
let isAutoGeneratedLabel = false;
if (typeof label !== 'string') {
if (typeof breadcrumb.label === 'string') {
label = breadcrumb.label;
}
else {
label = resolvedPath;
isAutoGeneratedLabel = true;
}
}
return {
label,
alias: alias || breadcrumb.alias,
skip: skip || breadcrumb.skip,
info: info || breadcrumb.info,
routeLink,
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" }
* ]
* @private
* @param {?} routeConfig
* @return {?}
*/
parseRouteData(routeConfig) {
const { path, data = {} } = routeConfig;
/** @type {?} */
const breadcrumb = this.mergeWithBaseChildData(routeConfig, Object.assign({}, data));
return { path, breadcrumb };
}
/**
* @private
* @param {?} breadcrumbAlias
* @param {?} routeLink
* @return {?}
*/
getFromStore(breadcrumbAlias, routeLink) {
/** @type {?} */
let matchingItem;
if (breadcrumbAlias) {
matchingItem = this.dynamicBreadcrumbStore.find((/**
* @param {?} item
* @return {?}
*/
item => item.alias === breadcrumbAlias));
}
if (!matchingItem && routeLink) {
matchingItem = this.dynamicBreadcrumbStore.find((/**
* @param {?} item
* @return {?}
*/
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 ':')
* @private
* @param {?} path
* @return {?}
*/
buildRouteRegExp(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 {?} */
const routeRegex = path.replace(new RegExp(this.pathParamRegexIdentifier, 'g'), this.pathParamRegexReplacer);
return { routeRegex };
}
else {
return { routeLink: path };
}
}
/**
* Update current breadcrumb definition and emit a new stream of breadcrumbs
* Also update the store to reuse dynamic declarations
* @private
* @param {?} breadcrumb
* @return {?}
*/
updateStore(breadcrumb) {
const { breadcrumbItemIndex, storeItemIndex } = this.getBreadcrumbIndexes(breadcrumb);
// if breadcrumb is present in current breadcrumbs update it and emit new stream
if (breadcrumbItemIndex > -1) {
this.currentBreadcrumbs[breadcrumbItemIndex] = Object.assign({}, this.currentBreadcrumbs[breadcrumbItemIndex], breadcrumb);
/** @type {?} */
const breacrumbsToShow = this.currentBreadcrumbs.filter((/**
* @param {?} item
* @return {?}
*/
item => !item.skip));
this.breadcrumbs.next([...breacrumbsToShow]);
}
// If the store already has this route definition update it, else add
if (storeItemIndex > -1) {
this.dynamicBreadcrumbStore[storeItemIndex] = Object.assign({}, this.dynamicBreadcrumbStore[storeItemIndex], breadcrumb);
}
else {
this.dynamicBreadcrumbStore.push(breadcrumb);
}
}
/**
* @private
* @param {?} breadcrumb
* @return {?}
*/
getBreadcrumbIndexes(breadcrumb) {
const { alias, routeLink, routeRegex } = breadcrumb;
/** @type {?} */
let 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 {?}
*/
getBreadcrumbIndexesByType(key, value) {
/** @type {?} */
const breadcrumbItemIndex = this.currentBreadcrumbs.findIndex((/**
* @param {?} item
* @return {?}
*/
item => value === item[key]));
/** @type {?} */
const storeItemIndex = this.dynamicBreadcrumbStore.findIndex((/**
* @param {?} item
* @return {?}
*/
item => value === item[key]));
return { breadcrumbItemIndex, storeItemIndex };
}
/**
* @private
* @param {?} path
* @param {?} activatedRoute
* @return {?}
*/
resolvePathParam(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
* @private
* @param {?} routeConfig
* @param {?} data
* @return {?}
*/
mergeWithBaseChildData(routeConfig, data) {
if (!routeConfig) {
return this.getBreadcrumbOptions(data);
}
/** @type {?} */
let baseChild;
if (routeConfig.loadChildren) {
// To handle a module with empty child route
baseChild = routeConfig._loadedConfig.routes.find((/**
* @param {?} route
* @return {?}
*/
route => route.path === ''));
}
else if (routeConfig.children) {
// To handle a component with empty child route
baseChild = routeConfig.children.find((/**
* @param {?} route
* @return {?}
*/
route => route.path === ''));
}
return baseChild && baseChild.data
? this.mergeWithBaseChildData(baseChild, Object.assign({}, this.getBreadcrumbOptions(data), this.getBreadcrumbOptions(baseChild.data)))
: this.getBreadcrumbOptions(data);
}
/**
* @private
* @param {?} pathOrAlias
* @param {?} breadcrumb
* @return {?}
*/
validateArguments(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
* @private
* @param {?} data
* @return {?}
*/
getBreadcrumbOptions(data) {
let { breadcrumb } = data;
if (typeof breadcrumb === 'string' || !breadcrumb) {
breadcrumb = {
label: breadcrumb
};
}
return breadcrumb;
}
}
BreadcrumbService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
BreadcrumbService.ctorParameters = () => [
{ 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" });
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJlYWRjcnVtYi5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6Im5nOi8veG5nLWJyZWFkY3J1bWIvIiwic291cmNlcyI6WyJsaWIvYnJlYWRjcnVtYi5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNDLE9BQU8sRUFBRSxjQUFjLEVBQUUsYUFBYSxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdkMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sRUFBRSxNQUFNLGdCQUFnQixDQUFDOzs7QUFNOUQsTUFBTSxPQUFPLGlCQUFpQjs7Ozs7SUFpQzVCLFlBQW9CLGNBQThCLEVBQVUsTUFBYztRQUF0RCxtQkFBYyxHQUFkLGNBQWMsQ0FBZ0I7UUFBVSxXQUFNLEdBQU4sTUFBTSxDQUFRO1FBM0JsRSxhQUFRLEdBQUcsR0FBRyxDQUFDOzs7Ozs7UUFPZiwyQkFBc0IsR0FBaUIsRUFBRSxDQUFDOzs7Ozs7UUFPMUMsdUJBQWtCLEdBQWlCLEVBQUUsQ0FBQzs7Ozs7UUFNdEMsZ0JBQVcsR0FBRyxJQUFJLGVBQWUsQ0FBZSxFQUFFLENBQUMsQ0FBQztRQUNyRCxpQkFBWSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsWUFBWSxFQUFFLENBQUM7UUFFOUMsb0JBQWUsR0FBRyxHQUFHLENBQUM7UUFDdEIsNkJBQXdCLEdBQUcsU0FBUyxDQUFDO1FBQ3JDLDJCQUFzQixHQUFHLFFBQVEsQ0FBQztRQUd4QyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztRQUN6QixJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztJQUM1QixDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7SUFnQkQsR0FBRyxDQUFDLFdBQW1CLEVBQUUsVUFBK0I7UUFDdEQsSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FB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WUFDMUQsT0FBTyxDQUFDLEtBQUssQ0FBQyxxRkFBcUYsQ0FBQyxDQUFDO1lBQ3JHLE9BQU8sS0FBSyxDQUFDO1NBQ2Q7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7Ozs7Ozs7O0lBTU8sb0JBQW9CLENBQUMsSUFBSTtZQUMzQixFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUk7UUFDekIsSUFBSSxPQUFPLFVBQVUsS0FBSyxRQUFRLElBQUksQ0FBQyxVQUFVLEVBQUU7WUFDakQsVUFBVSxHQUFHO2dCQUNYLEtBQUssRUFBRSxVQUFVO2FBQ2xCLENBQUM7U0FDSDtRQUNELE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUM7OztZQTdURixVQUFVLFNBQUM7Z0JBQ1YsVUFBVSxFQUFFLE1BQU07YUFDbkI7Ozs7WUFQUSxjQUFjO1lBQWlCLE1BQU07Ozs7Ozs7OztJQVk1QywyQ0FBbUM7Ozs7O0lBRW5DLHFDQUF1Qjs7Ozs7Ozs7SUFPdkIsbURBQWtEOzs7Ozs7OztJQU9sRCwrQ0FBOEM7Ozs7Ozs7SUFNOUMsd0NBQTREOztJQUM1RCx5Q0FBc0Q7Ozs7O0lBRXRELDRDQUE4Qjs7Ozs7SUFDOUIscURBQTZDOzs7OztJQUM3QyxtREFBMEM7Ozs7O0lBRTlCLDJDQUFzQzs7Ozs7SUFBRSxtQ0FBc0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZSwgTmF2aWdhdGlvbkVuZCwgUm91dGVyIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZGlzdGluY3RVbnRpbENoYW5nZWQsIGZpbHRlciB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IEJyZWFkY3J1bWIgfSBmcm9tICcuL2JyZWFkY3J1bWInO1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBCcmVhZGNydW1iU2VydmljZSB7XG4gIC8qKlxuICAgKiBicmVhZGNydW1iIGxhYmVsIGZvciBiYXNlIE9SIHJvb3QgcGF0aC4gVXN1YWxseSwgdGhpcyBjYW4gYmUgc2V0IGFzICdIb21lJ1xuICAgKi9cbiAgcHJpdmF0ZSBiYXNlQnJlYWRjcnVtYjogQnJlYWRjcnVtYjtcblxuICBwcml2YXRlIGJhc2VIcmVmID0gJy8nO1xuXG4gIC8qKlxuICAgKiBkeW5hbWljQnJlYWRjcnVtYlN0b3JlIGhvbGRzIGluZm9ybWF0aW9uIGFib3V0IGR5bmFtaWNhbGx5IHVwZGF0ZWQgYnJlYWRjcnVtYnMuXG4gICAqIEJyZWFkY3J1bWJzIGNhbiBiZSBzZXQgZnJvbSBhbnl3aGVyZSAoY29tcG9uZW50LCBzZXJ2aWNlKSBpbiB0aGUgYXBwLlxuICAgKiBPbiBldmVyeSBicmVhZGNydW1iIHVwZGF0ZSBjaGVjayB0aGlzIHN0b3JlIGFuZCB1c2UgdGhlIGluZm8gaWYgYXZhaWxhYmxlLlxuICAgKi9cbiAgcHJpdmF0ZSBkeW5hbWljQnJlYWRjcnVtYlN0b3JlOiBCcmVhZGNydW1iW10gPSBbXTtcblxuICAvKipcbiAgICogYnJlYWRjcnVtYkxpc3QgZm9yIHRoZSBjdXJyZW50IHJvdXRlXG4gICAqIFdoZW4gYnJlYWRjcnVtYiBpbmZvIGlzIGNoYW5nZWQgZHluYW1pY2FsbHksIGNoZWNrIGlmIHRoZSBjdXJyZW50QnJlYWRjcnVtYnMgaXMgZWZmZWN0ZWRcbiAgICogSWYgZWZmZWN0ZWQsIHVwZGF0ZSB0aGUgY2hhbmdlIGFuZCBlbWl0IGEgbmV3IHN0cmVhbVxuICAgKi9cbiAgcHJpdmF0ZSBjdXJyZW50QnJlYWRjcnVtYnM6IEJyZWFkY3J1bWJbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBCcmVhZGNydW1icyBvYnNlcnZhYmxlIHRvIGJlIHN1YnNjcmliZWQgYnkgQnJlYWRjcnVtYkNvbXBvbmVudFxuICAgKiBFbWl0cyBvbiBldmVyeSByb3V0ZSBjaGFuZ2UgT1IgZHluYW1pYyB1cGRhdGUgb2YgYnJlYWRjcnVtYlxuICAgKi9cbiAgcHJpdmF0ZSBicmVhZGNydW1icyA9IG5ldyBCZWhhdmlvclN1YmplY3Q8QnJlYWRjcnVtYltdPihbXSk7XG4gIHB1YmxpYyBicmVhZGNydW1icyQgPSB0aGlzLmJyZWFkY3J1bWJzLmFzT2JzZXJ2YWJsZSgpO1xuXG4gIHByaXZhdGUgcGF0aFBhcmFtUHJlZml4ID0gJzonO1xuICBwcml2YXRlIHBhdGhQYXJhbVJlZ2V4SWRlbnRpZmllciA9ICcvOlteL10rJztcbiAgcHJpdmF0ZSBwYXRoUGFyYW1SZWdleFJlcGxhY2VyID0gJy9bXi9dKyc7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBhY3RpdmF0ZWRSb3V0ZTogQWN0aXZhdGVkUm91dGUsIHByaXZhdGUgcm91dGVyOiBSb3V0ZXIpIHtcbiAgICB0aGlzLnNldEJhc2VCcmVhZGNydW1iKCk7XG4gICAgdGhpcy5kZXRlY3RSb3V0ZUNoYW5nZXMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgYnJlYWRjcnVtYiBsYWJlbCBvciBvcHRpb25zIGZvciAtXG4gICAqXG4gICAqIHJvdXRlIChjb21wbGV0ZSByb3V0ZSBwYXRoKS4gcm91dGUgY2FuIGJlIHBhc3NlZCB0aGUgc2FtZSB3YXkgeW91IGRlZmluZSBhbmd1bGFyIHJvdXRlc1xuICAgKiAxKSB1cGRhdGUgbGFiZWwgRXg6IHNldCgnL21lbnRvcicsICdNZW50b3InKSwgc2V0KCcvbWVudG9yLzppZCcsICdNZW50b3IgRGV0YWlscycpXG4gICAqIDIpIGNoYW5nZSB2aXNpYmlsaXR5IEV4OiBzZXQoJy9tZW50b3IvOmlkL2VkaXQnLCB7IHNraXA6IHRydWUgfSlcbiAgICogMykgYWRkIGluZm8gRXg6IHNldCgnL21lbnRvci86aWQvZWRpdCcsIHsgaW5mbzogeyBpY29uOiAnZWRpdCcsIGljb25Db2xvcjogJ2JsdWUnIH0gfSlcbiAgICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIE9SIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICpcbiAgICogYWxpYXMgKHByZWZpeGVkIHdpdGggJ0AnKS4gYnJlYWRjcnVtYiBhbGlhcyBpcyB1bmlxdWUgZm9yIGEgcm91dGVcbiAgICogMSkgdXBkYXRlIGxhYmVsIEV4OiBzZXQoJ0BtZW50b3InLCAnRW5hYmxlcicpXG4gICAqIDIpIGNoYW5nZSB2aXNpYmlsaXR5IEV4OiBzZXQoJ0BtZW50b3JFZGl0JywgeyBza2lwOiB0cnVlIH0pXG4gICAqIDMpIGFkZCBpbmZvIEV4OiBzZXQoJ0BtZW50b3JFZGl0JywgeyBpbmZvOiB7IGljb246ICdlZGl0JywgaWNvbkNvbG9yOiAnYmx1ZScgfSB9KVxuICAgKi9cbiAgc2V0KHBhdGhPckFsaWFzOiBzdHJpbmcsIGJyZWFkY3J1bWI6IHN0cmluZyB8IEJyZWFkY3J1bWIpIHtcbiAgICBpZiAoIXRoaXMudmFsaWRhdGVBcmd1bWVudHMocGF0aE9yQWxpYXMsIGJyZWFkY3J1bWIpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBicmVhZGNydW1iID09PSAnc3RyaW5nJykge1xuICAgICAgYnJlYWRjcnVtYiA9IHtcbiAgICAgICAgbGFiZWw6IGJyZWFkY3J1bWJcbiAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKHBhdGhPckFsaWFzLnN0YXJ0c1dpdGgoJ0AnKSkge1xuICAgICAgdGhpcy51cGRhdGVTdG9yZSh7IC4uLmJyZWFkY3J1bWIsIGFsaWFzOiBwYXRoT3JBbGlhcy5zbGljZSgxKSB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgYnJlYWRjcnVtYkV4dHJhUHJvcHMgPSB0aGlzLmJ1aWxkUm91dGVSZWdFeHAocGF0aE9yQWxpYXMpO1xuICAgICAgdGhpcy51cGRhdGVTdG9yZSh7IC4uLmJyZWFkY3J1bWIsIC4uLmJyZWFkY3J1bWJFeHRyYVByb3BzIH0pO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc2V0QmFzZUJyZWFkY3J1bWIoKSB7XG4gICAgY29uc3QgYmFzZUNvbmZpZyA9IHRoaXMucm91dGVyLmNvbmZpZy5maW5kKHBhdGhDb25maWcgPT4gcGF0aENvbmZpZy5wYXRoID09PSAnJyk7XG4gICAgaWYgKGJhc2VDb25maWcgJiYgYmFzZUNvbmZpZy5kYXRhKSB7XG4gICAgICBsZXQgeyBsYWJlbCwgYWxpYXMsIHNraXAgPSBmYWxzZSwgaW5mbyB9ID0gdGhpcy5nZXRCcmVhZGNydW1iT3B0aW9ucyhiYXNlQ29uZmlnLmRhdGEpO1xuXG4gICAgICBsZXQgaXNBdXRvR2VuZXJhdGVkTGFiZWwgPSBmYWxzZTtcbiAgICAgIGlmICh0eXBlb2YgbGFiZWwgIT09ICdzdHJpbmcnICYmICFsYWJlbCkge1xuICAgICAgICBsYWJlbCA9ICcnO1xuICAgICAgICBpc0F1dG9HZW5lcmF0ZWRMYWJlbCA9IHRydWU7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuYmFzZUJyZWFkY3J1bWIgPSB7XG4gICAgICAgIGxhYmVsLFxuICAgICAgICBhbGlhcyxcbiAgICAgICAgc2tpcCxcbiAgICAgICAgaW5mbyxcbiAgICAgICAgcm91dGVMaW5rOiB0aGlzLmJhc2VIcmVmLFxuICAgICAgICBpc0F1dG9HZW5lcmF0ZWRMYWJlbFxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogV2hlbmV2ZXIgcm91dGUgY2hhbmdlcyBidWlsZCBicmVhZGNydW1iIGxpc3QgYWdhaW5cbiAgICovXG4gIHByaXZhdGUgZGV0ZWN0Um91dGVDaGFuZ2VzKCkge1xuICAgIHRoaXMucm91dGVyLmV2ZW50c1xuICAgICAgLnBpcGUoXG4gICAgICAgIGZpbHRlcihldmVudCA9PiBldmVudCBpbnN0YW5jZW9mIE5hdmlnYXRpb25FbmQpLFxuICAgICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKGV2ZW50ID0+IHtcbiAgICAgICAgdGhpcy5jdXJyZW50QnJlYWRjcnVtYnMgPSB0aGlzLmJhc2VCcmVhZGNydW1iID8gW3RoaXMuYmFzZUJyZWFkY3J1bWJdIDogW107XG4gICAgICAgIHRoaXMucHJlcGFyZUJyZWFkY3J1bWJMaXN0KHRoaXMuYWN0aXZhdGVkUm91dGUucm9vdCwgdGhpcy5iYXNlSHJlZik7XG4gICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgcHJlcGFyZUJyZWFkY3J1bWJMaXN0KGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSwgcm91dGVMaW5rUHJlZml4OiBzdHJpbmcpOiBCcmVhZGNydW1iW10ge1xuICAgIGlmIChhY3RpdmF0ZWRSb3V0ZS5yb3V0ZUNvbmZpZyAmJiBhY3RpdmF0ZWRSb3V0ZS5yb3V0ZUNvbmZpZy5wYXRoKSB7XG4gICAgICBjb25zdCBicmVhZGNydW1iSXRlbSA9IHRoaXMucHJlcGFyZUJyZWFkY3J1bWJJdGVtKGFjdGl2YXRlZFJvdXRlLCByb3V0ZUxpbmtQcmVmaXgpO1xuICAgICAgdGhpcy5jdXJyZW50QnJlYWRjcnVtYnMucHVzaChicmVhZGNydW1iSXRlbSk7XG5cbiAgICAgIGlmIChhY3RpdmF0ZWRSb3V0ZS5maXJzdENoaWxkKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnByZXBhcmVCcmVhZGNydW1iTGlzdChhY3RpdmF0ZWRSb3V0ZS5maXJzdENoaWxkLCBicmVhZGNydW1iSXRlbS5yb3V0ZUxpbmsgKyAnLycpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoYWN0aXZhdGVkUm91dGUuZmlyc3RDaGlsZCkge1xuICAgICAgcmV0dXJuIHRoaXMucHJlcGFyZUJyZWFkY3J1bWJMaXN0KGFjdGl2YXRlZFJvdXRlLmZpcnN0Q2hpbGQsIHJvdXRlTGlua1ByZWZpeCk7XG4gICAgfVxuICAgIC8vIHJlbW92ZSBicmVhZGNydW1iIGl0ZW1zIHRoYXQgbmVlZHMgdG8gYmUgaGlkZGVuIG9yIGRvbid0IGhhdmUgYSBsYWJlbFxuICAgIGNvbnN0IGJyZWFjcnVtYnNUb1Nob3cgPSB0aGlzLmN1cnJlbnRCcmVhZGNydW1icy5maWx0ZXIoaXRlbSA9PiAhaXRlbS5za2lwKTtcblxuICAgIHRoaXMuYnJlYWRjcnVtYnMubmV4dChicmVhY3J1bWJzVG9TaG93KTtcbiAgfVxuXG4gIHByaXZhdGUgcHJlcGFyZUJyZWFkY3J1bWJJdGVtKGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSwgcm91dGVMaW5rUHJlZml4OiBzdHJpbmcpOiBCcmVhZGNydW1iIHtcbiAgICBjb25zdCB7IHBhdGgsIGJyZWFkY3J1bWIgfSA9IHRoaXMucGFyc2VSb3V0ZURhdGEoYWN0aXZhdGVkUm91dGUucm91dGVDb25maWcpO1xuXG4gICAgLy8gaW4gY2FzZSBvZiBwYXRoIHBhcmFtIGdldCB0aGUgcmVzb2x2ZWQgZm9yIHBhcmFtXG4gICAgY29uc3QgcmVzb2x2ZWRQYXRoID0gdGhpcy5yZXNvbHZlUGF0aFBhcmFtKHBhdGgsIGFjdGl2YXRlZFJvdXRlKTtcbiAgICBjb25zdCByb3V0ZUxpbmsgPSBgJHtyb3V0ZUxpbmtQcmVmaXh9JHtyZXNvbHZlZFBhdGh9YDtcblxuICAgIGxldCB7IGxhYmVsLCBhbGlhcywgc2tpcCwgaW5mbyB9ID0gdGhpcy5nZXRGcm9tU3RvcmUoYnJlYWRjcnVtYi5hbGlhcywgcm91dGVMaW5rKTtcbiAgICBsZXQgaXNBdXRvR2VuZXJhdGVkTGFiZWwgPSBmYWxzZTtcblxuICAgIGlmICh0eXBlb2YgbGFiZWwgIT09ICdzdHJpbmcnKSB7XG4gICAgICBpZiAodHlwZW9mIGJyZWFkY3J1bWIubGFiZWwgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIGxhYmVsID0gYnJlYWRjcnVtYi5sYWJlbDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxhYmVsID0gcmVzb2x2ZWRQYXRoO1xuICAgICAgICBpc0F1dG9HZW5lcmF0ZWRMYWJlbCA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGxhYmVsLFxuICAgICAgYWxpYXM6IGFsaWFzIHx8IGJyZWFkY3J1bWIuYWxpYXMsXG4gICAgICBza2lwOiBza2lwIHx8IGJyZWFkY3J1bWIuc2tpcCxcbiAgICAgIGluZm86IGluZm8gfHwgYnJlYWRjcnVtYi5pbmZvLFxuICAgICAgcm91dGVMaW5rLFxuICAgICAgaXNBdXRvR2VuZXJhdGVkTGFiZWxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEZvciBhIHNwZWNpZmljIHJvdXRlLCBicmVhZGNydW1iIGNhbiBiZSBkZWZpbmVkIGVpdGhlciBvbiBwYXJlbnQgZGF0YSBPUiBpdCdzIGNoaWxkKHdoaWNoIGhhcyBlbXB0eSBwYXRoKSBkYXRhXG4gICAqIFdoZW4gYm90aCBhcmUgZGVmaW5lZCwgY2hpbGQgdGFrZXMgcHJlY2VkZW5jZVxuICAgKlxuICAgKiBFeDogQmVsb3cgd2UgYXJlIHNldHRpbmcgYnJlYWRjcnVtYiBvbiBib3RoIHBhcmVudCBhbmQgY2hpbGQuXG4gICAqIFNvLCBjaGlsZCB0YWtlcyBwcmVjZWRlbmNlIGFuZCBcIkRlZmluZWQgT24gQ2hpbGRcIiBpcyBkaXNwbGF5ZWQgZm9yIHRoZSByb3V0ZSAnaG9tZSdcbiAgICogeyBwYXRoOiAnaG9tZScsIGxvYWRDaGlsZHJlbjogJy4vaG9tZS9ob21lLm1vZHVsZSNIb21lTW9kdWxlJyAsIGRhdGE6IHticmVhZGNydW1iOiBcIkRlZmluZWQgT24gTW9kdWxlXCJ9fVxuICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFORFxuICAgKiBjaGlsZHJlbjogW1xuICAgKiAgIHsgcGF0aDogJycsIGNvbXBvbmVudDogU2hvd1VzZXJDb21wb25lbnQsIGRhdGE6IHticmVhZGNydW1iOiBcIkRlZmluZWQgT24gQ2hpbGRcIiB9XG4gICAqIF1cbiAgICovXG4gIHByaXZhdGUgcGFyc2VSb3V0ZURhdGEocm91dGVDb25maWcpIHtcbiAgICBjb25zdCB7IHBhdGgsIGRhdGEgPSB7fSB9ID0gcm91dGVDb25maWc7XG4gICAgY29uc3QgYnJlYWRjcnVtYiA9IHRoaXMubWVyZ2VXaXRoQmFzZUNoaWxkRGF0YShyb3V0ZUNvbmZpZywgeyAuLi5kYXRhIH0pO1xuXG4gICAgcmV0dXJuIHsgcGF0aCwgYnJlYWRjcnVtYiB9O1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRGcm9tU3RvcmUoYnJlYWRjcnVtYkFsaWFzOiBzdHJpbmcsIHJvdXRlTGluazogc3RyaW5nKTogQnJlYWRjcnVtYiB7XG4gICAgbGV0IG1hdGNoaW5nSXRlbTtcbiAgICBpZiAoYnJlYWRjcnVtYkFsaWFzKSB7XG4gICAgICBtYXRjaGluZ0l0ZW0gPSB0aGlzLmR5bmFtaWNCcmVhZGNydW1iU3RvcmUuZmluZChpdGVtID0+IGl0ZW0uYWxpYXMgPT09IGJyZWFkY3J1bWJBbGlhcyk7XG4gICAgfVxuXG4gICAgaWYgKCFtYXRjaGluZ0l0ZW0gJiYgcm91dGVMaW5rKSB7XG4gICAgICBtYXRjaGluZ0l0ZW0gPSB0aGlzLmR5bmFtaWNCcmVhZGNydW1iU3RvcmUuZmluZChpdGVtID0+IHtcbiAgICAgICAgcmV0dXJuIChpdGVtLnJvdXRlTGluayAmJiBpdGVtLnJvdXRlTGluayA9PT0gcm91dGVMaW5rKSB8fCAoaXRlbS5yb3V0ZVJlZ2V4ICYmIG5ldyBSZWdFeHAoaXRlbS5yb3V0ZVJlZ2V4KS50ZXN0KHJvdXRlTGluayArICcvJykpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG1hdGNoaW5nSXRlbSB8fCB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUbyB1cGRhdGUgYnJlYWRjcnVtYiBsYWJlbCBmb3IgYSByb3V0ZSB3aXRoIHBhdGggcGFyYW0sIHdlIG5lZWQgcmVnZXggdGhhdCBtYXRjaGVzIHJvdXRlLlxuICAgKiBJbnN0ZWFkIG9mIHVzZXIgcHJvdmlkaW5nIHJlZ2V4LCB3ZSBoZWxwIGluIHByZXBhcmluZyByZWdleCBkeW5hbWljYWxseVxuICAgKlxuICAgKiBFeDogcm91dGUgZGVjbGFyYXRpb24gLSBwYXRoOiAnL21lbnRvci86aWQnXG4gICAqIGJyZWFkY3J1bWJTZXJ2aWNlLnNldCgnL21lbnRvci86aWQnLCAnVWRheScpO1xuICAgKiAnL21lbnRvci8yJyBPUiAnbWVudG9yL2FkYXNkJyB3ZSBzaG91bGQgdXNlICdVZGF5JyBhcyBsYWJlbFxuICAgKlxuICAgKiByZWdleCBzdHJpbmcgaXMgYnVpbHQsIGlmIHJvdXRlIGhhcyBwYXRoIHBhcmFtcyhjb250YWlucyB3aXRoICc6JylcbiAgICovXG4gIHByaXZhdGUgYnVpbGRSb3V0ZVJlZ0V4cChwYXRoKSB7XG4gICAgLy8gZW5zdXJlIGxlYWRpbmcgc2xhc2ggaXMgcHJvdmlkZWQgaW4gdGhlIHBhdGhcbiAgICBpZiAoIXBhdGguc3RhcnRzV2l0aCgnLycpKSB7XG4gICAgICBwYXRoID0gJy8nICsgcGF0aDtcbiAgICB9XG5cbiAgICBpZiAocGF0aC5pbmNsdWRlcyh0aGlzLnBhdGhQYXJhbVByZWZpeCkpIHtcbiAgICAgIC8vIHJlcGxhY2UgbWF0aGluZyBwYXRoIHBhcmFtIHdpdGggYSByZWdleFxuICAgICAgLy8gJy9tZW50b3IvOmlkJyBiZWNvbWVzICcvbWVudG9yL1teL10nLCB3aGljaCBmdXJ0aGVyIHdpbGwgYmUgbWF0Y2hlZCBpbiB1cGRhdGVTdG9yZVxuICAgICAgY29uc3Qgcm91dGVSZWdleCA9IHBhdGgucmVwbGFjZShuZXcgUmVnRXhwKHRoaXMucGF0aFBhcmFtUmVnZXhJZGVudGlmaWVyLCAnZycpLCB0aGlzLnBhdGhQYXJhbVJlZ2V4UmVwbGFjZXIpO1xuICAgICAgcmV0dXJuIHsgcm91dGVSZWdleCB9O1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4geyByb3V0ZUxpbms6IHBhdGggfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIGN1cnJlbnQgYnJlYWRjcnVtYiBkZWZpbml0aW9uIGFuZCBlbWl0IGEgbmV3IHN0cmVhbSBvZiBicmVhZGNydW1ic1xuICAgKiBBbHNvIHVwZGF0ZSB0aGUgc3RvcmUgdG8gcmV1c2UgZHluYW1pYyBkZWNsYXJhdGlvbnNcbiAgICovXG4gIHByaXZhdGUgdXBkYXRlU3RvcmUoYnJlYWRjcnVtYikge1xuICAgIGNvbnN0IHsgYnJlYWRjcnVtYkl0ZW1JbmRleCwgc3RvcmVJdGVtSW5kZXggfSA9IHRoaXMuZ2V0QnJlYWRjcnVtYkluZGV4ZXMoYnJlYWRjcnVtYik7XG5cbiAgICAvLyBpZiBicmVhZGNydW1iIGlzIHByZXNlbnQgaW4gY3VycmVudCBicmVhZGNydW1icyB1cGRhdGUgaXQgYW5kIGVtaXQgbmV3IHN0cmVhbVxuICAgIGlmIChicmVhZGNydW1iSXRlbUluZGV4ID4gLTEpIHtcbiAgICAgIHRoaXMuY3VycmVudEJyZWFkY3J1bWJzW2JyZWFkY3J1bWJJdGVtSW5kZXhdID0geyAuLi50aGlzLmN1cnJlbnRCcmVhZGNydW1ic1ticmVhZGNydW1iSXRlbUluZGV4XSwgLi4uYnJlYWRjcnVtYiB9O1xuICAgICAgY29uc3QgYnJlYWNydW1ic1RvU2hvdyA9IHRoaXMuY3VycmVudEJyZWFkY3J1bWJzLmZpbHRlcihpdGVtID0+ICFpdGVtLnNraXApO1xuICAgICAgdGhpcy5icmVhZGNydW1icy5uZXh0KFsuLi5icmVhY3J1bWJzVG9TaG93XSk7XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIHN0b3JlIGFscmVhZHkgaGFzIHRoaXMgcm91dGUgZGVmaW5pdGlvbiB1cGRhdGUgaXQsIGVsc2UgYWRkXG4gICAgaWYgKHN0b3JlSXRlbUluZGV4ID4gLTEpIHtcbiAgICAgIHRoaXMuZHluYW1pY0JyZWFkY3J1bWJTdG9yZVtzdG9yZUl0ZW1JbmRleF0gPSB7IC4uLnRoaXMuZHluYW1pY0JyZWFkY3J1bWJTdG9yZVtzdG9yZUl0ZW1JbmRleF0sIC4uLmJyZWFkY3J1bWIgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5keW5hbWljQnJlYWRjcnVtYlN0b3JlLnB1c2goYnJlYWRjcnVtYik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBnZXRCcmVhZGNydW1iSW5kZXhlcyhicmVhZGNydW1iKTogYW55IHtcbiAgICBjb25zdCB7IGFsaWFzLCByb3V0ZUxpbmssIHJvdXRlUmVnZXggfSA9IGJyZWFkY3J1bWI7XG4gICAgbGV0IGluZGV4TWFwID0ge307XG4gICAgLy8gaWRlbnRpZnkgbWFjdGhpbmcgYnJlYWRjcnVtYiBhbmQgc3RvcmUgaXRlbVxuICAgIGlmIChhbGlhcykge1xuICAgICAgaW5kZXhNYXAgPSB0aGlzLmdldEJyZWFkY3J1bWJJbmRleGVzQnlUeXBlKCdhbGlhcycsIGFsaWFzKTtcbiAgICB9IGVsc2UgaWYgKHJvdXRlTGluaykge1xuICAgICAgaW5kZXhNYXAgPSB0aGlzLmdldEJyZWFkY3J1bWJJbmRleGVzQnlUeXBlKCdyb3V0ZUxpbmsnLCByb3V0ZUxpbmspO1xuICAgIH0gZWxzZSBpZiAocm91dGVSZWdleCkge1xuICAgICAgaW5kZXhNYXAgPSB0aGlzLmdldEJyZWFkY3J1bWJJbmRleGVzQnlUeXBlKCdyb3V0ZVJlZ2V4Jywgcm91dGVSZWdleCk7XG4gICAgfVxuICAgIHJldHVybiBpbmRleE1hcDtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0QnJlYWRjcnVtYkluZGV4ZXNCeVR5cGUoa2V5OiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcpIHtcbiAgICBjb25zdCBicmVhZGNydW1iSXRlbUluZGV4ID0gdGhpcy5jdXJyZW50QnJlYWRjcnVtYnMuZmluZEluZGV4KGl0ZW0gPT4gdmFsdWUgPT09IGl0ZW1ba2V5XSk7XG4gICAgY29uc3Qgc3RvcmVJdGVtSW5kZXggPSB0aGlzLmR5bmFtaWNCcmVhZGNydW1iU3RvcmUuZmluZEluZGV4KGl0ZW0gPT4gdmFsdWUgPT09IGl0ZW1ba2V5XSk7XG4gICAgcmV0dXJuIHsgYnJlYWRjcnVtYkl0ZW1JbmRleCwgc3RvcmVJdGVtSW5kZXggfTtcbiAgfVxuXG4gIHByaXZhdGUgcmVzb2x2ZVBhdGhQYXJhbShwYXRoOiBzdHJpbmcsIGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSkge1xuICAgIC8vIGlmIHRoZSBwYXRoIHNlZ21lbnQgaXMgYSByb3V0ZSBwYXJhbSwgcmVhZCB0aGUgcGFyYW0gdmFsdWUgZnJvbSB1cmxcbiAgICBpZiAocGF0aC5zdGFydHNXaXRoKHRoaXMucGF0aFBhcmFtUHJlZml4KSkge1xuICAgICAgcmV0dXJuIGFjdGl2YXRlZFJvdXRlLnNuYXBzaG90LnBhcmFtc1twYXRoLnNsaWNlKDEpXTtcbiAgICB9XG4gICAgcmV0dXJuIHBhdGg7XG4gIH1cblxuICAvKipcbiAgICogZ2V0IGVtcHR5IGNoaWxkcmVuIG9mIGEgbW9kdWxlIG9yIENvbXBvbmVudC4gRW1wdHkgY2hpbGQgaXMgdGhlIG9uZSB3aXRoIHBhdGg6ICcnXG4gICAqIFdoZW4gcGFyZW50IGFuZCBpdCdzIGNoaWxkcmVuICh0aGF0IGhhcyBlbXB0eSByb3V0ZSBwYXRoKSBkZWZpbmUgZGF0YVxuICAgKiBtZXJnZSB0aGVtIGJvdGggd2l0aCBjaGlsZCB0YWtpbmcgcHJlY2VkZW5jZVxuICAgKi9cbiAgcHJpdmF0ZSBtZXJnZVdpdGhCYXNlQ2hpbGREYXRhKHJvdXRlQ29uZmlnLCBkYXRhKTogQnJlYWRjcnVtYiB7XG4gICAgaWYgKCFyb3V0ZUNvbmZpZykge1xuICAgICAgcmV0dXJuIHRoaXMuZ2V0QnJlYWRjcnVtYk9wdGlvbnMoZGF0YSk7XG4gICAgfVxuXG4gICAgbGV0IGJhc2VDaGlsZDtcbiAgICBpZiAocm91dGVDb25maWcubG9hZENoaWxkcmVuKSB7XG4gICAgICAvLyBUbyBoYW5kbGUgYSBtb2R1bGUgd2l0aCBlbXB0eSBjaGlsZCByb3V0ZVxuICAgICAgYmFzZUNoaWxkID0gcm91dGVDb25maWcuX2xvYWRlZENvbmZpZy5yb3V0ZXMuZmluZChyb3V0ZSA9PiByb3V0ZS5wYXRoID09PSAnJyk7XG4gICAgfSBlbHNlIGlmIChyb3V0ZUNvbmZpZy5jaGlsZHJlbikge1xuICAgICAgLy8gVG8gaGFuZGxlIGEgY29tcG9uZW50IHdpdGggZW1wdHkgY2hpbGQgcm91dGVcbiAgICAgIGJhc2VDaGlsZCA9IHJvdXRlQ29uZmlnLmNoaWxkcmVuLmZpbmQocm91dGUgPT4gcm91dGUucGF0aCA9PT0gJycpO1xuICAgIH1cblxuICAgIHJldHVybiBiYXNlQ2hpbGQgJiYgYmFzZUNoaWxkLmRhdGFcbiAgICAgID8gdGhpcy5tZXJnZVdpdGhCYXNlQ2hpbGREYXRhKGJhc2VDaGlsZCwge1xuICAgICAgICAgIC4uLnRoaXMuZ2V0QnJlYWRjcnVtYk9wdGlvbnMoZGF0YSksXG4gICAgICAgICAgLi4udGhpcy5nZXRCcmVhZGNydW1iT3B0aW9ucyhiYXNlQ2hpbGQuZGF0YSlcbiAgICAgICAgfSlcbiAgICAgIDogdGhpcy5nZXRCcmVhZGNydW1iT3B0aW9ucyhkYXRhKTtcbiAgfVxuXG4gIHByaXZhdGUgdmFsaWRhdGVBcmd1bWVudHMocGF0aE9yQWxpYXMsIGJyZWFkY3J1bWIpIHtcbiAgICBpZiAocGF0aE9yQWxpYXMgPT09IG51bGwgfHwgcGF0aE9yQWxpYXMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc29sZS5lcnJvcignSW52YWxpZCBmaXJzdCBhcmd1bWVudC4gUGxlYXNlIHBhc3MgYSByb3V0ZSBwYXRoIG9yIGEgYnJlYWRjcnVtYiBhbGlhcy4nKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9IGVsc2UgaWYgKGJyZWFkY3J1bWIgPT09IG51bGwgfHwgYnJlYWRjcnVtYiA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdJbnZhbGlkIHNlY29uZCBhcmd1bWVudC4gUGxlYXNlIHBhc3MgYSBzdHJpbmcgb3IgYW4gT2JqZWN0IHdpdGggYnJlYWRjcnVtYiBvcHRpb25zLicpO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBicmVhZGNydW1iIGNhbiBiZSBwYXNzZWQgYSBsYWJlbCBvciBhbiBvcHRpb25zIG9iamVjdFxuICAgKiBJZiBwYXNzZWQgYXMgYSBzdHJpbmcgY29udmVydCB0byBicmVhZGNydW1iIG9wdGlvbnMgb2JqZWN0XG4gICAqL1xuICBwcml2YXRlIGdldEJyZWFkY3J1bWJPcHRpb25zKGRhdGEpIHtcbiAgICBsZXQgeyBicmVhZGNydW1iIH0gPSBkYXRhO1xuICAgIGlmICh0eXBlb2YgYnJlYWRjcnVtYiA9PT0gJ3N0cmluZycgfHwgIWJyZWFkY3J1bWIpIHtcbiAgICAgIGJyZWFkY3J1bWIgPSB7XG4gICAgICAgIGxhYmVsOiBicmVhZGNydW1iXG4gICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gYnJlYWRjcnVtYjtcbiAgfVxufVxuIl19