* `scales.[x/y]Axes.time.max` was renamed to `scales[id].max`
* `scales.[x/y]Axes.time.min` was renamed to `scales[id].min`
* The dataset option `tension` was renamed to `lineTension`
+* To override the platform class used in a chart instance, pass `platform: PlatformClass` in the config object. Note that the class should be passed, not an instance of the class.
### Animations
* `helpers._alignPixel` was renamed to `helpers.canvas._alignPixel`
* `helpers._decimalPlaces` was renamed to `helpers.math._decimalPlaces`
+* `chart.initialize` was renamed to `chart._initialize` (labeled as private but not named as such)
### Changed
* The second parameter to `drawPoint` is now the full options object, so `style`, `rotation`, and `radius` are no longer passed explicitly
+#### Platform
+
+* `Chart.platform` is no longer the platform object used by charts. It contains only a single configuration option, `disableCSSInjection`. Every chart instance now has a separate platform instance.
+* `Chart.platforms` is an object that contains two usable platform classes, `BasicPlatform` and `DomPlatform`. It also contains `BasePlatform`, a class that all platforms must extend from.
+* If the canvas passed in is an instance of `OffscreenCanvas`, the `BasicPlatform` is automatically used.
var utils = Samples.utils;
+utils.srand(110);
// CSP: disable automatic style injection
Chart.platform.disableCSSInjection = true;
-utils.srand(110);
-
function generateData() {
var DATA_COUNT = 16;
var MIN_XY = -150;
import helpers from '../helpers/index';
import Interaction from './core.interaction';
import layouts from './core.layouts';
-import platform from '../platforms/platform';
+import {BasicPlatform, DomPlatform} from '../platform/platforms';
import plugins from './core.plugins';
import scaleService from '../core/core.scaleService';
helpers.callback(animationOptions && animationOptions.onProgress, arguments, chart);
}
+function isDomSupported() {
+ return typeof window !== undefined && typeof document !== undefined;
+}
+
+/**
+ * Chart.js can take a string id of a canvas element, a 2d context, or a canvas element itself.
+ * Attempt to unwrap the item passed into the chart constructor so that it is a canvas element (if possible).
+ */
+function getCanvas(item) {
+ if (isDomSupported() && typeof item === 'string') {
+ item = document.getElementById(item);
+ } else if (item.length) {
+ // Support for array based queries (such as jQuery)
+ item = item[0];
+ }
+
+ if (item && item.canvas) {
+ // Support for any object associated to a canvas (including a context2d)
+ item = item.canvas;
+ }
+ return item;
+}
+
class Chart {
constructor(item, config) {
const me = this;
config = initConfig(config);
+ const initialCanvas = getCanvas(item);
+ me._initializePlatform(initialCanvas, config);
- const context = platform.acquireContext(item, config);
+ const context = me.platform.acquireContext(initialCanvas, config);
const canvas = context && context.canvas;
const height = canvas && canvas.height;
const width = canvas && canvas.width;
Animator.listen(me, 'complete', onAnimationsComplete);
Animator.listen(me, 'progress', onAnimationProgress);
- me.initialize();
+ me._initialize();
me.update();
}
/**
* @private
*/
- initialize() {
+ _initialize() {
const me = this;
// Before init plugin notification
return me;
}
+ /**
+ * @private
+ */
+ _initializePlatform(canvas, config) {
+ const me = this;
+
+ if (config.platform) {
+ me.platform = new config.platform();
+ } else if (!isDomSupported()) {
+ me.platform = new BasicPlatform();
+ } else if (window.OffscreenCanvas && canvas instanceof window.OffscreenCanvas) {
+ me.platform = new BasicPlatform();
+ } else {
+ me.platform = new DomPlatform();
+ }
+ }
+
clear() {
helpers.canvas.clear(this);
return this;
// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
const newWidth = Math.max(0, Math.floor(helpers.dom.getMaximumWidth(canvas)));
const newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.dom.getMaximumHeight(canvas)));
- const newRatio = options.devicePixelRatio || platform.getDevicePixelRatio();
+ const newRatio = options.devicePixelRatio || me.platform.getDevicePixelRatio();
if (me.width === newWidth && me.height === newHeight && oldRatio === newRatio) {
return;
if (canvas) {
me.unbindEvents();
helpers.canvas.clear(me);
- platform.releaseContext(me.ctx);
+ me.platform.releaseContext(me.ctx);
me.canvas = null;
me.ctx = null;
}
};
helpers.each(me.options.events, function(type) {
- platform.addEventListener(me, type, listener);
+ me.platform.addEventListener(me, type, listener);
listeners[type] = listener;
});
me.resize();
};
- platform.addEventListener(me, 'resize', listener);
+ me.platform.addEventListener(me, 'resize', listener);
listeners.resize = listener;
}
}
delete me._listeners;
helpers.each(listeners, function(listener, type) {
- platform.removeEventListener(me, type, listener);
+ me.platform.removeEventListener(me, type, listener);
});
}
import elements from './elements';
import Interaction from './core/core.interaction';
import layouts from './core/core.layouts';
-import platform from './platforms/platform';
+import platforms from './platform/platforms';
+import platform from './platform/platform';
import pluginsCore from './core/core.plugins';
import Scale from './core/core.scale';
import scaleService from './core/core.scaleService';
Chart.elements = elements;
Chart.Interaction = Interaction;
Chart.layouts = layouts;
+Chart.platforms = platforms;
Chart.platform = platform;
Chart.plugins = pluginsCore;
Chart.Scale = Scale;
}
}
-Chart.platform.initialize();
-
if (typeof window !== 'undefined') {
window.Chart = Chart;
}
'use strict';
-import helpers from '../helpers/index';
-import basic from './platform.basic';
-import dom from './platform.dom';
-
-// @TODO Make possible to select another platform at build time.
-const implementation = dom._enabled ? dom : basic;
-
/**
- * @namespace Chart.platform
- * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
- * @since 2.4.0
+ * Abstract class that allows abstracting platform dependencies away from the chart.
*/
-export default helpers.extend({
+export default class BasePlatform {
/**
- * @since 2.7.0
+ * @constructor
*/
- initialize: function() {},
+ constructor() {}
/**
* Called at chart construction time, returns a context2d instance implementing
* the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
- * @param {*} item - The native item from which to acquire context (platform specific)
+ * @param {canvas} canvas - The canvas from which to acquire context (platform specific)
* @param {object} options - The chart options
* @returns {CanvasRenderingContext2D} context2d instance
*/
- acquireContext: function() {},
+ acquireContext() {}
/**
* Called at chart destruction time, releases any resources associated to the context
* @param {CanvasRenderingContext2D} context - The context2d instance
* @returns {boolean} true if the method succeeded, else false
*/
- releaseContext: function() {},
+ releaseContext() {}
/**
* Registers the specified listener on the given chart.
* @param {function} listener - Receives a notification (an object that implements
* the {@link IEvent} interface) when an event of the specified type occurs.
*/
- addEventListener: function() {},
+ addEventListener() {}
/**
* Removes the specified listener previously registered with addEventListener.
* @param {string} type - The ({@link IEvent}) type to remove
* @param {function} listener - The listener function to remove from the event target.
*/
- removeEventListener: function() {},
+ removeEventListener() {}
/**
- * Returs current devicePixelRatio of the device this platform is connected to.
+ * @returns {number} the current devicePixelRatio of the device this platform is connected to.
*/
- getDevicePixelRatio: function() {
+ getDevicePixelRatio() {
return 1;
}
-
-}, implementation);
-
-/**
- * @interface IPlatform
- * Allows abstracting platform dependencies away from the chart
- * @borrows Chart.platform.acquireContext as acquireContext
- * @borrows Chart.platform.releaseContext as releaseContext
- * @borrows Chart.platform.addEventListener as addEventListener
- * @borrows Chart.platform.removeEventListener as removeEventListener
- */
+}
/**
* @interface IEvent
--- /dev/null
+/**
+ * Platform fallback implementation (minimal).
+ * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
+ */
+
+'use strict';
+
+import BasePlatform from './platform.base';
+
+/**
+ * Platform class for charts without access to the DOM or to many element properties
+ * This platform is used by default for any chart passed an OffscreenCanvas.
+ * @extends BasePlatform
+ */
+export default class BasicPlatform extends BasePlatform {
+ acquireContext(item) {
+ // To prevent canvas fingerprinting, some add-ons undefine the getContext
+ // method, for example: https://github.com/kkapsner/CanvasBlocker
+ // https://github.com/chartjs/Chart.js/issues/2807
+ return item && item.getContext && item.getContext('2d') || null;
+ }
+}
import helpers from '../helpers';
import stylesheet from './platform.dom.css';
+import BasePlatform from './platform.base';
+import platform from './platform';
var EXPANDO_KEY = '$chartjs';
var CSS_PREFIX = 'chartjs-';
}
}
-export default {
+/**
+ * Platform class for charts that can access the DOM and global window/document properties
+ * @extends BasePlatform
+ */
+export default class DomPlatform extends BasePlatform {
/**
- * When `true`, prevents the automatic injection of the stylesheet required to
- * correctly detect when the chart is added to the DOM and then resized. This
- * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`)
- * to be manually imported to make this library compatible with any CSP.
- * See https://github.com/chartjs/Chart.js/issues/5208
+ * @constructor
*/
- disableCSSInjection: false,
-
- /**
- * This property holds whether this platform is enabled for the current environment.
- * Currently used by platform.js to select the proper implementation.
- * @private
- */
- _enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
+ constructor() {
+ super();
+
+ /**
+ * When `true`, prevents the automatic injection of the stylesheet required to
+ * correctly detect when the chart is added to the DOM and then resized. This
+ * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`)
+ * to be manually imported to make this library compatible with any CSP.
+ * See https://github.com/chartjs/Chart.js/issues/5208
+ */
+ this.disableCSSInjection = platform.disableCSSInjection;
+ }
/**
* Initializes resources that depend on platform options.
* @param {HTMLCanvasElement} canvas - The Canvas element.
* @private
*/
- _ensureLoaded: function(canvas) {
+ _ensureLoaded(canvas) {
if (!this.disableCSSInjection) {
// If the canvas is in a shadow DOM, then the styles must also be inserted
// into the same shadow DOM.
var targetNode = root.host ? root : document.head;
injectCSS(targetNode, stylesheet);
}
- },
-
- acquireContext: function(item, config) {
- if (typeof item === 'string') {
- item = document.getElementById(item);
- } else if (item.length) {
- // Support for array based queries (such as jQuery)
- item = item[0];
- }
-
- if (item && item.canvas) {
- // Support for any object associated to a canvas (including a context2d)
- item = item.canvas;
- }
+ }
+ acquireContext(canvas, config) {
// To prevent canvas fingerprinting, some add-ons undefine the getContext
// method, for example: https://github.com/kkapsner/CanvasBlocker
// https://github.com/chartjs/Chart.js/issues/2807
- var context = item && item.getContext && item.getContext('2d');
+ var context = canvas && canvas.getContext && canvas.getContext('2d');
- // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
+ // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the canvas is
// inside an iframe or when running in a protected environment. We could guess the
// types from their toString() value but let's keep things flexible and assume it's
- // a sufficient condition if the item has a context2D which has item as `canvas`.
+ // a sufficient condition if the canvas has a context2D which has canvas as `canvas`.
// https://github.com/chartjs/Chart.js/issues/3887
// https://github.com/chartjs/Chart.js/issues/4102
// https://github.com/chartjs/Chart.js/issues/4152
- if (context && context.canvas === item) {
+ if (context && context.canvas === canvas) {
// Load platform resources on first chart creation, to make it possible to
// import the library before setting platform options.
- this._ensureLoaded(item);
- initCanvas(item, config);
+ this._ensureLoaded(canvas);
+ initCanvas(canvas, config);
return context;
}
return null;
- },
+ }
- releaseContext: function(context) {
+ releaseContext(context) {
const canvas = context.canvas;
if (!canvas[EXPANDO_KEY]) {
return;
canvas.width = canvas.width;
delete canvas[EXPANDO_KEY];
- },
+ }
- addEventListener: function(chart, type, listener) {
+ addEventListener(chart, type, listener) {
var canvas = chart.canvas;
if (type === 'resize') {
// Note: the resize event is not supported on all browsers.
}, chart);
addListener(canvas, type, proxy);
- },
+ }
- removeEventListener: function(chart, type, listener) {
+ removeEventListener(chart, type, listener) {
var canvas = chart.canvas;
if (type === 'resize') {
// Note: the resize event is not supported on all browsers.
}
removeListener(canvas, type, proxy);
- },
+ }
- getDevicePixelRatio: function() {
+ getDevicePixelRatio() {
return window.devicePixelRatio;
}
-};
+}
--- /dev/null
+'use strict';
+
+export default {disableCSSInjection: false};
--- /dev/null
+'use strict';
+
+import BasePlatform from './platform.base';
+import BasicPlatform from './platform.basic';
+import DomPlatform from './platform.dom';
+
+export {BasicPlatform, DomPlatform, BasePlatform};
+
+/**
+ * @namespace Chart.platforms
+ * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
+*/
+export default {BasicPlatform, DomPlatform, BasePlatform};
+++ /dev/null
-/**
- * Platform fallback implementation (minimal).
- * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
- */
-
-module.exports = {
- acquireContext: function(item) {
- if (item && item.canvas) {
- // Support for any object associated to a canvas (including a context2d)
- item = item.canvas;
- }
-
- return item && item.getContext('2d') || null;
- }
-};
expect(Chart.Interaction instanceof Object).toBeTruthy();
expect(Chart.layouts instanceof Object).toBeTruthy();
expect(Chart.plugins instanceof Object).toBeTruthy();
- expect(Chart.platform instanceof Object).toBeTruthy();
+ expect(Chart.platforms instanceof Object).toBeTruthy();
expect(Chart.Scale instanceof Object).toBeTruthy();
expect(Chart.scaleService instanceof Object).toBeTruthy();
expect(Chart.Ticks instanceof Object).toBeTruthy();