UNPKG

@opitzconsulting/pie-chart

Version:
902 lines (901 loc) 85.3 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ import { Component, Input, ElementRef, Output, EventEmitter } from '@angular/core'; import * as d3 from 'd3'; /** * chart item properties * @record */ export function PieChartData() { } function PieChartData_tsickle_Closure_declarations() { /** * value of item * @type {?} */ PieChartData.prototype.value; /** * caption of item (must be unique) * @type {?} */ PieChartData.prototype.caption; /** * optional color of item (if not set, generated automatically) * @type {?|undefined} */ PieChartData.prototype.color; } /** * internal chart item properties * @record */ export function InternalPieChartData() { } function InternalPieChartData_tsickle_Closure_declarations() { /** * svg path for item * @type {?|undefined} */ InternalPieChartData.prototype.path; /** * delete flag for removing after transition * @type {?|undefined} */ InternalPieChartData.prototype.deleted; } var PieChartComponent = /** @class */ (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 EventEmitter(); /** * fired when user hovers a chart entry */ this.chartHover = new 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 (/** @type {?} */ (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 = (/** @type {?} */ (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 = (/** @type {?} */ (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((/** @type {?} */ (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 = (/** @type {?} */ (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((/** @type {?} */ (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: 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: ElementRef } ]; }; PieChartComponent.propDecorators = { data: [{ type: Input }], width: [{ type: Input }], height: [{ type: Input }], duration: [{ type: Input }], innerSpacing: [{ type: Input }], outerSpacing: [{ type: Input }], chartClick: [{ type: Output }], chartHover: [{ type: Output }] }; return PieChartComponent; }()); export { PieChartComponent }; function PieChartComponent_tsickle_Closure_declarations() { /** * chart data, which should be displayed * @type {?} */ PieChartComponent.prototype.data; /** * chart width in pixel * @type {?} */ PieChartComponent.prototype.width; /** * chart height in pixel * @type {?} */ PieChartComponent.prototype.height; /** * duration of animation transition * @type {?} */ PieChartComponent.prototype.duration; /** * inner spacing in pixel, if greater than 0 it defines the radius of the empty circle in the middle * @type {?} */ PieChartComponent.prototype.innerSpacing; /** * outer spacing in pixel * @type {?} */ PieChartComponent.prototype.outerSpacing; /** * fired when user clicks on a chart entry * @type {?} */ PieChartComponent.prototype.chartClick; /** * fired when user hovers a chart entry * @type {?} */ PieChartComponent.prototype.chartHover; /** * pie chart radius in pixel * @type {?} */ PieChartComponent.prototype.radius; /** * transform-attribute to center chart vertical and horizontal * @type {?} */ PieChartComponent.prototype.center; /** * current chart data with angle and path definitions, it will be consistent to the representation * @type {?} */ PieChartComponent.prototype.curData; /** * end chart data with angle and path definitions, it will representate the end state and used only for interpolation * @type {?} */ PieChartComponent.prototype.endData; /** * path generator function (internal use only) * @type {?} */ PieChartComponent.prototype.pathGenerator; /** * copy of last processed data, used to identify changes in ngDoCheck that Angular overlooked * @type {?} */ PieChartComponent.prototype.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! * @type {?} */ PieChartComponent.prototype.interrupt; /** * reference to tooltip div element * @type {?} */ PieChartComponent.prototype.tooltip; /** @type {?} */ PieChartComponent.prototype.element; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGllLWNoYXJ0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiJuZzovL0BvcGl0emNvbnN1bHRpbmcvcGllLWNoYXJ0LyIsInNvdXJjZXMiOlsibGliL3BpZS1jaGFydC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFzQixVQUFVLEVBQXlCLE1BQU0sRUFBRSxZQUFZLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFOUgsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0lBNEV2Qjs7O09BR0c7SUFDSCwyQkFDVTtRQUFBLFlBQU8sR0FBUCxPQUFPOzs7O29CQTVDb0IsRUFBRTs7OztxQkFFdEIsR0FBRzs7OztzQkFFRixHQUFHOzs7O3dCQUVELElBQUk7Ozs7NEJBRUEsQ0FBQzs7Ozs0QkFFRCxDQUFDOzs7OzBCQUUwQixJQUFJLFlBQVksRUFBRTs7OzswQkFFbEIsSUFBSSxZQUFZLEVBQUU7Ozs7dUJBT3RDLEVBQUU7Ozs7dUJBRUQsRUFBRTs7Ozt3QkFJTSxFQUFFOzs7Ozs7O3lCQW9QVixTQUFTO0tBbE9yQztJQWhCSjs7Ozs7T0FLRzs7Ozs7Ozs7O0lBQ08sb0NBQVE7Ozs7Ozs7O0lBQWxCLFVBQXNCLENBQUk7UUFDeEIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQ3RDO0lBQUEsQ0FBQztJQVFFLENBQUM7Ozs7SUFFTCxvQ0FBUTs7O0lBQVI7UUFDRSxJQUFJLENBQUMsT0FBTyxxQkFBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUMsdUJBQXVCLENBQW1CLENBQUEsQ0FBQztLQUNwRztJQUVEOzs7O09BSUc7Ozs7Ozs7SUFDSCx1Q0FBVzs7Ozs7O0lBQVgsVUFBWSxPQUFzQjs7UUFFaEMsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7S0FDekI7SUFBQSxDQUFDO0lBRUY7O09BRUc7Ozs7O0lBQ0gscUNBQVM7Ozs7SUFBVDs7UUFFRSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztLQUN6QjtJQUFBLENBQUM7SUFFRjs7O09BR0c7Ozs7OztJQUNPLDRDQUFnQjs7Ozs7SUFBMUI7O1FBRUUscUJBQUksV0FBVyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQzs7UUFFOUQsRUFBRSxDQUFBLENBQUMsV0FBVyxLQUFLLEtBQUssQ0FBQyxDQUFBLENBQUM7O1lBRXhCLEdBQUcsQ0FBQSxDQUFDLHFCQUFJLEdBQUcsR0FBQyxDQUFDLEVBQUUsR0FBRyxHQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFDLENBQUM7Z0JBQzFDLHFCQUFNLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUN6QixxQkFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQzs7Z0JBRTdCLFdBQVcsR0FBRyxXQUFXLElBQUksQ0FBQyxDQUFDLENBQUMsT0FBTyxLQUFLLENBQUMsQ0FBQyxPQUFPLElBQUksQ0FBQyxDQUFDLEtBQUssS0FBSyxDQUFDLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FBQyxLQUFLLEtBQUssQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDOztnQkFFckcsRUFBRSxDQUFBLENBQUMsV0FBVyxDQUFDO29CQUFDLEtBQUssQ0FBQzthQUN2QjtTQUNGOztRQUVELEVBQUUsQ0FBQSxDQUFDLFdBQVcsQ0FBQyxDQUFBLENBQUM7O1lBRWQsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDOztZQUVkLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDMUM7S0FDRjtJQUFBLENBQUM7SUFFRjs7T0FFRzs7Ozs7O0lBQ08sK0NBQW1COzs7OztJQUE3QixVQUE4QixLQUFhO1FBQ3pDLHFCQUFNLE9BQU8sR0FBRyxVQUFDLENBQVMsRUFBRSxDQUFTLEVBQUUsQ0FBUztZQUM5QyxFQUFFLENBQUEsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDakIsRUFBRSxDQUFBLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ2pCLEVBQUUsQ0FBQSxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUMsQ0FBQyxDQUFDO2dCQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN2QyxFQUFFLENBQUEsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFDLENBQUMsQ0FBQztnQkFBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBQ3JCLEVBQUUsQ0FBQSxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUMsQ0FBQyxDQUFDO2dCQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMvQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1NBQ1YsQ0FBQzs7UUFFRixxQkFBSSxLQUFLLENBQUM7UUFDVixxQkFBSSxvQkFBb0IsR0FBRyxLQUFLLENBQUM7UUFDakMsT0FBTSxvQkFBb0IsS0FBSyxLQUFLLEVBQUMsQ0FBQztZQUNwQyxxQkFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsaUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDbEQscUJBQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNiLHFCQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDYixxQkFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3RCLHFCQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNsQixxQkFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsR0FBQyxDQUFDLENBQUMsQ0FBQztZQUNqQyxxQkFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDM0IscUJBQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLEdBQUMsQ0FBQyxDQUFDLENBQUM7WUFDakMsS0FBSyxHQUFHLEdBQUc7a0JBQ1AsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztrQkFDaEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztrQkFDaEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ3JDLG9CQUFvQixHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUUsVUFBQyxDQUFDLElBQUssT0FBQSxDQUFDLENBQUMsS0FBSyxFQUFQLENBQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBRSxVQUFDLENBQUMsSUFBSyxPQUFBLENBQUMsS0FBSyxLQUFLLEVBQVgsQ0FBVyxDQUFDLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FBQyxDQUFDO1NBQ2xHO1FBQ0QsTUFBTSxDQUFDLEtBQUssQ0FBQztLQUNkO0lBQUEsQ0FBQztJQUVGOzs7Ozs7O09BT0c7Ozs7Ozs7Ozs7SUFDTyw4Q0FBa0I7Ozs7Ozs7OztJQUE1QixVQUE2QixJQUFrQixFQUFFLEtBQWEsRUFBRSxLQUFhLEVBQUUsVUFBa0IsRUFBRSxRQUFnQjs7UUFFakgscUJBQU0sTUFBTSxHQUFHO1lBQ2IsSUFBSSxFQUFFLElBQUk7WUFDVixLQUFLLEVBQUUsS0FBSztZQUNaLEtBQUssRUFBRSxLQUFLO1lBQ1osVUFBVSxFQUFFLFVBQVU7WUFDdEIsUUFBUSxFQUFFLFFBQVE7WUFDbEIsUUFBUSxFQUFFLENBQUM7WUFDWCxXQUFXLEVBQUUsSUFBSSxDQUFDLE1BQU0sR0FBRyxFQUFFO1lBQzdCLFdBQVcsRUFBRSxJQUFJLENBQUMsTUFBTTtTQUN6QixDQUFDOztRQUVGLG1CQUFDLE1BQU0sQ0FBQyxJQUE0QixFQUFDLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7O1FBRXhFLE1BQU0sQ0FBQyxNQUFNLENBQUM7S0FDZjtJQUFBLENBQUM7SUFFRjs7T0FFRzs7Ozs7SUFDTyxnREFBb0I7Ozs7SUFBOUI7UUFBQSxpQkFpQkM7O1FBZkMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUUsVUFBQyxPQUFPLEVBQUUsR0FBRzs7WUFFakMsRUFBRSxDQUFBLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUcsSUFBSSxDQUFDLENBQUEsQ0FBQzs7Z0JBRTlCLHFCQUFNLFNBQVMsR0FBRyxDQUFDLEtBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFFLFVBQUMsSUFBSSxJQUFLLE9BQUEsSUFBSSxDQUFDLE9BQU8sS0FBSyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBckMsQ0FBcUMsQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUMsQ0FBQzs7Z0JBRXBHLEVBQUUsQ0FBQSxDQUFDLFNBQVMsQ0FBQyxDQUFBLENBQUM7O29CQUVaLEFBREEseUNBQXlDO29CQUN6QyxLQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDOztvQkFFdEMsQUFEQSxzRUFBc0U7b0JBQ3RFLEtBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7b0JBQ3RDLEtBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQztpQkFDN0I7YUFDRjtTQUNGLENBQUMsQ0FBQztLQUNKO0lBQUEsQ0FBQztJQUVGOztPQUVHOzs7OztJQUNPLGlEQUFxQjs7OztJQUEvQjtRQUFBLGlCQW1CQzs7UUFqQkMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUUsVUFBQyxJQUFJLEVBQUUsR0FBRzs7WUFFM0IscUJBQU0sVUFBVSxHQUFHLENBQUMsS0FBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUUsVUFBQyxPQUFPLElBQUssT0FBQSxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sS0FBRyxJQUFJLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUssSUFBSSxDQUFDLE9BQU8sRUFBcEUsQ0FBb0UsQ0FBQyxDQUFDLE1BQU0sS0FBRyxDQUFDLENBQUMsQ0FBQzs7WUFFeEksRUFBRSxDQUFBLENBQUMsVUFBVSxDQUFDLENBQUEsQ0FBQztnQkFFYixDQUFDO29CQUNDLHFCQUFNLENBQUMsR0FBRyxLQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQ3ZFLEtBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7aUJBQ2hDO2dCQUVELENBQUM7b0JBQ0MscUJBQU0sQ0FBQyxHQUFHLEtBQUksQ0FBQyxrQkFBa0IsQ0FBQyxLQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQ2hGLEtBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7aUJBQ2hDO2FBQ0Y7U0FDRixDQUFDLENBQUM7S0FDSjtJQUFBLENBQUM7SUFFRjs7T0FFRzs7Ozs7SUFDTyw4Q0FBa0I7Ozs7SUFBNUI7O1FBRUUscUJBQUksUUFBUSxHQUFHLENBQUMsQ0FBQzs7UUFFakIsR0FBRyxDQUFBLENBQUMscUJBQUksS0FBSyxHQUFDLENBQUMsRUFBRSxLQUFLLEdBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxLQUFLLEVBQUMsQ0FBQzs7WUFFaEQsT0FBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPO2dCQUFFLEVBQUUsUUFBUSxDQUFDOztZQUV0RCxFQUFFLENBQUEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sS0FBSyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQSxDQUFDO2dCQUVuRSxDQUFDOztvQkFFQyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDOztvQkFFM0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztvQkFDM0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDO2lCQUNsQztnQkFFRCxDQUFDO29CQUNDLHFCQUFNLElBQUksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztvQkFDN0MscUJBQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQ3ZELElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7aUJBQ3JDO2dCQUVELENBQUM7b0JBQ0MscUJBQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO29CQUM3QyxxQkFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQ2hFLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7aUJBQ3JDOztnQkFFRCxFQUFFLFFBQVEsQ0FBQzthQUNaO1lBQ0QsRUFBRSxRQUFRLENBQUM7U0FDWjtLQUNGO0lBQUEsQ0FBQztJQUVGOztPQUVHOzs7OztJQUNPLHFDQUFTOzs7O0lBQW5CO1FBQUEsaUJBbUJDOztRQWpCQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBRSxVQUFDLElBQUksRUFBRSxLQUFLOztZQUU3QixxQkFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO1lBQ2pCLEdBQUcsQ0FBQSxDQUFDLHFCQUFJLENBQUMsR0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFDLEtBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxFQUFDLENBQUM7Z0JBQ3ZDLEVBQUUsQ0FBQSxDQUFDLENBQUMsS0FBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLEtBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sS0FBSyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUEsQ0FBQztvQkFDakYsUUFBUSxHQUFHLENBQUMsQ0FBQztvQkFDYixLQUFLLENBQUM7aUJBQ1A7YUFDRjs7WUFFRCxBQURBLGdDQUFnQztZQUNoQyxLQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztZQUMvQyxLQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQzs7WUFFL0MsQUFEQSxpREFBaUQ7WUFDakQsS0FBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQzs7WUFFMUMsQUFEQSxpREFBaUQ7WUFDakQsS0FBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7U0FDaEQsQ0FBQyxDQUFDO0tBQ0o7SUFBQSxDQUFDO0lBVUY7Ozs7T0FJRzs7Ozs7OztJQUNPLDBDQUFjOzs7Ozs7SUFBeEI7UUFBQSxpQkErQ0M7O1FBN0NDLHFCQUFNLEdBQUcsR0FBRyxtQkFBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFlLEVBQUMsQ0FBQzs7UUFFNUUscUJBQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDOztRQUUvQyxJQUFJLENBQUMsU0FBUyxHQUFHOztZQUVmLEtBQUssQ0FBQyxTQUFTLEVBQUUsQ0FBQzs7WUFFbEIsT0FBTyxLQUFJLENBQUMsU0FBUyxDQUFDO1NBQ3ZCLENBQUM7O1FBRUYsS0FBSzthQUNGLFVBQVUsRUFBRTthQUNaLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDO2FBR3ZCLFNBQVMsQ0FBQyxpQkFBaUIsRUFBRSxVQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsUUFBUTs7WUFFaEQscUJBQU0sTUFBTSxHQUFHLEVBQUUsQ0FBQyxXQUFXLENBQUMsS0FBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsS0FBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNoRixxQkFBTSxXQUFXLEdBQUcsRUFBRSxDQUFDLFdBQVcsQ0FBQyxLQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFVBQVUsRUFBRSxLQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQy9GLHFCQUFNLFNBQVMsR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDLEtBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxFQUFFLEtBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDekYscUJBQU0sTUFBTSxHQUFHLEVBQUUsQ0FBQyxXQUFXLENBQUMsS0FBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLEtBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDOztZQUUxRixNQUFNLENBQUMsVUFBQyxDQUFDOztnQkFFUCxBQURBLCtDQUErQztnQkFDL0MsS0FBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNwQyxLQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFVBQVUsR0FBRyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQzlDLEtBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDMUMsS0FBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQzs7Z0JBRXpDLEFBREEsb0JBQW9CO2dCQUNwQixLQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsS0FBSSxDQUFDLGFBQWEsQ0FBQyxLQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7O2dCQUVwRSxNQUFNLENBQUMsRUFBRSxDQUFDO2FBQ1gsQ0FBQztTQUNILENBQUM7YUFFRCxFQUFFLENBQUMsS0FBSyxFQUFFLFVBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxRQUFROztZQUU3QixFQUFFLENBQUEsQ0FBQyxHQUFHLEtBQUcsUUFBUSxDQUFDLE1BQU0sR0FBQyxDQUFDLENBQUMsQ0FBQSxDQUFDOztnQkFFMUIsQUFEQSxtQ0FBbUM7Z0JBQ25DLEtBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQzs7Z0JBRXZCLE9BQU8sS0FBSSxDQUFDLFNBQVMsQ0FBQzthQUN2QjtTQUNGLENBQUMsQ0FBQztLQUNOO0lBQUEsQ0FBQztJQUVGOzs7T0FHRzs7Ozs7O0lBQ08sMkNBQWU7Ozs7O0lBQXpCOztRQUVFLEdBQUcsQ0FBQSxDQUFDLHFCQUFJLENBQUMsR0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sR0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBQyxDQUFDO1lBQzFDLEVBQUUsQ0FBQSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sS0FBRyxJQUFJLENBQUMsQ0FBQSxDQUFDO2dCQUN0QyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7YUFDM0I7U0FDRjs7UUFFRCxHQUFHLENBQUEsQ0FBQyxxQkFBSSxDQUFDLEdBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUMsQ0FBQyxFQUFFLENBQUMsSUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUMsQ0FBQztZQUMxQyxFQUFFLENBQUEsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUcsSUFBSSxDQUFDLENBQUEsQ0FBQztnQkFDdEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFDLENBQUMsQ0FBQyxDQUFDO2FBQzFCO1NBQ0Y7S0FDRjtJQUFBLENBQUM7SUFFRjs7T0FFRzs7Ozs7SUFDTyxzQ0FBVTs7OztJQUFwQjtRQUFBLGlCQVNDOztRQVBDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFFLFVBQUMsSUFBSTs7WUFFdEIsRUFBRSxDQUFBLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUEsQ0FBQzs7Z0JBRWQsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO2FBQ25EO1NBQ0YsQ0FBQyxDQUFDO0tBQ0o7SUFBQSxDQUFDO0lBRUY7O09BRUc7Ozs7O0lBQ08sdUNBQVc7Ozs7SUFBckI7UUFDRSxxQkFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO1FBQ2pCLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFFLFVBQUMsT0FBTztZQUM1QixFQUFFLENBQUEsQ0FBQyxPQUFPLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQyxDQUFBLENBQUM7Z0JBQzlCLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO2FBQzdCO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxDQUFDLFFBQVEsQ0FBQztLQUNqQjtJQUFBLENBQUM7SUFFRjs7O09BR0c7Ozs7OztJQUNPLDJDQUFlOzs7OztJQUF6QixVQUEwQixRQUFnQjtRQUExQyxpQkFpQ0M7UUEvQkMsQ0FBQzs7WUFFQyxxQkFBTSxPQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsVUFBQyxDQUFDLEVBQUUsQ0FBQyxJQUFLLE9BQUEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQVgsQ0FBVyxFQUFFLENBQUMsQ0FBQyxDQUFDOztZQUU1RCxxQkFBSSxXQUFTLEdBQUcsQ0FBQyxDQUFDO1lBQ2xCLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFFLFVBQUMsSUFBSSxFQUFFLEdBQUc7O2dCQUU5QixxQkFBTSxTQUFTLEdBQUcsV0FBUyxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBSyxLQUFHLENBQUMsQ0FBQyxDQUFBLENBQUMsQ0FBQSxDQUFDLENBQUEsQ0FBQyxDQUFBLE9BQUssQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztnQkFDaEYsSUFBSSxDQUFDLFVBQVUsR0FBRyxXQUFTLENBQUM7Z0JBQzVCLElBQUksQ0FBQyxRQUFRLEdBQUcsU0FBUyxDQUFDO2dCQUMxQixJQUFJLENBQUMsS0FBSyxHQUFHLEdBQUcsQ0FBQztnQkFDakIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsS0FBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDMUMsV0FBUyxHQUFHLFNBQVMsQ0FBQzthQUN2QixDQUFDLENBQUM7U0FDSjtRQUVELENBQUM7O1lBRUMscUJBQU0sT0FBSyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxD