@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
230 lines • 34.4 kB
JavaScript
import { Component, EventEmitter, Input, Output, ViewChild, ViewContainerRef, Injector, SimpleChange, reflectComponentType } from '@angular/core';
import { DynamicComponentErrorStrategy, isLazyDynamicComponents, isEagerDynamicComponents } from './dynamic-component.model';
import { DynamicComponentService } from './dynamic-component.service';
import { isObservable, of, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { flatten, get, isUndefined, set, cloneDeep } from 'lodash-es';
import { DynamicComponentAlertAggregator } from './dynamic-component-alert-aggregator';
import { DynamicComponentAlert } from './dynamic-component-alert.model';
import * as i0 from "@angular/core";
import * as i1 from "./dynamic-component.service";
import * as i2 from "../common/icon.directive";
import * as i3 from "../i18n/c8y-translate.directive";
import * as i4 from "@angular/common";
import * as i5 from "ngx-bootstrap/collapse";
import * as i6 from "./dynamic-component-alerts.component";
import * as i7 from "../i18n/c8y-translate.pipe";
/**
* C8y dynamic component.
*
* register component in HOOK in module:
* ```typescript
* import { hookComponent } from '@c8y/ngx-components';
*
* @NgModule({
* ...,
* providers: [
* hookComponent({
* id: 'test-component',
* label: 'My test component',
* description: 'this is test component',
* component: TestComponent
* })
* ...
* ]
*
* ```
* Showing dynamic component:
* ```html
* <c8y-dynamic-component [componentId]="'test-component'" [config]="config"></c8y-dynamic-component>
* ```
*/
export class DynamicComponentComponent {
/**
* @ignore only DI
*/
constructor(dynamicComponentService, injector) {
this.dynamicComponentService = dynamicComponentService;
this.injector = injector;
/**
* DynamicComponents can have two modes, an edit (config) and an view (component) mode.
* By default it is shown in the component mode.
*/
this.mode = 'component';
/**
* Disable this to hide the error that is shown if the component was not found.
*/
this.notFoundError = true;
/**
* If set to true, it will execute the components resolvers before initializing the component.
* Defaults to false.
*/
this.executeResolvers = false;
/**
* Allows to set additional classes for widget styling.
*/
this.updateWidgetClasses = new EventEmitter();
/**
* @ignore
*/
this.expandErrorDetails = false;
this.dynamicComponentErrorStrategy = DynamicComponentErrorStrategy;
this.destroy$ = new Subject();
}
/**
* Calls the dynamic component life cycle hook. Currently only
* supporting onBeforeSave, a hook which is called before a config component
* is saved.
*/
callLifeCycleHooks() {
return this.callOnBeforeSaveHook();
}
/**
* @ignore
*/
async ngOnChanges(changes) {
const cmp = await this.dynamicComponentService.getById(this.componentId);
let errors = new Array();
if (cmp) {
this.errorStrategy = cmp.errorStrategy || DynamicComponentErrorStrategy.CUSTOM;
if (this.executeResolvers) {
const [resolvedConfig] = await this.dynamicComponentService.executeResolvers([
{ componentId: this.componentId, config: this.config }
]);
Object.entries(resolvedConfig).forEach(([key, value]) => set(this.config, key, value));
}
const resolvedValuesOfConfiguration = cmp.resolve
? Object.keys(cmp.resolve).map(resolvedKey => get(this.config, resolvedKey))
: [];
const flattenedResolvedValuesOfConfiguration = flatten(resolvedValuesOfConfiguration);
errors = flattenedResolvedValuesOfConfiguration.filter(potentialError => potentialError && potentialError instanceof DynamicComponentAlert);
if (errors.length && cmp.errorStrategy === DynamicComponentErrorStrategy.NOT_RENDER) {
return;
}
if (this.mode == 'config' && !cmp.loadConfigComponent && !cmp.configComponent) {
this.host.clear();
return;
}
}
await this.loadComponent(cmp, errors);
this.emitChangeOnComponent(changes);
}
/**
* If an outside component changes the configuration, this function triggers ngOnChange on the dynamic component.
* @param newConfigValues The new configuration value.
*/
emitConfigChange(newConfigValues) {
if (!this.componentInstance) {
return;
}
const oldConfig = cloneDeep(this.config);
Object.assign(this.config, newConfigValues);
this.componentInstance.config = this.config;
this.emitChangeOnComponent({ config: new SimpleChange(oldConfig, this.config, false) });
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
emitChangeOnComponent(changes) {
const ngOnChanges = this.componentRef?.instance?.ngOnChanges;
if (ngOnChanges) {
ngOnChanges.call(this.componentRef.instance, changes);
}
}
async loadComponent(dynamicComponent, errors = []) {
try {
this.error = undefined;
const componentType = await this.getComponentType(dynamicComponent);
this.host.clear();
this.componentRef = this.host.createComponent(componentType, {
environmentInjector: (dynamicComponent.injector || this.injector)
});
this.componentInstance = this.componentRef.instance;
const componentMetadata = this.safeReflectComponentType(componentType);
const hasConfigInput = componentMetadata?.inputs.some(input => input.propName === 'config');
if (hasConfigInput) {
this.componentRef.setInput('config', this.config);
}
else {
this.componentInstance.config = this.config;
}
this.componentInstance.alerts = new DynamicComponentAlertAggregator(errors);
this.subscribeForOverlayChange();
}
catch (ex) {
this.error = ex;
console.warn(ex);
}
}
/**
* Safely reflects the component metadata.
* If reflection fails, it returns a default object with an empty inputs array.
*/
safeReflectComponentType(componentType) {
try {
return reflectComponentType(componentType);
}
catch (error) {
console.warn('Failed to reflect component metadata:', error);
return { inputs: [] };
}
}
async getComponentType(dynamicComponent) {
if (isLazyDynamicComponents(dynamicComponent)) {
const componentPromiseFunction = this.mode === 'component'
? dynamicComponent.loadComponent
: dynamicComponent.loadConfigComponent;
if (!componentPromiseFunction) {
return;
}
return await componentPromiseFunction();
}
else if (isEagerDynamicComponents(dynamicComponent)) {
return this.mode === 'component'
? dynamicComponent.component
: dynamicComponent.configComponent;
}
}
subscribeForOverlayChange() {
this.componentInstance.alerts.anyAlertExists$
.pipe(takeUntil(this.destroy$))
.subscribe(anyAlertsExists => this.updateWidgetClasses.emit({ 'alerts-overlay': anyAlertsExists }));
}
callOnBeforeSaveHook() {
if (!this.componentRef) {
return of(true);
}
const hook = this.componentRef.instance.onBeforeSave;
if (hook) {
const result = hook.call(this.componentRef.instance, this.config);
if (isUndefined(result)) {
return of(true);
}
return isObservable(result) ? result : of(result);
}
return of(true);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DynamicComponentComponent, deps: [{ token: i1.DynamicComponentService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DynamicComponentComponent, selector: "c8y-dynamic-component", inputs: { componentId: "componentId", config: "config", mode: "mode", notFoundError: "notFoundError", executeResolvers: "executeResolvers" }, outputs: { updateWidgetClasses: "updateWidgetClasses" }, viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: "<ng-template #host></ng-template>\n\n<div class=\"alert alert-warning m-8\" role=\"alert\" *ngIf=\"error && notFoundError\">\n <strong class=\"message\">\n {{\n 'This widget cannot be rendered because the current application does not support the following component:'\n | translate\n }}\n {{ componentId }}.\n </strong>\n <p class=\"text-muted m-t-8\">\n <button class=\"btn btn-clean\" (click)=\"expandErrorDetails = !expandErrorDetails\">\n <i c8yIcon=\"chevron-down\"></i>\n <span *ngIf=\"!expandErrorDetails\" translate>Show details</span>\n <span *ngIf=\"expandErrorDetails\" translate>Hide details</span>\n </button>\n </p>\n <div [collapse]=\"!expandErrorDetails\" [isAnimated]=\"true\">\n <pre>\n {{ error }}\n </pre>\n </div>\n</div>\n\n<div\n class=\"overlay-center-vertically d-flex d-col a-i-stretch j-c-start p-16 p-r-24 p-l-24\"\n *ngIf=\"\n errorStrategy === dynamicComponentErrorStrategy.OVERLAY_ERROR &&\n componentInstance?.alerts?.anyAlertExists$ | async\n \"\n>\n <c8y-dynamic-component-alerts [alerts]=\"componentInstance.alerts\"></c8y-dynamic-component-alerts>\n</div>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.CollapseDirective, selector: "[collapse]", inputs: ["display", "isAnimated", "collapse"], outputs: ["collapsed", "collapses", "expanded", "expands"], exportAs: ["bs-collapse"] }, { kind: "component", type: i6.DynamicComponentAlertsComponent, selector: "c8y-dynamic-component-alerts", inputs: ["alerts"] }, { kind: "pipe", type: i7.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DynamicComponentComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-dynamic-component', template: "<ng-template #host></ng-template>\n\n<div class=\"alert alert-warning m-8\" role=\"alert\" *ngIf=\"error && notFoundError\">\n <strong class=\"message\">\n {{\n 'This widget cannot be rendered because the current application does not support the following component:'\n | translate\n }}\n {{ componentId }}.\n </strong>\n <p class=\"text-muted m-t-8\">\n <button class=\"btn btn-clean\" (click)=\"expandErrorDetails = !expandErrorDetails\">\n <i c8yIcon=\"chevron-down\"></i>\n <span *ngIf=\"!expandErrorDetails\" translate>Show details</span>\n <span *ngIf=\"expandErrorDetails\" translate>Hide details</span>\n </button>\n </p>\n <div [collapse]=\"!expandErrorDetails\" [isAnimated]=\"true\">\n <pre>\n {{ error }}\n </pre>\n </div>\n</div>\n\n<div\n class=\"overlay-center-vertically d-flex d-col a-i-stretch j-c-start p-16 p-r-24 p-l-24\"\n *ngIf=\"\n errorStrategy === dynamicComponentErrorStrategy.OVERLAY_ERROR &&\n componentInstance?.alerts?.anyAlertExists$ | async\n \"\n>\n <c8y-dynamic-component-alerts [alerts]=\"componentInstance.alerts\"></c8y-dynamic-component-alerts>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.DynamicComponentService }, { type: i0.Injector }], propDecorators: { componentId: [{
type: Input
}], config: [{
type: Input
}], mode: [{
type: Input
}], notFoundError: [{
type: Input
}], executeResolvers: [{
type: Input
}], updateWidgetClasses: [{
type: Output
}], host: [{
type: ViewChild,
args: ['host', { read: ViewContainerRef, static: true }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1jb21wb25lbnQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vY29yZS9keW5hbWljLWNvbXBvbmVudC9keW5hbWljLWNvbXBvbmVudC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9jb3JlL2R5bmFtaWMtY29tcG9uZW50L2R5bmFtaWMtY29tcG9uZW50LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxTQUFTLEVBQ1QsWUFBWSxFQUNaLEtBQUssRUFFTCxNQUFNLEVBQ04sU0FBUyxFQUNULGdCQUFnQixFQUVoQixRQUFRLEVBS1IsWUFBWSxFQUNaLG9CQUFvQixFQUNyQixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBR0wsNkJBQTZCLEVBQzdCLHVCQUF1QixFQUN2Qix3QkFBd0IsRUFFekIsTUFBTSwyQkFBMkIsQ0FBQztBQUNuQyxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUN0RSxPQUFPLEVBQUUsWUFBWSxFQUFjLEVBQUUsRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDN0QsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzNDLE9BQU8sRUFBRSxPQUFPLEVBQUUsR0FBRyxFQUFFLFdBQVcsRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3RFLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ3ZGLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDOzs7Ozs7Ozs7QUFFeEU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCRztBQUtILE1BQU0sT0FBTyx5QkFBeUI7SUErQ3BDOztPQUVHO0lBQ0gsWUFDVSx1QkFBZ0QsRUFDaEQsUUFBa0I7UUFEbEIsNEJBQXVCLEdBQXZCLHVCQUF1QixDQUF5QjtR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R0FBSSxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQXlCLENBQUMsWUFBWSxDQUFDO1FBQ3ZFLElBQUksSUFBSSxFQUFFLENBQUM7WUFDVCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUNsRSxJQUFJLFdBQVcsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO2dCQUN4QixPQUFPLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNsQixDQUFDO1lBQ0QsT0FBTyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBeUIsQ0FBQztRQUM3RSxDQUFDO1FBQ0QsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDbEIsQ0FBQzsrR0EvTVUseUJBQXlCO21HQUF6Qix5QkFBeUIsNlVBK0JULGdCQUFnQixnRUM1RjdDLDhvQ0FpQ0E7OzRGRDRCYSx5QkFBeUI7a0JBSnJDLFNBQVM7K0JBQ0UsdUJBQXVCO21IQVF4QixXQUFXO3NCQUFuQixLQUFLO2dCQUlHLE1BQU07c0JBQWQsS0FBSztnQkFLRyxJQUFJO3NCQUFaLEtBQUs7Z0JBSUcsYUFBYTtzQkFBckIsS0FBSztnQkFLRyxnQkFBZ0I7c0JBQXhCLEtBQUs7Z0JBSUksbUJBQW1CO3NCQUE1QixNQUFNO2dCQUlzRCxJQUFJO3NCQUFoRSxTQUFTO3VCQUFDLE1BQU0sRUFBRSxFQUFFLElBQUksRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ29tcG9uZW50LFxuICBFdmVudEVtaXR0ZXIsXG4gIElucHV0LFxuICBPbkRlc3Ryb3ksXG4gIE91dHB1dCxcbiAgVmlld0NoaWxkLFxuICBWaWV3Q29udGFpbmVyUmVmLFxuICBDb21wb25lbnRSZWYsXG4gIEluamVjdG9yLFxuICBTaW1wbGVDaGFuZ2VzLFxuICBPbkNoYW5nZXMsXG4gIEVudmlyb25tZW50SW5qZWN0b3IsXG4gIFR5cGUsXG4gIFNpbXBsZUNoYW5nZSxcbiAgcmVmbGVjdENvbXBvbmVudFR5cGVcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBEeW5hbWljQ29tcG9uZW50LFxuICBEeW5hbWljQ29tcG9uZW50RGVmaW5pdGlvbixcbiAgRHluYW1pY0NvbXBvbmVudEVycm9yU3RyYXRlZ3ksXG4gIGlzTGF6eUR5bmFtaWNDb21wb25lbnRzLFxuICBpc0VhZ2VyRHluYW1pY0NvbXBvbmVudHMsXG4gIE9uQmVmb3JlU2F2ZVxufSBmcm9tICcuL2R5bmFtaWMtY29tcG9uZW50Lm1vZGVsJztcbmltcG9ydCB7IER5bmFtaWNDb21wb25lbnRTZXJ2aWNlIH0gZnJvbSAnLi9keW5hbWljLWNvbXBvbmVudC5zZXJ2aWNlJztcbmltcG9ydCB7IGlzT2JzZXJ2YWJsZSwgT2JzZXJ2YWJsZSwgb2YsIFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IGZsYXR0ZW4sIGdldCwgaXNVbmRlZmluZWQsIHNldCwgY2xvbmVEZWVwIH0gZnJvbSAnbG9kYXNoLWVzJztcbmltcG9ydCB7IER5bmFtaWNDb21wb25lbnRBbGVydEFnZ3JlZ2F0b3IgfSBmcm9tICcuL2R5bmFtaWMtY29tcG9uZW50LWFsZXJ0LWFnZ3JlZ2F0b3InO1xuaW1wb3J0IHsgRHluYW1pY0NvbXBvbmVudEFsZXJ0IH0gZnJvbSAnLi9keW5hbWljLWNvbXBvbmVudC1hbGVydC5tb2RlbCc7XG5cbi8qKlxuICogQzh5IGR5bmFtaWMgY29tcG9uZW50LlxuICpcbiAqIHJlZ2lzdGVyIGNvbXBvbmVudCBpbiBIT09LIGluIG1vZHVsZTpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqICBpbXBvcnQgeyBob29rQ29tcG9uZW50IH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG4gKlxuICogQE5nTW9kdWxlKHtcbiAqICAuLi4sXG4gKiAgcHJvdmlkZXJzOiBbXG4gKiAgICBob29rQ29tcG9uZW50KHtcbiAqICAgICAgaWQ6ICd0ZXN0LWNvbXBvbmVudCcsXG4gKiAgICAgIGxhYmVsOiAnTXkgdGVzdCBjb21wb25lbnQnLFxuICogICAgICBkZXNjcmlwdGlvbjogJ3RoaXMgaXMgdGVzdCBjb21wb25lbnQnLFxuICogICAgICBjb21wb25lbnQ6IFRlc3RDb21wb25lbnRcbiAqICAgIH0pXG4gKiAgLi4uXG4gKiAgXVxuICpcbiAqIGBgYFxuICogU2hvd2luZyBkeW5hbWljIGNvbXBvbmVudDpcbiAqIGBgYGh0bWxcbiAqIDxjOHktZHluYW1pYy1jb21wb25lbnQgW2NvbXBvbmVudElkXT1cIid0ZXN0LWNvbXBvbmVudCdcIiBbY29uZmlnXT1cImNvbmZpZ1wiPjwvYzh5LWR5bmFtaWMtY29tcG9uZW50PlxuICogYGBgXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS1keW5hbWljLWNvbXBvbmVudCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9keW5hbWljLWNvbXBvbmVudC5jb21wb25lbnQuaHRtbCdcbn0pXG5leHBvcnQgY2xhc3MgRHluYW1pY0NvbXBvbmVudENvbXBvbmVudCBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG4gIC8qKlxuICAgKiBUaGUgSUQgb2YgdGhlIHJlZ2lzdGVyZWQgY29tcG9uZW50LiBJdCBuZWVkcyB0byBiZSBhIGNvbXBvbmVudCB0aGF0IGlzIGhvb2tlZFxuICAgKiB3aXRoIHRoZSBIT09LX0NPTVBPTkVOVFMgZXh0ZW5zaW9uIGhvb2suXG4gICAqL1xuICBASW5wdXQoKSBjb21wb25lbnRJZDogc3RyaW5nO1xuICAvKipcbiAgICogVGhlIGNvbmZpZ3VyYXRpb24gdG8gcGFzcy5cbiAgICovXG4gIEBJbnB1dCgpIGNvbmZpZzogdW5rbm93bjtcbiAgLyoqXG4gICAqIER5bmFtaWNDb21wb25lbnRzIGNhbiBoYXZlIHR3byBtb2RlcywgYW4gZWRpdCAoY29uZmlnKSBhbmQgYW4gdmlldyAoY29tcG9uZW50KSBtb2RlLlxuICAgKiBCeSBkZWZhdWx0IGl0IGlzIHNob3duIGluIHRoZSBjb21wb25lbnQgbW9kZS5cbiAgICovXG4gIEBJbnB1dCgpIG1vZGU6ICdjb25maWcnIHwgJ2NvbXBvbmVudCcgPSAnY29tcG9uZW50JztcbiAgLyoqXG4gICAqIERpc2FibGUgdGhpcyB0byBoaWRlIHRoZSBlcnJvciB0aGF0IGlzIHNob3duIGlmIHRoZSBjb21wb25lbnQgd2FzIG5vdCBmb3VuZC5cbiAgICovXG4gIEBJbnB1dCgpIG5vdEZvdW5kRXJyb3IgPSB0cnVlO1xuICAvKipcbiAgICogSWYgc2V0IHRvIHRydWUsIGl0IHdpbGwgZXhlY3V0ZSB0aGUgY29tcG9uZW50cyByZXNvbHZlcnMgYmVmb3JlIGluaXRpYWxpemluZyB0aGUgY29tcG9uZW50LlxuICAgKiBEZWZhdWx0cyB0byBmYWxzZS5cbiAgICovXG4gIEBJbnB1dCgpIGV4ZWN1dGVSZXNvbHZlcnMgPSBmYWxzZTtcbiAgLyoqXG4gICAqIEFsbG93cyB0byBzZXQgYWRkaXRpb25hbCBjbGFzc2VzIGZvciB3aWRnZXQgc3R5bGluZy5cbiAgICovXG4gIEBPdXRwdXQoKSB1cGRhdGVXaWRnZXRDbGFzc2VzID0gbmV3IEV2ZW50RW1pdHRlcjxSZWNvcmQ8c3RyaW5nLCBib29sZWFuPj4oKTtcbiAgLyoqXG4gICAqIEBpZ25vcmVcbiAgICovXG4gIEBWaWV3Q2hpbGQoJ2hvc3QnLCB7IHJlYWQ6IFZpZXdDb250YWluZXJSZWYsIHN0YXRpYzogdHJ1ZSB9KSBob3N0OiBWaWV3Q29udGFpbmVyUmVmO1xuICAvKipcbiAgICogQGlnbm9yZVxuICAgKi9cbiAgZXJyb3I6IGFueTtcbiAgLyoqXG4gICAqIEBpZ25vcmVcbiAgICovXG4gIGV4cGFuZEVycm9yRGV0YWlscyA9IGZhbHNlO1xuICBjb21wb25lbnRJbnN0YW5jZTogRHluYW1pY0NvbXBvbmVudDtcbiAgZXJyb3JTdHJhdGVneTogRHluYW1pY0NvbXBvbmVudEVycm9yU3RyYXRlZ3k7XG4gIGR5bmFtaWNDb21wb25lbnRFcnJvclN0cmF0ZWd5OiB0eXBlb2YgRHluYW1pY0NvbXBvbmVudEVycm9yU3RyYXRlZ3kgPVxuICAgIER5bmFtaWNDb21wb25lbnRFcnJvclN0cmF0ZWd5O1xuICBwcml2YXRlIGRlc3Ryb3kkOiBTdWJqZWN0PHZvaWQ+ID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcbiAgcHJpdmF0ZSBjb21wb25lbnRSZWY6IENvbXBvbmVudFJlZjxDb21wb25lbnQ+O1xuXG4gIC8qKlxuICAgKiBAaWdub3JlIG9ubHkgRElcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgZHluYW1pY0NvbXBvbmVudFNlcnZpY2U6IER5bmFtaWNDb21wb25lbnRTZXJ2aWNlLFxuICAgIHByaXZhdGUgaW5qZWN0b3I6IEluamVjdG9yXG4gICkge31cblxuICAvKipcbiAgICogQ2FsbHMgdGhlIGR5bmFtaWMgY29tcG9uZW50IGxpZmUgY3ljbGUgaG9vay4gQ3VycmVudGx5IG9ubHlcbiAgICogc3VwcG9ydGluZyBvbkJlZm9yZVNhdmUsIGEgaG9vayB3aGljaCBpcyBjYWxsZWQgYmVmb3JlIGEgY29uZmlnIGNvbXBvbmVudFxuICAgKiBpcyBzYXZlZC5cbiAgICovXG4gIGNhbGxMaWZlQ3ljbGVIb29rcygpIHtcbiAgICByZXR1cm4gdGhpcy5jYWxsT25CZWZvcmVTYXZlSG9vaygpO1xuICB9XG5cbiAgLyoqXG4gICAqIEBpZ25vcmVcbiAgICovXG4gIGFzeW5jIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpIHtcbiAgICBjb25zdCBjbXAgPSBhd2FpdCB0aGlzLmR5bmFtaWNDb21wb25lbnRTZXJ2aWNlLmdldEJ5SWQodGhpcy5jb21wb25lbnRJZCk7XG4gICAgbGV0IGVycm9ycyA9IG5ldyBBcnJheTxEeW5hbWljQ29tcG9uZW50QWxlcnQ+KCk7XG4gICAgaWYgKGNtcCkge1xuICAgICAgdGhpcy5lcnJvclN0cmF0ZWd5ID0gY21wLmVycm9yU3RyYXRlZ3kgfHwgRHluYW1pY0NvbXBvbmVudEVycm9yU3RyYXRlZ3kuQ1VTVE9NO1xuICAgICAgaWYgKHRoaXMuZXhlY3V0ZVJlc29sdmVycykge1xuICAgICAgICBjb25zdCBbcmVzb2x2ZWRDb25maWddID0gYXdhaXQgdGhpcy5keW5hbWljQ29tcG9uZW50U2VydmljZS5leGVjdXRlUmVzb2x2ZXJzKFtcbiAgICAgICAgICB7IGNvbXBvbmVudElkOiB0aGlzLmNvbXBvbmVudElkLCBjb25maWc6IHRoaXMuY29uZmlnIH1cbiAgICAgICAgXSk7XG4gICAgICAgIE9iamVjdC5lbnRyaWVzKHJlc29sdmVkQ29uZmlnKS5mb3JFYWNoKChba2V5LCB2YWx1ZV0pID0+IHNldCh0aGlzLmNvbmZpZywga2V5LCB2YWx1ZSkpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCByZXNvbHZlZFZhbHVlc09mQ29uZmlndXJhdGlvbjogQXJyYXk8RHluYW1pY0NvbXBvbmVudEFsZXJ0IHwgRHluYW1pY0NvbXBvbmVudEFsZXJ0W10+ID1cbiAgICAgICAgY21wLnJlc29sdmVcbiAgICAgICAgICA/IE9iamVjdC5rZXlzKGNtcC5yZXNvbHZlKS5tYXAocmVzb2x2ZWRLZXkgPT4gZ2V0KHRoaXMuY29uZmlnLCByZXNvbHZlZEtleSkpXG4gICAgICAgICAgOiBbXTtcbiAgICAgIGNvbnN0IGZsYXR0ZW5lZFJlc29sdmVkVmFsdWVzT2ZDb25maWd1cmF0aW9uID0gZmxhdHRlbihyZXNvbHZlZFZhbHVlc09mQ29uZmlndXJhdGlvbik7XG4gICAgICBlcnJvcnMgPSBmbGF0dGVuZWRSZXNvbHZlZFZhbHVlc09mQ29uZmlndXJhdGlvbi5maWx0ZXIoXG4gICAgICAgIHBvdGVudGlhbEVycm9yID0+IHBvdGVudGlhbEVycm9yICYmIHBvdGVudGlhbEVycm9yIGluc3RhbmNlb2YgRHluYW1pY0NvbXBvbmVudEFsZXJ0XG4gICAgICApO1xuICAgICAgaWYgKGVycm9ycy5sZW5ndGggJiYgY21wLmVycm9yU3RyYXRlZ3kgPT09IER5bmFtaWNDb21wb25lbnRFcnJvclN0cmF0ZWd5Lk5PVF9SRU5ERVIpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMubW9kZSA9PSAnY29uZmlnJyAmJiAhY21wLmxvYWRDb25maWdDb21wb25lbnQgJiYgIWNtcC5jb25maWdDb21wb25lbnQpIHtcbiAgICAgICAgdGhpcy5ob3N0LmNsZWFyKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLmxvYWRDb21wb25lbnQoY21wLCBlcnJvcnMpO1xuICAgIHRoaXMuZW1pdENoYW5nZU9uQ29tcG9uZW50KGNoYW5nZXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIElmIGFuIG91dHNpZGUgY29tcG9uZW50IGNoYW5nZXMgdGhlIGNvbmZpZ3VyYXRpb24sIHRoaXMgZnVuY3Rpb24gdHJpZ2dlcnMgbmdPbkNoYW5nZSBvbiB0aGUgZHluYW1pYyBjb21wb25lbnQuXG4gICAqIEBwYXJhbSBuZXdDb25maWdWYWx1ZXMgVGhlIG5ldyBjb25maWd1cmF0aW9uIHZhbHVlLlxuICAgKi9cbiAgZW1pdENvbmZpZ0NoYW5nZShuZXdDb25maWdWYWx1ZXM6IHVua25vd24pIHtcbiAgICBpZiAoIXRoaXMuY29tcG9uZW50SW5zdGFuY2UpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgb2xkQ29uZmlnID0gY2xvbmVEZWVwKHRoaXMuY29uZmlnKTtcbiAgICBPYmplY3QuYXNzaWduKHRoaXMuY29uZmlnLCBuZXdDb25maWdWYWx1ZXMpO1xuICAgIHRoaXMuY29tcG9uZW50SW5zdGFuY2UuY29uZmlnID0gdGhpcy5jb25maWc7XG4gICAgdGhpcy5lbWl0Q2hhbmdlT25Db21wb25lbnQoeyBjb25maWc6IG5ldyBTaW1wbGVDaGFuZ2Uob2xkQ29uZmlnLCB0aGlzLmNvbmZpZywgZmFsc2UpIH0pO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XG4gICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBlbWl0Q2hhbmdlT25Db21wb25lbnQoY2hhbmdlczogU2ltcGxlQ2hhbmdlcykge1xuICAgIGNvbnN0IG5nT25DaGFuZ2VzID0gKHRoaXMuY29tcG9uZW50UmVmPy5pbnN0YW5jZSBhcyBPbkNoYW5nZXMpPy5uZ09uQ2hhbmdlcztcbiAgICBpZiAobmdPbkNoYW5nZXMpIHtcbiAgICAgIG5nT25DaGFuZ2VzLmNhbGwodGhpcy5jb21wb25lbnRSZWYuaW5zdGFuY2UsIGNoYW5nZXMpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgbG9hZENvbXBvbmVudChcbiAgICBkeW5hbWljQ29tcG9uZW50OiBEeW5hbWljQ29tcG9uZW50RGVmaW5pdGlvbixcbiAgICBlcnJvcnM6IER5bmFtaWNDb21wb25lbnRBbGVydFtdID0gW11cbiAgKSB7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMuZXJyb3IgPSB1bmRlZmluZWQ7XG4gICAgICBjb25zdCBjb21wb25lbnRUeXBlID0gYXdhaXQgdGhpcy5nZXRDb21wb25lbnRUeXBlKGR5bmFtaWNDb21wb25lbnQpO1xuXG4gICAgICB0aGlzLmhvc3QuY2xlYXIoKTtcbiAgICAgIHRoaXMuY29tcG9uZW50UmVmID0gdGhpcy5ob3N0LmNyZWF0ZUNvbXBvbmVudChjb21wb25lbnRUeXBlLCB7XG4gICAgICAgIGVudmlyb25tZW50SW5qZWN0b3I6IChkeW5hbWljQ29tcG9uZW50LmluamVjdG9yIHx8IHRoaXMuaW5qZWN0b3IpIGFzIEVudmlyb25tZW50SW5qZWN0b3JcbiAgICAgIH0pO1xuICAgICAgdGhpcy5jb21wb25lbnRJbnN0YW5jZSA9IHRoaXMuY29tcG9uZW50UmVmLmluc3RhbmNlIGFzIER5bmFtaWNDb21wb25lbnQ7XG5cbiAgICAgIGNvbnN0IGNvbXBvbmVudE1ldGFkYXRhID0gdGhpcy5zYWZlUmVmbGVjdENvbXBvbmVudFR5cGUoY29tcG9uZW50VHlwZSk7XG4gICAgICBjb25zdCBoYXNDb25maWdJbnB1dCA9IGNvbXBvbmVudE1ldGFkYXRhPy5pbnB1dHMuc29tZShpbnB1dCA9PiBpbnB1dC5wcm9wTmFtZSA9PT0gJ2NvbmZpZycpO1xuICAgICAgaWYgKGhhc0NvbmZpZ0lucHV0KSB7XG4gICAgICAgIHRoaXMuY29tcG9uZW50UmVmLnNldElucHV0KCdjb25maWcnLCB0aGlzLmNvbmZpZyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmNvbXBvbmVudEluc3RhbmNlLmNvbmZpZyA9IHRoaXMuY29uZmlnO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmNvbXBvbmVudEluc3RhbmNlLmFsZXJ0cyA9IG5ldyBEeW5hbWljQ29tcG9uZW50QWxlcnRBZ2dyZWdhdG9yKGVycm9ycyk7XG4gICAgICB0aGlzLnN1YnNjcmliZUZvck92ZXJsYXlDaGFuZ2UoKTtcbiAgICB9IGNhdGNoIChleCkge1xuICAgICAgdGhpcy5lcnJvciA9IGV4O1xuICAgICAgY29uc29sZS53YXJuKGV4KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2FmZWx5IHJlZmxlY3RzIHRoZSBjb21wb25lbnQgbWV0YWRhdGEuXG4gICAqIElmIHJlZmxlY3Rpb24gZmFpbHMsIGl0IHJldHVybnMgYSBkZWZhdWx0IG9iamVjdCB3aXRoIGFuIGVtcHR5IGlucHV0cyBhcnJheS5cbiAgICovXG4gIHByaXZhdGUgc2FmZVJlZmxlY3RDb21wb25lbnRUeXBlKGNvbXBvbmVudFR5cGU6IFR5cGU8YW55Pikge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gcmVmbGVjdENvbXBvbmVudFR5cGUoY29tcG9uZW50VHlwZSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUud2FybignRmFpbGVkIHRvIHJlZmxlY3QgY29tcG9uZW50IG1ldGFkYXRhOicsIGVycm9yKTtcbiAgICAgIHJldHVybiB7IGlucHV0czogW10gfTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldENvbXBvbmVudFR5cGUoZHluYW1pY0NvbXBvbmVudDogRHluYW1pY0NvbXBvbmVudERlZmluaXRpb24pOiBQcm9taXNlPFR5cGU8YW55Pj4ge1xuICAgIGlmIChpc0xhenlEeW5hbWljQ29tcG9uZW50cyhkeW5hbWljQ29tcG9uZW50KSkge1xuICAgICAgY29uc3QgY29tcG9uZW50UHJvbWlzZUZ1bmN0aW9uID1cbiAgICAgICAgdGhpcy5tb2RlID09PSAnY29tcG9uZW50J1xuICAgICAgICAgID8gZHluYW1pY0NvbXBvbmVudC5sb2FkQ29tcG9uZW50XG4gICAgICAgICAgOiBkeW5hbWljQ29tcG9uZW50LmxvYWRDb25maWdDb21wb25lbnQ7XG4gICAgICBpZiAoIWNvbXBvbmVudFByb21pc2VGdW5jdGlvbikge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICByZXR1cm4gYXdhaXQgY29tcG9uZW50UHJvbWlzZUZ1bmN0aW9uKCk7XG4gICAgfSBlbHNlIGlmIChpc0VhZ2VyRHluYW1pY0NvbXBvbmVudHMoZHluYW1pY0NvbXBvbmVudCkpIHtcbiAgICAgIHJldHVybiB0aGlzLm1vZGUgPT09ICdjb21wb25lbnQnXG4gICAgICAgID8gZHluYW1pY0NvbXBvbmVudC5jb21wb25lbnRcbiAgICAgICAgOiBkeW5hbWljQ29tcG9uZW50LmNvbmZpZ0NvbXBvbmVudDtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHN1YnNjcmliZUZvck92ZXJsYXlDaGFuZ2UoKSB7XG4gICAgdGhpcy5jb21wb25lbnRJbnN0YW5jZS5hbGVydHMuYW55QWxlcnRFeGlzdHMkXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAuc3Vic2NyaWJlKGFueUFsZXJ0c0V4aXN0cyA9PlxuICAgICAgICB0aGlzLnVwZGF0ZVdpZGdldENsYXNzZXMuZW1pdCh7ICdhbGVydHMtb3ZlcmxheSc6IGFueUFsZXJ0c0V4aXN0cyB9KVxuICAgICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgY2FsbE9uQmVmb3JlU2F2ZUhvb2soKSB7XG4gICAgaWYgKCF0aGlzLmNvbXBvbmVudFJlZikge1xuICAgICAgcmV0dXJuIG9mKHRydWUpO1xuICAgIH1cbiAgICBjb25zdCBob29rID0gKHRoaXMuY29tcG9uZW50UmVmLmluc3RhbmNlIGFzIE9uQmVmb3JlU2F2ZSkub25CZWZvcmVTYXZlO1xuICAgIGlmIChob29rKSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBob29rLmNhbGwodGhpcy5jb21wb25lbnRSZWYuaW5zdGFuY2UsIHRoaXMuY29uZmlnKTtcbiAgICAgIGlmIChpc1VuZGVmaW5lZChyZXN1bHQpKSB7XG4gICAgICAgIHJldHVybiBvZih0cnVlKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBpc09ic2VydmFibGUocmVzdWx0KSA/IHJlc3VsdCA6IChvZihyZXN1bHQpIGFzIE9ic2VydmFibGU8Ym9vbGVhbj4pO1xuICAgIH1cbiAgICByZXR1cm4gb2YodHJ1ZSk7XG4gIH1cbn1cbiIsIjxuZy10ZW1wbGF0ZSAjaG9zdD48L25nLXRlbXBsYXRlPlxuXG48ZGl2IGNsYXNzPVwiYWxlcnQgYWxlcnQtd2FybmluZyBtLThcIiByb2xlPVwiYWxlcnRcIiAqbmdJZj1cImVycm9yICYmIG5vdEZvdW5kRXJyb3JcIj5cbiAgPHN0cm9uZyBjbGFzcz1cIm1lc3NhZ2VcIj5cbiAgICB7e1xuICAgICAgJ1RoaXMgd2lkZ2V0IGNhbm5vdCBiZSByZW5kZXJlZCBiZWNhdXNlIHRoZSBjdXJyZW50IGFwcGxpY2F0aW9uIGRvZXMgbm90IHN1cHBvcnQgdGhlIGZvbGxvd2luZyBjb21wb25lbnQ6J1xuICAgICAgICB8IHRyYW5zbGF0ZVxuICAgIH19XG4gICAge3sgY29tcG9uZW50SWQgfX0uXG4gIDwvc3Ryb25nPlxuICA8cCBjbGFzcz1cInRleHQtbXV0ZWQgbS10LThcIj5cbiAgICA8YnV0dG9uIGNsYXNzPVwiYnRuIGJ0bi1jbGVhblwiIChjbGljayk9XCJleHBhbmRFcnJvckRldGFpbHMgPSAhZXhwYW5kRXJyb3JEZXRhaWxzXCI+XG4gICAgICA8aSBjOHlJY29uPVwiY2hldnJvbi1kb3duXCI+PC9pPlxuICAgICAgPHNwYW4gKm5nSWY9XCIhZXhwYW5kRXJyb3JEZXRhaWxzXCIgdHJhbnNsYXRlPlNob3cgZGV0YWlsczwvc3Bhbj5cbiAgICAgIDxzcGFuICpuZ0lmPVwiZXhwYW5kRXJyb3JEZXRhaWxzXCIgdHJhbnNsYXRlPkhpZGUgZGV0YWlsczwvc3Bhbj5cbiAgICA8L2J1dHRvbj5cbiAgPC9wPlxuICA8ZGl2IFtjb2xsYXBzZV09XCIhZXhwYW5kRXJyb3JEZXRhaWxzXCIgW2lzQW5pbWF0ZWRdPVwidHJ1ZVwiPlxuICAgIDxwcmU+XG4gICAgICB7eyBlcnJvciB9fVxuICAgIDwvcHJlPlxuICA8L2Rpdj5cbjwvZGl2PlxuXG48ZGl2XG4gIGNsYXNzPVwib3ZlcmxheS1jZW50ZXItdmVydGljYWxseSBkLWZsZXggZC1jb2wgYS1pLXN0cmV0Y2ggai1jLXN0YXJ0IHAtMTYgcC1yLTI0IHAtbC0yNFwiXG4gICpuZ0lmPVwiXG4gICAgZXJyb3JTdHJhdGVneSA9PT0gZHluYW1pY0NvbXBvbmVudEVycm9yU3RyYXRlZ3kuT1ZFUkxBWV9FUlJPUiAmJlxuICAgICAgY29tcG9uZW50SW5zdGFuY2U/LmFsZXJ0cz8uYW55QWxlcnRFeGlzdHMkIHwgYXN5bmNcbiAgXCJcbj5cbiAgPGM4eS1keW5hbWljLWNvbXBvbmVudC1hbGVydHMgW2FsZXJ0c109XCJjb21wb25lbnRJbnN0YW5jZS5hbGVydHNcIj48L2M4eS1keW5hbWljLWNvbXBvbmVudC1hbGVydHM+XG48L2Rpdj5cbiJdfQ==