UNPKG

vix-ui

Version:

324 lines 28.7 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ import { Component, Input } from '@angular/core'; export class VixGraphComponent { constructor() { this.title = ''; this.subtitle = ''; this.xAxis = (/** @type {?} */ ({})); // can define anything for x-axis. E.g. instead of numeric x-axis, category 0, category 1, ... this.yAxis = (/** @type {?} */ ({})); this.data = []; // format: data => [data1, data2], where data1 => [1, 2, ...] this.graphType = ''; // current supported: line, bar, scatter, pie this.customOptions = (/** @type {?} */ ({})); // any other options that are supported by e-charts this.labelFormatter = ''; this.tooltipFormatter = ''; this.currentData = (/** @type {?} */ ({})); this.isLoading = false; this.isGraphExists = false; } /** * @return {?} */ ngDoCheck() { if (JSON.stringify(this.currentData).toLowerCase() !== JSON.stringify(this.options).toLowerCase()) { this.isLoading = true; this.currentData = Object.assign({}, this.options); } } /** * @param {?} changes * @return {?} */ ngOnChanges(changes) { this.isLoading = true; this.initializeGraph(); } /** * @return {?} */ ngOnInit() { this.isLoading = true; this.currentData = Object.assign({}, this.options); this.initializeGraph(); } /** * @return {?} */ initializeGraph() { this.isGraphExists = false; // Variable definitions if (this.graphType === '' || (this.data.length <= 0)) { return; } /** @type {?} */ const tempGraphType = this.graphType; // Define some common options this.options = { title: { text: this.title, subtext: this.subtitle, }, width: this.width, height: this.height, animationEasing: 'exponentialOut', grid: { top: '10%', containLabel: true, }, tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' } }; if (this.tooltipFormatter.length > 0) { this.options.tooltip.formatter = this.tooltipFormatter; } this.configLineAxis(this.xAxis, this.yAxis, tempGraphType); // config the axis this.configLegend(tempGraphType, this.data); this.configSeries(tempGraphType, this.data); // config the data // Custom options - this will override any existing fields, if they coincide. if (!this.isEmptyObject(this.customOptions)) { Object.assign(this.options, this.customOptions); } // console.log(this.options) this.isGraphExists = true; } /** * @param {?} xAxis * @param {?} yAxis * @param {?} graphType * @return {?} */ configLineAxis(xAxis, yAxis, graphType) { if (graphType !== 'line') { return; } // Xaxis and Yaxis definitions this.options.xAxis = {}; this.options.yAxis = {}; if (!this.isEmptyObject(xAxis)) { this.options.xAxis = { type: xAxis.type, data: xAxis.data, nameGap: 30, silent: true, splitLine: { show: false, }, boundaryGap: false, animationDelay: function (idx) { return idx * 10; } }; } if (!this.isEmptyObject(yAxis)) { this.options.yAxis = { type: yAxis.type, data: yAxis.data, nameGap: 30, silent: true, splitLine: { show: false, }, boundaryGap: false, animationDelay: function (idx) { return idx * 10; } }; } this.options.axisPointer = { show: true, type: this.graphType, snap: true, }; this.options.tooltip = { trigger: 'axis', }; } /** * @param {?} graphType * @param {?} data * @return {?} */ configLegend(graphType, data) { /** @type {?} */ const legend = { padding: [30, 0, 0, 0], type: 'scroll', orient: 'horizontal', bottom: 0, data: [] }; switch (graphType) { case 'line': data.forEach(function (tempData) { if (tempData.name) { legend.data.push(tempData.name); } else { legend.data.push(''); } }); break; case 'pie': // For more flexibility - the data can be in an array, or just an object // This is because one pie chart can only take one data set if (Array.isArray(data)) { data.forEach(function (tempData) { tempData.data.forEach(function (point) { legend.data.push(point.name); }); }); } else { data.data.forEach(function (point) { legend.data.push(point.name); }); } break; default: break; } this.options.legend = legend; } /** * @param {?} graphType * @param {?} data * @return {?} */ configSeries(graphType, data) { /** @type {?} */ const series = []; /** @type {?} */ const seriesTemplate = this.retrieveSeriesTemplate(graphType); // Insert the data switch (graphType) { case 'line': // Processing the data into options format (series parameter) data.forEach(function (value) { /** @type {?} */ const dataOpts = (/** @type {?} */ ({})); // dataOpts.name = value.name; Object.keys(value).forEach(key => { dataOpts[key] = value[key]; }); series.push(dataOpts); }); break; case 'pie': if (Array.isArray(data)) { series.push({ data: data[0].data }); } else { series.push({ data: data.data }); } break; default: series.push({ data: data[0].data }); break; } series.forEach(function (subSeries) { Object.assign(subSeries, seriesTemplate); }); this.options.series = series; } /** * @param {?} graphType * @return {?} */ retrieveSeriesTemplate(graphType) { /** @type {?} */ let seriesTemplate = {}; switch (graphType) { case 'line': break; case 'pie': /** @type {?} */ const label = this.labelFormatter.length > 0 ? this.labelFormatter : '{b}: {c} ({d}%)'; seriesTemplate = { label: { formatter: label } }; break; } Object.assign(seriesTemplate, { type: graphType }); return seriesTemplate; } /** * @param {?} obj * @return {?} */ isEmptyObject(obj) { for (const name in obj) { if (name) { return false; } } return true; } } VixGraphComponent.decorators = [ { type: Component, args: [{ selector: 'vix-graph', template: "<div *ngIf=\"isGraphExists\" echarts [options]=\"options\"\nclass=\"demo-echarts\">\n</div>\n", styles: [""] }] } ]; VixGraphComponent.ctorParameters = () => []; VixGraphComponent.propDecorators = { title: [{ type: Input }], subtitle: [{ type: Input }], xAxis: [{ type: Input }], yAxis: [{ type: Input }], data: [{ type: Input }], graphType: [{ type: Input }], customOptions: [{ type: Input }], width: [{ type: Input }], height: [{ type: Input }], labelFormatter: [{ type: Input }], tooltipFormatter: [{ type: Input }] }; if (false) { /** @type {?} */ VixGraphComponent.prototype.title; /** @type {?} */ VixGraphComponent.prototype.subtitle; /** @type {?} */ VixGraphComponent.prototype.xAxis; /** @type {?} */ VixGraphComponent.prototype.yAxis; /** @type {?} */ VixGraphComponent.prototype.data; /** @type {?} */ VixGraphComponent.prototype.graphType; /** @type {?} */ VixGraphComponent.prototype.customOptions; /** @type {?} */ VixGraphComponent.prototype.width; /** @type {?} */ VixGraphComponent.prototype.height; /** @type {?} */ VixGraphComponent.prototype.labelFormatter; /** @type {?} */ VixGraphComponent.prototype.tooltipFormatter; /** @type {?} */ VixGraphComponent.prototype.options; /** @type {?} */ VixGraphComponent.prototype.currentData; /** @type {?} */ VixGraphComponent.prototype.iterableDiffer; /** @type {?} */ VixGraphComponent.prototype.isLoading; /** @type {?} */ VixGraphComponent.prototype.isGraphExists; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JhcGguY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6Im5nOi8vdml4LXVpLyIsInNvdXJjZXMiOlsidml4LWdyYXBoL2dyYXBoLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQUEsT0FBTyxFQUFFLFNBQVMsRUFBbUIsS0FBSyxFQUE0QixNQUFNLGVBQWUsQ0FBQztBQU81RixNQUFNO0lBbUJGO1FBakJTLFVBQUssR0FBRyxFQUFFLENBQUM7UUFDWCxhQUFRLEdBQUcsRUFBRSxDQUFDO1FBQ2QsVUFBSyxHQUFHLG1CQUFLLEVBQUUsRUFBQSxDQUFDLENBQUMsOEZBQThGO1FBQy9HLFVBQUssR0FBRyxtQkFBSyxFQUFFLEVBQUEsQ0FBQztRQUNoQixTQUFJLEdBQUcsRUFBRSxDQUFDLENBQUMsNkRBQTZEO1FBQ3hFLGNBQVMsR0FBRyxFQUFFLENBQUMsQ0FBQyw2Q0FBNkM7UUFDN0Qsa0JBQWEsR0FBRyxtQkFBSyxFQUFFLEVBQUEsQ0FBQyxDQUFDLG1EQUFtRDtRQUc1RSxtQkFBYyxHQUFHLEVBQUUsQ0FBQztRQUNwQixxQkFBZ0IsR0FBRyxFQUFFLENBQUM7UUFFL0IsZ0JBQVcsR0FBRyxtQkFBSyxFQUFFLEVBQUEsQ0FBQztRQUV0QixjQUFTLEdBQUcsS0FBSyxDQUFDO1FBQ2xCLGtCQUFhLEdBQUcsS0FBSyxDQUFDO0lBR3RCLENBQUM7Ozs7SUFFRCxTQUFTO1FBQ0wsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQyxXQUFXLEVBQUUsS0FBSyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRTtZQUMvRixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztZQUN0QixJQUFJLENBQUMsV0FBVyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztTQUN0RDtJQUNMLENB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dXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgRG9DaGVjaywgT25Jbml0LCBJbnB1dCwgU2ltcGxlQ2hhbmdlcywgT25DaGFuZ2VzIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3ZpeC1ncmFwaCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9ncmFwaC5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2dyYXBoLmNvbXBvbmVudC5zY3NzJ11cbn0pXG5leHBvcnQgY2xhc3MgVml4R3JhcGhDb21wb25lbnQgaW1wbGVtZW50cyBEb0NoZWNrLCBPbkluaXQsIE9uQ2hhbmdlcyB7XG5cbiAgICBASW5wdXQoKSB0aXRsZSA9ICcnO1xuICAgIEBJbnB1dCgpIHN1YnRpdGxlID0gJyc7XG4gICAgQElucHV0KCkgeEF4aXMgPSA8YW55Pnt9OyAvLyBjYW4gZGVmaW5lIGFueXRoaW5nIGZvciB4LWF4aXMuIEUuZy4gaW5zdGVhZCBvZiBudW1lcmljIHgtYXhpcywgY2F0ZWdvcnkgMCwgY2F0ZWdvcnkgMSwgLi4uXG4gICAgQElucHV0KCkgeUF4aXMgPSA8YW55Pnt9O1xuICAgIEBJbnB1dCgpIGRhdGEgPSBbXTsgLy8gZm9ybWF0OiBkYXRhID0+IFtkYXRhMSwgZGF0YTJdLCB3aGVyZSBkYXRhMSA9PiBbMSwgMiwgLi4uXVxuICAgIEBJbnB1dCgpIGdyYXBoVHlwZSA9ICcnOyAvLyBjdXJyZW50IHN1cHBvcnRlZDogbGluZSwgYmFyLCBzY2F0dGVyLCBwaWVcbiAgICBASW5wdXQoKSBjdXN0b21PcHRpb25zID0gPGFueT57fTsgLy8gYW55IG90aGVyIG9wdGlvbnMgdGhhdCBhcmUgc3VwcG9ydGVkIGJ5IGUtY2hhcnRzXG4gICAgQElucHV0KCkgd2lkdGg6IGFueTtcbiAgICBASW5wdXQoKSBoZWlnaHQ6IGFueTtcbiAgICBASW5wdXQoKSBsYWJlbEZvcm1hdHRlciA9ICcnO1xuICAgIEBJbnB1dCgpIHRvb2x0aXBGb3JtYXR0ZXIgPSAnJztcbiAgICBvcHRpb25zOiBhbnk7XG4gICAgY3VycmVudERhdGEgPSA8YW55Pnt9O1xuICAgIGl0ZXJhYmxlRGlmZmVyOiBhbnk7XG4gICAgaXNMb2FkaW5nID0gZmFsc2U7XG4gICAgaXNHcmFwaEV4aXN0cyA9IGZhbHNlO1xuXG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgfVxuXG4gICAgbmdEb0NoZWNrKCkge1xuICAgICAgICBpZiAoSlNPTi5zdHJpbmdpZnkodGhpcy5jdXJyZW50RGF0YSkudG9Mb3dlckNhc2UoKSAhPT0gSlNPTi5zdHJpbmdpZnkodGhpcy5vcHRpb25zKS50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgICAgICB0aGlzLmlzTG9hZGluZyA9IHRydWU7XG4gICAgICAgICAgICB0aGlzLmN1cnJlbnREYXRhID0gT2JqZWN0LmFzc2lnbih7fSwgdGhpcy5vcHRpb25zKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpIHtcbiAgICAgICAgdGhpcy5pc0xvYWRpbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLmluaXRpYWxpemVHcmFwaCgpO1xuICAgIH1cblxuICAgIG5nT25Jbml0KCkge1xuICAgICAgICB0aGlzLmlzTG9hZGluZyA9IHRydWU7XG4gICAgICAgIHRoaXMuY3VycmVudERhdGEgPSBPYmplY3QuYXNzaWduKHt9LCB0aGlzLm9wdGlvbnMpO1xuICAgICAgICB0aGlzLmluaXRpYWxpemVHcmFwaCgpO1xuICAgIH1cblxuICAgIGluaXRpYWxpemVHcmFwaCgpIHtcbiAgICAgIHRoaXMuaXNHcmFwaEV4aXN0cyA9IGZhbHNlO1xuICAgICAgLy8gVmFyaWFibGUgZGVmaW5pdGlvbnNcbiAgICAgIGlmICh0aGlzLmdyYXBoVHlwZSA9PT0gJycgfHwgKHRoaXMuZGF0YS5sZW5ndGggPD0gMCkpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCB0ZW1wR3JhcGhUeXBlID0gdGhpcy5ncmFwaFR5cGU7XG5cbiAgICAgIC8vIERlZmluZSBzb21lIGNvbW1vbiBvcHRpb25zXG4gICAgICB0aGlzLm9wdGlvbnMgPSB7XG4gICAgICAgICAgdGl0bGU6IHtcbiAgICAgICAgICAgIHRleHQ6IHRoaXMudGl0bGUsXG4gICAgICAgICAgICBzdWJ0ZXh0OiB0aGlzLnN1YnRpdGxlLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgd2lkdGg6IHRoaXMud2lkdGgsXG4gICAgICAgICAgaGVpZ2h0OiB0aGlzLmhlaWdodCxcbiAgICAgICAgICBhbmltYXRpb25FYXNpbmc6ICdleHBvbmVudGlhbE91dCcsXG4gICAgICAgICAgZ3JpZDoge1xuICAgICAgICAgICAgdG9wOiAnMTAlJyxcbiAgICAgICAgICAgIGNvbnRhaW5MYWJlbDogdHJ1ZSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHRvb2x0aXA6IHtcbiAgICAgICAgICAgICAgdHJpZ2dlcjogJ2l0ZW0nLFxuICAgICAgICAgICAgICBmb3JtYXR0ZXI6ICd7Yn06IHtjfSAoe2R9JSknXG4gICAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGlmICh0aGlzLnRvb2x0aXBGb3JtYXR0ZXIubGVuZ3RoID4gMCkge1xuICAgICAgICAgIHRoaXMub3B0aW9ucy50b29sdGlwLmZvcm1hdHRlciA9IHRoaXMudG9vbHRpcEZvcm1hdHRlcjtcbiAgICAgIH1cblxuICAgICAgdGhpcy5jb25maWdMaW5lQXhpcyh0aGlzLnhBeGlzLCB0aGlzLnlBeGlzLCB0ZW1wR3JhcGhUeXBlKTsgLy8gY29uZmlnIHRoZSBheGlzXG4gICAgICB0aGlzLmNvbmZpZ0xlZ2VuZCh0ZW1wR3JhcGhUeXBlLCB0aGlzLmRhdGEpO1xuICAgICAgdGhpcy5jb25maWdTZXJpZXModGVtcEdyYXBoVHlwZSwgdGhpcy5kYXRhKTsgLy8gY29uZmlnIHRoZSBkYXRhXG5cbiAgICAgIC8vIEN1c3RvbSBvcHRpb25zIC0gdGhpcyB3aWxsIG92ZXJyaWRlIGFueSBleGlzdGluZyBmaWVsZHMsIGlmIHRoZXkgY29pbmNpZGUuXG4gICAgICBpZiAoIXRoaXMuaXNFbXB0eU9iamVjdCh0aGlzLmN1c3RvbU9wdGlvbnMpKSB7XG4gICAgICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLm9wdGlvbnMsIHRoaXMuY3VzdG9tT3B0aW9ucyk7XG4gICAgICB9XG4gICAgICAvLyBjb25zb2xlLmxvZyh0aGlzLm9wdGlvbnMpXG4gICAgICB0aGlzLmlzR3JhcGhFeGlzdHMgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbmZpZ0xpbmVBeGlzKHhBeGlzLCB5QXhpcywgZ3JhcGhUeXBlKSB7XG4gICAgICAgIGlmIChncmFwaFR5cGUgIT09ICdsaW5lJykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIFhheGlzIGFuZCBZYXhpcyBkZWZpbml0aW9uc1xuICAgICAgICB0aGlzLm9wdGlvbnMueEF4aXMgPSB7fTtcbiAgICAgICAgdGhpcy5vcHRpb25zLnlBeGlzID0ge307XG4gICAgICAgIGlmICghdGhpcy5pc0VtcHR5T2JqZWN0KHhBeGlzKSkge1xuICAgICAgICAgICAgdGhpcy5vcHRpb25zLnhBeGlzID0ge1xuICAgICAgICAgICAgICAgIHR5cGU6IHhBeGlzLnR5cGUsXG4gICAgICAgICAgICAgICAgZGF0YTogeEF4aXMuZGF0YSxcbiAgICAgICAgICAgICAgICBuYW1lR2FwOiAzMCxcbiAgICAgICAgICAgICAgICBzaWxlbnQ6IHRydWUsXG4gICAgICAgICAgICAgICAgc3BsaXRMaW5lOiB7XG4gICAgICAgICAgICAgICAgICAgIHNob3c6IGZhbHNlLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgYm91bmRhcnlHYXA6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGFuaW1hdGlvbkRlbGF5OiBmdW5jdGlvbiAoaWR4KSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBpZHggKiAxMDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCF0aGlzLmlzRW1wdHlPYmplY3QoeUF4aXMpKSB7XG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMueUF4aXMgPSB7XG4gICAgICAgICAgICAgICAgdHlwZTogeUF4aXMudHlwZSxcbiAgICAgICAgICAgICAgICBkYXRhOiB5QXhpcy5kYXRhLFxuICAgICAgICAgICAgICAgIG5hbWVHYXA6IDMwLFxuICAgICAgICAgICAgICAgIHNpbGVudDogdHJ1ZSxcbiAgICAgICAgICAgICAgICBzcGxpdExpbmU6IHtcbiAgICAgICAgICAgICAgICAgICAgc2hvdzogZmFsc2UsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBib3VuZGFyeUdhcDogZmFsc2UsXG4gICAgICAgICAgICAgICAgYW5pbWF0aW9uRGVsYXk6IGZ1bmN0aW9uIChpZHgpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGlkeCAqIDEwO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLm9wdGlvbnMuYXhpc1BvaW50ZXIgPSB7XG4gICAgICAgICAgc2hvdzogdHJ1ZSxcbiAgICAgICAgICB0eXBlOiB0aGlzLmdyYXBoVHlwZSxcbiAgICAgICAgICBzbmFwOiB0cnVlLFxuICAgICAgICB9O1xuXG4gICAgICAgIHRoaXMub3B0aW9ucy50b29sdGlwID0ge1xuICAgICAgICAgICAgdHJpZ2dlcjogJ2F4aXMnLFxuICAgICAgICB9O1xuICAgIH1cblxuICAgIGNvbmZpZ0xlZ2VuZChncmFwaFR5cGUsIGRhdGEpIHtcbiAgICAgICAgY29uc3QgbGVnZW5kID0ge1xuICAgICAgICAgICAgcGFkZGluZzogWzMwLCAwLCAwLCAwXSxcbiAgICAgICAgICAgIHR5cGU6ICdzY3JvbGwnLFxuICAgICAgICAgICAgb3JpZW50OiAnaG9yaXpvbnRhbCcsXG4gICAgICAgICAgICBib3R0b206IDAsXG4gICAgICAgICAgICBkYXRhOiBbXVxuICAgICAgICB9O1xuXG4gICAgICAgIHN3aXRjaCAoZ3JhcGhUeXBlKSB7XG4gICAgICAgICAgICBjYXNlICdsaW5lJzpcbiAgICAgICAgICAgICAgICBkYXRhLmZvckVhY2goZnVuY3Rpb24odGVtcERhdGEpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRlbXBEYXRhLm5hbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5kYXRhLnB1c2godGVtcERhdGEubmFtZSk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuZGF0YS5wdXNoKCcnKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAncGllJzpcbiAgICAgICAgICAgICAgICAvLyBGb3IgbW9yZSBmbGV4aWJpbGl0eSAtIHRoZSBkYXRhIGNhbiBiZSBpbiBhbiBhcnJheSwgb3IganVzdCBhbiBvYmplY3RcbiAgICAgICAgICAgICAgICAvLyBUaGlzIGlzIGJlY2F1c2Ugb25lIHBpZSBjaGFydCBjYW4gb25seSB0YWtlIG9uZSBkYXRhIHNldFxuICAgICAgICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KGRhdGEpKSB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEuZm9yRWFjaChmdW5jdGlvbih0ZW1wRGF0YSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGVtcERhdGEuZGF0YS5mb3JFYWNoKGZ1bmN0aW9uKHBvaW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmRhdGEucHVzaChwb2ludC5uYW1lKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLmRhdGEuZm9yRWFjaChmdW5jdGlvbihwb2ludCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmRhdGEucHVzaChwb2ludC5uYW1lKTtcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMub3B0aW9ucy5sZWdlbmQgPSBsZWdlbmQ7XG4gICAgfVxuXG4gICAgY29uZmlnU2VyaWVzKGdyYXBoVHlwZSwgZGF0YSkge1xuICAgICAgICAgY29uc3Qgc2VyaWVzID0gW107XG4gICAgICAgICBjb25zdCBzZXJpZXNUZW1wbGF0ZSA9IHRoaXMucmV0cmlldmVTZXJpZXNUZW1wbGF0ZShncmFwaFR5cGUpO1xuXG4gICAgICAgICAvLyBJbnNlcnQgdGhlIGRhdGFcbiAgICAgICAgIHN3aXRjaCAoZ3JhcGhUeXBlKSB7XG4gICAgICAgICAgICBjYXNlICdsaW5lJzpcbiAgICAgICAgICAgICAgICAvLyBQcm9jZXNzaW5nIHRoZSBkYXRhIGludG8gb3B0aW9ucyBmb3JtYXQgKHNlcmllcyBwYXJhbWV0ZXIpXG4gICAgICAgICAgICAgICAgZGF0YS5mb3JFYWNoKGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBkYXRhT3B0cyA9IDxhbnk+e307XG4gICAgICAgICAgICAgICAgICAgIC8vIGRhdGFPcHRzLm5hbWUgPSB2YWx1ZS5uYW1lO1xuICAgICAgICAgICAgICAgICAgICBPYmplY3Qua2V5cyh2YWx1ZSkuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YU9wdHNba2V5XSA9IHZhbHVlW2tleV07XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICBzZXJpZXMucHVzaChkYXRhT3B0cyk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdwaWUnOlxuICAgICAgICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KGRhdGEpKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlcmllcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IGRhdGFbMF0uZGF0YVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBzZXJpZXMucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiBkYXRhLmRhdGFcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgc2VyaWVzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgZGF0YTogZGF0YVswXS5kYXRhXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgIH1cbiAgICAgICAgIHNlcmllcy5mb3JFYWNoKGZ1bmN0aW9uKHN1YlNlcmllcykge1xuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbihzdWJTZXJpZXMsIHNlcmllc1RlbXBsYXRlKTtcbiAgICAgICAgIH0pO1xuXG4gICAgICAgICB0aGlzLm9wdGlvbnMuc2VyaWVzID0gc2VyaWVzO1xuICAgIH1cblxuICAgIHJldHJpZXZlU2VyaWVzVGVtcGxhdGUoZ3JhcGhUeXBlKSB7XG4gICAgICAgIGxldCBzZXJpZXNUZW1wbGF0ZSA9IHt9O1xuICAgICAgICBzd2l0Y2ggKGdyYXBoVHlwZSkge1xuICAgICAgICAgICAgY2FzZSAnbGluZSc6XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAncGllJzpcbiAgICAgICAgICAgICAgY29uc3QgbGFiZWwgPSB0aGlzLmxhYmVsRm9ybWF0dGVyLmxlbmd0aCA+IDAgPyB0aGlzLmxhYmVsRm9ybWF0dGVyIDogJ3tifToge2N9ICh7ZH0lKSc7XG4gICAgICAgICAgICAgIHNlcmllc1RlbXBsYXRlID0ge1xuICAgICAgICAgICAgICAgICAgbGFiZWw6IHtcbiAgICAgICAgICAgICAgICAgICAgICBmb3JtYXR0ZXI6IGxhYmVsXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIE9iamVjdC5hc3NpZ24oc2VyaWVzVGVtcGxhdGUsIHt0eXBlOiBncmFwaFR5cGV9KTtcbiAgICAgICAgcmV0dXJuIHNlcmllc1RlbXBsYXRlO1xuICAgIH1cblxuICAgIGlzRW1wdHlPYmplY3Qob2JqKSB7XG4gICAgICAgIGZvciAoY29uc3QgbmFtZSBpbiBvYmopIHtcbiAgICAgICAgICAgIGlmIChuYW1lKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbn1cbiJdfQ==