transcend-charts
Version:
Transcend is a charting and graph library for NUVI
826 lines (761 loc) • 36.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _Numbers = require('../helpers/Numbers');
var _Numbers2 = _interopRequireDefault(_Numbers);
var _Charts = require('../helpers/Charts');
var _Charts2 = _interopRequireDefault(_Charts);
var _Color = require('./Color');
var _Color2 = _interopRequireDefault(_Color);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var GroupedBarGraph = function () {
function GroupedBarGraph(el, graphData) {
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
_classCallCheck(this, GroupedBarGraph);
this.needsRender = true;
this.el = el;
this.lastFrame = new Date().getTime();
this.plotVars = {};
this.graphArea = {};
this.hits = {};
this.isDestroyed = false;
this.autoBind();
this.setOptions(options);
this.setData(graphData);
this.build();
this.attach();
this.onAnimateFrame();
window.setTimeout(this.fillParent.call(this), 0);
}
_createClass(GroupedBarGraph, [{
key: 'autoBind',
value: function autoBind() {
this.onAnimateFrame = this.onAnimateFrame.bind(this);
this.onFullscreenChange = this.onFullscreenChange.bind(this);
this.onWindowResize = this.onWindowResize.bind(this);
this.onMouseOut = this.onMouseOut.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onClick = this.onClick.bind(this);
}
}, {
key: 'build',
value: function build() {
this.htmlCanvas = document.createElement('CANVAS');
this.el.appendChild(this.htmlCanvas);
this.ctx = this.htmlCanvas.getContext('2d');
}
}, {
key: 'attach',
value: function attach() {
window.addEventListener('webkitfullscreenchange', this.onFullscreenChange);
window.addEventListener('fullscreenchange', this.onFullscreenChange);
window.addEventListener('mozfullscreenchange', this.onFullscreenChange);
window.addEventListener('msfullscreenchange', this.onFullscreenChange);
window.addEventListener('resize', this.onWindowResize);
this.htmlCanvas.addEventListener('mousemove', this.onMouseMove);
this.htmlCanvas.addEventListener('mouseout', this.onMouseOut);
this.htmlCanvas.addEventListener('click', this.onClick);
}
}, {
key: 'destroy',
value: function destroy() {
this.detach();
this.el.removeChild(this.htmlCanvas);
this.isDestroyed = true;
}
}, {
key: 'detach',
value: function detach() {
window.removeEventListener('webkitfullscreenchange', this.onFullscreenChange);
window.removeEventListener('fullscreenchange', this.onFullscreenChange);
window.removeEventListener('mozfullscreenchange', this.onFullscreenChange);
window.removeEventListener('msfullscreenchange', this.onFullscreenChange);
window.removeEventListener('resize', this.onWindowResize);
this.htmlCanvas.removeEventListener('mousemove', this.onMouseMove);
this.htmlCanvas.removeEventListener('mouseout', this.onMouseOut);
this.htmlCanvas.removeEventListener('click', this.onClick);
}
/**
* This function sets/resets the data for the graph
**/
}, {
key: 'setData',
value: function setData() {
var _this = this;
var someData = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];
// let's create a variable for each bar so we can track animation
this.data = _lodash2.default.map(someData, function (barGroup) {
var barData = Object.assign({}, barGroup);
barData.segments = _this.processSegmentData(barData.segments, barData);
return barData;
});
}
}, {
key: 'processSegmentData',
value: function processSegmentData(segments, barGroup) {
return _lodash2.default.map(segments, function (segmentData) {
var segment = Object.assign({}, segmentData || {});
var parts = _Numbers2.default.separateNumberUnits(segment.value);
if (segment.prefix === undefined) {
segment.prefix = parts.prefix;
}
if (segment.suffix === undefined) {
segment.suffix = parts.suffix;
}
segment.value = parts.value;
if (!barGroup.prefix) {
barGroup.prefix = segment.prefix;
}
if (!barGroup.suffix) {
barGroup.suffix = segment.suffix;
}
if (segment.barFillColor) {
segment.barFillColor = new _Color2.default.Color(segment.barFillColor);
}
if (segment.barBorderColor) {
segment.barBorderColor = new _Color2.default.Color(segment.barBorderColor);
}
if (segment.barStripeColor) {
segment.barStripeColor = new _Color2.default.Color(segment.barStripeColor);
}
return segment;
});
}
}, {
key: 'fillParent',
value: function fillParent() {
if (this.htmlCanvas && this.htmlCanvas.parentNode) {
this.needsRender = true;
var pxRatio = window.devicePixelRatio || 1;
var style = window.getComputedStyle(this.htmlCanvas.parentNode);
var width = this.htmlCanvas.parentNode.offsetWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
var height = this.htmlCanvas.parentNode.offsetHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);
// upscale this thang if the device pixel ratio is higher than 1
this.htmlCanvas.width = width * pxRatio;
this.htmlCanvas.height = height * pxRatio;
this.htmlCanvas.style.width = width + 'px';
this.htmlCanvas.style.height = height + 'px';
}
}
}, {
key: 'isPercentage',
value: function isPercentage(x) {
var str = String(x);
return str.substr(-1) === '%';
}
}, {
key: 'setOptions',
value: function setOptions(options) {
this.options = Object.assign({}, {
abbreviateGridValues: false,
animation: false,
backgroundColor: 'transparent',
barBorderColor: 'transparent',
barFillColor: '#888888',
barLabelFontColor: false,
barLabelFontFamily: 'Arial',
barLabelFontSize: '66%',
barLabelFontWeight: '400',
barLabelOrientation: 'horizontal',
barLabelSpacing: 3, // the spacing between the labels (when placed on the bottom) and the graph itself
barLegendSpacing: 12,
barSpacing: 1, // as a percent of the width of each ba,
barStripeColor: '#666666',
barStripeSpacing: 10,
barStripeType: 'none',
barStripeWidth: 2,
colorBarWidth: 12,
colorBarSpacing: 5, // the pixel spacing between a color swatch on the legend and a label
gridFontColor: false,
gridFontFamily: 'Arial',
gridFontSize: 12,
gridFontSizeAsPct: false,
gridLineColor: '#cccccc',
groupSpacing: 1, // as a percent of the width of each bar
legendBackgroundColor: 'rgba(255,255,255,0.3)',
legendFontColor: '#ffffff',
legendFontFamily: 'Arial',
legendFontSize: 12,
legendFontWeight: '400',
legendLabelValueSpacing: 10,
legendOutlineColor: 'transparent',
legendOutlineCornerSize: 8, // the width of the little corner notches around the legend
legendOutlineWidth: 1, // the width of the stroke around the legend
notchSpacing: 3,
notchWidth: 10,
padding: 0,
showLegend: true,
showValues: true,
tooltipPadding: 12, // the padding for the tooltip callout used on mouseover
valueFontColor: false,
valueFontFamily: 'Arial',
valueFontSize: '90%',
valueFontSizeAsPct: false,
valueFontWeight: '400'
}, options);
this.colorizeOptions();
this.percentageOptions();
this.pixelOptions();
}
}, {
key: 'onWindowResize',
value: function onWindowResize() {
this.fillParent();
}
}, {
key: 'onClick',
value: function onClick(event) {
var rect = this.htmlCanvas.getBoundingClientRect();
var pt = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
var hits = this.getHitsFromPoint(pt);
if (hits.segment && hits.segment.onClick) {
hits.segment.onClick(event);
}
}
}, {
key: 'onAnimateFrame',
value: function onAnimateFrame() {
var thisFrame = new Date().getTime();
var elapsed = thisFrame - this.lastFrame; // elapsed time since last render
this.fps = 1000 / elapsed;
if (!this.isDestroyed) {
if (this.needsRender) {
this.render();
this.needsRender = false;
}
this.lastFrame = thisFrame;
if (window.requestAnimationFrame) {
window.requestAnimationFrame(this.onAnimateFrame);
} else if (window.webkitRequestAnimationFrame) {
window.webkitRequestAnimationFrame(this.onAnimateFrame);
} else if (window.mozRequestAnimationFrame) {
window.mozRequestAnimationFrame(this.onAnimateFrame);
} else if (window.oRequestAnimationFrame) {
window.oRequestAnimationFrame(this.onAnimateFrame);
}
}
}
}, {
key: 'onFullscreenChange',
value: function onFullscreenChange() {
var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement;
if (!fullscreenElement) {
// exiting full screen
var targetElement = event.target;
// if this element contains a canvas, make it tiny so we don't mess up the size of its parent
// don't worry, the window.resize event that fires next will make it fill its parent just fine
var canvases = targetElement.getElementsByTagName('CANVAS');
for (var c = 0; c < canvases.length; c++) {
if (canvases[c] === this.htmlCanvas) {
canvases[c].width = 1;
canvases[c].height = 1;
canvases[c].style.width = '1px';
canvases[c].style.height = '1px';
break;
}
}
}
}
}, {
key: 'onMouseOut',
value: function onMouseOut() {
this.unhighlightAll();
}
}, {
key: 'onMouseMove',
value: function onMouseMove(event) {
if (!this.options.showLegend) {
return;
}
var rect = this.htmlCanvas.getBoundingClientRect();
var pt = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
this.hits = this.getHitsFromPoint(pt);
this.needsRender = true;
}
}, {
key: 'percentageOptions',
value: function percentageOptions() {
var _this2 = this;
['gridFontSize', 'valueFontSize', 'barLabelFontSize'].forEach(function (key) {
var val = _this2.options[key];
if (_this2.isPercentage(val)) {
_this2.options[key + 'AsPct'] = parseFloat(val.substr(0, val.length - 1)) / 100;
} else {
_this2.options[key] = parseFloat(val);
_this2.options[key + 'AsPct'] = false;
}
});
}
}, {
key: 'colorizeOptions',
value: function colorizeOptions() {
var _this3 = this;
var keys = ['backgroundColor', 'barBorderColor', 'barFillColor', 'barStripeColor', 'gridFontColor', 'gridLineColor', 'legendBackgroundColor', 'legendOutlineColor', 'legendFontColor', 'valueFontColor'];
keys.forEach(function (key) {
var color = _this3.options[key];
if (color) {
_this3.options[key] = new _Color2.default.Color(color);
}
});
}
}, {
key: 'pixelOptions',
value: function pixelOptions() {
var _this4 = this;
var keys = ['colorBarSpacing', 'colorBarWidth', 'barLabelSpacing', 'barStripeWidth', 'gridFontSize', 'legendFontSize', 'legendLabelValueSpacing', 'legendOutlineCornerSize', 'legendOutlineWidth', 'notchSpacing', 'notchWidth', 'padding', 'tooltipPadding'];
keys.forEach(function (key) {
_this4.options[key] = parseFloat(_this4.options[key]);
});
}
}, {
key: 'getWidestLabel',
value: function getWidestLabel() {
var fsize = 50;
var widestLabel = null;
var widestLabelWidth = 0;
this.ctx.font = this.options.barLabelFontWeight + ' ' + fsize + 'px ' + this.options.barLabelFontFamily;
for (var ii = 0; ii < this.data.length; ii++) {
var width = this.ctx.measureText(this.data[ii].name).width;
if (width > widestLabelWidth) {
widestLabel = this.data[ii].name;
widestLabelWidth = width;
}
}
return { label: widestLabel, width: widestLabelWidth };
}
}, {
key: 'calculateBarLabelFontSize',
value: function calculateBarLabelFontSize(widestLabelText, optimalBarLabelWidth) {
// now we know which label is the widest. Let's iterate until we find a font size that fits the size provided
var fsize = optimalBarLabelWidth;
var diff = 1;
while (diff > 0) {
this.ctx.font = this.options.barLabelFontWeight + ' ' + fsize + 'px ' + this.options.barLabelFontFamily;
var width = this.ctx.measureText(widestLabelText).width;
diff = width - optimalBarLabelWidth;
if (diff > 0) {
fsize = Math.floor(0.9 * fsize);
}
}
return fsize;
}
}, {
key: 'render',
value: function render(data) {
if (data) {
this.setData(data);
}
if (!this.needsRender) {
return;
}
var options = this.options;
var pxRatio = window.devicePixelRatio || 1;
var backingStoreRatio = this.ctx.webkitBackingStorePixelRatio || this.ctx.mozBackingStorePixelRatio || this.ctx.msBackingStorePixelRatio || this.ctx.oBackingStorePixelRatio || this.ctx.backingStorePixelRatio || 1;
var canvasWidth = this.htmlCanvas.width / (pxRatio / backingStoreRatio);
var canvasHeight = this.htmlCanvas.height / (pxRatio / backingStoreRatio);
this.plotVars.canvasHeight = canvasHeight;
this.plotVars.canvasWidth = canvasWidth;
// upscale this thang if the device pixel ratio is higher than 1
this.ctx.save();
if (pxRatio > 1) {
this.ctx.scale(pxRatio / backingStoreRatio, pxRatio / backingStoreRatio);
}
this.graphArea = {
top: 0,
right: 0,
bottom: 0,
left: 0,
width: 0,
height: 0
};
// fill background
this.ctx.clearRect(0, 0, canvasWidth, canvasHeight);
if (options.backgroundColor.toString() !== 'transparent') {
this.ctx.fillStyle = this.options.backgroundColor.toString();
this.ctx.fillRect(0, 0, canvasWidth, canvasHeight);
}
// calculate important measurements
var maxDataPoint = _lodash2.default.max(this.data, function (dataPoint) {
return _lodash2.default.max(dataPoint.segments, function (segment) {
return segment.value;
}).value;
});
this.plotVars.maxY = _lodash2.default.max(maxDataPoint.segments, function (segment) {
return segment.value;
}).value;
this.plotVars.minY = 0;
// if the grid font size was given as a percent, let's calculate the hard px value now
if (this.options.gridFontSizeAsPct) {
this.options.gridFontSize = this.options.gridFontSizeAsPct * (canvasHeight - this.options.padding * 2);
}
// calculate grid area
this.graphArea = {
left: this.options.padding + Number(this.options.notchWidth) + Number(this.options.notchSpacing),
top: this.options.padding,
right: this.options.padding,
bottom: this.options.padding
};
this.graphArea.width = canvasWidth - this.graphArea.left - this.graphArea.right;
// calculate the nice round values for the y axis
var _HelpersCharts$yAxis = _Charts2.default.yAxis(this.graphArea.height, this.plotVars.maxY, this.plotVars.minY, this.options.gridFontSize);
var numberOfLabels = _HelpersCharts$yAxis.numberOfLabels;
var valueBetweenLabels = _HelpersCharts$yAxis.valueBetweenLabels;
this.plotVars.chartMaxY = this.plotVars.minY + numberOfLabels * valueBetweenLabels;
// calculate the width of the y-value labels and adjust the graphArea to accommodate the longest one
this.ctx.textAlign = 'right';
this.ctx.textBaseline = 'middle';
this.ctx.font = this.options.gridFontSize + 'px ' + this.options.gridFontFamily;
var labelWidth = 0;
for (var i = 0; i <= numberOfLabels; i++) {
var labelstr = this.options.abbreviateGridValues ? _Numbers2.default.formatNumber(i * valueBetweenLabels, 1, true) : _Numbers2.default.formatNumber(i * valueBetweenLabels);
var lw = this.ctx.measureText(labelstr).width;
if (lw > labelWidth) {
labelWidth = lw;
}
}
this.graphArea.left = Number(this.options.padding) + Number(labelWidth) + Number(this.options.notchWidth) + this.options.notchSpacing;
this.graphArea.width = canvasWidth - this.graphArea.left - this.graphArea.right;
var allowedGroupWidth = this.graphArea.width / this.data.length;
var groupWidth = allowedGroupWidth / (1 + this.options.groupSpacing);
var maxBarsPerGroup = 0;
var allowedBarWidth = 0;
if (this.data.length) {
maxBarsPerGroup = _lodash2.default.max(this.data, function (datum) {
return datum.segments.length;
}).segments.length;
}
allowedBarWidth = groupWidth / maxBarsPerGroup;
var barWidth = allowedBarWidth / (1 + this.options.barSpacing);
// calculate where the bottom of the graph should be
var widestLabel = this.getWidestLabel();
if (this.options.barLabelOrientation === 'horizontal') {
if (this.options.barLabelFontSizeAsPct) {
this.options.barLabelFontSize = this.calculateBarLabelFontSize(widestLabel.label, this.options.barLabelFontSizeAsPct * groupWidth);
}
this.graphArea.bottom = this.options.padding + Number(this.options.barLabelFontSize * 1.25) + Number(this.options.barLabelSpacing);
} else {
if (this.options.barLabelFontSizeAsPct) {
this.options.barLabelFontSize = this.calculateBarLabelFontSize(widestLabel.label, this.options.barLabelFontSizeAsPct * (canvasHeight - 2 * this.options.padding));
}
this.ctx.font = this.options.barLabelFontWeight + ' ' + this.options.barLabelFontSize + 'px ' + this.options.barLabelFontFamily;
this.graphArea.bottom = this.options.padding + Number(this.ctx.measureText(widestLabel.label).width) + Number(this.options.barLabelSpacing);
}
this.graphArea.height = canvasHeight - this.graphArea.top - this.graphArea.bottom;
// draw horizontal grid lines
this.ctx.font = this.options.gridFontSize + 'px ' + this.options.gridFontFamily;
this.ctx.strokeStyle = this.options.gridLineColor.toString();
this.ctx.fillStyle = (this.options.gridFontColor || this.options.gridLineColor).toString();
for (var _i = 0; _i <= numberOfLabels; _i++) {
var y = canvasHeight - this.graphArea.bottom - _i * valueBetweenLabels / this.plotVars.chartMaxY * this.graphArea.height;
this.ctx.beginPath();
this.ctx.moveTo(this.graphArea.left - this.options.notchWidth, y);
this.ctx.lineTo(canvasWidth - this.graphArea.right, y);
this.ctx.stroke();
var _labelstr = this.options.abbreviateGridValues ? _Numbers2.default.formatNumber(_i * valueBetweenLabels, 1, true) : _Numbers2.default.formatNumber(_i * valueBetweenLabels);
this.ctx.fillText(_labelstr, this.graphArea.left - this.options.notchWidth - this.options.notchSpacing, y);
}
// calculate widest bar VALUE
if (this.options.valueFontSizeAsPct) {
var fsize = 50;
var widestValue = null;
var widestValueWidth = 0;
this.ctx.font = this.options.valueFontWeight + ' ' + fsize + 'px ' + this.options.valueFontFamily;
for (var _i2 = 0; _i2 < this.data.length; _i2++) {
var displayValue = this.data[_i2].displayValue;
if (!displayValue) {
var totalValue = _lodash2.default.sum(this.data[_i2].segments, function (segment) {
return segment.value;
});
displayValue = this.data[_i2].prefix + totalValue + this.data[_i2].suffix;
}
var width = this.ctx.measureText(displayValue).width;
if (width > widestValueWidth) {
widestValue = displayValue;
widestValueWidth = width;
break;
}
}
// now we know which value is the widest. Let's iterate until we find a font size that fits the percent provided
var optimalValueWidth = this.options.valueFontSizeAsPct * groupWidth;
var cont = true;
while (cont) {
this.ctx.font = this.options.valueFontWeight + ' ' + fsize + 'px ' + this.options.valueFontFamily;
var _width = this.ctx.measureText(widestValue).width;
if (_width <= optimalValueWidth) {
cont = false;
} else {
fsize = Math.floor(0.9 * fsize);
}
}
this.options.valueFontSize = fsize;
}
this.renderBars(allowedGroupWidth, groupWidth, allowedBarWidth, barWidth);
this.renderLegend(allowedGroupWidth, groupWidth, allowedBarWidth, barWidth);
if (this.hits.segment && (this.hits.segment.onClick || this.options.onClick)) {
this.htmlCanvas.style.cursor = 'pointer';
} else {
this.htmlCanvas.style.cursor = 'default';
}
// draw fps
if (this.options.DEBUG) {
this.ctx.fillStyle = '#666666';
this.ctx.fillRect(canvasWidth - 40, canvasHeight - 15, 40, 15);
this.ctx.textAlign = 'right';
this.ctx.textBaseline = 'bottom';
this.ctx.fillStyle = '#000000';
this.ctx.font = '11px sans-serif';
this.ctx.fillText(String(Math.round(this.fps)) + ' fps', canvasWidth - 5, canvasHeight - 1);
}
this.ctx.restore();
}
}, {
key: 'renderLegend',
value: function renderLegend(allowedGroupWidth, groupWidth) {
var _this5 = this;
// highlighted bar and show legend (if applicable)
this.data.forEach(function (group, i) {
if (group === _this5.hits.group) {
var groupX = _this5.graphArea.left + i * allowedGroupWidth + (allowedGroupWidth - groupWidth) / 2;
// const groupY = (this.plotVars.canvasHeight - this.graphArea.bottom);
// some vars for the highlight
// value at the top
if (_this5.options.showValues) {
_this5.ctx.fillStyle = _this5.options.valueFontColor || _this5.options.gridLineColor;
_this5.ctx.textAlign = 'center';
_this5.ctx.textBaseline = 'top';
_this5.ctx.font = _this5.options.valueFontWeight + ' ' + _this5.options.valueFontSize + 'px ' + _this5.options.valueFontFamily;
var totalDisplayValue = group.displayValue;
if (!totalDisplayValue) {
var totalValue = _lodash2.default.sum(group.segments, function (segment) {
return segment.value;
});
totalDisplayValue = group.prefix + totalValue + group.suffix;
}
_this5.ctx.fillText(totalDisplayValue, groupX + groupWidth / 2, _this5.graphArea.top);
}
// determine whether the legends should appear on the right or left of the bar
_this5.ctx.font = _this5.options.legendFontWeight + ' ' + parseFloat(_this5.options.legendFontSize) + 'px ' + _this5.options.legendFontFamily;
// render legend/tooltip for highlight of mouseover (if any)
// determine width of tooltip/legend
_this5.ctx.font = parseFloat(_this5.options.legendFontSize) + 'px ' + _this5.options.legendFontFamily;
_this5.ctx.textBaseline = 'baseline';
_this5.ctx.textAlign = 'left';
var widestValue = 0;
for (var s = 0; s < group.segments.length; s++) {
var w = _this5.ctx.measureText(group.segments[s].name + ': ' + group.segments[s].prefix + _Numbers2.default.formatNumber(group.segments[s].value) + group.segments[s].suffix).width;
if (w > widestValue) {
widestValue = w;
}
}
var legendWidth = _this5.options.colorBarWidth + _this5.options.colorBarSpacing + widestValue + _this5.options.tooltipPadding * 2;
// determine height of legend/tooltip
var legendHeight = group.segments.length * (parseFloat(_this5.options.legendFontSize) * 1.25) + _this5.options.tooltipPadding * 2;
// determine where the legend/tooltip will fit
var legendX = groupX + groupWidth + _this5.options.barLegendSpacing;
if (legendX + legendWidth > _this5.graphArea.left + _this5.graphArea.width) {
legendX = groupX - legendWidth - _this5.options.barLegendSpacing;
}
var legendY = _this5.graphArea.top;
// draw legend/tooltip box
_this5.ctx.fillStyle = _this5.options.legendBackgroundColor.toString();
_this5.ctx.fillRect(legendX, legendY, legendWidth, legendHeight);
// draw four corners
_this5.ctx.strokeStyle = _this5.options.legendOutlineColor.toString();
_this5.ctx.lineWidth = _this5.options.legendOutlineWidth;
_this5.ctx.beginPath();
_this5.ctx.moveTo(legendX + _this5.options.legendOutlineWidth, legendY + _this5.options.legendOutlineCornerSize);
_this5.ctx.lineTo(legendX + _this5.options.legendOutlineWidth, legendY + _this5.options.legendOutlineWidth);
_this5.ctx.lineTo(legendX + _this5.options.legendOutlineCornerSize, legendY + _this5.options.legendOutlineWidth);
_this5.ctx.moveTo(legendX + legendWidth - _this5.options.legendOutlineCornerSize, legendY + _this5.options.legendOutlineWidth);
_this5.ctx.lineTo(legendX + legendWidth - _this5.options.legendOutlineWidth, legendY + _this5.options.legendOutlineWidth);
_this5.ctx.lineTo(legendX + legendWidth - _this5.options.legendOutlineWidth, legendY + _this5.options.legendOutlineCornerSize);
_this5.ctx.moveTo(legendX + legendWidth - _this5.options.legendOutlineWidth, legendY + legendHeight - _this5.options.legendOutlineCornerSize);
_this5.ctx.lineTo(legendX + legendWidth - _this5.options.legendOutlineWidth, legendY + legendHeight - _this5.options.legendOutlineWidth);
_this5.ctx.lineTo(legendX + legendWidth - _this5.options.legendOutlineCornerSize, legendY + legendHeight - _this5.options.legendOutlineWidth);
_this5.ctx.moveTo(legendX + _this5.options.legendOutlineCornerSize, legendY + legendHeight - _this5.options.legendOutlineWidth);
_this5.ctx.lineTo(legendX + _this5.options.legendOutlineWidth, legendY + legendHeight - _this5.options.legendOutlineWidth);
_this5.ctx.lineTo(legendX + _this5.options.legendOutlineWidth, legendY + legendHeight - _this5.options.legendOutlineCornerSize);
_this5.ctx.stroke();
// draw values for each series (one per line)
for (var _s = 0; _s < group.segments.length; _s++) {
var lineY = _s * parseFloat(_this5.options.legendFontSize) * 1.25;
// draw a color bar for reference
_this5.ctx.fillStyle = group.segments[_s].barFillColor;
_this5.ctx.fillRect(legendX + _this5.options.tooltipPadding, legendY + _this5.options.tooltipPadding + lineY, _this5.options.colorBarWidth, parseFloat(_this5.options.legendFontSize));
var serieslabelw = _this5.ctx.measureText(group.segments[_s].name + ': ').width;
// draw the number/value in the series color
_this5.ctx.fillText(group.segments[_s].prefix + _Numbers2.default.formatNumber(group.segments[_s].value) + group.segments[_s].suffix, legendX + _this5.options.tooltipPadding + _this5.options.colorBarWidth + _this5.options.colorBarSpacing + serieslabelw, legendY + _this5.options.tooltipPadding + lineY);
// draw the series label in a plain label color
_this5.ctx.fillStyle = _this5.options.legendFontColor.toString();
_this5.ctx.fillText(group.segments[_s].name + ': ', legendX + _this5.options.tooltipPadding + _this5.options.colorBarWidth + _this5.options.colorBarSpacing, legendY + _this5.options.tooltipPadding + lineY);
}
}
});
}
}, {
key: 'renderBars',
value: function renderBars(allowedGroupWidth, groupWidth, allowedBarWidth, barWidth) {
var _this6 = this;
// draw bars
this.data.forEach(function (group, i) {
var groupX = _this6.graphArea.left + i * allowedGroupWidth + (allowedGroupWidth - groupWidth) / 2;
var groupY = _this6.plotVars.canvasHeight - _this6.graphArea.bottom;
group.x = groupX;
group.y = _this6.graphArea.bottom;
group.y1 = groupY;
group.x1 = groupX + groupWidth;
if (_this6.hits.group && _this6.hits.group === group) {
_this6.ctx.fillStyle = new _Color2.default.Color(_this6.options.gridLineColor).fade(0.5);
_this6.ctx.fillRect(groupX, groupY - _this6.graphArea.height, groupWidth, _this6.graphArea.height);
}
_this6.renderSegments(group, allowedBarWidth, barWidth);
// bar label
if (group.name !== undefined) {
_this6.ctx.fillStyle = (_this6.options.barLabelFontColor || _this6.options.gridLineColor).toString();
if (_this6.options.barLabelOrientation === 'horizontal') {
_this6.ctx.font = _this6.options.barLabelFontSize + 'px ' + _this6.options.barLabelFontFamily;
_this6.ctx.textBaseline = 'top';
_this6.ctx.textAlign = 'center';
_this6.ctx.fillText(group.name, groupX + groupWidth / 2, _this6.plotVars.canvasHeight - _this6.graphArea.bottom + _this6.options.barLabelSpacing);
} else {
_this6.ctx.font = _this6.options.barLabelFontSize + 'px ' + _this6.options.barLabelFontFamily;
_this6.ctx.textBaseline = 'middle';
_this6.ctx.textAlign = 'right';
_this6.ctx.save();
_this6.ctx.translate(groupX + groupWidth / 2, _this6.graphArea.top + _this6.graphArea.height + _this6.options.barLabelSpacing);
_this6.ctx.rotate(-Math.PI / 2);
_this6.ctx.fillText(group.name, 0, 0);
_this6.ctx.restore();
}
}
});
}
}, {
key: 'renderSegments',
value: function renderSegments(group, allowedBarWidth, barWidth) {
var _this7 = this;
// draw each segment
(group.segments || []).forEach(function (segment, i) {
if (segment.value !== undefined) {
var barFillColor = new _Color2.default.Color(_this7.options.barFillColor);
if (segment.barFillColor) {
barFillColor = new _Color2.default.Color(segment.barFillColor);
}
if (_this7.hits.group === group && _this7.hits.segment && _this7.hits.segment !== segment && (_this7.hits.segment.onClick || _this7.options.onClick)) {
barFillColor.fade(0.4);
}
_this7.ctx.fillStyle = barFillColor.toString();
var barHeight = segment.value / _this7.plotVars.chartMaxY * _this7.graphArea.height;
barHeight = Math.max(barHeight, 1);
var barX = group.x + i * allowedBarWidth + (allowedBarWidth - barWidth) / 2;
var barY = group.y1 - barHeight;
segment.x = barX;
segment.y = barY;
segment.y1 = group.y1;
segment.x1 = barX + barWidth;
_this7.ctx.fillRect(barX, barY, barWidth, barHeight);
// diagonal stripes
if (_this7.options.barStripeType !== 'none') {
_this7.ctx.save();
_this7.ctx.beginPath();
_this7.ctx.rect(barX, barY, barWidth, barHeight);
_this7.ctx.clip();
var barStripeColor = new _Color2.default.Color(_this7.options.barStripeColor);
if (segment.barStripeColor) {
barStripeColor = new _Color2.default.Color(segment.barStripeColor);
}
if (_this7.hits.group && group !== _this7.hits.group) {
barStripeColor.fade(0.7);
}
_this7.ctx.strokeStyle = barStripeColor.toString();
_this7.ctx.lineWidth = _this7.options.barStripeWidth;
_this7.ctx.beginPath();
switch (_this7.options.barStripeType) {
case 'diagonal':
for (var _stripeY = barY; _stripeY < barY + barHeight; _stripeY += _this7.options.barStripeSpacing) {
_this7.ctx.moveTo(barX, _stripeY + barWidth);
_this7.ctx.lineTo(barX + barWidth, _stripeY);
}
break;
case 'horizontal':
for (var _stripeY2 = barY; _stripeY2 < barY + barHeight; _stripeY2 += _this7.options.barStripeSpacing) {
_this7.ctx.moveTo(barX, _stripeY2);
_this7.ctx.lineTo(barX + barWidth, _stripeY2);
}
break;
case 'vertical':
var stripeY = barY;
for (var m = barX; m < barX + barWidth; m += _this7.options.barStripeSpacing) {
_this7.ctx.moveTo(m, stripeY);
_this7.ctx.lineTo(m, stripeY + barHeight);
}
break;
default:
break;
}
_this7.ctx.stroke();
_this7.ctx.restore();
}
// draw border around bar segments
if (segment.barBorderColor || _this7.options.barBorderColor && _this7.options.barBorderColor.toString() !== 'transparent') {
_this7.ctx.beginPath();
var barBorderColor = new _Color2.default.Color(_this7.options.barBorderColor);
if (segment.barBorderColor) {
barBorderColor = new _Color2.default.Color(segment.barBorderColor);
}
if (_this7.hits.group && _this7.hits.group !== group) {
barBorderColor.fade(0.7);
}
_this7.ctx.strokeStyle = barBorderColor.toString();
_this7.ctx.rect(barX, barY, barWidth, barHeight);
_this7.ctx.stroke();
}
}
});
}
}, {
key: 'unhighlightAll',
value: function unhighlightAll() {
this.hits = {};
}
}, {
key: 'getHitsFromPoint',
value: function getHitsFromPoint(pt) {
var _this8 = this;
var hits = {
segment: false,
group: false
};
hits.group = this.data.find(function (group) {
hits.segment = group.segments.find(function (segment) {
// find to get it to stop appropriately
return _this8.pointWithinSegment(pt, segment);
});
if (hits.segment) {
return true;
} else {
return _this8.pointWithinSegment(pt, group);
}
});
return hits;
}
}, {
key: 'pointWithinSegment',
value: function pointWithinSegment(pt, segment) {
return pt.x >= segment.x && pt.x <= segment.x1 && pt.y >= segment.y && pt.y <= segment.y1;
}
}]);
return GroupedBarGraph;
}();
exports.default = GroupedBarGraph;