123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017 |
- /*!
- * @license
- * chartjs-plugin-datalabels
- * http://chartjs.org/
- * Version: 0.3.0
- *
- * Copyright 2018 Chart.js Contributors
- * Released under the MIT license
- * https://github.com/chartjs/chartjs-plugin-datalabels/blob/master/LICENSE.md
- */
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('chart.js')) :
- typeof define === 'function' && define.amd ? define(['chart.js'], factory) :
- (factory(global.Chart));
- }(this, (function (Chart) { 'use strict';
- Chart = Chart && Chart.hasOwnProperty('default') ? Chart['default'] : Chart;
- 'use strict';
- var helpers$2 = Chart.helpers;
- var HitBox = function() {
- this._rect = null;
- this._rotation = 0;
- };
- helpers$2.extend(HitBox.prototype, {
- update: function(center, rect, rotation) {
- var margin = 1;
- var cx = center.x;
- var cy = center.y;
- var x = cx + rect.x;
- var y = cy + rect.y;
- this._rotation = rotation;
- this._rect = {
- x0: x - margin,
- y0: y - margin,
- x1: x + rect.w + margin * 2,
- y1: y + rect.h + margin * 2,
- cx: cx,
- cy: cy,
- };
- },
- contains: function(x, y) {
- var me = this;
- var rect = me._rect;
- var cx, cy, r, rx, ry;
- if (!rect) {
- return false;
- }
- cx = rect.cx;
- cy = rect.cy;
- r = me._rotation;
- rx = cx + (x - cx) * Math.cos(r) + (y - cy) * Math.sin(r);
- ry = cy - (x - cx) * Math.sin(r) + (y - cy) * Math.cos(r);
- return !(rx < rect.x0
- || ry < rect.y0
- || rx > rect.x1
- || ry > rect.y1);
- }
- });
- 'use strict';
- var helpers$3 = Chart.helpers;
- var utils = {
- // @todo move this in Chart.helpers.toTextLines
- toTextLines: function(inputs) {
- var lines = [];
- var input;
- inputs = [].concat(inputs);
- while (inputs.length) {
- input = inputs.pop();
- if (typeof input === 'string') {
- lines.unshift.apply(lines, input.split('\n'));
- } else if (Array.isArray(input)) {
- inputs.push.apply(inputs, input);
- } else if (!helpers$3.isNullOrUndef(inputs)) {
- lines.unshift('' + input);
- }
- }
- return lines;
- },
- // @todo move this method in Chart.helpers.canvas.toFont (deprecates helpers.fontString)
- // @see https://developer.mozilla.org/en-US/docs/Web/CSS/font
- toFontString: function(font) {
- if (!font || helpers$3.isNullOrUndef(font.size) || helpers$3.isNullOrUndef(font.family)) {
- return null;
- }
- return (font.style ? font.style + ' ' : '')
- + (font.weight ? font.weight + ' ' : '')
- + font.size + 'px '
- + font.family;
- },
- // @todo move this in Chart.helpers.canvas.textSize
- // @todo cache calls of measureText if font doesn't change?!
- textSize: function(ctx, lines, font) {
- var items = [].concat(lines);
- var ilen = items.length;
- var prev = ctx.font;
- var width = 0;
- var i;
- ctx.font = font.string;
- for (i = 0; i < ilen; ++i) {
- width = Math.max(ctx.measureText(items[i]).width, width);
- }
- ctx.font = prev;
- return {
- height: ilen * font.lineHeight,
- width: width
- };
- },
- // @todo move this method in Chart.helpers.options.toFont
- parseFont: function(value) {
- var global = Chart.defaults.global;
- var size = helpers$3.valueOrDefault(value.size, global.defaultFontSize);
- var font = {
- family: helpers$3.valueOrDefault(value.family, global.defaultFontFamily),
- lineHeight: helpers$3.options.toLineHeight(value.lineHeight, size),
- size: size,
- style: helpers$3.valueOrDefault(value.style, global.defaultFontStyle),
- weight: helpers$3.valueOrDefault(value.weight, null),
- string: ''
- };
- font.string = utils.toFontString(font);
- return font;
- },
- /**
- * Returns value bounded by min and max. This is equivalent to max(min, min(value, max)).
- * @todo move this method in Chart.helpers.bound
- * https://doc.qt.io/qt-5/qtglobal.html#qBound
- */
- bound: function(min, value, max) {
- return Math.max(min, Math.min(value, max));
- },
- /**
- * Returns an array of pair [value, state] where state is:
- * * -1: value is only in a0 (removed)
- * * 1: value is only in a1 (added)
- */
- arrayDiff: function(a0, a1) {
- var prev = a0.slice();
- var updates = [];
- var i, j, ilen, v;
- for (i = 0, ilen = a1.length; i < ilen; ++i) {
- v = a1[i];
- j = prev.indexOf(v);
- if (j === -1) {
- updates.push([v, 1]);
- } else {
- prev.splice(j, 1);
- }
- }
- for (i = 0, ilen = prev.length; i < ilen; ++i) {
- updates.push([prev[i], -1]);
- }
- return updates;
- }
- };
- 'use strict';
- function orient(point, origin) {
- var x0 = origin.x;
- var y0 = origin.y;
- if (x0 === null) {
- return {x: 0, y: -1};
- }
- if (y0 === null) {
- return {x: 1, y: 0};
- }
- var dx = point.x - x0;
- var dy = point.y - y0;
- var ln = Math.sqrt(dx * dx + dy * dy);
- return {
- x: ln ? dx / ln : 0,
- y: ln ? dy / ln : -1
- };
- }
- function aligned(x, y, vx, vy, align) {
- switch (align) {
- case 'center':
- vx = vy = 0;
- break;
- case 'bottom':
- vx = 0;
- vy = 1;
- break;
- case 'right':
- vx = 1;
- vy = 0;
- break;
- case 'left':
- vx = -1;
- vy = 0;
- break;
- case 'top':
- vx = 0;
- vy = -1;
- break;
- case 'start':
- vx = -vx;
- vy = -vy;
- break;
- case 'end':
- // keep the natural orientation
- break;
- default:
- // clockwise rotation (in degree)
- align *= (Math.PI / 180);
- vx = Math.cos(align);
- vy = Math.sin(align);
- break;
- }
- return {
- x: x,
- y: y,
- vx: vx,
- vy: vy
- };
- }
- var positioners = {
- arc: function(vm, anchor, align) {
- var angle = (vm.startAngle + vm.endAngle) / 2;
- var vx = Math.cos(angle);
- var vy = Math.sin(angle);
- var r0 = vm.innerRadius;
- var r1 = vm.outerRadius;
- var d;
- if (anchor === 'start') {
- d = r0;
- } else if (anchor === 'end') {
- d = r1;
- } else {
- d = (r0 + r1) / 2;
- }
- return aligned(
- vm.x + vx * d,
- vm.y + vy * d,
- vx,
- vy,
- align);
- },
- point: function(vm, anchor, align, origin) {
- var v = orient(vm, origin);
- var r = vm.radius;
- var d = 0;
- if (anchor === 'start') {
- d = -r;
- } else if (anchor === 'end') {
- d = r;
- }
- return aligned(
- vm.x + v.x * d,
- vm.y + v.y * d,
- v.x,
- v.y,
- align);
- },
- rect: function(vm, anchor, align, origin) {
- var horizontal = vm.horizontal;
- var size = Math.abs(vm.base - (horizontal ? vm.x : vm.y));
- var x = horizontal ? Math.min(vm.x, vm.base) : vm.x;
- var y = horizontal ? vm.y : Math.min(vm.y, vm.base);
- var v = orient(vm, origin);
- if (anchor === 'center') {
- if (horizontal) {
- x += size / 2;
- } else {
- y += size / 2;
- }
- } else if (anchor === 'start' && !horizontal) {
- y += size;
- } else if (anchor === 'end' && horizontal) {
- x += size;
- }
- return aligned(
- x,
- y,
- v.x,
- v.y,
- align);
- },
- fallback: function(vm, anchor, align, origin) {
- var v = orient(vm, origin);
- return aligned(
- vm.x,
- vm.y,
- v.x,
- v.y,
- align);
- }
- };
- 'use strict';
- var helpers$1 = Chart.helpers;
- function boundingRects(size, padding) {
- var th = size.height;
- var tw = size.width;
- var tx = -tw / 2;
- var ty = -th / 2;
- return {
- frame: {
- x: tx - padding.left,
- y: ty - padding.top,
- w: tw + padding.width,
- h: th + padding.height,
- },
- text: {
- x: tx,
- y: ty,
- w: tw,
- h: th
- }
- };
- }
- function getScaleOrigin(el) {
- var horizontal = el._model.horizontal;
- var scale = el._scale || (horizontal && el._xScale) || el._yScale;
- if (!scale) {
- return null;
- }
- if (scale.xCenter !== undefined && scale.yCenter !== undefined) {
- return {x: scale.xCenter, y: scale.yCenter};
- }
- var pixel = scale.getBasePixel();
- return horizontal ?
- {x: pixel, y: null} :
- {x: null, y: pixel};
- }
- function getPositioner(el) {
- if (el instanceof Chart.elements.Arc) {
- return positioners.arc;
- }
- if (el instanceof Chart.elements.Point) {
- return positioners.point;
- }
- if (el instanceof Chart.elements.Rectangle) {
- return positioners.rect;
- }
- return positioners.fallback;
- }
- function coordinates(el, model, rect) {
- var point = model.positioner(el._view, model.anchor, model.align, model.origin);
- var vx = point.vx;
- var vy = point.vy;
- if (!vx && !vy) {
- // if aligned center, we don't want to offset the center point
- return {x: point.x, y: point.y};
- }
- // include borders to the bounding rect
- var borderWidth = model.borderWidth || 0;
- var w = (rect.w + borderWidth * 2);
- var h = (rect.h + borderWidth * 2);
- // take in account the label rotation
- var rotation = model.rotation;
- var dx = Math.abs(w / 2 * Math.cos(rotation)) + Math.abs(h / 2 * Math.sin(rotation));
- var dy = Math.abs(w / 2 * Math.sin(rotation)) + Math.abs(h / 2 * Math.cos(rotation));
- // scale the unit vector (vx, vy) to get at least dx or dy equal to w or h respectively
- // (else we would calculate the distance to the ellipse inscribed in the bounding rect)
- var vs = 1 / Math.max(Math.abs(vx), Math.abs(vy));
- dx *= vx * vs;
- dy *= vy * vs;
- // finally, include the explicit offset
- dx += model.offset * vx;
- dy += model.offset * vy;
- return {
- x: point.x + dx,
- y: point.y + dy
- };
- }
- function drawFrame(ctx, rect, model) {
- var bgColor = model.backgroundColor;
- var borderColor = model.borderColor;
- var borderWidth = model.borderWidth;
- if (!bgColor && (!borderColor || !borderWidth)) {
- return;
- }
- ctx.beginPath();
- helpers$1.canvas.roundedRect(
- ctx,
- Math.round(rect.x) - borderWidth / 2,
- Math.round(rect.y) - borderWidth / 2,
- Math.round(rect.w) + borderWidth,
- Math.round(rect.h) + borderWidth,
- model.borderRadius);
- ctx.closePath();
- if (bgColor) {
- ctx.fillStyle = bgColor;
- ctx.fill();
- }
- if (borderColor && borderWidth) {
- ctx.strokeStyle = borderColor;
- ctx.lineWidth = borderWidth;
- ctx.lineJoin = 'miter';
- ctx.stroke();
- }
- }
- function drawText(ctx, lines, rect, model) {
- var align = model.textAlign;
- var font = model.font;
- var lh = font.lineHeight;
- var color = model.color;
- var ilen = lines.length;
- var x, y, i;
- if (!ilen || !color) {
- return;
- }
- x = rect.x;
- y = rect.y + lh / 2;
- if (align === 'center') {
- x += rect.w / 2;
- } else if (align === 'end' || align === 'right') {
- x += rect.w;
- }
- ctx.font = model.font.string;
- ctx.fillStyle = color;
- ctx.textAlign = align;
- ctx.textBaseline = 'middle';
- for (i = 0; i < ilen; ++i) {
- ctx.fillText(
- lines[i],
- Math.round(x),
- Math.round(y),
- Math.round(rect.w));
- y += lh;
- }
- }
- var Label = function(config, ctx, el, index) {
- var me = this;
- me._hitbox = new HitBox();
- me._config = config;
- me._index = index;
- me._model = null;
- me._ctx = ctx;
- me._el = el;
- };
- helpers$1.extend(Label.prototype, {
- /**
- * @private
- */
- _modelize: function(lines, config, context) {
- var me = this;
- var index = me._index;
- var resolve = helpers$1.options.resolve;
- var font = utils.parseFont(resolve([config.font, {}], context, index));
- return {
- align: resolve([config.align, 'center'], context, index),
- anchor: resolve([config.anchor, 'center'], context, index),
- backgroundColor: resolve([config.backgroundColor, null], context, index),
- borderColor: resolve([config.borderColor, null], context, index),
- borderRadius: resolve([config.borderRadius, 0], context, index),
- borderWidth: resolve([config.borderWidth, 0], context, index),
- color: resolve([config.color, Chart.defaults.global.defaultFontColor], context, index),
- font: font,
- lines: lines,
- offset: resolve([config.offset, 0], context, index),
- opacity: resolve([config.opacity, 1], context, index),
- origin: getScaleOrigin(me._el),
- padding: helpers$1.options.toPadding(resolve([config.padding, 0], context, index)),
- positioner: getPositioner(me._el),
- rotation: resolve([config.rotation, 0], context, index) * (Math.PI / 180),
- size: utils.textSize(me._ctx, lines, font),
- textAlign: resolve([config.textAlign, 'start'], context, index)
- };
- },
- update: function(context) {
- var me = this;
- var model = null;
- var index = me._index;
- var config = me._config;
- var value, label, lines;
- if (helpers$1.options.resolve([config.display, true], context, index)) {
- value = context.dataset.data[index];
- label = helpers$1.valueOrDefault(helpers$1.callback(config.formatter, [value, context]), value);
- lines = helpers$1.isNullOrUndef(label) ? [] : utils.toTextLines(label);
- model = lines.length ? me._modelize(lines, config, context) : null;
- }
- me._model = model;
- },
- draw: function(ctx) {
- var me = this;
- var model = me._model;
- var rects, center;
- if (!model || !model.opacity) {
- return;
- }
- rects = boundingRects(model.size, model.padding);
- center = coordinates(me._el, model, rects.frame);
- me._hitbox.update(center, rects.frame, model.rotation);
- ctx.save();
- ctx.globalAlpha = utils.bound(0, model.opacity, 1);
- ctx.translate(Math.round(center.x), Math.round(center.y));
- ctx.rotate(model.rotation);
- drawFrame(ctx, rects.frame, model);
- drawText(ctx, model.lines, rects.text, model);
- ctx.restore();
- },
- contains: function(x, y) {
- return this._hitbox.contains(x, y);
- }
- });
- /**
- * @module Options
- */
- 'use strict';
- var helpers$4 = Chart.helpers;
- var defaults = {
- /**
- * The label box alignment relative to `anchor` that can be expressed either by a number
- * representing the clockwise angle (in degree) or by one of the following string presets:
- * - 'start': before the anchor point, following the same direction
- * - 'end': after the anchor point, following the same direction
- * - 'center': centered on the anchor point
- * - 'right': 0°
- * - 'bottom': 90°
- * - 'left': 180°
- * - 'top': 270°
- * @member {String|Number|Array|Function}
- * @default 'center'
- */
- align: 'center',
- /**
- * The label box alignment relative to the element ('start'|'center'|'end')
- * @member {String|Array|Function}
- * @default 'center'
- */
- anchor: 'center',
- /**
- * The color used to draw the background of the surrounding frame.
- * @member {String|Array|Function|null}
- * @default null (no background)
- */
- backgroundColor: null,
- /**
- * The color used to draw the border of the surrounding frame.
- * @member {String|Array|Function|null}
- * @default null (no border)
- */
- borderColor: null,
- /**
- * The border radius used to add rounded corners to the surrounding frame.
- * @member {Number|Array|Function}
- * @default 0 (not rounded)
- */
- borderRadius: 0,
- /**
- * The border width of the surrounding frame.
- * @member {Number|Array|Function}
- * @default 0 (no border)
- */
- borderWidth: 0,
- /**
- * The color used to draw the label text.
- * @member {String|Array|Function}
- * @default undefined (use Chart.defaults.global.defaultFontColor)
- */
- color: undefined,
- /**
- * Whether to display labels global (boolean) or per data (function)
- * @member {Boolean|Array|Function}
- * @default true
- */
- display: true,
- /**
- * The font options used to draw the label text.
- * @member {Object|Array|Function}
- * @prop {String} font.family - defaults to Chart.defaults.global.defaultFontFamily
- * @prop {Number} font.lineHeight - defaults to 1.2
- * @prop {Number} font.size - defaults to Chart.defaults.global.defaultFontSize
- * @prop {String} font.style - defaults to Chart.defaults.global.defaultFontStyle
- * @prop {Number} font.weight - defaults to 'normal'
- * @default Chart.defaults.global.defaultFont.*
- */
- font: {
- family: undefined,
- lineHeight: 1.2,
- size: undefined,
- style: undefined,
- weight: null
- },
- /**
- * The distance (in pixels) to pull the label away from the anchor point, the direction
- * being determined by the `align` value (only applicable if `align` is `start` or `end`).
- * @member {Number|Array|Function}
- * @default 4
- */
- offset: 4,
- /**
- * The label global opacity, including the text, background, borders, etc., specified as
- * a number between 0.0 (fully transparent) and 1.0 (fully opaque).
- * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalAlpha
- * @member {Number|Array|Function}
- * @default 1
- */
- opacity: 1,
- /**
- * The padding (in pixels) to apply between the text and the surrounding frame.
- * @member {Number|Object|Array|Function}
- * @prop {Number} padding.top - Space above the text.
- * @prop {Number} padding.right - Space on the right of the text.
- * @prop {Number} padding.bottom - Space below the text.
- * @prop {Number} padding.left - Space on the left of the text.
- * @default 4 (all values)
- */
- padding: {
- top: 4,
- right: 4,
- bottom: 4,
- left: 4
- },
- /**
- * Clockwise rotation of the label relative to its center.
- * @member {Number|Array|Function}
- * @default 0
- */
- rotation: 0,
- /**
- * Text alignment for multi-lines labels ('left'|'right'|'start'|'center'|'end').
- * @member {String|Array|Function}
- * @default 'start'
- */
- textAlign: 'start',
- /**
- * Allows to customize the label text by transforming input data.
- * @member {Function|null}
- * @prop {*} value - The data value
- * @prop {Object} context - The function unique argument:
- * @prop {Chart} context.chart - The current chart
- * @prop {Number} context.dataIndex - Index of the current data
- * @prop {Object} context.dataset - The current dataset
- * @prop {Number} context.datasetIndex - Index of the current dataset
- * @default data[index]
- */
- formatter: function(value) {
- if (helpers$4.isNullOrUndef(value)) {
- return null;
- }
- var label = value;
- var keys, klen, k;
- if (helpers$4.isObject(value)) {
- if (!helpers$4.isNullOrUndef(value.label)) {
- label = value.label;
- } else if (!helpers$4.isNullOrUndef(value.r)) {
- label = value.r;
- } else {
- label = '';
- keys = Object.keys(value);
- for (k = 0, klen = keys.length; k < klen; ++k) {
- label += (k !== 0 ? ', ' : '') + keys[k] + ': ' + value[keys[k]];
- }
- }
- }
- return '' + label;
- },
- /**
- * Event listeners, where the property is the type of the event to listen and the value
- * a callback with a unique `context` argument containing the same information as for
- * scriptable options. If a callback explicitly returns `true`, the label is updated
- * with the current context and the chart re-rendered. This allows to implement visual
- * interactions with labels such as highlight, selection, etc.
- *
- * Event currently supported are:
- * - 'click': a mouse click is detected within a label
- * - 'enter': the mouse enters a label
- * -' leave': the mouse leaves a label
- *
- * @member {Object}
- * @default {}
- */
- listeners: {}
- };
- /**
- * @see https://github.com/chartjs/Chart.js/issues/4176
- */
- 'use strict';
- var helpers = Chart.helpers;
- var EXPANDO_KEY = '$datalabels';
- Chart.defaults.global.plugins.datalabels = defaults;
- function configure(dataset, options) {
- var override = dataset.datalabels;
- var config = {};
- if (override === false) {
- return null;
- }
- if (override === true) {
- override = {};
- }
- return helpers.merge(config, [options, override]);
- }
- function drawLabels(chart, datasetIndex) {
- var meta = chart.getDatasetMeta(datasetIndex);
- var elements = meta.data || [];
- var ilen = elements.length;
- var i, el, label;
- for (i = 0; i < ilen; ++i) {
- el = elements[i];
- label = el[EXPANDO_KEY];
- if (label) {
- label.draw(chart.ctx);
- }
- }
- }
- function labelAtXY(chart, x, y) {
- var items = chart[EXPANDO_KEY].labels;
- var i, j, labels, label;
- // Until we support z-index, let's hit test in the drawing reverse order
- for (i = items.length - 1; i >= 0; --i) {
- labels = items[i] || [];
- for (j = labels.length - 1; j >= 0; --j) {
- label = labels[j];
- if (label.contains(x, y)) {
- return {dataset: i, label: label};
- }
- }
- }
- return null;
- }
- function dispatchEvent(chart, listeners, target) {
- var callback = listeners && listeners[target.dataset];
- if (!callback) {
- return;
- }
- var label = target.label;
- var context = label.$context;
- if (helpers.callback(callback, [context]) === true) {
- // Users are allowed to tweak the given context by injecting values that can be
- // used in scriptable options to display labels differently based on the current
- // event (e.g. highlight an hovered label). That's why we update the label with
- // the output context and schedule a new chart render by setting it dirty.
- chart[EXPANDO_KEY].dirty = true;
- label.update(context);
- }
- }
- function dispatchMoveEvents(chart, listeners, previous, target) {
- var enter, leave;
- if (!previous && !target) {
- return;
- }
- if (!previous) {
- enter = true;
- } else if (!target) {
- leave = true;
- } else if (previous.label !== target.label) {
- leave = enter = true;
- }
- if (leave) {
- dispatchEvent(chart, listeners.leave, previous);
- }
- if (enter) {
- dispatchEvent(chart, listeners.enter, target);
- }
- }
- function handleMoveEvents(chart, event) {
- var expando = chart[EXPANDO_KEY];
- var listeners = expando.listeners;
- var previous, target;
- if (!listeners.enter && !listeners.leave) {
- return;
- }
- if (event.type === 'mousemove') {
- target = labelAtXY(chart, event.x, event.y);
- } else if (event.type !== 'mouseout') {
- return;
- }
- previous = expando.hovered;
- expando.hovered = target;
- dispatchMoveEvents(chart, listeners, previous, target);
- }
- function handleClickEvents(chart, event) {
- var handlers = chart[EXPANDO_KEY].listeners.click;
- var target = handlers && labelAtXY(chart, event.x, event.y);
- if (target) {
- dispatchEvent(chart, handlers, target);
- }
- }
- Chart.defaults.global.plugins.datalabels = defaults;
- Chart.plugins.register({
- id: 'datalabels',
- beforeInit: function(chart) {
- chart[EXPANDO_KEY] = {
- actives: []
- };
- },
- beforeUpdate: function(chart) {
- var expando = chart[EXPANDO_KEY];
- expando.listened = false;
- expando.listeners = {}; // {event-type: {dataset-index: function}}
- expando.labels = []; // [dataset-index: [labels]]
- },
- afterDatasetUpdate: function(chart, args, options) {
- var datasetIndex = args.index;
- var expando = chart[EXPANDO_KEY];
- var labels = expando.labels[datasetIndex] = [];
- var dataset = chart.data.datasets[datasetIndex];
- var config = configure(dataset, options);
- var elements = args.meta.data || [];
- var ilen = elements.length;
- var ctx = chart.ctx;
- var i, el, label;
- ctx.save();
- for (i = 0; i < ilen; ++i) {
- el = elements[i];
- if (el && !el.hidden && !el._model.skip) {
- labels.push(label = new Label(config, ctx, el, i));
- label.update(label.$context = {
- active: false,
- chart: chart,
- dataIndex: i,
- dataset: dataset,
- datasetIndex: datasetIndex
- });
- } else {
- label = null;
- }
- el[EXPANDO_KEY] = label;
- }
- ctx.restore();
- // Store listeners at the chart level and per event type to optimize
- // cases where no listeners are registered for a specific event
- helpers.merge(expando.listeners, config.listeners || {}, {
- merger: function(key, target, source) {
- target[key] = target[key] || {};
- target[key][args.index] = source[key];
- expando.listened = true;
- }
- });
- },
- // Draw labels on top of all dataset elements
- // https://github.com/chartjs/chartjs-plugin-datalabels/issues/29
- // https://github.com/chartjs/chartjs-plugin-datalabels/issues/32
- afterDatasetsDraw: function(chart) {
- for (var i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
- drawLabels(chart, i);
- }
- },
- beforeEvent: function(chart, event) {
- // If there is no listener registered for this chart, `listened` will be false,
- // meaning we can immediately ignore the incoming event and avoid useless extra
- // computation for users who don't implement label interactions.
- if (chart[EXPANDO_KEY].listened) {
- switch (event.type) {
- case 'mousemove':
- case 'mouseout':
- handleMoveEvents(chart, event);
- break;
- case 'click':
- handleClickEvents(chart, event);
- break;
- default:
- }
- }
- },
- afterEvent: function(chart) {
- var expando = chart[EXPANDO_KEY];
- var previous = expando.actives;
- var actives = expando.actives = chart.lastActive || []; // public API?!
- var updates = utils.arrayDiff(previous, actives);
- var i, ilen, update, label;
- for (i = 0, ilen = updates.length; i < ilen; ++i) {
- update = updates[i];
- if (update[1]) {
- label = update[0][EXPANDO_KEY];
- label.$context.active = (update[1] === 1);
- label.update(label.$context);
- }
- }
- if ((expando.dirty || updates.length) && !chart.animating) {
- chart.render();
- }
- delete expando.dirty;
- }
- });
- })));
|