mirror of
https://github.com/chartjs/Chart.js.git
synced 2024-10-07 20:59:08 +02:00
717 lines
20 KiB
JavaScript
Executable File
717 lines
20 KiB
JavaScript
Executable File
"use strict";
|
|
|
|
module.exports = function(Chart) {
|
|
|
|
var helpers = Chart.helpers;
|
|
|
|
Chart.defaults.global.tooltips = {
|
|
enabled: true,
|
|
custom: null,
|
|
mode: 'single',
|
|
backgroundColor: "rgba(0,0,0,0.8)",
|
|
titleFontStyle: "bold",
|
|
titleSpacing: 2,
|
|
titleMarginBottom: 6,
|
|
titleFontColor: "#fff",
|
|
titleAlign: "left",
|
|
bodySpacing: 2,
|
|
bodyFontColor: "#fff",
|
|
bodyAlign: "left",
|
|
footerFontStyle: "bold",
|
|
footerSpacing: 2,
|
|
footerMarginTop: 6,
|
|
footerFontColor: "#fff",
|
|
footerAlign: "left",
|
|
yPadding: 6,
|
|
xPadding: 6,
|
|
yAlign : 'center',
|
|
xAlign : 'center',
|
|
caretSize: 5,
|
|
cornerRadius: 6,
|
|
multiKeyBackground: '#fff',
|
|
callbacks: {
|
|
// Args are: (tooltipItems, data)
|
|
beforeTitle: helpers.noop,
|
|
title: function(tooltipItems, data) {
|
|
// Pick first xLabel for now
|
|
var title = '';
|
|
var labels = data.labels;
|
|
var labelCount = labels ? labels.length : 0;
|
|
|
|
if (tooltipItems.length > 0) {
|
|
var item = tooltipItems[0];
|
|
|
|
if (item.xLabel) {
|
|
title = item.xLabel;
|
|
} else if (labelCount > 0 && item.index < labelCount) {
|
|
title = labels[item.index];
|
|
}
|
|
}
|
|
|
|
return title;
|
|
},
|
|
afterTitle: helpers.noop,
|
|
|
|
// Args are: (tooltipItems, data)
|
|
beforeBody: helpers.noop,
|
|
|
|
// Args are: (tooltipItem, data)
|
|
beforeLabel: helpers.noop,
|
|
label: function(tooltipItem, data) {
|
|
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
|
|
return datasetLabel + ': ' + tooltipItem.yLabel;
|
|
},
|
|
labelColor: function(tooltipItem, chartInstance) {
|
|
var meta = chartInstance.getDatasetMeta(tooltipItem.datasetIndex);
|
|
var activeElement = meta.data[tooltipItem.index];
|
|
var view = activeElement._view;
|
|
return {
|
|
borderColor: view.borderColor,
|
|
backgroundColor: view.backgroundColor
|
|
};
|
|
},
|
|
afterLabel: helpers.noop,
|
|
|
|
// Args are: (tooltipItems, data)
|
|
afterBody: helpers.noop,
|
|
|
|
// Args are: (tooltipItems, data)
|
|
beforeFooter: helpers.noop,
|
|
footer: helpers.noop,
|
|
afterFooter: helpers.noop
|
|
}
|
|
};
|
|
|
|
// Helper to push or concat based on if the 2nd parameter is an array or not
|
|
function pushOrConcat(base, toPush) {
|
|
if (toPush) {
|
|
if (helpers.isArray(toPush)) {
|
|
//base = base.concat(toPush);
|
|
Array.prototype.push.apply(base, toPush);
|
|
} else {
|
|
base.push(toPush);
|
|
}
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
function getAveragePosition(elements) {
|
|
if (!elements.length) {
|
|
return false;
|
|
}
|
|
|
|
var i, len;
|
|
var xPositions = [];
|
|
var yPositions = [];
|
|
|
|
for (i = 0, len = elements.length; i < len; ++i) {
|
|
var el = elements[i];
|
|
if (el && el.hasValue()){
|
|
var pos = el.tooltipPosition();
|
|
xPositions.push(pos.x);
|
|
yPositions.push(pos.y);
|
|
}
|
|
}
|
|
|
|
var x = 0,
|
|
y = 0;
|
|
for (i = 0; i < xPositions.length; ++i) {
|
|
if (xPositions[ i ]) {
|
|
x += xPositions[i];
|
|
y += yPositions[i];
|
|
}
|
|
}
|
|
|
|
return {
|
|
x: Math.round(x / xPositions.length),
|
|
y: Math.round(y / xPositions.length)
|
|
};
|
|
}
|
|
|
|
// Private helper to create a tooltip iteam model
|
|
// @param element : the chart element (point, arc, bar) to create the tooltip item for
|
|
// @return : new tooltip item
|
|
function createTooltipItem(element) {
|
|
var xScale = element._xScale;
|
|
var yScale = element._yScale || element._scale; // handle radar || polarArea charts
|
|
var index = element._index,
|
|
datasetIndex = element._datasetIndex;
|
|
|
|
return {
|
|
xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
|
|
yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
|
|
index: index,
|
|
datasetIndex: datasetIndex
|
|
};
|
|
}
|
|
|
|
Chart.Tooltip = Chart.Element.extend({
|
|
initialize: function() {
|
|
var me = this;
|
|
var globalDefaults = Chart.defaults.global;
|
|
var tooltipOpts = me._options;
|
|
var getValueOrDefault = helpers.getValueOrDefault;
|
|
|
|
helpers.extend(me, {
|
|
_model: {
|
|
// Positioning
|
|
xPadding: tooltipOpts.xPadding,
|
|
yPadding: tooltipOpts.yPadding,
|
|
xAlign : tooltipOpts.xAlign,
|
|
yAlign : tooltipOpts.yAlign,
|
|
|
|
// Body
|
|
bodyFontColor: tooltipOpts.bodyFontColor,
|
|
_bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
|
|
_bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
|
|
_bodyAlign: tooltipOpts.bodyAlign,
|
|
bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
|
|
bodySpacing: tooltipOpts.bodySpacing,
|
|
|
|
// Title
|
|
titleFontColor: tooltipOpts.titleFontColor,
|
|
_titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
|
|
_titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
|
|
titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
|
|
_titleAlign: tooltipOpts.titleAlign,
|
|
titleSpacing: tooltipOpts.titleSpacing,
|
|
titleMarginBottom: tooltipOpts.titleMarginBottom,
|
|
|
|
// Footer
|
|
footerFontColor: tooltipOpts.footerFontColor,
|
|
_footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
|
|
_footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
|
|
footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
|
|
_footerAlign: tooltipOpts.footerAlign,
|
|
footerSpacing: tooltipOpts.footerSpacing,
|
|
footerMarginTop: tooltipOpts.footerMarginTop,
|
|
|
|
// Appearance
|
|
caretSize: tooltipOpts.caretSize,
|
|
cornerRadius: tooltipOpts.cornerRadius,
|
|
backgroundColor: tooltipOpts.backgroundColor,
|
|
opacity: 0,
|
|
legendColorBackground: tooltipOpts.multiKeyBackground
|
|
}
|
|
});
|
|
},
|
|
|
|
// Get the title
|
|
// Args are: (tooltipItem, data)
|
|
getTitle: function() {
|
|
var me = this;
|
|
var opts = me._options;
|
|
var callbacks = opts.callbacks;
|
|
|
|
var beforeTitle = callbacks.beforeTitle.apply(me, arguments),
|
|
title = callbacks.title.apply(me, arguments),
|
|
afterTitle = callbacks.afterTitle.apply(me, arguments);
|
|
|
|
var lines = [];
|
|
lines = pushOrConcat(lines, beforeTitle);
|
|
lines = pushOrConcat(lines, title);
|
|
lines = pushOrConcat(lines, afterTitle);
|
|
|
|
return lines;
|
|
},
|
|
|
|
// Args are: (tooltipItem, data)
|
|
getBeforeBody: function() {
|
|
var lines = this._options.callbacks.beforeBody.apply(this, arguments);
|
|
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
|
|
},
|
|
|
|
// Args are: (tooltipItem, data)
|
|
getBody: function(tooltipItems, data) {
|
|
var me = this;
|
|
var callbacks = me._options.callbacks;
|
|
var bodyItems = [];
|
|
|
|
helpers.each(tooltipItems, function(tooltipItem) {
|
|
var bodyItem = {
|
|
before: [],
|
|
lines: [],
|
|
after: []
|
|
};
|
|
pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));
|
|
pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
|
|
pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));
|
|
|
|
bodyItems.push(bodyItem);
|
|
});
|
|
|
|
return bodyItems;
|
|
},
|
|
|
|
// Args are: (tooltipItem, data)
|
|
getAfterBody: function() {
|
|
var lines = this._options.callbacks.afterBody.apply(this, arguments);
|
|
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
|
|
},
|
|
|
|
// Get the footer and beforeFooter and afterFooter lines
|
|
// Args are: (tooltipItem, data)
|
|
getFooter: function() {
|
|
var me = this;
|
|
var callbacks = me._options.callbacks;
|
|
|
|
var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
|
|
var footer = callbacks.footer.apply(me, arguments);
|
|
var afterFooter = callbacks.afterFooter.apply(me, arguments);
|
|
|
|
var lines = [];
|
|
lines = pushOrConcat(lines, beforeFooter);
|
|
lines = pushOrConcat(lines, footer);
|
|
lines = pushOrConcat(lines, afterFooter);
|
|
|
|
return lines;
|
|
},
|
|
|
|
update: function(changed) {
|
|
var me = this;
|
|
var opts = me._options;
|
|
var model = me._model;
|
|
var active = me._active;
|
|
|
|
var data = me._data;
|
|
var chartInstance = me._chartInstance;
|
|
|
|
var i, len;
|
|
|
|
if (active.length) {
|
|
model.opacity = 1;
|
|
|
|
var labelColors = [],
|
|
tooltipPosition = getAveragePosition(active);
|
|
|
|
var tooltipItems = [];
|
|
for (i = 0, len = active.length; i < len; ++i) {
|
|
tooltipItems.push(createTooltipItem(active[i]));
|
|
}
|
|
|
|
// If the user provided a sorting function, use it to modify the tooltip items
|
|
if (opts.itemSort) {
|
|
tooltipItems = tooltipItems.sort(function(a,b) {
|
|
return opts.itemSort(a,b, data);
|
|
});
|
|
}
|
|
|
|
// If there is more than one item, show color items
|
|
if (active.length > 1) {
|
|
helpers.each(tooltipItems, function(tooltipItem) {
|
|
labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance));
|
|
});
|
|
}
|
|
|
|
// Build the Text Lines
|
|
helpers.extend(model, {
|
|
title: me.getTitle(tooltipItems, data),
|
|
beforeBody: me.getBeforeBody(tooltipItems, data),
|
|
body: me.getBody(tooltipItems, data),
|
|
afterBody: me.getAfterBody(tooltipItems, data),
|
|
footer: me.getFooter(tooltipItems, data),
|
|
x: Math.round(tooltipPosition.x),
|
|
y: Math.round(tooltipPosition.y),
|
|
caretPadding: helpers.getValueOrDefault(tooltipPosition.padding, 2),
|
|
labelColors: labelColors
|
|
});
|
|
|
|
// We need to determine alignment of
|
|
var tooltipSize = me.getTooltipSize(model);
|
|
me.determineAlignment(tooltipSize); // Smart Tooltip placement to stay on the canvas
|
|
|
|
helpers.extend(model, me.getBackgroundPoint(model, tooltipSize));
|
|
} else {
|
|
me._model.opacity = 0;
|
|
}
|
|
|
|
if (changed && opts.custom) {
|
|
opts.custom.call(me, model);
|
|
}
|
|
|
|
return me;
|
|
},
|
|
getTooltipSize: function(vm) {
|
|
var ctx = this._chart.ctx;
|
|
|
|
var size = {
|
|
height: vm.yPadding * 2, // Tooltip Padding
|
|
width: 0
|
|
};
|
|
|
|
// Count of all lines in the body
|
|
var body = vm.body;
|
|
var combinedBodyLength = body.reduce(function(count, bodyItem) {
|
|
return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
|
|
}, 0);
|
|
combinedBodyLength += vm.beforeBody.length + vm.afterBody.length;
|
|
|
|
var titleLineCount = vm.title.length;
|
|
var footerLineCount = vm.footer.length;
|
|
var titleFontSize = vm.titleFontSize,
|
|
bodyFontSize = vm.bodyFontSize,
|
|
footerFontSize = vm.footerFontSize;
|
|
|
|
size.height += titleLineCount * titleFontSize; // Title Lines
|
|
size.height += (titleLineCount - 1) * vm.titleSpacing; // Title Line Spacing
|
|
size.height += titleLineCount ? vm.titleMarginBottom : 0; // Title's bottom Margin
|
|
size.height += combinedBodyLength * bodyFontSize; // Body Lines
|
|
size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing
|
|
size.height += footerLineCount ? vm.footerMarginTop : 0; // Footer Margin
|
|
size.height += footerLineCount * (footerFontSize); // Footer Lines
|
|
size.height += footerLineCount ? (footerLineCount - 1) * vm.footerSpacing : 0; // Footer Line Spacing
|
|
|
|
// Title width
|
|
var widthPadding = 0;
|
|
var maxLineWidth = function(line) {
|
|
size.width = Math.max(size.width, ctx.measureText(line).width + widthPadding);
|
|
};
|
|
|
|
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
|
|
helpers.each(vm.title, maxLineWidth);
|
|
|
|
// Body width
|
|
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
|
|
helpers.each(vm.beforeBody.concat(vm.afterBody), maxLineWidth);
|
|
|
|
// Body lines may include some extra width due to the color box
|
|
widthPadding = body.length > 1 ? (bodyFontSize + 2) : 0;
|
|
helpers.each(body, function(bodyItem) {
|
|
helpers.each(bodyItem.before, maxLineWidth);
|
|
helpers.each(bodyItem.lines, maxLineWidth);
|
|
helpers.each(bodyItem.after, maxLineWidth);
|
|
});
|
|
|
|
// Reset back to 0
|
|
widthPadding = 0;
|
|
|
|
// Footer width
|
|
ctx.font = helpers.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
|
|
helpers.each(vm.footer, maxLineWidth);
|
|
|
|
// Add padding
|
|
size.width += 2 * vm.xPadding;
|
|
|
|
return size;
|
|
},
|
|
determineAlignment: function(size) {
|
|
var me = this;
|
|
var model = me._model;
|
|
var chart = me._chart;
|
|
var chartArea = me._chartInstance.chartArea;
|
|
|
|
if (model.y < size.height) {
|
|
model.yAlign = 'top';
|
|
} else if (model.y > (chart.height - size.height)) {
|
|
model.yAlign = 'bottom';
|
|
}
|
|
|
|
var lf, rf; // functions to determine left, right alignment
|
|
var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
|
|
var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
|
|
var midX = (chartArea.left + chartArea.right) / 2;
|
|
var midY = (chartArea.top + chartArea.bottom) / 2;
|
|
|
|
if (model.yAlign === 'center') {
|
|
lf = function(x) {
|
|
return x <= midX;
|
|
};
|
|
rf = function(x) {
|
|
return x > midX;
|
|
};
|
|
} else {
|
|
lf = function(x) {
|
|
return x <= (size.width / 2);
|
|
};
|
|
rf = function(x) {
|
|
return x >= (chart.width - (size.width / 2));
|
|
};
|
|
}
|
|
|
|
olf = function(x) {
|
|
return x + size.width > chart.width;
|
|
};
|
|
orf = function(x) {
|
|
return x - size.width < 0;
|
|
};
|
|
yf = function(y) {
|
|
return y <= midY ? 'top' : 'bottom';
|
|
};
|
|
|
|
if (lf(model.x)) {
|
|
model.xAlign = 'left';
|
|
|
|
// Is tooltip too wide and goes over the right side of the chart.?
|
|
if (olf(model.x)) {
|
|
model.xAlign = 'center';
|
|
model.yAlign = yf(model.y);
|
|
}
|
|
} else if (rf(model.x)) {
|
|
model.xAlign = 'right';
|
|
|
|
// Is tooltip too wide and goes outside left edge of canvas?
|
|
if (orf(model.x)) {
|
|
model.xAlign = 'center';
|
|
model.yAlign = yf(model.y);
|
|
}
|
|
}
|
|
},
|
|
getBackgroundPoint: function(vm, size) {
|
|
// Background Position
|
|
var pt = {
|
|
x: vm.x,
|
|
y: vm.y
|
|
};
|
|
|
|
var caretSize = vm.caretSize,
|
|
caretPadding = vm.caretPadding,
|
|
cornerRadius = vm.cornerRadius,
|
|
xAlign = vm.xAlign,
|
|
yAlign = vm.yAlign,
|
|
paddingAndSize = caretSize + caretPadding,
|
|
radiusAndPadding = cornerRadius + caretPadding;
|
|
|
|
if (xAlign === 'right') {
|
|
pt.x -= size.width;
|
|
} else if (xAlign === 'center') {
|
|
pt.x -= (size.width / 2);
|
|
}
|
|
|
|
if (yAlign === 'top') {
|
|
pt.y += paddingAndSize;
|
|
} else if (yAlign === 'bottom') {
|
|
pt.y -= size.height + paddingAndSize;
|
|
} else {
|
|
pt.y -= (size.height / 2);
|
|
}
|
|
|
|
if (yAlign === 'center') {
|
|
if (xAlign === 'left') {
|
|
pt.x += paddingAndSize;
|
|
} else if (xAlign === 'right') {
|
|
pt.x -= paddingAndSize;
|
|
}
|
|
} else {
|
|
if (xAlign === 'left') {
|
|
pt.x -= radiusAndPadding;
|
|
} else if (xAlign === 'right') {
|
|
pt.x += radiusAndPadding;
|
|
}
|
|
}
|
|
|
|
return pt;
|
|
},
|
|
drawCaret: function(tooltipPoint, size, opacity) {
|
|
var vm = this._view;
|
|
var ctx = this._chart.ctx;
|
|
var x1, x2, x3;
|
|
var y1, y2, y3;
|
|
var caretSize = vm.caretSize;
|
|
var cornerRadius = vm.cornerRadius;
|
|
var xAlign = vm.xAlign,
|
|
yAlign = vm.yAlign;
|
|
var ptX = tooltipPoint.x,
|
|
ptY = tooltipPoint.y;
|
|
var width = size.width,
|
|
height = size.height;
|
|
|
|
if (yAlign === 'center') {
|
|
// Left or right side
|
|
if (xAlign === 'left') {
|
|
x1 = ptX;
|
|
x2 = x1 - caretSize;
|
|
x3 = x1;
|
|
} else {
|
|
x1 = ptX + width;
|
|
x2 = x1 + caretSize;
|
|
x3 = x1;
|
|
}
|
|
|
|
y2 = ptY + (height / 2);
|
|
y1 = y2 - caretSize;
|
|
y3 = y2 + caretSize;
|
|
} else {
|
|
if (xAlign === 'left') {
|
|
x1 = ptX + cornerRadius;
|
|
x2 = x1 + caretSize;
|
|
x3 = x2 + caretSize;
|
|
} else if (xAlign === 'right') {
|
|
x1 = ptX + width - cornerRadius;
|
|
x2 = x1 - caretSize;
|
|
x3 = x2 - caretSize;
|
|
} else {
|
|
x2 = ptX + (width / 2);
|
|
x1 = x2 - caretSize;
|
|
x3 = x2 + caretSize;
|
|
}
|
|
|
|
if (yAlign === 'top') {
|
|
y1 = ptY;
|
|
y2 = y1 - caretSize;
|
|
y3 = y1;
|
|
} else {
|
|
y1 = ptY + height;
|
|
y2 = y1 + caretSize;
|
|
y3 = y1;
|
|
}
|
|
}
|
|
|
|
var bgColor = helpers.color(vm.backgroundColor);
|
|
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x1, y1);
|
|
ctx.lineTo(x2, y2);
|
|
ctx.lineTo(x3, y3);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
},
|
|
drawTitle: function(pt, vm, ctx, opacity) {
|
|
var title = vm.title;
|
|
|
|
if (title.length) {
|
|
ctx.textAlign = vm._titleAlign;
|
|
ctx.textBaseline = "top";
|
|
|
|
var titleFontSize = vm.titleFontSize,
|
|
titleSpacing = vm.titleSpacing;
|
|
|
|
var titleFontColor = helpers.color(vm.titleFontColor);
|
|
ctx.fillStyle = titleFontColor.alpha(opacity * titleFontColor.alpha()).rgbString();
|
|
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
|
|
|
|
var i, len;
|
|
for (i = 0, len = title.length; i < len; ++i) {
|
|
ctx.fillText(title[i], pt.x, pt.y);
|
|
pt.y += titleFontSize + titleSpacing; // Line Height and spacing
|
|
|
|
if (i + 1 === title.length) {
|
|
pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
|
|
}
|
|
}
|
|
}
|
|
},
|
|
drawBody: function(pt, vm, ctx, opacity) {
|
|
var bodyFontSize = vm.bodyFontSize;
|
|
var bodySpacing = vm.bodySpacing;
|
|
var body = vm.body;
|
|
|
|
ctx.textAlign = vm._bodyAlign;
|
|
ctx.textBaseline = "top";
|
|
|
|
var bodyFontColor = helpers.color(vm.bodyFontColor);
|
|
var textColor = bodyFontColor.alpha(opacity * bodyFontColor.alpha()).rgbString();
|
|
ctx.fillStyle = textColor;
|
|
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
|
|
|
|
// Before Body
|
|
var xLinePadding = 0;
|
|
var fillLineOfText = function(line) {
|
|
ctx.fillText(line, pt.x + xLinePadding, pt.y);
|
|
pt.y += bodyFontSize + bodySpacing;
|
|
};
|
|
|
|
// Before body lines
|
|
helpers.each(vm.beforeBody, fillLineOfText);
|
|
|
|
var drawColorBoxes = body.length > 1;
|
|
xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
|
|
|
|
// Draw body lines now
|
|
helpers.each(body, function(bodyItem, i) {
|
|
helpers.each(bodyItem.before, fillLineOfText);
|
|
|
|
helpers.each(bodyItem.lines, function(line) {
|
|
// Draw Legend-like boxes if needed
|
|
if (drawColorBoxes) {
|
|
// Fill a white rect so that colours merge nicely if the opacity is < 1
|
|
ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString();
|
|
ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
|
|
|
|
// Border
|
|
ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString();
|
|
ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
|
|
|
|
// Inner square
|
|
ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString();
|
|
ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
|
|
|
|
ctx.fillStyle = textColor;
|
|
}
|
|
|
|
fillLineOfText(line);
|
|
});
|
|
|
|
helpers.each(bodyItem.after, fillLineOfText);
|
|
});
|
|
|
|
// Reset back to 0 for after body
|
|
xLinePadding = 0;
|
|
|
|
// After body lines
|
|
helpers.each(vm.afterBody, fillLineOfText);
|
|
pt.y -= bodySpacing; // Remove last body spacing
|
|
},
|
|
drawFooter: function(pt, vm, ctx, opacity) {
|
|
var footer = vm.footer;
|
|
|
|
if (footer.length) {
|
|
pt.y += vm.footerMarginTop;
|
|
|
|
ctx.textAlign = vm._footerAlign;
|
|
ctx.textBaseline = "top";
|
|
|
|
var footerFontColor = helpers.color(vm.footerFontColor);
|
|
ctx.fillStyle = footerFontColor.alpha(opacity * footerFontColor.alpha()).rgbString();
|
|
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
|
|
|
|
helpers.each(footer, function(line) {
|
|
ctx.fillText(line, pt.x, pt.y);
|
|
pt.y += vm.footerFontSize + vm.footerSpacing;
|
|
});
|
|
}
|
|
},
|
|
draw: function() {
|
|
var ctx = this._chart.ctx;
|
|
var vm = this._view;
|
|
|
|
if (vm.opacity === 0) {
|
|
return;
|
|
}
|
|
|
|
var tooltipSize = this.getTooltipSize(vm);
|
|
var pt = {
|
|
x: vm.x,
|
|
y: vm.y
|
|
};
|
|
|
|
// IE11/Edge does not like very small opacities, so snap to 0
|
|
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
|
|
|
|
if (this._options.enabled) {
|
|
// Draw Background
|
|
var bgColor = helpers.color(vm.backgroundColor);
|
|
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
|
|
helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
|
|
ctx.fill();
|
|
|
|
// Draw Caret
|
|
this.drawCaret(pt, tooltipSize, opacity);
|
|
|
|
// Draw Title, Body, and Footer
|
|
pt.x += vm.xPadding;
|
|
pt.y += vm.yPadding;
|
|
|
|
// Titles
|
|
this.drawTitle(pt, vm, ctx, opacity);
|
|
|
|
// Body
|
|
this.drawBody(pt, vm, ctx, opacity);
|
|
|
|
// Footer
|
|
this.drawFooter(pt, vm, ctx, opacity);
|
|
}
|
|
}
|
|
});
|
|
};
|