@opitzconsulting/pie-chart
Version:
animated pie chart component
813 lines (804 loc) • 87.7 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('d3'), require('@angular/platform-browser')) :
typeof define === 'function' && define.amd ? define('@opitzconsulting/pie-chart', ['exports', '@angular/core', 'd3', '@angular/platform-browser'], factory) :
(factory((global.opitzconsulting = global.opitzconsulting || {}, global.opitzconsulting['pie-chart'] = {}),global.ng.core,null,global.ng.platformBrowser));
}(this, (function (exports,i0,d3,platformBrowser) { 'use strict';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var PieChartService = (function () {
function PieChartService() {
}
PieChartService.decorators = [
{ type: i0.Injectable, args: [{
providedIn: 'root'
},] },
];
/** @nocollapse */
PieChartService.ctorParameters = function () { return []; };
/** @nocollapse */ PieChartService.ngInjectableDef = i0.defineInjectable({ factory: function PieChartService_Factory() { return new PieChartService(); }, token: PieChartService, providedIn: "root" });
return PieChartService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var PieChartComponent = (function () {
/**
* constructor
* @param element
*/
function PieChartComponent(element) {
this.element = element;
/**
* chart data, which should be displayed
*/
this.data = [];
/**
* chart width in pixel
*/
this.width = 250;
/**
* chart height in pixel
*/
this.height = 250;
/**
* duration of animation transition
*/
this.duration = 1000;
/**
* inner spacing in pixel, if greater than 0 it defines the radius of the empty circle in the middle
*/
this.innerSpacing = 0;
/**
* outer spacing in pixel
*/
this.outerSpacing = 1;
/**
* fired when user clicks on a chart entry
*/
this.chartClick = new i0.EventEmitter();
/**
* fired when user hovers a chart entry
*/
this.chartHover = new i0.EventEmitter();
/**
* current chart data with angle and path definitions, it will be consistent to the representation
*/
this.curData = [];
/**
* end chart data with angle and path definitions, it will representate the end state and used only for interpolation
*/
this.endData = [];
/**
* copy of last processed data, used to identify changes in ngDoCheck that Angular overlooked
*/
this.lastData = [];
/**
* Function for interrupt a running chart animation. Necessary because if transition is still active
* when a new transition is started, tween factory function from previos transition will still be fired
* until end of transition is reached. For entries which have a started transition the tween factory
* function will be fired multiple times with different tween interpolation range!
*/
this.interrupt = undefined;
}
/**
* Creates a deep copy of an variable. Do not use this function with recursive objects or
* browser objects like window or document.
* ToDo: should be outsourced.
* @param v
*/
/**
* Creates a deep copy of an variable. Do not use this function with recursive objects or
* browser objects like window or document.
* ToDo: should be outsourced.
* @template T
* @param {?} v
* @return {?}
*/
PieChartComponent.prototype.deepCopy = /**
* Creates a deep copy of an variable. Do not use this function with recursive objects or
* browser objects like window or document.
* ToDo: should be outsourced.
* @template T
* @param {?} v
* @return {?}
*/
function (v) {
return JSON.parse(JSON.stringify(v));
};
/**
* @return {?}
*/
PieChartComponent.prototype.ngOnInit = /**
* @return {?}
*/
function () {
this.tooltip = /** @type {?} */ (this.element.nativeElement.querySelector('div.pie-chart-tooltip'));
};
/**
* Fired when Angular (re-)sets data-bound properties. This function does not fire when changed data in bound objects or arrays.
* Angular only checks references.
* @param changes
*/
/**
* Fired when Angular (re-)sets data-bound properties. This function does not fire when changed data in bound objects or arrays.
* Angular only checks references.
* @param {?} changes
* @return {?}
*/
PieChartComponent.prototype.ngOnChanges = /**
* Fired when Angular (re-)sets data-bound properties. This function does not fire when changed data in bound objects or arrays.
* Angular only checks references.
* @param {?} changes
* @return {?}
*/
function (changes) {
// check if entries in bound data property has changed
this.detectDataChange();
};
/**
* Fired during every change detection run to detect and act upon changes that Angular can't or won't detect on its own.
*/
/**
* Fired during every change detection run to detect and act upon changes that Angular can't or won't detect on its own.
* @return {?}
*/
PieChartComponent.prototype.ngDoCheck = /**
* Fired during every change detection run to detect and act upon changes that Angular can't or won't detect on its own.
* @return {?}
*/
function () {
// check if entries in bound data property has changed
this.detectDataChange();
};
/**
* Checks whether the data property has changed. This function also check whether only an item property has
* changed. In case of change the chart will be rendered.
*/
/**
* Checks whether the data property has changed. This function also check whether only an item property has
* changed. In case of change the chart will be rendered.
* @return {?}
*/
PieChartComponent.prototype.detectDataChange = /**
* Checks whether the data property has changed. This function also check whether only an item property has
* changed. In case of change the chart will be rendered.
* @return {?}
*/
function () {
// fast check: if items were added or removed
var /** @type {?} */ dataChanged = (this.data.length !== this.lastData.length);
// detail check:
if (dataChanged === false) {
// loop all items
for (var /** @type {?} */ idx = 0; idx < this.data.length; ++idx) {
var /** @type {?} */ a = this.data[idx];
var /** @type {?} */ b = this.lastData[idx];
// check internal item properties
dataChanged = dataChanged || (a.caption !== b.caption || a.color !== b.color || a.value !== b.value);
// for optimization, stop if change detected
if (dataChanged)
break;
}
}
// if change detected
if (dataChanged) {
// render chart
this.render();
// copy current data to identify changes
this.lastData = this.deepCopy(this.data);
}
};
/**
* Generates a random color for a chart item.
*/
/**
* Generates a random color for a chart item.
* @param {?} value
* @return {?}
*/
PieChartComponent.prototype.generateRandomColor = /**
* Generates a random color for a chart item.
* @param {?} value
* @return {?}
*/
function (value) {
var /** @type {?} */ hue2rgb = function (p, q, t) {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1 / 6)
return p + (q - p) * 6 * t;
if (t < 1 / 2)
return q;
if (t < 2 / 3)
return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
// make sure, generated color does not exists yet in data array
var /** @type {?} */ color;
var /** @type {?} */ uniqueColorGenerated = false;
while (uniqueColorGenerated === false) {
var /** @type {?} */ h = (Math.random() + 0.618033988749895) % 1;
var /** @type {?} */ s = .5;
var /** @type {?} */ l = .6;
var /** @type {?} */ q = l + s - l * s;
var /** @type {?} */ p = 2 * l - q;
var /** @type {?} */ r = hue2rgb(p, q, h + 1 / 3);
var /** @type {?} */ g = hue2rgb(p, q, h);
var /** @type {?} */ b = hue2rgb(p, q, h - 1 / 3);
color = '#'
+ Math.round(r * 255).toString(16)
+ Math.round(g * 255).toString(16)
+ Math.round(b * 255).toString(16);
uniqueColorGenerated = (this.data.map(function (d) { return d.color; }).filter(function (d) { return d === color; }).length === 0);
}
return color;
};
/**
* generates a pie chart item definition
* @param item
* @param index
* @param value
* @param startAngle
* @param endAngle
*/
/**
* generates a pie chart item definition
* @param {?} item
* @param {?} index
* @param {?} value
* @param {?} startAngle
* @param {?} endAngle
* @return {?}
*/
PieChartComponent.prototype.generatePieArcData = /**
* generates a pie chart item definition
* @param {?} item
* @param {?} index
* @param {?} value
* @param {?} startAngle
* @param {?} endAngle
* @return {?}
*/
function (item, index, value, startAngle, endAngle) {
// generate definition
var /** @type {?} */ result = {
data: item,
index: index,
value: value,
startAngle: startAngle,
endAngle: endAngle,
padAngle: 0,
innerRadius: this.radius - 40,
outerRadius: this.radius
};
// generate svg path d-attribute from definition
((result.data)).path = this.pathGenerator(result);
// return definition
return result;
};
/**
* Checks whether items were deleted and initiate delete transition for these items.
*/
/**
* Checks whether items were deleted and initiate delete transition for these items.
* @return {?}
*/
PieChartComponent.prototype.detectDeletedEntries = /**
* Checks whether items were deleted and initiate delete transition for these items.
* @return {?}
*/
function () {
var _this = this;
// loop current state entries
this.curData.forEach(function (curItem, idx) {
// only check if current entry is not marked as deleted
if (curItem.data.deleted !== true) {
// check if entry not exists anymore
var /** @type {?} */ isDeleted = (_this.data.filter(function (item) { return item.caption === curItem.data.caption; }).length === 0);
// if entry is deleted
if (isDeleted) {
// mark entry in current state as deleted
// mark entry in current state as deleted
_this.curData[idx].data.deleted = true;
// mark entry in end state as deleted and set value to 0 for transtion
// mark entry in end state as deleted and set value to 0 for transtion
_this.endData[idx].data.deleted = true;
_this.endData[idx].value = 0;
}
}
});
};
/**
* Checks whether items were inserted and initiate insert transition for these items.
*/
/**
* Checks whether items were inserted and initiate insert transition for these items.
* @return {?}
*/
PieChartComponent.prototype.detectInsertedEntries = /**
* Checks whether items were inserted and initiate insert transition for these items.
* @return {?}
*/
function () {
var _this = this;
// loop given data array
this.data.forEach(function (item, idx) {
// check if entry is new
var /** @type {?} */ isInserted = (_this.curData.filter(function (curItem) { return curItem.data.deleted !== true && curItem.data.caption === item.caption; }).length === 0);
// if entry is new
if (isInserted) {
{
var /** @type {?} */ d = _this.generatePieArcData(_this.deepCopy(item), idx, 0, -1, -1);
_this.curData.splice(idx, 0, d);
}
{
var /** @type {?} */ d = _this.generatePieArcData(_this.deepCopy(item), idx, item.value, -1, -1);
_this.endData.splice(idx, 0, d);
}
}
});
};
/**
* Checks whether items were moved and initiate transition for these items.
*/
/**
* Checks whether items were moved and initiate transition for these items.
* @return {?}
*/
PieChartComponent.prototype.detectMovedEntries = /**
* Checks whether items were moved and initiate transition for these items.
* @return {?}
*/
function () {
// separate index in current state array
var /** @type {?} */ curIndex = 0;
// loop data array
for (var /** @type {?} */ index = 0; index < this.data.length; ++index) {
// find next index in current state array, skip items marked as deleted
while (this.curData[curIndex].data.deleted)
++curIndex;
// check if item is moved by comparing captions
if (this.data[index].caption !== this.curData[curIndex].data.caption) {
{
// mark item in current state array as deleted
this.curData[curIndex].data.deleted = true;
// mark item in end state array as deleted and set value to 0 for transition
this.endData[curIndex].data.deleted = true;
this.endData[curIndex].value = 0;
}
{
var /** @type {?} */ item = this.deepCopy(this.data[index]);
var /** @type {?} */ d = this.generatePieArcData(item, -1, 0, -1, -1);
this.curData.splice(curIndex, 0, d);
}
{
var /** @type {?} */ item = this.deepCopy(this.data[index]);
var /** @type {?} */ d = this.generatePieArcData(item, -1, item.value, -1, -1);
this.endData.splice(curIndex, 0, d);
}
// because of inserting item to the array's, increment index twice
++curIndex;
}
++curIndex;
}
};
/**
* Synchronize state arrays (curData / endData) with given items (data).
*/
/**
* Synchronize state arrays (curData / endData) with given items (data).
* @return {?}
*/
PieChartComponent.prototype.syncItems = /**
* Synchronize state arrays (curData / endData) with given items (data).
* @return {?}
*/
function () {
var _this = this;
// sync values and colors
this.data.forEach(function (item, index) {
// find item index in state array's
var /** @type {?} */ curIndex = 0;
for (var /** @type {?} */ i = 0; i < _this.curData.length; ++i) {
if (!_this.curData[i].data.deleted && _this.curData[i].data.caption === item.caption) {
curIndex = i;
break;
}
}
// update value in state entries
// update value in state entries
_this.curData[curIndex].data.value = item.value;
_this.endData[curIndex].data.value = item.value;
// update value in end state entry for transition
// update value in end state entry for transition
_this.endData[curIndex].value = item.value;
// update color in end state entry for transition
// update color in end state entry for transition
_this.endData[curIndex].data.color = item.color;
});
};
/**
* will be triggerd to animate chart changes.
* important! this method musst be called within a setTimeout function because of angulars
* rendering cycle.
*/
/**
* will be triggerd to animate chart changes.
* important! this method musst be called within a setTimeout function because of angulars
* rendering cycle.
* @return {?}
*/
PieChartComponent.prototype.animateChanges = /**
* will be triggerd to animate chart changes.
* important! this method musst be called within a setTimeout function because of angulars
* rendering cycle.
* @return {?}
*/
function () {
var _this = this;
// get svg element reference
var /** @type {?} */ svg = ((this.element.nativeElement.querySelector('svg')));
// reference all path elements in svg element
var /** @type {?} */ paths = d3.select(svg).selectAll('path');
// define interruption function to stop running animations
this.interrupt = function () {
// call paths interrupt method
paths.interrupt();
// delete interupt definition
delete _this.interrupt;
};
// start path animation
paths
.transition()
.duration(this.duration)
.attrTween('pie-tween-dummy', function (arg0, idx, nodeList) {
// create interpolation functions to calculate step values
var /** @type {?} */ iValue = d3.interpolate(_this.curData[idx].value, _this.endData[idx].value);
var /** @type {?} */ iStartAngle = d3.interpolate(_this.curData[idx].startAngle, _this.endData[idx].startAngle);
var /** @type {?} */ iEndAngle = d3.interpolate(_this.curData[idx].endAngle, _this.endData[idx].endAngle);
var /** @type {?} */ iColor = d3.interpolate(_this.curData[idx].data.color, _this.endData[idx].data.color);
// return factory function for animation steps
return function (t) {
// interpolate values by given transition value
// interpolate values by given transition value
_this.curData[idx].value = iValue(t);
_this.curData[idx].startAngle = iStartAngle(t);
_this.curData[idx].endAngle = iEndAngle(t);
_this.curData[idx].data.color = iColor(t);
// generate new path
// generate new path
_this.curData[idx].data.path = _this.pathGenerator(_this.curData[idx]);
// return empty string. This is only necessary for typescript compiler. Nothing should be changed here.
return '';
};
})
.on('end', function (arg0, idx, nodeList) {
// when transition is complete for the last item
if (idx === nodeList.length - 1) {
// remove as deleted marked entries
// remove as deleted marked entries
_this.cleanStateItems();
// Delete interupt definition, because everything has finished and nothing can be interrupted.
delete _this.interrupt;
}
});
};
/**
* Must be called after transition ends to remove entries in curData and endData which are marked
* as deleted.
*/
/**
* Must be called after transition ends to remove entries in curData and endData which are marked
* as deleted.
* @return {?}
*/
PieChartComponent.prototype.cleanStateItems = /**
* Must be called after transition ends to remove entries in curData and endData which are marked
* as deleted.
* @return {?}
*/
function () {
// clean current state array
for (var /** @type {?} */ i = this.curData.length - 1; i >= 0; --i) {
if (this.curData[i].data.deleted === true) {
this.curData.splice(i, 1);
}
}
// clean end state array
for (var /** @type {?} */ i = this.endData.length - 1; i >= 0; --i) {
if (this.endData[i].data.deleted === true) {
this.endData.splice(i, 1);
}
}
};
/**
* Checks whether all items have assigned color values and if necessary completes colors in given data array.
*/
/**
* Checks whether all items have assigned color values and if necessary completes colors in given data array.
* @return {?}
*/
PieChartComponent.prototype.initColors = /**
* Checks whether all items have assigned color values and if necessary completes colors in given data array.
* @return {?}
*/
function () {
var _this = this;
// loop all entries
this.data.forEach(function (item) {
// if no color is assigned
if (!item.color) {
// generate random color for item
item.color = _this.generateRandomColor(item.value);
}
});
};
/**
* Returns maximal angle of current state items.
*/
/**
* Returns maximal angle of current state items.
* @return {?}
*/
PieChartComponent.prototype.getMaxAngle = /**
* Returns maximal angle of current state items.
* @return {?}
*/
function () {
var /** @type {?} */ maxAngle = 0;
this.curData.forEach(function (curItem) {
if (curItem.endAngle > maxAngle) {
maxAngle = curItem.endAngle;
}
});
return maxAngle;
};
/**
* Calculates angles for current and end state items.
* @param maxAngle last maximal angle in current state to avoid "jumping" transitions
*/
/**
* Calculates angles for current and end state items.
* @param {?} maxAngle last maximal angle in current state to avoid "jumping" transitions
* @return {?}
*/
PieChartComponent.prototype.calculateAngles = /**
* Calculates angles for current and end state items.
* @param {?} maxAngle last maximal angle in current state to avoid "jumping" transitions
* @return {?}
*/
function (maxAngle) {
var _this = this;
{
// calculate sum of values
var /** @type {?} */ total_1 = this.curData.reduce(function (p, c) { return p + c.value; }, 0);
// loop items and calculate start and end angles, initialize rendering
var /** @type {?} */ lastAngle_1 = 0;
this.curData.forEach(function (item, idx) {
// calculate angles by last used maximal angle. without data (total=0) simulate 0 values, so draw items in clockwise direction.
var /** @type {?} */ nextAngle = lastAngle_1 + ((maxAngle) / ((total_1 === 0) ? 1 : total_1)) * item.value;
item.startAngle = lastAngle_1;
item.endAngle = nextAngle;
item.index = idx;
item.data.path = _this.pathGenerator(item);
lastAngle_1 = nextAngle;
});
}
{
// calculate sum of values
var /** @type {?} */ total_2 = this.endData.reduce(function (p, c) { return p + c.value; }, 0);
// loop items and calculate start and end angles, initialize rendering
var /** @type {?} */ lastAngle_2 = 0;
this.endData.forEach(function (item, idx) {
// calculate angles with circumference. without data (total=0) simulate 0 values, so draw items in anti-clockwise direction.
var /** @type {?} */ nextAngle = lastAngle_2 + ((2 * Math.PI) / ((total_2 === 0) ? 1 : total_2)) * item.value;
item.startAngle = lastAngle_2;
item.endAngle = nextAngle;
item.index = idx;
item.data.path = _this.pathGenerator(item);
lastAngle_2 = nextAngle;
});
}
};
/**
* fired when mouse enters a pie chart path element and shows tooltip
* @param {?} event
* @return {?}
*/
PieChartComponent.prototype.overPath = /**
* fired when mouse enters a pie chart path element and shows tooltip
* @param {?} event
* @return {?}
*/
function (event) {
// get tooltip-text of path element
var /** @type {?} */ txt = ((event.target)).getAttribute('tooltip');
// show tooltip and assign text
d3.select(this.tooltip)
.html(txt)
.style('display', 'block')
.transition()
.duration(250)
.style('opacity', 1);
// get index
var /** @type {?} */ idx = parseInt(((event.target)).getAttribute('idx'), 10);
// get caption of element
var /** @type {?} */ caption = this.curData[idx].data.caption;
// get original data by caption
var /** @type {?} */ item = this.data.filter(function (d) { return d.caption === caption; })[0];
// if data found then emit chart click event
if (item) {
this.chartHover.emit(item);
}
};
/**
* fired when mouse moves over a pie chart path element and adjusts tooltip
* @param {?} event
* @return {?}
*/
PieChartComponent.prototype.movePath = /**
* fired when mouse moves over a pie chart path element and adjusts tooltip
* @param {?} event
* @return {?}
*/
function (event) {
// aggregate scroll positions, because event.page* properties are relative to top left corner of document
var /** @type {?} */ offsetX = 0;
var /** @type {?} */ offsetY = 0;
var /** @type {?} */ element = ((this.tooltip.parentElement));
while (element) {
offsetX += element.scrollLeft;
offsetY += element.scrollTop;
element = element.parentElement;
}
// adjust tooltip
d3.select(this.tooltip)
.style('top', (event.pageY - offsetY + 10) + 'px')
.style('left', (event.pageX - offsetX + 10) + 'px');
};
/**
* fired when mouse leaves a pie chart path element and hides tooltip
* @param {?} event
* @return {?}
*/
PieChartComponent.prototype.outPath = /**
* fired when mouse leaves a pie chart path element and hides tooltip
* @param {?} event
* @return {?}
*/
function (event) {
var _this = this;
// hide tooltip
d3.select(this.tooltip)
.transition()
.duration(250)
.style('opacity', 0)
.on('end', function () {
d3.select(_this.tooltip).style('display', 'none');
});
};
/**
* fired when user clicks on a pie chart path element
* @param {?} event
* @return {?}
*/
PieChartComponent.prototype.clickPath = /**
* fired when user clicks on a pie chart path element
* @param {?} event
* @return {?}
*/
function (event) {
// get index
var /** @type {?} */ idx = parseInt(((event.target)).getAttribute('idx'), 10);
// get caption of element
var /** @type {?} */ caption = this.curData[idx].data.caption;
// get original data by caption
var /** @type {?} */ item = this.data.filter(function (d) { return d.caption === caption; })[0];
// if data found then emit chart click event
if (item) {
this.chartClick.emit(item);
}
};
/**
* main rendering function
*/
/**
* main rendering function
* @return {?}
*/
PieChartComponent.prototype.render = /**
* main rendering function
* @return {?}
*/
function () {
var _this = this;
// interrupt possible running animations
if (this.interrupt)
this.interrupt();
// initialize chart colors
this.initColors();
// calculate radius
this.radius = Math.min(this.width, this.height) / 2;
// calculate middle of chart
this.center = "translate(" + this.width / 2 + ", " + this.height / 2 + ")";
// create path generator
this.pathGenerator = d3.arc().outerRadius(this.radius - this.outerSpacing).innerRadius(this.innerSpacing);
// get current maximal angle, necessary to avoid "jumping" transitions
var /** @type {?} */ maxAngle = this.getMaxAngle();
// check data array for deleted entries and assign transition configuration
this.detectDeletedEntries();
// check data array for inserted entries and assign transition configuration
this.detectInsertedEntries();
// check data array for moved entries and assign transition configuration
this.detectMovedEntries();
// synchronize data entries with current and end state entries
this.syncItems();
// calculate angles for current and end state entries
this.calculateAngles(maxAngle);
// important! use setTimeout because angular first must exec change detection
setTimeout(function () {
// start change animations
// start change animations
_this.animateChanges();
}, 0);
};
PieChartComponent.decorators = [
{ type: i0.Component, args: [{
selector: 'oc-pie-chart',
template: "<div class=\"pie-chart-tooltip\"></div>\n<svg [attr.width]=\"width\" [attr.height]=\"height\">\n <g [attr.transform]=\"center\">\n <path *ngFor=\"let d of curData; let idx = index;\" [attr.idx]=\"idx\" \n [attr.fill]=\"d.data.color\" [attr.d]=\"d.data.path\" [attr.tooltip]=\"d.data.caption\"\n (mouseover)=\"overPath($event)\" (mousemove)=\"movePath($event);\" (mouseout)=\"outPath($event)\" (click)=\"clickPath($event)\" />\n </g>\n</svg>",
styles: ["div.pie-chart-tooltip{position:fixed;display:none;opacity:0;font:12px sans-serif;color:#fff;background-color:rgba(35,47,52,.8);padding:5px}path{opacity:.7;stroke:#fff;stroke-width:2px}path:hover{opacity:1;stroke:#e3e3e3}"]
},] },
];
/** @nocollapse */
PieChartComponent.ctorParameters = function () {
return [
{ type: i0.ElementRef }
];
};
PieChartComponent.propDecorators = {
data: [{ type: i0.Input }],
width: [{ type: i0.Input }],
height: [{ type: i0.Input }],
duration: [{ type: i0.Input }],
innerSpacing: [{ type: i0.Input }],
outerSpacing: [{ type: i0.Input }],
chartClick: [{ type: i0.Output }],
chartHover: [{ type: i0.Output }]
};
return PieChartComponent;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var PieChartModule = (function () {
function PieChartModule() {
}
PieChartModule.decorators = [
{ type: i0.NgModule, args: [{
imports: [platformBrowser.BrowserModule],
declarations: [PieChartComponent],
exports: [PieChartComponent]
},] },
];
return PieChartModule;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
exports.PieChartService = PieChartService;
exports.PieChartComponent = PieChartComponent;
exports.PieChartModule = PieChartModule;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BpdHpjb25zdWx0aW5nLXBpZS1jaGFydC51bWQuanMubWFwIiwic291cmNlcyI6WyJuZzovL0BvcGl0emNvbnN1bHRpbmcvcGllLWNoYXJ0L2xpYi9waWUtY2hhcnQuc2VydmljZS50cyIsIm5nOi8vQG9waXR6Y29uc3VsdGluZy9waWUtY2hhcnQvbGliL3BpZS1jaGFydC5jb21wb25lbnQudHMiLCJuZzovL0BvcGl0emNvbnN1bHRpbmcvcGllLWNoYXJ0L2xpYi9waWUtY2hhcnQubW9kdWxlLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuXHJcbkBJbmplY3RhYmxlKHtcclxuICBwcm92aWRlZEluOiAncm9vdCdcclxufSlcclxuZXhwb3J0IGNsYXNzIFBpZUNoYXJ0U2VydmljZSB7XHJcblxyXG4gIGNvbnN0cnVjdG9yKCkgeyB9XHJcbn1cclxuIiwiaW1wb3J0IHsgQ29tcG9uZW50LCBJbnB1dCwgT25DaGFuZ2VzLCBEb0NoZWNrLCBFbGVtZW50UmVmLCBTaW1wbGVDaGFuZ2VzLCBPbkluaXQsIE91dHB1dCwgRXZlbnRFbWl0dGVyIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcblxyXG5pbXBvcnQgKiBhcyBkMyBmcm9tICdkMyc7XHJcblxyXG4vKiogY2hhcnQgaXRlbSBwcm9wZXJ0aWVzICovXHJcbmV4cG9ydCBpbnRlcmZhY2UgUGllQ2hhcnREYXRhIHtcclxuICAvKiogdmFsdWUgb2YgaXRlbSAqL1xyXG4gIHZhbHVlOiBudW1iZXI7XHJcbiAgLyoqIGNhcHRpb24gb2YgaXRlbSAobXVzdCBiZSB1bmlxdWUpICovXHJcbiAgY2FwdGlvbjogc3RyaW5nO1xyXG4gIC8qKiBvcHRpb25hbCBjb2xvciBvZiBpdGVtIChpZiBub3Qgc2V0LCBnZW5lcmF0ZWQgYXV0b21hdGljYWxseSkgKi9cclxuICBjb2xvcj86IHN0cmluZztcclxufVxyXG5cclxuLyoqIGludGVybmFsIGNoYXJ0IGl0ZW0gcHJvcGVydGllcyAqL1xyXG5leHBvcnQgaW50ZXJmYWNlIEludGVybmFsUGllQ2hhcnREYXRhIGV4dGVuZHMgUGllQ2hhcnREYXRhIHtcclxuICAvKiogc3ZnIHBhdGggZm9yIGl0ZW0gKi9cclxuICBwYXRoPzogc3RyaW5nO1xyXG4gIC8qKiBkZWxldGUgZmxhZyBmb3IgcmVtb3ZpbmcgYWZ0ZXIgdHJhbnNpdGlvbiAqL1xyXG4gIGRlbGV0ZWQ/OiBib29sZWFuO1xyXG59XHJcblxyXG4vKiogaW50ZXJuYWwgdHlwZSBmb3Igb3B0aW1pemF0aW9uICovXHJcbmV4cG9ydCB0eXBlIFBpZUFyY0RhdGEgPSBkMy5QaWVBcmNEYXR1bTxJbnRlcm5hbFBpZUNoYXJ0RGF0YT4gJiBkMy5EZWZhdWx0QXJjT2JqZWN0O1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICdvYy1waWUtY2hhcnQnLFxyXG4gIHRlbXBsYXRlOiBgPGRpdiBjbGFzcz1cInBpZS1jaGFydC10b29sdGlwXCI+PC9kaXY+XHJcbjxzdmcgW2F0dHIud2lkdGhdPVwid2lkdGhcIiBbYXR0ci5oZWlnaHRdPVwiaGVpZ2h0XCI+XHJcbiAgICA8ZyBbYXR0ci50cmFuc2Zvcm1dPVwiY2VudGVyXCI+XHJcbiAgICAgICAgPHBhdGggKm5nRm9yPVwibGV0IGQgb2YgY3VyRGF0YTsgbGV0IGlkeCA9IGluZGV4O1wiIFthdHRyLmlkeF09XCJpZHhcIiBcclxuICAgICAgICAgICAgW2F0dHIuZmlsbF09XCJkLmRhdGEuY29sb3JcIiBbYXR0ci5kXT1cImQuZGF0YS5wYXRoXCIgW2F0dHIudG9vbHRpcF09XCJkLmRhdGEuY2FwdGlvblwiXHJcbiAgICAgICAgICAgIChtb3VzZW92ZXIpPVwib3ZlclBhdGgoJGV2ZW50KVwiIChtb3VzZW1vdmUpPVwibW92ZVBhdGgoJGV2ZW50KTtcIiAobW91c2VvdXQpPVwib3V0UGF0aCgkZXZlbnQpXCIgKGNsaWNrKT1cImNsaWNrUGF0aCgkZXZlbnQpXCIgLz5cclxuICAgIDwvZz5cclxuPC9zdmc+YCxcclxuICBzdHlsZXM6IFtgZGl2LnBpZS1jaGFydC10b29sdGlwe3Bvc2l0aW9uOmZpeGVkO2Rpc3BsYXk6bm9uZTtvcGFjaXR5OjA7Zm9udDoxMnB4IHNhbnMtc2VyaWY7Y29sb3I6I2ZmZjtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMzUsNDcsNTIsLjgpO3BhZGRpbmc6NXB4fXBhdGh7b3BhY2l0eTouNztzdHJva2U6I2ZmZjtzdHJva2Utd2lkdGg6MnB4fXBhdGg6aG92ZXJ7b3BhY2l0eToxO3N0cm9rZTojZTNlM2UzfWBdXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBQaWVDaGFydENvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzLCBEb0NoZWNrIHtcclxuICAvKiogY2hhcnQgZGF0YSwgd2hpY2ggc2hvdWxkIGJlIGRpc3BsYXllZCAqL1xyXG4gIEBJbnB1dCgpIGRhdGE6IEFycmF5PFBpZUNoYXJ0RGF0YT4gPSBbXTtcclxuICAvKiogY2hhcnQgd2lkdGggaW4gcGl4ZWwgKi9cclxuICBASW5wdXQoKSB3aWR0aCA9IDI1MDtcclxuICAvKiogY2hhcnQgaGVpZ2h0IGluIHBpeGVsICovXHJcbiAgQElucHV0KCkgaGVpZ2h0ID0gMjUwO1xyXG4gIC8qKiBkdXJhdGlvbiBvZiBhbmltYXRpb24gdHJhbnNpdGlvbiAqL1xyXG4gIEBJbnB1dCgpIGR1cmF0aW9uID0gMTAwMDtcclxuICAvKiogaW5uZXIgc3BhY2luZyBpbiBwaXhlbCwgaWYgZ3JlYXRlciB0aGFuIDAgaXQgZGVmaW5lcyB0aGUgcmFkaXVzIG9mIHRoZSBlbXB0eSBjaXJjbGUgaW4gdGhlIG1pZGRsZSAqL1xyXG4gIEBJbnB1dCgpIGlubmVyU3BhY2luZyA9IDA7XHJcbiAgLyoqIG91dGVyIHNwYWNpbmcgaW4gcGl4ZWwgKi9cclxuICBASW5wdXQoKSBvdXRlclNwYWNpbmcgPSAxO1xyXG4gIC8qKiBmaXJlZCB3aGVuIHVzZXIgY2xpY2tzIG9uIGEgY2hhcnQgZW50cnkgKi9cclxuICBAT3V0cHV0KCkgY2hhcnRDbGljazogRXZlbnRFbWl0dGVyPFBpZUNoYXJ0RGF0YT4gPSBuZXcgRXZlbnRFbWl0dGVyKCk7XHJcbiAgLyoqIGZpcmVkIHdoZW4gdXNlciBob3ZlcnMgYSBjaGFydCBlbnRyeSAqL1xyXG4gIEBPdXRwdXQoKSBjaGFydEhvdmVyOiBFdmVudEVtaXR0ZXI8UGllQ2hhcnREYXRhPiA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcclxuXHJcbiAgLyoqIHBpZSBjaGFydCByYWRpdXMgaW4gcGl4ZWwgKi9cclxuICBwdWJsaWMgcmFkaXVzOiBudW1iZXI7XHJcbiAgLyoqIHRyYW5zZm9ybS1hdHRyaWJ1dGUgdG8gY2VudGVyIGNoYXJ0IHZlcnRpY2FsIGFuZCBob3Jpem9udGFsICovXHJcbiAgcHVibGljIGNlbnRlcjogc3RyaW5nO1xyXG4gIC8qKiBjdXJyZW50IGNoYXJ0IGRhdGEgd2l0aCBhbmdsZSBhbmQgcGF0aCBkZWZpbml0aW9ucywgaXQgd2lsbCBiZSBjb25zaXN0ZW50IHRvIHRoZSByZXByZXNlbnRhdGlvbiAqL1xyXG4gIHB1YmxpYyBjdXJEYXRhOiBQaWVBcmNEYXRhW10gPSBbXTtcclxuICAvKiogZW5kIGNoYXJ0IGRhdGEgd2l0aCBhbmdsZSBhbmQgcGF0aCBkZWZpbml0aW9ucywgaXQgd2lsbCByZXByZXNlbnRhdGUgdGhlIGVuZCBzdGF0ZSBhbmQgdXNlZCBvbmx5IGZvciBpbnRlcnBvbGF0aW9uICovXHJcbiAgcHJpdmF0ZSBlbmREYXRhOiBQaWVBcmNEYXRhW10gPSBbXTtcclxuICAvKiogcGF0aCBnZW5lcmF0b3IgZnVuY3Rpb24gKGludGVybmFsIHVzZSBvbmx5KSAqL1xyXG4gIHByb3RlY3RlZCBwYXRoR2VuZXJhdG9yOiBkMy5BcmM8YW55LCBkMy5EZWZhdWx0QXJjT2JqZWN0PjtcclxuICAvKiogY29weSBvZiBsYXN0IHByb2Nlc3NlZCBkYXRhLCB1c2VkIHRvIGlkZW50aWZ5IGNoYW5nZXMgaW4gbmdEb0NoZWNrIHRoYXQgQW5ndWxhciBvdmVybG9va2VkICovXHJcbiAgcHJpdmF0ZSBsYXN0RGF0YTogQXJyYXk8UGllQ2hhcnREYXRhPiA9IFtdO1xyXG5cclxuICAvKipcclxuICAgKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIGFuIHZhcmlhYmxlLiBEbyBub3QgdXNlIHRoaXMgZnVuY3Rpb24gd2l0aCByZWN1cnNpdmUgb2JqZWN0cyBvclxyXG4gICAqIGJyb3dzZXIgb2JqZWN0cyBsaWtlIHdpbmRvdyBvciBkb2N1bWVudC5cclxuICAgKiBUb0RvOiBzaG91bGQgYmUgb3V0c291cmNlZC5cclxuICAgKiBAcGFyYW0gdiBcclxuICAgKi9cclxuICBwcm90ZWN0ZWQgZGVlcENvcHk8VD4odjogVCk6IFQge1xyXG4gICAgcmV0dXJuIEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkodikpO1xyXG4gIH07XHJcblxyXG4gIC8qKlxyXG4gICAqIGNvbnN0cnVjdG9yXHJcbiAgICogQHBhcmFtIGVsZW1lbnQgXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGVsZW1lbnQ6IEVsZW1lbnRSZWZcclxuICApIHt9O1xyXG5cclxuICBuZ09uSW5pdCgpIHtcclxuICAgIHRoaXMudG9vbHRpcCA9IHRoaXMuZWxlbWVudC5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJ2Rpdi5waWUtY2hhcnQtdG9vbHRpcCcpIGFzIEhUTUxEaXZFbGVtZW50O1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogRmlyZWQgd2hlbiBBbmd1bGFyIChyZS0pc2V0cyBkYXRhLWJvdW5kIHByb3BlcnRpZXMuIFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgZmlyZSB3aGVuIGNoYW5nZWQgZGF0YSBpbiBib3VuZCBvYmplY3RzIG9yIGFycmF5cy5cclxuICAgKiBBbmd1bGFyIG9ubHkgY2hlY2tzIHJlZmVyZW5jZXMuXHJcbiAgICogQHBhcmFtIGNoYW5nZXMgXHJcbiAgICovXHJcbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xyXG4gICAgLy8gY2hlY2sgaWYgZW50cmllcyBpbiBib3VuZCBkYXRhIHByb3BlcnR5IGhhcyBjaGFuZ2VkXHJcbiAgICB0aGlzLmRldGVjdERhdGFDaGFuZ2UoKTtcclxuICB9O1xyXG5cclxuICAvKipcclxuICAgKiBGaXJlZCBkdXJpbmcgZXZlcnkgY2hhbmdlIGRldGVjdGlvbiBydW4gdG8gZGV0ZWN0IGFuZCBhY3QgdXBvbiBjaGFuZ2VzIHRoYXQgQW5ndWxhciBjYW4ndCBvciB3b24ndCBkZXRlY3Qgb24gaXRzIG93bi5cclxuICAgKi9cclxuICBuZ0RvQ2hlY2soKSB7XHJcbiAgICAvLyBjaGVjayBpZiBlbnRyaWVzIGluIGJvdW5kIGRhdGEgcHJvcGVydHkgaGFzIGNoYW5nZWRcclxuICAgIHRoaXMuZGV0ZWN0RGF0YUNoYW5nZSgpO1xyXG4gIH07XHJcblxyXG4gIC8qKlxyXG4gICAqIENoZWNrcyB3aGV0aGVyIHRoZSBkYXRhIHByb3BlcnR5IGhhcyBjaGFuZ2VkLiBUaGlzIGZ1bmN0aW9uIGFsc28gY2hlY2sgd2hldGhlciBvbmx5IGFuIGl0ZW0gcHJvcGVydHkgaGFzXHJcbiAgICogY2hhbmdlZC4gSW4gY2FzZSBvZiBjaGFuZ2UgdGhlIGNoYXJ0IHdpbGwgYmUgcmVuZGVyZWQuXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGRldGVjdERhdGFDaGFuZ2UoKSB7XHJcbiAgICAvLyBmYXN0IGNoZWNrOiBpZiBpdGVtcyB3ZXJlIGFkZGVkIG9yIHJlbW92ZWRcclxuICAgIGxldCBkYXRhQ2hhbmdlZCA9ICh0aGlzLmRhdGEubGVuZ3RoICE9PSB0aGlzLmxhc3REYXRhLmxlbmd0aCk7XHJcbiAgICAvLyBkZXRhaWwgY2hlY2s6XHJcbiAgICBpZihkYXRhQ2hhbmdlZCA9PT0gZmFsc2Upe1xyXG4gICAgICAvLyBsb29wIGFsbCBpdGVtc1xyXG4gICAgICBmb3IobGV0IGlkeD0wOyBpZHg8dGhpcy5kYXRhLmxlbmd0aDsgKytpZHgpe1xyXG4gICAgICAgIGNvbnN0IGEgPSB0aGlzLmRhdGFbaWR4XTtcclxuICAgICAgICBjb25zdCBiID0gdGhpcy5sYXN0RGF0YVtpZHhdO1xyXG4gICAgICAgIC8vIGNoZWNrIGludGVybmFsIGl0ZW0gcHJvcGVydGllc1xyXG4gICAgICAgIGRhdGFDaGFuZ2VkID0gZGF0YUNoYW5nZWQgfHwgKGEuY2FwdGlvbiAhPT0gYi5jYXB0aW9uIHx8IGEuY29sb3IgIT09IGIuY29sb3IgfHwgYS52YWx1ZSAhPT0gYi52YWx1ZSk7XHJcbiAgICAgICAgLy8gZm9yIG9wdGltaXphdGlvbiwgc3RvcCBpZiBjaGFuZ2UgZGV0ZWN0ZWRcclxuICAgICAgICBpZihkYXRhQ2hhbmdlZCkgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIC8vIGlmIGNoYW5nZSBkZXRlY3RlZFxyXG4gICAgaWYoZGF0YUNoYW5nZWQpe1xyXG4gICAgICAvLyByZW5kZXIgY2hhcnRcclxuICAgICAgdGhpcy5yZW5kZXIoKTtcclxuICAgICAgLy8gY29weSBjdXJyZW50IGRhdGEgdG8gaWRlbnRpZnkgY2hhbmdlc1xyXG4gICAgICB0aGlzLmxhc3REYXRhID0gdGhpcy5kZWVwQ29weSh0aGlzLmRhdGEpO1xyXG4gICAgfVxyXG4gIH07XHJcblxyXG4gIC8qKlxyXG4gICAqIEdlbmVyYXRlcyBhIHJhbmRvbSBjb2xvciBmb3IgYSBjaGFydCBpdGVtLlxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCBnZW5lcmF0ZVJhbmRvbUNvbG9yKHZhbHVlOiBudW1iZXIpOiBzdHJpbmcge1xyXG4gICAgY29uc3QgaHVlMnJnYiA9IChwOiBudW1iZXIsIHE6IG51bWJlciwgdDogbnVtYmVyKSA9PiB7XHJcbiAgICAgIGlmKHQgPCAwKSB0ICs9IDE7IFxyXG4gICAgICBpZih0ID4gMSkgdCAtPSAxOyBcclxuICAgICAgaWYodCA8IDEvNikgcmV0dXJuIHAgKyAocSAtIHApICogNiAqIHQ7XHJcbiAgICAgIGlmKHQgPCAxLzIpIHJldHVybiBxO1xyXG4gICAgICBpZih0IDwgMi8zKSByZXR1cm4gcCArIChxIC0gcCkgKiAoMi8zIC0gdCkgKiA2O1xyXG4gICAgICByZXR1cm4gcDtcclxuICAgIH07XHJcbiAgICAvLyBtYWtlIHN1cmUsIGdlbmVyYXRlZCBjb2xvciBkb2VzIG5vdCBleGlzdHMgeWV0IGluIGRhdGEgYXJyYXlcclxuICAgIGxldCBjb2xvcjtcclxuICAgIGxldCB1bmlxdWVDb2xvckdlbmVyYXRlZCA9IGZhbHNlO1xyXG4gICAgd2hpbGUodW5pcXVlQ29sb3JHZW5lcmF0ZWQgPT09IGZhbHNlKXtcclxuICAgICAgY29uc3QgaCA9IChNYXRoLnJhbmRvbSgpICsgMC42MTgwMzM5ODg3NDk4OTUpICUgMTtcclxuICAgICAgY29uc3QgcyA9IC41O1xyXG4gICAgICBjb25zdCBsID0gLjY7XHJcbiAgICAgIGxldCBxID0gbCArIHMgLSBsICogcztcclxuICAgICAgbGV0IHAgPSAyICogbCAtIHE7XHJcbiAgICAgIGNvbnN0IHIgPSBodWUycmdiKHAsIHEsIGggKyAxLzMpO1xyXG4gICAgICBjb25zdCBnID0gaHVlMnJnYihwLCBxLCBoKTtcclxuICAgICAgY29uc3QgYiA9IGh1ZTJyZ2IocCwgcSwgaCAtIDEvMyk7XHJcbiAgICAgIGNvbG9yID0gJyMnIFxyXG4gICAgICAgICsgTWF0aC5yb3VuZChyICogMjU1KS50b1N0cmluZygxNilcclxuICAgICAgICArIE1hdGgucm91bmQoZyAqIDI1NSkudG9TdHJpbmcoMTYpXHJcbiAgICAgICAgKyBNYXRoLnJvdW5kKGIgKiAyNTUpLnRvU3RyaW5nKDE2KTtcclxuICAgICAgdW5pcXVlQ29sb3JHZW5lcmF0ZWQgPSAodGhpcy5kYXRhLm1hcCggKGQpID0+IGQuY29sb3IpLmZpbHRlciggKGQpID0+IGQgPT09IGNvbG9yKS5sZW5ndGggPT09IDApO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGNvbG9yO1xyXG4gIH07XHJcblxyXG4gIC8qKlxyXG4gICAqIGdlbmVyYXRlcyBhIHBpZSBjaGFydCBpdGVtIGRlZmluaXRpb25cclxuICAgKiBAcGFyYW0gaXRlbSBcclxuICAgKiBAcGFyYW0gaW5kZXggXHJcbiAgICogQHBhcmFtIHZhbHVlIFxyXG4gICAqIEBwYXJhbSBzdGFydEFuZ2xlIFxyXG4gICAqIEBwYXJhbSBlbmRBbmdsZSBcclxuICAgKi9cclxuICBwcm90ZWN0ZWQgZ2VuZXJhdGVQaWVBcmNEYXRhKGl0ZW06IFBpZUNoYXJ0RGF0YSwgaW5kZXg6IG51bWJlciwgdmFsdWU6IG51bWJlciwgc3RhcnRBbmdsZTogbnVtYmVyLCBlbmRBbmdsZTogbnVtYmVyKTogUGllQXJjRGF0YSB7XHJcbiAgICAvLyBnZW5lcmF0ZSBkZWZpbml0aW9uXHJcbiAgICBjb25zdCByZXN1bHQgPSB7XHJcbiAgICAgIGRhdGE6IGl0ZW0sXHJcbiAgICAgIGluZGV4OiBpbmRleCxcclxuICAgICAgdmFsdWU6IHZhbHVlLFxyXG4gICAgICBzdGFydEFuZ2xlOiBzdGFydEFuZ2xlLFxyXG4gICAgICBlbmRBbmdsZTogZW5kQW5nbGUsXHJcbiAgICAgIHBhZEFuZ2xlOiAwLFxyXG4gICAgICBpbm5lclJhZGl1czogdGhpcy5yYWRpdXMgLSA0MCxcclxuICAgICAgb3V0ZXJSYWRpdXM6IHRoaXMucmFkaXVzXHJcbiAgICB9O1xyXG4gICAgLy8gZ2VuZXJhdGUgc3ZnIHBhdGggZC1hdHRyaWJ1dGUgZnJvbSBkZWZpbml0aW9uXHJcbiAgICAocmVzdWx0LmRhdGEgYXMgSW50ZXJuYWxQaWVDaGFydERhdGEpLnBhdGggPSB0aGlzLnBhdGhHZW5lcmF0b3IocmVzdWx0KTtcclxuICAgIC8vIHJldHVybiBkZWZpbml0aW9uXHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH07XHJcblxyXG4gIC8qKlxyXG4gICAqIENoZWNrcyB3aGV0aGVyIGl0ZW1zIHdlcmUgZGVsZXRlZCBhbmQgaW5pdGlhdGUgZGVsZXRlIHRyYW5zaXRpb24gZm9yIHRoZXNlIGl0ZW1zLlxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCBkZXRlY3REZWxldGVkRW50cmllcygpIHtcclxuICAgIC8vIGxvb3AgY3VycmVudCBzdGF0ZSBlbnRyaWVzXHJcbiAgICB0aGlzLmN1ckRhdGEuZm9yRWFjaCggKGN1ckl0ZW0sIGlkeCkgPT4ge1xyXG4gICAgICAvLyBvbmx5IGNoZWNrIGlmIGN1cnJlbnQgZW50cnkgaXMgbm90IG1hcmtlZCBhcyBkZWxldGVkXHJcbiAgICAgIGlmKGN1ckl0ZW0uZGF0YS5kZWxldGVkIT09dHJ1ZSl7XHJcbiAgICAgICAgLy8gY2hlY2sgaWYgZW50cnkgbm90IGV4aXN0cyBhbnltb3JlXHJcbiAgICAgICAgY29uc3QgaXNEZWxldGVkID0gKHRoaXMuZGF0YS5maWx0ZXIoIChpdGVtKSA9PiBpdGVtLmNhcHRpb24gPT09IGN1ckl0ZW0uZGF0YS5jYXB0aW9uKS5sZW5ndGggPT09IDApO1xyXG4gICAgICAgIC8vIGlmIGVudHJ5IGlzIGRlbGV0ZWRcclxuICAgICAgICBpZihpc0RlbGV0ZWQpe1xyXG4gICAgICAgICAgLy8gbWFyayBlbnRyeSBpbiBjdXJyZW50IHN0YXRlIGFzIGRlbGV0ZWRcclxuICAgICAgICAgIHRoaXMuY3VyRGF0YVtpZHhdLmRhdGEuZGVsZXRlZCA9IHRydWU7XHJcbiAgICAgICAgICAvLyBtYXJrIGVudHJ5IGluIGVuZCBzdGF0ZSBhcyBkZWxldGVkIGFuZCBzZXQgdmFsdWUgdG8gMCBmb3IgdHJhbnN0aW9uXHJcbiAgICAgICAgICB0aGlzLmVuZERhdGFbaWR4XS5kYXRhLmRlbGV0ZWQgPSB0cnVlO1xyXG4gICAgICAgICAgdGhpcy5lbmREYXRhW2lkeF0udmFsdWUgPSAwO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfTtcclxuXHJcbiAgLyoqXHJcbiAgICogQ2hlY2tzIHdoZXRoZXIgaXRlbXMgd2VyZSBpbnNlcnRlZCBhbmQgaW5pdGlhdGUgaW5zZXJ0IHRyYW5zaXRpb24gZm9yIHRoZXNlIGl0ZW1zLlxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCBkZXRlY3RJbnNlcnRlZEVudHJpZXMoKTogdm9pZCB7XHJcbiAgICAvLyBsb29wIGdpdmVuIGRhdGEgYXJyYXlcclxuICAgIHRoaXMuZGF0YS5mb3JFYWNoKCAoaXRlbSwgaWR4KSA9PiB7XHJcbiAgICAgIC8vIGNoZWNrIGlmIGVudHJ5IGlzIG5ld1xyXG4gICAgICBjb25zdCBpc0luc2VydGVkID0gKHRoaXMuY3VyRGF0YS5maWx0ZXIoIChjdXJJdGVtKSA9PiBjdXJJdGVtLmRhdGEuZGVsZXRlZCE9PXRydWUgJiYgY3VySXRlbS5kYXRhLmNhcHRpb24gPT09IGl0ZW0uY2Fw