UNPKG

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
/** * @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