1/** 2 * [chartjs-plugin-labels]{@link https://github.com/emn178/chartjs-plugin-labels} 3 * 4 * @version 1.1.0 5 * @author Chen, Yi-Cyuan [[email protected]] 6 * @copyright Chen, Yi-Cyuan 2017-2018 7 * @license MIT 8 */ 9(function () { 10 'use strict'; 11 12 if (typeof Chart === 'undefined') { 13 console.error('Can not find Chart object.'); 14 return; 15 } 16 17 if (typeof Object.assign != 'function') { 18 Object.assign = function (target, varArgs) { 19 if (target == null) { 20 throw new TypeError('Cannot convert undefined or null to object'); 21 } 22 var to = Object(target); 23 for (var index = 1; index < arguments.length; index++) { 24 var nextSource = arguments[index]; 25 if (nextSource != null) { 26 for (var nextKey in nextSource) { 27 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 28 to[nextKey] = nextSource[nextKey]; 29 } 30 } 31 } 32 } 33 return to; 34 }; 35 } 36 37 var SUPPORTED_TYPES = {}; 38 ['pie', 'doughnut', 'polarArea', 'bar'].forEach(function (t) { 39 SUPPORTED_TYPES[t] = true; 40 }); 41 42 function Label() { 43 this.renderToDataset = this.renderToDataset.bind(this); 44 } 45 46 Label.prototype.setup = function (chart, options) { 47 this.chart = chart; 48 this.ctx = chart.ctx; 49 this.args = {}; 50 this.barTotal = {}; 51 var chartOptions = chart.config.options; 52 this.options = Object.assign({ 53 position: 'default', 54 precision: 0, 55 fontSize: chartOptions.defaultFontSize, 56 fontColor: chartOptions.defaultFontColor, 57 fontStyle: chartOptions.defaultFontStyle, 58 fontFamily: chartOptions.defaultFontFamily, 59 shadowOffsetX: 3, 60 shadowOffsetY: 3, 61 shadowColor: 'rgba(0,0,0,0.3)', 62 shadowBlur: 6, 63 images: [], 64 outsidePadding: 2, 65 textMargin: 2, 66 overlap: true 67 }, options); 68 if (chart.config.type === 'bar') { 69 this.options.position = 'default'; 70 this.options.arc = false; 71 this.options.overlap = true; 72 } 73 }; 74 75 Label.prototype.render = function () { 76 this.labelBounds = []; 77 this.chart.data.datasets.forEach(this.renderToDataset); 78 }; 79 80 Label.prototype.renderToDataset = function (dataset, index) { 81 this.totalPercentage = 0; 82 this.total = null; 83 var arg = this.args[index]; 84 arg.meta.data.forEach(function (element, index) { 85 this.renderToElement(dataset, arg, element, index); 86 }.bind(this)); 87 }; 88 89 Label.prototype.renderToElement = function (dataset, arg, element, index) { 90 if (!this.shouldRenderToElement(arg.meta, element)) { 91 return; 92 } 93 this.percentage = null; 94 var label = this.getLabel(dataset, element, index); 95 if (!label) { 96 return; 97 } 98 var ctx = this.ctx; 99 ctx.save(); 100 ctx.font = Chart.helpers.fontString(this.options.fontSize, this.options.fontStyle, this.options.fontFamily); 101 var renderInfo = this.getRenderInfo(element, label); 102 if (!this.drawable(element, label, renderInfo)) { 103 ctx.restore(); 104 return; 105 } 106 ctx.beginPath(); 107 ctx.fillStyle = this.getFontColor(dataset, element, index); 108 this.renderLabel(label, renderInfo); 109 ctx.restore(); 110 }; 111 112 Label.prototype.renderLabel = function (label, renderInfo) { 113 return this.options.arc ? this.renderArcLabel(label, renderInfo) : this.renderBaseLabel(label, renderInfo); 114 }; 115 116 Label.prototype.renderBaseLabel = function (label, position) { 117 var ctx = this.ctx; 118 if (typeof label === 'object') { 119 ctx.drawImage(label, position.x - label.width / 2, position.y - label.height / 2, label.width, label.height); 120 } else { 121 ctx.save(); 122 ctx.textBaseline = 'top'; 123 ctx.textAlign = 'center'; 124 125 if (this.options.textShadow) { 126 ctx.shadowOffsetX = this.options.shadowOffsetX; 127 ctx.shadowOffsetY = this.options.shadowOffsetY; 128 ctx.shadowColor = this.options.shadowColor; 129 ctx.shadowBlur = this.options.shadowBlur; 130 } 131 132 var lines = label.split('\n'); 133 for (var i = 0; i < lines.length; i++) { 134 //ctx.font = Chart.helpers.fontString(30, this.options.fontStyle, this.options.fontFamily); 135 if(lines[i].indexOf('<b>') !== -1){ 136 ctx.font = Chart.helpers.fontString(this.options.fontSize2, 'bold', 'Cabin'); 137 ctx.fillStyle = '#000'; 138 var y = position.y - this.options.fontSize / 2 * lines.length + this.options.fontSize * i; 139 ctx.fillText(lines[i].replace('<b>','').replace('</b>',''), position.x, y + 5); 140 } else { 141 var y = position.y - this.options.fontSize / 2 * lines.length + this.options.fontSize * i; 142 ctx.fillText(lines[i], position.x, y); 143 } 144 145 }
vendor: 9,065 bytes, lines 146-391
146 ctx.restore(); 147 } 148 }; 149 150 Label.prototype.renderArcLabel = function (label, renderInfo) { 151 var ctx = this.ctx, radius = renderInfo.radius, view = renderInfo.view; 152 ctx.save(); 153 ctx.translate(view.x, view.y); 154 if (typeof label === 'string') { 155 ctx.rotate(renderInfo.startAngle); 156 ctx.textBaseline = 'middle'; 157 ctx.textAlign = 'left'; 158 var lines = label.split('\n'), max = 0, widths = [], offset = 0; 159 if (this.options.position === 'border') { 160 offset = (lines.length - 1) * this.options.fontSize / 2; 161 } 162 for (var j = 0; j < lines.length; ++j) { 163 var mertrics = ctx.measureText(lines[j]); 164 if (mertrics.width > max) { 165 max = mertrics.width; 166 } 167 widths.push(mertrics.width); 168 } 169 for (var j = 0; j < lines.length; ++j) { 170 var line = lines[j]; 171 var y = (lines.length - 1 - j) * -this.options.fontSize + offset; 172 ctx.save(); 173 var padding = (max - widths[j]) / 2; 174 ctx.rotate(padding / radius); 175 for (var i = 0; i < line.length; i++) { 176 var char = line.charAt(i); 177 mertrics = ctx.measureText(char); 178 ctx.save(); 179 ctx.translate(0, -1 * radius); 180 ctx.fillText(char, 0, y); 181 ctx.restore(); 182 ctx.rotate(mertrics.width / radius); 183 } 184 ctx.restore(); 185 } 186 } else { 187 ctx.rotate((view.startAngle + Math.PI / 2 + renderInfo.endAngle) / 2); 188 ctx.translate(0, -1 * radius); 189 this.renderLabel(label, { x: 0, y: 0 }); 190 } 191 ctx.restore(); 192 }; 193 194 Label.prototype.shouldRenderToElement = function (meta, element) { 195 return !meta.hidden && !element.hidden && ( 196 this.options.showZero || 197 this.chart.config.type === 'polarArea' ? element._view.outerRadius !== 0 : element._view.circumference !== 0 198 ); 199 }; 200 201 Label.prototype.getLabel = function (dataset, element, index) { 202 var label; 203 if (typeof this.options.render === 'function') { 204 label = this.options.render({ 205 label: this.chart.config.data.labels[index], 206 value: dataset.data[index], 207 percentage: this.getPercentage(dataset, element, index), 208 dataset: dataset, 209 index: index 210 }); 211 } else { 212 switch (this.options.render) { 213 case 'value': 214 label = dataset.data[index]; 215 break; 216 case 'label': 217 label = this.chart.config.data.labels[index]; 218 break; 219 case 'image': 220 label = this.options.images[index] ? this.loadImage(this.options.images[index]) : ''; 221 break; 222 case 'percentage': 223 default: 224 label = this.getPercentage(dataset, element, index) + '%'; 225 break; 226 } 227 } 228 if (typeof label === 'object') { 229 label = this.loadImage(label); 230 } else if (label !== null && label !== undefined) { 231 label = label.toString(); 232 } 233 return label; 234 }; 235 236 Label.prototype.getFontColor = function (dataset, element, index) { 237 var fontColor = this.options.fontColor; 238 if (typeof fontColor === 'function') { 239 fontColor = fontColor({ 240 label: this.chart.config.data.labels[index], 241 value: dataset.data[index], 242 percentage: this.getPercentage(dataset, element, index), 243 backgroundColor: dataset.backgroundColor[index], 244 dataset: dataset, 245 index: index 246 }); 247 } else if (typeof fontColor !== 'string') { 248 fontColor = fontColor[index] || this.chart.config.options.defaultFontColor; 249 } 250 return fontColor; 251 }; 252 253 Label.prototype.getPercentage = function (dataset, element, index) { 254 if (this.percentage !== null) { 255 return this.percentage; 256 } 257 var percentage; 258 if (this.chart.config.type === 'polarArea') { 259 if (this.total === null) { 260 this.total = 0; 261 for (var i = 0;i < dataset.data.length; ++i) { 262 this.total += dataset.data[i]; 263 } 264 } 265 percentage = dataset.data[index] / this.total * 100; 266 } else if (this.chart.config.type === 'bar') { 267 if (this.barTotal[index] === undefined) { 268 this.barTotal[index] = 0; 269 for (var i = 0;i < this.chart.data.datasets.length; ++i) { 270 this.barTotal[index] += this.chart.data.datasets[i].data[index]; 271 } 272 } 273 percentage = dataset.data[index] / this.barTotal[index] * 100; 274 } else { 275 percentage = element._view.circumference / this.chart.config.options.circumference * 100; 276 } 277 percentage = parseFloat(percentage.toFixed(this.options.precision)); 278 if (!this.options.showActualPercentages) { 279 if (this.chart.config.type === 'bar') { 280 this.totalPercentage = this.barTotalPercentage[index] || 0; 281 } 282 this.totalPercentage += percentage; 283 if (this.totalPercentage > 100) { 284 percentage -= this.totalPercentage - 100; 285 percentage = parseFloat(percentage.toFixed(this.options.precision)); 286 } 287 if (this.chart.config.type === 'bar') { 288 this.barTotalPercentage[index] = this.totalPercentage 289 } 290 } 291 this.percentage = percentage; 292 return percentage; 293 }; 294 295 Label.prototype.getRenderInfo = function (element, label) { 296 if (this.chart.config.type === 'bar') { 297 return this.getBarRenderInfo(element, label); 298 } else { 299 return this.options.arc ? this.getArcRenderInfo(element, label) : this.getBaseRenderInfo(element, label); 300 } 301 }; 302 303 Label.prototype.getBaseRenderInfo = function (element, label) { 304 if (this.options.position === 'outside' || this.options.position === 'border') { 305 var renderInfo, rangeFromCentre, 306 view = element._view, 307 centreAngle = view.startAngle + (view.endAngle - view.startAngle) / 2, 308 innerRadius = view.outerRadius / 2; 309 if (this.options.position === 'border') { 310 rangeFromCentre = (view.outerRadius - innerRadius) / 2 + innerRadius; 311 } else if (this.options.position === 'outside') { 312 rangeFromCentre = (view.outerRadius - innerRadius) + innerRadius + this.options.textMargin; 313 } 314 renderInfo = { 315 x: view.x + (Math.cos(centreAngle) * rangeFromCentre), 316 y: view.y + (Math.sin(centreAngle) * rangeFromCentre) 317 }; 318 if (this.options.position === 'outside') { 319 var offset = this.options.textMargin + this.measureLabel(label).width / 2; 320 renderInfo.x += renderInfo.x < view.x ? -offset : offset; 321 } 322 return renderInfo; 323 } else { 324 return element.tooltipPosition(); 325 } 326 }; 327 328 Label.prototype.getArcRenderInfo = function (element, label) { 329 var radius, view = element._view; 330 if (this.options.position === 'outside') { 331 radius = view.outerRadius + this.options.fontSize + this.options.textMargin; 332 } else if (this.options.position === 'border') { 333 radius = (view.outerRadius / 2 + view.outerRadius) / 2; 334 } else { 335 radius = (view.innerRadius + view.outerRadius) / 2; 336 } 337 var startAngle = view.startAngle, endAngle = view.endAngle; 338 var totalAngle = endAngle - startAngle; 339 startAngle += Math.PI / 2; 340 endAngle += Math.PI / 2; 341 var mertrics = this.measureLabel(label); 342 startAngle += (endAngle - (mertrics.width / radius + startAngle)) / 2; 343 return { 344 radius: radius, 345 startAngle: startAngle, 346 endAngle: endAngle, 347 totalAngle: totalAngle, 348 view: view 349 } 350 }; 351 352 Label.prototype.getBarRenderInfo = function (element, label) { 353 var renderInfo = element.tooltipPosition(); 354 renderInfo.y -= this.measureLabel(label).height / 2 + this.options.textMargin; 355 return renderInfo; 356 }; 357 358 Label.prototype.drawable = function (element, label, renderInfo) { 359 if (this.options.overlap) { 360 return true; 361 } else if (this.options.arc) { 362 return renderInfo.endAngle - renderInfo.startAngle <= renderInfo.totalAngle; 363 } else { 364 var mertrics = this.measureLabel(label), 365 left = renderInfo.x - mertrics.width / 2, 366 right = renderInfo.x + mertrics.width / 2, 367 top = renderInfo.y - mertrics.height / 2, 368 bottom = renderInfo.y + mertrics.height / 2; 369 if (this.options.renderInfo === 'outside') { 370 return this.outsideInRange(left, right, top, bottom); 371 } else { 372 return element.inRange(left, top) && element.inRange(left, bottom) && 373 element.inRange(right, top) && element.inRange(right, bottom); 374 } 375 } 376 }; 377 378 Label.prototype.outsideInRange = function (left, right, top, bottom) { 379 var labelBounds = this.labelBounds; 380 for (var i = 0;i < labelBounds.length;++i) { 381 var bound = labelBounds[i]; 382 var potins = [ 383 [left, top], 384 [left, bottom], 385 [right, top], 386 [right, bottom] 387 ]; 388 for (var j = 0;j < potins.length;++j) { 389 var x = potins[j][0]; 390 var y = potins[j][1]; 391 if (x >
391= bound.left && x <= bound.right && y >= bound.top && y <= bound.bottom) { 392 return false; 393 } 394 } 395 potins = [ 396 [bound.left, bound.top], 397 [bound.left, bound.bottom], 398 [bound.right, bound.top], 399 [bound.right, bound.bottom] 400 ]; 401 for (var j = 0;j < potins.length;++j) { 402 var x = potins[j][0]; 403 var y = potins[j][1]; 404 if (x >= left && x <= right && y >= top && y <= bottom) { 405 return false; 406 } 407 } 408 } 409 labelBounds.push({ 410 left: left, 411 right: right, 412 top: top, 413 bottom: bottom 414 }); 415 return true; 416 }; 417 418 Label.prototype.measureLabel = function (label) { 419 if (typeof label === 'object') { 420 return { width: label.width, height: label.height }; 421 } else { 422 var width = 0; 423 var lines = label.split('\n'); 424 for (var i = 0; i < lines.length; ++i) { 425 var result = this.ctx.measureText(lines[i]); 426 if (result.width > width) { 427 width = result.width; 428 } 429 } 430 return { width: width, height: this.options.fontSize * lines.length }; 431 } 432 }; 433 434 Label.prototype.loadImage = function (obj) { 435 var image = new Image(); 436 image.src = obj.src; 437 image.width = obj.width; 438 image.height = obj.height; 439 return image; 440 }; 441 442 Chart.plugins.register({ 443 id: 'labels', 444 beforeDatasetsUpdate: function (chart, options) { 445 if (!SUPPORTED_TYPES[chart.config.type]) { 446 return; 447 } 448 if (!Array.isArray(options)) { 449 options = [options]; 450 } 451 var count = options.length; 452 if (!chart._labels || count !== chart._labels.length) { 453 chart._labels = options.map(function () { 454 return new Label(); 455 }); 456 } 457 var someOutside = false, maxPadding = 0; 458 for (var i = 0; i < count; ++i) { 459 var label = chart._labels[i]; 460 label.setup(chart, options[i]); 461 if (label.options.position === 'outside') { 462 someOutside = true; 463 var padding = label.options.fontSize * 1.5 + label.options.outsidePadding; 464 if (padding > maxPadding) { 465 maxPadding = padding; 466 } 467 } 468 } 469 if (someOutside) { 470 chart.chartArea.top += maxPadding; 471 chart.chartArea.bottom -= maxPadding; 472 } 473 }, 474 afterDatasetUpdate: function (chart, args, options) { 475 if (!SUPPORTED_TYPES[chart.config.type]) { 476 return; 477 } 478 chart._labels.forEach(function (label) { 479 label.args[args.index] = args; 480 }); 481 }, 482 beforeDraw: function (chart) { 483 if (!SUPPORTED_TYPES[chart.config.type]) { 484 return; 485 } 486 chart._labels.forEach(function (label) { 487 label.barTotalPercentage = {}; 488 }); 489 }, 490 afterDatasetsDraw: function (chart) { 491 if (!SUPPORTED_TYPES[chart.config.type]) { 492 return; 493 } 494 chart._labels.forEach(function (label) { 495 label.render(); 496 }); 497 } 498 }); 499})();
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.