@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
575 lines • 67.7 kB
JavaScript
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