@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
287 lines • 48.4 kB
JavaScript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core';
import { arc, easeCubic, interpolate, pointer, select, transition } from 'd3';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Color } from '../../common/colors/index';
import { ResizeService } from '../../directives/resize/index';
import { ColorService, ThemeColor } from '../../services/color/index';
import * as i0 from "@angular/core";
import * as i1 from "../tooltip/tooltip.component";
export class NestedDonutChartComponent {
constructor() {
this._colorService = inject(ColorService);
this._changeDetector = inject(ChangeDetectorRef);
this._elementRef = inject(ElementRef);
this._resizeService = inject(ResizeService);
/** Define the maximum range of the arcs */
this.max = 100;
/** Define the thickness of each arc */
this.thickness = 8;
/** Define the spacing of each arc */
this.spacing = 8;
/** Determine if we should show the hover effect */
this.disableHover = false;
/** Determine if we should show a tooltip on arc hover */
this.disableTooltip = false;
/** Determine the position of the tooltip */
this.tooltipPlacement = 'top';
/** Set the duration of the animation */
this.animationDuration = 750;
/** Emit whenever an arc is clicked */
this.itemClick = new EventEmitter();
/** Indicate if the tooltip should be visible */
this._tooltipVisible = false;
/** Store the previously processed data */
this._arcData = [];
/** Determine if the intial render has taken place */
this._isInitialized = false;
/** Unsubscribe from all observables automatically */
this._onDestroy = new Subject();
}
/** Determine the radius of the chart based on the specified size */
get _radius() {
return this._size / 2;
}
/**
* Get the size of the chart. The chart will always be square to
* the size will be the smaller of the width/height properties
*/
get _size() {
return Math.min(this._elementRef.nativeElement.offsetWidth, this._elementRef.nativeElement.offsetHeight);
}
/** Perform the initial render */
ngOnInit() {
// create the selection where we will draw the tracks
this._trackLayer = select(this._chartElement.nativeElement)
.append('g');
// create the selection where we will draw the arcs
this._arcLayer = select(this._chartElement.nativeElement)
.append('g');
// create the arcs representing the data
this.render();
// mark the component as initialized
this._isInitialized = true;
// listen for any resizing - skip the first emission as it always emits on first subscribe
this._resizeService.addResizeListener(this._elementRef.nativeElement)
.pipe(takeUntil(this._onDestroy)).subscribe(() => {
this.render();
this._changeDetector.markForCheck();
});
}
/** Any time an input changes we must re-render the chart */
ngOnChanges() {
if (this._isInitialized) {
this.render();
}
}
ngOnDestroy() {
this._resizeService.removeResizeListener(this._elementRef.nativeElement);
this._onDestroy.next();
this._onDestroy.complete();
}
/** Inset the content so it never overlaps the arcs */
_getContentInset() {
return this.dataset.length * (this.spacing + this.thickness);
}
/** Get the dimensions of the content area */
_getContentSize() {
return this._size - (this._getContentInset() * 2);
}
/** Get the dataset formated in an accessible manner */
_getAriaLabel() {
return this.dataset.map(data => `${data.value} ${data.name}`).join('. ');
}
/**
* Display the tracks and arcs defined by the dataset.
* We also provide the transition configuration so anytime the dataset
* changes we will animate the update.
*/
render() {
// update the transform of the layers
this._trackLayer.attr('transform', `translate(${this._radius}, ${this._radius})`);
this._arcLayer.attr('transform', `translate(${this._radius}, ${this._radius})`);
// create the arcs based on the dataset
this._arcs = this._arcLayer.selectAll('path')
.data(this.getChartData());
// create the default transition based on the specified duration
const arcTransition = transition('nestedDonutArcTransition').ease(easeCubic).duration(this.animationDuration);
// create the tracks based on the dataset
this._tracks = this._trackLayer.selectAll('path')
.data(this.getChartData())
.enter()
.append('path')
.attr('class', 'ux-nested-donut-chart-track');
// set the track color on each render in case the input has changed
this._trackLayer.selectAll('path')
.attr('d', this.getTrackArc())
.style('fill', () => this.getTrackColor());
// if an arc is removed then also remove the track
this._tracks.exit().remove();
// When a new arc is added we should create the element
// size it and provide the background color and begin the
// animation until it reaches its final angle
this._arcs.enter()
.append('path')
.attr('class', 'ux-nested-donut-chart-arc')
.style('fill', data => this.getColor(data.color))
.attr('opacity', 1)
.on('click', data => this.itemClick.emit(data))
.on('mouseenter', (event, node) => this.onArcMouseEnter(event.srcElement, node))
.on('mousemove', (event) => this.onArcMouseMove(pointer(event, this._chartElement.nativeElement)))
.on('mouseleave', (event) => this.onArcMouseLeave(event.srcElement))
.transition(arcTransition)
.attrTween('d', this.getArcTween.bind(this));
// any time an existing dataset value changes
// we should update the angle with an animation
// we also animate any color changes also.
this._arcs.transition(arcTransition)
.style('fill', data => this.getColor(data.color))
.attrTween('d', this.getArcTween.bind(this));
// when a dataset it removed animate the arc out
// and then remove the associated DOM element
this._arcs.exit().transition(arcTransition)
.attrTween('d', this.getArcTween.bind(this))
.remove();
}
/** Get the interpolation function based on the new and previous angle */
getArcTween(data) {
// create a new interpolation function with a new endAngle
const interpolation = interpolate({ ...data, endAngle: data.previousEndAngle }, data);
// return the function that will produce the interpolation
return (delta) => this.getArc()(interpolation(delta));
}
/** Get the arc layout for a specific item in the dataset */
getArc() {
return arc()
.innerRadius(data => this.getArcRadius(data.index))
.outerRadius(data => this.getArcRadius(data.index) + this.thickness)
.startAngle(data => data.startAngle)
.endAngle(data => data.endAngle);
}
/**
* Get the track arc layout for a specific item in the dataset.
* This will match the arc of that represents the actual data
* however the endAngle will always be a complete circle
*/
getTrackArc() {
return this.getArc().endAngle(() => Math.PI * 2);
}
/**
* Get the radius of an arc. This is calculated
* based on the chart radius that has been defined,
* minus the thickness defined, then taking into account
* the depth of the arc and the spacing between each arc.
*/
getArcRadius(index) {
return (this._radius - this.thickness) - (index * (this.thickness + this.spacing));
}
/**
* Map the dataset to the NestedDonutChartArc interface
*/
getChartData() {
const dataset = this.dataset.map((data, index) => {
let previousEndAngle = 0;
// check if there was a previous dataset at this index
if (this._arcData && this._arcData[index]) {
previousEndAngle = this._arcData[index].endAngle;
}
return { ...data, index, startAngle: 0, endAngle: this.getAngle(data), previousEndAngle };
});
// store the latest processed arc data
this._arcData = dataset;
return dataset;
}
/** Convert the data value to radians */
getAngle(data) {
const fraction = data.value / this.max;
const degrees = fraction * 360.0;
return degrees * (Math.PI / 180);
}
/**
* Get the color of the arc, this may be a CSS color value, the name of a color
* from the color set or a ThemeColor object. We return this as a rgba color to
* support the alpha channel
*/
getColor(color) {
return ThemeColor.isInstanceOf(color) ? color.toRgba() : this._colorService.resolve(color);
}
/** If no track color is specified then default to a specific color based on the active colorset */
getTrackColor() {
if (this.trackColor) {
return this.getColor(this.trackColor);
}
// otherwise default to a color based on the colorset (note we can't use the Color enum from MF package)
if (this._colorService.colorExists(Color.Grey6)) {
return this.getColor(Color.Grey6);
}
if (this._colorService.colorExists('bright-gray')) {
return this.getColor('bright-gray');
}
}
/** Define the on hover event */
onArcMouseEnter(target, data) {
// update the hover effect if it is enabled
if (this.disableHover === false) {
select(target).transition().duration(250).attr('opacity', 0.5);
}
// update the tooltip context
this._tooltipContext = { ...data, color: this.getColor(data.color) };
// update the tooltip visibility
this._tooltipVisible = true;
// run change detection to ensure the visibility is updated
this._changeDetector.detectChanges();
}
/** Update the tooltip position on mouse move */
onArcMouseMove([x, y]) {
this._tooltipX = x;
this._tooltipY = y - 2; // subtract 2 so that it appears slightly above the cursor
// run change detection to update the element position
this._changeDetector.detectChanges();
}
/** Define the on hover out event */
onArcMouseLeave(target) {
// update the hover effect if it is enabled
if (this.disableHover === false) {
select(target).transition().duration(250).attr('opacity', 1);
}
// clear the tooltip context
this._tooltipContext = null;
// update the tooltip visibility
this._tooltipVisible = false;
// run change detection to ensure the visibility is updated
this._changeDetector.detectChanges();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: NestedDonutChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: NestedDonutChartComponent, selector: "ux-nested-donut-chart", inputs: { dataset: "dataset", max: "max", thickness: "thickness", spacing: "spacing", trackColor: "trackColor", disableHover: "disableHover", disableTooltip: "disableTooltip", tooltipPlacement: "tooltipPlacement", animationDuration: "animationDuration" }, outputs: { itemClick: "itemClick" }, queries: [{ propertyName: "_customTooltip", first: true, predicate: ["tooltip"], descendants: true }], viewQueries: [{ propertyName: "_chartElement", first: true, predicate: ["chart"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<svg #chart\n class=\"ux-nested-donut-chart\"\n [attr.focusable]=\"false\"\n [attr.width]=\"_size\"\n [attr.height]=\"_size\"\n [attr.aria-label]=\"_getAriaLabel()\">\n</svg>\n\n<!-- Custom content in center of the chart -->\n<div class=\"ux-nested-donut-chart-content\"\n [style.top.px]=\"_getContentInset()\"\n [style.right.px]=\"_getContentInset()\"\n [style.bottom.px]=\"_getContentInset()\"\n [style.left.px]=\"_getContentInset()\"\n [style.width.px]=\"_getContentSize()\"\n [style.height.px]=\"_getContentSize()\">\n <ng-content></ng-content>\n</div>\n\n<!-- Tooltip to appear on arc hover -->\n@if (_tooltipVisible && !disableTooltip) {\n <div class=\"ux-nested-donut-chart-tooltip\">\n <ux-tooltip\n [placement]=\"tooltipPlacement\"\n [content]=\"_customTooltip || tooltip\"\n [context]=\"_tooltipContext\"\n [style.top.px]=\"_tooltipY\"\n [style.left.px]=\"_tooltipX\">\n </ux-tooltip>\n </div>\n}\n\n<!-- Default tooltip template -->\n<ng-template #tooltip let-name=\"name\" let-value=\"value\">\n <span class=\"ux-nested-donut-chart-tooltip-content\">\n {{ name }}: {{ value }}\n </span>\n</ng-template>\n\n", dependencies: [{ kind: "component", type: i1.TooltipComponent, selector: "ux-tooltip", inputs: ["content", "context", "placement", "alignment"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: NestedDonutChartComponent, decorators: [{
type: Component,
args: [{ selector: 'ux-nested-donut-chart', changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg #chart\n class=\"ux-nested-donut-chart\"\n [attr.focusable]=\"false\"\n [attr.width]=\"_size\"\n [attr.height]=\"_size\"\n [attr.aria-label]=\"_getAriaLabel()\">\n</svg>\n\n<!-- Custom content in center of the chart -->\n<div class=\"ux-nested-donut-chart-content\"\n [style.top.px]=\"_getContentInset()\"\n [style.right.px]=\"_getContentInset()\"\n [style.bottom.px]=\"_getContentInset()\"\n [style.left.px]=\"_getContentInset()\"\n [style.width.px]=\"_getContentSize()\"\n [style.height.px]=\"_getContentSize()\">\n <ng-content></ng-content>\n</div>\n\n<!-- Tooltip to appear on arc hover -->\n@if (_tooltipVisible && !disableTooltip) {\n <div class=\"ux-nested-donut-chart-tooltip\">\n <ux-tooltip\n [placement]=\"tooltipPlacement\"\n [content]=\"_customTooltip || tooltip\"\n [context]=\"_tooltipContext\"\n [style.top.px]=\"_tooltipY\"\n [style.left.px]=\"_tooltipX\">\n </ux-tooltip>\n </div>\n}\n\n<!-- Default tooltip template -->\n<ng-template #tooltip let-name=\"name\" let-value=\"value\">\n <span class=\"ux-nested-donut-chart-tooltip-content\">\n {{ name }}: {{ value }}\n </span>\n</ng-template>\n\n" }]
}], propDecorators: { dataset: [{
type: Input
}], max: [{
type: Input
}], thickness: [{
type: Input
}], spacing: [{
type: Input
}], trackColor: [{
type: Input
}], disableHover: [{
type: Input
}], disableTooltip: [{
type: Input
}], tooltipPlacement: [{
type: Input
}], animationDuration: [{
type: Input
}], itemClick: [{
type: Output
}], _chartElement: [{
type: ViewChild,
args: ['chart', { static: true }]
}], _customTooltip: [{
type: ContentChild,
args: ['tooltip', { static: false }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmVzdGVkLWRvbnV0LWNoYXJ0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL25lc3RlZC1kb251dC1jaGFydC9uZXN0ZWQtZG9udXQtY2hhcnQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbmVzdGVkLWRvbnV0LWNoYXJ0L25lc3RlZC1kb251dC1jaGFydC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsaUJBQWlCLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQWdDLE1BQU0sRUFBZSxTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM00sT0FBTyxFQUFFLEdBQUcsRUFBaUIsU0FBUyxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFhLFVBQVUsRUFBRSxNQUFNLElBQUksQ0FBQztBQUN4RyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQzlELE9BQU8sRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7OztBQU90RSxNQUFNLE9BQU8seUJBQXlCO0lBTHRDO1FBTXFCLGtCQUFhLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBRXJDLG9CQUFlLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFNUMsZ0JBQVcsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFFakMsbUJBQWMsR0FBRyxN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QUFDLFlBQVksS0FBSyxLQUFLLEVBQUUsQ0FBQztZQUM5QixNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxFQUFFLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDakUsQ0FBQztRQUVELDRCQUE0QjtRQUM1QixJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztRQUU1QixnQ0FBZ0M7UUFDaEMsSUFBSSxDQUFDLGVBQWUsR0FBRyxLQUFLLENBQUM7UUFFN0IsMkRBQTJEO1FBQzNELElBQUksQ0FBQyxlQUFlLENBQUMsYUFBYSxFQUFFLENBQUM7SUFDekMsQ0FBQzsrR0F2VlEseUJBQXlCO21HQUF6Qix5QkFBeUIsb2xCQ2R0QyxrcENBdUNBOzs0RkR6QmEseUJBQXlCO2tCQUxyQyxTQUFTOytCQUNJLHVCQUF1QixtQkFFaEIsdUJBQXVCLENBQUMsTUFBTTs4QkFZdEMsT0FBTztzQkFBZixLQUFLO2dCQUdHLEdBQUc7c0JBQVgsS0FBSztnQkFHRyxTQUFTO3NCQUFqQixLQUFLO2dCQUdHLE9BQU87c0JBQWYsS0FBSztnQkFHRyxVQUFVO3NCQUFsQixLQUFLO2dCQUdHLFlBQVk7c0JBQXBCLEtBQUs7Z0JBR0csY0FBYztzQkFBdEIsS0FBSztnQkFHRyxnQkFBZ0I7c0JBQXhCLEtBQUs7Z0JBR0csaUJBQWlCO3NCQUF6QixLQUFLO2dCQUdJLFNBQVM7c0JBQWxCLE1BQU07Z0JBRytCLGFBQWE7c0JBQWxELFNBQVM7dUJBQUMsT0FBTyxFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRTtnQkFHUSxjQUFjO3NCQUF6RCxZQUFZO3VCQUFDLFNBQVMsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ2hhbmdlRGV0ZWN0b3JSZWYsIENvbXBvbmVudCwgQ29udGVudENoaWxkLCBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIGluamVjdCwgSW5wdXQsIE9uQ2hhbmdlcywgT25EZXN0cm95LCBPbkluaXQsIE91dHB1dCwgVGVtcGxhdGVSZWYsIFZpZXdDaGlsZCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgYXJjLCBBcmMsIEJhc2VUeXBlLCBlYXNlQ3ViaWMsIGludGVycG9sYXRlLCBwb2ludGVyLCBzZWxlY3QsIFNlbGVjdGlvbiwgdHJhbnNpdGlvbiB9IGZyb20gJ2QzJztcbmltcG9ydCB7IFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IENvbG9yIH0gZnJvbSAnLi4vLi4vY29tbW9uL2NvbG9ycy9pbmRleCc7XG5pbXBvcnQgeyBBbmNob3JQbGFjZW1lbnQgfSBmcm9tICcuLi8uLi9jb21tb24vb3ZlcmxheS9pbmRleCc7XG5pbXBvcnQgeyBSZXNpemVTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vZGlyZWN0aXZlcy9yZXNpemUvaW5kZXgnO1xuaW1wb3J0IHsgQ29sb3JTZXJ2aWNlLCBUaGVtZUNvbG9yIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvY29sb3IvaW5kZXgnO1xuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ3V4LW5lc3RlZC1kb251dC1jaGFydCcsXG4gICAgdGVtcGxhdGVVcmw6ICcuL25lc3RlZC1kb251dC1jaGFydC5jb21wb25lbnQuaHRtbCcsXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2hcbn0pXG5leHBvcnQgY2xhc3MgTmVzdGVkRG9udXRDaGFydENvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzLCBPbkRlc3Ryb3kge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2NvbG9yU2VydmljZSA9IGluamVjdChDb2xvclNlcnZpY2UpO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBfY2hhbmdlRGV0ZWN0b3IgPSBpbmplY3QoQ2hhbmdlRGV0ZWN0b3JSZWYpO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBfZWxlbWVudFJlZiA9IGluamVjdChFbGVtZW50UmVmKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX3Jlc2l6ZVNlcnZpY2UgPSBpbmplY3QoUmVzaXplU2VydmljZSk7XG5cbiAgICAvKiogRGVmaW5lIGEgdGhlIGRhdGFzZXQgdG8gZGlzcGxheSAqL1xuICAgIEBJbnB1dCgpIGRhdGFzZXQ6IFJlYWRvbmx5QXJyYXk8TmVzdGVkRG9udXRDaGFydERhdGE+O1xuXG4gICAgLyoqIERlZmluZSB0aGUgbWF4aW11bSByYW5nZSBvZiB0aGUgYXJjcyAqL1xuICAgIEBJbnB1dCgpIG1heDogbnVtYmVyID0gMTAwO1xuXG4gICAgLyoqIERlZmluZSB0aGUgdGhpY2tuZXNzIG9mIGVhY2ggYXJjICovXG4gICAgQElucHV0KCkgdGhpY2tuZXNzOiBudW1iZXIgPSA4O1xuXG4gICAgLyoqIERlZmluZSB0aGUgc3BhY2luZyBvZiBlYWNoIGFyYyAqL1xuICAgIEBJbnB1dCgpIHNwYWNpbmc6IG51bWJlciA9IDg7XG5cbiAgICAvKiogRGVmaW5lIHRoZSB0cmFjayBjb2xvciAqL1xuICAgIEBJbnB1dCgpIHRyYWNrQ29sb3I6IHN0cmluZyB8IFRoZW1lQ29sb3I7XG5cbiAgICAvKiogRGV0ZXJtaW5lIGlmIHdlIHNob3VsZCBzaG93IHRoZSBob3ZlciBlZmZlY3QgKi9cbiAgICBASW5wdXQoKSBkaXNhYmxlSG92ZXI6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgd2Ugc2hvdWxkIHNob3cgYSB0b29sdGlwIG9uIGFyYyBob3ZlciAqL1xuICAgIEBJbnB1dCgpIGRpc2FibGVUb29sdGlwOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogRGV0ZXJtaW5lIHRoZSBwb3NpdGlvbiBvZiB0aGUgdG9vbHRpcCAqL1xuICAgIEBJbnB1dCgpIHRvb2x0aXBQbGFjZW1lbnQ6IEFuY2hvclBsYWNlbWVudCA9ICd0b3AnO1xuXG4gICAgLyoqIFNldCB0aGUgZHVyYXRpb24gb2YgdGhlIGFuaW1hdGlvbiAqL1xuICAgIEBJbnB1dCgpIGFuaW1hdGlvbkR1cmF0aW9uOiBudW1iZXIgPSA3NTA7XG5cbiAgICAvKiogRW1pdCB3aGVuZXZlciBhbiBhcmMgaXMgY2xpY2tlZCAqL1xuICAgIEBPdXRwdXQoKSBpdGVtQ2xpY2sgPSBuZXcgRXZlbnRFbWl0dGVyPE5lc3RlZERvbnV0Q2hhcnREYXRhPigpO1xuXG4gICAgLyoqIEFjY2VzcyB0aGUgU1ZHIGVsZW1lbnQgKi9cbiAgICBAVmlld0NoaWxkKCdjaGFydCcsIHsgc3RhdGljOiB0cnVlIH0pIF9jaGFydEVsZW1lbnQ6IEVsZW1lbnRSZWY7XG5cbiAgICAvKiogQWxsb3cgY3VzdG9tIHRvb2x0aXAgdGVtcGxhdGUgKi9cbiAgICBAQ29udGVudENoaWxkKCd0b29sdGlwJywgeyBzdGF0aWM6IGZhbHNlIH0pIF9jdXN0b21Ub29sdGlwOiBUZW1wbGF0ZVJlZjxOZXN0ZWREb251dENoYXJ0RGF0YT47XG5cbiAgICAvKiogSW5kaWNhdGUgaWYgdGhlIHRvb2x0aXAgc2hvdWxkIGJlIHZpc2libGUgKi9cbiAgICBfdG9vbHRpcFZpc2libGU6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBTdG9yZSB0aGUgdG9vbHRpcCB4IHBvc2l0aW9uICovXG4gICAgX3Rvb2x0aXBYOiBudW1iZXI7XG5cbiAgICAvKiogU3RvcmUgdGhlIHRvb2x0aXAgeSBwb3NpdGlvbiAqL1xuICAgIF90b29sdGlwWTogbnVtYmVyO1xuXG4gICAgLyoqIFN0b3JlIHRoZSBjb250ZXh0IHRvIHByb3ZpZGUgdG8gdGhlIHRvb2x0aXAgKi9cbiAgICBfdG9vbHRpcENvbnRleHQ6IE5lc3RlZERvbnV0Q2hhcnREYXRhO1xuXG4gICAgLyoqIERldGVybWluZSB0aGUgcmFkaXVzIG9mIHRoZSBjaGFydCBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIHNpemUgKi9cbiAgICBnZXQgX3JhZGl1cygpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5fc2l6ZSAvIDI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IHRoZSBzaXplIG9mIHRoZSBjaGFydC4gVGhlIGNoYXJ0IHdpbGwgYWx3YXlzIGJlIHNxdWFyZSB0b1xuICAgICAqIHRoZSBzaXplIHdpbGwgYmUgdGhlIHNtYWxsZXIgb2YgdGhlIHdpZHRoL2hlaWdodCBwcm9wZXJ0aWVzXG4gICAgICovXG4gICAgZ2V0IF9zaXplKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiBNYXRoLm1pbih0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQub2Zmc2V0V2lkdGgsIHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5vZmZzZXRIZWlnaHQpO1xuICAgIH1cblxuICAgIC8qKiBTdG9yZSB0aGUgc2VsZWN0aW9uIHdoZW4gdGhlIGFyY3Mgd2lsbCBiZSBkcmF3biAqL1xuICAgIHByaXZhdGUgX2FyY0xheWVyOiBTZWxlY3Rpb248QmFzZVR5cGUsIE5lc3RlZERvbnV0Q2hhcnREYXRhLCBIVE1MRWxlbWVudCwgTmVzdGVkRG9udXRDaGFydERhdGE+O1xuXG4gICAgLyoqIFN0b3JlIHRoZSBzZWxlY3Rpb24gd2hlbiB0aGUgdHJhY2tzIHdpbGwgYmUgZHJhd24gKi9cbiAgICBwcml2YXRlIF90cmFja0xheWVyOiBTZWxlY3Rpb248QmFzZVR5cGUsIE5lc3RlZERvbnV0Q2hhcnREYXRhLCBIVE1MRWxlbWVudCwgTmVzdGVkRG9udXRDaGFydERhdGE+O1xuXG4gICAgLyoqIFN0b3JlIHRoZSBhcmMgc2VsZWN0aW9uICovXG4gICAgcHJpdmF0ZSBfYXJjczogU2VsZWN0aW9uPEJhc2VUeXBlLCBOZXN0ZWREb251dENoYXJ0QXJjLCBCYXNlVHlwZSwgTmVzdGVkRG9udXRDaGFydERhdGE+O1xuXG4gICAgLyoqIFN0b3JlIHRoZSB0cmFja3Mgc2VsZWN0aW9uICovXG4gICAgcHJpdmF0ZSBfdHJhY2tzOiBTZWxlY3Rpb248QmFzZVR5cGUsIE5lc3RlZERvbnV0Q2hhcnRBcmMsIEJhc2VUeXBlLCBOZXN0ZWREb251dENoYXJ0RGF0YT47XG5cbiAgICAvKiogU3RvcmUgdGhlIHByZXZpb3VzbHkgcHJvY2Vzc2VkIGRhdGEgKi9cbiAgICBwcml2YXRlIF9hcmNEYXRhOiBOZXN0ZWREb251dENoYXJ0QXJjW10gPSBbXTtcblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgdGhlIGludGlhbCByZW5kZXIgaGFzIHRha2VuIHBsYWNlICovXG4gICAgcHJpdmF0ZSBfaXNJbml0aWFsaXplZDogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgLyoqIFVuc3Vic2NyaWJlIGZyb20gYWxsIG9ic2VydmFibGVzIGF1dG9tYXRpY2FsbHkgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9vbkRlc3Ryb3kgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgLyoqIFBlcmZvcm0gdGhlIGluaXRpYWwgcmVuZGVyICovXG4gICAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgICAgIC8vIGNyZWF0ZSB0aGUgc2VsZWN0aW9uIHdoZXJlIHdlIHdpbGwgZHJhdyB0aGUgdHJhY2tzXG4gICAgICAgIHRoaXMuX3RyYWNrTGF5ZXIgPSBzZWxlY3Q8U1ZHRWxlbWVudCwgTmVzdGVkRG9udXRDaGFydERhdGE+KHRoaXMuX2NoYXJ0RWxlbWVudC5uYXRpdmVFbGVtZW50KVxuICAgICAgICAgICAgLmFwcGVuZCgnZycpO1xuXG4gICAgICAgIC8vIGNyZWF0ZSB0aGUgc2VsZWN0aW9uIHdoZXJlIHdlIHdpbGwgZHJhdyB0aGUgYXJjc1xuICAgICAgICB0aGlzLl9hcmNMYXllciA9IHNlbGVjdDxTVkdFbGVtZW50LCBOZXN0ZWREb251dENoYXJ0RGF0YT4odGhpcy5fY2hhcnRFbGVtZW50Lm5hdGl2ZUVsZW1lbnQpXG4gICAgICAgICAgICAuYXBwZW5kKCdnJyk7XG5cbiAgICAgICAgLy8gY3JlYXRlIHRoZSBhcmNzIHJlcHJlc2VudGluZyB0aGUgZGF0YVxuICAgICAgICB0aGlzLnJlbmRlcigpO1xuXG4gICAgICAgIC8vIG1hcmsgdGhlIGNvbXBvbmVudCBhcyBpbml0aWFsaXplZFxuICAgICAgICB0aGlzLl9pc0luaXRpYWxpemVkID0gdHJ1ZTtcblxuICAgICAgICAvLyBsaXN0ZW4gZm9yIGFueSByZXNpemluZyAtIHNraXAgdGhlIGZpcnN0IGVtaXNzaW9uIGFzIGl0IGFsd2F5cyBlbWl0cyBvbiBmaXJzdCBzdWJzY3JpYmVcbiAgICAgICAgdGhpcy5fcmVzaXplU2VydmljZS5hZGRSZXNpemVMaXN0ZW5lcih0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQpXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnJlbmRlcigpO1xuICAgICAgICAgICAgICAgIHRoaXMuX2NoYW5nZURldGVjdG9yLm1hcmtGb3JDaGVjaygpO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqIEFueSB0aW1lIGFuIGlucHV0IGNoYW5nZXMgd2UgbXVzdCByZS1yZW5kZXIgdGhlIGNoYXJ0ICovXG4gICAgbmdPbkNoYW5nZXMoKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLl9pc0luaXRpYWxpemVkKSB7XG4gICAgICAgICAgICB0aGlzLnJlbmRlcigpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuX3Jlc2l6ZVNlcnZpY2UucmVtb3ZlUmVzaXplTGlzdGVuZXIodGhpcy5fZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50KTtcbiAgICAgICAgdGhpcy5fb25EZXN0cm95Lm5leHQoKTtcbiAgICAgICAgdGhpcy5fb25EZXN0cm95LmNvbXBsZXRlKCk7XG4gICAgfVxuXG4gICAgLyoqIEluc2V0IHRoZSBjb250ZW50IHNvIGl0IG5ldmVyIG92ZXJsYXBzIHRoZSBhcmNzICovXG4gICAgX2dldENvbnRlbnRJbnNldCgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5kYXRhc2V0Lmxlbmd0aCAqICh0aGlzLnNwYWNpbmcgKyB0aGlzLnRoaWNrbmVzcyk7XG4gICAgfVxuXG5cbiAgICAvKiogR2V0IHRoZSBkaW1lbnNpb25zIG9mIHRoZSBjb250ZW50IGFyZWEgKi9cbiAgICBfZ2V0Q29udGVudFNpemUoKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3NpemUgLSAodGhpcy5fZ2V0Q29udGVudEluc2V0KCkgKiAyKTtcbiAgICB9XG5cbiAgICAvKiogR2V0IHRoZSBkYXRhc2V0IGZvcm1hdGVkIGluIGFuIGFjY2Vzc2libGUgbWFubmVyICovXG4gICAgX2dldEFyaWFMYWJlbCgpOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gdGhpcy5kYXRhc2V0Lm1hcChkYXRhID0+IGAke2RhdGEudmFsdWV9ICR7ZGF0YS5uYW1lfWApLmpvaW4oJy4gJyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRGlzcGxheSB0aGUgdHJhY2tzIGFuZCBhcmNzIGRlZmluZWQgYnkgdGhlIGRhdGFzZXQuXG4gICAgICogV2UgYWxzbyBwcm92aWRlIHRoZSB0cmFuc2l0aW9uIGNvbmZpZ3VyYXRpb24gc28gYW55dGltZSB0aGUgZGF0YXNldFxuICAgICAqIGNoYW5nZXMgd2Ugd2lsbCBhbmltYXRlIHRoZSB1cGRhdGUuXG4gICAgICovXG4gICAgcHJpdmF0ZSByZW5kZXIoKTogdm9pZCB7XG5cbiAgICAgICAgLy8gdXBkYXRlIHRoZSB0cmFuc2Zvcm0gb2YgdGhlIGxheWVyc1xuICAgICAgICB0aGlzLl90cmFja0xheWVyLmF0dHIoJ3RyYW5zZm9ybScsIGB0cmFuc2xhdGUoJHt0aGlzLl9yYWRpdXN9LCAke3RoaXMuX3JhZGl1c30pYCk7XG4gICAgICAgIHRoaXMuX2FyY0xheWVyLmF0dHIoJ3RyYW5zZm9ybScsIGB0cmFuc2xhdGUoJHt0aGlzLl9yYWRpdXN9LCAke3RoaXMuX3JhZGl1c30pYCk7XG5cbiAgICAgICAgLy8gY3JlYXRlIHRoZSBhcmNzIGJhc2VkIG9uIHRoZSBkYXRhc2V0XG4gICAgICAgIHRoaXMuX2FyY3MgPSB0aGlzLl9hcmNMYXllci5zZWxlY3RBbGwoJ3BhdGgnKVxuICAgICAgICAgICAgLmRhdGEodGhpcy5nZXRDaGFydERhdGEoKSk7XG5cbiAgICAgICAgLy8gY3JlYXRlIHRoZSBkZWZhdWx0IHRyYW5zaXRpb24gYmFzZWQgb24gdGhlIHNwZWNpZmllZCBkdXJhdGlvblxuICAgICAgICBjb25zdCBhcmNUcmFuc2l0aW9uID0gdHJhbnNpdGlvbignbmVzdGVkRG9udXRBcmNUcmFuc2l0aW9uJykuZWFzZShlYXNlQ3ViaWMpLmR1cmF0aW9uKHRoaXMuYW5pbWF0aW9uRHVyYXRpb24pO1xuXG4gICAgICAgIC8vIGNyZWF0ZSB0aGUgdHJhY2tzIGJhc2VkIG9uIHRoZSBkYXRhc2V0XG4gICAgICAgIHRoaXMuX3RyYWNrcyA9IHRoaXMuX3RyYWNrTGF5ZXIuc2VsZWN0QWxsKCdwYXRoJylcbiAgICAgICAgICAgIC5kYXRhKHRoaXMuZ2V0Q2hhcnREYXRhKCkpXG4gICAgICAgICAgICAuZW50ZXIoKVxuICAgICAgICAgICAgLmFwcGVuZCgncGF0aCcpXG4gICAgICAgICAgICAuYXR0cignY2xhc3MnLCAndXgtbmVzdGVkLWRvbnV0LWNoYXJ0LXRyYWNrJyk7XG5cbiAgICAgICAgLy8gc2V0IHRoZSB0cmFjayBjb2xvciBvbiBlYWNoIHJlbmRlciBpbiBjYXNlIHRoZSBpbnB1dCBoYXMgY2hhbmdlZFxuICAgICAgICB0aGlzLl90cmFja0xheWVyLnNlbGVjdEFsbCgncGF0aCcpXG4gICAgICAgICAgICAuYXR0cignZCcsIHRoaXMuZ2V0VHJhY2tBcmMoKSlcbiAgICAgICAgICAgIC5zdHlsZSgnZmlsbCcsICgpID0+IHRoaXMuZ2V0VHJhY2tDb2xvcigpKTtcblxuICAgICAgICAvLyBpZiBhbiBhcmMgaXMgcmVtb3ZlZCB0aGVuIGFsc28gcmVtb3ZlIHRoZSB0cmFja1xuICAgICAgICB0aGlzLl90cmFja3MuZXhpdCgpLnJlbW92ZSgpO1xuXG4gICAgICAgIC8vIFdoZW4gYSBuZXcgYXJjIGlzIGFkZGVkIHdlIHNob3VsZCBjcmVhdGUgdGhlIGVsZW1lbnRcbiAgICAgICAgLy8gc2l6ZSBpdCBhbmQgcHJvdmlkZSB0aGUgYmFja2dyb3VuZCBjb2xvciBhbmQgYmVnaW4gdGhlXG4gICAgICAgIC8vIGFuaW1hdGlvbiB1bnRpbCBpdCByZWFjaGVzIGl0cyBmaW5hbCBhbmdsZVxuICAgICAgICB0aGlzLl9hcmNzLmVudGVyKClcbiAgICAgICAgICAgIC5hcHBlbmQoJ3BhdGgnKVxuICAgICAgICAgICAgLmF0dHIoJ2NsYXNzJywgJ3V4LW5lc3RlZC1kb251dC1jaGFydC1hcmMnKVxuICAgICAgICAgICAgLnN0eWxlKCdmaWxsJywgZGF0YSA9PiB0aGlzLmdldENvbG9yKGRhdGEuY29sb3IpKVxuICAgICAgICAgICAgLmF0dHIoJ29wYWNpdHknLCAxKVxuICAgICAgICAgICAgLm9uKCdjbGljaycsIGRhdGEgPT4gdGhpcy5pdGVtQ2xpY2suZW1pdChkYXRhKSlcbiAgICAgICAgICAgIC5vbignbW91c2VlbnRlcicsIChldmVudCwgbm9kZSkgPT4gdGhpcy5vbkFyY01vdXNlRW50ZXIoZXZlbnQuc3JjRWxlbWVudCwgbm9kZSkpXG4gICAgICAgICAgICAub24oJ21vdXNlbW92ZScsIChldmVudCkgPT4gdGhpcy5vbkFyY01vdXNlTW92ZShwb2ludGVyKGV2ZW50LCB0aGlzLl9jaGFydEVsZW1lbnQubmF0aXZlRWxlbWVudCkpKVxuICAgICAgICAgICAgLm9uKCdtb3VzZWxlYXZlJywgKGV2ZW50KSA9PiB0aGlzLm9uQXJjTW91c2VMZWF2ZShldmVudC5zcmNFbGVtZW50KSlcbiAgICAgICAgICAgIC50cmFuc2l0aW9uKGFyY1RyYW5zaXRpb24pXG4gICAgICAgICAgICAuYXR0clR3ZWVuKCdkJywgdGhpcy5nZXRBcmNUd2Vlbi5iaW5kKHRoaXMpKTtcblxuICAgICAgICAvLyBhbnkgdGltZSBhbiBleGlzdGluZyBkYXRhc2V0IHZhbHVlIGNoYW5nZXNcbiAgICAgICAgLy8gd2Ugc2hvdWxkIHVwZGF0ZSB0aGUgYW5nbGUgd2l0aCBhbiBhbmltYXRpb25cbiAgICAgICAgLy8gd2UgYWxzbyBhbmltYXRlIGFueSBjb2xvciBjaGFuZ2VzIGFsc28uXG4gICAgICAgIHRoaXMuX2FyY3MudHJhbnNpdGlvbihhcmNUcmFuc2l0aW9uKVxuICAgICAgICAgICAgLnN0eWxlKCdmaWxsJywgZGF0YSA9PiB0aGlzLmdldENvbG9yKGRhdGEuY29sb3IpKVxuICAgICAgICAgICAgLmF0dHJUd2VlbignZCcsIHRoaXMuZ2V0QXJjVHdlZW4uYmluZCh0aGlzKSk7XG5cbiAgICAgICAgLy8gd2hlbiBhIGRhdGFzZXQgaXQgcmVtb3ZlZCBhbmltYXRlIHRoZSBhcmMgb3V0XG4gICAgICAgIC8vIGFuZCB0aGVuIHJlbW92ZSB0aGUgYXNzb2NpYXRlZCBET00gZWxlbWVudFxuICAgICAgICB0aGlzLl9hcmNzLmV4aXQoKS50cmFuc2l0aW9uKGFyY1RyYW5zaXRpb24pXG4gICAgICAgICAgICAuYXR0clR3ZWVuKCdkJywgdGhpcy5nZXRBcmNUd2Vlbi5iaW5kKHRoaXMpKVxuICAgICAgICAgICAgLnJlbW92ZSgpO1xuICAgIH1cblxuICAgIC8qKiBHZXQgdGhlIGludGVycG9sYXRpb24gZnVuY3Rpb24gYmFzZWQgb24gdGhlIG5ldyBhbmQgcHJldmlvdXMgYW5nbGUgKi9cbiAgICBwcml2YXRlIGdldEFyY1R3ZWVuKGRhdGE6IE5lc3RlZERvbnV0Q2hhcnRBcmMpIHtcblxuICAgICAgICAvLyBjcmVhdGUgYSBuZXcgaW50ZXJwb2xhdGlvbiBmdW5jdGlvbiB3aXRoIGEgbmV3IGVuZEFuZ2xlXG4gICAgICAgIGNvbnN0IGludGVycG9sYXRpb24gPSBpbnRlcnBvbGF0ZSh7IC4uLmRhdGEsIGVuZEFuZ2xlOiBkYXRhLnByZXZpb3VzRW5kQW5nbGUgfSwgZGF0YSk7XG5cbiAgICAgICAgLy8gcmV0dXJuIHRoZSBmdW5jdGlvbiB0aGF0IHdpbGwgcHJvZHVjZSB0aGUgaW50ZXJwb2xhdGlvblxuICAgICAgICByZXR1cm4gKGRlbHRhOiBudW1iZXIpID0+IHRoaXMuZ2V0QXJjKCkoaW50ZXJwb2xhdGlvbihkZWx0YSkpO1xuICAgIH1cblxuICAgIC8qKiBHZXQgdGhlIGFyYyBsYXlvdXQgZm9yIGEgc3BlY2lmaWMgaXRlbSBpbiB0aGUgZGF0YXNldCAqL1xuICAgICBcbiAgICBwcml2YXRlIGdldEFyYygpOiBBcmM8YW55LCBOZXN0ZWREb251dENoYXJ0QXJjPiB7XG4gICAgICAgIHJldHVybiBhcmM8TmVzdGVkRG9udXRDaGFydEFyYz4oKVxuICAgICAgICAgICAgLmlubmVyUmFkaXVzKGRhdGEgPT4gdGhpcy5nZXRBcmNSYWRpdXMoZGF0YS5pbmRleCkpXG4gICAgICAgICAgICAub3V0ZXJSYWRpdXMoZGF0YSA9PiB0aGlzLmdldEFyY1JhZGl1cyhkYXRhLmluZGV4KSArIHRoaXMudGhpY2tuZXNzKVxuICAgICAgICAgICAgLnN0YXJ0QW5nbGUoZGF0YSA9PiBkYXRhLnN0YXJ0QW5nbGUpXG4gICAgICAgICAgICAuZW5kQW5nbGUoZGF0YSA9PiBkYXRhLmVuZEFuZ2xlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBHZXQgdGhlIHRyYWNrIGFyYyBsYXlvdXQgZm9yIGEgc3BlY2lmaWMgaXRlbSBpbiB0aGUgZGF0YXNldC5cbiAgICAgKiBUaGlzIHdpbGwgbWF0Y2ggdGhlIGFyYyBvZiB0aGF0IHJlcHJlc2VudHMgdGhlIGFjdHVhbCBkYXRhXG4gICAgICogaG93ZXZlciB0aGUgZW5kQW5nbGUgd2lsbCBhbHdheXMgYmUgYSBjb21wbGV0ZSBjaXJjbGVcbiAgICAgKi9cbiAgICAgXG4gICAgcHJpdmF0ZSBnZXRUcmFja0FyYygpOiBBcmM8YW55LCBOZXN0ZWREb251dENoYXJ0QXJjPiB7XG4gICAgICAgIHJldHVybiB0aGlzLmdldEFyYygpLmVuZEFuZ2xlKCgpID0+IE1hdGguUEkgKiAyKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBHZXQgdGhlIHJhZGl1cyBvZiBhbiBhcmMuIFRoaXMgaXMgY2FsY3VsYXRlZFxuICAgICAqIGJhc2VkIG9uIHRoZSBjaGFydCByYWRpdXMgdGhhdCBoYXMgYmVlbiBkZWZpbmVkLFxuICAgICAqIG1pbnVzIHRoZSB0aGlja25lc3MgZGVmaW5lZCwgdGhlbiB0YWtpbmcgaW50byBhY2NvdW50XG4gICAgICogdGhlIGRlcHRoIG9mIHRoZSBhcmMgYW5kIHRoZSBzcGFjaW5nIGJldHdlZW4gZWFjaCBhcmMuXG4gICAgICovXG4gICAgcHJpdmF0ZSBnZXRBcmNSYWRpdXMoaW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgICAgIHJldHVybiAodGhpcy5fcmFkaXVzIC0gdGhpcy50aGlja25lc3MpIC0gKGluZGV4ICogKHRoaXMudGhpY2tuZXNzICsgdGhpcy5zcGFjaW5nKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTWFwIHRoZSBkYXRhc2V0IHRvIHRoZSBOZXN0ZWREb251dENoYXJ0QXJjIGludGVyZmFjZVxuICAgICAqL1xuICAgIHByaXZhdGUgZ2V0Q2hhcnREYXRhKCk6IE5lc3RlZERvbnV0Q2hhcnRBcmNbXSB7XG4gICAgICAgIGNvbnN0IGRhdGFzZXQgPSB0aGlzLmRhdGFzZXQubWFwKChkYXRhLCBpbmRleCkgPT4ge1xuXG4gICAgICAgICAgICBsZXQgcHJldmlvdXNFbmRBbmdsZTogbnVtYmVyID0gMDtcblxuICAgICAgICAgICAgLy8gY2hlY2sgaWYgdGhlcmUgd2FzIGEgcHJldmlvdXMgZGF0YXNldCBhdCB0aGlzIGluZGV4XG4gICAgICAgICAgICBpZiAodGhpcy5fYXJjRGF0YSAmJiB0aGlzLl9hcmNEYXRhW2luZGV4XSkge1xuICAgICAgICAgICAgICAgIHByZXZpb3VzRW5kQW5nbGUgPSB0aGlzLl9hcmNEYXRhW2luZGV4XS5lbmRBbmdsZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHsgLi4uZGF0YSwgaW5kZXgsIHN0YXJ0QW5nbGU6IDAsIGVuZEFuZ2xlOiB0aGlzLmdldEFuZ2xlKGRhdGEpLCBwcmV2aW91c0VuZEFuZ2xlIH07XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIHN0b3JlIHRoZSBsYXRlc3QgcHJvY2Vzc2VkIGFyYyBkYXRhXG4gICAgICAgIHRoaXMuX2FyY0RhdGEgPSBkYXRhc2V0O1xuXG4gICAgICAgIHJldHVybiBkYXRhc2V0O1xuICAgIH1cblxuICAgIC8qKiBDb252ZXJ0IHRoZSBkYXRhIHZhbHVlIHRvIHJhZGlhbnMgKi9cbiAgICBwcml2YXRlIGdldEFuZ2xlKGRhdGE6IE5lc3RlZERvbnV0Q2hhcnREYXRhKTogbnVtYmVyIHtcbiAgICAgICAgY29uc3QgZnJhY3Rpb24gPSBkYXRhLnZhbHVlIC8gdGhpcy5tYXg7XG4gICAgICAgIGNvbnN0IGRlZ3JlZXMgPSBmcmFjdGlvbiAqIDM2MC4wO1xuICAgICAgICByZXR1cm4gZGVncmVlcyAqIChNYXRoLlBJIC8gMTgwKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBHZXQgdGhlIGNvbG9yIG9mIHRoZSBhcmMsIHRoaXMgbWF5IGJlIGEgQ1NTIGNvbG9yIHZhbHVlLCB0aGUgbmFtZSBvZiBhIGNvbG9yXG4gICAgICogZnJvbSB0aGUgY29sb3Igc2V0IG9yIGEgVGhlbWVDb2xvciBvYmplY3QuIFdlIHJldHVybiB0aGlzIGFzIGEgcmdiYSBjb2xvciB0b1xuICAgICAqIHN1cHBvcnQgdGhlIGFscGhhIGNoYW5uZWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGdldENvbG9yKGNvbG9yOiBzdHJpbmcgfCBUaGVtZUNvbG9yKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIFRoZW1lQ29sb3IuaXNJbnN0YW5jZU9mKGNvbG9yKSA/IChjb2xvciBhcyBUaGVtZUNvbG9yKS50b1JnYmEoKSA6IHRoaXMuX2NvbG9yU2VydmljZS5yZXNvbHZlKGNvbG9yIGFzIHN0cmluZyk7XG4gICAgfVxuXG4gICAgLyoqIElmIG5vIHRyYWNrIGNvbG9yIGlzIHNwZWNpZmllZCB0aGVuIGRlZmF1bHQgdG8gYSBzcGVjaWZpYyBjb2xvciBiYXNlZCBvbiB0aGUgYWN0aXZlIGNvbG9yc2V0ICovXG4gICAgcHJpdmF0ZSBnZXRUcmFja0NvbG9yKCk6IHN0cmluZyB7XG4gICAgICAgIGlmICh0aGlzLnRyYWNrQ29sb3IpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmdldENvbG9yKHRoaXMudHJhY2tDb2xvcik7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBvdGhlcndpc2UgZGVmYXVsdCB0byBhIGNvbG9yIGJhc2VkIG9uIHRoZSBjb2xvcnNldCAobm90ZSB3ZSBjYW4ndCB1c2UgdGhlIENvbG9yIGVudW0gZnJvbSBNRiBwYWNrYWdlKVxuICAgICAgICBpZiAodGhpcy5fY29sb3JTZXJ2aWNlLmNvbG9yRXhpc3RzKENvbG9yLkdyZXk2KSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0Q29sb3IoQ29sb3IuR3JleTYpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuX2NvbG9yU2VydmljZS5jb2xvckV4aXN0cygnYnJpZ2h0LWdyYXknKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0Q29sb3IoJ2JyaWdodC1ncmF5Jyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogRGVmaW5lIHRoZSBvbiBob3ZlciBldmVudCAqL1xuICAgIHByaXZhdGUgb25BcmNNb3VzZUVudGVyKHRhcmdldDogQmFzZVR5cGUsIGRhdGE6IE5lc3RlZERvbnV0Q2hhcnREYXRhKTogdm9pZCB7XG5cbiAgICAgICAgLy8gdXBkYXRlIHRoZSBob3ZlciBlZmZlY3QgaWYgaXQgaXMgZW5hYmxlZFxuICAgICAgICBpZiAodGhpcy5kaXNhYmxlSG92ZXIgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICBzZWxlY3QodGFyZ2V0KS50cmFuc2l0aW9uKCkuZHVyYXRpb24oMjUwKS5hdHRyKCdvcGFjaXR5JywgMC41KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHVwZGF0ZSB0aGUgdG9vbHRpcCBjb250ZXh0XG4gICAgICAgIHRoaXMuX3Rvb2x0aXBDb250ZXh0ID0geyAuLi5kYXRhLCBjb2xvcjogdGhpcy5nZXRDb2xvcihkYXRhLmNvbG9yKSB9O1xuXG4gICAgICAgIC8vIHVwZGF0ZSB0aGUgdG9vbHRpcCB2aXNpYmlsaXR5XG4gICAgICAgIHRoaXMuX3Rvb2x0aXBWaXNpYmxlID0gdHJ1ZTtcblxuICAgICAgICAvLyBydW4gY2hhbmdlIGRldGVjdGlvbiB0byBlbnN1cmUgdGhlIHZpc2liaWxpdHkgaXMgdXBkYXRlZFxuICAgICAgICB0aGlzLl9jaGFuZ2VEZXRlY3Rvci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfVxuXG4gICAgLyoqIFVwZGF0ZSB0aGUgdG9vbHRpcCBwb3NpdGlvbiBvbiBtb3VzZSBtb3ZlICovXG4gICAgcHJpdmF0ZSBvbkFyY01vdXNlTW92ZShbeCwgeV06IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICAgICAgdGhpcy5fdG9vbHRpcFggPSB4O1xuICAgICAgICB0aGlzLl90b29sdGlwWSA9IHkgLSAyOyAvLyBzdWJ0cmFjdCAyIHNvIHRoYXQgaXQgYXBwZWFycyBzbGlnaHRseSBhYm92ZSB0aGUgY3Vyc29yXG5cbiAgICAgICAgLy8gcnVuIGNoYW5nZSBkZXRlY3Rpb24gdG8gdXBkYXRlIHRoZSBlbGVtZW50IHBvc2l0aW9uXG4gICAgICAgIHRoaXMuX2NoYW5nZURldGVjdG9yLmRldGVjdENoYW5nZXMoKTtcbiAgICB9XG5cbiAgICAvKiogRGVmaW5lIHRoZSBvbiBob3ZlciBvdXQgZXZlbnQgKi9cbiAgICBwcml2YXRlIG9uQXJjTW91c2VMZWF2ZSh0YXJnZXQ6IEJhc2VUeXBlKTogdm9pZCB7XG5cbiAgICAgICAgLy8gdXBkYXRlIHRoZSBob3ZlciBlZmZlY3QgaWYgaXQgaXMgZW5hYmxlZFxuICAgICAgICBpZiAodGhpcy5kaXNhYmxlSG92ZXIgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICBzZWxlY3QodGFyZ2V0KS50cmFuc2l0aW9uKCkuZHVyYXRpb24oMjUwKS5hdHRyKCdvcGFjaXR5JywgMSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBjbGVhciB0aGUgdG9vbHRpcCBjb250ZXh0XG4gICAgICAgIHRoaXMuX3Rvb2x0aXBDb250ZXh0ID0gbnVsbDtcblxuICAgICAgICAvLyB1cGRhdGUgdGhlIHRvb2x0aXAgdmlzaWJpbGl0eVxuICAgICAgICB0aGlzLl90b29sdGlwVmlzaWJsZSA9IGZhbHNlO1xuXG4gICAgICAgIC8vIHJ1biBjaGFuZ2UgZGV0ZWN0aW9uIHRvIGVuc3VyZSB0aGUgdmlzaWJpbGl0eSBpcyB1cGRhdGVkXG4gICAgICAgIHRoaXMuX2NoYW5nZURldGVjdG9yLmRldGVjdENoYW5nZXMoKTtcbiAgICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTmVzdGVkRG9udXRDaGFydERhdGEge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB2YWx1ZTogbnVtYmVyO1xuICAgIGNvbG9yOiBUaGVtZUNvbG9yIHwgc3RyaW5nO1xuICAgIGRhdGE/OiB7IFtrZXk6IHN0cmluZ106IHVua25vd24gfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOZXN0ZWREb251dENoYXJ0QXJjIGV4dGVuZHMgTmVzdGVkRG9udXRDaGFydERhdGEge1xuICAgIGluZGV4OiBudW1iZXI7XG4gICAgc3RhcnRBbmdsZTogbnVtYmVyO1xuICAgIGVuZEFuZ2xlOiBudW1iZXI7XG4gICAgcHJldmlvdXNFbmRBbmdsZTogbnVtYmVyO1xufVxuIiwiPHN2ZyAjY2hhcnRcbiAgY2xhc3M9XCJ1eC1uZXN0ZWQtZG9udXQtY2hhcnRcIlxuICBbYXR0ci5mb2N1c2FibGVdPVwiZmFsc2VcIlxuICBbYXR0ci53aWR0aF09XCJfc2l6ZVwiXG4gIFthdHRyLmhlaWdodF09XCJfc2l6ZVwiXG4gIFthdHRyLmFyaWEtbGFiZWxdPVwiX2dldEFyaWFMYWJlbCgpXCI+XG48L3N2Zz5cblxuPCEtLSBDdXN0b20gY29udGVudCBpbiBjZW50ZXIgb2YgdGhlIGNoYXJ0IC0tPlxuPGRpdiBjbGFzcz1cInV4LW5lc3RlZC1kb251dC1jaGFydC1jb250ZW50XCJcbiAgW3N0eWxlLnRvcC5weF09XCJfZ2V0Q29udGVudEluc2V0KClcIlxuICBbc3R5bGUucmlnaHQucHhdPVwiX2dldENvbnRlbnRJbnNldCgpXCJcbiAgW3N0eWxlLmJvdHRvbS5weF09XCJfZ2V0Q29udGVudEluc2V0KClcIlxuICBbc3R5bGUubGVmdC5weF09XCJfZ2V0Q29udGVudEluc2V0KClcIlxuICBbc3R5bGUud2lkdGgucHhdPVwiX2dldENvbnRlbnRTaXplKClcIlxuICBbc3R5bGUuaGVpZ2h0LnB4XT1cIl9nZXRDb250ZW50U2l6ZSgpXCI+XG4gIDxuZy1jb250ZW50PjwvbmctY29udGVudD5cbjwvZGl2PlxuXG48IS0tIFRvb2x0aXAgdG8gYXBwZWFyIG9uIGFyYyBob3ZlciAtLT5cbkBpZiAoX3Rvb2x0aXBWaXNpYmxlICYmICFkaXNhYmxlVG9vbHRpcCkge1xuICA8ZGl2IGNsYXNzPVwidXgtbmVzdGVkLWRvbnV0LWNoYXJ0LXRvb2x0aXBcIj5cbiAgICA8dXgtdG9vbHRpcFxuICAgICAgW3BsYWNlbWVudF09XCJ0b29sdGlwUGxhY2VtZW50XCJcbiAgICAgIFtjb250ZW50XT1cIl9jdXN0b21Ub29sdGlwIHx8IHRvb2x0aXBcIlxuICAgICAgW2NvbnRleHRdPVwiX3Rvb2x0aXBDb250ZXh0XCJcbiAgICAgIFtzdHlsZS50b3AucHhdPVwiX3Rvb2x0aXBZXCJcbiAgICAgIFtzdHlsZS5sZWZ0LnB4XT1cIl90b29sdGlwWFwiPlxuICAgIDwvdXgtdG9vbHRpcD5cbiAgPC9kaXY+XG59XG5cbjwhLS0gRGVmYXVsdCB0b29sdGlwIHRlbXBsYXRlIC0tPlxuPG5nLXRlbXBsYXRlICN0b29sdGlwIGxldC1uYW1lPVwibmFtZVwiIGxldC12YWx1ZT1cInZhbHVlXCI+XG4gIDxzcGFuIGNsYXNzPVwidXgtbmVzdGVkLWRvbnV0LWNoYXJ0LXRvb2x0aXAtY29udGVudFwiPlxuICAgIHt7IG5hbWUgfX06IHt7IHZhbHVlIH19XG4gIDwvc3Bhbj5cbjwvbmctdGVtcGxhdGU+XG5cbiJdfQ==