ngx-trend
Version:
ngx-trend Angular component
255 lines • 25.4 kB
JavaScript
import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';
import { Component, Input, ViewChild } from '@angular/core';
import { buildLinearPath, buildSmoothPath } from '../helpers/DOM.helpers';
import { normalize } from '../helpers/math.helpers';
import { generateId } from '../helpers/misc.helpers';
import { normalizeDataset } from './trend.helpers';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
export class TrendComponent {
constructor() {
this.autoDraw = false;
this.autoDrawDuration = 2000;
this.autoDrawEasing = 'ease';
this.padding = 8;
this.radius = 10;
this.stroke = 'black';
this.strokeLinecap = '';
this.strokeWidth = 1;
this.gradient = [];
this.svgHeight = '25%';
this.svgWidth = '100%';
this.animationState = '';
this.id = generateId();
this.gradientId = `ngx-trend-vertical-gradient-${this.id}`;
}
ngOnChanges() {
// We need at least 2 points to draw a graph.
if (!this.data || this.data.length < 2) {
return;
}
// `data` can either be an array of numbers:
// [1, 2, 3]
// or, an array of objects containing a value:
// [{ value: 1 }, { value: 2 }, { value: 3 }]
//
// For now, we're just going to convert the second form to the first.
// Later on, if/when we support tooltips, we may adjust.
const plainValues = this.data.map(point => {
if (typeof point === 'number') {
return point;
}
return point.value;
});
// Our viewbox needs to be in absolute units, so we'll default to 300x75
// Our SVG can be a %, though; this is what makes it scalable.
// By defaulting to percentages, the SVG will grow to fill its parent
// container, preserving a 1/4 aspect ratio.
const viewBoxWidth = this.width || 300;
const viewBoxHeight = this.height || 75;
this.svgWidth = this.width || '100%';
this.svgHeight = this.height || '25%';
this.viewBox = `0 0 ${viewBoxWidth} ${viewBoxHeight}`;
const root = location.href.split(location.hash || '#')[0];
this.pathStroke =
this.gradient && this.gradient.length ? `url('${root}#${this.gradientId}')` : undefined;
this.gradientTrimmed = this.gradient
.slice()
.reverse()
.map((val, idx) => {
return {
idx,
stopColor: val,
offset: normalize(idx, 0, this.gradient.length - 1 || 1),
};
});
const normalizedValues = normalizeDataset(plainValues, this.padding, viewBoxWidth - this.padding,
// NOTE: Because SVGs are indexed from the top left, but most data is
// indexed from the bottom left, we're inverting the Y min/max.
viewBoxHeight - this.padding, this.padding);
if (this.autoDraw && this.animationState !== 'active') {
this.animationState = 'inactive';
setTimeout(() => {
this.lineLength = this.pathEl.nativeElement.getTotalLength();
this.animationState = 'active';
});
}
this.d = this.smooth
? buildSmoothPath(normalizedValues, this.radius)
: buildLinearPath(normalizedValues);
}
}
TrendComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: TrendComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
TrendComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.1", type: TrendComponent, selector: "ngx-trend", inputs: { data: "data", smooth: "smooth", autoDraw: "autoDraw", autoDrawDuration: "autoDrawDuration", autoDrawEasing: "autoDrawEasing", width: "width", height: "height", padding: "padding", radius: "radius", stroke: "stroke", strokeLinecap: "strokeLinecap", strokeWidth: "strokeWidth", gradient: "gradient", preserveAspectRatio: "preserveAspectRatio", svgHeight: "svgHeight", svgWidth: "svgWidth" }, viewQueries: [{ propertyName: "pathEl", first: true, predicate: ["pathEl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
<svg
*ngIf="data && data.length >= 2"
[attr.width]="svgWidth"
[attr.height]="svgHeight"
[attr.stroke]="stroke"
[attr.stroke-width]="strokeWidth"
[attr.stroke-linecap]="strokeLinecap"
[attr.viewBox]="viewBox"
[attr.preserveAspectRatio]="preserveAspectRatio"
>
<defs *ngIf="gradient && gradient.length">
<linearGradient [attr.id]="gradientId" x1="0%" y1="0%" x2="0%" y2="100%">
<stop
*ngFor="let g of gradientTrimmed"
[attr.key]="g.idx"
[attr.offset]="g.offset"
[attr.stop-color]="g.stopColor"
/>
</linearGradient>
</defs>
<path
fill="none"
#pathEl
[attr.stroke]="pathStroke"
[attr.d]="d"
[@pathAnimaiton]="{
value: animationState,
params: {
autoDrawDuration: autoDrawDuration,
autoDrawEasing: autoDrawEasing,
lineLength: lineLength
}
}"
/>
</svg>
`, isInline: true, directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], animations: [
trigger('pathAnimaiton', [
state('inactive', style({ display: 'none' })),
transition('* => active', [
style({ display: 'initial' }),
// We do the animation using the dash array/offset trick
// https://css-tricks.com/svg-line-animation-works/
animate('{{ autoDrawDuration }}ms {{ autoDrawEasing }}', keyframes([
style({
'stroke-dasharray': '{{ lineLength }}px',
'stroke-dashoffset': '{{ lineLength }}px',
}),
style({
'stroke-dasharray': '{{ lineLength }}px',
'stroke-dashoffset': 0,
}),
])),
// One unfortunate side-effect of the auto-draw is that the line is
// actually 1 big dash, the same length as the line itself. If the
// line length changes (eg. radius change, new data), that dash won't
// be the same length anymore. We can fix that by removing those
// properties once the auto-draw is completed.
style({
'stroke-dashoffset': '',
'stroke-dasharray': '',
}),
]),
]),
] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.1", ngImport: i0, type: TrendComponent, decorators: [{
type: Component,
args: [{
selector: 'ngx-trend',
template: `
<svg
*ngIf="data && data.length >= 2"
[attr.width]="svgWidth"
[attr.height]="svgHeight"
[attr.stroke]="stroke"
[attr.stroke-width]="strokeWidth"
[attr.stroke-linecap]="strokeLinecap"
[attr.viewBox]="viewBox"
[attr.preserveAspectRatio]="preserveAspectRatio"
>
<defs *ngIf="gradient && gradient.length">
<linearGradient [attr.id]="gradientId" x1="0%" y1="0%" x2="0%" y2="100%">
<stop
*ngFor="let g of gradientTrimmed"
[attr.key]="g.idx"
[attr.offset]="g.offset"
[attr.stop-color]="g.stopColor"
/>
</linearGradient>
</defs>
<path
fill="none"
#pathEl
[attr.stroke]="pathStroke"
[attr.d]="d"
[@pathAnimaiton]="{
value: animationState,
params: {
autoDrawDuration: autoDrawDuration,
autoDrawEasing: autoDrawEasing,
lineLength: lineLength
}
}"
/>
</svg>
`,
animations: [
trigger('pathAnimaiton', [
state('inactive', style({ display: 'none' })),
transition('* => active', [
style({ display: 'initial' }),
// We do the animation using the dash array/offset trick
// https://css-tricks.com/svg-line-animation-works/
animate('{{ autoDrawDuration }}ms {{ autoDrawEasing }}', keyframes([
style({
'stroke-dasharray': '{{ lineLength }}px',
'stroke-dashoffset': '{{ lineLength }}px',
}),
style({
'stroke-dasharray': '{{ lineLength }}px',
'stroke-dashoffset': 0,
}),
])),
// One unfortunate side-effect of the auto-draw is that the line is
// actually 1 big dash, the same length as the line itself. If the
// line length changes (eg. radius change, new data), that dash won't
// be the same length anymore. We can fix that by removing those
// properties once the auto-draw is completed.
style({
'stroke-dashoffset': '',
'stroke-dasharray': '',
}),
]),
]),
],
}]
}], ctorParameters: function () { return []; }, propDecorators: { data: [{
type: Input
}], smooth: [{
type: Input
}], autoDraw: [{
type: Input
}], autoDrawDuration: [{
type: Input
}], autoDrawEasing: [{
type: Input
}], width: [{
type: Input
}], height: [{
type: Input
}], padding: [{
type: Input
}], radius: [{
type: Input
}], stroke: [{
type: Input
}], strokeLinecap: [{
type: Input
}], strokeWidth: [{
type: Input
}], gradient: [{
type: Input
}], preserveAspectRatio: [{
type: Input
}], svgHeight: [{
type: Input
}], svgWidth: [{
type: Input
}], pathEl: [{
type: ViewChild,
args: ['pathEl']
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlbmQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi90cmVuZC90cmVuZC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDNUYsT0FBTyxFQUFFLFNBQVMsRUFBYyxLQUFLLEVBQWEsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRW5GLE9BQU8sRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDMUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQzs7O0FBMEVuRCxNQUFNLE9BQU8sY0FBYztJQTJCekI7UUF2QlMsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUNqQixxQkFBZ0IsR0FBRyxJQUFJLENBQUM7UUFDeEIsbUJBQWMsR0FBRyxNQUFNLENBQUM7UUFHeEIsWUFBTyxHQUFHLENBQUMsQ0FBQztRQUNaLFdBQU0sR0FBRyxFQUFFLENBQUM7UUFDWixXQUFNLEdBQUcsT0FBTyxDQUFDO1FBQ2pCLGtCQUFhLEdBQUcsRUFBRSxDQUFDO1FBQ25CLGdCQUFXLEdBQUcsQ0FBQyxDQUFDO1FBQ2hCLGFBQVEsR0FBYSxFQUFFLENBQUM7UUFFeEIsY0FBUyxHQUFvQixLQUFLLENBQUM7UUFDbkMsYUFBUSxHQUFvQixNQUFNLENBQUM7UUFRNUMsbUJBQWMsR0FBRyxFQUFFLENBQUM7UUFHbEIsSUFBSSxDQUFDLEVBQUUsR0FBRyxVQUFVLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsVUFBVSxHQUFHLCtCQUErQixJQUFJ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O3NCQUExQixTQUFTO3VCQUFDLFFBQVEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhbmltYXRlLCBrZXlmcmFtZXMsIHN0YXRlLCBzdHlsZSwgdHJhbnNpdGlvbiwgdHJpZ2dlciB9IGZyb20gJ0Bhbmd1bGFyL2FuaW1hdGlvbnMnO1xuaW1wb3J0IHsgQ29tcG9uZW50LCBFbGVtZW50UmVmLCBJbnB1dCwgT25DaGFuZ2VzLCBWaWV3Q2hpbGQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHsgYnVpbGRMaW5lYXJQYXRoLCBidWlsZFNtb290aFBhdGggfSBmcm9tICcuLi9oZWxwZXJzL0RPTS5oZWxwZXJzJztcbmltcG9ydCB7IG5vcm1hbGl6ZSB9IGZyb20gJy4uL2hlbHBlcnMvbWF0aC5oZWxwZXJzJztcbmltcG9ydCB7IGdlbmVyYXRlSWQgfSBmcm9tICcuLi9oZWxwZXJzL21pc2MuaGVscGVycyc7XG5pbXBvcnQgeyBub3JtYWxpemVEYXRhc2V0IH0gZnJvbSAnLi90cmVuZC5oZWxwZXJzJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbmd4LXRyZW5kJyxcbiAgdGVtcGxhdGU6IGBcbiAgICA8c3ZnXG4gICAgICAqbmdJZj1cImRhdGEgJiYgZGF0YS5sZW5ndGggPj0gMlwiXG4gICAgICBbYXR0ci53aWR0aF09XCJzdmdXaWR0aFwiXG4gICAgICBbYXR0ci5oZWlnaHRdPVwic3ZnSGVpZ2h0XCJcbiAgICAgIFthdHRyLnN0cm9rZV09XCJzdHJva2VcIlxuICAgICAgW2F0dHIuc3Ryb2tlLXdpZHRoXT1cInN0cm9rZVdpZHRoXCJcbiAgICAgIFthdHRyLnN0cm9rZS1saW5lY2FwXT1cInN0cm9rZUxpbmVjYXBcIlxuICAgICAgW2F0dHIudmlld0JveF09XCJ2aWV3Qm94XCJcbiAgICAgIFthdHRyLnByZXNlcnZlQXNwZWN0UmF0aW9dPVwicHJlc2VydmVBc3BlY3RSYXRpb1wiXG4gICAgPlxuICAgICAgPGRlZnMgKm5nSWY9XCJncmFkaWVudCAmJiBncmFkaWVudC5sZW5ndGhcIj5cbiAgICAgICAgPGxpbmVhckdyYWRpZW50IFthdHRyLmlkXT1cImdyYWRpZW50SWRcIiB4MT1cIjAlXCIgeTE9XCIwJVwiIHgyPVwiMCVcIiB5Mj1cIjEwMCVcIj5cbiAgICAgICAgICA8c3RvcFxuICAgICAgICAgICAgKm5nRm9yPVwibGV0IGcgb2YgZ3JhZGllbnRUcmltbWVkXCJcbiAgICAgICAgICAgIFthdHRyLmtleV09XCJnLmlkeFwiXG4gICAgICAgICAgICBbYXR0ci5vZmZzZXRdPVwiZy5vZmZzZXRcIlxuICAgICAgICAgICAgW2F0dHIuc3RvcC1jb2xvcl09XCJnLnN0b3BDb2xvclwiXG4gICAgICAgICAgLz5cbiAgICAgICAgPC9saW5lYXJHcmFkaWVudD5cbiAgICAgIDwvZGVmcz5cbiAgICAgIDxwYXRoXG4gICAgICAgIGZpbGw9XCJub25lXCJcbiAgICAgICAgI3BhdGhFbFxuICAgICAgICBbYXR0ci5zdHJva2VdPVwicGF0aFN0cm9rZVwiXG4gICAgICAgIFthdHRyLmRdPVwiZFwiXG4gICAgICAgIFtAcGF0aEFuaW1haXRvbl09XCJ7XG4gICAgICAgICAgdmFsdWU6IGFuaW1hdGlvblN0YXRlLFxuICAgICAgICAgIHBhcmFtczoge1xuICAgICAgICAgICAgYXV0b0RyYXdEdXJhdGlvbjogYXV0b0RyYXdEdXJhdGlvbixcbiAgICAgICAgICAgIGF1dG9EcmF3RWFzaW5nOiBhdXRvRHJhd0Vhc2luZyxcbiAgICAgICAgICAgIGxpbmVMZW5ndGg6IGxpbmVMZW5ndGhcbiAgICAgICAgICB9XG4gICAgICAgIH1cIlxuICAgICAgLz5cbiAgICA8L3N2Zz5cbiAgYCxcbiAgYW5pbWF0aW9uczogW1xuICAgIHRyaWdnZXIoJ3BhdGhBbmltYWl0b24nLCBbXG4gICAgICBzdGF0ZSgnaW5hY3RpdmUnLCBzdHlsZSh7IGRpc3BsYXk6ICdub25lJyB9KSksXG4gICAgICB0cmFuc2l0aW9uKCcqID0+IGFjdGl2ZScsIFtcbiAgICAgICAgc3R5bGUoeyBkaXNwbGF5OiAnaW5pdGlhbCcgfSksXG4gICAgICAgIC8vIFdlIGRvIHRoZSBhbmltYXRpb24gdXNpbmcgdGhlIGRhc2ggYXJyYXkvb2Zmc2V0IHRyaWNrXG4gICAgICAgIC8vIGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vc3ZnLWxpbmUtYW5pbWF0aW9uLXdvcmtzL1xuICAgICAgICBhbmltYXRlKFxuICAgICAgICAgICd7eyBhdXRvRHJhd0R1cmF0aW9uIH19bXMge3sgYXV0b0RyYXdFYXNpbmcgfX0nLFxuICAgICAgICAgIGtleWZyYW1lcyhbXG4gICAgICAgICAgICBzdHlsZSh7XG4gICAgICAgICAgICAgICdzdHJva2UtZGFzaGFycmF5JzogJ3t7IGxpbmVMZW5ndGggfX1weCcsXG4gICAgICAgICAgICAgICdzdHJva2UtZGFzaG9mZnNldCc6ICd7eyBsaW5lTGVuZ3RoIH19cHgnLFxuICAgICAgICAgICAgfSksXG4gICAgICAgICAgICBzdHlsZSh7XG4gICAgICAgICAgICAgICdzdHJva2UtZGFzaGFycmF5JzogJ3t7IGxpbmVMZW5ndGggfX1weCcsXG4gICAgICAgICAgICAgICdzdHJva2UtZGFzaG9mZnNldCc6IDAsXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICBdKSxcbiAgICAgICAgKSxcbiAgICAgICAgLy8gT25lIHVuZm9ydHVuYXRlIHNpZGUtZWZmZWN0IG9mIHRoZSBhdXRvLWRyYXcgaXMgdGhhdCB0aGUgbGluZSBpc1xuICAgICAgICAvLyBhY3R1YWxseSAxIGJpZyBkYXNoLCB0aGUgc2FtZSBsZW5ndGggYXMgdGhlIGxpbmUgaXRzZWxmLiBJZiB0aGVcbiAgICAgICAgLy8gbGluZSBsZW5ndGggY2hhbmdlcyAoZWcuIHJhZGl1cyBjaGFuZ2UsIG5ldyBkYXRhKSwgdGhhdCBkYXNoIHdvbid0XG4gICAgICAgIC8vIGJlIHRoZSBzYW1lIGxlbmd0aCBhbnltb3JlLiBXZSBjYW4gZml4IHRoYXQgYnkgcmVtb3ZpbmcgdGhvc2VcbiAgICAgICAgLy8gcHJvcGVydGllcyBvbmNlIHRoZSBhdXRvLWRyYXcgaXMgY29tcGxldGVkLlxuICAgICAgICBzdHlsZSh7XG4gICAgICAgICAgJ3N0cm9rZS1kYXNob2Zmc2V0JzogJycsXG4gICAgICAgICAgJ3N0cm9rZS1kYXNoYXJyYXknOiAnJyxcbiAgICAgICAgfSksXG4gICAgICBdKSxcbiAgICBdKSxcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgVHJlbmRDb21wb25lbnQgaW1wbGVtZW50cyBPbkNoYW5nZXMge1xuICBpZDogbnVtYmVyO1xuICBASW5wdXQoKSBkYXRhPzogQXJyYXk8KG51bWJlciB8IHsgdmFsdWU6IG51bWJlciB9KT47XG4gIEBJbnB1dCgpIHNtb290aD86IGJvb2xlYW47XG4gIEBJbnB1dCgpIGF1dG9EcmF3ID0gZmFsc2U7XG4gIEBJbnB1dCgpIGF1dG9EcmF3RHVyYXRpb24gPSAyMDAwO1xuICBASW5wdXQoKSBhdXRvRHJhd0Vhc2luZyA9ICdlYXNlJztcbiAgQElucHV0KCkgd2lkdGg/OiBudW1iZXI7XG4gIEBJbnB1dCgpIGhlaWdodD86IG51bWJlcjtcbiAgQElucHV0KCkgcGFkZGluZyA9IDg7XG4gIEBJbnB1dCgpIHJhZGl1cyA9IDEwO1xuICBASW5wdXQoKSBzdHJva2UgPSAnYmxhY2snO1xuICBASW5wdXQoKSBzdHJva2VMaW5lY2FwID0gJyc7XG4gIEBJbnB1dCgpIHN0cm9rZVdpZHRoID0gMTtcbiAgQElucHV0KCkgZ3JhZGllbnQ6IHN0cmluZ1tdID0gW107XG4gIEBJbnB1dCgpIHByZXNlcnZlQXNwZWN0UmF0aW8/OiBzdHJpbmc7XG4gIEBJbnB1dCgpIHN2Z0hlaWdodDogc3RyaW5nIHwgbnVtYmVyID0gJzI1JSc7XG4gIEBJbnB1dCgpIHN2Z1dpZHRoOiBzdHJpbmcgfCBudW1iZXIgPSAnMTAwJSc7XG4gIEBWaWV3Q2hpbGQoJ3BhdGhFbCcpIHBhdGhFbCE6IEVsZW1lbnRSZWY7XG4gIGdyYWRpZW50VHJpbW1lZCE6IEFycmF5PHsgaWR4OiBudW1iZXI7IHN0b3BDb2xvcjogc3RyaW5nOyBvZmZzZXQ6IG51bWJlciB9PjtcbiAgZDogYW55O1xuICB2aWV3Qm94ITogc3RyaW5nO1xuICBwYXRoU3Ryb2tlOiBhbnk7XG4gIGdyYWRpZW50SWQ6IHN0cmluZztcbiAgbGluZUxlbmd0aCE6IG51bWJlcjtcbiAgYW5pbWF0aW9uU3RhdGUgPSAnJztcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmlkID0gZ2VuZXJhdGVJZCgpO1xuICAgIHRoaXMuZ3JhZGllbnRJZCA9IGBuZ3gtdHJlbmQtdmVydGljYWwtZ3JhZGllbnQtJHt0aGlzLmlkfWA7XG4gIH1cbiAgbmdPbkNoYW5nZXMoKTogdm9pZCB7XG4gICAgLy8gV2UgbmVlZCBhdCBsZWFzdCAyIHBvaW50cyB0byBkcmF3IGEgZ3JhcGguXG4gICAgaWYgKCF0aGlzLmRhdGEgfHwgdGhpcy5kYXRhLmxlbmd0aCA8IDIpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBgZGF0YWAgY2FuIGVpdGhlciBiZSBhbiBhcnJheSBvZiBudW1iZXJzOlxuICAgIC8vIFsxLCAyLCAzXVxuICAgIC8vIG9yLCBhbiBhcnJheSBvZiBvYmplY3RzIGNvbnRhaW5pbmcgYSB2YWx1ZTpcbiAgICAvLyBbeyB2YWx1ZTogMSB9LCB7IHZhbHVlOiAyIH0sIHsgdmFsdWU6IDMgfV1cbiAgICAvL1xuICAgIC8vIEZvciBub3csIHdlJ3JlIGp1c3QgZ29pbmcgdG8gY29udmVydCB0aGUgc2Vjb25kIGZvcm0gdG8gdGhlIGZpcnN0LlxuICAgIC8vIExhdGVyIG9uLCBpZi93aGVuIHdlIHN1cHBvcnQgdG9vbHRpcHMsIHdlIG1heSBhZGp1c3QuXG4gICAgY29uc3QgcGxhaW5WYWx1ZXMgPSB0aGlzLmRhdGEubWFwKHBvaW50ID0+IHtcbiAgICAgIGlmICh0eXBlb2YgcG9pbnQgPT09ICdudW1iZXInKSB7XG4gICAgICAgIHJldHVybiBwb2ludDtcbiAgICAgIH1cbiAgICAgIHJldHVybiBwb2ludC52YWx1ZTtcbiAgICB9KTtcblxuICAgIC8vIE91ciB2aWV3Ym94IG5lZWRzIHRvIGJlIGluIGFic29sdXRlIHVuaXRzLCBzbyB3ZSdsbCBkZWZhdWx0IHRvIDMwMHg3NVxuICAgIC8vIE91ciBTVkcgY2FuIGJlIGEgJSwgdGhvdWdoOyB0aGlzIGlzIHdoYXQgbWFrZXMgaXQgc2NhbGFibGUuXG4gICAgLy8gQnkgZGVmYXVsdGluZyB0byBwZXJjZW50YWdlcywgdGhlIFNWRyB3aWxsIGdyb3cgdG8gZmlsbCBpdHMgcGFyZW50XG4gICAgLy8gY29udGFpbmVyLCBwcmVzZXJ2aW5nIGEgMS80IGFzcGVjdCByYXRpby5cbiAgICBjb25zdCB2aWV3Qm94V2lkdGggPSB0aGlzLndpZHRoIHx8IDMwMDtcbiAgICBjb25zdCB2aWV3Qm94SGVpZ2h0ID0gdGhpcy5oZWlnaHQgfHwgNzU7XG4gICAgdGhpcy5zdmdXaWR0aCA9IHRoaXMud2lkdGggfHwgJzEwMCUnO1xuICAgIHRoaXMuc3ZnSGVpZ2h0ID0gdGhpcy5oZWlnaHQgfHwgJzI1JSc7XG4gICAgdGhpcy52aWV3Qm94ID0gYDAgMCAke3ZpZXdCb3hXaWR0aH0gJHt2aWV3Qm94SGVpZ2h0fWA7XG4gICAgY29uc3Qgcm9vdCA9IGxvY2F0aW9uLmhyZWYuc3BsaXQobG9jYXRpb24uaGFzaCB8fCAnIycpWzBdO1xuICAgIHRoaXMucGF0aFN0cm9rZSA9XG4gICAgICB0aGlzLmdyYWRpZW50ICYmIHRoaXMuZ3JhZGllbnQubGVuZ3RoID8gYHVybCgnJHtyb290fSMke3RoaXMuZ3JhZGllbnRJZH0nKWAgOiB1bmRlZmluZWQ7XG5cbiAgICB0aGlzLmdyYWRpZW50VHJpbW1lZCA9IHRoaXMuZ3JhZGllbnRcbiAgICAgIC5zbGljZSgpXG4gICAgICAucmV2ZXJzZSgpXG4gICAgICAubWFwKCh2YWwsIGlkeCkgPT4ge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGlkeCxcbiAgICAgICAgICBzdG9wQ29sb3I6IHZhbCxcbiAgICAgICAgICBvZmZzZXQ6IG5vcm1hbGl6ZShpZHgsIDAsIHRoaXMuZ3JhZGllbnQubGVuZ3RoIC0gMSB8fCAxKSxcbiAgICAgICAgfTtcbiAgICAgIH0pO1xuXG4gICAgY29uc3Qgbm9ybWFsaXplZFZhbHVlcyA9IG5vcm1hbGl6ZURhdGFzZXQoXG4gICAgICBwbGFpblZhbHVlcyxcbiAgICAgIHRoaXMucGFkZGluZyxcbiAgICAgIHZpZXdCb3hXaWR0aCAtIHRoaXMucGFkZGluZyxcbiAgICAgIC8vIE5PVEU6IEJlY2F1c2UgU1ZHcyBhcmUgaW5kZXhlZCBmcm9tIHRoZSB0b3AgbGVmdCwgYnV0IG1vc3QgZGF0YSBpc1xuICAgICAgLy8gaW5kZXhlZCBmcm9tIHRoZSBib3R0b20gbGVmdCwgd2UncmUgaW52ZXJ0aW5nIHRoZSBZIG1pbi9tYXguXG4gICAgICB2aWV3Qm94SGVpZ2h0IC0gdGhpcy5wYWRkaW5nLFxuICAgICAgdGhpcy5wYWRkaW5nLFxuICAgICk7XG5cbiAgICBpZiAodGhpcy5hdXRvRHJhdyAmJiB0aGlzLmFuaW1hdGlvblN0YXRlICE9PSAnYWN0aXZlJykge1xuICAgICAgdGhpcy5hbmltYXRpb25TdGF0ZSA9ICdpbmFjdGl2ZSc7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgdGhpcy5saW5lTGVuZ3RoID0gdGhpcy5wYXRoRWwubmF0aXZlRWxlbWVudC5nZXRUb3RhbExlbmd0aCgpO1xuICAgICAgICB0aGlzLmFuaW1hdGlvblN0YXRlID0gJ2FjdGl2ZSc7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICB0aGlzLmQgPSB0aGlzLnNtb290aFxuICAgICAgPyBidWlsZFNtb290aFBhdGgobm9ybWFsaXplZFZhbHVlcywgdGhpcy5yYWRpdXMpXG4gICAgICA6IGJ1aWxkTGluZWFyUGF0aChub3JtYWxpemVkVmFsdWVzKTtcbiAgfVxufVxuIl19