vendor: 15,981 bytes, lines 1-490
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 var y = position.y - this.options.fontSize / 2 * lines.length + this.options.fontSize * i; 135 ctx.fillText(lines[i], position.x, y); 136 } 137 ctx.restore(); 138 } 139 }; 140 141 Label.prototype.renderArcLabel = function (label, renderInfo) { 142 var ctx = this.ctx, radius = renderInfo.radius, view = renderInfo.view; 143 ctx.save(); 144 ctx.translate(view.x, view.y); 145 if (typeof label === 'string') { 146 ctx.rotate(renderInfo.startAngle); 147 ctx.textBaseline = 'middle'; 148 ctx.textAlign = 'left'; 149 var lines = label.split('\n'), max = 0, widths = [], offset = 0; 150 if (this.options.position === 'border') { 151 offset = (lines.length - 1) * this.options.fontSize / 2; 152 } 153 for (var j = 0; j < lines.length; ++j) { 154 var mertrics = ctx.measureText(lines[j]); 155 if (mertrics.width > max) { 156 max = mertrics.width; 157 } 158 widths.push(mertrics.width); 159 } 160 for (var j = 0; j < lines.length; ++j) { 161 var line = lines[j]; 162 var y = (lines.length - 1 - j) * -this.options.fontSize + offset; 163 ctx.save(); 164 var padding = (max - widths[j]) / 2; 165 ctx.rotate(padding / radius); 166 for (var i = 0; i < line.length; i++) { 167 var char = line.charAt(i); 168 mertrics = ctx.measureText(char); 169 ctx.save(); 170 ctx.translate(0, -1 * radius); 171 ctx.fillText(char, 0, y); 172 ctx.restore(); 173 ctx.rotate(mertrics.width / radius); 174 } 175 ctx.restore(); 176 } 177 } else { 178 ctx.rotate((view.startAngle + Math.PI / 2 + renderInfo.endAngle) / 2); 179 ctx.translate(0, -1 * radius); 180 this.renderLabel(label, { x: 0, y: 0 }); 181 } 182 ctx.restore(); 183 }; 184 185 Label.prototype.shouldRenderToElement = function (meta, element) { 186 return !meta.hidden && !element.hidden && ( 187 this.options.showZero || 188 this.chart.config.type === 'polarArea' ? element._view.outerRadius !== 0 : element._view.circumference !== 0 189 ); 190 }; 191 192 Label.prototype.getLabel = function (dataset, element, index) { 193 var label; 194 if (typeof this.options.render === 'function') { 195 label = this.options.render({ 196 label: this.chart.config.data.labels[index], 197 value: dataset.data[index], 198 percentage: this.getPercentage(dataset, element, index), 199 dataset: dataset, 200 index: index 201 }); 202 } else { 203 switch (this.options.render) { 204 case 'value': 205 label = dataset.data[index]; 206 break; 207 case 'label': 208 label = this.chart.config.data.labels[index]; 209 break; 210 case 'image': 211 label = this.options.images[index] ? this.loadImage(this.options.images[index]) : ''; 212 break; 213 case 'percentage': 214 default: 215 label = this.getPercentage(dataset, element, index) + '%'; 216 break; 217 } 218 } 219 if (typeof label === 'object') { 220 label = this.loadImage(label); 221 } else if (label !== null && label !== undefined) { 222 label = label.toString(); 223 } 224 return label; 225 }; 226 227 Label.prototype.getFontColor = function (dataset, element, index) { 228 var fontColor = this.options.fontColor; 229 if (typeof fontColor === 'function') { 230 fontColor = fontColor({ 231 label: this.chart.config.data.labels[index], 232 value: dataset.data[index], 233 percentage: this.getPercentage(dataset, element, index), 234 backgroundColor: dataset.backgroundColor[index], 235 dataset: dataset, 236 index: index 237 }); 238 } else if (typeof fontColor !== 'string') { 239 fontColor = fontColor[index] || this.chart.config.options.defaultFontColor; 240 } 241 return fontColor; 242 }; 243 244 Label.prototype.getPercentage = function (dataset, element, index) { 245 if (this.percentage !== null) { 246 return this.percentage; 247 } 248 var percentage; 249 if (this.chart.config.type === 'polarArea') { 250 if (this.total === null) { 251 this.total = 0; 252 for (var i = 0;i < dataset.data.length; ++i) { 253 this.total += dataset.data[i]; 254 } 255 } 256 percentage = dataset.data[index] / this.total * 100; 257 } else if (this.chart.config.type === 'bar') { 258 if (this.barTotal[index] === undefined) { 259 this.barTotal[index] = 0; 260 for (var i = 0;i < this.chart.data.datasets.length; ++i) { 261 this.barTotal[index] += this.chart.data.datasets[i].data[index]; 262 } 263 } 264 percentage = dataset.data[index] / this.barTotal[index] * 100; 265 } else { 266 percentage = element._view.circumference / this.chart.config.options.circumference * 100; 267 } 268 percentage = parseFloat(percentage.toFixed(this.options.precision)); 269 if (!this.options.showActualPercentages) { 270 if (this.chart.config.type === 'bar') { 271 this.totalPercentage = this.barTotalPercentage[index] || 0; 272 } 273 this.totalPercentage += percentage; 274 if (this.totalPercentage > 100) { 275 percentage -= this.totalPercentage - 100; 276 percentage = parseFloat(percentage.toFixed(this.options.precision)); 277 } 278 if (this.chart.config.type === 'bar') { 279 this.barTotalPercentage[index] = this.totalPercentage 280 } 281 } 282 this.percentage = percentage; 283 return percentage; 284 }; 285 286 Label.prototype.getRenderInfo = function (element, label) { 287 if (this.chart.config.type === 'bar') { 288 return this.getBarRenderInfo(element, label); 289 } else { 290 return this.options.arc ? this.getArcRenderInfo(element, label) : this.getBaseRenderInfo(element, label); 291 } 292 }; 293 294 Label.prototype.getBaseRenderInfo = function (element, label) { 295 if (this.options.position === 'outside' || this.options.position === 'border') { 296 var renderInfo, rangeFromCentre, 297 view = element._view, 298 centreAngle = view.startAngle + (view.endAngle - view.startAngle) / 2, 299 innerRadius = view.outerRadius / 2; 300 if (this.options.position === 'border') { 301 rangeFromCentre = (view.outerRadius - innerRadius) / 2 + innerRadius; 302 } else if (this.options.position === 'outside') { 303 rangeFromCentre = (view.outerRadius - innerRadius) + innerRadius + this.options.textMargin; 304 } 305 renderInfo = { 306 x: view.x + (Math.cos(centreAngle) * rangeFromCentre), 307 y: view.y + (Math.sin(centreAngle) * rangeFromCentre) 308 }; 309 if (this.options.position === 'outside') { 310 var offset = this.options.textMargin + this.measureLabel(label).width / 2; 311 renderInfo.x += renderInfo.x < view.x ? -offset : offset; 312 } 313 return renderInfo; 314 } else { 315 return element.tooltipPosition(); 316 } 317 }; 318 319 Label.prototype.getArcRenderInfo = function (element, label) { 320 var radius, view = element._view; 321 if (this.options.position === 'outside') { 322 radius = view.outerRadius + this.options.fontSize + this.options.textMargin; 323 } else if (this.options.position === 'border') { 324 radius = (view.outerRadius / 2 + view.outerRadius) / 2; 325 } else { 326 radius = (view.innerRadius + view.outerRadius) / 2; 327 } 328 var startAngle = view.startAngle, endAngle = view.endAngle; 329 var totalAngle = endAngle - startAngle; 330 startAngle += Math.PI / 2; 331 endAngle += Math.PI / 2; 332 var mertrics = this.measureLabel(label); 333 startAngle += (endAngle - (mertrics.width / radius + startAngle)) / 2; 334 return { 335 radius: radius, 336 startAngle: startAngle, 337 endAngle: endAngle, 338 totalAngle: totalAngle, 339 view: view 340 } 341 }; 342 343 Label.prototype.getBarRenderInfo = function (element, label) { 344 var renderInfo = element.tooltipPosition(); 345 renderInfo.y -= this.measureLabel(label).height / 2 + this.options.textMargin; 346 return renderInfo; 347 }; 348 349 Label.prototype.drawable = function (element, label, renderInfo) { 350 if (this.options.overlap) { 351 return true; 352 } else if (this.options.arc) { 353 return renderInfo.endAngle - renderInfo.startAngle <= renderInfo.totalAngle; 354 } else { 355 var mertrics = this.measureLabel(label), 356 left = renderInfo.x - mertrics.width / 2, 357 right = renderInfo.x + mertrics.width / 2, 358 top = renderInfo.y - mertrics.height / 2, 359 bottom = renderInfo.y + mertrics.height / 2; 360 if (this.options.renderInfo === 'outside') { 361 return this.outsideInRange(left, right, top, bottom); 362 } else { 363 return element.inRange(left, top) && element.inRange(left, bottom) && 364 element.inRange(right, top) && element.inRange(right, bottom); 365 } 366 } 367 }; 368 369 Label.prototype.outsideInRange = function (left, right, top, bottom) { 370 var labelBounds = this.labelBounds; 371 for (var i = 0;i < labelBounds.length;++i) { 372 var bound = labelBounds[i]; 373 var potins = [ 374 [left, top], 375 [left, bottom], 376 [right, top], 377 [right, bottom] 378 ]; 379 for (var j = 0;j < potins.length;++j) { 380 var x = potins[j][0]; 381 var y = potins[j][1]; 382 if (x >= bound.left && x <= bound.right && y >= bound.top && y <= bound.bottom) { 383 return false; 384 } 385 } 386 potins = [ 387 [bound.left, bound.top], 388 [bound.left, bound.bottom], 389 [bound.right, bound.top], 390 [bound.right, bound.bottom] 391 ]; 392 for (var j = 0;j < potins.length;++j) { 393 var x = potins[j][0]; 394 var y = potins[j][1]; 395 if (x >= left && x <= right && y >= top && y <= bottom) { 396 return false; 397 } 398 } 399 } 400 labelBounds.push({ 401 left: left, 402 right: right, 403 top: top, 404 bottom: bottom 405 }); 406 return true; 407 }; 408 409 Label.prototype.measureLabel = function (label) { 410 if (typeof label === 'object') { 411 return { width: label.width, height: label.height }; 412 } else { 413 var width = 0; 414 var lines = label.split('\n'); 415 for (var i = 0; i < lines.length; ++i) { 416 var result = this.ctx.measureText(lines[i]); 417 if (result.width > width) { 418 width = result.width; 419 } 420 } 421 return { width: width, height: this.options.fontSize * lines.length }; 422 } 423 }; 424 425 Label.prototype.loadImage = function (obj) { 426 var image = new Image(); 427 image.src = obj.src; 428 image.width = obj.width; 429 image.height = obj.height; 430 return image; 431 }; 432 433 Chart.plugins.register({ 434 id: 'labels', 435 beforeDatasetsUpdate: function (chart, options) { 436 if (!SUPPORTED_TYPES[chart.config.type]) { 437 return; 438 } 439 if (!Array.isArray(options)) { 440 options = [options]; 441 } 442 var count = options.length; 443 if (!chart._labels || count !== chart._labels.length) { 444 chart._labels = options.map(function () { 445 return new Label(); 446 }); 447 } 448 var someOutside = false, maxPadding = 0; 449 for (var i = 0; i < count; ++i) { 450 var label = chart._labels[i]; 451 label.setup(chart, options[i]); 452 if (label.options.position === 'outside') { 453 someOutside = true; 454 var padding = label.options.fontSize * 1.5 + label.options.outsidePadding; 455 if (padding > maxPadding) { 456 maxPadding = padding; 457 } 458 } 459 } 460 if (someOutside) { 461 chart.chartArea.top += maxPadding; 462 chart.chartArea.bottom -= maxPadding; 463 } 464 }, 465 afterDatasetUpdate: function (chart, args, options) { 466 if (!SUPPORTED_TYPES[chart.config.type]) { 467 return; 468 } 469 chart._labels.forEach(function (label) { 470 label.args[args.index] = args; 471 }); 472 }, 473 beforeDraw: function (chart) { 474 if (!SUPPORTED_TYPES[chart.config.type]) { 475 return; 476 } 477 chart._labels.forEach(function (label) { 478 label.barTotalPercentage = {}; 479 }); 480 }, 481 afterDatasetsDraw: function (chart) { 482 if (!SUPPORTED_TYPES[chart.config.type]) { 483 return; 484 } 485 chart._labels.forEach(function (label) { 486 label.render(); 487 }); 488 } 489 }); 490})();
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.