PageSourceSearch

https://interreg-euro-med.eu/wp-content/themes/divi-child/node_mod…/chartjs-plugin-datalabels.js?ver=1.0.0

js interreg-euro-med.eu collected 2026-10-01 10:40:05 UTC 32,707 bytes, 1,358 lines download raw bytes

vendor: 13,854 bytes, lines 1-609
1/*!
2 * chartjs-plugin-datalabels v2.0.0
3 * https://chartjs-plugin-datalabels.netlify.app
4 * (c) 2017-2021 chartjs-plugin-datalabels contributors
5 * Released under the MIT license
6 */
7(function (global, factory) {
8typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js/helpers'), require('chart.js')) :
9typeof define === 'function' && define.amd ? define(['chart.js/helpers', 'chart.js'], factory) :
10(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ChartDataLabels = factory(global.Chart.helpers, global.Chart));
11}(this, (function (helpers, chart_js) { 'use strict';
12
13var devicePixelRatio = (function() {
14  if (typeof window !== 'undefined') {
15    if (window.devicePixelRatio) {
16      return window.devicePixelRatio;
17    }
18
19    // devicePixelRatio is undefined on IE10
20    // https://stackoverflow.com/a/20204180/8837887
21    // https://github.com/chartjs/chartjs-plugin-datalabels/issues/85
22    var screen = window.screen;
23    if (screen) {
24      return (screen.deviceXDPI || 1) / (screen.logicalXDPI || 1);
25    }
26  }
27
28  return 1;
29}());
30
31var utils = {
32  // @todo move this in Chart.helpers.toTextLines
33  toTextLines: function(inputs) {
34    var lines = [];
35    var input;
36
37    inputs = [].concat(inputs);
38    while (inputs.length) {
39      input = inputs.pop();
40      if (typeof input === 'string') {
41        lines.unshift.apply(lines, input.split('\n'));
42      } else if (Array.isArray(input)) {
43        inputs.push.apply(inputs, input);
44      } else if (!helpers.isNullOrUndef(inputs)) {
45        lines.unshift('' + input);
46      }
47    }
48
49    return lines;
50  },
51
52  // @todo move this in Chart.helpers.canvas.textSize
53  // @todo cache calls of measureText if font doesn't change?!
54  textSize: function(ctx, lines, font) {
55    var items = [].concat(lines);
56    var ilen = items.length;
57    var prev = ctx.font;
58    var width = 0;
59    var i;
60
61    ctx.font = font.string;
62
63    for (i = 0; i < ilen; ++i) {
64      width = Math.max(ctx.measureText(items[i]).width, width);
65    }
66
67    ctx.font = prev;
68
69    return {
70      height: ilen * font.lineHeight,
71      width: width
72    };
73  },
74
75  /**
76   * Returns value bounded by min and max. This is equivalent to max(min, min(value, max)).
77   * @todo move this method in Chart.helpers.bound
78   * https://doc.qt.io/qt-5/qtglobal.html#qBound
79   */
80  bound: function(min, value, max) {
81    return Math.max(min, Math.min(value, max));
82  },
83
84  /**
85   * Returns an array of pair [value, state] where state is:
86   * * -1: value is only in a0 (removed)
87   * *  1: value is only in a1 (added)
88   */
89  arrayDiff: function(a0, a1) {
90    var prev = a0.slice();
91    var updates = [];
92    var i, j, ilen, v;
93
94    for (i = 0, ilen = a1.length; i < ilen; ++i) {
95      v = a1[i];
96      j = prev.indexOf(v);
97
98      if (j === -1) {
99        updates.push([v, 1]);
100      } else {
101        prev.splice(j, 1);
102      }
103    }
104
105    for (i = 0, ilen = prev.length; i < ilen; ++i) {
106      updates.push([prev[i], -1]);
107    }
108
109    return updates;
110  },
111
112  /**
113   * https://github.com/chartjs/chartjs-plugin-datalabels/issues/70
114   */
115  rasterize: function(v) {
116    return Math.round(v * devicePixelRatio) / devicePixelRatio;
117  }
118};
119
120function orient(point, origin) {
121  var x0 = origin.x;
122  var y0 = origin.y;
123
124  if (x0 === null) {
125    return {x: 0, y: -1};
126  }
127  if (y0 === null) {
128    return {x: 1, y: 0};
129  }
130
131  var dx = point.x - x0;
132  var dy = point.y - y0;
133  var ln = Math.sqrt(dx * dx + dy * dy);
134
135  return {
136    x: ln ? dx / ln : 0,
137    y: ln ? dy / ln : -1
138  };
139}
140
141function aligned(x, y, vx, vy, align) {
142  switch (align) {
143  case 'center':
144    vx = vy = 0;
145    break;
146  case 'bottom':
147    vx = 0;
148    vy = 1;
149    break;
150  case 'right':
151    vx = 1;
152    vy = 0;
153    break;
154  case 'left':
155    vx = -1;
156    vy = 0;
157    break;
158  case 'top':
159    vx = 0;
160    vy = -1;
161    break;
162  case 'start':
163    vx = -vx;
164    vy = -vy;
165    break;
166  case 'end':
167    // keep natural orientation
168    break;
169  default:
170    // clockwise rotation (in degree)
171    align *= (Math.PI / 180);
172    vx = Math.cos(align);
173    vy = Math.sin(align);
174    break;
175  }
176
177  return {
178    x: x,
179    y: y,
180    vx: vx,
181    vy: vy
182  };
183}
184
185// Line clipping (Cohen–Sutherland algorithm)
186// https://en.wikipedia.org/wiki/Cohen–Sutherland_algorithm
187
188var R_INSIDE = 0;
189var R_LEFT = 1;
190var R_RIGHT = 2;
191var R_BOTTOM = 4;
192var R_TOP = 8;
193
194function region(x, y, rect) {
195  var res = R_INSIDE;
196
197  if (x < rect.left) {
198    res |= R_LEFT;
199  } else if (x > rect.right) {
200    res |= R_RIGHT;
201  }
202  if (y < rect.top) {
203    res |= R_TOP;
204  } else if (y > rect.bottom) {
205    res |= R_BOTTOM;
206  }
207
208  return res;
209}
210
211function clipped(segment, area) {
212  var x0 = segment.x0;
213  var y0 = segment.y0;
214  var x1 = segment.x1;
215  var y1 = segment.y1;
216  var r0 = region(x0, y0, area);
217  var r1 = region(x1, y1, area);
218  var r, x, y;
219
220  // eslint-disable-next-line no-constant-condition
221  while (true) {
222    if (!(r0 | r1) || (r0 & r1)) {
223      // both points inside or on the same side: no clipping
224      break;
225    }
226
227    // at least one point is outside
228    r = r0 || r1;
229
230    if (r & R_TOP) {
231      x = x0 + (x1 - x0) * (area.top - y0) / (y1 - y0);
232      y = area.top;
233    } else if (r & R_BOTTOM) {
234      x = x0 + (x1 - x0) * (area.bottom - y0) / (y1 - y0);
235      y = area.bottom;
236    } else if (r & R_RIGHT) {
237      y = y0 + (y1 - y0) * (area.right - x0) / (x1 - x0);
238      x = area.right;
239    } else if (r & R_LEFT) {
240      y = y0 + (y1 - y0) * (area.left - x0) / (x1 - x0);
241      x = area.left;
242    }
243
244    if (r === r0) {
245      x0 = x;
246      y0 = y;
247      r0 = region(x0, y0, area);
248    } else {
249      x1 = x;
250      y1 = y;
251      r1 = region(x1, y1, area);
252    }
253  }
254
255  return {
256    x0: x0,
257    x1: x1,
258    y0: y0,
259    y1: y1
260  };
261}
262
263function compute$1(range, config) {
264  var anchor = config.anchor;
265  var segment = range;
266  var x, y;
267
268  if (config.clamp) {
269    segment = clipped(segment, config.area);
270  }
271
272  if (anchor === 'start') {
273    x = segment.x0;
274    y = segment.y0;
275  } else if (anchor === 'end') {
276    x = segment.x1;
277    y = segment.y1;
278  } else {
279    x = (segment.x0 + segment.x1) / 2;
280    y = (segment.y0 + segment.y1) / 2;
281  }
282
283  return aligned(x, y, range.vx, range.vy, config.align);
284}
285
286var positioners = {
287  arc: function(el, config) {
288    var angle = (el.startAngle + el.endAngle) / 2;
289    var vx = Math.cos(angle);
290    var vy = Math.sin(angle);
291    var r0 = el.innerRadius;
292    var r1 = el.outerRadius;
293
294    return compute$1({
295      x0: el.x + vx * r0,
296      y0: el.y + vy * r0,
297      x1: el.x + vx * r1,
298      y1: el.y + vy * r1,
299      vx: vx,
300      vy: vy
301    }, config);
302  },
303
304  point: function(el, config) {
305    var v = orient(el, config.origin);
306    var rx = v.x * el.options.radius;
307    var ry = v.y * el.options.radius;
308
309    return compute$1({
310      x0: el.x - rx,
311      y0: el.y - ry,
312      x1: el.x + rx,
313      y1: el.y + ry,
314      vx: v.x,
315      vy: v.y
316    }, config);
317  },
318
319  bar: function(el, config) {
320    var v = orient(el, config.origin);
321    var x = el.x;
322    var y = el.y;
323    var sx = 0;
324    var sy = 0;
325
326    if (el.horizontal) {
327      x = Math.min(el.x, el.base);
328      sx = Math.abs(el.base - el.x);
329    } else {
330      y = Math.min(el.y, el.base);
331      sy = Math.abs(el.base - el.y);
332    }
333
334    return compute$1({
335      x0: x,
336      y0: y + sy,
337      x1: x + sx,
338      y1: y,
339      vx: v.x,
340      vy: v.y
341    }, config);
342  },
343
344  fallback: function(el, config) {
345    var v = orient(el, config.origin);
346
347    return compute$1({
348      x0: el.x,
349      y0: el.y,
350      x1: el.x,
351      y1: el.y,
352      vx: v.x,
353      vy: v.y
354    }, config);
355  }
356};
357
358var rasterize = utils.rasterize;
359
360function boundingRects(model) {
361  var borderWidth = model.borderWidth || 0;
362  var padding = model.padding;
363  var th = model.size.height;
364  var tw = model.size.width;
365  var tx = -tw / 2;
366  var ty = -th / 2;
367
368  return {
369    frame: {
370      x: tx - padding.left - borderWidth,
371      y: ty - padding.top - borderWidth,
372      w: tw + padding.width + borderWidth * 2,
373      h: th + padding.height + borderWidth * 2
374    },
375    text: {
376      x: tx,
377      y: ty,
378      w: tw,
379      h: th
380    }
381  };
382}
383
384function getScaleOrigin(el, context) {
385  var scale = context.chart.getDatasetMeta(context.datasetIndex).vScale;
386
387  if (!scale) {
388    return null;
389  }
390
391  if (scale.xCenter !== undefined && scale.yCenter !== undefined) {
392    return {x: scale.xCenter, y: scale.yCenter};
393  }
394
395  var pixel = scale.getBasePixel();
396  return el.horizontal ?
397    {x: pixel, y: null} :
398    {x: null, y: pixel};
399}
400
401function getPositioner(el) {
402  if (el instanceof chart_js.ArcElement) {
403    return positioners.arc;
404  }
405  if (el instanceof chart_js.PointElement) {
406    return positioners.point;
407  }
408  if (el instanceof chart_js.BarElement) {
409    return positioners.bar;
410  }
411  return positioners.fallback;
412}
413
414function drawRoundedRect(ctx, x, y, w, h, radius) {
415  var HALF_PI = Math.PI / 2;
416
417  if (radius) {
418    var r = Math.min(radius, h / 2, w / 2);
419    var left = x + r;
420    var top = y + r;
421    var right = x + w - r;
422    var bottom = y + h - r;
423
424    ctx.moveTo(x, top);
425    if (left < right && top < bottom) {
426      ctx.arc(left, top, r, -Math.PI, -HALF_PI);
427      ctx.arc(right, top, r, -HALF_PI, 0);
428      ctx.arc(right, bottom, r, 0, HALF_PI);
429      ctx.arc(left, bottom, r, HALF_PI, Math.PI);
430    } else if (left < right) {
431      ctx.moveTo(left, y);
432      ctx.arc(right, top, r, -HALF_PI, HALF_PI);
433      ctx.arc(left, top, r, HALF_PI, Math.PI + HALF_PI);
434    } else if (top < bottom) {
435      ctx.arc(left, top, r, -Math.PI, 0);
436      ctx.arc(left, bottom, r, 0, Math.PI);
437    } else {
438      ctx.arc(left, top, r, -Math.PI, Math.PI);
439    }
440    ctx.closePath();
441    ctx.moveTo(x, y);
442  } else {
443    ctx.rect(x, y, w, h);
444  }
445}
446
447function drawFrame(ctx, rect, model) {
448  var bgColor = model.backgroundColor;
449  var borderColor = model.borderColor;
450  var borderWidth = model.borderWidth;
451
452  if (!bgColor && (!borderColor || !borderWidth)) {
453    return;
454  }
455
456  ctx.beginPath();
457
458  drawRoundedRect(
459    ctx,
460    rasterize(rect.x) + borderWidth / 2,
461    rasterize(rect.y) + borderWidth / 2,
462    rasterize(rect.w) - borderWidth,
463    rasterize(rect.h) - borderWidth,
464    model.borderRadius);
465
466  ctx.closePath();
467
468  if (bgColor) {
469    ctx.fillStyle = bgColor;
470    ctx.fill();
471  }
472
473  if (borderColor && borderWidth) {
474    ctx.strokeStyle = borderColor;
475    ctx.lineWidth = borderWidth;
476    ctx.lineJoin = 'miter';
477    ctx.stroke();
478  }
479}
480
481function textGeometry(rect, align, font) {
482  var h = font.lineHeight;
483  var w = rect.w;
484  var x = rect.x;
485  var y = rect.y + h / 2;
486
487  if (align === 'center') {
488    x += w / 2;
489  } else if (align === 'end' || align === 'right') {
490    x += w;
491  }
492
493  return {
494    h: h,
495    w: w,
496    x: x,
497    y: y
498  };
499}
500
501function drawTextLine(ctx, text, cfg) {
502  var shadow = ctx.shadowBlur;
503  var stroked = cfg.stroked;
504  var x = rasterize(cfg.x);
505  var y = rasterize(cfg.y);
506  var w = rasterize(cfg.w);
507
508  if (stroked) {
509    ctx.strokeText(text, x, y, w);
510  }
511
512  if (cfg.filled) {
513    if (shadow && stroked) {
514      // Prevent drawing shadow on both the text stroke and fill, so
515      // if the text is stroked, remove the shadow for the text fill.
516      ctx.shadowBlur = 0;
517    }
518
519    ctx.fillText(text, x, y, w);
520
521    if (shadow && stroked) {
522      ctx.shadowBlur = shadow;
523    }
524  }
525}
526
527function drawText(ctx, lines, rect, model) {
528  var align = model.textAlign;
529  var color = model.color;
530  var filled = !!color;
531  var font = model.font;
532  var ilen = lines.length;
533  var strokeColor = model.textStrokeColor;
534  var strokeWidth = model.textStrokeWidth;
535  var stroked = strokeColor && strokeWidth;
536  var i;
537
538  if (!ilen || (!filled && !stroked)) {
539    return;
540  }
541
542  // Adjust coordinates based on text alignment and line height
543  rect = textGeometry(rect, align, font);
544
545  ctx.font = font.string;
546  ctx.textAlign = align;
547  ctx.textBaseline = 'middle';
548  ctx.shadowBlur = model.textShadowBlur;
549  ctx.shadowColor = model.textShadowColor;
550
551  if (filled) {
552    ctx.fillStyle = color;
553  }
554  if (stroked) {
555    ctx.lineJoin = 'round';
556    ctx.lineWidth = strokeWidth;
557    ctx.strokeStyle = strokeColor;
558  }
559
560  for (i = 0, ilen = lines.length; i < ilen; ++i) {
561    drawTextLine(ctx, lines[i], {
562      stroked: stroked,
563      filled: filled,
564      w: rect.w,
565      x: rect.x,
566      y: rect.y + rect.h * i
567    });
568  }
569}
570
571var Label = function(config, ctx, el, index) {
572  var me = this;
573
574  me._config = config;
575  me._index = index;
576  me._model = null;
577  me._rects = null;
578  me._ctx = ctx;
579  me._el = el;
580};
581
582helpers.merge(Label.prototype, {
583  /**
584   * @private
585   */
586  _modelize: function(display, lines, config, context) {
587    var me = this;
588    var index = me._index;
589    var font = helpers.toFont(helpers.resolve([config.font, {}], context, index));
590    var color = helpers.resolve([config.color, chart_js.defaults.color], context, index);
591
592    return {
593      align: helpers.resolve([config.align, 'center'], context, index),
594      anchor: helpers.resolve([config.anchor, 'center'], context, index),
595      area: context.chart.chartArea,
596      backgroundColor: helpers.resolve([config.backgroundColor, null], context, index),
597      borderColor: helpers.resolve([config.borderColor, null], context, index),
598      borderRadius: helpers.resolve([config.borderRadius, 0], context, index),
599      borderWidth: helpers.resolve([config.borderWidth, 0], context, index),
600      clamp: helpers.resolve([config.clamp, false], context, index),
601      clip: helpers.resolve([config.clip, false], context, index),
602      color: color,
603      display: display,
604      font: font,
605      lines: lines,
606      offset: helpers.resolve([config.offset, 0], context, index),
607      opacity: helpers.resolve([config.opacity, 1], context, index),
608      origin: getScaleOrigin(me._el, context),
609      padding: helpers.toPadding(helpers.resolve([config.padding, 0
vendor: 8,706 bytes, lines 609-946
609], context, index)),
610      positioner: getPositioner(me._el),
611      rotation: helpers.resolve([config.rotation, 0], context, index) * (Math.PI / 180),
612      size: utils.textSize(me._ctx, lines, font),
613      textAlign: helpers.resolve([config.textAlign, 'start'], context, index),
614      textShadowBlur: helpers.resolve([config.textShadowBlur, 0], context, index),
615      textShadowColor: helpers.resolve([config.textShadowColor, color], context, index),
616      textStrokeColor: helpers.resolve([config.textStrokeColor, color], context, index),
617      textStrokeWidth: helpers.resolve([config.textStrokeWidth, 0], context, index)
618    };
619  },
620
621  update: function(context) {
622    var me = this;
623    var model = null;
624    var rects = null;
625    var index = me._index;
626    var config = me._config;
627    var value, label, lines;
628
629    // We first resolve the display option (separately) to avoid computing
630    // other options in case the label is hidden (i.e. display: false).
631    var display = helpers.resolve([config.display, true], context, index);
632
633    if (display) {
634      value = context.dataset.data[index];
635      label = helpers.valueOrDefault(helpers.callback(config.formatter, [value, context]), value);
636      lines = helpers.isNullOrUndef(label) ? [] : utils.toTextLines(label);
637
638      if (lines.length) {
639        model = me._modelize(display, lines, config, context);
640        rects = boundingRects(model);
641      }
642    }
643
644    me._model = model;
645    me._rects = rects;
646  },
647
648  geometry: function() {
649    return this._rects ? this._rects.frame : {};
650  },
651
652  rotation: function() {
653    return this._model ? this._model.rotation : 0;
654  },
655
656  visible: function() {
657    return this._model && this._model.opacity;
658  },
659
660  model: function() {
661    return this._model;
662  },
663
664  draw: function(chart, center) {
665    var me = this;
666    var ctx = chart.ctx;
667    var model = me._model;
668    var rects = me._rects;
669    var area;
670
671    if (!this.visible()) {
672      return;
673    }
674
675    ctx.save();
676
677    if (model.clip) {
678      area = model.area;
679      ctx.beginPath();
680      ctx.rect(
681        area.left,
682        area.top,
683        area.right - area.left,
684        area.bottom - area.top);
685      ctx.clip();
686    }
687
688    ctx.globalAlpha = utils.bound(0, model.opacity, 1);
689    ctx.translate(rasterize(center.x), rasterize(center.y));
690    ctx.rotate(model.rotation);
691
692    drawFrame(ctx, rects.frame, model);
693    drawText(ctx, model.lines, rects.text, model);
694
695    ctx.restore();
696  }
697});
698
699var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; // eslint-disable-line es/no-number-minsafeinteger
700var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;  // eslint-disable-line es/no-number-maxsafeinteger
701
702function rotated(point, center, angle) {
703  var cos = Math.cos(angle);
704  var sin = Math.sin(angle);
705  var cx = center.x;
706  var cy = center.y;
707
708  return {
709    x: cx + cos * (point.x - cx) - sin * (point.y - cy),
710    y: cy + sin * (point.x - cx) + cos * (point.y - cy)
711  };
712}
713
714function projected(points, axis) {
715  var min = MAX_INTEGER;
716  var max = MIN_INTEGER;
717  var origin = axis.origin;
718  var i, pt, vx, vy, dp;
719
720  for (i = 0; i < points.length; ++i) {
721    pt = points[i];
722    vx = pt.x - origin.x;
723    vy = pt.y - origin.y;
724    dp = axis.vx * vx + axis.vy * vy;
725    min = Math.min(min, dp);
726    max = Math.max(max, dp);
727  }
728
729  return {
730    min: min,
731    max: max
732  };
733}
734
735function toAxis(p0, p1) {
736  var vx = p1.x - p0.x;
737  var vy = p1.y - p0.y;
738  var ln = Math.sqrt(vx * vx + vy * vy);
739
740  return {
741    vx: (p1.x - p0.x) / ln,
742    vy: (p1.y - p0.y) / ln,
743    origin: p0,
744    ln: ln
745  };
746}
747
748var HitBox = function() {
749  this._rotation = 0;
750  this._rect = {
751    x: 0,
752    y: 0,
753    w: 0,
754    h: 0
755  };
756};
757
758helpers.merge(HitBox.prototype, {
759  center: function() {
760    var r = this._rect;
761    return {
762      x: r.x + r.w / 2,
763      y: r.y + r.h / 2
764    };
765  },
766
767  update: function(center, rect, rotation) {
768    this._rotation = rotation;
769    this._rect = {
770      x: rect.x + center.x,
771      y: rect.y + center.y,
772      w: rect.w,
773      h: rect.h
774    };
775  },
776
777  contains: function(point) {
778    var me = this;
779    var margin = 1;
780    var rect = me._rect;
781
782    point = rotated(point, me.center(), -me._rotation);
783
784    return !(point.x < rect.x - margin
785      || point.y < rect.y - margin
786      || point.x > rect.x + rect.w + margin * 2
787      || point.y > rect.y + rect.h + margin * 2);
788  },
789
790  // Separating Axis Theorem
791  // https://gamedevelopment.tutsplus.com/tutorials/collision-detection-using-the-separating-axis-theorem--gamedev-169
792  intersects: function(other) {
793    var r0 = this._points();
794    var r1 = other._points();
795    var axes = [
796      toAxis(r0[0], r0[1]),
797      toAxis(r0[0], r0[3])
798    ];
799    var i, pr0, pr1;
800
801    if (this._rotation !== other._rotation) {
802      // Only separate with r1 axis if the rotation is different,
803      // else it's enough to separate r0 and r1 with r0 axis only!
804      axes.push(
805        toAxis(r1[0], r1[1]),
806        toAxis(r1[0], r1[3])
807      );
808    }
809
810    for (i = 0; i < axes.length; ++i) {
811      pr0 = projected(r0, axes[i]);
812      pr1 = projected(r1, axes[i]);
813
814      if (pr0.max < pr1.min || pr1.max < pr0.min) {
815        return false;
816      }
817    }
818
819    return true;
820  },
821
822  /**
823   * @private
824   */
825  _points: function() {
826    var me = this;
827    var rect = me._rect;
828    var angle = me._rotation;
829    var center = me.center();
830
831    return [
832      rotated({x: rect.x, y: rect.y}, center, angle),
833      rotated({x: rect.x + rect.w, y: rect.y}, center, angle),
834      rotated({x: rect.x + rect.w, y: rect.y + rect.h}, center, angle),
835      rotated({x: rect.x, y: rect.y + rect.h}, center, angle)
836    ];
837  }
838});
839
840function coordinates(el, model, geometry) {
841  var point = model.positioner(el, model);
842  var vx = point.vx;
843  var vy = point.vy;
844
845  if (!vx && !vy) {
846    // if aligned center, we don't want to offset the center point
847    return {x: point.x, y: point.y};
848  }
849
850  var w = geometry.w;
851  var h = geometry.h;
852
853  // take in account the label rotation
854  var rotation = model.rotation;
855  var dx = Math.abs(w / 2 * Math.cos(rotation)) + Math.abs(h / 2 * Math.sin(rotation));
856  var dy = Math.abs(w / 2 * Math.sin(rotation)) + Math.abs(h / 2 * Math.cos(rotation));
857
858  // scale the unit vector (vx, vy) to get at least dx or dy equal to
859  // w or h respectively (else we would calculate the distance to the
860  // ellipse inscribed in the bounding rect)
861  var vs = 1 / Math.max(Math.abs(vx), Math.abs(vy));
862  dx *= vx * vs;
863  dy *= vy * vs;
864
865  // finally, include the explicit offset
866  dx += model.offset * vx;
867  dy += model.offset * vy;
868
869  return {
870    x: point.x + dx,
871    y: point.y + dy
872  };
873}
874
875function collide(labels, collider) {
876  var i, j, s0, s1;
877
878  // IMPORTANT Iterate in the reverse order since items at the end of the
879  // list have an higher weight/priority and thus should be less impacted
880  // by the overlapping strategy.
881
882  for (i = labels.length - 1; i >= 0; --i) {
883    s0 = labels[i].$layout;
884
885    for (j = i - 1; j >= 0 && s0._visible; --j) {
886      s1 = labels[j].$layout;
887
888      if (s1._visible && s0._box.intersects(s1._box)) {
889        collider(s0, s1);
890      }
891    }
892  }
893
894  return labels;
895}
896
897function compute(labels) {
898  var i, ilen, label, state, geometry, center, proxy;
899
900  // Initialize labels for overlap detection
901  for (i = 0, ilen = labels.length; i < ilen; ++i) {
902    label = labels[i];
903    state = label.$layout;
904
905    if (state._visible) {
906      // Chart.js 3 removed el._model in favor of getProps(), making harder to
907      // abstract reading values in positioners. Also, using string arrays to
908      // read values (i.e. var {a,b,c} = el.getProps(["a","b","c"])) would make
909      // positioners inefficient in the normal case (i.e. not the final values)
910      // and the code a bit ugly, so let's use a Proxy instead.
911      proxy = new Proxy(label._el, {get: (el, p) => el.getProps([p], true)[p]});
912
913      geometry = label.geometry();
914      center = coordinates(proxy, label.model(), geometry);
915      state._box.update(center, geometry, label.rotation());
916    }
917  }
918
919  // Auto hide overlapping labels
920  return collide(labels, function(s0, s1) {
921    var h0 = s0._hidable;
922    var h1 = s1._hidable;
923
924    if ((h0 && h1) || h1) {
925      s1._visible = false;
926    } else if (h0) {
927      s0._visible = false;
928    }
929  });
930}
931
932var layout = {
933  prepare: function(datasets) {
934    var labels = [];
935    var i, j, ilen, jlen, label;
936
937    for (i = 0, ilen = datasets.length; i < ilen; ++i) {
938      for (j = 0, jlen = datasets[i].length; j < jlen; ++j) {
939        label = datasets[i][j];
940        labels.push(label);
941        label.$layout = {
942          _box: new HitBox(),
943          _hidable: false,
944          _visible: true,
945          _set: i,
946          _idx: 
vendor: 10,147 bytes, lines 946-1358
946j
947        };
948      }
949    }
950
951    // TODO New `z` option: labels with a higher z-index are drawn
952    // of top of the ones with a lower index. Lowest z-index labels
953    // are also discarded first when hiding overlapping labels.
954    labels.sort(function(a, b) {
955      var sa = a.$layout;
956      var sb = b.$layout;
957
958      return sa._idx === sb._idx
959        ? sb._set - sa._set
960        : sb._idx - sa._idx;
961    });
962
963    this.update(labels);
964
965    return labels;
966  },
967
968  update: function(labels) {
969    var dirty = false;
970    var i, ilen, label, model, state;
971
972    for (i = 0, ilen = labels.length; i < ilen; ++i) {
973      label = labels[i];
974      model = label.model();
975      state = label.$layout;
976      state._hidable = model && model.display === 'auto';
977      state._visible = label.visible();
978      dirty |= state._hidable;
979    }
980
981    if (dirty) {
982      compute(labels);
983    }
984  },
985
986  lookup: function(labels, point) {
987    var i, state;
988
989    // IMPORTANT Iterate in the reverse order since items at the end of
990    // the list have an higher z-index, thus should be picked first.
991
992    for (i = labels.length - 1; i >= 0; --i) {
993      state = labels[i].$layout;
994
995      if (state && state._visible && state._box.contains(point)) {
996        return labels[i];
997      }
998    }
999
1000    return null;
1001  },
1002
1003  draw: function(chart, labels) {
1004    var i, ilen, label, state, geometry, center;
1005
1006    for (i = 0, ilen = labels.length; i < ilen; ++i) {
1007      label = labels[i];
1008      state = label.$layout;
1009
1010      if (state._visible) {
1011        geometry = label.geometry();
1012        center = coordinates(label._el, label.model(), geometry);
1013        state._box.update(center, geometry, label.rotation());
1014        label.draw(chart, center);
1015      }
1016    }
1017  }
1018};
1019
1020var formatter = function(value) {
1021  if (helpers.isNullOrUndef(value)) {
1022    return null;
1023  }
1024
1025  var label = value;
1026  var keys, klen, k;
1027  if (helpers.isObject(value)) {
1028    if (!helpers.isNullOrUndef(value.label)) {
1029      label = value.label;
1030    } else if (!helpers.isNullOrUndef(value.r)) {
1031      label = value.r;
1032    } else {
1033      label = '';
1034      keys = Object.keys(value);
1035      for (k = 0, klen = keys.length; k < klen; ++k) {
1036        label += (k !== 0 ? ', ' : '') + keys[k] + ': ' + value[keys[k]];
1037      }
1038    }
1039  }
1040
1041  return '' + label;
1042};
1043
1044/**
1045 * IMPORTANT: make sure to also update tests and TypeScript definition
1046 * files (`/test/specs/defaults.spec.js` and `/types/options.d.ts`)
1047 */
1048
1049var defaults = {
1050  align: 'center',
1051  anchor: 'center',
1052  backgroundColor: null,
1053  borderColor: null,
1054  borderRadius: 0,
1055  borderWidth: 0,
1056  clamp: false,
1057  clip: false,
1058  color: undefined,
1059  display: true,
1060  font: {
1061    family: undefined,
1062    lineHeight: 1.2,
1063    size: undefined,
1064    style: undefined,
1065    weight: null
1066  },
1067  formatter: formatter,
1068  labels: undefined,
1069  listeners: {},
1070  offset: 4,
1071  opacity: 1,
1072  padding: {
1073    top: 4,
1074    right: 4,
1075    bottom: 4,
1076    left: 4
1077  },
1078  rotation: 0,
1079  textAlign: 'start',
1080  textStrokeColor: undefined,
1081  textStrokeWidth: 0,
1082  textShadowBlur: 0,
1083  textShadowColor: undefined
1084};
1085
1086/**
1087 * @see https://github.com/chartjs/Chart.js/issues/4176
1088 */
1089
1090var EXPANDO_KEY = '$datalabels';
1091var DEFAULT_KEY = '$default';
1092
1093function configure(dataset, options) {
1094  var override = dataset.datalabels;
1095  var listeners = {};
1096  var configs = [];
1097  var labels, keys;
1098
1099  if (override === false) {
1100    return null;
1101  }
1102  if (override === true) {
1103    override = {};
1104  }
1105
1106  options = helpers.merge({}, [options, override]);
1107  labels = options.labels || {};
1108  keys = Object.keys(labels);
1109  delete options.labels;
1110
1111  if (keys.length) {
1112    keys.forEach(function(key) {
1113      if (labels[key]) {
1114        configs.push(helpers.merge({}, [
1115          options,
1116          labels[key],
1117          {_key: key}
1118        ]));
1119      }
1120    });
1121  } else {
1122    // Default label if no "named" label defined.
1123    configs.push(options);
1124  }
1125
1126  // listeners: {<event-type>: {<label-key>: <fn>}}
1127  listeners = configs.reduce(function(target, config) {
1128    helpers.each(config.listeners || {}, function(fn, event) {
1129      target[event] = target[event] || {};
1130      target[event][config._key || DEFAULT_KEY] = fn;
1131    });
1132
1133    delete config.listeners;
1134    return target;
1135  }, {});
1136
1137  return {
1138    labels: configs,
1139    listeners: listeners
1140  };
1141}
1142
1143function dispatchEvent(chart, listeners, label) {
1144  if (!listeners) {
1145    return;
1146  }
1147
1148  var context = label.$context;
1149  var groups = label.$groups;
1150  var callback;
1151
1152  if (!listeners[groups._set]) {
1153    return;
1154  }
1155
1156  callback = listeners[groups._set][groups._key];
1157  if (!callback) {
1158    return;
1159  }
1160
1161  if (helpers.callback(callback, [context]) === true) {
1162    // Users are allowed to tweak the given context by injecting values that can be
1163    // used in scriptable options to display labels differently based on the current
1164    // event (e.g. highlight an hovered label). That's why we update the label with
1165    // the output context and schedule a new chart render by setting it dirty.
1166    chart[EXPANDO_KEY]._dirty = true;
1167    label.update(context);
1168  }
1169}
1170
1171function dispatchMoveEvents(chart, listeners, previous, label) {
1172  var enter, leave;
1173
1174  if (!previous && !label) {
1175    return;
1176  }
1177
1178  if (!previous) {
1179    enter = true;
1180  } else if (!label) {
1181    leave = true;
1182  } else if (previous !== label) {
1183    leave = enter = true;
1184  }
1185
1186  if (leave) {
1187    dispatchEvent(chart, listeners.leave, previous);
1188  }
1189  if (enter) {
1190    dispatchEvent(chart, listeners.enter, label);
1191  }
1192}
1193
1194function handleMoveEvents(chart, event) {
1195  var expando = chart[EXPANDO_KEY];
1196  var listeners = expando._listeners;
1197  var previous, label;
1198
1199  if (!listeners.enter && !listeners.leave) {
1200    return;
1201  }
1202
1203  if (event.type === 'mousemove') {
1204    label = layout.lookup(expando._labels, event);
1205  } else if (event.type !== 'mouseout') {
1206    return;
1207  }
1208
1209  previous = expando._hovered;
1210  expando._hovered = label;
1211  dispatchMoveEvents(chart, listeners, previous, label);
1212}
1213
1214function handleClickEvents(chart, event) {
1215  var expando = chart[EXPANDO_KEY];
1216  var handlers = expando._listeners.click;
1217  var label = handlers && layout.lookup(expando._labels, event);
1218  if (label) {
1219    dispatchEvent(chart, handlers, label);
1220  }
1221}
1222
1223var plugin = {
1224  id: 'datalabels',
1225
1226  defaults: defaults,
1227
1228  beforeInit: function(chart) {
1229    chart[EXPANDO_KEY] = {
1230      _actives: []
1231    };
1232  },
1233
1234  beforeUpdate: function(chart) {
1235    var expando = chart[EXPANDO_KEY];
1236    expando._listened = false;
1237    expando._listeners = {};     // {<event-type>: {<dataset-index>: {<label-key>: <fn>}}}
1238    expando._datasets = [];      // per dataset labels: [Label[]]
1239    expando._labels = [];        // layouted labels: Label[]
1240  },
1241
1242  afterDatasetUpdate: function(chart, args, options) {
1243    var datasetIndex = args.index;
1244    var expando = chart[EXPANDO_KEY];
1245    var labels = expando._datasets[datasetIndex] = [];
1246    var visible = chart.isDatasetVisible(datasetIndex);
1247    var dataset = chart.data.datasets[datasetIndex];
1248    var config = configure(dataset, options);
1249    var elements = args.meta.data || [];
1250    var ctx = chart.ctx;
1251    var i, j, ilen, jlen, cfg, key, el, label;
1252
1253    ctx.save();
1254
1255    for (i = 0, ilen = elements.length; i < ilen; ++i) {
1256      el = elements[i];
1257      el[EXPANDO_KEY] = [];
1258
1259      if (visible && el && chart.getDataVisibility(i) && !el.skip) {
1260        for (j = 0, jlen = config.labels.length; j < jlen; ++j) {
1261          cfg = config.labels[j];
1262          key = cfg._key;
1263
1264          label = new Label(cfg, ctx, el, i);
1265          label.$groups = {
1266            _set: datasetIndex,
1267            _key: key || DEFAULT_KEY
1268          };
1269          label.$context = {
1270            active: false,
1271            chart: chart,
1272            dataIndex: i,
1273            dataset: dataset,
1274            datasetIndex: datasetIndex
1275          };
1276
1277          label.update(label.$context);
1278          el[EXPANDO_KEY].push(label);
1279          labels.push(label);
1280        }
1281      }
1282    }
1283
1284    ctx.restore();
1285
1286    // Store listeners at the chart level and per event type to optimize
1287    // cases where no listeners are registered for a specific event.
1288    helpers.merge(expando._listeners, config.listeners, {
1289      merger: function(event, target, source) {
1290        target[event] = target[event] || {};
1291        target[event][args.index] = source[event];
1292        expando._listened = true;
1293      }
1294    });
1295  },
1296
1297  afterUpdate: function(chart, options) {
1298    chart[EXPANDO_KEY]._labels = layout.prepare(
1299      chart[EXPANDO_KEY]._datasets,
1300      options);
1301  },
1302
1303  // Draw labels on top of all dataset elements
1304  // https://github.com/chartjs/chartjs-plugin-datalabels/issues/29
1305  // https://github.com/chartjs/chartjs-plugin-datalabels/issues/32
1306  afterDatasetsDraw: function(chart) {
1307    layout.draw(chart, chart[EXPANDO_KEY]._labels);
1308  },
1309
1310  beforeEvent: function(chart, args) {
1311    // If there is no listener registered for this chart, `listened` will be false,
1312    // meaning we can immediately ignore the incoming event and avoid useless extra
1313    // computation for users who don't implement label interactions.
1314    if (chart[EXPANDO_KEY]._listened) {
1315      var event = args.event;
1316      switch (event.type) {
1317      case 'mousemove':
1318      case 'mouseout':
1319        handleMoveEvents(chart, event);
1320        break;
1321      case 'click':
1322        handleClickEvents(chart, event);
1323        break;
1324      }
1325    }
1326  },
1327
1328  afterEvent: function(chart) {
1329    var expando = chart[EXPANDO_KEY];
1330    var previous = expando._actives;
1331    var actives = expando._actives = chart.getActiveElements();
1332    var updates = utils.arrayDiff(previous, actives);
1333    var i, ilen, j, jlen, update, label, labels;
1334
1335    for (i = 0, ilen = updates.length; i < ilen; ++i) {
1336      update = updates[i];
1337      if (update[1]) {
1338        labels = update[0].element[EXPANDO_KEY] || [];
1339        for (j = 0, jlen = labels.length; j < jlen; ++j) {
1340          label = labels[j];
1341          label.$context.active = (update[1] === 1);
1342          label.update(label.$context);
1343        }
1344      }
1345    }
1346
1347    if (expando._dirty || updates.length) {
1348      layout.update(expando._labels);
1349      chart.render();
1350    }
1351
1352    delete expando._dirty;
1353  }
1354};
1355
1356return plugin;
1357
1358})));

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.