var defaults = require('./core.defaults');
var helpers = require('../helpers/index');
+var extend = helpers.extend;
+
function filterByPosition(array, position) {
return helpers.where(array, function(v) {
- return v.position === position;
+ return v.pos === position;
});
}
function sortByWeight(array, reverse) {
- array.forEach(function(v, i) {
- v._tmpIndex_ = i;
- return v;
- });
- array.sort(function(a, b) {
+ return array.sort(function(a, b) {
var v0 = reverse ? b : a;
var v1 = reverse ? a : b;
return v0.weight === v1.weight ?
- v0._tmpIndex_ - v1._tmpIndex_ :
+ v0.index - v1.index :
v0.weight - v1.weight;
});
- array.forEach(function(v) {
- delete v._tmpIndex_;
- });
}
-function findMaxPadding(boxes) {
- var top = 0;
- var left = 0;
- var bottom = 0;
- var right = 0;
- helpers.each(boxes, function(box) {
- if (box.getPadding) {
- var boxPadding = box.getPadding();
- top = Math.max(top, boxPadding.top);
- left = Math.max(left, boxPadding.left);
- bottom = Math.max(bottom, boxPadding.bottom);
- right = Math.max(right, boxPadding.right);
- }
- });
+function wrapBoxes(boxes) {
+ var layoutBoxes = [];
+ var i, ilen, box;
+
+ for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) {
+ box = boxes[i];
+ layoutBoxes.push({
+ index: i,
+ box: box,
+ pos: box.position,
+ horizontal: box.isHorizontal(),
+ weight: box.weight
+ });
+ }
+ return layoutBoxes;
+}
+
+function setLayoutDims(layouts, params) {
+ var i, ilen, layout;
+ for (i = 0, ilen = layouts.length; i < ilen; ++i) {
+ layout = layouts[i];
+ // store width used instead of chartArea.w in fitBoxes
+ layout.width = layout.horizontal
+ ? layout.box.fullWidth && params.availableWidth
+ : params.vBoxMaxWidth;
+ // store height used instead of chartArea.h in fitBoxes
+ layout.height = layout.horizontal && params.hBoxMaxHeight;
+ }
+}
+
+function buildLayoutBoxes(boxes) {
+ var layoutBoxes = wrapBoxes(boxes);
+ var left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);
+ var right = sortByWeight(filterByPosition(layoutBoxes, 'right'));
+ var top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);
+ var bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));
+
return {
- top: top,
- left: left,
- bottom: bottom,
- right: right
+ leftAndTop: left.concat(top),
+ rightAndBottom: right.concat(bottom),
+ chartArea: filterByPosition(layoutBoxes, 'chartArea'),
+ vertical: left.concat(right),
+ horizontal: top.concat(bottom)
};
}
-function addSizeByPosition(boxes, size) {
- helpers.each(boxes, function(box) {
- size[box.position] += box.isHorizontal() ? box.height : box.width;
- });
+function getCombinedMax(maxPadding, chartArea, a, b) {
+ return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
+}
+
+function updateDims(chartArea, params, layout) {
+ var box = layout.box;
+ var maxPadding = chartArea.maxPadding;
+ var newWidth, newHeight;
+
+ if (layout.size) {
+ // this layout was already counted for, lets first reduce old size
+ chartArea[layout.pos] -= layout.size;
+ }
+ layout.size = layout.horizontal ? box.height : box.width;
+ chartArea[layout.pos] += layout.size;
+
+ if (box.getPadding) {
+ var boxPadding = box.getPadding();
+ maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
+ maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
+ maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
+ maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
+ }
+
+ newWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right');
+ newHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom');
+
+ if (newWidth !== chartArea.w || newHeight !== chartArea.h) {
+ chartArea.w = newWidth;
+ chartArea.h = newHeight;
+
+ // return true if chart area changed in layout's direction
+ return layout.horizontal ? newWidth !== chartArea.w : newHeight !== chartArea.h;
+ }
+}
+
+function handleMaxPadding(chartArea) {
+ var maxPadding = chartArea.maxPadding;
+
+ function updatePos(pos) {
+ var change = Math.max(maxPadding[pos] - chartArea[pos], 0);
+ chartArea[pos] += change;
+ return change;
+ }
+ chartArea.y += updatePos('top');
+ chartArea.x += updatePos('left');
+ updatePos('right');
+ updatePos('bottom');
+}
+
+function getMargins(horizontal, chartArea) {
+ var maxPadding = chartArea.maxPadding;
+
+ function marginForPositions(positions) {
+ var margin = {left: 0, top: 0, right: 0, bottom: 0};
+ positions.forEach(function(pos) {
+ margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);
+ });
+ return margin;
+ }
+
+ return horizontal
+ ? marginForPositions(['left', 'right'])
+ : marginForPositions(['top', 'bottom']);
+}
+
+function fitBoxes(boxes, chartArea, params) {
+ var refitBoxes = [];
+ var i, ilen, layout, box, refit, changed;
+
+ for (i = 0, ilen = boxes.length; i < ilen; ++i) {
+ layout = boxes[i];
+ box = layout.box;
+
+ box.update(
+ layout.width || chartArea.w,
+ layout.height || chartArea.h,
+ getMargins(layout.horizontal, chartArea)
+ );
+ if (updateDims(chartArea, params, layout)) {
+ changed = true;
+ if (refitBoxes.length) {
+ // Dimensions changed and there were non full width boxes before this
+ // -> we have to refit those
+ refit = true;
+ }
+ }
+ if (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case
+ refitBoxes.push(layout);
+ }
+ }
+
+ return refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed;
+}
+
+function placeBoxes(boxes, chartArea, params) {
+ var userPadding = params.padding;
+ var x = chartArea.x;
+ var y = chartArea.y;
+ var i, ilen, layout, box;
+
+ for (i = 0, ilen = boxes.length; i < ilen; ++i) {
+ layout = boxes[i];
+ box = layout.box;
+ if (layout.horizontal) {
+ box.left = box.fullWidth ? userPadding.left : chartArea.left;
+ box.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w;
+ box.top = y;
+ box.bottom = y + box.height;
+ box.width = box.right - box.left;
+ y = box.bottom;
+ } else {
+ box.left = x;
+ box.right = x + box.width;
+ box.top = chartArea.top;
+ box.bottom = chartArea.top + chartArea.h;
+ box.height = box.bottom - box.top;
+ x = box.right;
+ }
+ }
+
+ chartArea.x = x;
+ chartArea.y = y;
}
defaults._set('global', {
var layoutOptions = chart.options.layout || {};
var padding = helpers.options.toPadding(layoutOptions.padding);
- var leftPadding = padding.left;
- var rightPadding = padding.right;
- var topPadding = padding.top;
- var bottomPadding = padding.bottom;
-
- var leftBoxes = filterByPosition(chart.boxes, 'left');
- var rightBoxes = filterByPosition(chart.boxes, 'right');
- var topBoxes = filterByPosition(chart.boxes, 'top');
- var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
- var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');
-
- // Sort boxes by weight. A higher weight is further away from the chart area
- sortByWeight(leftBoxes, true);
- sortByWeight(rightBoxes, false);
- sortByWeight(topBoxes, true);
- sortByWeight(bottomBoxes, false);
-
- var verticalBoxes = leftBoxes.concat(rightBoxes);
- var horizontalBoxes = topBoxes.concat(bottomBoxes);
- var outerBoxes = verticalBoxes.concat(horizontalBoxes);
+
+ var availableWidth = width - padding.width;
+ var availableHeight = height - padding.height;
+ var boxes = buildLayoutBoxes(chart.boxes);
+ var verticalBoxes = boxes.vertical;
+ var horizontalBoxes = boxes.horizontal;
// Essentially we now have any number of boxes on each of the 4 sides.
// Our canvas looks like the following.
// | B2 (Full Width) |
// |----------------------------------------------------|
//
- // What we do to find the best sizing, we do the following
- // 1. Determine the minimum size of the chart area.
- // 2. Split the remaining width equally between each vertical axis
- // 3. Split the remaining height equally between each horizontal axis
- // 4. Give each layout the maximum size it can be. The layout will return it's minimum size
- // 5. Adjust the sizes of each axis based on it's minimum reported size.
- // 6. Refit each axis
- // 7. Position each axis in the final location
- // 8. Tell the chart the final location of the chart area
- // 9. Tell any axes that overlay the chart area the positions of the chart area
-
- // Step 1
- var chartWidth = width - leftPadding - rightPadding;
- var chartHeight = height - topPadding - bottomPadding;
- var chartAreaWidth = chartWidth / 2; // min 50%
-
- // Step 2
- var verticalBoxWidth = (width - chartAreaWidth) / verticalBoxes.length;
-
- // Step 3
- // TODO re-limit horizontal axis height (this limit has affected only padding calculation since PR 1837)
- // var horizontalBoxHeight = (height - chartAreaHeight) / horizontalBoxes.length;
-
- // Step 4
- var maxChartAreaWidth = chartWidth;
- var maxChartAreaHeight = chartHeight;
- var outerBoxSizes = {top: topPadding, left: leftPadding, bottom: bottomPadding, right: rightPadding};
- var minBoxSizes = [];
- var maxPadding;
-
- function getMinimumBoxSize(box) {
- var minSize;
- var isHorizontal = box.isHorizontal();
-
- if (isHorizontal) {
- minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2);
- maxChartAreaHeight -= minSize.height;
- } else {
- minSize = box.update(verticalBoxWidth, maxChartAreaHeight);
- maxChartAreaWidth -= minSize.width;
- }
-
- minBoxSizes.push({
- horizontal: isHorizontal,
- width: minSize.width,
- box: box,
- });
- }
-
- helpers.each(outerBoxes, getMinimumBoxSize);
-
- // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
- maxPadding = findMaxPadding(outerBoxes);
-
- // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
- // be if the axes are drawn at their minimum sizes.
- // Steps 5 & 6
-
- // Function to fit a box
- function fitBox(box) {
- var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
- return minBox.box === box;
- });
-
- if (minBoxSize) {
- if (minBoxSize.horizontal) {
- var scaleMargin = {
- left: Math.max(outerBoxSizes.left, maxPadding.left),
- right: Math.max(outerBoxSizes.right, maxPadding.right),
- top: 0,
- bottom: 0
- };
-
- // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
- // on the margin. Sometimes they need to increase in size slightly
- box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
- } else {
- box.update(minBoxSize.width, maxChartAreaHeight);
- }
- }
- }
-
- // Update, and calculate the left and right margins for the horizontal boxes
- helpers.each(verticalBoxes, fitBox);
- addSizeByPosition(verticalBoxes, outerBoxSizes);
-
- // Set the Left and Right margins for the horizontal boxes
- helpers.each(horizontalBoxes, fitBox);
- addSizeByPosition(horizontalBoxes, outerBoxSizes);
-
- function finalFitVerticalBox(box) {
- var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
- return minSize.box === box;
- });
-
- var scaleMargin = {
- left: 0,
- right: 0,
- top: outerBoxSizes.top,
- bottom: outerBoxSizes.bottom
- };
-
- if (minBoxSize) {
- box.update(minBoxSize.width, maxChartAreaHeight, scaleMargin);
- }
- }
-
- // Let the left layout know the final margin
- helpers.each(verticalBoxes, finalFitVerticalBox);
-
- // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
- outerBoxSizes = {top: topPadding, left: leftPadding, bottom: bottomPadding, right: rightPadding};
- addSizeByPosition(outerBoxes, outerBoxSizes);
-
- // We may be adding some padding to account for rotated x axis labels
- var leftPaddingAddition = Math.max(maxPadding.left - outerBoxSizes.left, 0);
- outerBoxSizes.left += leftPaddingAddition;
- outerBoxSizes.right += Math.max(maxPadding.right - outerBoxSizes.right, 0);
-
- var topPaddingAddition = Math.max(maxPadding.top - outerBoxSizes.top, 0);
- outerBoxSizes.top += topPaddingAddition;
- outerBoxSizes.bottom += Math.max(maxPadding.bottom - outerBoxSizes.bottom, 0);
-
- // Figure out if our chart area changed. This would occur if the dataset layout label rotation
- // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
- // without calling `fit` again
- var newMaxChartAreaHeight = height - outerBoxSizes.top - outerBoxSizes.bottom;
- var newMaxChartAreaWidth = width - outerBoxSizes.left - outerBoxSizes.right;
-
- if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
- helpers.each(verticalBoxes, function(box) {
- box.height = newMaxChartAreaHeight;
- });
-
- helpers.each(horizontalBoxes, function(box) {
- if (!box.fullWidth) {
- box.width = newMaxChartAreaWidth;
- }
- });
- maxChartAreaHeight = newMaxChartAreaHeight;
- maxChartAreaWidth = newMaxChartAreaWidth;
+ var params = Object.freeze({
+ outerWidth: width,
+ outerHeight: height,
+ padding: padding,
+ availableWidth: availableWidth,
+ vBoxMaxWidth: availableWidth / 2 / verticalBoxes.length,
+ hBoxMaxHeight: availableHeight / 2
+ });
+ var chartArea = extend({
+ maxPadding: extend({}, padding),
+ w: availableWidth,
+ h: availableHeight,
+ x: padding.left,
+ y: padding.top
+ }, padding);
+
+ setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
+
+ // First fit vertical boxes
+ fitBoxes(verticalBoxes, chartArea, params);
+
+ // Then fit horizontal boxes
+ if (fitBoxes(horizontalBoxes, chartArea, params)) {
+ // if the area changed, re-fit vertical boxes
+ fitBoxes(verticalBoxes, chartArea, params);
}
- // Step 7 - Position the boxes
- var left = leftPadding + leftPaddingAddition;
- var top = topPadding + topPaddingAddition;
-
- function placeBox(box) {
- if (box.isHorizontal()) {
- box.left = box.fullWidth ? leftPadding : outerBoxSizes.left;
- box.right = box.fullWidth ? width - rightPadding : outerBoxSizes.left + maxChartAreaWidth;
- box.top = top;
- box.bottom = top + box.height;
-
- // Move to next point
- top = box.bottom;
+ handleMaxPadding(chartArea, params);
- } else {
+ // Finally place the boxes to correct coordinates
+ placeBoxes(boxes.leftAndTop, chartArea, params);
- box.left = left;
- box.right = left + box.width;
- box.top = outerBoxSizes.top;
- box.bottom = outerBoxSizes.top + maxChartAreaHeight;
+ // Move to opposite side of chart
+ chartArea.x += chartArea.w;
+ chartArea.y += chartArea.h;
- // Move to next point
- left = box.right;
- }
- }
+ placeBoxes(boxes.rightAndBottom, chartArea, params);
- helpers.each(leftBoxes.concat(topBoxes), placeBox);
-
- // Account for chart width and height
- left += maxChartAreaWidth;
- top += maxChartAreaHeight;
-
- helpers.each(rightBoxes, placeBox);
- helpers.each(bottomBoxes, placeBox);
-
- // Step 8
chart.chartArea = {
- left: outerBoxSizes.left,
- top: outerBoxSizes.top,
- right: outerBoxSizes.left + maxChartAreaWidth,
- bottom: outerBoxSizes.top + maxChartAreaHeight
+ left: chartArea.left,
+ top: chartArea.top,
+ right: chartArea.left + chartArea.w,
+ bottom: chartArea.top + chartArea.h
};
- // Step 9
- helpers.each(chartAreaBoxes, function(box) {
- box.left = chart.chartArea.left;
- box.top = chart.chartArea.top;
- box.right = chart.chartArea.right;
- box.bottom = chart.chartArea.bottom;
-
- box.update(maxChartAreaWidth, maxChartAreaHeight);
+ // Finally update boxes in chartArea (radial scale for example)
+ helpers.each(boxes.chartArea, function(layout) {
+ var box = layout.box;
+ extend(box, chart.chartArea);
+ box.update(chartArea.w, chartArea.h);
});
}
};
// Width
if (isHorizontal) {
- // subtract the margins to line up with the chartArea if we are a full width scale
- minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
+ minSize.width = me.maxWidth;
} else if (display) {
minSize.width = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts);
}
handleMargins: function() {
var me = this;
if (me.margins) {
- me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
- me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
- me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
- me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
+ me.margins.left = Math.max(me.paddingLeft, me.margins.left);
+ me.margins.top = Math.max(me.paddingTop, me.margins.top);
+ me.margins.right = Math.max(me.paddingRight, me.margins.right);
+ me.margins.bottom = Math.max(me.paddingBottom, me.margins.bottom);
}
},
getPixelForTick: function(index) {
var me = this;
var offset = me.options.offset;
+ var numTicks = me._ticks.length;
+ if (index < 0 || index > numTicks - 1) {
+ return null;
+ }
if (me.isHorizontal()) {
- var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
- var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
- var pixel = (tickWidth * index) + me.paddingLeft;
+ var tickWidth = me.width / Math.max((numTicks - (offset ? 0 : 1)), 1);
+ var pixel = (tickWidth * index);
if (offset) {
pixel += tickWidth / 2;
}
- var finalVal = me.left + pixel;
- finalVal += me.isFullWidth() ? me.margins.left : 0;
- return finalVal;
+ return me.left + pixel;
}
- var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
- return me.top + (index * (innerHeight / (me._ticks.length - 1)));
+ return me.top + (index * (me.height / (numTicks - 1)));
},
/**
*/
getPixelForDecimal: function(decimal) {
var me = this;
- if (me.isHorizontal()) {
- var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
- var valueOffset = (innerWidth * decimal) + me.paddingLeft;
-
- var finalVal = me.left + valueOffset;
- finalVal += me.isFullWidth() ? me.margins.left : 0;
- return finalVal;
- }
- return me.top + (decimal * me.height);
+ return me.isHorizontal()
+ ? me.left + decimal * me.width
+ : me.top + decimal * me.height;
},
/**
var ticksLength = me._tickSize() * (tickCount - 1);
// Axis length
- var axisLength = isHorizontal
- ? me.width - (me.paddingLeft + me.paddingRight)
- : me.height - (me.paddingTop + me.PaddingBottom);
+ var axisLength = isHorizontal ? me.width : me.height;
var result = [];
var i, tick;
},
getPixelForTick: function(index) {
- return this.getPixelForValue(this.ticks[index], index + this.minIndex);
+ var ticks = this.ticks;
+ if (index < 0 || index > ticks.length - 1) {
+ return null;
+ }
+ return this.getPixelForValue(ticks[index], index + this.minIndex);
},
getValueForPixel: function(pixel) {
},
getPixelForTick: function(index) {
- return this.getPixelForValue(this.ticksAsNumbers[index]);
+ var ticks = this.ticksAsNumbers;
+ if (index < 0 || index > ticks.length - 1) {
+ return null;
+ }
+ return this.getPixelForValue(ticks[index]);
}
});
},
getPixelForTick: function(index) {
- return this.getPixelForValue(this.tickValues[index]);
+ var ticks = this.tickValues;
+ if (index < 0 || index > ticks.length - 1) {
+ return null;
+ }
+ return this.getPixelForValue(ticks[index]);
},
/**
var me = this;
var ticksOpts = me.options.ticks;
var tickLabelWidth = me.ctx.measureText(label).width;
- var angle = helpers.toRadians(ticksOpts.maxRotation);
+ var angle = helpers.toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
var cosRotation = Math.cos(angle);
var sinRotation = Math.sin(angle);
var tickFontSize = valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);
var me = this;
var timeOpts = me.options.time;
var displayFormats = timeOpts.displayFormats;
- var margins = me.margins;
// pick the longest format (milliseconds) for guestimation
var format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
var exampleLabel = me.tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format);
var size = me._getLabelSize(exampleLabel);
-
- // Using margins instead of padding because padding is not calculated
- // at this point (buildTicks). Margins are provided from previous calculation
- // in layout steps 5/6
- var capacity = Math.floor(me.isHorizontal()
- ? (me.width - margins.left - margins.right) / size.w
- : (me.height - margins.top - margins.bottom) / size.h);
+ var capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h);
if (me.options.offset) {
capacity--;
expect(Chart.layouts.update).toBeDefined();
});
- // Disable tests which need to be rewritten based on changes introduced by
- // the following changes: https://github.com/chartjs/Chart.js/pull/2346
- // using xit marks the test as pending: https://jasmine.github.io/2.0/introduction.html#section-Pending_Specs
- xit('should fit a simple chart with 2 scales', function() {
+ it('should fit a simple chart with 2 scales', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
}
});
- expect(chart.chartArea.bottom).toBeCloseToPixel(112);
- expect(chart.chartArea.left).toBeCloseToPixel(41);
- expect(chart.chartArea.right).toBeCloseToPixel(250);
+ expect(chart.chartArea.bottom).toBeCloseToPixel(120);
+ expect(chart.chartArea.left).toBeCloseToPixel(34);
+ expect(chart.chartArea.right).toBeCloseToPixel(247);
expect(chart.chartArea.top).toBeCloseToPixel(32);
// Is xScale at the right spot
expect(chart.scales.xScale.bottom).toBeCloseToPixel(150);
- expect(chart.scales.xScale.left).toBeCloseToPixel(41);
- expect(chart.scales.xScale.right).toBeCloseToPixel(250);
- expect(chart.scales.xScale.top).toBeCloseToPixel(112);
- expect(chart.scales.xScale.labelRotation).toBeCloseTo(25);
+ expect(chart.scales.xScale.left).toBeCloseToPixel(34);
+ expect(chart.scales.xScale.right).toBeCloseToPixel(247);
+ expect(chart.scales.xScale.top).toBeCloseToPixel(120);
+ expect(chart.scales.xScale.labelRotation).toBeCloseTo(0);
// Is yScale at the right spot
- expect(chart.scales.yScale.bottom).toBeCloseToPixel(112);
+ expect(chart.scales.yScale.bottom).toBeCloseToPixel(120);
expect(chart.scales.yScale.left).toBeCloseToPixel(0);
- expect(chart.scales.yScale.right).toBeCloseToPixel(41);
+ expect(chart.scales.yScale.right).toBeCloseToPixel(34);
expect(chart.scales.yScale.top).toBeCloseToPixel(32);
expect(chart.scales.yScale.labelRotation).toBeCloseTo(0);
});
- xit('should fit scales that are in the top and right positions', function() {
+ it('should fit scales that are in the top and right positions', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
}
});
- expect(chart.chartArea.bottom).toBeCloseToPixel(150);
- expect(chart.chartArea.left).toBeCloseToPixel(0);
- expect(chart.chartArea.right).toBeCloseToPixel(209);
- expect(chart.chartArea.top).toBeCloseToPixel(71);
+ expect(chart.chartArea.bottom).toBeCloseToPixel(142);
+ expect(chart.chartArea.left).toBeCloseToPixel(3);
+ expect(chart.chartArea.right).toBeCloseToPixel(216);
+ expect(chart.chartArea.top).toBeCloseToPixel(62);
// Is xScale at the right spot
- expect(chart.scales.xScale.bottom).toBeCloseToPixel(71);
- expect(chart.scales.xScale.left).toBeCloseToPixel(0);
- expect(chart.scales.xScale.right).toBeCloseToPixel(209);
+ expect(chart.scales.xScale.bottom).toBeCloseToPixel(62);
+ expect(chart.scales.xScale.left).toBeCloseToPixel(3);
+ expect(chart.scales.xScale.right).toBeCloseToPixel(216);
expect(chart.scales.xScale.top).toBeCloseToPixel(32);
- expect(chart.scales.xScale.labelRotation).toBeCloseTo(25);
+ expect(chart.scales.xScale.labelRotation).toBeCloseTo(0);
// Is yScale at the right spot
- expect(chart.scales.yScale.bottom).toBeCloseToPixel(150);
- expect(chart.scales.yScale.left).toBeCloseToPixel(209);
+ expect(chart.scales.yScale.bottom).toBeCloseToPixel(142);
+ expect(chart.scales.yScale.left).toBeCloseToPixel(216);
expect(chart.scales.yScale.right).toBeCloseToPixel(250);
- expect(chart.scales.yScale.top).toBeCloseToPixel(71);
+ expect(chart.scales.yScale.top).toBeCloseToPixel(62);
expect(chart.scales.yScale.labelRotation).toBeCloseTo(0);
});
+ it('should fit scales with long labels correctly', function() {
+ var chart = window.acquireChart({
+ type: 'line',
+ data: {
+ datasets: [
+ {data: [10, 5, 0, 25, 78, -10]}
+ ],
+ labels: ['tick1 is very long one', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6 is very long one']
+ },
+ options: {
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{
+ id: 'xScale',
+ type: 'category',
+ ticks: {
+ maxRotation: 0,
+ autoSkip: false
+ }
+ }],
+ yAxes: [{
+ id: 'yScale',
+ type: 'linear',
+ position: 'right'
+ }]
+ }
+ }
+ }, {
+ canvas: {
+ height: 150,
+ width: 512
+ }
+ });
+
+ expect(chart.chartArea.bottom).toBeCloseToPixel(120);
+ expect(chart.chartArea.left).toBeCloseToPixel(60);
+ expect(chart.chartArea.right).toBeCloseToPixel(452);
+ expect(chart.chartArea.top).toBeCloseToPixel(7);
+
+ // Is xScale at the right spot
+ expect(chart.scales.xScale.bottom).toBeCloseToPixel(150);
+ expect(chart.scales.xScale.left).toBeCloseToPixel(60);
+ expect(chart.scales.xScale.right).toBeCloseToPixel(452);
+ expect(chart.scales.xScale.top).toBeCloseToPixel(120);
+ expect(chart.scales.xScale.labelRotation).toBeCloseTo(0);
+
+ expect(chart.scales.xScale.height).toBeCloseToPixel(30);
+ expect(chart.scales.xScale.paddingLeft).toBeCloseToPixel(60);
+ expect(chart.scales.xScale.paddingTop).toBeCloseToPixel(0);
+ expect(chart.scales.xScale.paddingRight).toBeCloseToPixel(60);
+ expect(chart.scales.xScale.paddingBottom).toBeCloseToPixel(0);
+
+ // Is yScale at the right spot
+ expect(chart.scales.yScale.bottom).toBeCloseToPixel(120);
+ expect(chart.scales.yScale.left).toBeCloseToPixel(452);
+ expect(chart.scales.yScale.right).toBeCloseToPixel(486);
+ expect(chart.scales.yScale.top).toBeCloseToPixel(7);
+ expect(chart.scales.yScale.labelRotation).toBeCloseTo(0);
+
+ expect(chart.scales.yScale.width).toBeCloseToPixel(34);
+ expect(chart.scales.yScale.paddingLeft).toBeCloseToPixel(0);
+ expect(chart.scales.yScale.paddingTop).toBeCloseToPixel(7);
+ expect(chart.scales.yScale.paddingRight).toBeCloseToPixel(0);
+ expect(chart.scales.yScale.paddingBottom).toBeCloseToPixel(7);
+ });
+
it('should fit scales that overlap the chart area', function() {
var chart = window.acquireChart({
type: 'radar',
expect(chart.scale.height).toBeCloseToPixel(480);
});
- xit('should fit multiple axes in the same position', function() {
+ it('should fit multiple axes in the same position', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
}
});
- expect(chart.chartArea.bottom).toBeCloseToPixel(102);
- expect(chart.chartArea.left).toBeCloseToPixel(86);
- expect(chart.chartArea.right).toBeCloseToPixel(250);
+ expect(chart.chartArea.bottom).toBeCloseToPixel(118);
+ expect(chart.chartArea.left).toBeCloseToPixel(73);
+ expect(chart.chartArea.right).toBeCloseToPixel(247);
expect(chart.chartArea.top).toBeCloseToPixel(32);
// Is xScale at the right spot
expect(chart.scales.xScale.bottom).toBeCloseToPixel(150);
- expect(chart.scales.xScale.left).toBeCloseToPixel(86);
- expect(chart.scales.xScale.right).toBeCloseToPixel(250);
- expect(chart.scales.xScale.top).toBeCloseToPixel(103);
- expect(chart.scales.xScale.labelRotation).toBeCloseTo(50);
+ expect(chart.scales.xScale.left).toBeCloseToPixel(73);
+ expect(chart.scales.xScale.right).toBeCloseToPixel(247);
+ expect(chart.scales.xScale.top).toBeCloseToPixel(118);
+ expect(chart.scales.xScale.labelRotation).toBeCloseTo(40, -1);
// Are yScales at the right spot
- expect(chart.scales.yScale1.bottom).toBeCloseToPixel(102);
- expect(chart.scales.yScale1.left).toBeCloseToPixel(0);
- expect(chart.scales.yScale1.right).toBeCloseToPixel(41);
+ expect(chart.scales.yScale1.bottom).toBeCloseToPixel(118);
+ expect(chart.scales.yScale1.left).toBeCloseToPixel(41);
+ expect(chart.scales.yScale1.right).toBeCloseToPixel(73);
expect(chart.scales.yScale1.top).toBeCloseToPixel(32);
expect(chart.scales.yScale1.labelRotation).toBeCloseTo(0);
- expect(chart.scales.yScale2.bottom).toBeCloseToPixel(102);
- expect(chart.scales.yScale2.left).toBeCloseToPixel(41);
- expect(chart.scales.yScale2.right).toBeCloseToPixel(86);
+ expect(chart.scales.yScale2.bottom).toBeCloseToPixel(118);
+ expect(chart.scales.yScale2.left).toBeCloseToPixel(0);
+ expect(chart.scales.yScale2.right).toBeCloseToPixel(41);
expect(chart.scales.yScale2.top).toBeCloseToPixel(32);
expect(chart.scales.yScale2.labelRotation).toBeCloseTo(0);
});
- xit ('should fix a full width box correctly', function() {
+ it ('should fit a full width box correctly', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
});
expect(chart.chartArea.bottom).toBeCloseToPixel(484);
- expect(chart.chartArea.left).toBeCloseToPixel(45);
- expect(chart.chartArea.right).toBeCloseToPixel(512);
- expect(chart.chartArea.top).toBeCloseToPixel(60);
+ expect(chart.chartArea.left).toBeCloseToPixel(40);
+ expect(chart.chartArea.right).toBeCloseToPixel(496);
+ expect(chart.chartArea.top).toBeCloseToPixel(62);
// Are xScales at the right spot
expect(chart.scales.xScale1.bottom).toBeCloseToPixel(512);
- expect(chart.scales.xScale1.left).toBeCloseToPixel(45);
- expect(chart.scales.xScale1.right).toBeCloseToPixel(512);
+ expect(chart.scales.xScale1.left).toBeCloseToPixel(40);
+ expect(chart.scales.xScale1.right).toBeCloseToPixel(496);
expect(chart.scales.xScale1.top).toBeCloseToPixel(484);
- expect(chart.scales.xScale2.bottom).toBeCloseToPixel(60);
+ expect(chart.scales.xScale2.bottom).toBeCloseToPixel(62);
expect(chart.scales.xScale2.left).toBeCloseToPixel(0);
expect(chart.scales.xScale2.right).toBeCloseToPixel(512);
expect(chart.scales.xScale2.top).toBeCloseToPixel(32);
// Is yScale at the right spot
expect(chart.scales.yScale.bottom).toBeCloseToPixel(484);
expect(chart.scales.yScale.left).toBeCloseToPixel(0);
- expect(chart.scales.yScale.right).toBeCloseToPixel(45);
- expect(chart.scales.yScale.top).toBeCloseToPixel(60);
+ expect(chart.scales.yScale.right).toBeCloseToPixel(40);
+ expect(chart.scales.yScale.top).toBeCloseToPixel(62);
});
describe('padding settings', function() {
});
var xScale = chart.scales.xScale0;
+ var yScale = chart.scales.yScale0;
expect(xScale.paddingTop).toBeCloseToPixel(0);
expect(xScale.paddingBottom).toBeCloseToPixel(0);
- expect(xScale.paddingLeft).toBeCloseToPixel(0);
- expect(xScale.paddingRight).toBeCloseToPixel(0);
+ expect(xScale.paddingLeft).toBeCloseToPixel(12);
+ expect(xScale.paddingRight).toBeCloseToPixel(13.5);
expect(xScale.width).toBeCloseToPixel(468 - 6); // minus lineSpace
expect(xScale.height).toBeCloseToPixel(30);
- var yScale = chart.scales.yScale0;
- expect(yScale.paddingTop).toBeCloseToPixel(0);
- expect(yScale.paddingBottom).toBeCloseToPixel(0);
+ expect(yScale.paddingTop).toBeCloseToPixel(7);
+ expect(yScale.paddingBottom).toBeCloseToPixel(7);
expect(yScale.paddingLeft).toBeCloseToPixel(0);
expect(yScale.paddingRight).toBeCloseToPixel(0);
expect(yScale.width).toBeCloseToPixel(30 + 6); // plus lineSpace
expect(xScale.paddingTop).toBeCloseToPixel(0);
expect(xScale.paddingBottom).toBeCloseToPixel(0);
- expect(xScale.paddingLeft).toBeCloseToPixel(0);
- expect(xScale.paddingRight).toBeCloseToPixel(0);
+ expect(xScale.paddingLeft).toBeCloseToPixel(12);
+ expect(xScale.paddingRight).toBeCloseToPixel(13.5);
expect(xScale.width).toBeCloseToPixel(440);
expect(xScale.height).toBeCloseToPixel(53);
- expect(yScale.paddingTop).toBeCloseToPixel(0);
- expect(yScale.paddingBottom).toBeCloseToPixel(0);
+ expect(yScale.paddingTop).toBeCloseToPixel(7);
+ expect(yScale.paddingBottom).toBeCloseToPixel(7);
expect(yScale.paddingLeft).toBeCloseToPixel(0);
expect(yScale.paddingRight).toBeCloseToPixel(0);
expect(yScale.width).toBeCloseToPixel(58);
xAxes: [{
id: 'xScale0',
type: 'time',
+ time: {
+ displayFormats: {
+ second: 'h:mm:ss'
+ }
+ },
ticks: {
callback: function(value) {
return '<' + value + '>';
expect(scale._ticks.map(function(tick) {
return tick.major;
- })).toEqual([true, false, false, false, true]);
- expect(scale.ticks).toEqual(['<8:00:00 pm>', '<8:00:15 pm>', '<8:00:30 pm>', '<8:00:45 pm>', '<8:01:00 pm>']);
+ })).toEqual([true, false, false, false, false, false, true]);
+ expect(scale.ticks).toEqual(['<8:00:00>', '<8:00:10>', '<8:00:20>', '<8:00:30>', '<8:00:40>', '<8:00:50>', '<8:01:00>']);
});
it('should update ticks.callback correctly', function() {
return '{' + value + '}';
};
chart.update();
- expect(scale.ticks).toEqual(['{8:00:00 pm}', '{8:00:15 pm}', '{8:00:30 pm}', '{8:00:45 pm}', '{8:01:00 pm}']);
+ expect(scale.ticks).toEqual(['{8:00:00}', '{8:00:10}', '{8:00:20}', '{8:00:30}', '{8:00:40}', '{8:00:50}', '{8:01:00}']);
});
});
expect(scale._ticks.map(function(tick) {
return tick.major;
- })).toEqual([true, false, false, false, true]);
- expect(scale.ticks).toEqual(['[[8:00 pm]]', '(8:00:15 pm)', '(8:00:30 pm)', '(8:00:45 pm)', '[[8:01 pm]]']);
+ })).toEqual([true, false, false, false, false, false, true]);
+ expect(scale.ticks).toEqual(['[[8:00 pm]]', '(8:00:10 pm)', '(8:00:20 pm)', '(8:00:30 pm)', '(8:00:40 pm)', '(8:00:50 pm)', '[[8:01 pm]]']);
});
it('should only use ticks.minor callback if ticks.major.enabled is false', function() {
chart.options.scales.xAxes[0].ticks.major.enabled = false;
chart.update();
- expect(scale.ticks).toEqual(['(8:00:00 pm)', '(8:00:15 pm)', '(8:00:30 pm)', '(8:00:45 pm)', '(8:01:00 pm)']);
+ expect(scale.ticks).toEqual(['(8:00:00 pm)', '(8:00:10 pm)', '(8:00:20 pm)', '(8:00:30 pm)', '(8:00:40 pm)', '(8:00:50 pm)', '(8:01:00 pm)']);
});
it('should use ticks.callback if ticks.major.callback is omitted', function() {
chart.options.scales.xAxes[0].ticks.major.callback = undefined;
chart.update();
- expect(scale.ticks).toEqual(['<8:00 pm>', '(8:00:15 pm)', '(8:00:30 pm)', '(8:00:45 pm)', '<8:01 pm>']);
+ expect(scale.ticks).toEqual(['<8:00 pm>', '(8:00:10 pm)', '(8:00:20 pm)', '(8:00:30 pm)', '(8:00:40 pm)', '(8:00:50 pm)', '<8:01 pm>']);
});
it('should use ticks.callback if ticks.minor.callback is omitted', function() {
chart.options.scales.xAxes[0].ticks.minor.callback = undefined;
chart.update();
- expect(scale.ticks).toEqual(['[[8:00 pm]]', '<8:00:15 pm>', '<8:00:30 pm>', '<8:00:45 pm>', '[[8:01 pm]]']);
+ expect(scale.ticks).toEqual(['[[8:00 pm]]', '<8:00:10 pm>', '<8:00:20 pm>', '<8:00:30 pm>', '<8:00:40 pm>', '<8:00:50 pm>', '[[8:01 pm]]']);
});
});