UNPKG

ngx-trend

Version:
255 lines 25.4 kB
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