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.