UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

130 lines 42.6 kB
import { Component, QueryList, ViewChild, ViewChildren } from '@angular/core'; import { DatePickerComponent, OperationBulkRealtimeService } from '@c8y/ngx-components'; import { BulkOperationListItemComponent, BULK_OPERATION_STATUS_OPTIONS } from '@c8y/ngx-components/operations/bulk-operation-list-item'; import { BulkOperationsService } from '@c8y/ngx-components/operations/bulk-operations-service'; import { ACTIONS_OPERATIONS_BULK, BULK_OPERATION_EVENT } from '@c8y/ngx-components/operations/product-experience'; import { StatusFilterComponent } from '@c8y/ngx-components/operations/status-filter'; import { flatten } from 'lodash-es'; import { BehaviorSubject, combineLatest, pipe } from 'rxjs'; import { map, shareReplay, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import { BulkOperationModalsService } from './modals/bulk-operation-modals.service'; import * as i0 from "@angular/core"; import * as i1 from "@c8y/ngx-components"; import * as i2 from "@c8y/ngx-components/operations/bulk-operations-service"; import * as i3 from "./modals/bulk-operation-modals.service"; import * as i4 from "@angular/common"; import * as i5 from "@c8y/ngx-components/operations/status-filter"; import * as i6 from "@c8y/ngx-components/operations/bulk-operation-list-item"; export class BulkOperationsListComponent { constructor(realtime, bulkOperationsService, bulkOperationModalsService) { this.realtime = realtime; this.bulkOperationsService = bulkOperationsService; this.bulkOperationModalsService = bulkOperationModalsService; this.bulkTypes = []; this.selectedTypeFilters = this.getTypeFilters(); this.bulkOperationStatusOptions = BULK_OPERATION_STATUS_OPTIONS; this.BULK_OPERATION_EVENT = BULK_OPERATION_EVENT; this.bulkActions = ACTIONS_OPERATIONS_BULK; this.refreshLoading = false; this.statusFilter$ = new BehaviorSubject(null); this.typeFilter$ = new BehaviorSubject(null); this.timeFilter$ = new BehaviorSubject(null); this.reload$ = new BehaviorSubject(null); this.bulkOperations$ = combineLatest(this.statusFilter$, this.timeFilter$, this.typeFilter$, this.reload$).pipe(tap(() => { this.refreshLoading = true; }), switchMap(([statusFilters, timeFilters]) => this.filter(statusFilters, timeFilters)), withLatestFrom(this.typeFilter$), map(([result, typeFilter]) => { this.filterPipe = pipe(map(data => this.filterByType(data, typeFilter))); return { ...result, data: this.filterByType(result.data, typeFilter) }; }), tap(() => { this.refreshLoading = false; }), shareReplay(1)); this.allFilterFragments = this.flattenFilterFragments(this.getTypeFilters()); } ngOnInit() { this.bulkTypes = this.bulkOperationsService.getBulkTypes(); } filterByType(bulkOperations, typeFilter) { const flattenedFragments = this.flattenFilterFragments(typeFilter); if ( // return data unfiltered if no filters selected... !flattenedFragments.length || // ...or when all filters are selected this.allFilterFragments.every(fragment => flattenedFragments.includes(fragment))) { return bulkOperations; } const filteredData = bulkOperations.filter(item => { return Object.keys(item.operationPrototype).some(key => flattenedFragments.includes(key)); }); return filteredData; } resetFilter() { this.statusFilter$.next(null); this.timeFilter$.next(null); this.typeFilter$.next(null); this.datePicker.clearFilter(); this.selectedTypeFilters = this.getTypeFilters(); this.statusFilter.reset(); } isFilterApplied() { return (!!this.statusFilter$.getValue()?.length || !!this.typeFilter$.getValue()?.length || !!this.timeFilter$.getValue()); } filter(statusFilters, timeFilter) { const status = statusFilters && statusFilters.length > 0 ? { generalStatus: flatten(statusFilters.map(statusFilter => statusFilter.generalStatuses)) } : {}; const time = timeFilter ? { ...(timeFilter.dateFrom && { dateFrom: timeFilter.dateFrom.toISOString() }), ...(timeFilter.dateTo && { dateTo: timeFilter.dateTo.toISOString() }) } : {}; return this.getBulkOperations({ ...status, ...time }); } getBulkOperations(filter) { return this.bulkOperationsService.getBulkOperations(filter); } getTypeFilters() { return this.bulkOperationsService.getBulkTypes(); } addBulkOperation() { this.bulkOperationModalsService.showNewBulkOperationModal(); } openFailedOperation(failedParentId) { this.listItems.forEach(item => { if (item.bulkOperation.id === failedParentId) { item.listItem.collapsed = false; item.listItem.element.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }); } compareOperations(operationA, operationB) { return new Date(operationA.startDate).getTime() - new Date(operationB.startDate).getTime(); } flattenFilterFragments(filters) { return (filters || []).reduce((flattened, current) => flattened.concat(current.fragments), []); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BulkOperationsListComponent, deps: [{ token: i1.OperationBulkRealtimeService }, { token: i2.BulkOperationsService }, { token: i3.BulkOperationModalsService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: BulkOperationsListComponent, selector: "c8y-bulk-operations", providers: [OperationBulkRealtimeService], viewQueries: [{ propertyName: "statusFilter", first: true, predicate: ["statusFilter"], descendants: true, static: true }, { propertyName: "datePicker", first: true, predicate: DatePickerComponent, descendants: true, static: true }, { propertyName: "listItems", predicate: BulkOperationListItemComponent, descendants: true }], ngImport: i0, template: "<c8y-title>{{ 'Bulk operations' | translate }}</c8y-title>\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-overviews'\"\n [label]=\"'Overviews' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-device-control'\"\n [label]=\"'Device control' | translate\"\n [path]=\"'devicecontrol/single'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-energy'\"\n [label]=\"'Bulk operations' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n *ngIf=\"bulkTypes?.length\"\n itemClass=\"navbar-form\"\n [placement]=\"'left'\"\n>\n <label\n class=\"hidden-sm hidden-xs\"\n translate\n >\n Type\n </label>\n <c8y-select-legacy\n style=\"width: 180px\"\n [items]=\"bulkTypes\"\n [selected]=\"selectedTypeFilters\"\n [disableApplyOnNoSelection]=\"true\"\n (onChange)=\"selectedTypeFilters = $event; typeFilter$.next(selectedTypeFilters)\"\n ></c8y-select-legacy>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form\"\n>\n <c8y-status-filter\n #statusFilter\n [options]=\"bulkOperationStatusOptions\"\n (onFilterChanged)=\"statusFilter$.next($event)\"\n ></c8y-status-filter>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form\"\n>\n <c8y-date-picker (onDateSelected)=\"timeFilter$.next($event)\"></c8y-date-picker>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <c8y-realtime-btn [service]=\"realtime\"></c8y-realtime-btn>\n</c8y-action-bar-item>\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link d-flex a-i-center\"\n title=\"{{ 'Add bulk operation' | translate }}\"\n *ngIf=\"bulkTypes?.length\"\n (click)=\"addBulkOperation()\"\n c8yProductExperience\n [actionName]=\"BULK_OPERATION_EVENT\"\n [actionData]=\"{ action: bulkActions.OPEN_ADD_BULK_OPERATION_DIALOG }\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n <span class=\"text-truncate\">\n {{ 'Add bulk operation' | translate }}\n </span>\n </button>\n</c8y-action-bar-item>\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link d-flex a-i-center\"\n title=\"{{ 'Reload' | translate }}\"\n (click)=\"reload$.next()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': refreshLoading }\"\n ></i>\n <span class=\"text-truncate\">\n {{ 'Reload' | translate }}\n </span>\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/monitoring-and-controlling-devices/#to-view-bulk-operations\"\n></c8y-help>\n\n<!-- Empty state -->\n<c8y-ui-empty-state\n icon=\"c8y-energy\"\n [title]=\"'No items to display' | translate\"\n [subtitle]=\"'Bulk operations will be displayed here' | translate\"\n *ngIf=\"(bulkOperations$ | async)?.data.length === 0 && !isFilterApplied()\"\n>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Add bulk operation' | translate }}\"\n type=\"button\"\n *ngIf=\"bulkTypes?.length\"\n (click)=\"addBulkOperation()\"\n translate\n >\n Add bulk operation\n </button>\n</c8y-ui-empty-state>\n\n<!-- No results empty state -->\n<c8y-ui-empty-state\n icon=\"search\"\n [title]=\"'No results to display.' | translate\"\n [subtitle]=\"'Adjust or reset the filter.' | translate\"\n *ngIf=\"(bulkOperations$ | async)?.data.length === 0 && isFilterApplied()\"\n>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Reset filter' | translate }}\"\n type=\"button\"\n (click)=\"resetFilter()\"\n translate\n >\n Reset filter\n </button>\n</c8y-ui-empty-state>\n\n<!-- Detailed list of operations + load more button -->\n<c8y-list-group class=\"m-b-24\">\n <div\n class=\"page-sticky-header hidden-xs c8y-list__item--double-actions c8y-list__item\"\n *ngIf=\"(bulkOperations$ | async)?.data.length\"\n >\n <div class=\"c8y-list__item__block\">\n <div class=\"c8y-list__item__icon\">\n <i\n class=\"invisible\"\n c8yIcon=\"refresh\"\n ></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <div class=\"content-flex-57\">\n <div class=\"col-5\">\n {{ 'Operation' | translate }}\n </div>\n <div class=\"flex-grow\">\n {{ 'Progress' | translate }}\n </div>\n <div class=\"col-4\">\n {{ 'Status' | translate }}\n </div>\n </div>\n </div>\n <div class=\"c8y-list__item__actions\"></div>\n </div>\n </div>\n <div\n class=\"d-contents\"\n *c8yFor=\"\n let bulkOperation of bulkOperations$ | async;\n let i = index;\n realtime: realtime;\n pipe: filterPipe;\n comparator: compareOperations.bind(this);\n loadMore: 'auto'\n \"\n >\n <c8y-bulk-operation-list-item\n class=\"d-contents\"\n [bulkOperation]=\"bulkOperation\"\n (reload)=\"reload$.next()\"\n (showFailedOperation)=\"openFailedOperation($event)\"\n ></c8y-bulk-operation-list-item>\n </div>\n</c8y-list-group>\n", dependencies: [{ kind: "component", type: i1.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: i1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i1.SelectLegacyComponent, selector: "c8y-select-legacy", inputs: ["placeholder", "selectedLabel", "applyLabel", "items", "selected", "updateItems", "disableApplyOnNoSelection", "addDropdownContainerToBody"], outputs: ["onChange"] }, { kind: "component", type: i1.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i1.DatePickerComponent, selector: "c8y-date-picker", inputs: ["placeholder"], outputs: ["onDateSelected"] }, { kind: "directive", type: i1.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "component", type: i1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "component", type: i1.RealtimeButtonComponent, selector: "c8y-realtime-btn", inputs: ["service", "label", "title", "disabled"], outputs: ["onToggle"] }, { kind: "component", type: i5.StatusFilterComponent, selector: "c8y-status-filter", inputs: ["options", "multiple", "small"], outputs: ["onFilterChanged"] }, { kind: "component", type: i6.BulkOperationListItemComponent, selector: "c8y-bulk-operation-list-item", inputs: ["bulkOperation", "detailsCollapsed", "readOnly"], outputs: ["showFailedOperation", "reload"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BulkOperationsListComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-bulk-operations', providers: [OperationBulkRealtimeService], template: "<c8y-title>{{ 'Bulk operations' | translate }}</c8y-title>\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-overviews'\"\n [label]=\"'Overviews' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-device-control'\"\n [label]=\"'Device control' | translate\"\n [path]=\"'devicecontrol/single'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-energy'\"\n [label]=\"'Bulk operations' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n *ngIf=\"bulkTypes?.length\"\n itemClass=\"navbar-form\"\n [placement]=\"'left'\"\n>\n <label\n class=\"hidden-sm hidden-xs\"\n translate\n >\n Type\n </label>\n <c8y-select-legacy\n style=\"width: 180px\"\n [items]=\"bulkTypes\"\n [selected]=\"selectedTypeFilters\"\n [disableApplyOnNoSelection]=\"true\"\n (onChange)=\"selectedTypeFilters = $event; typeFilter$.next(selectedTypeFilters)\"\n ></c8y-select-legacy>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form\"\n>\n <c8y-status-filter\n #statusFilter\n [options]=\"bulkOperationStatusOptions\"\n (onFilterChanged)=\"statusFilter$.next($event)\"\n ></c8y-status-filter>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form\"\n>\n <c8y-date-picker (onDateSelected)=\"timeFilter$.next($event)\"></c8y-date-picker>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <c8y-realtime-btn [service]=\"realtime\"></c8y-realtime-btn>\n</c8y-action-bar-item>\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link d-flex a-i-center\"\n title=\"{{ 'Add bulk operation' | translate }}\"\n *ngIf=\"bulkTypes?.length\"\n (click)=\"addBulkOperation()\"\n c8yProductExperience\n [actionName]=\"BULK_OPERATION_EVENT\"\n [actionData]=\"{ action: bulkActions.OPEN_ADD_BULK_OPERATION_DIALOG }\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"plus-circle\"\n ></i>\n <span class=\"text-truncate\">\n {{ 'Add bulk operation' | translate }}\n </span>\n </button>\n</c8y-action-bar-item>\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link d-flex a-i-center\"\n title=\"{{ 'Reload' | translate }}\"\n (click)=\"reload$.next()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': refreshLoading }\"\n ></i>\n <span class=\"text-truncate\">\n {{ 'Reload' | translate }}\n </span>\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/monitoring-and-controlling-devices/#to-view-bulk-operations\"\n></c8y-help>\n\n<!-- Empty state -->\n<c8y-ui-empty-state\n icon=\"c8y-energy\"\n [title]=\"'No items to display' | translate\"\n [subtitle]=\"'Bulk operations will be displayed here' | translate\"\n *ngIf=\"(bulkOperations$ | async)?.data.length === 0 && !isFilterApplied()\"\n>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Add bulk operation' | translate }}\"\n type=\"button\"\n *ngIf=\"bulkTypes?.length\"\n (click)=\"addBulkOperation()\"\n translate\n >\n Add bulk operation\n </button>\n</c8y-ui-empty-state>\n\n<!-- No results empty state -->\n<c8y-ui-empty-state\n icon=\"search\"\n [title]=\"'No results to display.' | translate\"\n [subtitle]=\"'Adjust or reset the filter.' | translate\"\n *ngIf=\"(bulkOperations$ | async)?.data.length === 0 && isFilterApplied()\"\n>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Reset filter' | translate }}\"\n type=\"button\"\n (click)=\"resetFilter()\"\n translate\n >\n Reset filter\n </button>\n</c8y-ui-empty-state>\n\n<!-- Detailed list of operations + load more button -->\n<c8y-list-group class=\"m-b-24\">\n <div\n class=\"page-sticky-header hidden-xs c8y-list__item--double-actions c8y-list__item\"\n *ngIf=\"(bulkOperations$ | async)?.data.length\"\n >\n <div class=\"c8y-list__item__block\">\n <div class=\"c8y-list__item__icon\">\n <i\n class=\"invisible\"\n c8yIcon=\"refresh\"\n ></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <div class=\"content-flex-57\">\n <div class=\"col-5\">\n {{ 'Operation' | translate }}\n </div>\n <div class=\"flex-grow\">\n {{ 'Progress' | translate }}\n </div>\n <div class=\"col-4\">\n {{ 'Status' | translate }}\n </div>\n </div>\n </div>\n <div class=\"c8y-list__item__actions\"></div>\n </div>\n </div>\n <div\n class=\"d-contents\"\n *c8yFor=\"\n let bulkOperation of bulkOperations$ | async;\n let i = index;\n realtime: realtime;\n pipe: filterPipe;\n comparator: compareOperations.bind(this);\n loadMore: 'auto'\n \"\n >\n <c8y-bulk-operation-list-item\n class=\"d-contents\"\n [bulkOperation]=\"bulkOperation\"\n (reload)=\"reload$.next()\"\n (showFailedOperation)=\"openFailedOperation($event)\"\n ></c8y-bulk-operation-list-item>\n </div>\n</c8y-list-group>\n" }] }], ctorParameters: () => [{ type: i1.OperationBulkRealtimeService }, { type: i2.BulkOperationsService }, { type: i3.BulkOperationModalsService }], propDecorators: { listItems: [{ type: ViewChildren, args: [BulkOperationListItemComponent] }], statusFilter: [{ type: ViewChild, args: ['statusFilter', { static: true }] }], datePicker: [{ type: ViewChild, args: [DatePickerComponent, { static: true }] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVsay1vcGVyYXRpb25zLWxpc3QuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vb3BlcmF0aW9ucy9idWxrLW9wZXJhdGlvbnMtbGlzdC9idWxrLW9wZXJhdGlvbnMtbGlzdC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9vcGVyYXRpb25zL2J1bGstb3BlcmF0aW9ucy1saXN0L2J1bGstb3BlcmF0aW9ucy1saXN0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQVUsU0FBUyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFdEYsT0FBTyxFQUNMLG1CQUFtQixFQUVuQiw0QkFBNEIsRUFDN0IsTUFBTSxxQkFBcUIsQ0FBQztBQUM3QixPQUFPLEVBQ0wsOEJBQThCLEVBQzlCLDZCQUE2QixFQUU5QixNQUFNLHlEQUF5RCxDQUFDO0FBQ2pFLE9BQU8sRUFDTCxxQkFBcUIsRUFFdEIsTUFBTSx3REFBd0QsQ0FBQztBQUNoRSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLG9CQUFvQixFQUNyQixNQUFNLG1EQUFtRCxDQUFDO0FBRTNELE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDhDQUE4QyxDQUFDO0FBQ3JGLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDcEMsT0FBTyxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQWMsSUFBSSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3hFLE9BQU8sRUFBRSxHQUFHLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsY0FBYyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEYsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sd0NBQXdDLENBQUM7Ozs7Ozs7O0FBTXBGLE1BQU0sT0FBTywyQkFBMkI7SUF5Q3RDLFlBQ1MsUUFBc0MsRUFDckMscUJBQTRDLEVB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QUFPLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDN0YsQ0FBQztJQUVPLHNCQUFzQixDQUFDLE9BQXdCO1FBQ3JELE9BQU8sQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsU0FBUyxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDakcsQ0FBQzsrR0F6SVUsMkJBQTJCO21HQUEzQiwyQkFBMkIsOENBRjNCLENBQUMsNEJBQTRCLENBQUMsbUxBa0I5QixtQkFBbUIsNkVBSGhCLDhCQUE4QixnREM1QzlDLDBtS0F1TEE7OzRGRHhKYSwyQkFBMkI7a0JBTHZDLFNBQVM7K0JBQ0UscUJBQXFCLGFBRXBCLENBQUMsNEJBQTRCLENBQUM7OEtBZ0J6QyxTQUFTO3NCQURSLFlBQVk7dUJBQUMsOEJBQThCO2dCQUVDLFlBQVk7c0JBQXhELFNBQVM7dUJBQUMsY0FBYyxFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRTtnQkFDTyxVQUFVO3NCQUEzRCxTQUFTO3VCQUFDLG1CQUFtQixFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgT25Jbml0LCBRdWVyeUxpc3QsIFZpZXdDaGlsZCwgVmlld0NoaWxkcmVuIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBJT3BlcmF0aW9uQnVsaywgSVJlc3VsdExpc3QgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQge1xuICBEYXRlUGlja2VyQ29tcG9uZW50LFxuICBGb3JPZkZpbHRlclBpcGUsXG4gIE9wZXJhdGlvbkJ1bGtSZWFsdGltZVNlcnZpY2Vcbn0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQge1xuICBCdWxrT3BlcmF0aW9uTGlzdEl0ZW1Db21wb25lbnQsXG4gIEJVTEtfT1BFUkFUSU9OX1NUQVRVU19PUFRJT05TLFxuICBPcGVyYXRpb25TdGF0dXNPcHRpb25zTWFwXG59IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMvb3BlcmF0aW9ucy9idWxrLW9wZXJhdGlvbi1saXN0LWl0ZW0nO1xuaW1wb3J0IHtcbiAgQnVsa09wZXJhdGlvbnNTZXJ2aWNlLFxuICBPcGVyYXRpb25UeXBlXG59IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMvb3BlcmF0aW9ucy9idWxrLW9wZXJhdGlvbnMtc2VydmljZSc7XG5pbXBvcnQge1xuICBBQ1RJT05TX09QRVJBVElPTlNfQlVMSyxcbiAgQlVMS19PUEVSQVRJT05fRVZFTlRcbn0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cy9vcGVyYXRpb25zL3Byb2R1Y3QtZXhwZXJpZW5jZSc7XG5pbXBvcnQgeyBPcGVyYXRpb25TdGF0dXNPcHRpb24gfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL29wZXJhdGlvbnMvc2hhcmVkJztcbmltcG9ydCB7IFN0YXR1c0ZpbHRlckNvbXBvbmVudCB9IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMvb3BlcmF0aW9ucy9zdGF0dXMtZmlsdGVyJztcbmltcG9ydCB7IGZsYXR0ZW4gfSBmcm9tICdsb2Rhc2gtZXMnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBjb21iaW5lTGF0ZXN0LCBPYnNlcnZhYmxlLCBwaXBlIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBtYXAsIHNoYXJlUmVwbGF5LCBzd2l0Y2hNYXAsIHRhcCwgd2l0aExhdGVzdEZyb20gfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBCdWxrT3BlcmF0aW9uTW9kYWxzU2VydmljZSB9IGZyb20gJy4vbW9kYWxzL2J1bGstb3BlcmF0aW9uLW1vZGFscy5zZXJ2aWNlJztcbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS1idWxrLW9wZXJhdGlvbnMnLFxuICB0ZW1wbGF0ZVVybDogJy4vYnVsay1vcGVyYXRpb25zLWxpc3QuY29tcG9uZW50Lmh0bWwnLFxuICBwcm92aWRlcnM6IFtPcGVyYXRpb25CdWxrUmVhbHRpbWVTZXJ2aWNlXVxufSlcbmV4cG9ydCBjbGFzcyBCdWxrT3BlcmF0aW9uc0xpc3RDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQge1xuICBidWxrVHlwZXM6IE9wZXJhdGlvblR5cGVbXSA9IFtdO1xuICBzZWxlY3RlZFR5cGVGaWx0ZXJzID0gdGhpcy5nZXRUeXBlRmlsdGVycygpO1xuICBidWxrT3BlcmF0aW9uU3RhdHVzT3B0aW9uczogT3BlcmF0aW9uU3RhdHVzT3B0aW9uc01hcCA9IEJVTEtfT1BFUkFUSU9OX1NUQVRVU19PUFRJT05TO1xuICBCVUxLX09QRVJBVElPTl9FVkVOVCA9IEJVTEtfT1BFUkFUSU9OX0VWRU5UO1xuICBidWxrQWN0aW9ucyA9IEFDVElPTlNfT1BFUkFUSU9OU19CVUxLO1xuXG4gIGZpbHRlclBpcGU6IEZvck9mRmlsdGVyUGlwZTxJT3BlcmF0aW9uQnVsaz47XG4gIHJlZnJlc2hMb2FkaW5nID0gZmFsc2U7XG4gIHN0YXR1c0ZpbHRlciQ6IEJlaGF2aW9yU3ViamVjdDxPcGVyYXRpb25TdGF0dXNPcHRpb25bXT4gPSBuZXcgQmVoYXZpb3JTdWJqZWN0KG51bGwpO1xuICB0eXBlRmlsdGVyJDogQmVoYXZpb3JTdWJqZWN0PE9wZXJhdGlvblR5cGVbXT4gPSBuZXcgQmVoYXZpb3JTdWJqZWN0KG51bGwpO1xuICB0aW1lRmlsdGVyJDogQmVoYXZpb3JTdWJqZWN0PGFueT4gPSBuZXcgQmVoYXZpb3JTdWJqZWN0KG51bGwpO1xuICByZWxvYWQkOiBCZWhhdmlvclN1YmplY3Q8dm9pZD4gPSBuZXcgQmVoYXZpb3JTdWJqZWN0KG51bGwpO1xuICBAVmlld0NoaWxkcmVuKEJ1bGtPcGVyYXRpb25MaXN0SXRlbUNvbXBvbmVudClcbiAgbGlzdEl0ZW1zOiBRdWVyeUxpc3Q8QnVsa09wZXJhdGlvbkxpc3RJdGVtQ29tcG9uZW50PjtcbiAgQFZpZXdDaGlsZCgnc3RhdHVzRmlsdGVyJywgeyBzdGF0aWM6IHRydWUgfSkgc3RhdHVzRmlsdGVyOiBTdGF0dXNGaWx0ZXJDb21wb25lbnQ7XG4gIEBWaWV3Q2hpbGQoRGF0ZVBpY2tlckNvbXBvbmVudCwgeyBzdGF0aWM6IHRydWUgfSkgZGF0ZVBpY2tlcjogRGF0ZVBpY2tlckNvbXBvbmVudDtcblxuICBidWxrT3BlcmF0aW9ucyQ6IE9ic2VydmFibGU8SVJlc3VsdExpc3Q8SU9wZXJhdGlvbkJ1bGs+PiA9IGNvbWJpbmVMYXRlc3QoXG4gICAgdGhpcy5zdGF0dXNGaWx0ZXIkLFxuICAgIHRoaXMudGltZUZpbHRlciQsXG4gICAgdGhpcy50eXBlRmlsdGVyJCxcbiAgICB0aGlzLnJlbG9hZCRcbiAgKS5waXBlKFxuICAgIHRhcCgoKSA9PiB7XG4gICAgICB0aGlzLnJlZnJlc2hMb2FkaW5nID0gdHJ1ZTtcbiAgICB9KSxcbiAgICBzd2l0Y2hNYXAoKFtzdGF0dXNGaWx0ZXJzLCB0aW1lRmlsdGVyc10pID0+IHRoaXMuZmlsdGVyKHN0YXR1c0ZpbHRlcnMsIHRpbWVGaWx0ZXJzKSksXG4gICAgd2l0aExhdGVzdEZyb20odGhpcy50eXBlRmlsdGVyJCksXG4gICAgbWFwKChbcmVzdWx0LCB0eXBlRmlsdGVyXTogW0lSZXN1bHRMaXN0PElPcGVyYXRpb25CdWxrPiwgT3BlcmF0aW9uVHlwZVtdXSkgPT4ge1xuICAgICAgdGhpcy5maWx0ZXJQaXBlID0gcGlwZShtYXAoZGF0YSA9PiB0aGlzLmZpbHRlckJ5VHlwZShkYXRhLCB0eXBlRmlsdGVyKSkpO1xuICAgICAgcmV0dXJuIHsgLi4ucmVzdWx0LCBkYXRhOiB0aGlzLmZpbHRlckJ5VHlwZShyZXN1bHQuZGF0YSwgdHlwZUZpbHRlcikgfTtcbiAgICB9KSxcbiAgICB0YXAoKCkgPT4ge1xuICAgICAgdGhpcy5yZWZyZXNoTG9hZGluZyA9IGZhbHNlO1xuICAgIH0pLFxuICAgIHNoYXJlUmVwbGF5KDEpXG4gICk7XG5cbiAgcHJpdmF0ZSBhbGxGaWx0ZXJGcmFnbWVudHM6IHN0cmluZ1tdO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHB1YmxpYyByZWFsdGltZTogT3BlcmF0aW9uQnVsa1JlYWx0aW1lU2VydmljZSxcbiAgICBwcml2YXRlIGJ1bGtPcGVyYXRpb25zU2VydmljZTogQnVsa09wZXJhdGlvbnNTZXJ2aWNlLFxuICAgIHByaXZhdGUgYnVsa09wZXJhdGlvbk1vZGFsc1NlcnZpY2U6IEJ1bGtPcGVyYXRpb25Nb2RhbHNTZXJ2aWNlXG4gICkge1xuICAgIHRoaXMuYWxsRmlsdGVyRnJhZ21lbnRzID0gdGhpcy5mbGF0dGVuRmlsdGVyRnJhZ21lbnRzKHRoaXMuZ2V0VHlwZUZpbHRlcnMoKSk7XG4gIH1cblxuICBuZ09uSW5pdCgpIHtcbiAgICB0aGlzLmJ1bGtUeXBlcyA9IHRoaXMuYnVsa09wZXJhdGlvbnNTZXJ2aWNlLmdldEJ1bGtUeXBlcygpO1xuICB9XG5cbiAgZmlsdGVyQnlUeXBlKGJ1bGtPcGVyYXRpb25zOiBJT3BlcmF0aW9uQnVsa1tdLCB0eXBlRmlsdGVyKSB7XG4gICAgY29uc3QgZmxhdHRlbmVkRnJhZ21lbnRzOiBzdHJpbmdbXSA9IHRoaXMuZmxhdHRlbkZpbHRlckZyYWdtZW50cyh0eXBlRmlsdGVyKTtcbiAgICBpZiAoXG4gICAgICAvLyByZXR1cm4gZGF0YSB1bmZpbHRlcmVkIGlmIG5vIGZpbHRlcnMgc2VsZWN0ZWQuLi5cbiAgICAgICFmbGF0dGVuZWRGcmFnbWVudHMubGVuZ3RoIHx8XG4gICAgICAvLyAuLi5vciB3aGVuIGFsbCBmaWx0ZXJzIGFyZSBzZWxlY3RlZFxuICAgICAgdGhpcy5hbGxGaWx0ZXJGcmFnbWVudHMuZXZlcnkoZnJhZ21lbnQgPT4gZmxhdHRlbmVkRnJhZ21lbnRzLmluY2x1ZGVzKGZyYWdtZW50KSlcbiAgICApIHtcbiAgICAgIHJldHVybiBidWxrT3BlcmF0aW9ucztcbiAgICB9XG5cbiAgICBjb25zdCBmaWx0ZXJlZERhdGEgPSBidWxrT3BlcmF0aW9ucy5maWx0ZXIoaXRlbSA9PiB7XG4gICAgICByZXR1cm4gT2JqZWN0LmtleXMoaXRlbS5vcGVyYXRpb25Qcm90b3R5cGUpLnNvbWUoa2V5ID0+IGZsYXR0ZW5lZEZyYWdtZW50cy5pbmNsdWRlcyhrZXkpKTtcbiAgICB9KTtcblxuICAgIHJldHVybiBmaWx0ZXJlZERhdGE7XG4gIH1cblxuICByZXNldEZpbHRlcigpIHtcbiAgICB0aGlzLnN0YXR1c0ZpbHRlciQubmV4dChudWxsKTtcbiAgICB0aGlzLnRpbWVGaWx0ZXIkLm5leHQobnVsbCk7XG4gICAgdGhpcy50eXBlRmlsdGVyJC5uZXh0KG51bGwpO1xuXG4gICAgdGhpcy5kYXRlUGlja2VyLmNsZWFyRmlsdGVyKCk7XG4gICAgdGhpcy5zZWxlY3RlZFR5cGVGaWx0ZXJzID0gdGhpcy5nZXRUeXBlRmlsdGVycygpO1xuICAgIHRoaXMuc3RhdHVzRmlsdGVyLnJlc2V0KCk7XG4gIH1cblxuICBpc0ZpbHRlckFwcGxpZWQoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgICEhdGhpcy5zdGF0dXNGaWx0ZXIkLmdldFZhbHVlKCk/Lmxlbmd0aCB8fFxuICAgICAgISF0aGlzLnR5cGVGaWx0ZXIkLmdldFZhbHVlKCk/Lmxlbmd0aCB8fFxuICAgICAgISF0aGlzLnRpbWVGaWx0ZXIkLmdldFZhbHVlKClcbiAgICApO1xuICB9XG5cbiAgZmlsdGVyKHN0YXR1c0ZpbHRlcnMsIHRpbWVGaWx0ZXIpIHtcbiAgICBjb25zdCBzdGF0dXMgPVxuICAgICAgc3RhdHVzRmlsdGVycyAmJiBzdGF0dXNGaWx0ZXJzLmxlbmd0aCA+IDBcbiAgICAgICAgPyB7XG4gICAgICAgICAgICBnZW5lcmFsU3RhdHVzOiBmbGF0dGVuKHN0YXR1c0ZpbHRlcnMubWFwKHN0YXR1c0ZpbHRlciA9PiBzdGF0dXNGaWx0ZXIuZ2VuZXJhbFN0YXR1c2VzKSlcbiAgICAgICAgICB9XG4gICAgICAgIDoge307XG5cbiAgICBjb25zdCB0aW1lID0gdGltZUZpbHRlclxuICAgICAgPyB7XG4gICAgICAgICAgLi4uKHRpbWVGaWx0ZXIuZGF0ZUZyb20gJiYge1xuICAgICAgICAgICAgZGF0ZUZyb206IHRpbWVGaWx0ZXIuZGF0ZUZyb20udG9JU09TdHJpbmcoKVxuICAgICAgICAgIH0pLFxuICAgICAgICAgIC4uLih0aW1lRmlsdGVyLmRhdGVUbyAmJiB7XG4gICAgICAgICAgICBkYXRlVG86IHRpbWVGaWx0ZXIuZGF0ZVRvLnRvSVNPU3RyaW5nKClcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICA6IHt9O1xuICAgIHJldHVybiB0aGlzLmdldEJ1bGtPcGVyYXRpb25zKHsgLi4uc3RhdHVzLCAuLi50aW1lIH0pO1xuICB9XG5cbiAgZ2V0QnVsa09wZXJhdGlvbnMoZmlsdGVyPykge1xuICAgIHJldHVybiB0aGlzLmJ1bGtPcGVyYXRpb25zU2VydmljZS5nZXRCdWxrT3BlcmF0aW9ucyhmaWx0ZXIpO1xuICB9XG5cbiAgZ2V0VHlwZUZpbHRlcnMoKSB7XG4gICAgcmV0dXJuIHRoaXMuYnVsa09wZXJhdGlvbnNTZXJ2aWNlLmdldEJ1bGtUeXBlcygpO1xuICB9XG5cbiAgYWRkQnVsa09wZXJhdGlvbigpIHtcbiAgICB0aGlzLmJ1bGtPcGVyYXRpb25Nb2RhbHNTZXJ2aWNlLnNob3dOZXdCdWxrT3BlcmF0aW9uTW9kYWwoKTtcbiAgfVxuXG4gIG9wZW5GYWlsZWRPcGVyYXRpb24oZmFpbGVkUGFyZW50SWQpIHtcbiAgICB0aGlzLmxpc3RJdGVtcy5mb3JFYWNoKGl0ZW0gPT4ge1xuICAgICAgaWYgKGl0ZW0uYnVsa09wZXJhdGlvbi5pZCA9PT0gZmFpbGVkUGFyZW50SWQpIHtcbiAgICAgICAgaXRlbS5saXN0SXRlbS5jb2xsYXBzZWQgPSBmYWxzZTtcbiAgICAgICAgaXRlbS5saXN0SXRlbS5lbGVtZW50Lm5hdGl2ZUVsZW1lbnQuc2Nyb2xsSW50b1ZpZXcoeyBiZWhhdmlvcjogJ3Ntb290aCcsIGJsb2NrOiAnY2VudGVyJyB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGNvbXBhcmVPcGVyYXRpb25zKG9wZXJhdGlvbkE6IElPcGVyYXRpb25CdWxrLCBvcGVyYXRpb25COiBJT3BlcmF0aW9uQnVsayk6IG51bWJlciB7XG4gICAgcmV0dXJuIG5ldyBEYXRlKG9wZXJhdGlvbkEuc3RhcnREYXRlKS5nZXRUaW1lKCkgLSBuZXcgRGF0ZShvcGVyYXRpb25CLnN0YXJ0RGF0ZSkuZ2V0VGltZSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBmbGF0dGVuRmlsdGVyRnJhZ21lbnRzKGZpbHRlcnM6IE9wZXJhdGlvblR5cGVbXSk6IHN0cmluZ1tdIHtcbiAgICByZXR1cm4gKGZpbHRlcnMgfHwgW10pLnJlZHVjZSgoZmxhdHRlbmVkLCBjdXJyZW50KSA9PiBmbGF0dGVuZWQuY29uY2F0KGN1cnJlbnQuZnJhZ21lbnRzKSwgW10pO1xuICB9XG59XG4iLCI8Yzh5LXRpdGxlPnt7ICdCdWxrIG9wZXJhdGlvbnMnIHwgdHJhbnNsYXRlIH19PC9jOHktdGl0bGU+XG48Yzh5LWJyZWFkY3J1bWI+XG4gIDxjOHktYnJlYWRjcnVtYi1pdGVtXG4gICAgW2ljb25dPVwiJ2M4eS1vdmVydmlld3MnXCJcbiAgICBbbGFiZWxdPVwiJ092ZXJ2aWV3cycgfCB0cmFuc2xhdGVcIlxuICA+PC9jOHktYnJlYWRjcnVtYi1pdGVtPlxuICA8Yzh5LWJyZWFkY3J1bWItaXRlbVxuICAgIFtpY29uXT1cIidjOHktZGV2aWNlLWNvbnRyb2wnXCJcbiAgICBbbGFiZWxdPVwiJ0RldmljZSBjb250cm9sJyB8IHRyYW5zbGF0ZVwiXG4gICAgW3BhdGhdPVwiJ2RldmljZWNvbnRyb2wvc2luZ2xlJ1wiXG4gID48L2M4eS1icmVhZGNydW1iLWl0ZW0+XG4gIDxjOHktYnJlYWRjcnVtYi1pdGVtXG4gICAgW2ljb25dPVwiJ2M4eS1lbmVyZ3knXCJcbiAgICBbbGFiZWxdPVwiJ0J1bGsgb3BlcmF0aW9ucycgfCB0cmFuc2xhdGVcIlxuICA+PC9jOHktYnJlYWRjcnVtYi1pdGVtPlxuPC9jOHktYnJlYWRjcnVtYj5cblxuPGM4eS1hY3Rpb24tYmFyLWl0ZW1cbiAgKm5nSWY9XCJidWxrVHlwZXM/Lmxlbmd0aFwiXG4gIGl0ZW1DbGFzcz1cIm5hdmJhci1mb3JtXCJcbiAgW3BsYWNlbWVudF09XCInbGVmdCdcIlxuPlxuICA8bGFiZWxcbiAgICBjbGFzcz1cImhpZGRlbi1zbSBoaWRkZW4teHNcIlxuICAgIHRyYW5zbGF0ZVxuICA+XG4gICAgVHlwZVxuICA8L2xhYmVsPlxuICA8Yzh5LXNlbGVjdC1sZWdhY3lcbiAgICBzdHlsZT1cIndpZHRoOiAxODBweFwiXG4gICAgW2l0ZW1zXT1cImJ1bGtUeXBlc1wiXG4gICAgW3NlbGVjdGVkXT1cInNlbGVjdGVkVHlwZUZpbHRlcnNcIlxuICAgIFtkaXNhYmxlQXBwbHlPbk5vU2VsZWN0aW9uXT1cInRydWVcIlxuICAgIChvbkNoYW5nZSk9XCJzZWxlY3RlZFR5cGVGaWx0ZXJzID0gJGV2ZW50OyB0eXBlRmlsdGVyJC5uZXh0KHNlbGVjdGVkVHlwZUZpbHRlcnMpXCJcbiAgPjwvYzh5LXNlbGVjdC1sZWdhY3k+XG48L2M4eS1hY3Rpb24tYmFyLWl0ZW0+XG5cbjxjOHktYWN0aW9uLWJhci1pdGVtXG4gIFtwbGFjZW1lbnRdPVwiJ2xlZnQnXCJcbiAgaXRlbUNsYXNzPVwibmF2YmFyLWZvcm1cIlxuPlxuICA8Yzh5LXN0YXR1cy1maWx0ZXJcbiAgICAjc3RhdHVzRmlsdGVyXG4gICAgW29wdGlvbnNdPVwiYnVsa09wZXJhdGlvblN0YXR1c09wdGlvbnNcIlxuICAgIChvbkZpbHRlckNoYW5nZWQpPVwic3RhdHVzRmlsdGVyJC5uZXh0KCRldmVudClcIlxuICA+PC9jOHktc3RhdHVzLWZpbHRlcj5cbjwvYzh5LWFjdGlvbi1iYXItaXRlbT5cblxuPGM4eS1hY3Rpb24tYmFyLWl0ZW1cbiAgW3BsYWNlbWVudF09XCInbGVmdCdcIlxuICBpdGVtQ2xhc3M9XCJuYXZiYXItZm9ybVwiXG4+XG4gIDxjOHktZGF0ZS1waWNrZXIgKG9uRGF0ZVNlbGVjdGVkKT1cInRpbWVGaWx0ZXIkLm5leHQoJGV2ZW50KVwiPjwvYzh5LWRhdGUtcGlja2VyPlxuPC9jOHktYWN0aW9uLWJhci1pdGVtPlxuXG48Yzh5LWFjdGlvbi1iYXItaXRlbSBbcGxhY2VtZW50XT1cIidyaWdodCdcIj5cbiAgPGM4eS1yZWFsdGltZS1idG4gW3NlcnZpY2VdPVwicmVhbHRpbWVcIj48L2M4eS1yZWFsdGltZS1idG4+XG48L2M4eS1hY3Rpb24tYmFyLWl0ZW0+XG48Yzh5LWFjdGlvbi1iYXItaXRlbSBbcGxhY2VtZW50XT1cIidyaWdodCdcIj5cbiAgPGJ1dHRvblxuICAgIGNsYXNzPVwiYnRuIGJ0bi1saW5rIGQtZmxleCBhLWktY2VudGVyXCJcbiAgICB0aXRsZT1cInt7ICdBZGQgYnVsayBvcGVyYXRpb24nIHwgdHJhbnNsYXRlIH19XCJcbiAgICAqbmdJZj1cImJ1bGtUeXBlcz8ubGVuZ3RoXCJcbiAgICAoY2xpY2spPVwiYWRkQnVsa09wZXJhdGlvbigpXCJcbiAgICBjOHlQcm9kdWN0RXhwZXJpZW5jZVxuICAgIFthY3Rpb25OYW1lXT1cIkJVTEtfT1BFUkFUSU9OX0VWRU5UXCJcbiAgICBbYWN0aW9uRGF0YV09XCJ7IGFjdGlvbjogYnVsa0FjdGlvbnMuT1BFTl9BRERfQlVMS19PUEVSQVRJT05fRElBTE9HIH1cIlxuICA+XG4gICAgPGlcbiAgICAgIGNsYXNzPVwibS1yLTRcIlxuICAgICAgYzh5SWNvbj1cInBsdXMtY2lyY2xlXCJcbiAgICA+PC9pPlxuICAgIDxzcGFuIGNsYXNzPVwidGV4dC10cnVuY2F0ZVwiPlxuICAgICAge3sgJ0FkZCBidWxrIG9wZXJhdGlvbicgfCB0cmFuc2xhdGUgfX1cbiAgICA8L3NwYW4+XG4gIDwvYnV0dG9uPlxuPC9jOHktYWN0aW9uLWJhci1pdGVtPlxuPGM4eS1hY3Rpb24tYmFyLWl0ZW0gW3BsYWNlbWVudF09XCIncmlnaHQnXCI+XG4gIDxidXR0b25cbiAgICBjbGFzcz1cImJ0biBidG4tbGluayBkLWZsZXggYS1pLWNlbnRlclwiXG4gICAgdGl0bGU9XCJ7eyAnUmVsb2FkJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgKGNsaWNrKT1cInJlbG9hZCQubmV4dCgpXCJcbiAgPlxuICAgIDxpXG4gICAgICBjbGFzcz1cIm0tci00XCJcbiAgICAgIGM4eUljb249XCJyZWZyZXNoXCJcbiAgICAgIFtuZ0NsYXNzXT1cInsgJ2ljb24tc3Bpbic6IHJlZnJlc2hMb2FkaW5nIH1cIlxuICAgID48L2k+XG4gICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LXRydW5jYXRlXCI+XG4gICAgICB7eyAnUmVsb2FkJyB8IHRyYW5zbGF0ZSB9fVxuICAgIDwvc3Bhbj5cbiAgPC9idXR0b24+XG48L2M4eS1hY3Rpb24tYmFyLWl0ZW0+XG5cbjxjOHktaGVscFxuICBzcmM9XCIvZG9jcy9kZXZpY2UtbWFuYWdlbWVudC1hcHBsaWNhdGlvbi9tb25pdG9yaW5nLWFuZC1jb250cm9sbGluZy1kZXZpY2VzLyN0by12aWV3LWJ1bGstb3BlcmF0aW9uc1wiXG4+PC9jOHktaGVscD5cblxuPCEtLSBFbXB0eSBzdGF0ZSAtLT5cbjxjOHktdWktZW1wdHktc3RhdGVcbiAgaWNvbj1cImM4eS1lbmVyZ3lcIlxuICBbdGl0bGVdPVwiJ05vIGl0ZW1zIHRvIGRpc3BsYXknIHwgdHJhbnNsYXRlXCJcbiAgW3N1YnRpdGxlXT1cIidCdWxrIG9wZXJhdGlvbnMgd2lsbCBiZSBkaXNwbGF5ZWQgaGVyZScgfCB0cmFuc2xhdGVcIlxuICAqbmdJZj1cIihidWxrT3BlcmF0aW9ucyQgfCBhc3luYyk/LmRhdGEubGVuZ3RoID09PSAwICYmICFpc0ZpbHRlckFwcGxpZWQoKVwiXG4+XG4gIDxidXR0b25cbiAgICBjbGFzcz1cImJ0biBidG4tcHJpbWFyeVwiXG4gICAgdGl0bGU9XCJ7eyAnQWRkIGJ1bGsgb3BlcmF0aW9uJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgdHlwZT1cImJ1dHRvblwiXG4gICAgKm5nSWY9XCJidWxrVHlwZXM/Lmxlbmd0aFwiXG4gICAgKGNsaWNrKT1cImFkZEJ1bGtPcGVyYXRpb24oKVwiXG4gICAgdHJhbnNsYXRlXG4gID5cbiAgICBBZGQgYnVsayBvcGVyYXRpb25cbiAgPC9idXR0b24+XG48L2M4eS11aS1lbXB0eS1zdGF0ZT5cblxuPCEtLSBObyByZXN1bHRzIGVtcHR5IHN0YXRlIC0tPlxuPGM4eS11aS1lbXB0eS1zdGF0ZVxuICBpY29uPVwic2VhcmNoXCJcbiAgW3RpdGxlXT1cIidObyByZXN1bHRzIHRvIGRpc3BsYXkuJyB8IHRyYW5zbGF0ZVwiXG4gIFtzdWJ0aXRsZV09XCInQWRqdXN0IG9yIHJlc2V0IHRoZSBmaWx0ZXIuJyB8IHRyYW5zbGF0ZVwiXG4gICpuZ0lmPVwiKGJ1bGtPcGVyYXRpb25zJCB8IGFzeW5jKT8uZGF0YS5sZW5ndGggPT09IDAgJiYgaXNGaWx0ZXJBcHBsaWVkKClcIlxuPlxuICA8YnV0dG9uXG4gICAgY2xhc3M9XCJidG4gYnRuLXByaW1hcnlcIlxuICAgIHRpdGxlPVwie3sgJ1Jlc2V0IGZpbHRlcicgfCB0cmFuc2xhdGUgfX1cIlxuICAgIHR5cGU9XCJidXR0b25cIlxuICAgIChjbGljayk9XCJyZXNldEZpbHRlcigpXCJcbiAgICB0cmFuc2xhdGVcbiAgPlxuICAgIFJlc2V0IGZpbHRlclxuICA8L2J1dHRvbj5cbjwvYzh5LXVpLWVtcHR5LXN0YXRlPlxuXG48IS0tIERldGFpbGVkIGxpc3Qgb2Ygb3BlcmF0aW9ucyArIGxvYWQgbW9yZSBidXR0b24gLS0+XG48Yzh5LWxpc3QtZ3JvdXAgY2xhc3M9XCJtLWItMjRcIj5cbiAgPGRpdlxuICAgIGNsYXNzPVwicGFnZS1zdGlja3ktaGVhZGVyIGhpZGRlbi14cyBjOHktbGlzdF9faXRlbS0tZG91YmxlLWFjdGlvbnMgYzh5LWxpc3RfX2l0ZW1cIlxuICAgICpuZ0lmPVwiKGJ1bGtPcGVyYXRpb25zJCB8IGFzeW5jKT8uZGF0YS5sZW5ndGhcIlxuICA+XG4gICAgPGRpdiBjbGFzcz1cImM4eS1saXN0X19pdGVtX19ibG9ja1wiPlxuICAgICAgPGRpdiBjbGFzcz1cImM4eS1saXN0X19pdGVtX19pY29uXCI+XG4gICAgICAgIDxpXG4gICAgICAgICAgY2xhc3M9XCJpbnZpc2libGVcIlxuICAgICAgICAgIGM4eUljb249XCJyZWZyZXNoXCJcbiAgICAgICAgPjwvaT5cbiAgICAgIDwvZGl2PlxuICAgICAgPGRpdiBjbGFzcz1cImM4eS1saXN0X19pdGVtX19ib2R5XCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJjb250ZW50LWZsZXgtNTdcIj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiY29sLTVcIj5cbiAgICAgICAgICAgIHt7ICdPcGVyYXRpb24nIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImZsZXgtZ3Jvd1wiPlxuICAgICAgICAgICAge3sgJ1Byb2dyZXNzJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJjb2wtNFwiPlxuICAgICAgICAgICAge3sgJ1N0YXR1cycgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXYgY2xhc3M9XCJjOHktbGlzdF9faXRlbV9fYWN0aW9uc1wiPjwvZGl2PlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cbiAgPGRpdlxuICAgIGNsYXNzPVwiZC1jb250ZW50c1wiXG4gICAgKmM4eUZvcj1cIlxuICAgICAgbGV0IGJ1bGtPcGVyYXRpb24gb2YgYnVsa09wZXJhdGlvbnMkIHwgYXN5bmM7XG4gICAgICBsZXQgaSA9IGluZGV4O1xuICAgICAgcmVhbHRpbWU6IHJlYWx0aW1lO1xuICAgICAgcGlwZTogZmlsdGVyUGlwZTtcbiAgICAgIGNvbXBhcmF0b3I6IGNvbXBhcmVPcGVyYXRpb25zLmJpbmQodGhpcyk7XG4gICAgICBsb2FkTW9yZTogJ2F1dG8nXG4gICAgXCJcbiAgPlxuICAgIDxjOHktYnVsay1vcGVyYXRpb24tbGlzdC1pdGVtXG4gICAgICBjbGFzcz1cImQtY29udGVudHNcIlxuICAgICAgW2J1bGtPcGVyYXRpb25dPVwiYnVsa09wZXJhdGlvblwiXG4gICAgICAocmVsb2FkKT1cInJlbG9hZCQubmV4dCgpXCJcbiAgICAgIChzaG93RmFpbGVkT3BlcmF0aW9uKT1cIm9wZW5GYWlsZWRPcGVyYXRpb24oJGV2ZW50KVwiXG4gICAgPjwvYzh5LWJ1bGstb3BlcmF0aW9uLWxpc3QtaXRlbT5cbiAgPC9kaXY+XG48L2M4eS1saXN0LWdyb3VwPlxuIl19