PageSourceSearch

https://nsdcarena.com/document/js/chartjs-plugin-datalabels.js

js nsdcarena.com collected 2026-10-02 16:03:18 UTC 14,675 bytes, 633 lines download raw bytes

1/*!
2 * chartjs-plugin-datalabels
3 * http://chartjs.org/
4 * Version: 0.1.0
5 *
6 * Copyright 2017 Chart.js Contributors
7 * Released under the MIT license
8 * https://github.com/chartjs/chartjs-plugin-datalabels/blob/master/LICENSE.md
9 */
10(function (global, factory) {
11	typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('chart.js')) :
12	typeof define === 'function' && define.amd ? define(['chart.js'], factory) :
13	(factory(global.Chart));
14}(this, (function (Chart) { 'use strict';
15
16Chart = Chart && Chart.hasOwnProperty('default') ? Chart['default'] : Chart;
17
18/**
19 * @module Options
20 */
21
22var helpers$1 = Chart.helpers;
23
24var defaults = {
25	/**
26	 * The label box alignment relative to `anchor` ('start'|'center'|'end')
27	 * @member {String|Array|Function}
28	 * @default 'center'
29	 */
30	align: 'center',
31
32	/**
33	 * The label box alignment relative to the element ('start'|'center'|'end')
34	 * @member {String|Array|Function}
35	 * @default 'center'
36	 */
37	anchor: 'center',
38
39	/**
40	 * The color used to draw the background of the surrounding frame.
41	 * @member {String|Array|Function|null}
42	 * @default null (no background)
43	 */
44	backgroundColor: null,
45
46	/**
47	 * The color used to draw the border of the surrounding frame.
48	 * @member {String|Array|Function|null}
49	 * @default null (no border)
50	 */
51	borderColor: null,
52
53	/**
54	 * The border radius used to add rounded corners to the surrounding frame.
55	 * @member {Number|Array|Function}
56	 * @default 0 (not rounded)
57	 */
58	borderRadius: 0,
59
60	/**
61	 * The border width of the surrounding frame.
62	 * @member {Number|Array|Function}
63	 * @default 0 (no border)
64	 */
65	borderWidth: 0,
66
67	/**
68	 * The color used to draw the label text.
69	 * @member {String|Array|Function}
70	 * @default undefined (use Chart.defaults.global.defaultFontColor)
71	 */
72	color: undefined,
73
74	/**
75	 * Whether to display labels global (boolean) or per data (function)
76	 * @member {Boolean|Array|Function}
77	 * @default true
78	 */
79	display: true,
80
81	/**
82	 * The font options used to draw the label text.
83	 * @member {Object|Array|Function}
84	 * @prop {Boolean} font.family - defaults to Chart.defaults.global.defaultFontFamily
85	 * @prop {Boolean} font.size - defaults to Chart.defaults.global.defaultFontSize
86	 * @prop {Boolean} font.style - defaults to Chart.defaults.global.defaultFontStyle
87	 * @prop {Boolean} font.weight - defaults to 'normal'
88	 * @default Chart.defaults.global.defaultFont.*
89	 */
90	font: {
91		family: undefined,
92		size: undefined,
93		style: undefined,
94		weight: null
95	},
96
97	/**
98	 * The line height (in pixel) to use for multi-lines labels.
99	 * @member {Number|Array|Function|undefined}
100	 * @default 1.2
101	 */
102	lineHeight: 1.2,
103
104	/**
105	 * The distance (in pixels) to pull the label away from the anch
105or point, the direction
106	 * being determined by the `align` value (only applicable if `align` is `start` or `end`).
107	 * @member {Number|Array|Function}
108	 * @default 4
109	 */
110	offset: 4,
111
112	/**
113	 * The padding (in pixels) to apply between the text and the surrounding frame.
114	 * @member {Number|Object|Array|Function}
115	 * @prop {Number} padding.top - Space above the text.
116	 * @prop {Number} padding.right - Space on the right of the text.
117	 * @prop {Number} padding.bottom - Space below the text.
118	 * @prop {Number} padding.left - Space on the left of the text.
119	 * @default 4 (all values)
120	 */
121	padding: {
122		top: 4,
123		right: 4,
124		bottom: 4,
125		left: 4
126	},
127
128	/**
129	 * Rotation of the label relative to its center.
130	 * @member {Number|Array|Function}
131	 * @default 0
132	 */
133	rotation: 0,
134
135	/**
136	 * Text alignment for multi-lines labels ('left'|'right'|'start'|'center'|'end').
137	 * @member {String|Array|Function}
138	 * @default 'start'
139	 */
140	textAlign: 'start',
141
142	/**
143	 * Allows to customize the label text by transforming input data.
144	 * @member {Function|null}
145	 * @prop {*} value - The data value
146	 * @prop {Object} context - The function unique argument:
147	 * @prop {Chart} context.chart - The current chart
148	 * @prop {Number} context.dataIndex - Index of the current data
149	 * @prop {Object} context.dataset - The current dataset
150	 * @prop {Number} context.datasetIndex - Index of the current dataset
151	 * @default data[index]
152	 */
153	formatter: function(value) {
154		var label = value;
155		var keys, klen, k;
156
157		if (helpers$1.isObject(value)) {
158			if (!helpers$1.isNullOrUndef(value.label)) {
159				label = value.label;
160			} else if (!helpers$1.isNullOrUndef(value.r)) {
161				label = value.r;
162			} else {
163				label = '';
164				keys = Object.keys(value);
165				for (k = 0, klen = keys.length; k < klen; ++k) {
166					label += (k !== 0 ? ', ' : '') + keys[k] + ': ' + value[keys[k]];
167				}
168			}
169		}
170		return '' + label;
171	}
172};
173
174function orient(point, origin) {
175	var x0 = origin.x;
176	var y0 = origin.y;
177
178	if (x0 === null) {
179		return {x: 0, y: -1};
180	}
181	if (y0 === null) {
182		return {x: 1, y: 0};
183	}
184
185	var dx = point.x - x0;
186	var dy = point.y - y0;
187	var ln = Math.sqrt(dx * dx + dy * dy);
188	return {
189		x: ln ? dx / ln : 0,
190		y: ln ? dy / ln : -1
191	};
192}
193
194var positioners = {
195	arc: function(vm, anchor) {
196		var angle = (vm.startAngle + vm.endAngle) / 2;
197		var vx = Math.cos(angle);
198		var vy = Math.sin(angle);
199		var r0 = vm.innerRadius;
200		var r1 = vm.outerRadius;
201		var d;
202
203		if (anchor === 'start') {
204			d = r0;
205		} else if (anchor === 'end') {
206			d = r1;
207		} else {
208			d = (r0 + r1) / 2;
209		}
210
211		return {
212			x: vm.x + vx * d,
213			y: vm.y + vy * d,
214			vx: vx,
215			vy: vy
216		};
217	},
218
219	point: function(vm, anchor, origin) {
220		var v = orient(vm, origin);
221		var r = vm.radius;
222		var d = 0;
223
224		if (anchor === 'start') {
225			d = -r;
226		} else if (anchor === 'end') {
227			d = r;
228		}
229
230		return {
231			x: vm.x + v.x * d,
232			y: vm.y + v.y * d,
233			vx: v.x,
234			vy: v.y
235		};
236	},
237
238	rect: function(vm, anchor, origin) {
239		var horizontal = vm.horizontal;
240		var size = Math.abs(vm.base - (horizontal ? vm.x : vm.y));
241		var x = horizontal ? Math.min(vm.x, vm.base) : vm.x;
242		var y = horizontal ? vm.y : Math.min(vm.y, vm.base);
243		var v = orient(vm, origin);
244
245		if (anchor === 'center') {
246			if (horizontal) {
247				x += size / 2;
248			} else {
249				y += size / 2;
250			}
251		} else if (anchor === 'start' && !horizontal) {
252			y += size;
253		} else if (anchor === 'end' && horizontal) {
254			x += size;
255		}
256
257		return {
258			x: x,
259			y: y,
260			vx: v.x,
261			vy: v.y
262		};
263	},
264
265	fallback: function(vm, anchor, origin) {
266		var v = orient(vm, origin);
267		return {
268			x: vm.x,
269			y: vm.y,
270			vx: v.x,
271			vy: v.y
272		};
273	}
274};
275
276/**
277 * @see https://github.com/chartjs/Chart.js/issues/4176
278 */
279
280Chart.defaults.global.plugins.datalabels = defaults;
281
282var helpers = Chart.helpers;
283var MODEL_KEY = '$datalabels';
284
285// @todo move this in Chart.helpers.toTextLines
286function toTextLines(inputs) {
287	var lines = [];
288	var input;
289
290	inputs = [].concat(inputs);
291	while (inputs.length) {
292		input = inputs.pop();
293		if (typeof input === 'string') {
294			lines.unshift.apply(lines, input.split('\n'));
295		} else if (Array.isArray(input)) {
296			inputs.push.apply(inputs, input);
297		} else if (!helpers.isNullOrUndef(inputs)) {
298			lines.unshift('' + input);
299		}
300	}
301
302	return lines;
303}
304
305// @todo move this method in Chart.helpers.canvas.toFont (deprecates helpers.fontString)
306// @see https://developer.mozilla.org/en-US/docs/Web/CSS/font
307function toFontString(font) {
308	if (!font || helpers.isNullOrUndef(font.size) || helpers.isNullOrUndef(font.family)) {
309		return null;
310	}
311
312	return (font.style ? font.style + ' ' : '')
313		+ (font.weight ? font.weight + ' ' : '')
314		+ font.size + 'px '
315		+ font.family;
316}
317
318// @todo move this in Chart.helpers.canvas.textSize
319// @todo cache calls of measureText if font doesn't change?!
320function textSize(ctx, lines, font) {
321	var items = [].concat(lines);
322	var ilen = items.length;
323	var prev = ctx.font;
324	var width = 0;
325	var i;
326
327	ctx.font = font.string;
328
329	for (i = 0; i < ilen; ++i) {
330		width = Math.max(ctx.measureText(items[i]).width, width);
331	}
332
333	ctx.font = prev;
334
335	return {
336		height: ilen * font.lineHeight,
337		width: width
338	};
339}
340
341// @todo move this method in Chart.helpers.options.toFont
342function parseFont(value) {
343	var global = Chart.defaults.global;
344	var size = helpers.valueOrDefault(value.size, global.defaultFontSize);
345	var font = {
346		family: helpers.valueOrDefault(value.family, global.defaultFontFamily),
347		lineHeight: helpers.options.toLineHeight(value.lineHeight, size),
348		size: size,
349		style: helpers.valueOrDefault(value.style, global.defaultFontStyle),
350		weight: helpers.valueOrDefault(value.weight, null),
351		string: ''
352	};
353
354	font.string = toFontString(font);
355	return font;
356}
357
358function coordinates(el, model) {
359	var point = model.positioner(el._view, model.anchor, model.origin);
360
361	if (model.align === 'center') {
362		// if aligned center, we don't want to offset the center point
363		return {x: point.x, y: point.y};
364	}
365
366	var padding = model.padding;
367	var rotation = model.rotation;
368	var th = model.size.height;
369	var tw = model.size.width;
370	var vx = point.vx;
371	var vy = point.vy;
372	var dx = 0;
373	var dy = 0;
374
375	if (model.align === 'start') {
376		vx = -vx;
377		vy = -vy;
378	}
379
380	// take in account the label rotation
381	dx += Math.abs(tw / 2 * Math.cos(rotation)) + Math.abs(th / 2 * Math.sin(rotation));
382	dy += Math.abs(tw / 2 * Math.sin(rotation)) + Math.abs(th / 2 * Math.cos(rotation));
383
384	// ... and padding
385	dx += vx > 0 ? padding.right : padding.left;
386	dy += vy > 0 ? padding.bottom : padding.top;
387
388	// ... and borders
389	dx += model.borderWidth || 0;
390	dy += model.borderWidth || 0;
391
392	// ... and explicit offset
393	dx += model.offset;
394	dy += model.offset;
395
396	return {
397		x: point.x + dx * vx,
398		y: point.y + dy * vy
399	};
400}
401
402function boundingRects(size, padding) {
403	var th = size.height;
404	var tw = size.width;
405	var tx = -tw / 2;
406	var ty = -th / 2;
407
408	return {
409		frame: {
410			x: tx - padding.left,
411			y: ty - padding.top,
412			w: tw + padding.width,
413			h: th + padding.height,
414		},
415		text: {
416			x: tx,
417			y: ty,
418			w: tw,
419			h: th
420		}
421	};
422}
423
424function drawFrame(ctx, rect, model) {
425	var bgColor = model.backgroundColor;
426	var borderColor = model.borderColor;
427	var borderWidth = model.borderWidth;
428
429	if (!bgColor && (!borderColor || !borderWidth)) {
430		return;
431	}
432
433	ctx.beginPath();
434
435	helpers.canvas.roundedRect(
436		ctx,
437		Math.round(rect.x) - borderWidth / 2,
438		Math.round(rect.y) - borderWidth / 2,
439		Math.round(rect.w) + borderWidth,
440		Math.round(rect.h) + borderWidth,
441		model.borderRadius);
442
443	ctx.closePath();
444
445	if (bgColor) {
446		ctx.fillStyle = bgColor;
447		ctx.fill();
448	}
449
450	if (borderColor && borderWidth) {
451		ctx.strokeStyle = borderColor;
452		ctx.lineWidth = borderWidth;
453		ctx.lineJoin = 'miter';
454		ctx.stroke();
455	}
456}
457
458function drawText(ctx, lines, rect, model) {
459	var align = model.textAlign;
460	var font = model.font;
461	var lh = font.lineHeight;
462	var color = model.color;
463	var ilen = lines.length;
464	var x, y, i;
465
466	if (!ilen || !color) {
467		return;
468	}
469
470	x = rect.x;
471	y = rect.y + lh / 2;
472
473	if (align === 'center') {
474		x += rect.w / 2;
475	}
475 else if (align === 'end' || align === 'right') {
476		x += rect.w;
477	}
478
479	ctx.font = model.font.string;
480	ctx.fillStyle = color;
481	ctx.textAlign = align;
482	ctx.textBaseline = 'middle';
483
484	for (i = 0; i < ilen; ++i) {
485		ctx.fillText(
486			lines[i],
487			Math.round(x),
488			Math.round(y),
489			Math.round(rect.w));
490
491		y += lh;
492	}
493}
494
495function getPositioner(el) {
496	if (el instanceof Chart.elements.Arc) {
497		return positioners.arc;
498	}
499	if (el instanceof Chart.elements.Point) {
500		return positioners.point;
501	}
502	if (el instanceof Chart.elements.Rectangle) {
503		return positioners.rect;
504	}
505	return positioners.fallback;
506}
507
508function getScaleOrigin(el) {
509	var horizontal = el._model.horizontal;
510	var scale = el._scale || (horizontal && el._xScale) || el._yScale;
511
512	if (!scale) {
513		return null;
514	}
515
516	if (scale.xCenter !== undefined && scale.yCenter !== undefined) {
517		return {x: scale.xCenter, y: scale.yCenter};
518	}
519
520	var pixel = scale.getBasePixel();
521	return horizontal ?
522		{x: pixel, y: null} :
523		{x: null, y: pixel};
524}
525
526function modelize(el, index, ctx, config, context) {
527	var resolve = Chart.helpers.options.resolve;
528	if (!resolve([config.display, true], context, index)) {
529		return null;
530	}
531
532	var value = context.dataset.data[index];
533	var label = helpers.valueOrDefault(helpers.callback(config.formatter, [value, context]), value);
534	var lines = helpers.isNullOrUndef(label) ? [] : toTextLines(label);
535	if (!lines.length) {
536		return null;
537	}
538
539	var font = parseFont(resolve([config.font, {}], context, index));
540	var model = {
541		align: resolve([config.align, 'center'], context, index),
542		anchor: resolve([config.anchor, 'center'], context, index),
543		backgroundColor: resolve([config.backgroundColor, null], context, index),
544		borderColor: resolve([config.borderColor, null], context, index),
545		borderRadius: resolve([config.borderRadius, 0], context, index),
546		borderWidth: resolve([config.borderWidth, 0], context, index),
547		color: resolve([config.color, Chart.defaults.global.defaultFontColor], context, index),
548		font: font,
549		lines: lines,
550		offset: resolve([config.offset, 0], context, index),
551		padding: helpers.options.toPadding(resolve([config.padding, 0], context, index)),
552		rotation: resolve([config.rotation, 0], context, index) * (Math.PI / 180),
553		textAlign: resolve([config.textAlign, 'start'], context, index),
554		origin: getScaleOrigin(el),
555		positioner: getPositioner(el),
556		size: textSize(ctx, lines, font)
557	};
558
559	return model;
560}
561
562function configure(dataset, options) {
563	var override = dataset.datalabels;
564	var config = {};
565
566	if (override === false) {
567		return null;
568	}
569	if (override === true) {
570		override = {};
571	}
572
573	return helpers.merge(config, [options, override]);
574}
575
576Chart.plugins.register({
577	id: 'datalabels',
578
579	afterDatasetUpdate: function(chart, args, options) {
580		var dataset = chart.data.datasets[args.index];
581		var config = configure(dataset, options);
582		var display = config && config.display;
583		var elements = args.meta.data || [];
584		var ilen = elements.length;
585		var ctx = chart.ctx;
586		var i, el;
587
588		ctx.save();
589
590		for (i = 0; i < ilen; ++i) {
591			el = elements[i];
592			el[MODEL_KEY] = display && el && !el.hidden ?
593				modelize(el, i, ctx, config, {
594					chart: chart,
595					dataIndex: i,
596					dataset: dataset,
597					datasetIndex: args.index
598				}) :
599				null;
600		}
601
602		ctx.restore();
603	},
604
605	afterDatasetDraw: function(chart, args) {
606		var elements = args.meta.data || [];
607		var ilen = elements.length;
608		var ctx = chart.ctx;
609		var i, el, model, center, rects;
610
611		for (i = 0; i < ilen; ++i) {
612			el = elements[i];
613			model = el[MODEL_KEY];
614			if (!model) {
615				continue;
616			}
617
618			center = coordinates(el, model);
619			rects = boundingRects(model.size, model.padding);
620
621			ctx.save();
622			ctx.translate(Math.round(center.x), Math.round(center.y));
623			ctx.rotate(model.rotation);
624
625			drawFrame(ctx, rects.frame, model);
626			drawText(ctx, model.lines, rects.text, model);
627
628			ctx.restore();
629		}
630	}
631});
632
633})));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.