## Option Context
The option context is used to give contextual information when resolving options and currently only applies to [scriptable options](#scriptable-options).
+The object is preserved, so it can be used to store and pass information between calls.
+
+There are multiple levels of context objects:
+
+- `chart`
+ - `dataset`
+ - `data`
+ - `scale`
+ - `tick`
+
+Each level inherits its parent(s) and any contextual information stored in the parent is available through the child.
The context object contains the following properties:
+### chart
+
- `chart`: the associated chart
-- `dataPoint`: the parsed data values for the given `dataIndex` and `datasetIndex`
-- `dataIndex`: index of the current data
+
+### dataset
+
+In addition to [chart](#chart)
+
+- `active`: true if element is active (hovered)
- `dataset`: dataset at index `datasetIndex`
- `datasetIndex`: index of the current dataset
+- `index`: getter for `datasetIndex`
+
+### data
+
+In addition to [dataset](#dataset)
+
- `active`: true if element is active (hovered)
+- `dataIndex`: index of the current data
+- `dataPoint`: the parsed data values for the given `dataIndex` and `datasetIndex`
+- `element`: the element (point, arc, bar, etc.) for this data
+- `index`: getter for `dataIndex`
+
+### scale
+
+In addition to [chart](#chart)
+
+- `scale`: the associated scale
+
+### tick
+
+In addition to [scale](#scale)
-**Important**: since the context can represent different types of entities (dataset, data, ticks, etc.), some properties may be `undefined` so be sure to test any context property before using it.
+- `tick`: the associated tick object
+- `index`: tick index
};
window.onload = function() {
var ctx = document.getElementById('canvas').getContext('2d');
- var started = {};
window.myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
var delay = 0;
var dsIndex = context.datasetIndex;
var index = context.dataIndex;
- if (!started[index + dsIndex * 1000]) {
+ if (context.dataPoint && !context.delayed) {
delay = index * 300 + dsIndex * 100;
- started[index + dsIndex * 1000] = true;
+ context.delayed = true;
}
return {
easing: 'linear',
this._hiddenIndices = {};
this.attached = false;
this._animationsDisabled = undefined;
+ this.$context = undefined;
// Add the chart instance to the global namespace
Chart.instances[me.id] = me;
return meta;
}
+ getContext() {
+ return this.$context || (this.$context = Object.create(null, {
+ chart: {
+ value: this
+ }
+ }));
+ }
+
getVisibleDatasetCount() {
return this.getSortedVisibleDatasetMetas().length;
}
return Object.keys(scales).filter(key => scales[key].axis === axis).shift();
}
+function createDatasetContext(parent, index, dataset) {
+ return Object.create(parent, {
+ active: {
+ writable: true,
+ value: false
+ },
+ dataset: {
+ value: dataset
+ },
+ datasetIndex: {
+ value: index
+ },
+ index: {
+ get() {
+ return this.datasetIndex;
+ }
+ }
+ });
+}
+
+function createDataContext(parent, index, point, element) {
+ return Object.create(parent, {
+ active: {
+ writable: true,
+ value: false
+ },
+ dataIndex: {
+ value: index
+ },
+ dataPoint: {
+ value: point
+ },
+ element: {
+ value: element
+ },
+ index: {
+ get() {
+ return this.dataIndex;
+ }
+ }
+ });
+}
+
const optionKeys = (optionNames) => isArray(optionNames) ? optionNames : Object.keys(optionNames);
const optionKey = (key, active) => active ? 'hover' + _capitalize(key) : key;
const isDirectUpdateMode = (mode) => mode === 'reset' || mode === 'none';
this._drawStart = undefined;
this._drawCount = undefined;
this.enableOptionSharing = false;
+ this.$context = undefined;
this.initialize();
}
return this._cachedMeta._parsed[index];
}
+ /**
+ * @protected
+ */
+ getDataElement(index) {
+ return this._cachedMeta.data[index];
+ }
+
/**
* @protected
*/
* @protected
*/
getContext(index, active) {
- return {
- chart: this.chart,
- dataPoint: this.getParsed(index),
- dataIndex: index,
- dataset: this.getDataset(),
- datasetIndex: this.index,
- active
- };
+ const me = this;
+ let context;
+ if (index >= 0 && index < me._cachedMeta.data.length) {
+ const element = me._cachedMeta.data[index];
+ context = element.$context ||
+ (element.$context = createDataContext(me.getContext(), index, me.getParsed(index), element));
+ } else {
+ context = me.$context || (me.$context = createDatasetContext(me.chart.getContext(), me.index, me.getDataset()));
+ }
+
+ context.active = !!active;
+ return context;
}
/**
/**
* @typedef { import("./core.controller").default } Chart
- * @typedef {{value:any, label?:string, major?:boolean}} Tick
+ * @typedef {{value:any, label?:string, major?:boolean, $context?:any}} Tick
*/
defaults.set('scale', {
}
}
+function createScaleContext(parent, scale) {
+ return Object.create(parent, {
+ scale: {
+ value: scale
+ },
+ });
+}
+
+function createTickContext(parent, index, tick) {
+ return Object.create(parent, {
+ tick: {
+ value: tick
+ },
+ index: {
+ value: index
+ }
+ });
+}
+
export default class Scale extends Element {
// eslint-disable-next-line max-statements
this._ticksLength = 0;
this._borderValue = 0;
this._cache = {};
+ this.$context = undefined;
}
/**
* @protected
*/
getContext(index) {
- const ticks = this.ticks || [];
- return {
- chart: this.chart,
- scale: this,
- tick: ticks[index],
- index
- };
+ const me = this;
+ const ticks = me.ticks || [];
+
+ if (index >= 0 && index < ticks.length) {
+ const tick = ticks[index];
+ return tick.$context ||
+ (tick.$context = createTickContext(me.getContext(), index, tick));
+ }
+ return me.$context ||
+ (me.$context = createScaleContext(me.chart.getContext(), me));
}
/**
// option in dataset
data: [4, 5, 10, null, -10, -5],
backgroundColor: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index === 0 ? '#ff0000'
: index === 1 ? '#00ff00'
: '#ff00ff';
elements: {
line: {
backgroundColor: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index === 0 ? '#ff0000'
: index === 1 ? '#00ff00'
: '#ff00ff';
// option in dataset
data: [4, 5, 10, null, -10, -5],
borderColor: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index === 0 ? '#ff0000'
: index === 1 ? '#00ff00'
: '#0000ff';
elements: {
line: {
borderColor: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index === 0 ? '#ff0000'
: index === 1 ? '#00ff00'
: '#0000ff';
data: [4, 5, 10, null, -10, -5],
borderColor: '#0000ff',
borderWidth: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index % 2 ? 10 : 20;
},
pointBorderColor: '#00ff00'
line: {
borderColor: '#ff0000',
borderWidth: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index % 2 ? 10 : 20;
},
fill: false,
// option in dataset
data: [0, 5, 10, null, -10, -5],
backgroundColor: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index === 0 ? '#ff0000'
: index === 1 ? '#00ff00'
: '#ff00ff';
elements: {
line: {
backgroundColor: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index === 0 ? '#ff0000'
: index === 1 ? '#00ff00'
: '#ff00ff';
// option in dataset
data: [0, 5, 10, null, -10, -5],
borderColor: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index === 0 ? '#ff0000'
: index === 1 ? '#00ff00'
: '#0000ff';
elements: {
line: {
borderColor: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index === 0 ? '#ff0000'
: index === 1 ? '#00ff00'
: '#0000ff';
data: [0, 5, 10, null, -10, -5],
borderColor: '#0000ff',
borderWidth: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index % 2 ? 10 : 20;
},
pointBorderColor: '#00ff00'
line: {
borderColor: '#ff0000',
borderWidth: function(ctx) {
- var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
+ var index = ctx.index;
return index % 2 ? 10 : 20;
},
fill: false