UNPKG

xng-breadcrumb

Version:

A declarative and reactive breadcrumb approach for Angular 6 and beyond https://www.npmjs.com/package/xng-breadcrumb

328 lines 46.4 kB
import { Injectable } from '@angular/core'; import { ActivatedRoute, GuardsCheckEnd, Router, } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; import { filter } from 'rxjs/operators'; import * as i0 from "@angular/core"; import * as i1 from "@angular/router"; const PATH_PARAM = { PREFIX: ':', REGEX_IDENTIFIER: '/:[^/]+', REGEX_REPLACER: '/[^/]+', }; const ALIAS_PREFIX = '@'; const isNonEmpty = (obj) => { return obj && Object.keys(obj).length > 0; }; export class BreadcrumbService { 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 = []; this.previousBreadcrumbs = []; /** * 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.detectRouteChanges(); } /** * Whenever route changes build breadcrumb list again */ detectRouteChanges() { // Special case where breadcrumb service & component instantiates after a route is navigated. // Ex: put breadcrumbs within *ngIf and this.router.events would be empty // This check is also required where { initialNavigation: 'enabledBlocking' } is applied to routes this.setupBreadcrumbs(this.activatedRoute.snapshot); this.router.events .pipe(filter((event) => event instanceof GuardsCheckEnd)) .subscribe((event) => { // activatedRoute doesn't carry data when shouldReuseRoute returns false // use the event data with GuardsCheckEnd as workaround // Check for shouldActivate in case where the authGuard returns false the breadcrumbs shouldn't be changed if (event.shouldActivate) { this.setupBreadcrumbs(event.state.root); } }); } setupBreadcrumbs(activatedRouteSnapshot) { this.previousBreadcrumbs = this.currentBreadcrumbs; // breadcrumb label for base OR root path. Usually, this can be set as 'Home' const rootBreadcrumb = this.getRootBreadcrumb(); this.currentBreadcrumbs = rootBreadcrumb ? [rootBreadcrumb] : []; this.prepareBreadcrumbList(activatedRouteSnapshot, this.baseHref); } getRootBreadcrumb() { const rootConfig = this.router.config.find((config) => config.path === ''); const rootBreadcrumb = this.extractObject(rootConfig?.data?.breadcrumb); const storeItem = this.getFromStore(rootBreadcrumb.alias, '/'); if (isNonEmpty(rootBreadcrumb) || isNonEmpty(storeItem)) { return { ...storeItem, ...rootBreadcrumb, routeLink: this.baseHref, ...this.getQueryParamsFromPreviousList('/'), }; } } prepareBreadcrumbItem(activatedRouteSnapshot, routeLinkPrefix) { const { path, breadcrumb } = this.parseRouteData(activatedRouteSnapshot.routeConfig); const resolvedSegment = this.resolvePathSegment(path, activatedRouteSnapshot); const routeLink = `${routeLinkPrefix}${resolvedSegment}`; const storeItem = this.getFromStore(breadcrumb.alias, routeLink); const label = this.extractLabel(storeItem?.label || breadcrumb?.label, resolvedSegment); let isAutoGeneratedLabel = false; let autoGeneratedLabel = ''; if (!label) { isAutoGeneratedLabel = true; autoGeneratedLabel = resolvedSegment; } return { ...storeItem, ...breadcrumb, label: isAutoGeneratedLabel ? autoGeneratedLabel : label, routeLink, isAutoGeneratedLabel, ...this.getQueryParamsFromPreviousList(routeLink), }; } prepareBreadcrumbList(activatedRouteSnapshot, routeLinkPrefix) { if (activatedRouteSnapshot.routeConfig?.path) { const breadcrumbItem = this.prepareBreadcrumbItem(activatedRouteSnapshot, routeLinkPrefix); this.currentBreadcrumbs.push(breadcrumbItem); if (activatedRouteSnapshot.firstChild) { return this.prepareBreadcrumbList(activatedRouteSnapshot.firstChild, breadcrumbItem.routeLink + '/'); } } else if (activatedRouteSnapshot.firstChild) { return this.prepareBreadcrumbList(activatedRouteSnapshot.firstChild, routeLinkPrefix); } const lastCrumb = this.currentBreadcrumbs[this.currentBreadcrumbs.length - 1]; this.setQueryParamsForActiveBreadcrumb(lastCrumb, activatedRouteSnapshot); // remove breadcrumb items that needs to be hidden const breadcrumbsToShow = this.currentBreadcrumbs.filter((item) => !item.skip); this.breadcrumbs.next(breadcrumbsToShow); } getFromStore(alias, routeLink) { return this.dynamicBreadcrumbStore.find((item) => { return ((alias && alias === item.alias) || (routeLink && routeLink === item.routeLink) || this.matchRegex(routeLink, item.routeRegex)); }); } /** * use exact match instead of regexp.test * for /mentor/[^/]+ we should match '/mentor/12' but not '/mentor/12/abc' */ matchRegex(routeLink, routeRegex) { const match = routeLink.match(new RegExp(routeRegex)); return match?.[0] === routeLink; } /** * if the path segment has route params, read the param value from url * for each segment of route this gets called * * for mentor/:id/view - it gets called with mentor, :id, view 3 times */ resolvePathSegment(segment, activatedRouteSnapshot) { //quirk -segment can be defined as view/:id in route config in which case you need to make it view/<resolved-param> if (segment.includes(PATH_PARAM.PREFIX)) { Object.entries(activatedRouteSnapshot.params).forEach(([key, value]) => { segment = segment.replace(`:${key}`, `${value}`); }); } return segment; } /** * queryParams & fragments for previous breadcrumb path are copied over to new list */ getQueryParamsFromPreviousList(routeLink) { const { queryParams, fragment } = this.previousBreadcrumbs.find((item) => item.routeLink === routeLink) || {}; return { queryParams, fragment }; } /** * set current activated route query params to the last breadcrumb item */ setQueryParamsForActiveBreadcrumb(lastItem, activatedRouteSnapshot) { if (lastItem) { const { queryParams, fragment } = activatedRouteSnapshot; lastItem.queryParams = queryParams ? { ...queryParams } : undefined; lastItem.fragment = fragment; } } /** * For a specific route, breadcrumb can be defined either on parent OR it's child(which has empty path) * 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: () => import('./home/home.module').then((m) => m.HomeModule) , data: {breadcrumb: "Defined On Module"}} * AND * children: [ * { path: '', component: ShowUserComponent, data: {breadcrumb: "Defined On Child" } * ] */ parseRouteData(routeConfig) { const { path, data } = routeConfig; const breadcrumb = this.mergeWithBaseChildData(routeConfig, data?.breadcrumb); return { path, breadcrumb }; } /** * 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 */ mergeWithBaseChildData(routeConfig, config) { if (!routeConfig) { return this.extractObject(config); } let baseChild; if (routeConfig.loadChildren) { // To handle a module with empty child route baseChild = routeConfig._loadedRoutes.find((route) => route.path === ''); } else if (routeConfig.children) { // To handle a component with empty child route baseChild = routeConfig.children.find((route) => route.path === ''); } const childConfig = baseChild?.data?.breadcrumb; return childConfig ? this.mergeWithBaseChildData(baseChild, { ...this.extractObject(config), ...this.extractObject(childConfig), }) : this.extractObject(config); } /** * Update breadcrumb dynamically * * key can be a path | alias * * 1) Using complete route path. route can be passed the same way you define angular routes * - path can be passed as 'exact path(routeLink)' or 'path with params(routeRegex)' * - update label Ex: set('/mentor', 'Mentor'), set('/mentor/:id', 'Mentor Details') * - change visibility Ex: set('/mentor/:id/edit', { skip: true }) * ------------------------------------------ OR ------------------------------------------ * 2) Using route alias (prefixed with '@'). alias should be unique for a route * - update label Ex: set('@mentor', 'Enabler') * - change visibility Ex: set('@mentorEdit', { skip: true }) * * * value can be string | BreadcrumbObject | BreadcrumbFunction */ set(key, breadcrumb) { const breadcrumbObject = this.extractObject(breadcrumb); let updateArgs; if (key.startsWith(ALIAS_PREFIX)) { updateArgs = ['alias', { ...breadcrumbObject, alias: key.slice(1) }]; } else if (key.includes(PATH_PARAM.PREFIX)) { updateArgs = [ 'routeRegex', { ...breadcrumbObject, routeRegex: this.buildRegex(key) }, ]; } else { updateArgs = [ 'routeLink', { ...breadcrumbObject, routeLink: this.ensureLeadingSlash(key) }, ]; } // For this route if previously a breadcrumb is not defined that sets isAutoGeneratedLabel: true // change it to false since this is user supplied value updateArgs[1].isAutoGeneratedLabel = false; this.updateStore(...updateArgs); this.updateCurrentBreadcrumbs(...updateArgs); } /** * Update the store to reuse for dynamic declarations * If the store already has this route definition update it, else add */ updateStore(key, breadcrumb) { const storeItemIndex = this.dynamicBreadcrumbStore.findIndex((item) => { return breadcrumb[key] === item[key]; }); if (storeItemIndex > -1) { this.dynamicBreadcrumbStore[storeItemIndex] = { ...this.dynamicBreadcrumbStore[storeItemIndex], ...breadcrumb, }; } else { this.dynamicBreadcrumbStore.push({ ...breadcrumb }); } } /** * If breadcrumb is present in current breadcrumbs update it and emit new stream */ updateCurrentBreadcrumbs(key, breadcrumb) { const itemIndex = this.currentBreadcrumbs.findIndex((item) => { return key === 'routeRegex' ? this.matchRegex(item.routeLink, breadcrumb[key]) : breadcrumb[key] === item[key]; }); if (itemIndex > -1) { this.currentBreadcrumbs[itemIndex] = { ...this.currentBreadcrumbs[itemIndex], ...breadcrumb, }; const breadcrumbsToShow = this.currentBreadcrumbs.filter((item) => !item.skip); this.breadcrumbs.next([...breadcrumbsToShow]); } } /** * For a route with path param, we create regex dynamically from angular route syntax * '/mentor/:id' becomes '/mentor/[^/]', * breadcrumbService.set('/mentor/:id', 'Uday') should update 'Uday' as label for '/mentor/2' OR 'mentor/ada' */ buildRegex(path) { return this.ensureLeadingSlash(path).replace(new RegExp(PATH_PARAM.REGEX_IDENTIFIER, 'g'), PATH_PARAM.REGEX_REPLACER); } ensureLeadingSlash(path) { return path.startsWith('/') ? path : `/${path}`; } /** * In App's RouteConfig, breadcrumb can be defined as a string OR a function OR an object * * string: simple static breadcrumb label for a path * function: callback that gets invoked with resolved path param * object: additional data defined along with breadcrumb label that gets passed to *xngBreadcrumbItem directive */ extractLabel(config, resolvedParam) { const label = typeof config === 'object' ? config.label : config; if (typeof label === 'function') { return label(resolvedParam); } return label; } extractObject(config) { // don't include {label} if config is undefined. This is important since we merge the configs if (config && (typeof config === 'string' || typeof config === 'function')) { return { label: config }; } return config || {}; } } BreadcrumbService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbService, deps: [{ token: i1.ActivatedRoute }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable }); BreadcrumbService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: function () { return [{ type: i1.ActivatedRoute }, { type: i1.Router }]; } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJlYWRjcnVtYi5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vbGlicy94bmctYnJlYWRjcnVtYi9zcmMvbGliL2JyZWFkY3J1bWIuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNDLE9BQU8sRUFDTCxjQUFjLEVBRWQsY0FBYyxFQUNkLE1BQU0sR0FDUCxNQUFNLGlCQUFpQixDQUFDO0FBQ3pCLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdkMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGdCQUFnQixDQUFDOzs7QUFVeEMsTUFBTSxVQUFVLEdBQUc7SUFDakIsTUFBTSxFQUFFLEdBQUc7SUFDWCxnQkFBZ0IsRUFBRSxTQUFTO0lBQzNCLGNBQWMsRUFBRSxRQUFRO0NBQ3pCLENBQUM7QUFDRixNQUFNLFlBQVksR0FBRyxHQUFHLENBQUM7QUFDekIsTUFBTSxVQUFVLEdBQUcsQ0FBQyxHQUFZLEVBQVcsRUFBRTtJQUMzQyxPQUFPLEdBQUcsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7QUFDNUMsQ0FBQyxDQUFDO0FBS0YsTUFBTSxPQUFPLGlCQUFpQjtJQXlCNUIsWUFBb0IsY0FBOEIsRUFBVSxNQUFjO1FBQXRELG1CQUFjLEdBQWQsY0FBYyxDQUFnQjtRQUFVLFdBQU0sR0FBTixNQUFNLENBQVE7UUF4QmxFLGFBQVEsR0FBRyxHQUFHLENBQUM7UUFFdkI7Ozs7V0FJRztRQUNLLDJCQUFzQixHQUEyQixFQUFFLENBQUM7UUFFNUQ7Ozs7V0FJRztRQUNLLHVCQUFrQixHQUEyQixFQUFFLENBQUM7UUFDaEQsd0JBQW1CLEdBQTJCLEVBQUUsQ0FBQztRQUV6RDs7O1dBR0c7UUFDSyxnQkFBVyxHQUFHLElBQUksZUFBZSxDQUF5QixF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Y0FBYyxDQUMxQixDQUFDO0lBQ0osQ0FBQztJQUVPLGtCQUFrQixDQUFDLElBQVk7UUFDckMsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxFQUFFLENBQUM7SUFDbEQsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNLLFlBQVksQ0FBQyxNQUF3QixFQUFFLGFBQXNCO1FBQ25FLE1BQU0sS0FBSyxHQUFHLE9BQU8sTUFBTSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO1FBQ2pFLElBQUksT0FBTyxLQUFLLEtBQUssVUFBVSxFQUFFO1lBQy9CLE9BQU8sS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1NBQzdCO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRU8sYUFBYSxDQUFDLE1BQXdCO1FBQzVDLDZGQUE2RjtRQUM3RixJQUNFLE1BQU07WUFDTixDQUFDLE9BQU8sTUFBTSxLQUFLLFFBQVEsSUFBSSxPQUFPLE1BQU0sS0FBSyxVQUFVLENBQUMsRUFDNUQ7WUFDQSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDO1NBQzFCO1FBQ0QsT0FBUSxNQUEyQixJQUFJLEVBQUUsQ0FBQztJQUM1QyxDQUFDOzs4R0FsWVUsaUJBQWlCO2tIQUFqQixpQkFBaUIsY0FGaEIsTUFBTTsyRkFFUCxpQkFBaUI7a0JBSDdCLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgQWN0aXZhdGVkUm91dGUsXG4gIEFjdGl2YXRlZFJvdXRlU25hcHNob3QsXG4gIEd1YXJkc0NoZWNrRW5kLFxuICBSb3V0ZXIsXG59IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGZpbHRlciB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IEJyZWFkY3J1bWIgfSBmcm9tICcuL3R5cGVzL2JyZWFkY3J1bWInO1xuaW1wb3J0IHtcbiAgQnJlYWRjcnVtYkZ1bmN0aW9uLFxuICBCcmVhZGNydW1iT2JqZWN0LFxufSBmcm9tICcuL3R5cGVzL2JyZWFkY3J1bWIuY29uZmlnJztcblxudHlwZSBCcmVhZGNydW1iQ29uZmlnID0gQnJlYWRjcnVtYk9iamVjdCB8IEJyZWFkY3J1bWJGdW5jdGlvbiB8IHN0cmluZztcbnR5cGUgU3RvcmVNYXRjaGVyS2V5ID0gJ3JvdXRlTGluaycgfCAncm91dGVSZWdleCcgfCAnYWxpYXMnO1xuZXhwb3J0IHR5cGUgQnJlYWRjcnVtYkRlZmluaXRpb24gPSBCcmVhZGNydW1iICYgQnJlYWRjcnVtYk9iamVjdDtcbmNvbnN0IFBBVEhfUEFSQU0gPSB7XG4gIFBSRUZJWDogJzonLFxuICBSRUdFWF9JREVOVElGSUVSOiAnLzpbXi9dKycsXG4gIFJFR0VYX1JFUExBQ0VSOiAnL1teL10rJyxcbn07XG5jb25zdCBBTElBU19QUkVGSVggPSAnQCc7XG5jb25zdCBpc05vbkVtcHR5ID0gKG9iajogdW5rbm93bik6IGJvb2xlYW4gPT4ge1xuICByZXR1cm4gb2JqICYmIE9iamVjdC5rZXlzKG9iaikubGVuZ3RoID4gMDtcbn07XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnLFxufSlcbmV4cG9ydCBjbGFzcyBCcmVhZGNydW1iU2VydmljZSB7XG4gIHByaXZhdGUgYmFzZUhyZWYgPSAnLyc7XG5cbiAgLyoqXG4gICAqIGR5bmFtaWNCcmVhZGNydW1iU3RvcmUgaG9sZHMgaW5mb3JtYXRpb24gYWJvdXQgZHluYW1pY2FsbHkgdXBkYXRlZCBicmVhZGNydW1icy5cbiAgICogQnJlYWRjcnVtYnMgY2FuIGJlIHNldCBmcm9tIGFueXdoZXJlIChjb21wb25lbnQsIHNlcnZpY2UpIGluIHRoZSBhcHAuXG4gICAqIE9uIGV2ZXJ5IGJyZWFkY3J1bWIgdXBkYXRlIGNoZWNrIHRoaXMgc3RvcmUgYW5kIHVzZSB0aGUgaW5mbyBpZiBhdmFpbGFibGUuXG4gICAqL1xuICBwcml2YXRlIGR5bmFtaWNCcmVhZGNydW1iU3RvcmU6IEJyZWFkY3J1bWJEZWZpbml0aW9uW10gPSBbXTtcblxuICAvKipcbiAgICogYnJlYWRjcnVtYkxpc3QgZm9yIHRoZSBjdXJyZW50IHJvdXRlXG4gICAqIFdoZW4gYnJlYWRjcnVtYiBpbmZvIGlzIGNoYW5nZWQgZHluYW1pY2FsbHksIGNoZWNrIGlmIHRoZSBjdXJyZW50QnJlYWRjcnVtYnMgaXMgZWZmZWN0ZWRcbiAgICogSWYgZWZmZWN0ZWQsIHVwZGF0ZSB0aGUgY2hhbmdlIGFuZCBlbWl0IGEgbmV3IHN0cmVhbVxuICAgKi9cbiAgcHJpdmF0ZSBjdXJyZW50QnJlYWRjcnVtYnM6IEJyZWFkY3J1bWJEZWZpbml0aW9uW10gPSBbXTtcbiAgcHJpdmF0ZSBwcmV2aW91c0JyZWFkY3J1bWJzOiBCcmVhZGNydW1iRGVmaW5pdGlvbltdID0gW107XG5cbiAgLyoqXG4gICAqIEJyZWFkY3J1bWJzIG9ic2VydmFibGUgdG8gYmUgc3Vic2NyaWJlZCBieSBCcmVhZGNydW1iQ29tcG9uZW50XG4gICAqIEVtaXRzIG9uIGV2ZXJ5IHJvdXRlIGNoYW5nZSBPUiBkeW5hbWljIHVwZGF0ZSBvZiBicmVhZGNydW1iXG4gICAqL1xuICBwcml2YXRlIGJyZWFkY3J1bWJzID0gbmV3IEJlaGF2aW9yU3ViamVjdDxCcmVhZGNydW1iRGVmaW5pdGlvbltdPihbXSk7XG4gIHB1YmxpYyBicmVhZGNydW1icyQgPSB0aGlzLmJyZWFkY3J1bWJzLmFzT2JzZXJ2YWJsZSgpO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgYWN0aXZhdGVkUm91dGU6IEFjdGl2YXRlZFJvdXRlLCBwcml2YXRlIHJvdXRlcjogUm91dGVyKSB7XG4gICAgdGhpcy5kZXRlY3RSb3V0ZUNoYW5nZXMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGVuZXZlciByb3V0ZSBjaGFuZ2VzIGJ1aWxkIGJyZWFkY3J1bWIgbGlzdCBhZ2FpblxuICAgKi9cbiAgcHJpdmF0ZSBkZXRlY3RSb3V0ZUNoYW5nZXMoKSB7XG4gICAgLy8gU3BlY2lhbCBjYXNlIHdoZXJlIGJyZWFkY3J1bWIgc2VydmljZSAmIGNvbXBvbmVudCBpbnN0YW50aWF0ZXMgYWZ0ZXIgYSByb3V0ZSBpcyBuYXZpZ2F0ZWQuXG4gICAgLy8gRXg6IHB1dCBicmVhZGNydW1icyB3aXRoaW4gKm5nSWYgYW5kIHRoaXMucm91dGVyLmV2ZW50cyB3b3VsZCBiZSBlbXB0eVxuICAgIC8vIFRoaXMgY2hlY2sgaXMgYWxzbyByZXF1aXJlZCB3aGVyZSAgeyBpbml0aWFsTmF2aWdhdGlvbjogJ2VuYWJsZWRCbG9ja2luZycgfSBpcyBhcHBsaWVkIHRvIHJvdXRlc1xuICAgIHRoaXMuc2V0dXBCcmVhZGNydW1icyh0aGlzLmFjdGl2YXRlZFJvdXRlLnNuYXBzaG90KTtcblxuICAgIHRoaXMucm91dGVyLmV2ZW50c1xuICAgICAgLnBpcGUoXG4gICAgICAgIGZpbHRlcihcbiAgICAgICAgICAoZXZlbnQpOiBldmVudCBpcyBHdWFyZHNDaGVja0VuZCA9PiBldmVudCBpbnN0YW5jZW9mIEd1YXJkc0NoZWNrRW5kXG4gICAgICAgIClcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKGV2ZW50KSA9PiB7XG4gICAgICAgIC8vIGFjdGl2YXRlZFJvdXRlIGRvZXNuJ3QgY2FycnkgZGF0YSB3aGVuIHNob3VsZFJldXNlUm91dGUgcmV0dXJucyBmYWxzZVxuICAgICAgICAvLyB1c2UgdGhlIGV2ZW50IGRhdGEgd2l0aCBHdWFyZHNDaGVja0VuZCBhcyB3b3JrYXJvdW5kXG4gICAgICAgIC8vIENoZWNrIGZvciBzaG91bGRBY3RpdmF0ZSBpbiBjYXNlIHdoZXJlIHRoZSBhdXRoR3VhcmQgcmV0dXJucyBmYWxzZSB0aGUgYnJlYWRjcnVtYnMgc2hvdWxkbid0IGJlIGNoYW5nZWRcbiAgICAgICAgaWYgKGV2ZW50LnNob3VsZEFjdGl2YXRlKSB7XG4gICAgICAgICAgdGhpcy5zZXR1cEJyZWFkY3J1bWJzKGV2ZW50LnN0YXRlLnJvb3QpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0dXBCcmVhZGNydW1icyhhY3RpdmF0ZWRSb3V0ZVNuYXBzaG90OiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90KSB7XG4gICAgdGhpcy5wcmV2aW91c0JyZWFkY3J1bWJzID0gdGhpcy5jdXJyZW50QnJlYWRjcnVtYnM7XG4gICAgLy8gYnJlYWRjcnVtYiBsYWJlbCBmb3IgYmFzZSBPUiByb290IHBhdGguIFVzdWFsbHksIHRoaXMgY2FuIGJlIHNldCBhcyAnSG9tZSdcbiAgICBjb25zdCByb290QnJlYWRjcnVtYiA9IHRoaXMuZ2V0Um9vdEJyZWFkY3J1bWIoKTtcbiAgICB0aGlzLmN1cnJlbnRCcmVhZGNydW1icyA9IHJvb3RCcmVhZGNydW1iID8gW3Jvb3RCcmVhZGNydW1iXSA6IFtdO1xuICAgIHRoaXMucHJlcGFyZUJyZWFkY3J1bWJMaXN0KGFjdGl2YXRlZFJvdXRlU25hcHNob3QsIHRoaXMuYmFzZUhyZWYpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRSb290QnJlYWRjcnVtYigpOiBCcmVhZGNydW1iIHwgdm9pZCB7XG4gICAgY29uc3Qgcm9vdENvbmZpZyA9IHRoaXMucm91dGVyLmNvbmZpZy5maW5kKChjb25maWcpID0+IGNvbmZpZy5wYXRoID09PSAnJyk7XG4gICAgY29uc3Qgcm9vdEJyZWFkY3J1bWIgPSB0aGlzLmV4dHJhY3RPYmplY3Qocm9vdENvbmZpZz8uZGF0YT8uYnJlYWRjcnVtYik7XG4gICAgY29uc3Qgc3RvcmVJdGVtID0gdGhpcy5nZXRGcm9tU3RvcmUocm9vdEJyZWFkY3J1bWIuYWxpYXMsICcvJyk7XG5cbiAgICBpZiAoaXNOb25FbXB0eShyb290QnJlYWRjcnVtYikgfHwgaXNOb25FbXB0eShzdG9yZUl0ZW0pKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICAuLi5zdG9yZUl0ZW0sXG4gICAgICAgIC4uLnJvb3RCcmVhZGNydW1iLFxuICAgICAgICByb3V0ZUxpbms6IHRoaXMuYmFzZUhyZWYsXG4gICAgICAgIC4uLnRoaXMuZ2V0UXVlcnlQYXJhbXNGcm9tUHJldmlvdXNMaXN0KCcvJyksXG4gICAgICB9O1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgcHJlcGFyZUJyZWFkY3J1bWJJdGVtKFxuICAgIGFjdGl2YXRlZFJvdXRlU25hcHNob3Q6IEFjdGl2YXRlZFJvdXRlU25hcHNob3QsXG4gICAgcm91dGVMaW5rUHJlZml4OiBzdHJpbmdcbiAgKTogQnJlYWRjcnVtYkRlZmluaXRpb24ge1xuICAgIGNvbnN0IHsgcGF0aCwgYnJlYWRjcnVtYiB9ID0gdGhpcy5wYXJzZVJvdXRlRGF0YShcbiAgICAgIGFjdGl2YXRlZFJvdXRlU25hcHNob3Qucm91dGVDb25maWdcbiAgICApO1xuICAgIGNvbnN0IHJlc29sdmVkU2VnbWVudCA9IHRoaXMucmVzb2x2ZVBhdGhTZWdtZW50KFxuICAgICAgcGF0aCxcbiAgICAgIGFjdGl2YXRlZFJvdXRlU25hcHNob3RcbiAgICApO1xuICAgIGNvbnN0IHJvdXRlTGluayA9IGAke3JvdXRlTGlua1ByZWZpeH0ke3Jlc29sdmVkU2VnbWVudH1gO1xuICAgIGNvbnN0IHN0b3JlSXRlbSA9IHRoaXMuZ2V0RnJvbVN0b3JlKGJyZWFkY3J1bWIuYWxpYXMsIHJvdXRlTGluayk7XG5cbiAgICBjb25zdCBsYWJlbCA9IHRoaXMuZXh0cmFjdExhYmVsKFxuICAgICAgc3RvcmVJdGVtPy5sYWJlbCB8fCBicmVhZGNydW1iPy5sYWJlbCxcbiAgICAgIHJlc29sdmVkU2VnbWVudFxuICAgICk7XG4gICAgbGV0IGlzQXV0b0dlbmVyYXRlZExhYmVsID0gZmFsc2U7XG4gICAgbGV0IGF1dG9HZW5lcmF0ZWRMYWJlbCA9ICcnO1xuICAgIGlmICghbGFiZWwpIHtcbiAgICAgIGlzQXV0b0dlbmVyYXRlZExhYmVsID0gdHJ1ZTtcbiAgICAgIGF1dG9HZW5lcmF0ZWRMYWJlbCA9IHJlc29sdmVkU2VnbWVudDtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgLi4uc3RvcmVJdGVtLFxuICAgICAgLi4uYnJlYWRjcnVtYixcbiAgICAgIGxhYmVsOiBpc0F1dG9HZW5lcmF0ZWRMYWJlbCA/IGF1dG9HZW5lcmF0ZWRMYWJlbCA6IGxhYmVsLFxuICAgICAgcm91dGVMaW5rLFxuICAgICAgaXNBdXRvR2VuZXJhdGVkTGFiZWwsXG4gICAgICAuLi50aGlzLmdldFF1ZXJ5UGFyYW1zRnJvbVByZXZpb3VzTGlzdChyb3V0ZUxpbmspLFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIHByZXBhcmVCcmVhZGNydW1iTGlzdChcbiAgICBhY3RpdmF0ZWRSb3V0ZVNuYXBzaG90OiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90LFxuICAgIHJvdXRlTGlua1ByZWZpeDogc3RyaW5nXG4gICk6IEJyZWFkY3J1bWJbXSB8IHZvaWQge1xuICAgIGlmIChhY3RpdmF0ZWRSb3V0ZVNuYXBzaG90LnJvdXRlQ29uZmlnPy5wYXRoKSB7XG4gICAgICBjb25zdCBicmVhZGNydW1iSXRlbSA9IHRoaXMucHJlcGFyZUJyZWFkY3J1bWJJdGVtKFxuICAgICAgICBhY3RpdmF0ZWRSb3V0ZVNuYXBzaG90LFxuICAgICAgICByb3V0ZUxpbmtQcmVmaXhcbiAgICAgICk7XG4gICAgICB0aGlzLmN1cnJlbnRCcmVhZGNydW1icy5wdXNoKGJyZWFkY3J1bWJJdGVtKTtcblxuICAgICAgaWYgKGFjdGl2YXRlZFJvdXRlU25hcHNob3QuZmlyc3RDaGlsZCkge1xuICAgICAgICByZXR1cm4gdGhpcy5wcmVwYXJlQnJlYWRjcnVtYkxpc3QoXG4gICAgICAgICAgYWN0aXZhdGVkUm91dGVTbmFwc2hvdC5maXJzdENoaWxkLFxuICAgICAgICAgIGJyZWFkY3J1bWJJdGVtLnJvdXRlTGluayArICcvJ1xuICAgICAgICApO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoYWN0aXZhdGVkUm91dGVTbmFwc2hvdC5maXJzdENoaWxkKSB7XG4gICAgICByZXR1cm4gdGhpcy5wcmVwYXJlQnJlYWRjcnVtYkxpc3QoXG4gICAgICAgIGFjdGl2YXRlZFJvdXRlU25hcHNob3QuZmlyc3RDaGlsZCxcbiAgICAgICAgcm91dGVMaW5rUHJlZml4XG4gICAgICApO1xuICAgIH1cbiAgICBjb25zdCBsYXN0Q3J1bWIgPVxuICAgICAgdGhpcy5jdXJyZW50QnJlYWRjcnVtYnNbdGhpcy5jdXJyZW50QnJlYWRjcnVtYnMubGVuZ3RoIC0gMV07XG4gICAgdGhpcy5zZXRRdWVyeVBhcmFtc0ZvckFjdGl2ZUJyZWFkY3J1bWIobGFzdENydW1iLCBhY3RpdmF0ZWRSb3V0ZVNuYXBzaG90KTtcblxuICAgIC8vIHJlbW92ZSBicmVhZGNydW1iIGl0ZW1zIHRoYXQgbmVlZHMgdG8gYmUgaGlkZGVuXG4gICAgY29uc3QgYnJlYWRjcnVtYnNUb1Nob3cgPSB0aGlzLmN1cnJlbnRCcmVhZGNydW1icy5maWx0ZXIoXG4gICAgICAoaXRlbSkgPT4gIWl0ZW0uc2tpcFxuICAgICk7XG5cbiAgICB0aGlzLmJyZWFkY3J1bWJzLm5leHQoYnJlYWRjcnVtYnNUb1Nob3cpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRGcm9tU3RvcmUoYWxpYXM6IHN0cmluZywgcm91dGVMaW5rOiBzdHJpbmcpOiBCcmVhZGNydW1iRGVmaW5pdGlvbiB7XG4gICAgcmV0dXJuIHRoaXMuZHluYW1pY0JyZWFkY3J1bWJTdG9yZS5maW5kKChpdGVtKSA9PiB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICAoYWxpYXMgJiYgYWxpYXMgPT09IGl0ZW0uYWxpYXMpIHx8XG4gICAgICAgIChyb3V0ZUxpbmsgJiYgcm91dGVMaW5rID09PSBpdGVtLnJvdXRlTGluaykgfHxcbiAgICAgICAgdGhpcy5tYXRjaFJlZ2V4KHJvdXRlTGluaywgaXRlbS5yb3V0ZVJlZ2V4KVxuICAgICAgKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiB1c2UgZXhhY3QgbWF0Y2ggaW5zdGVhZCBvZiByZWdleHAudGVzdFxuICAgKiBmb3IgL21lbnRvci9bXi9dKyB3ZSBzaG91bGQgbWF0Y2ggJy9tZW50b3IvMTInIGJ1dCBub3QgJy9tZW50b3IvMTIvYWJjJ1xuICAgKi9cbiAgcHJpdmF0ZSBtYXRjaFJlZ2V4KHJvdXRlTGluazogc3RyaW5nLCByb3V0ZVJlZ2V4OiBzdHJpbmcpIHtcbiAgICBjb25zdCBtYXRjaCA9IHJvdXRlTGluay5tYXRjaChuZXcgUmVnRXhwKHJvdXRlUmVnZXgpKTtcbiAgICByZXR1cm4gbWF0Y2g/LlswXSA9PT0gcm91dGVMaW5rO1xuICB9XG5cbiAgLyoqXG4gICAqIGlmIHRoZSBwYXRoIHNlZ21lbnQgaGFzIHJvdXRlIHBhcmFtcywgcmVhZCB0aGUgcGFyYW0gdmFsdWUgZnJvbSB1cmxcbiAgICogZm9yIGVhY2ggc2VnbWVudCBvZiByb3V0ZSB0aGlzIGdldHMgY2FsbGVkXG4gICAqXG4gICAqIGZvciBtZW50b3IvOmlkL3ZpZXcgLSBpdCBnZXRzIGNhbGxlZCB3aXRoIG1lbnRvciwgOmlkLCB2aWV3IDMgdGltZXNcbiAgICovXG4gIHByaXZhdGUgcmVzb2x2ZVBhdGhTZWdtZW50KFxuICAgIHNlZ21lbnQ6IHN0cmluZyxcbiAgICBhY3RpdmF0ZWRSb3V0ZVNuYXBzaG90OiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90XG4gICkge1xuICAgIC8vcXVpcmsgLXNlZ21lbnQgY2FuIGJlIGRlZmluZWQgYXMgdmlldy86aWQgaW4gcm91dGUgY29uZmlnIGluIHdoaWNoIGNhc2UgeW91IG5lZWQgdG8gbWFrZSBpdCB2aWV3LzxyZXNvbHZlZC1wYXJhbT5cbiAgICBpZiAoc2VnbWVudC5pbmNsdWRlcyhQQVRIX1BBUkFNLlBSRUZJWCkpIHtcbiAgICAgIE9iamVjdC5lbnRyaWVzKGFjdGl2YXRlZFJvdXRlU25hcHNob3QucGFyYW1zKS5mb3JFYWNoKChba2V5LCB2YWx1ZV0pID0+IHtcbiAgICAgICAgc2VnbWVudCA9IHNlZ21lbnQucmVwbGFjZShgOiR7a2V5fWAsIGAke3ZhbHVlfWApO1xuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBzZWdtZW50O1xuICB9XG5cbiAgLyoqXG4gICAqIHF1ZXJ5UGFyYW1zICYgZnJhZ21lbnRzIGZvciBwcmV2aW91cyBicmVhZGNydW1iIHBhdGggYXJlIGNvcGllZCBvdmVyIHRvIG5ldyBsaXN0XG4gICAqL1xuICBwcml2YXRlIGdldFF1ZXJ5UGFyYW1zRnJvbVByZXZpb3VzTGlzdChyb3V0ZUxpbms6IHN0cmluZyk6IEJyZWFkY3J1bWIge1xuICAgIGNvbnN0IHsgcXVlcnlQYXJhbXMsIGZyYWdtZW50IH0gPVxuICAgICAgdGhpcy5wcmV2aW91c0JyZWFkY3J1bWJzLmZpbmQoKGl0ZW0pID0+IGl0ZW0ucm91dGVMaW5rID09PSByb3V0ZUxpbmspIHx8XG4gICAgICB7fTtcbiAgICByZXR1cm4geyBxdWVyeVBhcmFtcywgZnJhZ21lbnQgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBzZXQgY3VycmVudCBhY3RpdmF0ZWQgcm91dGUgcXVlcnkgcGFyYW1zIHRvIHRoZSBsYXN0IGJyZWFkY3J1bWIgaXRlbVxuICAgKi9cbiAgcHJpdmF0ZSBzZXRRdWVyeVBhcmFtc0ZvckFjdGl2ZUJyZWFkY3J1bWIoXG4gICAgbGFzdEl0ZW06IEJyZWFkY3J1bWIsXG4gICAgYWN0aXZhdGVkUm91dGVTbmFwc2hvdDogQWN0aXZhdGVkUm91dGVTbmFwc2hvdFxuICApIHtcbiAgICBpZiAobGFzdEl0ZW0pIHtcbiAgICAgIGNvbnN0IHsgcXVlcnlQYXJhbXMsIGZyYWdtZW50IH0gPSBhY3RpdmF0ZWRSb3V0ZVNuYXBzaG90O1xuICAgICAgbGFzdEl0ZW0ucXVlcnlQYXJhbXMgPSBxdWVyeVBhcmFtcyA/IHsgLi4ucXVlcnlQYXJhbXMgfSA6IHVuZGVmaW5lZDtcbiAgICAgIGxhc3RJdGVtLmZyYWdtZW50ID0gZnJhZ21lbnQ7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZvciBhIHNwZWNpZmljIHJvdXRlLCBicmVhZGNydW1iIGNhbiBiZSBkZWZpbmVkIGVpdGhlciBvbiBwYXJlbnQgT1IgaXQncyBjaGlsZCh3aGljaCBoYXMgZW1wdHkgcGF0aClcbiAgICogV2hlbiBib3RoIGFyZSBkZWZpbmVkLCBjaGlsZCB0YWtlcyBwcmVjZWRlbmNlXG4gICAqXG4gICAqIEV4OiBCZWxvdyB3ZSBhcmUgc2V0dGluZyBicmVhZGNydW1iIG9uIGJvdGggcGFyZW50IGFuZCBjaGlsZC5cbiAgICogU28sIGNoaWxkIHRha2VzIHByZWNlZGVuY2UgYW5kIFwiRGVmaW5lZCBPbiBDaGlsZFwiIGlzIGRpc3BsYXllZCBmb3IgdGhlIHJvdXRlICdob21lJ1xuICAgKiB7IHBhdGg6ICdob21lJywgbG9hZENoaWxkcmVuOiAoKSA9PiBpbXBvcnQoJy4vaG9tZS9ob21lLm1vZHVsZScpLnRoZW4oKG0pID0+IG0uSG9tZU1vZHVsZSkgLCBkYXRhOiB7YnJlYWRjcnVtYjogXCJEZWZpbmVkIE9uIE1vZHVsZVwifX1cbiAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBTkRcbiAgICogY2hpbGRyZW46IFtcbiAgICogICB7IHBhdGg6ICcnLCBjb21wb25lbnQ6IFNob3dVc2VyQ29tcG9uZW50LCBkYXRhOiB7YnJlYWRjcnVtYjogXCJEZWZpbmVkIE9uIENoaWxkXCIgfVxuICAgKiBdXG4gICAqL1xuICBwcml2YXRlIHBhcnNlUm91dGVEYXRhKHJvdXRlQ29uZmlnKSB7XG4gICAgY29uc3QgeyBwYXRoLCBkYXRhIH0gPSByb3V0ZUNvbmZpZztcbiAgICBjb25zdCBicmVhZGNydW1iID0gdGhpcy5tZXJnZVdpdGhCYXNlQ2hpbGREYXRhKFxuICAgICAgcm91dGVDb25maWcsXG4gICAgICBkYXRhPy5icmVhZGNydW1iXG4gICAgKTtcblxuICAgIHJldHVybiB7IHBhdGgsIGJyZWFkY3J1bWIgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXQgZW1wdHkgY2hpbGRyZW4gb2YgYSBtb2R1bGUgb3IgQ29tcG9uZW50LiBFbXB0eSBjaGlsZCBpcyB0aGUgb25lIHdpdGggcGF0aDogJydcbiAgICogV2hlbiBwYXJlbnQgYW5kIGl0J3MgY2hpbGRyZW4gKHRoYXQgaGFzIGVtcHR5IHJvdXRlIHBhdGgpIGRlZmluZSBkYXRhIG1lcmdlIHRoZW0gYm90aCB3aXRoIGNoaWxkIHRha2luZyBwcmVjZWRlbmNlXG4gICAqL1xuICBwcml2YXRlIG1lcmdlV2l0aEJhc2VDaGlsZERhdGEoXG4gICAgcm91dGVDb25maWcsXG4gICAgY29uZmlnOiBCcmVhZGNydW1iQ29uZmlnXG4gICk6IEJyZWFkY3J1bWJPYmplY3Qge1xuICAgIGlmICghcm91dGVDb25maWcpIHtcbiAgICAgIHJldHVybiB0aGlzLmV4dHJhY3RPYmplY3QoY29uZmlnKTtcbiAgICB9XG5cbiAgICBsZXQgYmFzZUNoaWxkO1xuICAgIGlmIChyb3V0ZUNvbmZpZy5sb2FkQ2hpbGRyZW4pIHtcbiAgICAgIC8vIFRvIGhhbmRsZSBhIG1vZHVsZSB3aXRoIGVtcHR5IGNoaWxkIHJvdXRlXG4gICAgICBiYXNlQ2hpbGQgPSByb3V0ZUNvbmZpZy5fbG9hZGVkUm91dGVzLmZpbmQoKHJvdXRlKSA9PiByb3V0ZS5wYXRoID09PSAnJyk7XG4gICAgfSBlbHNlIGlmIChyb3V0ZUNvbmZpZy5jaGlsZHJlbikge1xuICAgICAgLy8gVG8gaGFuZGxlIGEgY29tcG9uZW50IHdpdGggZW1wdHkgY2hpbGQgcm91dGVcbiAgICAgIGJhc2VDaGlsZCA9IHJvdXRlQ29uZmlnLmNoaWxkcmVuLmZpbmQoKHJvdXRlKSA9PiByb3V0ZS5wYXRoID09PSAnJyk7XG4gICAgfVxuXG4gICAgY29uc3QgY2hpbGRDb25maWcgPSBiYXNlQ2hpbGQ/LmRhdGE/LmJyZWFkY3J1bWI7XG4gICAgcmV0dXJuIGNoaWxkQ29uZmlnXG4gICAgICA/IHRoaXMubWVyZ2VXaXRoQmFzZUNoaWxkRGF0YShiYXNlQ2hpbGQsIHtcbiAgICAgICAgICAuLi50aGlzLmV4dHJhY3RPYmplY3QoY29uZmlnKSxcbiAgICAgICAgICAuLi50aGlzLmV4dHJhY3RPYmplY3QoY2hpbGRDb25maWcpLFxuICAgICAgICB9KVxuICAgICAgOiB0aGlzLmV4dHJhY3RPYmplY3QoY29uZmlnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgYnJlYWRjcnVtYiBkeW5hbWljYWxseVxuICAgKlxuICAgKiBrZXkgY2FuIGJlIGEgcGF0aCB8IGFsaWFzXG4gICAqXG4gICAqIDEpIFVzaW5nIGNvbXBsZXRlIHJvdXRlIHBhdGguIHJvdXRlIGNhbiBiZSBwYXNzZWQgdGhlIHNhbWUgd2F5IHlvdSBkZWZpbmUgYW5ndWxhciByb3V0ZXNcbiAgICogLSBwYXRoIGNhbiBiZSBwYXNzZWQgYXMgJ2V4YWN0IHBhdGgocm91dGVMaW5rKScgb3IgJ3BhdGggd2l0aCBwYXJhbXMocm91dGVSZWdleCknXG4gICAqIC0gdXBkYXRlIGxhYmVsIEV4OiBzZXQoJy9tZW50b3InLCAnTWVudG9yJyksIHNldCgnL21lbnRvci86aWQnLCAnTWVudG9yIERldGFpbHMnKVxuICAgKiAtIGNoYW5nZSB2aXNpYmlsaXR5IEV4OiBzZXQoJy9tZW50b3IvOmlkL2VkaXQnLCB7IHNraXA6IHRydWUgfSlcbiAgICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIE9SIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgKiAyKSBVc2luZyByb3V0ZSBhbGlhcyAocHJlZml4ZWQgd2l0aCAnQCcpLiBhbGlhcyBzaG91bGQgYmUgdW5pcXVlIGZvciBhIHJvdXRlXG4gICAqIC0gdXBkYXRlIGxhYmVsIEV4OiBzZXQoJ0BtZW50b3InLCAnRW5hYmxlcicpXG4gICAqIC0gY2hhbmdlIHZpc2liaWxpdHkgRXg6IHNldCgnQG1lbnRvckVkaXQnLCB7IHNraXA6IHRydWUgfSlcbiAgICpcbiAgICpcbiAgICogdmFsdWUgY2FuIGJlIHN0cmluZyB8IEJyZWFkY3J1bWJPYmplY3QgfCBCcmVhZGNydW1iRnVuY3Rpb25cbiAgICovXG4gIHNldChrZXk6IHN0cmluZywgYnJlYWRjcnVtYjogc3RyaW5nIHwgQnJlYWRjcnVtYk9iamVjdCkge1xuICAgIGNvbnN0IGJyZWFkY3J1bWJPYmplY3QgPSB0aGlzLmV4dHJhY3RPYmplY3QoYnJlYWRjcnVtYik7XG4gICAgbGV0IHVwZGF0ZUFyZ3M6IFtTdG9yZU1hdGNoZXJLZXksIEJyZWFkY3J1bWJEZWZpbml0aW9uXTtcblxuICAgIGlmIChrZXkuc3RhcnRzV2l0aChBTElBU19QUkVGSVgpKSB7XG4gICAgICB1cGRhdGVBcmdzID0gWydhbGlhcycsIHsgLi4uYnJlYWRjcnVtYk9iamVjdCwgYWxpYXM6IGtleS5zbGljZSgxKSB9XTtcbiAgICB9IGVsc2UgaWYgKGtleS5pbmNsdWRlcyhQQVRIX1BBUkFNLlBSRUZJWCkpIHtcbiAgICAgIHVwZGF0ZUFyZ3MgPSBbXG4gICAgICAgICdyb3V0ZVJlZ2V4JyxcbiAgICAgICAgeyAuLi5icmVhZGNydW1iT2JqZWN0LCByb3V0ZVJlZ2V4OiB0aGlzLmJ1aWxkUmVnZXgoa2V5KSB9LFxuICAgICAgXTtcbiAgICB9IGVsc2Uge1xuICAgICAgdXBkYXRlQXJncyA9IFtcbiAgICAgICAgJ3JvdXRlTGluaycsXG4gICAgICAgIHsgLi4uYnJlYWRjcnVtYk9iamVjdCwgcm91dGVMaW5rOiB0aGlzLmVuc3VyZUxlYWRpbmdTbGFzaChrZXkpIH0sXG4gICAgICBdO1xuICAgIH1cblxuICAgIC8vIEZvciB0aGlzIHJvdXRlIGlmIHByZXZpb3VzbHkgYSBicmVhZGNydW1iIGlzIG5vdCBkZWZpbmVkIHRoYXQgc2V0cyBpc0F1dG9HZW5lcmF0ZWRMYWJlbDogdHJ1ZVxuICAgIC8vIGNoYW5nZSBpdCB0byBmYWxzZSBzaW5jZSB0aGlzIGlzIHVzZXIgc3VwcGxpZWQgdmFsdWVcbiAgICB1cGRhdGVBcmdzWzFdLmlzQXV0b0dlbmVyYXRlZExhYmVsID0gZmFsc2U7XG5cbiAgICB0aGlzLnVwZGF0ZVN0b3JlKC4uLnVwZGF0ZUFyZ3MpO1xuICAgIHRoaXMudXBkYXRlQ3VycmVudEJyZWFkY3J1bWJzKC4uLnVwZGF0ZUFyZ3MpO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSB0aGUgc3RvcmUgdG8gcmV1c2UgZm9yIGR5bmFtaWMgZGVjbGFyYXRpb25zXG4gICAqIElmIHRoZSBzdG9yZSBhbHJlYWR5IGhhcyB0aGlzIHJvdXRlIGRlZmluaXRpb24gdXBkYXRlIGl0LCBlbHNlIGFkZFxuICAgKi9cbiAgcHJpdmF0ZSB1cGRhdGVTdG9yZShrZXk6IHN0cmluZywgYnJlYWRjcnVtYjogQnJlYWRjcnVtYkRlZmluaXRpb24pIHtcbiAgICBjb25zdCBzdG9yZUl0ZW1JbmRleCA9IHRoaXMuZHluYW1pY0JyZWFkY3J1bWJTdG9yZS5maW5kSW5kZXgoKGl0ZW0pID0+IHtcbiAgICAgIHJldHVybiBicmVhZGNydW1iW2tleV0gPT09IGl0ZW1ba2V5XTtcbiAgICB9KTtcbiAgICBpZiAoc3RvcmVJdGVtSW5kZXggPiAtMSkge1xuICAgICAgdGhpcy5keW5hbWljQnJlYWRjcnVtYlN0b3JlW3N0b3JlSXRlbUluZGV4XSA9IHtcbiAgICAgICAgLi4udGhpcy5keW5hbWljQnJlYWRjcnVtYlN0b3JlW3N0b3JlSXRlbUluZGV4XSxcbiAgICAgICAgLi4uYnJlYWRjcnVtYixcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZHluYW1pY0JyZWFkY3J1bWJTdG9yZS5wdXNoKHsgLi4uYnJlYWRjcnVtYiB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSWYgYnJlYWRjcnVtYiBpcyBwcmVzZW50IGluIGN1cnJlbnQgYnJlYWRjcnVtYnMgdXBkYXRlIGl0IGFuZCBlbWl0IG5ldyBzdHJlYW1cbiAgICovXG4gIHByaXZhdGUgdXBkYXRlQ3VycmVudEJyZWFkY3J1bWJzKFxuICAgIGtleTogc3RyaW5nLFxuICAgIGJyZWFkY3J1bWI6IEJyZWFkY3J1bWJEZWZpbml0aW9uXG4gICkge1xuICAgIGNvbnN0IGl0ZW1JbmRleCA9IHRoaXMuY3VycmVudEJyZWFkY3J1bWJzLmZpbmRJbmRleCgoaXRlbSkgPT4ge1xuICAgICAgcmV0dXJuIGtleSA9PT0gJ3JvdXRlUmVnZXgnXG4gICAgICAgID8gdGhpcy5tYXRjaFJlZ2V4KGl0ZW0ucm91dGVMaW5rLCBicmVhZGNydW1iW2tleV0pXG4gICAgICAgIDogYnJlYWRjcnVtYltrZXldID09PSBpdGVtW2tleV07XG4gICAgfSk7XG4gICAgaWYgKGl0ZW1JbmRleCA+IC0xKSB7XG4gICAgICB0aGlzLmN1cnJlbnRCcmVhZGNydW1ic1tpdGVtSW5kZXhdID0ge1xuICAgICAgICAuLi50aGlzLmN1cnJlbnRCcmVhZGNydW1ic1tpdGVtSW5kZXhdLFxuICAgICAgICAuLi5icmVhZGNydW1iLFxuICAgICAgfTtcbiAgICAgIGNvbnN0IGJyZWFkY3J1bWJzVG9TaG93ID0gdGhpcy5jdXJyZW50QnJlYWRjcnVtYnMuZmlsdGVyKFxuICAgICAgICAoaXRlbSkgPT4gIWl0ZW0uc2tpcFxuICAgICAgKTtcbiAgICAgIHRoaXMuYnJlYWRjcnVtYnMubmV4dChbLi4uYnJlYWRjcnVtYnNUb1Nob3ddKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRm9yIGEgcm91dGUgd2l0aCBwYXRoIHBhcmFtLCB3ZSBjcmVhdGUgcmVnZXggZHluYW1pY2FsbHkgZnJvbSBhbmd1bGFyIHJvdXRlIHN5bnRheFxuICAgKiAnL21lbnRvci86aWQnIGJlY29tZXMgJy9tZW50b3IvW14vXScsXG4gICAqIGJyZWFkY3J1bWJTZXJ2aWNlLnNldCgnL21lbnRvci86aWQnLCAnVWRheScpIHNob3VsZCB1cGRhdGUgJ1VkYXknIGFzIGxhYmVsIGZvciAnL21lbnRvci8yJyBPUiAnbWVudG9yL2FkYSdcbiAgICovXG4gIHByaXZhdGUgYnVpbGRSZWdleChwYXRoOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVMZWFkaW5nU2xhc2gocGF0aCkucmVwbGFjZShcbiAgICAgIG5ldyBSZWdFeHAoUEFUSF9QQVJBTS5SRUdFWF9JREVOVElGSUVSLCAnZycpLFxuICAgICAgUEFUSF9QQVJBTS5SRUdFWF9SRVBMQUNFUlxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIGVuc3VyZUxlYWRpbmdTbGFzaChwYXRoOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gcGF0aC5zdGFydHNXaXRoKCcvJykgPyBwYXRoIDogYC8ke3BhdGh9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbiBBcHAncyBSb3V0ZUNvbmZpZywgYnJlYWRjcnVtYiBjYW4gYmUgZGVmaW5lZCBhcyBhIHN0cmluZyBPUiBhIGZ1bmN0aW9uIE9SIGFuIG9iamVjdFxuICAgKlxuICAgKiBzdHJpbmc6IHNpbXBsZSBzdGF0aWMgYnJlYWRjcnVtYiBsYWJlbCBmb3IgYSBwYXRoXG4gICAqIGZ1bmN0aW9uOiBjYWxsYmFjayB0aGF0IGdldHMgaW52b2tlZCB3aXRoIHJlc29sdmVkIHBhdGggcGFyYW1cbiAgICogb2JqZWN0OiBhZGRpdGlvbmFsIGRhdGEgZGVmaW5lZCBhbG9uZyB3aXRoIGJyZWFkY3J1bWIgbGFiZWwgdGhhdCBnZXRzIHBhc3NlZCB0byAqeG5nQnJlYWRjcnVtYkl0ZW0gZGlyZWN0aXZlXG4gICAqL1xuICBwcml2YXRlIGV4dHJhY3RMYWJlbChjb25maWc6IEJyZWFkY3J1bWJDb25maWcsIHJlc29sdmVkUGFyYW0/OiBzdHJpbmcpIHtcbiAgICBjb25zdCBsYWJlbCA9IHR5cGVvZiBjb25maWcgPT09ICdvYmplY3QnID8gY29uZmlnLmxhYmVsIDogY29uZmlnO1xuICAgIGlmICh0eXBlb2YgbGFiZWwgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIHJldHVybiBsYWJlbChyZXNvbHZlZFBhcmFtKTtcbiAgICB9XG4gICAgcmV0dXJuIGxhYmVsO1xuICB9XG5cbiAgcHJpdmF0ZSBleHRyYWN0T2JqZWN0KGNvbmZpZzogQnJlYWRjcnVtYkNvbmZpZyk6IEJyZWFkY3J1bWJPYmplY3Qge1xuICAgIC8vIGRvbid0IGluY2x1ZGUge2xhYmVsfSBpZiBjb25maWcgaXMgdW5kZWZpbmVkLiBUaGlzIGlzIGltcG9ydGFudCBzaW5jZSB3ZSBtZXJnZSB0aGUgY29uZmlnc1xuICAgIGlmIChcbiAgICAgIGNvbmZpZyAmJlxuICAgICAgKHR5cGVvZiBjb25maWcgPT09ICdzdHJpbmcnIHx8IHR5cGVvZiBjb25maWcgPT09ICdmdW5jdGlvbicpXG4gICAgKSB7XG4gICAgICByZXR1cm4geyBsYWJlbDogY29uZmlnIH07XG4gICAgfVxuICAgIHJldHVybiAoY29uZmlnIGFzIEJyZWFkY3J1bWJPYmplY3QpIHx8IHt9O1xuICB9XG59XG4iXX0=