diff --git a/docs/00-Getting-Started.md b/docs/00-Getting-Started.md
index cbbb5ba29c5..39db54b3792 100644
--- a/docs/00-Getting-Started.md
+++ b/docs/00-Getting-Started.md
@@ -101,8 +101,22 @@ Chart.defaults.global = {
// Element defaults defined in element extensions
elements: {},
- // Legend template string
- legendTemplate: "
+
@@ -59,6 +59,35 @@
Legend
},
options: {
responsive: true,
+ tooltips: {
+ mode: 'label',
+ callbacks: {
+ // beforeTitle: function() {
+ // return '...beforeTitle';
+ // },
+ // afterTitle: function() {
+ // return '...afterTitle';
+ // },
+ // beforeBody: function() {
+ // return '...beforeBody';
+ // },
+ // afterBody: function() {
+ // return '...afterBody';
+ // },
+ // beforeFooter: function() {
+ // return '...beforeFooter';
+ // },
+ // footer: function() {
+ // return 'Footer';
+ // },
+ // afterFooter: function() {
+ // return '...afterFooter';
+ // },
+ }
+ },
+ hover: {
+ mode: 'label'
+ },
scales: {
xAxes: [{
display: true,
diff --git a/samples/tooltip-hooks.html b/samples/tooltip-hooks.html
new file mode 100644
index 00000000000..5b8ef3fa8aa
--- /dev/null
+++ b/samples/tooltip-hooks.html
@@ -0,0 +1,191 @@
+
+
+
+
+
Line Chart
+
+
+
+
+
+
+
+
+
+
+
+
Randomize Data
+
Add Dataset
+
Remove Dataset
+
Add Data
+
Remove Data
+
+
+
+
+
diff --git a/src/core/core.controller.js b/src/core/core.controller.js
index 8cb9bcd2d48..13695081a09 100644
--- a/src/core/core.controller.js
+++ b/src/core/core.controller.js
@@ -327,7 +327,7 @@
},
generateLegend: function generateLegend() {
- return helpers.template(this.options.legendTemplate, this);
+ return this.options.legendCallback(this);
},
destroy: function destroy() {
@@ -371,10 +371,11 @@
},
eventHandler: function eventHandler(e) {
this.lastActive = this.lastActive || [];
+ this.lastTooltipActive = this.lastTooltipActive || [];
- // Find Active Elements
+ // Find Active Elements for hover and tooltips
if (e.type == 'mouseout') {
- this.active = [];
+ this.active = this.tooltipActive = [];
} else {
this.active = function() {
switch (this.options.hover.mode) {
@@ -388,6 +389,18 @@
return e;
}
}.call(this);
+ this.tooltipActive = function() {
+ switch (this.options.tooltips.mode) {
+ case 'single':
+ return this.getElementAtEvent(e);
+ case 'label':
+ return this.getElementsAtEvent(e);
+ case 'dataset':
+ return this.getDatasetAtEvent(e);
+ default:
+ return e;
+ }
+ }.call(this);
}
// On Hover hook
@@ -403,6 +416,7 @@
var dataset;
var index;
+
// Remove styling for last active (even if it may still be active)
if (this.lastActive.length) {
switch (this.options.hover.mode) {
@@ -445,11 +459,11 @@
this.tooltip.initialize();
// Active
- if (this.active.length) {
+ if (this.tooltipActive.length) {
this.tooltip._model.opacity = 1;
helpers.extend(this.tooltip, {
- _active: this.active,
+ _active: this.tooltipActive,
});
this.tooltip.update();
@@ -471,10 +485,19 @@
}
}, this);
+ helpers.each(this.tooltipActive, function(element, index) {
+ if (element !== this.lastTooltipActive[index]) {
+ changed = true;
+ }
+ }, this);
+
// If entering, leaving, or changing elements, animate the change via pivot
if ((!this.lastActive.length && this.active.length) ||
(this.lastActive.length && !this.active.length) ||
- (this.lastActive.length && this.active.length && changed)) {
+ (this.lastActive.length && this.active.length && changed) ||
+ (!this.lastTooltipActive.length && this.tooltipActive.length) ||
+ (this.lastTooltipActive.length && !this.tooltipActive.length) ||
+ (this.lastTooltipActive.length && this.tooltipActive.length && changed)) {
this.stop();
@@ -484,8 +507,9 @@
}
}
- // Remember Last Active
+ // Remember Last Actives
this.lastActive = this.active;
+ this.lastTooltipActive = this.tooltipActive;
return this;
},
});
diff --git a/src/core/core.helpers.js b/src/core/core.helpers.js
index ca27c7c3c85..9e1272ce290 100644
--- a/src/core/core.helpers.js
+++ b/src/core/core.helpers.js
@@ -382,57 +382,6 @@
return niceFraction * Math.pow(10, exponent);
},
- /* jshint ignore:start */
- // Blows up jshint errors based on the new Function constructor
- //Templating methods
- //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
- templateStringCache = {},
- template = helpers.template = function(templateString, valuesObject) {
-
- // If templateString is function rather than string-template - call the function for valuesObject
-
- if (templateString instanceof Function) {
- return templateString(valuesObject);
- }
-
- function tmpl(str, data) {
- // Figure out if we're getting a template, or if we need to
- // load the template - and be sure to cache the result.
- var fn;
-
- if (templateStringCache.hasOwnProperty(str)) {
- fn = templateStringCache[str];
- } else {
- // Generate a reusable function that will serve as a template
- // generator (and which will be cached).
- var functionCode = "var p=[],print=function(){p.push.apply(p,arguments);};" +
-
- // Introduce the data as local variables using with(){}
- "with(obj){p.push('" +
-
- // Convert the template into pure JavaScript
- str
- .replace(/[\r\t\n]/g, " ")
- .split("<%").join("\t")
- .replace(/((^|%>)[^\t]*)'/g, "$1\r")
- .replace(/\t=(.*?)%>/g, "',$1,'")
- .split("\t").join("');")
- .split("%>").join("p.push('")
- .split("\r").join("\\'") +
- "');}return p.join('');";
- fn = new Function("obj", functionCode);
-
- // Cache the result
- templateStringCache[str] = fn;
- }
-
- // Provide some basic currying to the user
- return data ? fn(data) : fn;
- }
- return tmpl(templateString, valuesObject);
- },
- /* jshint ignore:end */
- //--Animation methods
//Easing functions adapted from Robert Penner's easing equations
//http://www.robertpenner.com/easing/
easingEffects = helpers.easingEffects = {
diff --git a/src/core/core.js b/src/core/core.js
index fb84f0e7304..922a845f104 100755
--- a/src/core/core.js
+++ b/src/core/core.js
@@ -91,8 +91,21 @@
// Element defaults defined in element extensions
elements: {},
- // Legend template string
- legendTemplate: "
-legend\"><% for (var i = 0; i < data.datasets.length; i++){%>\"><%if(data.datasets[i].label){%><%=data.datasets[i].label%><%}%> <%}%> ",
+ // Legend callback string
+ legendCallback: function(chart) {
+ var text = [];
+ text.push('
');
+ for (var i = 0; i < chart.data.datasets.length; i++) {
+ text.push('');
+ if (chart.data.datasets[i].label) {
+ text.push(chart.data.datasets[i].label);
+ }
+ text.push(' ');
+ }
+ text.push(' ');
+
+ return text.join("");
+ }
},
};
diff --git a/src/core/core.scale.js b/src/core/core.scale.js
index 3a1373efa92..1338011500c 100644
--- a/src/core/core.scale.js
+++ b/src/core/core.scale.js
@@ -47,7 +47,9 @@
padding: 10,
reverse: false,
show: true,
- template: "<%=value%>",
+ callback: function(value) {
+ return '' + value;
+ },
},
};
@@ -133,14 +135,12 @@
convertTicksToLabels: function() {
// Convert ticks to strings
this.ticks = this.ticks.map(function(numericalTick, index, ticks) {
- if (this.options.ticks.userCallback) {
- return this.options.ticks.userCallback(numericalTick, index, ticks);
- } else {
- return helpers.template(this.options.ticks.template, {
- value: numericalTick
- });
- }
- }, this);
+ if (this.options.ticks.userCallback) {
+ return this.options.ticks.userCallback(numericalTick, index, ticks);
+ }
+ return this.options.ticks.callback(numericalTick);
+ },
+ this);
},
afterTickToLabelConversion: helpers.noop,
@@ -242,13 +242,13 @@
}
// Are we showing a title for the scale?
- if (this.options.scaleLabel.show) {
- if (this.isHorizontal()) {
- this.minSize.height += (this.options.scaleLabel.fontSize * 1.5);
- } else {
- this.minSize.width += (this.options.scaleLabel.fontSize * 1.5);
- }
- }
+ if (this.options.scaleLabel.show) {
+ if (this.isHorizontal()) {
+ this.minSize.height += (this.options.scaleLabel.fontSize * 1.5);
+ } else {
+ this.minSize.width += (this.options.scaleLabel.fontSize * 1.5);
+ }
+ }
if (this.options.ticks.show && this.options.display) {
// Don't bother fitting the ticks if we are not showing them
@@ -315,6 +315,15 @@
isHorizontal: function() {
return this.options.position == "top" || this.options.position == "bottom";
},
+
+ // Get the correct value. If the value type is object get the x or y based on whether we are horizontal or not
+ getRightValue: function(rawValue) {
+ return (typeof(rawValue) === "object" && rawValue !== null) ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
+ },
+
+ // Used to get the value to display in the tooltip for the data at the given index
+ // function getLabelForIndex(index, datasetIndex)
+ getLabelForIndex: helpers.noop,
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: helpers.noop,
@@ -344,7 +353,7 @@
return this.left + Math.round(valueOffset);
} else {
- return this.top + (decimal * (this.height / this.ticks.length));
+ return this.top + (decimal * this.height);
}
},
@@ -503,7 +512,7 @@
}
}
-
+
this.ctx.translate(xLabelValue, yLabelValue);
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
this.ctx.font = labelFont;
diff --git a/src/core/core.tooltip.js b/src/core/core.tooltip.js
index 92e2b136463..35760763710 100644
--- a/src/core/core.tooltip.js
+++ b/src/core/core.tooltip.js
@@ -9,35 +9,71 @@
Chart.defaults.global.tooltips = {
enabled: true,
custom: null,
+ mode: 'single',
backgroundColor: "rgba(0,0,0,0.8)",
- fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
- fontSize: 10,
- fontStyle: "normal",
- fontColor: "#fff",
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
titleFontSize: 12,
titleFontStyle: "bold",
- titleFontColor: "#fff",
+ titleSpacing: 2,
+ titleMarginBottom: 6,
+ titleColor: "#fff",
+ titleAlign: "left",
+ bodyFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+ bodyFontSize: 12,
+ bodyFontStyle: "normal",
+ bodySpacing: 2,
+ bodyColor: "#fff",
+ bodyAlign: "left",
+ footerFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+ footerFontSize: 12,
+ footerFontStyle: "bold",
+ footerSpacing: 2,
+ footerMarginTop: 6,
+ footerColor: "#fff",
+ footerAlign: "left",
yPadding: 6,
xPadding: 6,
- caretSize: 8,
+ caretSize: 5,
cornerRadius: 6,
xOffset: 10,
- template: [
- '<% if(label){ %>',
- '<%=label %>: ',
- '<% } %>',
- '<%=value %>',
- ].join(''),
- multiTemplate: [
- '<%if (datasetLabel){ %>',
- '<%=datasetLabel %>: ',
- '<% } %>',
- '<%=value %>'
- ].join(''),
multiKeyBackground: '#fff',
+ callbacks: {
+ beforeTitle: helpers.noop,
+ title: function(xLabel, yLabel, index, datasetIndex, data) {
+ // Pick first label for now
+ return helpers.isArray(xLabel) ? xLabel[0] : xLabel;
+ },
+ afterTitle: helpers.noop,
+
+ beforeBody: helpers.noop,
+
+ beforeLabel: helpers.noop,
+ label: function(xLabel, yLabel, index, datasetIndex, data) {
+ return this._data.datasets[datasetIndex].label + ': ' + yLabel;
+ },
+ afterLabel: helpers.noop,
+
+ afterBody: helpers.noop,
+
+ 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);
+ } else {
+ base.push(toPush);
+ }
+ }
+
+ return base;
+ }
+
Chart.Tooltip = Chart.Element.extend({
initialize: function() {
var options = this._options;
@@ -48,20 +84,34 @@
yPadding: options.tooltips.yPadding,
xOffset: options.tooltips.xOffset,
- // Labels
- textColor: options.tooltips.fontColor,
- _fontFamily: options.tooltips.fontFamily,
- _fontStyle: options.tooltips.fontStyle,
- fontSize: options.tooltips.fontSize,
+ // Body
+ bodyColor: options.tooltips.bodyColor,
+ _bodyFontFamily: options.tooltips.bodyFontFamily,
+ _bodyFontStyle: options.tooltips.bodyFontStyle,
+ bodyFontSize: options.tooltips.bodyFontSize,
+ bodySpacing: options.tooltips.bodySpacing,
+ _bodposition: options.tooltips.bodposition,
// Title
- titleTextColor: options.tooltips.titleFontColor,
+ titleColor: options.tooltips.titleColor,
_titleFontFamily: options.tooltips.titleFontFamily,
_titleFontStyle: options.tooltips.titleFontStyle,
titleFontSize: options.tooltips.titleFontSize,
+ _titleAlign: options.tooltips.titleAlign,
+ titleSpacing: options.tooltips.titleSpacing,
+ titleMarginBottom: options.tooltips.titleMarginBottom,
+
+ // Footer
+ footerColor: options.tooltips.footerColor,
+ _footerFontFamily: options.tooltips.footerFontFamily,
+ _footerFontStyle: options.tooltips.footerFontStyle,
+ footerFontSize: options.tooltips.footerFontSize,
+ _footerAlign: options.tooltips.footerAlign,
+ footerSpacing: options.tooltips.footerSpacing,
+ footerMarginTop: options.tooltips.footerMarginTop,
// Appearance
- caretHeight: options.tooltips.caretSize,
+ caretSize: options.tooltips.caretSize,
cornerRadius: options.tooltips.cornerRadius,
backgroundColor: options.tooltips.backgroundColor,
opacity: 0,
@@ -69,141 +119,145 @@
},
});
},
- update: function() {
- var ctx = this._chart.ctx;
+ // Get the title
+ getTitle: function() {
+ var beforeTitle = this._options.tooltips.callbacks.beforeTitle.apply(this, arguments),
+ title = this._options.tooltips.callbacks.title.apply(this, arguments),
+ afterTitle = this._options.tooltips.callbacks.afterTitle.apply(this, arguments);
- switch (this._options.hover.mode) {
- case 'single':
- helpers.extend(this._model, {
- text: helpers.template(this._options.tooltips.template, {
- // These variables are available in the template function. Add others here
- element: this._active[0],
- value: this._data.datasets[this._active[0]._datasetIndex].data[this._active[0]._index],
- label: this._active[0]._model.label !== undefined ? this._active[0]._model.label : this._data.labels ? this._data.labels[this._active[0]._index] : '',
- }),
- });
+ var lines = [];
+ lines = pushOrConcat(lines, beforeTitle);
+ lines = pushOrConcat(lines, title);
+ lines = pushOrConcat(lines, afterTitle);
- var tooltipPosition = this._active[0].tooltipPosition();
- helpers.extend(this._model, {
- x: Math.round(tooltipPosition.x),
- y: Math.round(tooltipPosition.y),
- caretPadding: tooltipPosition.padding
- });
+ return lines;
+ },
- break;
+ getBeforeBody: function(xLabel, yLabel, index, datasetIndex, data) {
+ var lines = this._options.tooltips.callbacks.beforeBody.call(this, xLabel, yLabel, index, datasetIndex, data);
+ return helpers.isArray(lines) ? lines : [lines];
+ },
- case 'label':
+ getBody: function(xLabel, yLabel, index, datasetIndex) {
- // Tooltip Content
+ var lines = [];
- var dataArray,
- dataIndex;
+ var beforeLabel,
+ afterLabel,
+ label;
- var labels = [],
- colors = [];
+ if (helpers.isArray(xLabel)) {
- for (var i = this._data.datasets.length - 1; i >= 0; i--) {
- dataArray = this._data.datasets[i].metaData;
- dataIndex = helpers.indexOf(dataArray, this._active[0]);
- if (dataIndex !== -1) {
- break;
- }
- }
+ var labels = [];
- var medianPosition = (function(index) {
- // Get all the points at that particular index
- var elements = [],
- dataCollection,
- xPositions = [],
- yPositions = [],
- xMax,
- yMax,
- xMin,
- yMin;
- helpers.each(this._data.datasets, function(dataset) {
- dataCollection = dataset.metaData;
- if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()) {
- elements.push(dataCollection[dataIndex]);
- }
- }, this);
-
- // Reverse labels if stacked
- helpers.each(this._options.stacked ? elements.reverse() : elements, function(element) {
- xPositions.push(element._view.x);
- yPositions.push(element._view.y);
-
- //Include any colour information about the element
- labels.push(helpers.template(this._options.tooltips.multiTemplate, {
- // These variables are available in the template function. Add others here
- element: element,
- datasetLabel: this._data.datasets[element._datasetIndex].label,
- value: this._data.datasets[element._datasetIndex].data[element._index],
- }));
- colors.push({
- fill: element._view.backgroundColor,
- stroke: element._view.borderColor
- });
-
- }, this);
-
- yMin = helpers.min(yPositions);
- yMax = helpers.max(yPositions);
-
- xMin = helpers.min(xPositions);
- xMax = helpers.max(xPositions);
-
- return {
- x: (xMin > this._chart.width / 2) ? xMin : xMax,
- y: (yMin + yMax) / 2,
- };
- }).call(this, dataIndex);
-
- // Apply for now
- helpers.extend(this._model, {
- x: medianPosition.x,
- y: medianPosition.y,
- labels: labels,
- title: (function() {
- return this._data.timeLabels ? this._data.timeLabels[this._active[0]._index] :
- (this._data.labels && this._data.labels.length) ? this._data.labels[this._active[0]._index] :
- '';
- }).call(this),
- legendColors: colors,
- legendBackgroundColor: this._options.tooltips.multiKeyBackground,
- });
+ // Run EACH label pair through the label callback this time.
+ for (var i = 0; i < xLabel.length; i++) {
+ beforeLabel = this._options.tooltips.callbacks.beforeLabel.call(this, xLabel[i], yLabel[i], index, datasetIndex);
+ afterLabel = this._options.tooltips.callbacks.afterLabel.call(this, xLabel[i], yLabel[i], index, datasetIndex);
- // Calculate Appearance Tweaks
+ labels.push((beforeLabel ? beforeLabel : '') + this._options.tooltips.callbacks.label.call(this, xLabel[i], yLabel[i], index, datasetIndex) + (afterLabel ? afterLabel : ''));
- this._model.height = (labels.length * this._model.fontSize) + ((labels.length - 1) * (this._model.fontSize / 2)) + (this._model.yPadding * 2) + this._model.titleFontSize * 1.5;
+ }
- var titleWidth = ctx.measureText(this._model.title).width,
- //Label has a legend square as well so account for this.
- labelWidth = helpers.longestText(ctx, this.font, labels) + this._model.fontSize + 3,
- longestTextWidth = helpers.max([labelWidth, titleWidth]);
+ if (labels.length) {
+ lines = lines.concat(labels);
+ }
- this._model.width = longestTextWidth + (this._model.xPadding * 2);
+ } else {
+ // Run the single label through the callback
- var halfHeight = this._model.height / 2;
+ beforeLabel = this._options.tooltips.callbacks.beforeLabel.apply(this, arguments);
+ label = this._options.tooltips.callbacks.label.apply(this, arguments);
+ afterLabel = this._options.tooltips.callbacks.afterLabel.apply(this, arguments);
- //Check to ensure the height will fit on the canvas
- if (this._model.y - halfHeight < 0) {
- this._model.y = halfHeight;
- } else if (this._model.y + halfHeight > this._chart.height) {
- this._model.y = this._chart.height - halfHeight;
- }
+ if (beforeLabel || label || afterLabel) {
+ lines.push((beforeLabel ? afterLabel : '') + label + (afterLabel ? afterLabel : ''));
+ }
+ }
+
+ return lines;
+ },
+
+ getAfterBody: function(xLabel, yLabel, index, datasetIndex, data) {
+ var lines = this._options.tooltips.callbacks.afterBody.call(this, xLabel, yLabel, index, datasetIndex, data);
+ return helpers.isArray(lines) ? lines : [lines];
+ },
+
+ // Get the footer and beforeFooter and afterFooter lines
+ getFooter: function() {
+ var beforeFooter = this._options.tooltips.callbacks.beforeFooter.apply(this, arguments);
+ var footer = this._options.tooltips.callbacks.footer.apply(this, arguments);
+ var afterFooter = this._options.tooltips.callbacks.afterFooter.apply(this, arguments);
+
+ var lines = [];
+ lines = pushOrConcat(lines, beforeFooter);
+ lines = pushOrConcat(lines, footer);
+ lines = pushOrConcat(lines, afterFooter);
+
+ return lines;
+ },
- //Decide whether to align left or right based on position on canvas
- if (this._model.x > this._chart.width / 2) {
- this._model.x -= this._model.xOffset + this._model.width;
- } else {
- this._model.x += this._model.xOffset;
+ update: function() {
+
+ var ctx = this._chart.ctx;
+
+ var element = this._active[0],
+ xLabel,
+ yLabel,
+ labelColors = [],
+ tooltipPosition;
+
+ if (this._options.tooltips.mode == 'single') {
+
+ xLabel = element._xScale.getLabelForIndex(element._index, element._datasetIndex);
+ yLabel = element._yScale.getLabelForIndex(element._index, element._datasetIndex);
+ tooltipPosition = this._active[0].tooltipPosition();
+
+ } else {
+
+ xLabel = [];
+ yLabel = [];
+
+ helpers.each(this._data.datasets, function(dataset, datasetIndex) {
+ if (!helpers.isDatasetVisible(dataset)) {
+ return;
}
- break;
+ xLabel.push(element._xScale.getLabelForIndex(element._index, datasetIndex));
+ yLabel.push(element._yScale.getLabelForIndex(element._index, datasetIndex));
+ });
+
+ helpers.each(this._active, function(active, i) {
+ labelColors.push({
+ borderColor: active._view.borderColor,
+ backgroundColor: active._view.backgroundColor
+ });
+ }, this);
+
+ tooltipPosition = this._active[0].tooltipPosition();
+ tooltipPosition.y = this._active[0]._yScale.getPixelForDecimal(0.5);
+
}
+
+ // Build the Text Lines
+ helpers.extend(this._model, {
+ title: this.getTitle(xLabel, yLabel, element._index, element._datasetIndex, this._data),
+ beforeBody: this.getBeforeBody(xLabel, yLabel, element._index, element._datasetIndex, this._data),
+ body: this.getBody(xLabel, yLabel, element._index, element._datasetIndex, this._data),
+ afterBody: this.getAfterBody(xLabel, yLabel, element._index, element._datasetIndex, this._data),
+ footer: this.getFooter(xLabel, yLabel, element._index, element._datasetIndex, this._data),
+ });
+
+ helpers.extend(this._model, {
+ x: Math.round(tooltipPosition.x),
+ y: Math.round(tooltipPosition.y),
+ caretPadding: tooltipPosition.padding,
+ labelColors: labelColors,
+ });
+
return this;
},
draw: function() {
@@ -211,136 +265,203 @@
var ctx = this._chart.ctx;
var vm = this._view;
- switch (this._options.hover.mode) {
- case 'single':
+ if (this._view.opacity === 0) {
+ return;
+ }
- ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
+ // Get Dimensions
- vm.xAlign = "center";
- vm.yAlign = "above";
+ vm.position = "top";
- //Distance between the actual element.y position and the start of the tooltip caret
- var caretPadding = vm.caretPadding || 2;
+ var caretPadding = vm.caretPadding || 2;
- var tooltipWidth = ctx.measureText(vm.text).width + 2 * vm.xPadding,
- tooltipRectHeight = vm.fontSize + 2 * vm.yPadding,
- tooltipHeight = tooltipRectHeight + vm.caretHeight + caretPadding;
+ var combinedBodyLength = vm.body.length + vm.beforeBody.length + vm.afterBody.length;
- if (vm.x + tooltipWidth / 2 > this._chart.width) {
- vm.xAlign = "left";
- } else if (vm.x - tooltipWidth / 2 < 0) {
- vm.xAlign = "right";
- }
+ // Height
+ var tooltipHeight = vm.yPadding * 2; // Tooltip Padding
- if (vm.y - tooltipHeight < 0) {
- vm.yAlign = "below";
- }
+ tooltipHeight += vm.title.length * vm.titleFontSize; // Title Lines
+ tooltipHeight += (vm.title.length - 1) * vm.titleSpacing; // Title Line Spacing
+ tooltipHeight += vm.title.length ? vm.titleMarginBottom : 0; // Title's bottom Margin
- var tooltipX = vm.x - tooltipWidth / 2,
- tooltipY = vm.y - tooltipHeight;
+ tooltipHeight += combinedBodyLength * vm.bodyFontSize; // Body Lines
+ tooltipHeight += (combinedBodyLength - 1) * vm.bodySpacing; // Body Line Spacing
- ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
+ tooltipHeight += vm.footer.length ? vm.footerMarginTop : 0; // Footer Margin
+ tooltipHeight += vm.footer.length * (vm.footerFontSize); // Footer Lines
+ tooltipHeight += (vm.footer.length - 1) * vm.footerSpacing; // Footer Line Spacing
- // Custom Tooltips
- if (this._options.tooltips.custom) {
- this._options.tooltips.custom(this);
- }
- if (!this._options.tooltips.enabled) {
- return;
- }
+ // Width
+ var tooltipWidth = 0;
+ helpers.each(vm.title, function(line, i) {
+ ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
+ tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width);
+ });
+ helpers.each(vm.body, function(line, i) {
+ ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
+ tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width + (this._options.tooltips.mode != 'single' ? (vm.bodyFontSize + 2) : 0));
+ }, this);
+ helpers.each(vm.footer, function(line, i) {
+ ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
+ tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width);
+ });
+ tooltipWidth += 2 * vm.xPadding;
+ var tooltipTotalWidth = tooltipWidth + vm.caretSize + caretPadding;
- switch (vm.yAlign) {
- case "above":
- //Draw a caret above the x/y
- ctx.beginPath();
- ctx.moveTo(vm.x, vm.y - caretPadding);
- ctx.lineTo(vm.x + vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
- ctx.lineTo(vm.x - vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
- ctx.closePath();
- ctx.fill();
- break;
- case "below":
- tooltipY = vm.y + caretPadding + vm.caretHeight;
- //Draw a caret below the x/y
- ctx.beginPath();
- ctx.moveTo(vm.x, vm.y + caretPadding);
- ctx.lineTo(vm.x + vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
- ctx.lineTo(vm.x - vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
- ctx.closePath();
- ctx.fill();
- break;
- }
- switch (vm.xAlign) {
- case "left":
- tooltipX = vm.x - tooltipWidth + (vm.cornerRadius + vm.caretHeight);
- break;
- case "right":
- tooltipX = vm.x - (vm.cornerRadius + vm.caretHeight);
- break;
- }
- helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipRectHeight, vm.cornerRadius);
+ // Smart Tooltip placement to stay on the canvas
+ // Top, center, or bottom
+ vm.yAlign = "center";
+ if (vm.y - (tooltipHeight / 2) < 0) {
+ vm.yAlign = "top";
+ } else if (vm.y + (tooltipHeight / 2) > this._chart.height) {
+ vm.yAlign = "bottom";
+ }
- ctx.fill();
- ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
- ctx.textAlign = "center";
- ctx.textBaseline = "middle";
- ctx.fillText(vm.text, tooltipX + tooltipWidth / 2, tooltipY + tooltipRectHeight / 2);
- break;
- case 'label':
+ // Left or Right
+ vm.xAlign = "right";
+ if (vm.x + tooltipTotalWidth > this._chart.width) {
+ vm.xAlign = "left";
+ }
- // Custom Tooltips
- if (this._options.tooltips.custom) {
- this._options.tooltips.custom(this);
- }
- if (!this._options.tooltips.enabled) {
- return;
- }
- helpers.drawRoundedRectangle(ctx, vm.x, vm.y - vm.height / 2, vm.width, vm.height, vm.cornerRadius);
- ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
+ // Background Position
+ var tooltipX = vm.x,
+ tooltipY = vm.y;
+
+ if (vm.yAlign == 'top') {
+ tooltipY = vm.y - vm.caretSize - vm.cornerRadius;
+ } else if (vm.yAlign == 'bottom') {
+ tooltipY = vm.y - tooltipHeight + vm.caretSize + vm.cornerRadius;
+ } else {
+ tooltipY = vm.y - (tooltipHeight / 2);
+ }
+
+ if (vm.xAlign == 'left') {
+ tooltipX = vm.x - tooltipTotalWidth;
+ } else if (vm.xAlign == 'right') {
+ tooltipX = vm.x + caretPadding + vm.caretSize;
+ } else {
+ tooltipX = vm.x + (tooltipTotalWidth / 2);
+ }
+
+ // Draw Background
+
+ if (this._options.tooltips.enabled) {
+ ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
+ helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipHeight, vm.cornerRadius);
+ ctx.fill();
+ }
+
+
+ // Draw Caret
+ if (this._options.tooltips.enabled) {
+ ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
+
+ if (vm.xAlign == 'left') {
+
+ ctx.beginPath();
+ ctx.moveTo(vm.x - caretPadding, vm.y);
+ ctx.lineTo(vm.x - caretPadding - vm.caretSize, vm.y - vm.caretSize);
+ ctx.lineTo(vm.x - caretPadding - vm.caretSize, vm.y + vm.caretSize);
+ ctx.closePath();
ctx.fill();
+ } else {
+ ctx.beginPath();
+ ctx.moveTo(vm.x + caretPadding, vm.y);
+ ctx.lineTo(vm.x + caretPadding + vm.caretSize, vm.y - vm.caretSize);
+ ctx.lineTo(vm.x + caretPadding + vm.caretSize, vm.y + vm.caretSize);
ctx.closePath();
+ ctx.fill();
+ }
+ }
+
+ // Draw Title, Body, and Footer
+
+ if (this._options.tooltips.enabled) {
+
+ var yBase = tooltipY + vm.yPadding;
+ var xBase = tooltipX + vm.xPadding;
- ctx.textAlign = "left";
- ctx.textBaseline = "middle";
- ctx.fillStyle = helpers.color(vm.titleTextColor).alpha(vm.opacity).rgbString();
- ctx.font = helpers.fontString(vm.fontSize, vm._titleFontStyle, vm._titleFontFamily);
- ctx.fillText(vm.title, vm.x + vm.xPadding, this.getLineHeight(0));
+ // Titles
- ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
- helpers.each(vm.labels, function(label, index) {
- ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
- ctx.fillText(label, vm.x + vm.xPadding + vm.fontSize + 3, this.getLineHeight(index + 1));
+ if (vm.title.length) {
+ ctx.textAlign = vm._titleAlign;
+ ctx.textBaseline = "top";
+ ctx.fillStyle = helpers.color(vm.titleColor).alpha(vm.opacity).rgbString();
+ ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
+
+ helpers.each(vm.title, function(title, i) {
+ ctx.fillText(title, xBase, yBase);
+ yBase += vm.titleFontSize + vm.titleSpacing; // Line Height and spacing
+ if (i + 1 == vm.title.length) {
+ yBase += vm.titleMarginBottom - vm.titleSpacing; // If Last, add margin, remove spacing
+ }
+ }, this);
+ }
- //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
- //ctx.clearRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize/2, vm.fontSize, vm.fontSize);
- //Instead we'll make a white filled block to put the legendColour palette over.
- ctx.fillStyle = helpers.color(vm.legendColors[index].stroke).alpha(vm.opacity).rgbString();
- ctx.fillRect(vm.x + vm.xPadding - 1, this.getLineHeight(index + 1) - vm.fontSize / 2 - 1, vm.fontSize + 2, vm.fontSize + 2);
+ // Body
+ ctx.textAlign = vm._bodyAlign;
+ ctx.textBaseline = "top";
+ ctx.fillStyle = helpers.color(vm.bodyColor).alpha(vm.opacity).rgbString();
+ ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
- ctx.fillStyle = helpers.color(vm.legendColors[index].fill).alpha(vm.opacity).rgbString();
- ctx.fillRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize / 2, vm.fontSize, vm.fontSize);
+ // Before Body
+ helpers.each(vm.beforeBody, function(beforeBody, i) {
+ ctx.fillText(vm.beforeBody, xBase, yBase);
+ yBase += vm.bodyFontSize + vm.bodySpacing;
+ });
+
+ helpers.each(vm.body, function(body, i) {
+
+
+ // Draw Legend-like boxes if needed
+ if (this._options.tooltips.mode != 'single') {
+ ctx.fillStyle = helpers.color(vm.labelColors[i].borderColor).alpha(vm.opacity).rgbString();
+ ctx.fillRect(xBase, yBase, vm.bodyFontSize, vm.bodyFontSize);
+
+ ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(vm.opacity).rgbString();
+ ctx.fillRect(xBase + 1, yBase + 1, vm.bodyFontSize - 2, vm.bodyFontSize - 2);
+
+ ctx.fillStyle = helpers.color(vm.bodyColor).alpha(vm.opacity).rgbString(); // Return fill style for text
+ }
+ // Body Line
+ ctx.fillText(body, xBase + (this._options.tooltips.mode != 'single' ? (vm.bodyFontSize + 2) : 0), yBase);
+ yBase += vm.bodyFontSize + vm.bodySpacing;
+
+ }, this);
+
+ // After Body
+ helpers.each(vm.afterBody, function(afterBody, i) {
+ ctx.fillText(vm.afterBody, xBase, yBase);
+ yBase += vm.bodyFontSize;
+ });
+
+ yBase -= vm.bodySpacing; // Remove last body spacing
+
+
+ // Footer
+ if (vm.footer.length) {
+
+ yBase += vm.footerMarginTop;
+
+ ctx.textAlign = vm._footerAlign;
+ ctx.textBaseline = "top";
+ ctx.fillStyle = helpers.color(vm.footerColor).alpha(vm.opacity).rgbString();
+ ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
+
+ helpers.each(vm.footer, function(footer, i) {
+ ctx.fillText(footer, xBase, yBase);
+ yBase += vm.footerFontSize + vm.footerSpacing;
}, this);
- break;
- }
- },
- getLineHeight: function(index) {
- var baseLineHeight = this._view.y - (this._view.height / 2) + this._view.yPadding,
- afterTitleIndex = index - 1;
+ }
- //If the index is zero, we're getting the title
- if (index === 0) {
- return baseLineHeight + this._view.titleFontSize / 2;
- } else {
- return baseLineHeight + ((this._view.fontSize * 1.5 * afterTitleIndex) + this._view.fontSize / 2) + this._view.titleFontSize * 1.5;
}
-
},
});
diff --git a/src/scales/scale.category.js b/src/scales/scale.category.js
index 7e572b864c3..039a9bef674 100644
--- a/src/scales/scale.category.js
+++ b/src/scales/scale.category.js
@@ -15,9 +15,12 @@
this.ticks = this.data.labels;
},
+ getLabelForIndex: function(index, datasetIndex) {
+ return this.ticks[index];
+ },
+
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
-
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
var valueWidth = innerWidth / Math.max((this.data.labels.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
diff --git a/src/scales/scale.linear.js b/src/scales/scale.linear.js
index 79ea5937f20..9de4d81cf6c 100644
--- a/src/scales/scale.linear.js
+++ b/src/scales/scale.linear.js
@@ -147,6 +147,10 @@
this.zeroLineIndex = this.ticks.indexOf(0);
},
+ getLabelForIndex: function(index, datasetIndex) {
+ return this.getRightValue(this.data.datasets[datasetIndex].data[index]);
+ },
+
// Utils
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
// This must be called after fit has been run so that
@@ -165,12 +169,6 @@
return Math.round(pixel);
}
},
-
- // Get the correct value. If the value type is object get the x or y based on whether we are horizontal or not
- getRightValue: function(rawValue) {
- return (typeof(rawValue) === "object" && rawValue !== null) ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
- },
-
});
Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig);
diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js
index eba0877bae4..9f5b62c47f6 100644
--- a/src/scales/scale.logarithmic.js
+++ b/src/scales/scale.logarithmic.js
@@ -10,7 +10,15 @@
// label settings
ticks: {
- template: "<%var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));if (remain === 1 || remain === 2 || remain === 5) {%><%=value.toExponential()%><%} else {%><%= null %><%}%>",
+ callback: function(value) {
+ var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));
+
+ if (remain === 1 || remain === 2 || remain === 5) {
+ return value.toExponential()
+ } else {
+ return '';
+ }
+ }
}
};
@@ -121,9 +129,9 @@
this.ticks = this.tickValues.slice();
},
- // Get the correct value. If the value type is object get the x or y based on whether we are horizontal or not
- getRightValue: function(rawValue) {
- return typeof rawValue === "object" ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
+ // Get the correct tooltip label
+ getLabelForIndex: function(index, datasetIndex) {
+ return this.getRightValue(this.data.datasets[datasetIndex].data[index]);
},
getPixelForTick: function(index, includeOffset) {
return this.getPixelForValue(this.tickValues[index], null, null, includeOffset);
diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js
index f55b321e348..d094eb4c89d 100644
--- a/src/scales/scale.radialLinear.js
+++ b/src/scales/scale.radialLinear.js
@@ -203,9 +203,7 @@
for (i = 0; i < this.getValueCount(); i++) {
// 5px to space the text slightly out - similar to what we do in the draw function.
pointPosition = this.getPointPosition(i, largestPossibleRadius);
- textWidth = this.ctx.measureText(helpers.template(this.options.ticks.template, {
- value: this.data.labels[i]
- })).width + 5;
+ textWidth = this.ctx.measureText(this.options.ticks.callback(this.data.labels[i])).width + 5;
if (i === 0 || i === this.getValueCount() / 2) {
// If we're at index zero, or exactly the middle, we're at exactly the top/bottom
// of the radar chart, so text will be aligned centrally, so we'll half it and compare
diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js
index 9ba486ce422..88af1436bf4 100644
--- a/src/scales/scale.time.js
+++ b/src/scales/scale.time.js
@@ -74,23 +74,73 @@
};
var TimeScale = Chart.Scale.extend({
- buildTicks: function(index) {
+ getLabelMoment: function(datasetIndex, index) {
+ return this.labelMoments[datasetIndex][index];
+ },
- this.ticks = [];
- this.labelMoments = [];
+ buildLabelMoments: function() {
+ // Only parse these once. If the dataset does not have data as x,y pairs, we will use
+ // these
+ var scaleLabelMoments = [];
+ if (this.data.labels && this.data.labels.length > 0) {
+ helpers.each(this.data.labels, function(label, index) {
+ var labelMoment = this.parseTime(label);
+ if (this.options.time.round) {
+ labelMoment.startOf(this.options.time.round);
+ }
+ scaleLabelMoments.push(labelMoment);
+ }, this);
+
+ if (this.options.time.min) {
+ this.firstTick = this.parseTime(this.options.time.min);
+ } else {
+ this.firstTick = moment.min.call(this, scaleLabelMoments);
+ }
+
+ if (this.options.time.max) {
+ this.lastTick = this.parseTime(this.options.time.max);
+ } else {
+ this.lastTick = moment.max.call(this, scaleLabelMoments);
+ }
+ } else {
+ this.firstTick = null;
+ this.lastTick = null;
+ }
- // Parse each label into a moment
- this.data.labels.forEach(function(label, index) {
- var labelMoment = this.parseTime(label);
- if (this.options.time.round) {
- labelMoment.startOf(this.options.time.round);
+ helpers.each(this.data.datasets, function(dataset, datasetIndex) {
+ var momentsForDataset = [];
+
+ if (typeof dataset.data[0] === 'object') {
+ helpers.each(dataset.data, function(value, index) {
+ var labelMoment = this.parseTime(this.getRightValue(value));
+ if (this.options.time.round) {
+ labelMoment.startOf(this.options.time.round);
+ }
+ momentsForDataset.push(labelMoment);
+
+ // May have gone outside the scale ranges, make sure we keep the first and last ticks updated
+ this.firstTick = this.firstTick !== null ? moment.min(this.firstTick, labelMoment) : labelMoment;
+ this.lastTick = this.lastTick !== null ? moment.max(this.lastTick, labelMoment) : labelMoment;
+ }, this);
+ } else {
+ // We have no labels. Use the ones from the scale
+ momentsForDataset = scaleLabelMoments;
}
- this.labelMoments.push(labelMoment);
+
+ this.labelMoments.push(momentsForDataset);
}, this);
- // Find the first and last moments, and range
- this.firstTick = moment.min.call(this, this.labelMoments).clone();
- this.lastTick = moment.max.call(this, this.labelMoments).clone();
+ // We will modify these, so clone for later
+ this.firstTick = this.firstTick.clone();
+ this.lastTick = this.lastTick.clone();
+ },
+
+ buildTicks: function(index) {
+
+ this.ticks = [];
+ this.labelMoments = [];
+
+ this.buildLabelMoments();
// Set unit override if applicable
if (this.options.time.unit) {
@@ -124,11 +174,11 @@
this.lastTick.endOf(this.tickUnit);
this.smallestLabelSeparation = this.width;
- var i = 0;
-
- for (i = 1; i < this.labelMoments.length; i++) {
- this.smallestLabelSeparation = Math.min(this.smallestLabelSeparation, this.labelMoments[i].diff(this.labelMoments[i - 1], this.tickUnit, true));
- }
+ helpers.each(this.data.datasets, function(dataset, datasetIndex) {
+ for (var i = 1; i < this.labelMoments[datasetIndex].length; i++) {
+ this.smallestLabelSeparation = Math.min(this.smallestLabelSeparation, this.labelMoments[datasetIndex][i].diff(this.labelMoments[datasetIndex][i - 1], this.tickUnit, true));
+ }
+ }, this);
// Tick displayFormat override
if (this.options.time.displayFormat) {
@@ -136,10 +186,20 @@
}
// For every unit in between the first and last moment, create a moment and add it to the ticks tick
- for (i = 0; i <= this.tickRange; ++i) {
+ for (var i = 0; i <= this.tickRange; ++i) {
this.ticks.push(this.firstTick.clone().add(i, this.tickUnit));
}
},
+ // Get tooltip label
+ getLabelForIndex: function(index, datasetIndex) {
+ var label = this.data.labels && index < this.data.labels.length ? this.data.labels[index] : '';
+
+ if (typeof this.data.datasets[datasetIndex].data[0] === 'object') {
+ label = this.getRightValue(this.data.datasets[datasetIndex].data[index]);
+ }
+
+ return label;
+ },
convertTicksToLabels: function() {
this.ticks = this.ticks.map(function(tick, index, ticks) {
var formattedTick = tick.format(this.options.time.displayFormat ? this.options.time.displayFormat : time.unit[this.tickUnit].display);
@@ -152,8 +212,8 @@
}, this);
},
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
-
- var offset = this.labelMoments[index].diff(this.firstTick, this.tickUnit, true);
+ var labelMoment = this.getLabelMoment(datasetIndex, index);
+ var offset = labelMoment.diff(this.firstTick, this.tickUnit, true);
var decimal = offset / this.tickRange;
diff --git a/test/core.helpers.tests.js b/test/core.helpers.tests.js
index ce0c987579f..0320a84aa57 100644
--- a/test/core.helpers.tests.js
+++ b/test/core.helpers.tests.js
@@ -244,7 +244,7 @@ describe('Core helper tests', function() {
padding: 10,
reverse: false,
show: true,
- template: "<%=value%>"
+ callback: merged.scales.yAxes[1].ticks.callback, // make it nicer, then check explicitly below
},
type: 'linear'
}, {
@@ -281,12 +281,16 @@ describe('Core helper tests', function() {
padding: 10,
reverse: false,
show: true,
- template: "<%=value%>"
+ callback: merged.scales.yAxes[2].ticks.callback, // make it nicer, then check explicitly below
},
type: 'linear'
}]
}
});
+
+ // Are these actually functions
+ expect(merged.scales.yAxes[1].ticks.callback).toEqual(jasmine.any(Function));
+ expect(merged.scales.yAxes[2].ticks.callback).toEqual(jasmine.any(Function));
});
it('should get value or default', function() {
diff --git a/test/scale.category.tests.js b/test/scale.category.tests.js
index d6ec96f960a..e5ac45a297c 100644
--- a/test/scale.category.tests.js
+++ b/test/scale.category.tests.js
@@ -43,9 +43,12 @@ describe('Category scale tests', function() {
padding: 10,
reverse: false,
show: true,
- template: "<%=value%>"
+ callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
}
});
+
+ // Is this actually a function
+ expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('Should generate ticks from the data labales', function() {
diff --git a/test/scale.linear.tests.js b/test/scale.linear.tests.js
index cd7a2c4b574..d4a29eec3da 100644
--- a/test/scale.linear.tests.js
+++ b/test/scale.linear.tests.js
@@ -42,9 +42,11 @@ describe('Linear Scale', function() {
padding: 10,
reverse: false,
show: true,
- template: "<%=value%>"
+ callback: defaultConfig.ticks.callback, // make this work nicer, then check below
}
});
+
+ expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('Should correctly determine the max & min data values', function() {
diff --git a/test/scale.logarithmic.tests.js b/test/scale.logarithmic.tests.js
index ea1092749be..72250df73ba 100644
--- a/test/scale.logarithmic.tests.js
+++ b/test/scale.logarithmic.tests.js
@@ -41,9 +41,12 @@ describe('Logarithmic Scale tests', function() {
padding: 10,
reverse: false,
show: true,
- template: "<%var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));if (remain === 1 || remain === 2 || remain === 5) {%><%=value.toExponential()%><%} else {%><%= null %><%}%>",
+ callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
},
});
+
+ // Is this actually a function
+ expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('Should correctly determine the max & min data values', function() {
diff --git a/test/scale.radialLinear.tests.js b/test/scale.radialLinear.tests.js
index 5a762078918..0b2512e9b3b 100644
--- a/test/scale.radialLinear.tests.js
+++ b/test/scale.radialLinear.tests.js
@@ -58,10 +58,13 @@ describe('Test the radial linear scale', function() {
reverse: false,
showLabelBackdrop: true,
show: true,
- template: "<%=value%>",
+ callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
},
});
+
+ // Is this actually a function
+ expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('Should correctly determine the max & min data values', function() {
diff --git a/test/scale.time.tests.js b/test/scale.time.tests.js
index eb573df4a27..0e8771e99c3 100644
--- a/test/scale.time.tests.js
+++ b/test/scale.time.tests.js
@@ -46,7 +46,7 @@ describe('Time scale tests', function() {
padding: 10,
reverse: false,
show: true,
- template: "<%=value%>"
+ callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
},
time: {
format: false,
@@ -55,6 +55,9 @@ describe('Time scale tests', function() {
displayFormat: false,
}
});
+
+ // Is this actually a function
+ expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('should build ticks using days', function() {
@@ -132,6 +135,9 @@ describe('Time scale tests', function() {
var mockData = {
labels: ["2015-01-01T20:00:00", "2015-01-02T21:00:00", "2015-01-03T22:00:00", "2015-01-05T23:00:00", "2015-01-07T03:00", "2015-01-08T10:00", "2015-01-10T12:00"], // days
+ datasets: [{
+ data: [],
+ }]
};
var mockContext = window.createMockContext();