@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
118 lines • 33.7 kB
JavaScript
import { ChangeDetectionStrategy, Component, ElementRef, inject, Input, ViewChild, ViewChildren } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { HierarchyBarNodeComponent } from '../hierarchy-bar-node/hierarchy-bar-node.component';
import { HierarchyBarService } from '../hierarchy-bar.service';
import * as i0 from "@angular/core";
import * as i1 from "../../../directives/accessibility/focus-indicator/focus-indicator.directive";
import * as i2 from "../../../directives/accessibility/focus-indicator/focus-indicator-origin/focus-indicator-origin.directive";
import * as i3 from "../../../directives/accessibility/tabbable-list/tabbable-list.directive";
import * as i4 from "../../../directives/accessibility/tabbable-list/tabbable-list-item.directive";
import * as i5 from "@angular/common";
import * as i6 from "../../../directives/focus-if/focus-if.directive";
import * as i7 from "../../icon/icon.component";
import * as i8 from "../../popover/popover.directive";
import * as i9 from "../../../directives/resize/resize.directive";
import * as i10 from "../hierarchy-bar-node/hierarchy-bar-node.component";
import * as i11 from "../hierarchy-bar-popover/hierarchy-bar-popover.component";
import * as i12 from "../hierarchy-bar-popover-item/hierarchy-bar-popover-item.component";
export class HierarchyBarStandardComponent {
constructor() {
this.hierarchyBar = inject(HierarchyBarService);
/** Value in pixels to translate the visible nodes by to fill the empty space occupied by hidden nodes */
this.overflowTranslateOffset = 0;
/** Identify which nodes are overflowing */
this.overflow$ = new BehaviorSubject([]);
/** Determine if there is any overflow */
this.isOverflowing$ = new BehaviorSubject(false);
/** Unsubscribe from all subscriptions when component is destroyed */
this._onDestroy = new Subject();
// subscribe to changes in the selected node - update the UI after the render
this.hierarchyBar.nodes$
.pipe(takeUntil(this._onDestroy))
.subscribe(() => requestAnimationFrame(this.scrollIntoView.bind(this)));
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
/**
* When there is overflow ensure that the rightmost
* node remains in view at all times. The nodes no longer
* visible should be displayed in a popover available on the
* overflow indicator
*/
scrollIntoView() {
if (!this.nodelist) {
return;
}
// get the native element
const { nativeElement } = this.nodelist;
const isOverflowing = nativeElement.scrollWidth > nativeElement.offsetWidth;
// emit whether we are overflowing or not
this.isOverflowing$.next(isOverflowing);
// we don't need to do anything else if there is no overflow
if (!isOverflowing) {
this.nodeInstances.forEach((node) => (node.visible = true));
this.overflowTranslateOffset = 0;
return;
}
let isFull = false;
// find the nodes that should be visible (we start at the rightmost node)
const nodes = this.nodeInstances.toArray().reduceRight((visibleNodes, node) => {
// there must always be one visible node
if (visibleNodes.length === 0) {
return [node];
}
// if the hierarchy bar is already occupying the available space then we can skip calculations
if (isFull) {
// hide the node
node.visible = false;
return visibleNodes;
}
// get the cumulative width of all the visible nodes
const consumedWidth = visibleNodes.reduce((totalWidth, visibleNode) => totalWidth + visibleNode.width, 0);
// get the width that would be consumed if this node was included
const width = node.width + consumedWidth;
isFull = width > nativeElement.offsetWidth;
node.visible = !isFull;
if (isFull) {
this.overflowTranslateOffset = this.nodelist.nativeElement.clientWidth - consumedWidth;
}
return isFull ? visibleNodes : [...visibleNodes, node];
}, []);
if (nodes.length === 1 && this.nodelist.nativeElement.offsetWidth < nodes[0].width) {
this.overflowTranslateOffset = 0;
}
// move the scroll position to always show the last item
this.nodelist.nativeElement.scrollLeft = nativeElement.scrollWidth - nativeElement.offsetWidth;
// determine which nodes should be hidden
const nodesHidden = this.nodeInstances.filter(node => {
return !node.visible;
});
this.overflow$.next(nodesHidden.map(node => node.node));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: HierarchyBarStandardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: HierarchyBarStandardComponent, selector: "ux-hierarchy-bar-standard", inputs: { mode: "mode", readonly: "readonly" }, viewQueries: [{ propertyName: "nodelist", first: true, predicate: ["nodelist"], descendants: true, static: true }, { propertyName: "nodes", predicate: HierarchyBarNodeComponent, descendants: true, read: ElementRef }, { propertyName: "nodeInstances", predicate: HierarchyBarNodeComponent, descendants: true }, { propertyName: "barNodes", predicate: ["barNodes"], descendants: true, read: ElementRef }], ngImport: i0, template: "<!-- Allow content to be placed on the left of the items -->\n<div class=\"hierarchy-bar-addons\">\n @if (isOverflowing$ | async) {\n <div\n #popover=\"ux-popover\"\n class=\"hierarchy-bar-overflow-indicator\"\n [uxPopover]=\"overflow\"\n [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n [popoverContext]=\"{ popover: popover }\"\n placement=\"bottom\"\n popoverClass=\"hierarchy-bar-popover\">\n <ng-container\n [ngTemplateOutlet]=\"hierarchyBar.overflowTemplate || defaultOverflowTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: overflow$ | async }\">\n </ng-container>\n <button\n [attr.aria-label]=\"hierarchyBar.showSiblingsAriaLabel\"\n uxFocusIndicator\n uxFocusIndicatorOrigin\n class=\"hierarchy-bar-node-arrow\"\n placement=\"bottom\"\n [uxPopover]=\"nodeList\"\n popoverClass=\"hierarchy-bar-popover\"\n role=\"button\"\n tabindex=\"0\"\n [disabled]=\"readonly\"\n #popover=\"ux-popover\"\n [popoverContext]=\"{ popover: popover }\"\n [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n type=\"button\">\n <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"></ux-icon>\n </button>\n </div>\n }\n <ng-content select=\"left-addons\"></ng-content>\n</div>\n\n<div #nodelist class=\"hierarchy-bar-nodes\" (uxResize)=\"scrollIntoView()\">\n @for (node of hierarchyBar.nodes$ | async; track node; let i = $index; let last = $last) {\n <ux-hierarchy-bar-node\n #barNodes\n [mode]=\"mode\"\n [readonly]=\"readonly\"\n [node]=\"node\"\n [popoverTemplate]=\"content\"\n (selected)=\"hierarchyBar.selectNode(node)\"\n [offset]=\"-overflowTranslateOffset\"\n [style.max-width.px]=\"(isOverflowing$ | async) && last ? nodelist.offsetWidth : null\">\n </ux-hierarchy-bar-node>\n }\n\n <!-- Allow content to be placed after the last node -->\n <div class=\"hierarchy-bar-addons\">\n <ng-content select=\"trailing-addons\"></ng-content>\n </div>\n</div>\n\n<!-- Allow content to be placed on the right of the items -->\n<div class=\"hierarchy-bar-addons\">\n <ng-content select=\"right-addons\"></ng-content>\n</div>\n\n<!-- Template for the popover list -->\n<ng-template #content let-node=\"node\" let-popover=\"popover\">\n <ux-hierarchy-bar-popover\n [loading]=\"(hierarchyBar.getChildren(node) | async)?.loading\"\n [nodes]=\"(hierarchyBar.getChildren(node) | async)?.children\"\n (selected)=\"hierarchyBar.selectNode($event); popover.hide()\">\n </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the popover list -->\n<ng-template #nodeList let-node=\"node\" let-popover=\"popover\">\n <ux-hierarchy-bar-popover\n [nodes]=\"node\"\n (selected)=\"hierarchyBar.selectNode($event); popover.hide()\">\n </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the overflow popover list -->\n<ng-template #overflow let-popover=\"popover\">\n <div uxTabbableList [returnFocus]=\"true\">\n @for (child of overflow$ | async; track child; let first = $first) {\n <ux-hierarchy-bar-popover-item\n uxFocusIndicator\n [node]=\"child\"\n [focusIf]=\"first\"\n uxTabbableListItem\n (selected)=\"hierarchyBar.selectNode(child); popover.hide()\">\n </ux-hierarchy-bar-popover-item>\n }\n </div>\n</ng-template>\n\n<!-- Default Overflow Template -->\n<ng-template #defaultOverflowTemplate>\n <button\n uxFocusIndicator\n uxFocusIndicatorOrigin\n [disabled]=\"readonly\"\n class=\"overflow-button\"\n aria-label=\"Show parents\"\n role=\"button\"\n type=\"button\">\n <ux-icon name=\"more\"></ux-icon>\n </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i1.FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i2.FocusIndicatorOriginDirective, selector: "[uxFocusIndicatorOrigin]" }, { kind: "directive", type: i3.TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: i4.TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i6.FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "component", type: i7.IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: i8.PopoverDirective, selector: "[uxPopover]", inputs: ["uxPopover", "popoverTitle", "popoverDisabled", "popoverClass", "popoverRole", "popoverContext", "popoverDelay", "showTriggers", "hideTriggers"], exportAs: ["ux-popover"] }, { kind: "directive", type: i9.ResizeDirective, selector: "[uxResize]", inputs: ["throttle"], outputs: ["uxResize"] }, { kind: "component", type: i10.HierarchyBarNodeComponent, selector: "ux-hierarchy-bar-node", inputs: ["node", "popoverTemplate", "mode", "readonly", "offset"], outputs: ["selected"] }, { kind: "component", type: i11.HierarchyBarPopoverComponent, selector: "ux-hierarchy-bar-popover", inputs: ["nodes", "loading", "separator"], outputs: ["selected"] }, { kind: "component", type: i12.HierarchyBarPopoverItemComponent, selector: "ux-hierarchy-bar-popover-item", inputs: ["node"], outputs: ["selected"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: HierarchyBarStandardComponent, decorators: [{
type: Component,
args: [{ selector: 'ux-hierarchy-bar-standard', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Allow content to be placed on the left of the items -->\n<div class=\"hierarchy-bar-addons\">\n @if (isOverflowing$ | async) {\n <div\n #popover=\"ux-popover\"\n class=\"hierarchy-bar-overflow-indicator\"\n [uxPopover]=\"overflow\"\n [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n [popoverContext]=\"{ popover: popover }\"\n placement=\"bottom\"\n popoverClass=\"hierarchy-bar-popover\">\n <ng-container\n [ngTemplateOutlet]=\"hierarchyBar.overflowTemplate || defaultOverflowTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: overflow$ | async }\">\n </ng-container>\n <button\n [attr.aria-label]=\"hierarchyBar.showSiblingsAriaLabel\"\n uxFocusIndicator\n uxFocusIndicatorOrigin\n class=\"hierarchy-bar-node-arrow\"\n placement=\"bottom\"\n [uxPopover]=\"nodeList\"\n popoverClass=\"hierarchy-bar-popover\"\n role=\"button\"\n tabindex=\"0\"\n [disabled]=\"readonly\"\n #popover=\"ux-popover\"\n [popoverContext]=\"{ popover: popover }\"\n [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n type=\"button\">\n <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"></ux-icon>\n </button>\n </div>\n }\n <ng-content select=\"left-addons\"></ng-content>\n</div>\n\n<div #nodelist class=\"hierarchy-bar-nodes\" (uxResize)=\"scrollIntoView()\">\n @for (node of hierarchyBar.nodes$ | async; track node; let i = $index; let last = $last) {\n <ux-hierarchy-bar-node\n #barNodes\n [mode]=\"mode\"\n [readonly]=\"readonly\"\n [node]=\"node\"\n [popoverTemplate]=\"content\"\n (selected)=\"hierarchyBar.selectNode(node)\"\n [offset]=\"-overflowTranslateOffset\"\n [style.max-width.px]=\"(isOverflowing$ | async) && last ? nodelist.offsetWidth : null\">\n </ux-hierarchy-bar-node>\n }\n\n <!-- Allow content to be placed after the last node -->\n <div class=\"hierarchy-bar-addons\">\n <ng-content select=\"trailing-addons\"></ng-content>\n </div>\n</div>\n\n<!-- Allow content to be placed on the right of the items -->\n<div class=\"hierarchy-bar-addons\">\n <ng-content select=\"right-addons\"></ng-content>\n</div>\n\n<!-- Template for the popover list -->\n<ng-template #content let-node=\"node\" let-popover=\"popover\">\n <ux-hierarchy-bar-popover\n [loading]=\"(hierarchyBar.getChildren(node) | async)?.loading\"\n [nodes]=\"(hierarchyBar.getChildren(node) | async)?.children\"\n (selected)=\"hierarchyBar.selectNode($event); popover.hide()\">\n </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the popover list -->\n<ng-template #nodeList let-node=\"node\" let-popover=\"popover\">\n <ux-hierarchy-bar-popover\n [nodes]=\"node\"\n (selected)=\"hierarchyBar.selectNode($event); popover.hide()\">\n </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the overflow popover list -->\n<ng-template #overflow let-popover=\"popover\">\n <div uxTabbableList [returnFocus]=\"true\">\n @for (child of overflow$ | async; track child; let first = $first) {\n <ux-hierarchy-bar-popover-item\n uxFocusIndicator\n [node]=\"child\"\n [focusIf]=\"first\"\n uxTabbableListItem\n (selected)=\"hierarchyBar.selectNode(child); popover.hide()\">\n </ux-hierarchy-bar-popover-item>\n }\n </div>\n</ng-template>\n\n<!-- Default Overflow Template -->\n<ng-template #defaultOverflowTemplate>\n <button\n uxFocusIndicator\n uxFocusIndicatorOrigin\n [disabled]=\"readonly\"\n class=\"overflow-button\"\n aria-label=\"Show parents\"\n role=\"button\"\n type=\"button\">\n <ux-icon name=\"more\"></ux-icon>\n </button>\n</ng-template>\n" }]
}], ctorParameters: () => [], propDecorators: { mode: [{
type: Input
}], readonly: [{
type: Input
}], nodelist: [{
type: ViewChild,
args: ['nodelist', { static: true }]
}], nodes: [{
type: ViewChildren,
args: [HierarchyBarNodeComponent, { read: ElementRef }]
}], nodeInstances: [{
type: ViewChildren,
args: [HierarchyBarNodeComponent]
}], barNodes: [{
type: ViewChildren,
args: ['barNodes', { read: ElementRef }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGllcmFyY2h5LWJhci1zdGFuZGFyZC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy9oaWVyYXJjaHktYmFyL2hpZXJhcmNoeS1iYXItc3RhbmRhcmQvaGllcmFyY2h5LWJhci1zdGFuZGFyZC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy9oaWVyYXJjaHktYmFyL2hpZXJhcmNoeS1iYXItc3RhbmRhcmQvaGllcmFyY2h5LWJhci1zdGFuZGFyZC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUF3QixTQUFTLEVBQUUsWUFBWSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzdJLE9BQU8sRUFBRSxlQUFlLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2hELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSxvREFBb0QsQ0FBQztBQUMvRixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUFRL0QsTUFBTSxPQUFPLDZCQUE2QjtJQWdDdEM7UUEvQlMsaUJBQVksR0FBRyxNQUFNLENBQUMsbUJBQW1CLENBQUMsQ0FBQztRQW1CcEQseUdBQXlHO1FBQ3pHLDRCQUF1QixHQUFHLENBQUMsQ0FBQztRQUU1QiwyQ0FBMkM7UUFDM0MsY0FBUyxHQUFHLElBQUksZUFBZSxDQUFxQixFQUFFLENBQUMsQ0FBQztRQUV4RCx5Q0FBeUM7UUFDekMsbUJBQWMsR0FBRyxJQUFJLGVBQWUsQ0FBVSxLQUFLLENBQUMsQ0FBQztRQUVyRCxxRUFBcUU7UUFDcEQsZUFBVSxHQUFHLElBQUksT0FB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bGVtZW50UmVmLCBpbmplY3QsIElucHV0LCBPbkRlc3Ryb3ksIFF1ZXJ5TGlzdCwgVmlld0NoaWxkLCBWaWV3Q2hpbGRyZW4gfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgSGllcmFyY2h5QmFyTm9kZUNvbXBvbmVudCB9IGZyb20gJy4uL2hpZXJhcmNoeS1iYXItbm9kZS9oaWVyYXJjaHktYmFyLW5vZGUuY29tcG9uZW50JztcbmltcG9ydCB7IEhpZXJhcmNoeUJhclNlcnZpY2UgfSBmcm9tICcuLi9oaWVyYXJjaHktYmFyLnNlcnZpY2UnO1xuaW1wb3J0IHsgSGllcmFyY2h5QmFyTm9kZSB9IGZyb20gJy4uL2ludGVyZmFjZXMvaGllcmFyY2h5LWJhci1ub2RlLmludGVyZmFjZSc7XG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAndXgtaGllcmFyY2h5LWJhci1zdGFuZGFyZCcsXG4gICAgdGVtcGxhdGVVcmw6ICcuL2hpZXJhcmNoeS1iYXItc3RhbmRhcmQuY29tcG9uZW50Lmh0bWwnLFxuICAgIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBIaWVyYXJjaHlCYXJTdGFuZGFyZENvbXBvbmVudCBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG4gICAgcmVhZG9ubHkgaGllcmFyY2h5QmFyID0gaW5qZWN0KEhpZXJhcmNoeUJhclNlcnZpY2UpO1xuXG4gICAgLyoqIERldGVybWluZSB0aGUgbW9kZSBvZiB0aGUgaGllcmFyY2h5IGJhciAqL1xuICAgIEBJbnB1dCgpIG1vZGU6IHN0cmluZztcblxuICAgIC8qKiBEZXRlcm1pbmUgcmVhZCBvbmx5IHN0YXRlICovXG4gICAgQElucHV0KCkgcmVhZG9ubHk6IGJvb2xlYW47XG5cbiAgICAvKiogR2V0IHRoZSBlbGVtZW50UmVmIG9mIHRoZSBub2RlIGxpc3QgKi9cbiAgICBAVmlld0NoaWxkKCdub2RlbGlzdCcsIHsgc3RhdGljOiB0cnVlIH0pIG5vZGVsaXN0OiBFbGVtZW50UmVmPEhUTUxEaXZFbGVtZW50PjtcblxuICAgIC8qKiBHZXQgZWxlbWVudFJlZnMgZm9yIGFsbCB0aGUgbm9kZXMgKi9cbiAgICBAVmlld0NoaWxkcmVuKEhpZXJhcmNoeUJhck5vZGVDb21wb25lbnQsIHsgcmVhZDogRWxlbWVudFJlZiB9KSBub2RlczogUXVlcnlMaXN0PEVsZW1lbnRSZWY+O1xuXG4gICAgLyoqIEdldCBpbnN0YW5jZXMgZm9yIGFsbCB0aGUgbm9kZXMgKi9cbiAgICBAVmlld0NoaWxkcmVuKEhpZXJhcmNoeUJhck5vZGVDb21wb25lbnQpIG5vZGVJbnN0YW5jZXM6IFF1ZXJ5TGlzdDxIaWVyYXJjaHlCYXJOb2RlQ29tcG9uZW50PjtcblxuICAgIEBWaWV3Q2hpbGRyZW4oJ2Jhck5vZGVzJywgeyByZWFkOiBFbGVtZW50UmVmIH0pIGJhck5vZGVzOiBRdWVyeUxpc3Q8RWxlbWVudFJlZj47XG5cbiAgICAvKiogVmFsdWUgaW4gcGl4ZWxzIHRvIHRyYW5zbGF0ZSB0aGUgdmlzaWJsZSBub2RlcyBieSB0byBmaWxsIHRoZSBlbXB0eSBzcGFjZSBvY2N1cGllZCBieSBoaWRkZW4gbm9kZXMgKi9cbiAgICBvdmVyZmxvd1RyYW5zbGF0ZU9mZnNldCA9IDA7XG5cbiAgICAvKiogSWRlbnRpZnkgd2hpY2ggbm9kZXMgYXJlIG92ZXJmbG93aW5nICovXG4gICAgb3ZlcmZsb3ckID0gbmV3IEJlaGF2aW9yU3ViamVjdDxIaWVyYXJjaHlCYXJOb2RlW10+KFtdKTtcblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgdGhlcmUgaXMgYW55IG92ZXJmbG93ICovXG4gICAgaXNPdmVyZmxvd2luZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcblxuICAgIC8qKiBVbnN1YnNjcmliZSBmcm9tIGFsbCBzdWJzY3JpcHRpb25zIHdoZW4gY29tcG9uZW50IGlzIGRlc3Ryb3llZCAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX29uRGVzdHJveSA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgLy8gc3Vic2NyaWJlIHRvIGNoYW5nZXMgaW4gdGhlIHNlbGVjdGVkIG5vZGUgLSB1cGRhdGUgdGhlIFVJIGFmdGVyIHRoZSByZW5kZXJcbiAgICAgICAgdGhpcy5oaWVyYXJjaHlCYXIubm9kZXMkXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKCkgPT4gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKHRoaXMuc2Nyb2xsSW50b1ZpZXcuYmluZCh0aGlzKSkpO1xuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kubmV4dCgpO1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBXaGVuIHRoZXJlIGlzIG92ZXJmbG93IGVuc3VyZSB0aGF0IHRoZSByaWdodG1vc3RcbiAgICAgKiBub2RlIHJlbWFpbnMgaW4gdmlldyBhdCBhbGwgdGltZXMuIFRoZSBub2RlcyBubyBsb25nZXJcbiAgICAgKiB2aXNpYmxlIHNob3VsZCBiZSBkaXNwbGF5ZWQgaW4gYSBwb3BvdmVyIGF2YWlsYWJsZSBvbiB0aGVcbiAgICAgKiBvdmVyZmxvdyBpbmRpY2F0b3JcbiAgICAgKi9cbiAgICBzY3JvbGxJbnRvVmlldygpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLm5vZGVsaXN0KSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBnZXQgdGhlIG5hdGl2ZSBlbGVtZW50XG4gICAgICAgIGNvbnN0IHsgbmF0aXZlRWxlbWVudCB9ID0gdGhpcy5ub2RlbGlzdDtcbiAgICAgICAgY29uc3QgaXNPdmVyZmxvd2luZyA9IG5hdGl2ZUVsZW1lbnQuc2Nyb2xsV2lkdGggPiBuYXRpdmVFbGVtZW50Lm9mZnNldFdpZHRoO1xuXG4gICAgICAgIC8vIGVtaXQgd2hldGhlciB3ZSBhcmUgb3ZlcmZsb3dpbmcgb3Igbm90XG4gICAgICAgIHRoaXMuaXNPdmVyZmxvd2luZyQubmV4dChpc092ZXJmbG93aW5nKTtcblxuICAgICAgICAvLyB3ZSBkb24ndCBuZWVkIHRvIGRvIGFueXRoaW5nIGVsc2UgaWYgdGhlcmUgaXMgbm8gb3ZlcmZsb3dcbiAgICAgICAgaWYgKCFpc092ZXJmbG93aW5nKSB7XG4gICAgICAgICAgICB0aGlzLm5vZGVJbnN0YW5jZXMuZm9yRWFjaCgobm9kZSkgPT4gKG5vZGUudmlzaWJsZSA9IHRydWUpKTtcbiAgICAgICAgICAgIHRoaXMub3ZlcmZsb3dUcmFuc2xhdGVPZmZzZXQgPSAwO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGlzRnVsbDogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgICAgIC8vIGZpbmQgdGhlIG5vZGVzIHRoYXQgc2hvdWxkIGJlIHZpc2libGUgKHdlIHN0YXJ0IGF0IHRoZSByaWdodG1vc3Qgbm9kZSlcbiAgICAgICAgY29uc3Qgbm9kZXMgPSB0aGlzLm5vZGVJbnN0YW5jZXMudG9BcnJheSgpLnJlZHVjZVJpZ2h0PEhpZXJhcmNoeUJhck5vZGVDb21wb25lbnRbXT4oKHZpc2libGVOb2Rlcywgbm9kZSkgPT4ge1xuICAgICAgICAgICAgLy8gdGhlcmUgbXVzdCBhbHdheXMgYmUgb25lIHZpc2libGUgbm9kZVxuICAgICAgICAgICAgaWYgKHZpc2libGVOb2Rlcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gW25vZGVdO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBpZiB0aGUgaGllcmFyY2h5IGJhciBpcyBhbHJlYWR5IG9jY3VweWluZyB0aGUgYXZhaWxhYmxlIHNwYWNlIHRoZW4gd2UgY2FuIHNraXAgY2FsY3VsYXRpb25zXG4gICAgICAgICAgICBpZiAoaXNGdWxsKSB7XG4gICAgICAgICAgICAgICAgLy8gaGlkZSB0aGUgbm9kZVxuICAgICAgICAgICAgICAgIG5vZGUudmlzaWJsZSA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHJldHVybiB2aXNpYmxlTm9kZXM7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIGdldCB0aGUgY3VtdWxhdGl2ZSB3aWR0aCBvZiBhbGwgdGhlIHZpc2libGUgbm9kZXNcbiAgICAgICAgICAgIGNvbnN0IGNvbnN1bWVkV2lkdGggPSB2aXNpYmxlTm9kZXMucmVkdWNlKCh0b3RhbFdpZHRoLCB2aXNpYmxlTm9kZSkgPT4gdG90YWxXaWR0aCArIHZpc2libGVOb2RlLndpZHRoLCAwKTtcblxuICAgICAgICAgICAgLy8gZ2V0IHRoZSB3aWR0aCB0aGF0IHdvdWxkIGJlIGNvbnN1bWVkIGlmIHRoaXMgbm9kZSB3YXMgaW5jbHVkZWRcbiAgICAgICAgICAgIGNvbnN0IHdpZHRoID0gbm9kZS53aWR0aCArIGNvbnN1bWVkV2lkdGg7XG4gICAgICAgICAgICBpc0Z1bGwgPSB3aWR0aCA+IG5hdGl2ZUVsZW1lbnQub2Zmc2V0V2lkdGg7XG4gICAgICAgICAgICBub2RlLnZpc2libGUgPSAhaXNGdWxsO1xuXG4gICAgICAgICAgICBpZiAoaXNGdWxsKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vdmVyZmxvd1RyYW5zbGF0ZU9mZnNldCA9IHRoaXMubm9kZWxpc3QubmF0aXZlRWxlbWVudC5jbGllbnRXaWR0aCAtIGNvbnN1bWVkV2lkdGg7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBpc0Z1bGwgPyB2aXNpYmxlTm9kZXMgOiBbLi4udmlzaWJsZU5vZGVzLCBub2RlXTtcbiAgICAgICAgfSwgW10pO1xuXG4gICAgICAgIGlmIChub2Rlcy5sZW5ndGggPT09IDEgJiYgdGhpcy5ub2RlbGlzdC5uYXRpdmVFbGVtZW50Lm9mZnNldFdpZHRoIDwgbm9kZXNbMF0ud2lkdGgpIHtcbiAgICAgICAgICAgIHRoaXMub3ZlcmZsb3dUcmFuc2xhdGVPZmZzZXQgPSAwO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gbW92ZSB0aGUgc2Nyb2xsIHBvc2l0aW9uIHRvIGFsd2F5cyBzaG93IHRoZSBsYXN0IGl0ZW1cbiAgICAgICAgdGhpcy5ub2RlbGlzdC5uYXRpdmVFbGVtZW50LnNjcm9sbExlZnQgPSBuYXRpdmVFbGVtZW50LnNjcm9sbFdpZHRoIC0gbmF0aXZlRWxlbWVudC5vZmZzZXRXaWR0aDtcblxuICAgICAgICAvLyBkZXRlcm1pbmUgd2hpY2ggbm9kZXMgc2hvdWxkIGJlIGhpZGRlblxuICAgICAgICBjb25zdCBub2Rlc0hpZGRlbiA9IHRoaXMubm9kZUluc3RhbmNlcy5maWx0ZXIobm9kZSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gIW5vZGUudmlzaWJsZTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMub3ZlcmZsb3ckLm5leHQobm9kZXNIaWRkZW4ubWFwKG5vZGUgPT4gbm9kZS5ub2RlKSk7XG4gICAgfVxufVxuIiwiPCEtLSBBbGxvdyBjb250ZW50IHRvIGJlIHBsYWNlZCBvbiB0aGUgbGVmdCBvZiB0aGUgaXRlbXMgLS0+XG48ZGl2IGNsYXNzPVwiaGllcmFyY2h5LWJhci1hZGRvbnNcIj5cbiAgQGlmIChpc092ZXJmbG93aW5nJCB8IGFzeW5jKSB7XG4gICAgPGRpdlxuICAgICAgI3BvcG92ZXI9XCJ1eC1wb3BvdmVyXCJcbiAgICAgIGNsYXNzPVwiaGllcmFyY2h5LWJhci1vdmVyZmxvdy1pbmRpY2F0b3JcIlxuICAgICAgW3V4UG9wb3Zlcl09XCJvdmVyZmxvd1wiXG4gICAgICBbc2hvd1RyaWdnZXJzXT1cImhpZXJhcmNoeUJhci5wb3BvdmVyU2hvd1RyaWdnZXJzXCJcbiAgICAgIFtoaWRlVHJpZ2dlcnNdPVwiaGllcmFyY2h5QmFyLnBvcG92ZXJIaWRlVHJpZ2dlcnNcIlxuICAgICAgW3BvcG92ZXJDb250ZXh0XT1cInsgcG9wb3ZlcjogcG9wb3ZlciB9XCJcbiAgICAgIHBsYWNlbWVudD1cImJvdHRvbVwiXG4gICAgICBwb3BvdmVyQ2xhc3M9XCJoaWVyYXJjaHktYmFyLXBvcG92ZXJcIj5cbiAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgW25nVGVtcGxhdGVPdXRsZXRdPVwiaGllcmFyY2h5QmFyLm92ZXJmbG93VGVtcGxhdGUgfHwgZGVmYXVsdE92ZXJmbG93VGVtcGxhdGVcIlxuICAgICAgICBbbmdUZW1wbGF0ZU91dGxldENvbnRleHRdPVwieyAkaW1wbGljaXQ6IG92ZXJmbG93JCB8IGFzeW5jIH1cIj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPGJ1dHRvblxuICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cImhpZXJhcmNoeUJhci5zaG93U2libGluZ3NBcmlhTGFiZWxcIlxuICAgICAgICB1eEZvY3VzSW5kaWNhdG9yXG4gICAgICAgIHV4Rm9jdXNJbmRpY2F0b3JPcmlnaW5cbiAgICAgICAgY2xhc3M9XCJoaWVyYXJjaHktYmFyLW5vZGUtYXJyb3dcIlxuICAgICAgICBwbGFjZW1lbnQ9XCJib3R0b21cIlxuICAgICAgICBbdXhQb3BvdmVyXT1cIm5vZGVMaXN0XCJcbiAgICAgICAgcG9wb3ZlckNsYXNzPVwiaGllcmFyY2h5LWJhci1wb3BvdmVyXCJcbiAgICAgICAgcm9sZT1cImJ1dHRvblwiXG4gICAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICAgIFtkaXNhYmxlZF09XCJyZWFkb25seVwiXG4gICAgICAgICNwb3BvdmVyPVwidXgtcG9wb3ZlclwiXG4gICAgICAgIFtwb3BvdmVyQ29udGV4dF09XCJ7IHBvcG92ZXI6IHBvcG92ZXIgfVwiXG4gICAgICAgIFtzaG93VHJpZ2dlcnNdPVwiaGllcmFyY2h5QmFyLnBvcG92ZXJTaG93VHJpZ2dlcnNcIlxuICAgICAgICBbaGlkZVRyaWdnZXJzXT1cImhpZXJhcmNoeUJhci5wb3BvdmVySGlkZVRyaWdnZXJzXCJcbiAgICAgICAgdHlwZT1cImJ1dHRvblwiPlxuICAgICAgICA8dXgtaWNvbiBuYW1lPVwibmV4dFwiIGNsYXNzPVwiaGllcmFyY2h5LWJhci1ub2RlLWFycm93LWljb25cIj48L3V4LWljb24+XG4gICAgICA8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgfVxuICA8bmctY29udGVudCBzZWxlY3Q9XCJsZWZ0LWFkZG9uc1wiPjwvbmctY29udGVudD5cbjwvZGl2PlxuXG48ZGl2ICNub2RlbGlzdCBjbGFzcz1cImhpZXJhcmNoeS1iYXItbm9kZXNcIiAodXhSZXNpemUpPVwic2Nyb2xsSW50b1ZpZXcoKVwiPlxuICBAZm9yIChub2RlIG9mIGhpZXJhcmNoeUJhci5ub2RlcyQgfCBhc3luYzsgdHJhY2sgbm9kZTsgbGV0IGkgPSAkaW5kZXg7IGxldCBsYXN0ID0gJGxhc3QpIHtcbiAgICA8dXgtaGllcmFyY2h5LWJhci1ub2RlXG4gICAgICAjYmFyTm9kZXNcbiAgICAgIFttb2RlXT1cIm1vZGVcIlxuICAgICAgW3JlYWRvbmx5XT1cInJlYWRvbmx5XCJcbiAgICAgIFtub2RlXT1cIm5vZGVcIlxuICAgICAgW3BvcG92ZXJUZW1wbGF0ZV09XCJjb250ZW50XCJcbiAgICAgIChzZWxlY3RlZCk9XCJoaWVyYXJjaHlCYXIuc2VsZWN0Tm9kZShub2RlKVwiXG4gICAgICBbb2Zmc2V0XT1cIi1vdmVyZmxvd1RyYW5zbGF0ZU9mZnNldFwiXG4gICAgICBbc3R5bGUubWF4LXdpZHRoLnB4XT1cIihpc092ZXJmbG93aW5nJCB8IGFzeW5jKSAmJiBsYXN0ID8gbm9kZWxpc3Qub2Zmc2V0V2lkdGggOiBudWxsXCI+XG4gICAgPC91eC1oaWVyYXJjaHktYmFyLW5vZGU+XG4gIH1cblxuICA8IS0tIEFsbG93IGNvbnRlbnQgdG8gYmUgcGxhY2VkIGFmdGVyIHRoZSBsYXN0IG5vZGUgLS0+XG4gIDxkaXYgY2xhc3M9XCJoaWVyYXJjaHktYmFyLWFkZG9uc1wiPlxuICAgIDxuZy1jb250ZW50IHNlbGVjdD1cInRyYWlsaW5nLWFkZG9uc1wiPjwvbmctY29udGVudD5cbiAgPC9kaXY+XG48L2Rpdj5cblxuPCEtLSBBbGxvdyBjb250ZW50IHRvIGJlIHBsYWNlZCBvbiB0aGUgcmlnaHQgb2YgdGhlIGl0ZW1zIC0tPlxuPGRpdiBjbGFzcz1cImhpZXJhcmNoeS1iYXItYWRkb25zXCI+XG4gIDxuZy1jb250ZW50IHNlbGVjdD1cInJpZ2h0LWFkZG9uc1wiPjwvbmctY29udGVudD5cbjwvZGl2PlxuXG48IS0tIFRlbXBsYXRlIGZvciB0aGUgcG9wb3ZlciBsaXN0IC0tPlxuPG5nLXRlbXBsYXRlICNjb250ZW50IGxldC1ub2RlPVwibm9kZVwiIGxldC1wb3BvdmVyPVwicG9wb3ZlclwiPlxuICA8dXgtaGllcmFyY2h5LWJhci1wb3BvdmVyXG4gICAgW2xvYWRpbmddPVwiKGhpZXJhcmNoeUJhci5nZXRDaGlsZHJlbihub2RlKSB8IGFzeW5jKT8ubG9hZGluZ1wiXG4gICAgW25vZGVzXT1cIihoaWVyYXJjaHlCYXIuZ2V0Q2hpbGRyZW4obm9kZSkgfCBhc3luYyk/LmNoaWxkcmVuXCJcbiAgICAoc2VsZWN0ZWQpPVwiaGllcmFyY2h5QmFyLnNlbGVjdE5vZGUoJGV2ZW50KTsgcG9wb3Zlci5oaWRlKClcIj5cbiAgPC91eC1oaWVyYXJjaHktYmFyLXBvcG92ZXI+XG48L25nLXRlbXBsYXRlPlxuXG48IS0tIFRlbXBsYXRlIGZvciB0aGUgcG9wb3ZlciBsaXN0IC0tPlxuPG5nLXRlbXBsYXRlICNub2RlTGlzdCBsZXQtbm9kZT1cIm5vZGVcIiBsZXQtcG9wb3Zlcj1cInBvcG92ZXJcIj5cbiAgPHV4LWhpZXJhcmNoeS1iYXItcG9wb3ZlclxuICAgIFtub2Rlc109XCJub2RlXCJcbiAgICAoc2VsZWN0ZWQpPVwiaGllcmFyY2h5QmFyLnNlbGVjdE5vZGUoJGV2ZW50KTsgcG9wb3Zlci5oaWRlKClcIj5cbiAgPC91eC1oaWVyYXJjaHktYmFyLXBvcG92ZXI+XG48L25nLXRlbXBsYXRlPlxuXG48IS0tIFRlbXBsYXRlIGZvciB0aGUgb3ZlcmZsb3cgcG9wb3ZlciBsaXN0IC0tPlxuPG5nLXRlbXBsYXRlICNvdmVyZmxvdyBsZXQtcG9wb3Zlcj1cInBvcG92ZXJcIj5cbiAgPGRpdiB1eFRhYmJhYmxlTGlzdCBbcmV0dXJuRm9jdXNdPVwidHJ1ZVwiPlxuICAgIEBmb3IgKGNoaWxkIG9mIG92ZXJmbG93JCB8IGFzeW5jOyB0cmFjayBjaGlsZDsgbGV0IGZpcnN0ID0gJGZpcnN0KSB7XG4gICAgICA8dXgtaGllcmFyY2h5LWJhci1wb3BvdmVyLWl0ZW1cbiAgICAgICAgdXhGb2N1c0luZGljYXRvclxuICAgICAgICBbbm9kZV09XCJjaGlsZFwiXG4gICAgICAgIFtmb2N1c0lmXT1cImZpcnN0XCJcbiAgICAgICAgdXhUYWJiYWJsZUxpc3RJdGVtXG4gICAgICAgIChzZWxlY3RlZCk9XCJoaWVyYXJjaHlCYXIuc2VsZWN0Tm9kZShjaGlsZCk7IHBvcG92ZXIuaGlkZSgpXCI+XG4gICAgICA8L3V4LWhpZXJhcmNoeS1iYXItcG9wb3Zlci1pdGVtPlxuICAgIH1cbiAgPC9kaXY+XG48L25nLXRlbXBsYXRlPlxuXG48IS0tIERlZmF1bHQgT3ZlcmZsb3cgVGVtcGxhdGUgLS0+XG48bmctdGVtcGxhdGUgI2RlZmF1bHRPdmVyZmxvd1RlbXBsYXRlPlxuICA8YnV0dG9uXG4gICAgdXhGb2N1c0luZGljYXRvclxuICAgIHV4Rm9jdXNJbmRpY2F0b3JPcmlnaW5cbiAgICBbZGlzYWJsZWRdPVwicmVhZG9ubHlcIlxuICAgIGNsYXNzPVwib3ZlcmZsb3ctYnV0dG9uXCJcbiAgICBhcmlhLWxhYmVsPVwiU2hvdyBwYXJlbnRzXCJcbiAgICByb2xlPVwiYnV0dG9uXCJcbiAgICB0eXBlPVwiYnV0dG9uXCI+XG4gICAgPHV4LWljb24gbmFtZT1cIm1vcmVcIj48L3V4LWljb24+XG4gIDwvYnV0dG9uPlxuPC9uZy10ZW1wbGF0ZT5cbiJdfQ==