return color(c);
};
helpers.addResizeListener = function(node, callback) {
- // Hide an iframe before the node
var iframe = document.createElement('iframe');
-
iframe.className = 'chartjs-hidden-iframe';
iframe.style.cssText =
'display:block;'+
// https://github.com/chartjs/Chart.js/issues/3090
iframe.tabIndex = -1;
- // Insert the iframe so that contentWindow is available
- node.insertBefore(iframe, node.firstChild);
-
// Let's keep track of this added iframe and thus avoid DOM query when removing it.
- node._chartjs = {
- resizer: iframe
+ var stub = node._chartjs = {
+ resizer: iframe,
+ ticking: false
+ };
+
+ // Throttle the callback notification until the next animation frame.
+ var notify = function() {
+ if (!stub.ticking) {
+ stub.ticking = true;
+ helpers.requestAnimFrame.call(window, function() {
+ if (stub.resizer) {
+ stub.ticking = false;
+ return callback();
+ }
+ });
+ }
};
- this.addEvent(iframe.contentWindow || iframe, 'resize', callback);
+ // If the iframe is re-attached to the DOM, the resize listener is removed because the
+ // content is reloaded, so make sure to install the handler after the iframe is loaded.
+ // https://github.com/chartjs/Chart.js/issues/3521
+ helpers.addEvent(iframe, 'load', function() {
+ helpers.addEvent(iframe.contentWindow || iframe, 'resize', notify);
+
+ // The iframe size might have changed while loading, which can also
+ // happen if the size has been changed while detached from the DOM.
+ notify();
+ });
+
+ node.insertBefore(iframe, node.firstChild);
};
helpers.removeResizeListener = function(node) {
if (!node || !node._chartjs) {
var iframe = node._chartjs.resizer;
if (iframe) {
iframe.parentNode.removeChild(iframe);
+ node._chartjs.resizer = null;
}
delete node._chartjs;
describe('Chart.Controller', function() {
- function processResizeEvent(chart, callback) {
- setTimeout(callback, 100);
+ function waitForResize(chart, callback) {
+ var resizer = chart.chart.canvas.parentNode._chartjs.resizer;
+ var content = resizer.contentWindow || resizer;
+ var state = content.document.readyState || 'complete';
+ var handler = function() {
+ Chart.helpers.removeEvent(content, 'load', handler);
+ Chart.helpers.removeEvent(content, 'resize', handler);
+ setTimeout(callback, 50);
+ };
+
+ Chart.helpers.addEvent(content, state !== 'complete'? 'load' : 'resize', handler);
}
describe('context acquisition', function() {
var wrapper = chart.chart.canvas.parentNode;
wrapper.style.width = '455px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 455, dh: 350,
rw: 455, rh: 350,
});
wrapper.style.width = '150px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 150, dh: 350,
rw: 150, rh: 350,
var wrapper = chart.chart.canvas.parentNode;
wrapper.style.height = '455px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 300, dh: 455,
rw: 300, rh: 455,
});
wrapper.style.height = '150px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 300, dh: 150,
rw: 300, rh: 150,
var wrapper = chart.chart.canvas.parentNode;
wrapper.style.height = '355px';
wrapper.style.width = '455px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 455, dh: 355,
rw: 455, rh: 355,
var canvas = chart.chart.canvas;
canvas.style.display = 'block';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 320, dh: 350,
rw: 320, rh: 350,
var wrapper = chart.chart.canvas.parentNode;
wrapper.style.display = 'block';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 460, dh: 380,
rw: 460, rh: 380,
done();
});
});
+
+ // https://github.com/chartjs/Chart.js/issues/3521
+ it('should resize the canvas after the wrapper has been re-attached to the DOM', function(done) {
+ var chart = acquireChart({
+ options: {
+ responsive: true,
+ maintainAspectRatio: false
+ }
+ }, {
+ canvas: {
+ style: ''
+ },
+ wrapper: {
+ style: 'width: 320px; height: 350px'
+ }
+ });
+
+ expect(chart).toBeChartOfSize({
+ dw: 320, dh: 350,
+ rw: 320, rh: 350,
+ });
+
+ var wrapper = chart.chart.canvas.parentNode;
+ var parent = wrapper.parentNode;
+ parent.removeChild(wrapper);
+ parent.appendChild(wrapper);
+ wrapper.style.height = '355px';
+
+ waitForResize(chart, function() {
+ expect(chart).toBeChartOfSize({
+ dw: 320, dh: 355,
+ rw: 320, rh: 355,
+ });
+
+ parent.removeChild(wrapper);
+ wrapper.style.width = '455px';
+ parent.appendChild(wrapper);
+
+ waitForResize(chart, function() {
+ expect(chart).toBeChartOfSize({
+ dw: 455, dh: 355,
+ rw: 455, rh: 355,
+ });
+
+ done();
+ });
+ });
+ });
});
describe('config.options.responsive: true (maintainAspectRatio: true)', function() {
var wrapper = chart.chart.canvas.parentNode;
wrapper.style.width = '450px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 450, dh: 225,
rw: 450, rh: 225,
});
wrapper.style.width = '150px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 150, dh: 75,
rw: 150, rh: 75,
var wrapper = chart.chart.canvas.parentNode;
wrapper.style.height = '455px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 320, dh: 160,
rw: 320, rh: 160,
});
wrapper.style.height = '150px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 320, dh: 160,
rw: 320, rh: 160,
var canvas = chart.chart.canvas;
var wrapper = canvas.parentNode;
wrapper.style.width = '475px';
- processResizeEvent(chart, function() {
+ waitForResize(chart, function() {
expect(chart).toBeChartOfSize({
dw: 475, dh: 450,
rw: 475, rh: 450,