UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

575 lines 67.7 kB
import { coerceNumberProperty } from '@angular/cdk/coercion'; import { ChangeDetectorRef, Directive, EventEmitter, Input, Output, TemplateRef, ViewContainerRef } from '@angular/core'; import { assign, get } from 'lodash-es'; import { combineLatest, isObservable, of, pipe, Subject } from 'rxjs'; import { filter, map, takeUntil, tap } from 'rxjs/operators'; import { LoadMoreComponent } from './load-more.component'; import { VirtualScrollerWrapperComponent } from './virtual-scroll/virtual-scroller-wrapper.component'; import * as i0 from "@angular/core"; /** * A directive to iterate over IResultList<T> data from @c8y/client. * Depending on the [c8yForLoadMore] a load more button is: * - auto: Tries to automatically load more data (default maximum 10 iterations; can be * change with maxIterations settings). * - show: Shows a load more button for the user to decide * - none: Doesn't perform any load more action. * - hidden: Loads more data automatically but with no visible button for the user. * * Additional, any rxjs operator pipe can be applied to the [c8yForPipe] input, e.g. to * filter the data displayed currently as well as the data loaded by subsequent requests. * * ```html * <div *c8yFor="let device of devices; loadMore: 'auto'; let i = index; pipe: filterPipe;"> * {{ i + 1 }}. {{device.name}} * </div> * ``` * The above example will list all entities that are applied to `devices`: * ```typescript * this.devices = this.inventoryService.list({ pageSize: 10, fragmentType: 'c8y_IsDevice' }) * ``` * It will display the first 10 items, if there is more space left on the screen, and there are more * than 10 devices, it will automatically load up to 10 pages more. If it still can't fit the screen * it will stop and switch to `show` mode. * * A pipe can be applied e.g. for filtering or grouping. This pipe is attached to every follow up * request done by the load more component: * ```typescript * this.filterPipe = pipe( * map((data: []) => { * return data.filter( * (mo: any) => mo.name && mo.name.toLowerCase().indexOf(value.toLowerCase()) > -1 * ); * }) * ); * ``` * The pipe must be an rxjs pipe and can take any operator. * * Example with realtime support and items count output (e.g. for handling empty state and header): * * ```html * <c8y-list-group> * <div class="c8y-empty-state" *ngIf="count === 0"> * (...) * </div> * * <div class="page-sticky-header hidden-xs c8y-list__item c8y-list--timeline" *ngIf="count > 0"> * (...) * </div> * * <ng-template * c8yFor * let-operation * [c8yForOf]="items$" * [c8yForPipe]="filterPipe" * [c8yForRealtime]="realtime" * [c8yForRealtimeOptions]="realtimeOptions" * (c8yForCount)="count = $event" * > * <c8y-li-timeline> * (...) * </c8y-li-timeline> * </ng-template> * </c8y-list-group> * ``` * * ```typescript * @Component({ * (...) * }) * export class ExampleComponent { * @Input() deviceId: IIdentified; * items$ = this.operationService.list({ * deviceId: this.deviceId, * fragmentType: 'c8y_MyOperation', * dateFrom: new Date(0).toISOString(), * dateTo: new Date(Date.now()).toISOString(), * revert: true, * withTotalPages: true * }); * filterPipe = pipe(map((ops: IOperation[]) => ops.filter(op => op.c8y_MyOperation))); * realtimeOptions: ForOfRealtimeOptions = { * entityOrId: this.deviceId, * removeOnUpdate: true, * insertOnUpdate: true * } as ForOfRealtimeOptions; * count: number; * * constructor( * private operationService: OperationService, * public realtime: OperationRealtimeService * ) {} * } * ``` */ export class ForOfDirective { get shouldUseLoadMoreButton() { return (this.loadMoreMode === 'auto' || this.loadMoreMode === 'show' || this.loadMoreMode === 'hidden'); } get hasMoreData() { return this.loadMore && this.loadMore.hasMore; } get length() { return this.cachedData.length; } /** * The data setter. Must be a response from @c8y/data or an observable. * You can pass an observable with null to explicitly clear the list. */ set c8yForOf(fetchData) { if (fetchData) { this.obs$ = (isObservable(fetchData) ? fetchData : of(fetchData)).pipe(map(result => { if (result === null) { this.paging = null; return []; } const { paging, data } = result; this.paging = paging; return data; })); } } /** * The mode setter: * - auto: Tries to automatically load more data (default maximum 10 iterations; can be * change with maxIterations settings). * - show: Shows a load more button for the user to decide * - none: Doesn't perform any load more action. * - hidden: Loads more data automatically but with no visible button for the user. */ set c8yForLoadMore(type) { this.loadMoreMode = type; } /** * The pipe setter to attach any rxjs pipe to the current and more loaded data. */ set c8yForPipe(dataPipe) { if (dataPipe) { this.dataPipe = dataPipe; } } /** * A template to use if no data is found at all (e.g. if you apply a filter pipe). */ set c8yForNotFound(notFoundTemplate) { this.notFoundTemplate = notFoundTemplate; if (this.loadMore) { this.loadMore.noMoreDataHint = notFoundTemplate; } } /** * The maximum numbers of iterations to call data from the api. */ set c8yForMaxIterations(maxIterations) { this.maxIterations = maxIterations; } /** * A custom loading component. */ set c8yForLoadingTemplate(loadingTemplate) { this.loadingTemplate = loadingTemplate; } /** * Load next text label. */ set c8yForLoadNextLabel(loadNextLabel) { this.loadNextLabel = loadNextLabel; } /** * Loading text label. */ set c8yForLoadingLabel(loadingLabel) { this.loadingLabel = loadingLabel; } /** * A RealtimeService instance. */ set c8yForRealtime(source) { this.realtime = source; } /** * Realtime options. */ set c8yForRealtimeOptions(realtimeOptions) { this.realtimeOptions = realtimeOptions; } /** * A comparator function for comparing list items. Used to determine * the position at which a new element should be added to the list. */ set c8yForComparator(comparator) { this.comparator = comparator; } constructor(tpl, vcr, cdRef) { this.tpl = tpl; this.vcr = vcr; this.cdRef = cdRef; this.cachedData = []; this.loadMoreMode = 'auto'; this.dataPipe = pipe(tap()); this.itemDataPipe = pipe(map(item => [item]), src => this.dataPipe(src), map(([item]) => item)); this.maxIterations = 10; this.realtimeOptions = {}; this.unsubscribe$ = new Subject(); /** * Enable virtual scroll rendering method. */ this.c8yForEnableVirtualScroll = false; /** * Sets mode of virtual scroller instance. * window is used for case when whole viewport is scrolled. * fixed can be used on inner-scroll containers. */ this.c8yForVirtualScrollStrategy = 'window'; /** * The number of items currently loaded in the list. * * Note: This can only be used if the forOf isn't used with * the sugared asterisk (*) syntax. Instead you need to use an ng-template: * ```html * <ng-template * c8yFor * let-operation * [c8yForOf]="operations$" * (c8yForCount)="operationCount = $event" * > * </ng-template> * ``` */ this.c8yForCount = new EventEmitter(); /** * The items change event emitting the newly loaded items. * * Note: This can only be used if the forOf isn't used with * the sugared asterisk (*) syntax. Instead you need to use an ng-template: * ```html * <ng-template * c8yFor * let-operation * [c8yForOf]="operations$" * (c8yForChange)="onChangeForOf($event)" * > * </ng-template> * ``` */ this.c8yForChange = new EventEmitter(); /** * The current instance of the `LoadMoreComponent`. */ this.c8yForLoadMoreComponent = new EventEmitter(); this.count = 0; } ngOnInit() { this.handleRealtime(); } ngOnChanges(changes) { if (this.obs$ && (changes.c8yForPipe || changes.c8yForOf)) { this.unsubscribePaging(); if (this.virtualScrollInstance) { this.virtualScrollInstance.filterPipe = this.dataPipe; window.scrollTo({ top: 0 }); } // only re-rendering on filtering if all data is already loaded // from the backend const reRender = !this.hasMoreData && !!changes.c8yForPipe && !changes.c8yForOf; if (reRender) { this.obs$ = of(this.cachedData); } this.pagingSub = this.obs$ .pipe(tap(data => { if (!reRender) { this.cachedData = data; } })) .pipe(src => this.dataPipe(src)) .subscribe((data) => { this.updateCount(data.length); this.c8yForChange.emit(data); this.render(data, reRender); }); } } ngOnDestroy() { this.unsubscribePaging(); this.unsubscribe$.next(); this.unsubscribe$.complete(); } handleRealtime() { if (this.realtime) { this.handleRealtimeCreate(); this.handleRealtimeUpdate(); this.handleRealtimeDelete(); } } /** * On create notification: * - if item passes data pipe, then insert it. * @private */ handleRealtimeCreate() { const { entityOrId } = this.realtimeOptions; this.realtime .onCreate$(entityOrId) .pipe(item$ => this.itemDataPipe(item$), filter(item => !!item), takeUntil(this.unsubscribe$)) .subscribe(item => this.insert(item)); } /** * On update notification: * - if item is displayed and passes data pipe, then update it, * - if item is displayed and doesn't pass data pipe, then remove it (if `removeOnUpdate` is true), * - if item is not displayed and passes data pipe, then insert it (if `insertOnUpdate` is true), * - if item is not displayed and doesn't pass data pipe, then ignore it. * @private */ handleRealtimeUpdate() { const { entityOrId } = this.realtimeOptions; this.realtime .onUpdate$(entityOrId) .pipe(item$ => combineLatest([ item$, item$.pipe(src => this.itemDataPipe(src), map(item => item !== undefined)) ]), takeUntil(this.unsubscribe$)) .subscribe(([item, passesDataPipe]) => { const { insertOnUpdate, removeOnUpdate } = this.realtimeOptions; const displayed = this.isDisplayed(coerceNumberProperty(item.id)); if (displayed) { if (passesDataPipe) { this.update(item); } else if (removeOnUpdate) { this.remove(coerceNumberProperty(item.id)); } } else if (passesDataPipe && insertOnUpdate) { this.insert(item); } }); } /** * On delete notification: * - remove item from the list (if not there, it will be just ignored). * @private */ handleRealtimeDelete() { const { entityOrId } = this.realtimeOptions; this.realtime .onDelete$(entityOrId) .pipe(takeUntil(this.unsubscribe$)) .subscribe(id => this.remove(coerceNumberProperty(id))); } render(data, reRender = false) { if (this.c8yForEnableVirtualScroll) { if (!this.virtualScrollInstance) { this.virtualScrollInstance = this.createVirtualScrollWrapperComponent(); if (this.shouldUseLoadMoreButton) { this.loadMore = this.createLoadMoreButtonComponent(false); } } this.setVirtualScrollContents(data); return; } this.vcr.clear(); data.forEach((item, index) => { const context = { $implicit: item, index, length: this.length, hasMore: this.hasMoreData, loadMoreComponent: this.loadMore }; this.vcr.createEmbeddedView(this.tpl, context); }); if (this.shouldUseLoadMoreButton) { this.loadMore = this.createLoadMoreButtonComponent(reRender); } } append(data) { if (this.c8yForEnableVirtualScroll) { this.appendVirtualScrollContent(data); return; } data.forEach(item => { const index = this.shouldUseLoadMoreButton ? this.vcr.length - 1 : this.vcr.length; const context = { $implicit: item, index, length: this.length, hasMore: this.hasMoreData, loadMoreComponent: this.loadMore }; this.vcr.createEmbeddedView(this.tpl, context, index); }); } loadMoreData(data) { if (data.length > 0) { this.updateCount(data.length); this.c8yForChange.emit(data); this.append(data); } this.cdRef.detectChanges(); } createLoadMoreButtonComponent(reRender) { const componentRef = this.vcr.createComponent(LoadMoreComponent); const instance = componentRef.instance; instance.paging = this.paging; instance.useIntersection = this.loadMoreMode === 'auto' || this.loadMoreMode === 'hidden'; instance.hidden = this.loadMoreMode === 'hidden'; instance.maxIterations = this.maxIterations; instance.noMoreDataHint = this.notFoundTemplate; instance.loadingTemplate = this.loadingTemplate; instance.loadNextLabel = this.loadNextLabel; instance.loadingLabel = this.loadingLabel; this.c8yForLoadMoreComponent.emit(instance); this.pagingSub = instance.onLoad .pipe(map((data) => this.checkForDuplicates(data)), tap((data) => { this.cachedData = this.cachedData.concat(data); })) .pipe(src => this.dataPipe(src)) .subscribe(data => this.loadMoreData(data)); if (reRender) { assign(instance, this.loadMore); } return instance; } createVirtualScrollWrapperComponent() { const componentRef = this.vcr.createComponent(VirtualScrollerWrapperComponent); const instance = componentRef.instance; instance.items = this.cachedData; instance.itemHeight = this.c8yForVirtualScrollElementSize; instance.template = this.tpl; instance.strategy = this.c8yForVirtualScrollStrategy; instance.containerHeight = this.c8yForVirtualScrollContainerHeight; return instance; } insert(item) { let index = 0; if (this.comparator && this.cachedData.length) { let comparisionResult; do { const view = this.vcr.get(index); const itemB = get(view, 'context.$implicit'); comparisionResult = item && itemB ? this.comparator(item, itemB) : 0; if (comparisionResult <= 0) { index++; } } while (comparisionResult <= 0 && index < this.cachedData.length); } // Do not append elements after the last one currently loaded, // as it may belong further down there on the list and will // be eventually loaded with one of the next pages. if (index < this.cachedData.length || this.cachedData.length === 0) { const context = { $implicit: item, index, length: this.length, hasMore: this.hasMoreData }; this.cachedData.splice(index, 0, item); const viewRef = this.tpl.createEmbeddedView(context); this.vcr.insert(viewRef, index); this.updateCount(1); } } update(updatedItem) { this.forMatchingEmbeddedViewRef((item) => item && updatedItem && item.id === updatedItem.id, (view) => { view.context.$implicit = updatedItem; view.markForCheck(); }); } remove(idToRemove) { if (this.isDisplayed(idToRemove)) { this.updateCount(-1); } const index = this.cachedData.findIndex(op => op.id === idToRemove); this.cachedData.splice(index, 1); this.forMatchingEmbeddedViewRef((item) => item && coerceNumberProperty(item.id, NaN) === idToRemove, (view) => view.destroy()); this.c8yForChange.emit([...this.cachedData]); this.cdRef.detectChanges(); } updateCount(countChange) { this.count += countChange; this.c8yForCount.emit(this.count); } isDisplayed(idToCheck) { let displayed = false; this.forMatchingEmbeddedViewRef((item) => item && coerceNumberProperty(item.id, NaN) === idToCheck, () => { displayed = true; }); return displayed; } forMatchingEmbeddedViewRef(filterFn, callbackFn) { for (let i = 0; i < this.vcr.length; i++) { const view = this.vcr.get(i); const item = get(view, 'context.$implicit'); if (filterFn(item)) { callbackFn(view); } } } checkForDuplicates(data) { return this.realtime ? data.filter(item => !this.cachedData.some(cached => cached.id === item.id)) : data; } unsubscribePaging() { if (this.pagingSub) { this.pagingSub.unsubscribe(); } } setVirtualScrollContents(items) { if (this.c8yForEnableVirtualScroll && this.virtualScrollInstance) { this.virtualScrollInstance.items = items; this.virtualScrollInstance.apply(); } } appendVirtualScrollContent(items) { if (this.c8yForEnableVirtualScroll && this.virtualScrollInstance) { this.virtualScrollInstance.items = this.virtualScrollInstance.items.concat(items); this.virtualScrollInstance.apply(); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ForOfDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: ForOfDirective, selector: "[c8yFor]", inputs: { c8yForOf: "c8yForOf", c8yForLoadMore: "c8yForLoadMore", c8yForPipe: "c8yForPipe", c8yForNotFound: "c8yForNotFound", c8yForMaxIterations: "c8yForMaxIterations", c8yForLoadingTemplate: "c8yForLoadingTemplate", c8yForLoadNextLabel: "c8yForLoadNextLabel", c8yForLoadingLabel: "c8yForLoadingLabel", c8yForRealtime: "c8yForRealtime", c8yForRealtimeOptions: "c8yForRealtimeOptions", c8yForComparator: "c8yForComparator", c8yForEnableVirtualScroll: "c8yForEnableVirtualScroll", c8yForVirtualScrollElementSize: "c8yForVirtualScrollElementSize", c8yForVirtualScrollStrategy: "c8yForVirtualScrollStrategy", c8yForVirtualScrollContainerHeight: "c8yForVirtualScrollContainerHeight" }, outputs: { c8yForCount: "c8yForCount", c8yForChange: "c8yForChange", c8yForLoadMoreComponent: "c8yForLoadMoreComponent" }, usesOnChanges: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ForOfDirective, decorators: [{ type: Directive, args: [{ selector: '[c8yFor]' }] }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }], propDecorators: { c8yForOf: [{ type: Input }], c8yForLoadMore: [{ type: Input }], c8yForPipe: [{ type: Input }], c8yForNotFound: [{ type: Input }], c8yForMaxIterations: [{ type: Input }], c8yForLoadingTemplate: [{ type: Input }], c8yForLoadNextLabel: [{ type: Input }], c8yForLoadingLabel: [{ type: Input }], c8yForRealtime: [{ type: Input }], c8yForRealtimeOptions: [{ type: Input }], c8yForComparator: [{ type: Input }], c8yForEnableVirtualScroll: [{ type: Input }], c8yForVirtualScrollElementSize: [{ type: Input }], c8yForVirtualScrollStrategy: [{ type: Input }], c8yForVirtualScrollContainerHeight: [{ type: Input }], c8yForCount: [{ type: Output }], c8yForChange: [{ type: Output }], c8yForLoadMoreComponent: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9yT2YuZGlyZWN0aXZlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vY29yZS9jb21tb24vZm9yT2YuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzdELE9BQU8sRUFDTCxpQkFBaUIsRUFDakIsU0FBUyxFQUVULFlBQVksRUFDWixLQUFLLEVBQ0wsTUFBTSxFQUVOLFdBQVcsRUFDWCxnQkFBZ0IsRUFFakIsTUFBTSxlQUFlLENBQUM7QUFFdkIsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDeEMsT0FBTyxFQUNMLGFBQWEsRUFDYixZQUFZLEVBRVosRUFBRSxFQUNGLElBQUksRUFDSixPQUFPLEVBR1IsTUFBTSxNQUFNLENBQUM7QUFDZCxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFHN0QsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFMUQsT0FBTyxFQUFFLCtCQUErQixFQUFFLE1BQU0scURBQXFELENBQUM7O0FBRXRHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBOEZHO0FBSUgsTUFBTSxPQUFPLGNBQWM7SUF3QnpCLElBQVksdUJBQXVCO1FBQ2pDLE9BQU8sQ0FDTCxJQUFJLENBQUMsWUFBWSxLQUFLLE1BQU0sSUFBSSxJQUFJLENBQUMsWUFBWSxLQUFLLE1BQU0sSUFBSSxJQUFJLENBQUMsWUFBWSxLQUFLLFFBQVEsQ0FDL0YsQ0FBQztJQUNKLENBQUM7SUFFRCxJQUFZLFdBQVc7UUFDckIsT0FBTyxJQUFJLENBQUMsUUFBUSxJQUFJLElBQUks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QkFBcUIsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNyQyxDQUFDO0lBQ0gsQ0FBQztJQUVPLDBCQUEwQixDQUFDLEtBQUs7UUFDdEMsSUFBSSxJQUFJLENBQUMseUJBQXlCLElBQUksSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7WUFDakUsSUFBSSxDQUFDLHFCQUFxQixDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMscUJBQXFCLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNsRixJQUFJLENBQUMscUJBQXFCLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckMsQ0FBQztJQUNILENBQUM7K0dBNWlCVSxjQUFjO21HQUFkLGNBQWM7OzRGQUFkLGNBQWM7a0JBSDFCLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLFVBQVU7aUJBQ3JCOytJQTRDSyxRQUFRO3NCQURYLEtBQUs7Z0JBMEJGLGNBQWM7c0JBRGpCLEtBQUs7Z0JBU0YsVUFBVTtzQkFEYixLQUFLO2dCQVdGLGNBQWM7c0JBRGpCLEtBQUs7Z0JBWUYsbUJBQW1CO3NCQUR0QixLQUFLO2dCQVNGLHFCQUFxQjtzQkFEeEIsS0FBSztnQkFTRixtQkFBbUI7c0JBRHRCLEtBQUs7Z0JBU0Ysa0JBQWtCO3NCQURyQixLQUFLO2dCQVNGLGNBQWM7c0JBRGpCLEtBQUs7Z0JBU0YscUJBQXFCO3NCQUR4QixLQUFLO2dCQVVGLGdCQUFnQjtzQkFEbkIsS0FBSztnQkFTTix5QkFBeUI7c0JBRHhCLEtBQUs7Z0JBT04sOEJBQThCO3NCQUQ3QixLQUFLO2dCQVNOLDJCQUEyQjtzQkFEMUIsS0FBSztnQkFPTixrQ0FBa0M7c0JBRGpDLEtBQUs7Z0JBbUJFLFdBQVc7c0JBRGxCLE1BQU07Z0JBbUJDLFlBQVk7c0JBRG5CLE1BQU07Z0JBT0MsdUJBQXVCO3NCQUQ5QixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY29lcmNlTnVtYmVyUHJvcGVydHkgfSBmcm9tICdAYW5ndWxhci9jZGsvY29lcmNpb24nO1xuaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gIERpcmVjdGl2ZSxcbiAgRW1iZWRkZWRWaWV3UmVmLFxuICBFdmVudEVtaXR0ZXIsXG4gIElucHV0LFxuICBPdXRwdXQsXG4gIFNpbXBsZUNoYW5nZXMsXG4gIFRlbXBsYXRlUmVmLFxuICBWaWV3Q29udGFpbmVyUmVmLFxuICBWaWV3UmVmXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSUlkZW50aWZpZWQsIElSZXN1bHRMaXN0LCBQYWdpbmcgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQgeyBhc3NpZ24sIGdldCB9IGZyb20gJ2xvZGFzaC1lcyc7XG5pbXBvcnQge1xuICBjb21iaW5lTGF0ZXN0LFxuICBpc09ic2VydmFibGUsXG4gIE9ic2VydmFibGUsXG4gIG9mLFxuICBwaXBlLFxuICBTdWJqZWN0LFxuICBTdWJzY3JpcHRpb24sXG4gIFVuYXJ5RnVuY3Rpb25cbn0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBmaWx0ZXIsIG1hcCwgdGFrZVVudGlsLCB0YXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBSZWFsdGltZVNlcnZpY2UgfSBmcm9tICcuLi9yZWFsdGltZS9yZWFsdGltZS5zZXJ2aWNlJztcbmltcG9ydCB7IEZvck9mRmlsdGVyUGlwZSwgRm9yT2ZSZWFsdGltZU9wdGlvbnMgfSBmcm9tICcuL2Zvck9mLm1vZGVsJztcbmltcG9ydCB7IExvYWRNb3JlQ29tcG9uZW50IH0gZnJvbSAnLi9sb2FkLW1vcmUuY29tcG9uZW50JztcbmltcG9ydCB7IExvYWRNb3JlTW9kZSB9IGZyb20gJy4vbG9hZC1tb3JlLm1vZGVsJztcbmltcG9ydCB7IFZpcnR1YWxTY3JvbGxlcldyYXBwZXJDb21wb25lbnQgfSBmcm9tICcuL3ZpcnR1YWwtc2Nyb2xsL3ZpcnR1YWwtc2Nyb2xsZXItd3JhcHBlci5jb21wb25lbnQnO1xuXG4vKipcbiAqIEEgZGlyZWN0aXZlIHRvIGl0ZXJhdGUgb3ZlciBJUmVzdWx0TGlzdDxUPiBkYXRhIGZyb20gQGM4eS9jbGllbnQuXG4gKiBEZXBlbmRpbmcgb24gdGhlIFtjOHlGb3JMb2FkTW9yZV0gYSBsb2FkIG1vcmUgYnV0dG9uIGlzOlxuICogIC0gYXV0bzogVHJpZXMgdG8gYXV0b21hdGljYWxseSBsb2FkIG1vcmUgZGF0YSAoZGVmYXVsdCBtYXhpbXVtIDEwIGl0ZXJhdGlvbnM7IGNhbiBiZVxuICogICAgICAgICAgY2hhbmdlIHdpdGggbWF4SXRlcmF0aW9ucyBzZXR0aW5ncykuXG4gKiAgLSBzaG93OiBTaG93cyBhIGxvYWQgbW9yZSBidXR0b24gZm9yIHRoZSB1c2VyIHRvIGRlY2lkZVxuICogIC0gbm9uZTogRG9lc24ndCBwZXJmb3JtIGFueSBsb2FkIG1vcmUgYWN0aW9uLlxuICogIC0gaGlkZGVuOiBMb2FkcyBtb3JlIGRhdGEgYXV0b21hdGljYWxseSBidXQgd2l0aCBubyB2aXNpYmxlIGJ1dHRvbiBmb3IgdGhlIHVzZXIuXG4gKlxuICogQWRkaXRpb25hbCwgYW55IHJ4anMgb3BlcmF0b3IgcGlwZSBjYW4gYmUgYXBwbGllZCB0byB0aGUgW2M4eUZvclBpcGVdIGlucHV0LCBlLmcuIHRvXG4gKiBmaWx0ZXIgdGhlIGRhdGEgZGlzcGxheWVkIGN1cnJlbnRseSBhcyB3ZWxsIGFzIHRoZSBkYXRhIGxvYWRlZCBieSBzdWJzZXF1ZW50IHJlcXVlc3RzLlxuICpcbiAqIGBgYGh0bWxcbiAqIDxkaXYgKmM4eUZvcj1cImxldCBkZXZpY2Ugb2YgZGV2aWNlczsgbG9hZE1vcmU6ICdhdXRvJzsgbGV0IGkgPSBpbmRleDsgcGlwZTogZmlsdGVyUGlwZTtcIj5cbiAqICB7eyBpICsgMSB9fS4ge3tkZXZpY2UubmFtZX19XG4gKiA8L2Rpdj5cbiAqIGBgYFxuICogVGhlIGFib3ZlIGV4YW1wbGUgd2lsbCBsaXN0IGFsbCBlbnRpdGllcyB0aGF0IGFyZSBhcHBsaWVkIHRvIGBkZXZpY2VzYDpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIHRoaXMuZGV2aWNlcyA9IHRoaXMuaW52ZW50b3J5U2VydmljZS5saXN0KHsgcGFnZVNpemU6IDEwLCBmcmFnbWVudFR5cGU6ICdjOHlfSXNEZXZpY2UnIH0pXG4gKiBgYGBcbiAqIEl0IHdpbGwgZGlzcGxheSB0aGUgZmlyc3QgMTAgaXRlbXMsIGlmIHRoZXJlIGlzIG1vcmUgc3BhY2UgbGVmdCBvbiB0aGUgc2NyZWVuLCBhbmQgdGhlcmUgYXJlIG1vcmVcbiAqIHRoYW4gMTAgZGV2aWNlcywgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGxvYWQgdXAgdG8gMTAgcGFnZXMgbW9yZS4gSWYgaXQgc3RpbGwgY2FuJ3QgZml0IHRoZSBzY3JlZW5cbiAqIGl0IHdpbGwgc3RvcCBhbmQgc3dpdGNoIHRvIGBzaG93YCBtb2RlLlxuICpcbiAqIEEgcGlwZSBjYW4gYmUgYXBwbGllZCBlLmcuIGZvciBmaWx0ZXJpbmcgb3IgZ3JvdXBpbmcuIFRoaXMgcGlwZSBpcyBhdHRhY2hlZCB0byBldmVyeSBmb2xsb3cgdXBcbiAqIHJlcXVlc3QgZG9uZSBieSB0aGUgbG9hZCBtb3JlIGNvbXBvbmVudDpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIHRoaXMuZmlsdGVyUGlwZSA9IHBpcGUoXG4gKiAgICBtYXAoKGRhdGE6IFtdKSA9PiB7XG4gKiAgICAgcmV0dXJuIGRhdGEuZmlsdGVyKFxuICogICAgICAobW86IGFueSkgPT4gbW8ubmFtZSAmJiBtby5uYW1lLnRvTG93ZXJDYXNlKCkuaW5kZXhPZih2YWx1ZS50b0xvd2VyQ2FzZSgpKSA+IC0xXG4gKiAgICApO1xuICogIH0pXG4gKiApO1xuICogYGBgXG4gKiBUaGUgcGlwZSBtdXN0IGJlIGFuIHJ4anMgcGlwZSBhbmQgY2FuIHRha2UgYW55IG9wZXJhdG9yLlxuICpcbiAqIEV4YW1wbGUgd2l0aCByZWFsdGltZSBzdXBwb3J0IGFuZCBpdGVtcyBjb3VudCBvdXRwdXQgKGUuZy4gZm9yIGhhbmRsaW5nIGVtcHR5IHN0YXRlIGFuZCBoZWFkZXIpOlxuICpcbiAqIGBgYGh0bWxcbiAqIDxjOHktbGlzdC1ncm91cD5cbiAqICAgPGRpdiBjbGFzcz1cImM4eS1lbXB0eS1zdGF0ZVwiICpuZ0lmPVwiY291bnQgPT09IDBcIj5cbiAqICAgICAoLi4uKVxuICogICA8L2Rpdj5cbiAqXG4gKiAgIDxkaXYgY2xhc3M9XCJwYWdlLXN0aWNreS1oZWFkZXIgaGlkZGVuLXhzIGM4eS1saXN0X19pdGVtIGM4eS1saXN0LS10aW1lbGluZVwiICpuZ0lmPVwiY291bnQgPiAwXCI+XG4gKiAgICAgKC4uLilcbiAqICAgPC9kaXY+XG4gKlxuICogICA8bmctdGVtcGxhdGVcbiAqICAgICBjOHlGb3JcbiAqICAgICBsZXQtb3BlcmF0aW9uXG4gKiAgICAgW2M4eUZvck9mXT1cIml0ZW1zJFwiXG4gKiAgICAgW2M4eUZvclBpcGVdPVwiZmlsdGVyUGlwZVwiXG4gKiAgICAgW2M4eUZvclJlYWx0aW1lXT1cInJlYWx0aW1lXCJcbiAqICAgICBbYzh5Rm9yUmVhbHRpbWVPcHRpb25zXT1cInJlYWx0aW1lT3B0aW9uc1wiXG4gKiAgICAgKGM4eUZvckNvdW50KT1cImNvdW50ID0gJGV2ZW50XCJcbiAqICAgPlxuICogICAgIDxjOHktbGktdGltZWxpbmU+XG4gKiAgICAgICAoLi4uKVxuICogICAgIDwvYzh5LWxpLXRpbWVsaW5lPlxuICogICA8L25nLXRlbXBsYXRlPlxuICogPC9jOHktbGlzdC1ncm91cD5cbiAqIGBgYFxuICpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIEBDb21wb25lbnQoe1xuICogICAoLi4uKVxuICogfSlcbiAqIGV4cG9ydCBjbGFzcyBFeGFtcGxlQ29tcG9uZW50IHtcbiAqICAgQElucHV0KCkgZGV2aWNlSWQ6IElJZGVudGlmaWVkO1xuICogICBpdGVtcyQgPSB0aGlzLm9wZXJhdGlvblNlcnZpY2UubGlzdCh7XG4gKiAgICAgZGV2aWNlSWQ6IHRoaXMuZGV2aWNlSWQsXG4gKiAgICAgZnJhZ21lbnRUeXBlOiAnYzh5X015T3BlcmF0aW9uJyxcbiAqICAgICBkYXRlRnJvbTogbmV3IERhdGUoMCkudG9JU09TdHJpbmcoKSxcbiAqICAgICBkYXRlVG86IG5ldyBEYXRlKERhdGUubm93KCkpLnRvSVNPU3RyaW5nKCksXG4gKiAgICAgcmV2ZXJ0OiB0cnVlLFxuICogICAgIHdpdGhUb3RhbFBhZ2VzOiB0cnVlXG4gKiAgIH0pO1xuICogICBmaWx0ZXJQaXBlID0gcGlwZShtYXAoKG9wczogSU9wZXJhdGlvbltdKSA9PiBvcHMuZmlsdGVyKG9wID0+IG9wLmM4eV9NeU9wZXJhdGlvbikpKTtcbiAqICAgcmVhbHRpbWVPcHRpb25zOiBGb3JPZlJlYWx0aW1lT3B0aW9ucyA9IHtcbiAqICAgICBlbnRpdHlPcklkOiB0aGlzLmRldmljZUlkLFxuICogICAgIHJlbW92ZU9uVXBkYXRlOiB0cnVlLFxuICogICAgIGluc2VydE9uVXBkYXRlOiB0cnVlXG4gKiAgIH0gYXMgRm9yT2ZSZWFsdGltZU9wdGlvbnM7XG4gKiAgIGNvdW50OiBudW1iZXI7XG4gKlxuICogICBjb25zdHJ1Y3RvcihcbiAqICAgICBwcml2YXRlIG9wZXJhdGlvblNlcnZpY2U6IE9wZXJhdGlvblNlcnZpY2UsXG4gKiAgICAgcHVibGljIHJlYWx0aW1lOiBPcGVyYXRpb25SZWFsdGltZVNlcnZpY2VcbiAqICAgKSB7fVxuICogfVxuICogYGBgXG4gKi9cbkBEaXJlY3RpdmUoe1xuICBzZWxlY3RvcjogJ1tjOHlGb3JdJ1xufSlcbmV4cG9ydCBjbGFzcyBGb3JPZkRpcmVjdGl2ZSB7XG4gIHByaXZhdGUgY2FjaGVkRGF0YTogSUlkZW50aWZpZWRbXSA9IFtdO1xuICBwcml2YXRlIHBhZ2luZzogUGFnaW5nPElJZGVudGlmaWVkPjtcbiAgcHJpdmF0ZSBsb2FkTW9yZU1vZGU6IExvYWRNb3JlTW9kZSA9ICdhdXRvJztcbiAgcHJpdmF0ZSBkYXRhUGlwZTogRm9yT2ZGaWx0ZXJQaXBlID0gcGlwZSh0YXAoKSk7XG4gIHByaXZhdGUgaXRlbURhdGFQaXBlOiBVbmFyeUZ1bmN0aW9uPE9ic2VydmFibGU8SUlkZW50aWZpZWQ+LCBPYnNlcnZhYmxlPElJZGVudGlmaWVkPj4gPSBwaXBlKFxuICAgIG1hcChpdGVtID0+IFtpdGVtXSksXG4gICAgc3JjID0+IHRoaXMuZGF0YVBpcGUoc3JjKSxcbiAgICBtYXAoKFtpdGVtXSkgPT4gaXRlbSlcbiAgKTtcbiAgcHJpdmF0ZSBwYWdpbmdTdWI6IFN1YnNjcmlwdGlvbjtcbiAgcHJpdmF0ZSBvYnMkOiBPYnNlcnZhYmxlPElJZGVudGlmaWVkW10+O1xuICBwcml2YXRlIGxvYWRNb3JlOiBMb2FkTW9yZUNvbXBvbmVudDtcbiAgcHJpdmF0ZSBsb2FkaW5nVGVtcGxhdGU6IFRlbXBsYXRlUmVmPGFueT47XG4gIHByaXZhdGUgbWF4SXRlcmF0aW9ucyA9IDEwO1xuICBwcml2YXRlIG5vdEZvdW5kVGVtcGxhdGU6IFRlbXBsYXRlUmVmPGFueT47XG4gIHByaXZhdGUgbG9hZE5leHRMYWJlbDogc3RyaW5nO1xuICBwcml2YXRlIGxvYWRpbmdMYWJlbDogc3RyaW5nO1xuICBwcml2YXRlIHJlYWx0aW1lOiBSZWFsdGltZVNlcnZpY2U8SUlkZW50aWZpZWQ+O1xuICBwcml2YXRlIHJlYWx0aW1lT3B0aW9uczogRm9yT2ZSZWFsdGltZU9wdGlvbnMgPSB7fTtcbiAgcHJpdmF0ZSBjb21wYXJhdG9yOiAoaXRlbUE6IElJZGVudGlmaWVkLCBpdGVtQjogSUlkZW50aWZpZWQpID0+IG51bWJlcjtcbiAgcHJpdmF0ZSB1bnN1YnNjcmliZSQ6IFN1YmplY3Q8dm9pZD4gPSBuZXcgU3ViamVjdCgpO1xuICBwcml2YXRlIHZpcnR1YWxTY3JvbGxJbnN0YW5jZTogVmlydHVhbFNjcm9sbGVyV3JhcHBlckNvbXBvbmVudDtcblxuICBwcml2YXRlIGdldCBzaG91bGRVc2VMb2FkTW9yZUJ1dHRvbigpIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5sb2FkTW9yZU1vZGUgPT09ICdhdXRvJyB8fCB0aGlzLmxvYWRNb3JlTW9kZSA9PT0gJ3Nob3cnIHx8IHRoaXMubG9hZE1vcmVNb2RlID09PSAnaGlkZGVuJ1xuICAgICk7XG4gIH1cblxuICBwcml2YXRlIGdldCBoYXNNb3JlRGF0YSgpIHtcbiAgICByZXR1cm4gdGhpcy5sb2FkTW9yZSAmJiB0aGlzLmxvYWRNb3JlLmhhc01vcmU7XG4gIH1cblxuICBwcml2YXRlIGdldCBsZW5ndGgoKSB7XG4gICAgcmV0dXJuIHRoaXMuY2FjaGVkRGF0YS5sZW5ndGg7XG4gIH1cblxuICAvKipcbiAgICogVGhlIGRhdGEgc2V0dGVyLiBNdXN0IGJlIGEgcmVzcG9uc2UgZnJvbSBAYzh5L2RhdGEgb3IgYW4gb2JzZXJ2YWJsZS5cbiAgICogWW91IGNhbiBwYXNzIGFuIG9ic2VydmFibGUgd2l0aCBudWxsIHRvIGV4cGxpY2l0bHkgY2xlYXIgdGhlIGxpc3QuXG4gICAqL1xuICBASW5wdXQoKVxuICBzZXQgYzh5Rm9yT2YoZmV0Y2hEYXRhOiBJUmVzdWx0TGlzdDxJSWRlbnRpZmllZD4gfCBPYnNlcnZhYmxlPElSZXN1bHRMaXN0PElJZGVudGlmaWVkPj4pIHtcbiAgICBpZiAoZmV0Y2hEYXRhKSB7XG4gICAgICB0aGlzLm9icyQgPSAoaXNPYnNlcnZhYmxlKGZldGNoRGF0YSkgPyBmZXRjaERhdGEgOiBvZihmZXRjaERhdGEpKS5waXBlKFxuICAgICAgICBtYXAocmVzdWx0ID0+IHtcbiAgICAgICAgICBpZiAocmVzdWx0ID09PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLnBhZ2luZyA9IG51bGw7XG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IHsgcGFnaW5nLCBkYXRhIH0gPSByZXN1bHQ7XG4gICAgICAgICAgdGhpcy5wYWdpbmcgPSBwYWdpbmc7XG4gICAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICAgIH0pXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgbW9kZSBzZXR0ZXI6XG4gICAqICAtIGF1dG86IFRyaWVzIHRvIGF1dG9tYXRpY2FsbHkgbG9hZCBtb3JlIGRhdGEgKGRlZmF1bHQgbWF4aW11bSAxMCBpdGVyYXRpb25zOyBjYW4gYmVcbiAgICogICAgICAgICAgY2hhbmdlIHdpdGggbWF4SXRlcmF0aW9ucyBzZXR0aW5ncykuXG4gICAqICAtIHNob3c6IFNob3dzIGEgbG9hZCBtb3JlIGJ1dHRvbiBmb3IgdGhlIHVzZXIgdG8gZGVjaWRlXG4gICAqICAtIG5vbmU6IERvZXNuJ3QgcGVyZm9ybSBhbnkgbG9hZCBtb3JlIGFjdGlvbi5cbiAgICogIC0gaGlkZGVuOiBMb2FkcyBtb3JlIGRhdGEgYXV0b21hdGljYWxseSBidXQgd2l0aCBubyB2aXNpYmxlIGJ1dHRvbiBmb3IgdGhlIHVzZXIuXG4gICAqL1xuICBASW5wdXQoKVxuICBzZXQgYzh5Rm9yTG9hZE1vcmUodHlwZTogRm9yT2ZEaXJlY3RpdmVbJ2xvYWRNb3JlTW9kZSddKSB7XG4gICAgdGhpcy5sb2FkTW9yZU1vZGUgPSB0eXBlO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBwaXBlIHNldHRlciB0byBhdHRhY2ggYW55IHJ4anMgcGlwZSB0byB0aGUgY3VycmVudCBhbmQgbW9yZSBsb2FkZWQgZGF0YS5cbiAgICovXG4gIEBJbnB1dCgpXG4gIHNldCBjOHlGb3JQaXBlKGRhdGFQaXBlOiBGb3JPZkRpcmVjdGl2ZVsnZGF0YVBpcGUnXSkge1xuICAgIGlmIChkYXRhUGlwZSkge1xuICAgICAgdGhpcy5kYXRhUGlwZSA9IGRhdGFQaXBlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBIHRlbXBsYXRlIHRvIHVzZSBpZiBubyBkYXRhIGlzIGZvdW5kIGF0IGFsbCAoZS5nLiBpZiB5b3UgYXBwbHkgYSBmaWx0ZXIgcGlwZSkuXG4gICAqL1xuICBASW5wdXQoKVxuICBzZXQgYzh5Rm9yTm90Rm91bmQobm90Rm91bmRUZW1wbGF0ZTogRm9yT2ZEaXJlY3RpdmVbJ25vdEZvdW5kVGVtcGxhdGUnXSkge1xuICAgIHRoaXMubm90Rm91bmRUZW1wbGF0ZSA9IG5vdEZvdW5kVGVtcGxhdGU7XG4gICAgaWYgKHRoaXMubG9hZE1vcmUpIHtcbiAgICAgIHRoaXMubG9hZE1vcmUubm9Nb3JlRGF0YUhpbnQgPSBub3RGb3VuZFRlbXBsYXRlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgbWF4aW11bSBudW1iZXJzIG9mIGl0ZXJhdGlvbnMgdG8gY2FsbCBkYXRhIGZyb20gdGhlIGFwaS5cbiAgICovXG4gIEBJbnB1dCgpXG4gIHNldCBjOHlGb3JNYXhJdGVyYXRpb25zKG1heEl0ZXJhdGlvbnM6IG51bWJlcikge1xuICAgIHRoaXMubWF4SXRlcmF0aW9ucyA9IG1heEl0ZXJhdGlvbnM7XG4gIH1cblxuICAvKipcbiAgICogQSBjdXN0b20gbG9hZGluZyBjb21wb25lbnQuXG4gICAqL1x