1(function (root, factory) { 2 if (typeof define === 'function' && define.amd) { 3 // AMD. Register as an anonymous module. 4 define([], function () { 5 return (root.returnExportsGlobal = factory()); 6 }); 7 } else if (typeof exports === 'object') { 8 // Node. Does not work with strict CommonJS, but 9 // only CommonJS-like enviroments that support module.exports, 10 // like Node. 11 module.exports = factory(); 12 } else { 13 root['Chartist'] = factory(); 14 } 15}(this, function () { 16 17 /* Chartist.js 0.5.0 18 * Copyright © 2014 Gion Kunz 19 * Free to use under the WTFPL license. 20 * http://www.wtfpl.net/ 21 */ 22 /** 23 * The core module of Chartist that is mainly providing static functions and higher level functions for chart modules. 24 * 25 * @module Chartist.Core 26 */ 27 var Chartist = { 28 version: '0.5.0' 29 }; 30 31 (function (window, document, Chartist) { 32 'use strict'; 33 34 /** 35 * Helps to simplify functional style code 36 * 37 * @memberof Chartist.Core 38 * @param {*} n This exact value will be returned by the noop function 39 * @return {*} The same value that was provided to the n parameter 40 */ 41 Chartist.noop = function (n) { 42 return n; 43 }; 44 45 /** 46 * Generates a-z from a number 0 to 26 47 * 48 * @memberof Chartist.Core 49 * @param {Number} n A number from 0 to 26 that will result in a letter a-z 50 * @return {String} A character from a-z based on the input number n 51 */ 52 Chartist.alphaNumerate = function (n) { 53 // Limit to a-z 54 return String.fromCharCode(97 + n % 26); 55 }; 56 57 /** 58 * Simple recursive object extend 59 * 60 * @memberof Chartist.Core 61 * @param {Object} target Target object where the source will be merged into 62 * @param {Object...} sources This object (objects) will be merged into target and then target is returned 63 * @return {Object} An object that has the same reference as target but is extended and merged with the properties of source 64 */ 65 Chartist.extend = function (target) { 66 target = target || {}; 67 68 var sources = Array.prototype.slice.call(arguments, 1); 69 sources.forEach(function(source) { 70 for (var prop in source) { 71 if (typeof source[prop] === 'object' && !(source[prop] instanceof Array)) { 72 target[prop] = Chartist.extend(target[prop], source[prop]); 73 } else { 74 target[prop] = source[prop]; 75 } 76 } 77 }); 78 79 return target; 80 }; 81 82 /** 83 * Converts a string to a number while removing the unit if present. If a number is passed then this will be returned unmodified. 84 * 85 * @memberof Chartist.Core 86 * @param {String|Number} value 87 * @returns {Number} Returns the string as number or NaN if the passed length could not be converted to pixel 88 */ 89 Chartist.stripUnit = function(value) { 90 if(typeof value === 'string') { 91 value = value.replace(/[^0-9\+-\.]/g, ''); 92 } 93 94 return +value; 95 }; 96 97 /** 98 * Converts a number to a string with a unit. If a string is passed then this will be returned unmodified. 99 * 100 * @memberof Chartist.Core 101 * @param {Number} value 102 * @param {String} unit 103 * @returns {String} Returns the passed number value with unit. 104 */ 105 Chartist.ensureUnit = function(value, unit) { 106 if(typeof value === 'number') { 107 value = value + unit; 108 } 109 110 return value; 111 }; 112 113 /** 114 * This is a wrapper around document.querySelector that will return the query if it's already of type Node 115 * 116 * @memberof Chartist.Core 117 * @param {String|Node} query The query to use for selecting a Node or a DOM node that will be returned directly 118 * @return {Node} 119 */ 120 Chartist.querySelector = function(query) { 121 return query instanceof Node ? query : document.querySelector(query); 122 }; 123 124 /** 125 * Functional style helper to produce array with given length initialized with undefined values 126 * 127 * @memberof Chartist.Core 128 * @param length 129 * @returns {Array} 130 */ 131 Chartist.times = function(length) { 132 return Array.apply(null, new Array(length)); 133 }; 134 135 /** 136 * Sum helper to be used in reduce functions 137 * 138 * @memberof Chartist.Core 139 * @param previous 140 * @param current 141 * @returns {*} 142 */ 143 Chartist.sum = function(previous, current) { 144 return previous + current; 145 }; 146 147 /** 148 * Map for multi dimensional arrays where their nested arrays will be mapped in serial. The output array will have the length of the largest nested array. The callback function is called with variable arguments where each argument is the nested array value (or undefined if there are no more values). 149 * 150 * @memberof Chartist.Core 151 * @param arr 152 * @param cb 153 * @returns {Array} 154 */ 155 Chartist.serialMap = function(arr, cb) { 156 var result = [], 157 length = Math.max.apply(null, arr.map(function(e) { 158 return e.length; 159 })); 160
161 Chartist.times(length).forEach(function(e, index) { 162 var args = arr.map(function(e) { 163 return e[index]; 164 }); 165 166 result[index] = cb.apply(null, args); 167 }); 168 169 return result; 170 }; 171 172 /** 173 * Create or reinitialize the SVG element for the chart 174 * 175 * @memberof Chartist.Core 176 * @param {Node} container The containing DOM Node object that will be used to plant the SVG element 177 * @param {String} width Set the width of the SVG element. Default is 100% 178 * @param {String} height Set the height of the SVG element. Default is 100% 179 * @param {String} className Specify a class to be added to the SVG element 180 * @return {Object} The created/reinitialized SVG element 181 */ 182 Chartist.createSvg = function (container, width, height, className) { 183 var svg; 184 185 width = width || '100%'; 186 height = height || '100%'; 187 188 svg = container.querySelector('svg'); 189 if(svg) { 190 container.removeChild(svg); 191 } 192 193 // Create svg object with width and height or use 100% as default 194 svg = new Chartist.Svg('svg').attr({ 195 width: width, 196 height: height 197 }).addClass(className).attr({ 198 style: 'width: ' + width + '; height: ' + height + ';' 199 }); 200 201 // Add the DOM node to our container 202 container.appendChild(svg._node); 203 204 return svg; 205 }; 206 207 /** 208 * Convert data series into plain array 209 * 210 * @memberof Chartist.Core 211 * @param {Object} data The series object that contains the data to be visualized in the chart 212 * @return {Array} A plain array that contains the data to be visualized in the chart 213 */ 214 Chartist.getDataArray = function (data) { 215 var array = []; 216 217 for (var i = 0; i < data.series.length; i++) { 218 // If the series array contains an object with a data property we will use the property 219 // otherwise the value directly (array or number) 220 array[i] = typeof(data.series[i]) === 'object' && data.series[i].data !== undefined ? 221 data.series[i].data : data.series[i]; 222 223 // Convert values to number 224 for (var j = 0; j < array[i].length; j++) { 225 array[i][j] = +array[i][j]; 226 } 227 } 228 229 return array; 230 }; 231 232 /** 233 * Adds missing values at the end of the array. This array contains the data, that will be visualized in the chart 234 * 235 * @memberof Chartist.Core 236 * @param {Array} dataArray The array that contains the data to be visualized in the chart. The array in this parameter will be modified by function. 237 * @param {Number} length The length of the x-axis data array. 238 * @return {Array} The array that got updated with missing values. 239 */ 240 Chartist.normalizeDataArray = function (dataArray, length) { 241 for (var i = 0; i < dataArray.length; i++) { 242 if (dataArray[i].length === length) { 243 continue; 244 } 245 246 for (var j = dataArray[i].length; j < length; j++) { 247 dataArray[i][j] = 0; 248 } 249 } 250 251 return dataArray; 252 }; 253 254 /** 255 * Calculate the order of magnitude for the chart scale 256 * 257 * @memberof Chartist.Core 258 * @param {Number} value The value Range of the chart 259 * @return {Number} The order of magnitude 260 */ 261 Chartist.orderOfMagnitude = function (value) { 262 return Math.floor(Math.log(Math.abs(value)) / Math.LN10); 263 }; 264 265 /** 266 * Project a data length into screen coordinates (pixels) 267 * 268 * @memberof Chartist.Core 269 * @param {Object} svg The svg element for the chart 270 * @param {Number} length Single data value from a series array 271 * @param {Object} bounds All the values to set the bounds of the chart 272 * @param {Object} options The Object that contains all the optional values for the chart 273 * @return {Number} The projected data length in pixels 274 */ 275 Chartist.projectLength = function (svg, length, bounds, options) { 276 var availableHeight = Chartist.getAvailableHeight(svg, options); 277 return (length / bounds.range * availableHeight); 278 }; 279 280 /** 281 * Get the height of the area in the chart for the data series 282 * 283 * @memberof Chartist.Core 284 * @param {Object} svg The svg element for the chart 285 * @param {Object} options The Object that contains all the optional values for the chart 286 * @return {Number} The height of the area in the chart for the data series 287 */ 288 Chartist.getAvailableHeight = function (svg, options) { 289 return Math.max((Chartist.stripUnit(options.height) || svg.height()) - (options.chartPadding * 2) - options.axisX.offset, 0); 290 }; 291 292 /** 293 * Get highest and lowest value of data array. This Array contains the data that will be visualized in the chart. 294 * 295 * @memberof Chartist.Core 296 * @param {Array} dataArray The array that contains the data to be visualized in the chart 297 * @return {Object} An object that contains the highest and lowest value that will be visualized on the chart. 298 */ 299 Chartist.getHighLow = function (dataArray) { 300 var i, 301 j, 302 highLow = { 303 high: -Number.MAX_VALUE, 304 low: Number.MAX_VALUE 305 }; 306 307 for (i = 0; i < dataArray.length; i++) { 308 for (j = 0; j < dataArray[i].length; j++) { 309 if (dataArray[i][j] > highLow.high) { 310 highLow.high = dataArray[i][j];
311 } 312 313 if (dataArray[i][j] < highLow.low) { 314 highLow.low = dataArray[i][j]; 315 } 316 } 317 } 318 319 return highLow; 320 }; 321 322 /** 323 * Calculate and retrieve all the bounds for the chart and return them in one array 324 * 325 * @memberof Chartist.Core 326 * @param {Object} svg The svg element for the chart 327 * @param {Object} highLow An object containing a high and low property indicating the value range of the chart. 328 * @param {Object} options The Object that contains all the optional values for the chart 329 * @param {Number} referenceValue The reference value for the chart. 330 * @return {Object} All the values to set the bounds of the chart 331 */ 332 Chartist.getBounds = function (svg, highLow, options, referenceValue) { 333 var i, 334 newMin, 335 newMax, 336 bounds = { 337 high: highLow.high, 338 low: highLow.low 339 }; 340 341 // Overrides of high / low from settings 342 bounds.high = +options.high || (options.high === 0 ? 0 : bounds.high); 343 bounds.low = +options.low || (options.low === 0 ? 0 : bounds.low); 344 345 // If high and low are the same because of misconfiguration or flat data (only the same value) we need 346 // to set the high or low to 0 depending on the polarity 347 if(bounds.high === bounds.low) { 348 // If both values are 0 we set high to 1 349 if(bounds.low === 0) { 350 bounds.high = 1; 351 } else if(bounds.low < 0) { 352 // If we have the same negative value for the bounds we set bounds.high to 0 353 bounds.high = 0; 354 } else { 355 // If we have the same positive value for the bounds we set bounds.low to 0 356 bounds.low = 0; 357 } 358 } 359 360 // Overrides of high / low based on reference value, it will make sure that the invisible reference value is 361 // used to generate the chart. This is useful when the chart always needs to contain the position of the 362 // invisible reference value in the view i.e. for bipolar scales. 363 if (referenceValue || referenceValue === 0) { 364 bounds.high = Math.max(referenceValue, bounds.high); 365 bounds.low = Math.min(referenceValue, bounds.low); 366 } 367 368 bounds.valueRange = bounds.high - bounds.low; 369 bounds.oom = Chartist.orderOfMagnitude(bounds.valueRange); 370 bounds.min = Math.floor(bounds.low / Math.pow(10, bounds.oom)) * Math.pow(10, bounds.oom); 371 bounds.max = Math.ceil(bounds.high / Math.pow(10, bounds.oom)) * Math.pow(10, bounds.oom); 372 bounds.range = bounds.max - bounds.min; 373 bounds.step = Math.pow(10, bounds.oom); 374 bounds.numberOfSteps = Math.round(bounds.range / bounds.step); 375 376 // Optimize scale step by checking if subdivision is possible based on horizontalGridMinSpace 377 // If we are already below the scaleMinSpace value we will scale up 378 var length = Chartist.projectLength(svg, bounds.step, bounds, options), 379 scaleUp = length < options.axisY.scaleMinSpace; 380 381 while (true) { 382 if (scaleUp && Chartist.projectLength(svg, bounds.step, bounds, options) <= options.axisY.scaleMinSpace) { 383 bounds.step *= 2; 384 } else if (!scaleUp && Chartist.projectLength(svg, bounds.step / 2, bounds, options) >= options.axisY.scaleMinSpace) { 385 bounds.step /= 2; 386 } else { 387 break; 388 } 389 } 390 391 // Narrow min and max based on new step 392 newMin = bounds.min; 393 newMax = bounds.max; 394 for (i = bounds.min; i <= bounds.max; i += bounds.step) { 395 if (i + bounds.step < bounds.low) { 396 newMin += bounds.step; 397 } 398 399 if (i - bounds.step >= bounds.high) { 400 newMax -= bounds.step; 401 } 402 } 403 bounds.min = newMin; 404 bounds.max = newMax; 405 bounds.range = bounds.max - bounds.min; 406 407 bounds.values = []; 408 for (i = bounds.min; i <= bounds.max; i += bounds.step) { 409 bounds.values.push(i); 410 } 411 412 return bounds; 413 }; 414 415 /** 416 * Calculate cartesian coordinates of polar coordinates 417 * 418 * @memberof Chartist.Core 419 * @param {Number} centerX X-axis coordinates of center point of circle segment 420 * @param {Number} centerY X-axis coordinates of center point of circle segment 421 * @param {Number} radius Radius of circle segment 422 * @param {Number} angleInDegrees Angle of circle segment in degrees 423 * @return {Number} Coordinates of point on circumference 424 */ 425 Chartist.polarToCartesian = function (centerX, centerY, radius, angleInDegrees) {
426 var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0; 427 428 return { 429 x: centerX + (radius * Math.cos(angleInRadians)), 430 y: centerY + (radius * Math.sin(angleInRadians)) 431 }; 432 }; 433 434 /** 435 * Initialize chart drawing rectangle (area where chart is drawn) x1,y1 = bottom left / x2,y2 = top right 436 * 437 * @memberof Chartist.Core 438 * @param {Object} svg The svg element for the chart 439 * @param {Object} options The Object that contains all the optional values for the chart 440 * @return {Object} The chart rectangles coordinates inside the svg element plus the rectangles measurements 441 */ 442 Chartist.createChartRect = function (svg, options) { 443 var yOffset = options.axisY ? options.axisY.offset : 0, 444 xOffset = options.axisX ? options.axisX.offset : 0; 445 446 return { 447 x1: options.chartPadding + yOffset, 448 y1: Math.max((Chartist.stripUnit(options.height) || svg.height()) - options.chartPadding - xOffset, options.chartPadding), 449 x2: Math.max((Chartist.stripUnit(options.width) || svg.width()) - options.chartPadding, options.chartPadding + yOffset), 450 y2: options.chartPadding, 451 width: function () { 452 return this.x2 - this.x1; 453 }, 454 height: function () { 455 return this.y1 - this.y2; 456 } 457 }; 458 }; 459 460 /** 461 * Creates a label with text and based on support of SVG1.1 extensibility will use a foreignObject with a SPAN element or a fallback to a regular SVG text element. 462 * 463 * @param {Object} parent The SVG element where the label should be created as a child 464 * @param {String} text The label text 465 * @param {Object} attributes An object with all attributes that should be set on the label element 466 * @param {String} className The class names that should be set for this element 467 * @param {Boolean} supportsForeignObject If this is true then a foreignObject will be used instead of a text element 468 * @returns {Object} The newly created SVG element 469 */ 470 Chartist.createLabel = function(parent, text, attributes, className, supportsForeignObject) { 471 if(supportsForeignObject) { 472 var content = '<span class="' + className + '">
472' + text + '</span>'; 473 return parent.foreignObject(content, attributes); 474 } else { 475 return parent.elem('text', attributes, className).text(text); 476 } 477 }; 478 479 /** 480 * Generate grid lines and labels for the x-axis into grid and labels group SVG elements 481 * 482 * @memberof Chartist.Core 483 * @param {Object} chartRect The rectangle that sets the bounds for the chart in the svg element 484 * @param {Object} data The Object that contains the data to be visualized in the chart 485 * @param {Object} grid Chartist.Svg wrapper object to be filled with the grid lines of the chart 486 * @param {Object} labels Chartist.Svg wrapper object to be filled with the lables of the chart 487 * @param {Object} options The Object that contains all the optional values for the chart 488 * @param {Object} eventEmitter The passed event emitter will be used to emit draw events for labels and gridlines 489 * @param {Boolean} supportsForeignObject If this is true then a foreignObject will be used instead of a text element 490 */ 491 Chartist.createXAxis = function (chartRect, data, grid, labels, options, eventEmitter, supportsForeignObject) { 492 // Create X-Axis 493 data.labels.forEach(function (value, index) { 494 var interpolatedValue = options.axisX.labelInterpolationFnc(value, index), 495 width = chartRect.width() / (data.labels.length - (options.fullWidth ? 1 : 0)), 496 height = options.axisX.offset, 497 pos = chartRect.x1 + width * index; 498 499 // If interpolated value returns falsey (except 0) we don't draw the grid line 500 if (!interpolatedValue && interpolatedValue !== 0) { 501 return; 502 } 503 504 if (options.axisX.showGrid) { 505 var gridElement = grid.elem('line', { 506 x1: pos, 507 y1: chartRect.y1, 508 x2: pos, 509 y2: chartRect.y2 510 }, [options.classNames.grid, options.classNames.horizontal].join(' ')); 511 512 // Event for grid draw 513 eventEmitter.emit('draw', { 514 type: 'grid', 515 axis: 'x', 516 index: index, 517 group: grid, 518 element: gridElement, 519 x1: pos, 520 y1: chartRect.y1, 521 x2: pos, 522 y2: chartRect.y2 523 }); 524 } 525 526 if (options.axisX.showLabel) { 527 var labelPosition = { 528 x: pos + options.axisX.labelOffset.x, 529 y: chartRect.y1 + options.axisX.labelOffset.y + (supportsForeignObject ? 5 : 20) 530 }; 531 532 var labelElement = Chartist.createLabel(labels, '' + interpolatedValue, { 533 x: labelPosition.x, 534 y: labelPosition.y, 535 width: width, 536 height: height, 537 style: 'overflow: visible;' 538 }, [options.classNames.label, options.classNames.horizontal].join(' '), supportsForeignObject); 539 540 eventEmitter.emit('draw', { 541 type: 'label', 542 axis: 'x', 543 index: index, 544 group: labels, 545 element: labelElement, 546 text: '' + interpolatedValue, 547 x: labelPosition.x, 548 y: labelPosition.y, 549 width: width, 550 height: height, 551 // TODO: Remove in next major release 552 get space() { 553 window.console.warn('EventEmitter: space is deprecated, use width or height instead.'); 554 return this.width; 555 } 556 }); 557 } 558 }); 559 }; 560 561 /** 562 * Generate grid lines and labels for the y-axis into grid and labels group SVG elements 563 * 564 * @memberof Chartist.Core 565 * @param {Object} chartRect The rectangle that sets the bounds for the chart in the svg element 566 * @param {Object} bounds All the values to set the bounds of the chart 567 * @param {Object} grid Chartist.Svg wrapper object to be filled with the grid lines of the chart 568 * @param {Object} labels Chartist.Svg wrapper object to be filled with the lables of the chart 569 * @param {Object} options The Object that contains all the optional values for the chart 570 * @param {Object} eventEmitter The passed event emitter will be used to emit draw events for labels and gridlines 571 * @param {Boolean} supportsForeignObject If this is true then a foreignObject will be used instead of a text element 572 */ 573 Chartist.createYAxis = function (chartRect, bounds, grid, labels, options, eventEmitter, supportsForeignObject) { 574 // Create Y-Axis
575 bounds.values.forEach(function (value, index) { 576 var interpolatedValue = options.axisY.labelInterpolationFnc(value, index), 577 width = options.axisY.offset, 578 height = chartRect.height() / bounds.values.length, 579 pos = chartRect.y1 - height * index; 580 581 // If interpolated value returns falsey (except 0) we don't draw the grid line 582 if (!interpolatedValue && interpolatedValue !== 0) { 583 return; 584 } 585 586 if (options.axisY.showGrid) { 587 var gridElement = grid.elem('line', { 588 x1: chartRect.x1, 589 y1: pos, 590 x2: chartRect.x2, 591 y2: pos 592 }, [options.classNames.grid, options.classNames.vertical].join(' ')); 593 594 // Event for grid draw 595 eventEmitter.emit('draw', { 596 type: 'grid', 597 axis: 'y', 598 index: index, 599 group: grid, 600 element: gridElement, 601 x1: chartRect.x1, 602 y1: pos, 603 x2: chartRect.x2, 604 y2: pos 605 }); 606 } 607 608 if (options.axisY.showLabel) { 609 var labelPosition = { 610 x: options.chartPadding + options.axisY.labelOffset.x + (supportsForeignObject ? -10 : 0), 611 y: pos + options.axisY.labelOffset.y + (supportsForeignObject ? -15 : 0) 612 }; 613 614 var labelElement = Chartist.createLabel(labels, '' + interpolatedValue, { 615 x: labelPosition.x, 616 y: labelPosition.y, 617 width: width, 618 height: height, 619 style: 'overflow: visible;' 620 }, [options.classNames.label, options.classNames.vertical].join(' '), supportsForeignObject); 621 622 eventEmitter.emit('draw', { 623 type: 'label', 624 axis: 'y', 625 index: index, 626 group: labels, 627 element: labelElement, 628 text: '' + interpolatedValue, 629 x: labelPosition.x, 630 y: labelPosition.y, 631 width: width, 632 height: height, 633 // TODO: Remove in next major release 634 get space() { 635 window.console.warn('EventEmitter: space is deprecated, use width or height instead.'); 636 return this.height; 637 } 638 }); 639 } 640 }); 641 }; 642 643 /** 644 * Determine the current point on the svg element to draw the data series 645 * 646 * @memberof Chartist.Core 647 * @param {Object} chartRect The rectangle that sets the bounds for the chart in the svg element 648 * @param {Object} bounds All the values to set the bounds of the chart 649 * @param {Array} data The array that contains the data to be visualized in the chart 650 * @param {Number} index The index of the current project point 651 * @param {Object} options The chart options that are used to influence the calculations 652 * @return {Object} The coordinates object of the current project point containing an x and y number property 653 */ 654 Chartist.projectPoint = function (chartRect, bounds, data, index, options) { 655 return { 656 x: chartRect.x1 + chartRect.width() / (data.length - (data.length > 1 && options.fullWidth ? 1 : 0)) * index, 657 y: chartRect.y1 - chartRect.height() * (data[index] - bounds.min) / (bounds.range + bounds.step) 658 }; 659 }; 660 661 // TODO: With multiple media queries the handleMediaChange function is triggered too many times, only need one 662 /** 663 * Provides options handling functionality with callback for options changes triggered by responsive options and media query matches 664 * 665 * @memberof Chartist.Core 666 * @param {Object} options Options set by user 667 * @param {Array} responsiveOptions Optional functions to add responsive behavior to chart 668 * @param {Object} eventEmitter The event emitter that will be used to emit the options changed events 669 * @return {Object} The consolidated options object from the defaults, base and matching responsive options 670 */ 671 Chartist.optionsProvider = function (options, responsiveOptions, eventEmitter) { 672 var baseOptions = Chartist.extend({}, options), 673 currentOptions, 674 mediaQueryListeners = [], 675 i; 676 677 function updateCurrentOptions() { 678 var previousOptions = currentOptions; 679 currentOptions = Chartist.extend({}, baseOptions); 680 681 if (responsiveOptions) { 682 for (i = 0; i < responsiveOptions.length; i++) { 683 var mql = window.matchMedia(responsiveOptions[i][0]); 684 if (mql.matches) { 685 currentOptions = Chartist.extend(currentOptions, responsiveOptions[i][1]); 686 } 687 } 688 } 689 690 if(eventEmitter) { 691 eventEmitter.emit('optionsChanged', { 692 previousOptions: previousOptions, 693 currentOptions: currentOptions 694 });
695 } 696 } 697 698 function removeMediaQueryListeners() { 699 mediaQueryListeners.forEach(function(mql) { 700 mql.removeListener(updateCurrentOptions); 701 }); 702 } 703 704 if (!window.matchMedia) { 705 throw 'window.matchMedia not found! Make sure you\'re using a polyfill.'; 706 } else if (responsiveOptions) { 707 708 for (i = 0; i < responsiveOptions.length; i++) { 709 var mql = window.matchMedia(responsiveOptions[i][0]); 710 mql.addListener(updateCurrentOptions); 711 mediaQueryListeners.push(mql); 712 } 713 } 714 // Execute initially so we get the correct options 715 updateCurrentOptions(); 716 717 return { 718 get currentOptions() { 719 return Chartist.extend({}, currentOptions); 720 }, 721 removeMediaQueryListeners: removeMediaQueryListeners 722 }; 723 }; 724 725 //http://schepers.cc/getting-to-the-point 726 Chartist.catmullRom2bezier = function (crp, z) { 727 var d = []; 728 for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) { 729 var p = [ 730 {x: +crp[i - 2], y: +crp[i - 1]}, 731 {x: +crp[i], y: +crp[i + 1]}, 732 {x: +crp[i + 2], y: +crp[i + 3]}, 733 {x: +crp[i + 4], y: +crp[i + 5]} 734 ]; 735 if (z) { 736 if (!i) { 737 p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]}; 738 } else if (iLen - 4 === i) { 739 p[3] = {x: +crp[0], y: +crp[1]}; 740 } else if (iLen - 2 === i) { 741 p[2] = {x: +crp[0], y: +crp[1]}; 742 p[3] = {x: +crp[2], y: +crp[3]}; 743 } 744 } else { 745 if (iLen - 4 === i) { 746 p[3] = p[2]; 747 } else if (!i) { 748 p[0] = {x: +crp[i], y: +crp[i + 1]}; 749 } 750 } 751 d.push( 752 [ 753 (-p[0].x + 6 * p[1].x + p[2].x) / 6, 754 (-p[0].y + 6 * p[1].y + p[2].y) / 6, 755 (p[1].x + 6 * p[2].x - p[3].x) / 6, 756 (p[1].y + 6 * p[2].y - p[3].y) / 6, 757 p[2].x, 758 p[2].y 759 ] 760 ); 761 } 762 763 return d; 764 }; 765 766 }(window, document, Chartist)); 767 ;/** 768 * A very basic event module that helps to generate and catch events. 769 * 770 * @module Chartist.Event 771 */ 772 /* global Chartist */ 773 (function (window, document, Chartist) { 774 'use strict'; 775 776 Chartist.EventEmitter = function () { 777 var handlers = []; 778 779 /** 780 * Add an event handler for a specific event 781 * 782 * @memberof Chartist.Event 783 * @param {String} event The event name 784 * @param {Function} handler A event handler function 785 */ 786 function addEventHandler(event, handler) { 787 handlers[event] = handlers[event] || []; 788 handlers[event].push(handler); 789 } 790 791 /** 792 * Remove an event handler of a specific event name or remove all event handlers for a specific event. 793 * 794 * @memberof Chartist.Event 795 * @param {String} event The event name where a specific or all handlers should be removed 796 * @param {Function} [handler] An optional event handler function. If specified only this specific handler will be removed and otherwise all handlers are removed. 797 */ 798 function removeEventHandler(event, handler) { 799 // Only do something if there are event handlers with this name existing 800 if(handlers[event]) { 801 // If handler is set we will look for a specific handler and only remove this 802 if(handler) { 803 handlers[event].splice(handlers[event].indexOf(handler), 1); 804 if(handlers[event].length === 0) { 805 delete handlers[event]; 806 } 807 } else { 808 // If no handler is specified we remove all handlers for this event 809 delete handlers[event]; 810 } 811 } 812 } 813 814 /** 815 * Use this function to emit an event. All handlers that are listening for this event will be triggered with the data parameter. 816 * 817 * @memberof Chartist.Event 818 * @param {String} event The event name that should be triggered 819 * @param {*} data Arbitrary data that will be passed to the event handler callback functions 820 */ 821 function emit(event, data) { 822 // Only do something if there are event handlers with this name existing 823 if(handlers[event]) { 824 handlers[event].forEach(function(handler) { 825 handler(data); 826 }); 827 } 828 829 // Emit event to star event handlers 830 if(handlers['*']) { 831 handlers['*'].forEach(function(starHandler) { 832 starHandler(event, data); 833 });
834 } 835 } 836 837 return { 838 addEventHandler: addEventHandler, 839 removeEventHandler: removeEventHandler, 840 emit: emit 841 }; 842 }; 843 844 }(window, document, Chartist)); 845 ;/** 846 * This module provides some basic prototype inheritance utilities. 847 * 848 * @module Chartist.Class 849 */ 850 /* global Chartist */ 851 (function(window, document, Chartist) { 852 'use strict'; 853 854 function listToArray(list) { 855 var arr = []; 856 if (list.length) { 857 for (var i = 0; i < list.length; i++) { 858 arr.push(list[i]); 859 } 860 } 861 return arr; 862 } 863 864 /** 865 * Method to extend from current prototype. 866 * 867 * @memberof Chartist.Class 868 * @param {Object} properties The object that serves as definition for the prototype that gets created for the new class. This object should always contain a constructor property that is the desired constructor for the newly created class. 869 * @param {Object} [superProtoOverride] By default extens will use the current class prototype or Chartist.class. With this parameter you can specify any super prototype that will be used. 870 * @returns {Function} Constructor function of the new class 871 * 872 * @example 873 * var Fruit = Class.extend({ 874 * color: undefined, 875 * sugar: undefined, 876 * 877 * constructor: function(color, sugar) { 878 * this.color = color; 879 * this.sugar = sugar; 880 * }, 881 * 882 * eat: function() { 883 * this.sugar = 0; 884 * return this; 885 * } 886 * }); 887 * 888 * var Banana = Fruit.extend({ 889 * length: undefined, 890 * 891 * constructor: function(length, sugar) { 892 * Banana.super.constructor.call(this, 'Yellow', sugar); 893 * this.length = length; 894 * } 895 * }); 896 * 897 * var banana = new Banana(20, 40); 898 * console.log('banana instanceof Fruit', banana instanceof Fruit); 899 * console.log('Fruit is prototype of banana', Fruit.prototype.isPrototypeOf(banana)); 900 * console.log('bananas prototype is Fruit', Object.getPrototypeOf(banana) === Fruit.prototype); 901 * console.log(banana.sugar); 902 * console.log(banana.eat().sugar); 903 * console.log(banana.color); 904 */ 905 function extend(properties, superProtoOverride) { 906 var superProto = superProtoOverride || this.prototype || Chartist.Class; 907 var proto = Object.create(superProto); 908 909 Chartist.Class.cloneDefinitions(proto, properties); 910 911 var constr = function() { 912 var fn = proto.constructor || function () {}, 913 instance; 914 915 // If this is linked to the Chartist namespace the constructor was not called with new 916 // To provide a fallback we will instantiate here and return the instance 917 instance = this === Chartist ? Object.create(proto) : this; 918 fn.apply(instance, Array.prototype.slice.call(arguments, 0)); 919 920 // If this constructor was not called with new we need to return the instance 921 // This will not harm when the constructor has been called with new as the returned value is ignored 922 return instance; 923 }; 924 925 constr.prototype = proto; 926 constr.super = superProto; 927 constr.extend = this.extend; 928 929 return constr; 930 } 931 932 /** 933 * Creates a mixin from multiple super prototypes. 934 * 935 * @memberof Chartist.Class 936 * @param {Array} mixProtoArr An array of super prototypes or an array of super prototype constructors. 937 * @param {Object} properties The object that serves as definition for the prototype that gets created for the new class. This object should always contain a constructor property that is the desired constructor for the newly created class. 938 * @returns {Function} Constructor function of the newly created mixin class 939 * 940 * @example 941 * var Fruit = Class.extend({ 942 * color: undefined, 943 * sugar: undefined, 944 * 945 * constructor: function(color, sugar) { 946 * this.color = color; 947 * this.sugar = sugar; 948 * }, 949 * 950 * eat: function() { 951 * this.sugar = 0; 952 * return this; 953 * } 954 * }); 955 * 956 * var Banana = Fruit.extend({ 957 * length: undefined, 958 * 959 * constructor: function(length, sugar) { 960 * Banana.super.constructor.call(this, 'Yellow', sugar); 961 * this.length = length; 962 * } 963 * }); 964 * 965 * var banana = new Banana(20, 40); 966 * console.log('banana instanceof Fruit', banana instanceof Fruit); 967 * console.log('Fruit is prototype of banana', Fruit.prototype.isPrototypeOf(banana)); 968 * console.log('bananas prototype is Fruit', Object.getPrototypeOf(banana) === Fruit.prototype); 969 * console.log(banana.sugar); 970 * console.log(banana.eat().sugar); 971 * console.log(banana.color); 972 * 973 * 974 * var KCal = Class.extend({ 975 * sugar: 0, 976 * 977 * constructor: function(sugar) { 978 * this.sugar = sugar; 979 * }, 980 * 981 * get kcal() { 982 * return [this.sugar * 4, 'kcal'].join(''); 983 * } 984 * }); 985 * 986 * var Nameable = Class.extend({ 987 * name: undefined, 988 * 989 * constructor: function(name) { 990 * this.name = name; 991 * } 992 * }); 993 * 994 * var NameableKCalBanana = Class.mix([Banana, KCal, Nameable], { 995 * constructor: function(name, length, sugar) { 996 * Nameable.prototype.constructor.call(this, name); 997 * Banana.prototype.constructor.call(this, length, sugar); 998 * }, 999 * 1000 * toString: function() { 1001 * return [this.name, 'with', this.length + 'cm', 'and', this.kcal].join(' '); 1002 * } 1003 * }); 1004 * 1005 * 1006 * 1007 * var banana = new Banana(20, 40); 1008 * console.log('banana instanceof Fruit', banana instanceof Fruit); 1009 * console.log('Fruit is prototype of banana', Fruit.prototype.isPrototypeOf(banana)); 1010 * console.log('bananas prototype is Fruit', Object.getPrototypeOf(banana) === Fruit.prototype); 1011 * console.log(banana.sugar); 1012 * console.log(banana.eat().sugar); 1013 * console.log(banana.color); 1014 * 1015 * var superBanana = new NameableKCalBanana('Super Mixin Banana', 30, 80); 1016 * console.log(superBanana.toString()); 1017 * 1018 */ 1019 function mix(mixProtoArr, properties) { 1020 if(this !== Chartist.Class) { 1021 throw new Error('Chartist.Class.mix should only be called on the type and never on an instance!'); 1022 } 1023 1024 // Make sure our mixin prototypes are the class objects and not the constructors 1025 var superPrototypes = [{}] 1026 .concat(mixProtoArr) 1027 .map(function (prototype) { 1028 return prototype instanceof Function ? prototype.prototype : prototype; 1029 }); 1030 1031 var mixedSuperProto = Chartist.Class.cloneDefinitions.apply(undefined, superPrototypes); 1032 // Delete the constructor if present because we don't want to invoke a constructor on a mixed prototype 1033 delete mixedSuperProto.constructor;
1034 return this.extend(properties, mixedSuperProto); 1035 } 1036 1037 // Variable argument list clones args > 0 into args[0] and retruns modified args[0] 1038 function cloneDefinitions() { 1039 var args = listToArray(arguments); 1040 var target = args[0]; 1041 1042 args.splice(1, args.length - 1).forEach(function (source) { 1043 Object.getOwnPropertyNames(source).forEach(function (propName) { 1044 // If this property already exist in target we delete it first 1045 delete target[propName]; 1046 // Define the property with the descriptor from source 1047 Object.defineProperty(target, propName, 1048 Object.getOwnPropertyDescriptor(source, propName)); 1049 }); 1050 }); 1051 1052 return target; 1053 } 1054 1055 Chartist.Class = { 1056 extend: extend, 1057 mix: mix, 1058 cloneDefinitions: cloneDefinitions 1059 }; 1060 1061 }(window, document, Chartist)); 1062 ;/** 1063 * Base for all chart types. The methods in Chartist.Base are inherited to all chart types. 1064 * 1065 * @module Chartist.Base 1066 */ 1067 /* global Chartist */ 1068 (function(window, document, Chartist) { 1069 'use strict'; 1070 1071 // TODO: Currently we need to re-draw the chart on window resize. This is usually very bad and will affect performance. 1072 // This is done because we can't work with relative coordinates when drawing the chart because SVG Path does not 1073 // work with relative positions yet. We need to check if we can do a viewBox hack to switch to percentage. 1074 // See http://mozilla.6506.n7.nabble.com/Specyfing-paths-with-percentages-unit-td247474.html 1075 // Update: can be done using the above method tested here: http://codepen.io/gionkunz/pen/KDvLj 1076 // The problem is with the label offsets that can't be converted into percentage and affecting the chart container 1077 /** 1078 * Updates the chart which currently does a full reconstruction of the SVG DOM 1079 * 1080 * @param {Object} [data] Optional data you'd like to set for the chart before it will update. If not specified the update method will use the data that is already configured with the chart. 1081 * @memberof Chartist.Base 1082 */ 1083 function update(data) { 1084 this.data = data || this.data; 1085 this.createChart(this.optionsProvider.currentOptions); 1086 return this; 1087 } 1088 1089 /** 1090 * This method can be called on the API object of each chart and will un-register all event listeners that were added to other components. This currently includes a window.resize listener as well as media query listeners if any responsive options have been provided. Use this function if you need to destroy and recreate Chartist charts dynamically. 1091 * 1092 * @memberof Chartist.Base 1093 */ 1094 function detach() { 1095 window.removeEventListener('resize', this.resizeListener); 1096 this.optionsProvider.removeMediaQueryListeners(); 1097 return this; 1098 } 1099 1100 /** 1101 * Use this function to register event handlers. The handler callbacks are synchronous and will run in the main thread rather than the event loop. 1102 * 1103 * @memberof Chartist.Base 1104 * @param {String} event Name of the event. Check the examples for supported events. 1105 * @param {Function} handler The handler function that will be called when an event with the given name was emitted. This function will receive a data argument which contains event data. See the example for more details. 1106 */ 1107 function on(event, handler) { 1108 this.eventEmitter.addEventHandler(event, handler); 1109 return this; 1110 } 1111 1112 /** 1113 * Use this function to un-register event handlers. If the handler function parameter is omitted all handlers for the given event will be un-registered. 1114 * 1115 * @memberof Chartist.Base 1116 * @param {String} event Name of the event for which a handler should be removed 1117 * @param {Function} [handler] The handler function that that was previously used to register a new event handler. This handler will be removed from the event handler list. If this parameter is omitted then all event handlers for the given event are removed from the list. 1118 */ 1119 function off(event, handler) { 1120 this.eventEmitter.removeEventHandler(event, handler); 1121 return this; 1122 } 1123 1124 function initialize() { 1125 // Add window resize listener that re-creates the chart 1126 window.addEventListener('resize', this.resizeListener); 1127 1128 // Obtain current options based on matching media queries (if responsive options are given) 1129 // This will also register a listener that is re-creating the chart based on media changes 1130 this.optionsProvider = Chartist.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter); 1131 1132 // Before the first chart creation we need to register us with all plugins that are configured 1133 // Initialize all relevant plugins with our chart object and the plugin options specified in the config 1134 if(this.options.plugins) { 1135 this.options.plugins.forEach(function(plugin) { 1136 if(plugin instanceof Array) { 1137 plugin[0](this, plugin[1]); 1138 } else { 1139 plugin(this); 1140 } 1141 }.bind(this)); 1142 } 1143 1144 // Create the first chart 1145 this.createChart(this.optionsProvider.currentOptions); 1146 1147 // As chart is initialized from the event loop now we can reset our timeout reference 1148 // This is important if the chart gets initialized on the same element twice 1149 this.initializeTimeoutId = undefined; 1150 } 1151 1152 /** 1153 * Constructor of chart base class. 1154 * 1155 * @param query 1156 * @param data 1157 * @param options 1158 * @param responsiveOptions 1159 * @constructor 1160 */ 1161 function Base(query, data, options, responsiveOptions) { 1162 this.container = Chartist.querySelector(query); 1163 this.data = data; 1164 this.options = options; 1165 this.responsiveOptions = responsiveOptions; 1166 this.eventEmitter = Chartist.EventEmitter(); 1167 this.supportsForeignObject = Chartist.Svg.isSupported('Extensibility'); 1168 this.supportsAnimations = Chartist.Svg.isSupported('AnimationEventsAttribute'); 1169 this.resizeListener = function resizeListener(){ 1170 this.update(); 1171 }.bind(this); 1172 1173 if(this.container) { 1174 // If chartist was already initialized in this container we are detaching all event listeners first 1175 if(this.container.__chartist__) { 1176 if(this.container.__chartist__.initializeTimeoutId) { 1177 // If the initializeTimeoutId is still set we can safely assume that the initialization function has not 1178 // been called yet from the event loop. Therefore we should cancel the timeout and don't need to detach 1179 window.clearTimeout(this.container.__chartist__.initializeTimeoutId); 1180 } else { 1181 // The timeout reference has already been reset which means we need to detach the old chart first 1182 this.container.__chartist__.detach(); 1183 } 1184 } 1185 1186 this.container.__chartist__ = this; 1187 } 1188 1189 // Using event loop for first draw to make it possible to register event listeners in the same call stack where 1190 // the chart was created. 1191 this.initializeTimeoutId = setTimeout(initialize.bind(this), 0); 1192 } 1193 1194 // Creating the chart base class 1195 Chartist.Base = Chartist.Class.extend({ 1196 constructor: Base, 1197 optionsProvider: undefined, 1198 container: undefined, 1199 svg: undefined, 1200 eventEmitter: undefined, 1201 createChart: function() { 1202 throw new Error('Base chart type can\'t be instantiated!'); 1203 }, 1204 update: update, 1205 detach: detach, 1206 on: on, 1207 off: off, 1208 version: Chartist.version, 1209 supportsForeignObject: false 1210 }); 1211 1212 }(window, document, Chartist)); 1213 ;/** 1214 * Chartist SVG module for simple SVG DOM abstraction 1215 * 1216 * @module Chartist.Svg 1217 */ 1218 /* global Chartist */ 1219 (function(window, document, Chartist) { 1220 'use strict'; 1221 1222 var svgNs = 'http://www.w3.org/2000/svg', 1223 xmlNs = 'http://www.w3.org/2000/xmlns/', 1224 xhtmlNs = 'http://www.w3.org/1999/xhtml'; 1225 1226 Chartist.xmlNs = { 1227 qualifiedName: 'xmlns:ct', 1228 prefix: 'ct', 1229 uri: 'http://gionkunz.github.com/chartist-js/ct' 1230 }; 1231 1232 /** 1233 * Chartist.Svg creates a new SVG object wrapper with a starting element. You can use the wrapper to fluently create sub-elements and modify them. 1234 * 1235 * @memberof Chartist.Svg 1236 * @constructor 1237 * @param {String|SVGElement} name The name of the SVG element to create or an SVG dom element which should be wrapped into Chartist.Svg 1238 * @param {Object} attributes An object with properties that will be added as attributes to the SVG element that is created. Attributes with undefined values will not be added. 1239 * @param {String} className This class or class list will be added to the SVG element 1240 * @param {Object} parent The parent SVG wrapper object where this newly created wrapper and it's element will be attached to as child 1241 * @param {Boolean} insertFirst If this param is set to true in conjunction with a parent element the newly created element will be added as first child element in the parent element 1242 */ 1243 function Svg(name, attributes, className, parent, insertFirst) { 1244 // If Svg is getting called with an SVG element we just return the wrapper 1245 if(name instanceof SVGElement) { 1246 this._node = name; 1247 } else { 1248 this._node = document.createElementNS(svgNs, name); 1249 1250 // If this is an SVG element created then custom namespace 1251 if(name === 'svg') { 1252 this._node.setAttributeNS(xmlNs, Chartist.xmlNs.qualifiedName, Chartist.xmlNs.uri); 1253 } 1254 1255 if(attributes) { 1256 this.attr(attributes); 1257 } 1258 1259 if(className) { 1260 this.addClass(className); 1261 } 1262 1263 if(parent) { 1264 if (insertFirst && parent._node.firstChild) { 1265 parent._node.insertBefore(this._node, parent._node.firstChild); 1266 } else { 1267 parent._node.appendChild(this._node); 1268 } 1269 } 1270 } 1271 } 1272 1273 /** 1274 * Set attributes on the current SVG element of the wrapper you're currently working on. 1275 * 1276 * @memberof Chartist.Svg 1277 * @param {Object|String} attributes An object with properties that will be added as attributes to the SVG element that is created. Attributes with undefined values will not be added. If this parameter is a String then the function is used as a getter and will return the attribute value. 1278 * @param {String} ns If specified, the attributes will be set as namespace attributes with ns as prefix. 1279 * @returns {Object|String} The current wrapper object will be returned so it can be used for chaining or the attribute value if used as getter function. 1280 */ 1281 function attr(attributes, ns) { 1282 if(typeof attributes === 'string') { 1283 if(ns) { 1284 return this._node.getAttributeNS(ns, attributes); 1285 } else { 1286 return this._node.getAttribute(attributes); 1287 } 1288 } 1289
1290 Object.keys(attributes).forEach(function(key) { 1291 // If the attribute value is undefined we can skip this one 1292 if(attributes[key] === undefined) { 1293 return; 1294 } 1295 1296 if(ns) { 1297 this._node.setAttributeNS(ns, [Chartist.xmlNs.prefix, ':', key].join(''), attributes[key]); 1298 } else { 1299 this._node.setAttribute(key, attributes[key]); 1300 } 1301 }.bind(this)); 1302 1303 return this; 1304 } 1305 1306 /** 1307 * Create a new SVG element whose wrapper object will be selected for further operations. This way you can also create nested groups easily. 1308 * 1309 * @memberof Chartist.Svg 1310 * @param {String} name The name of the SVG element that should be created as child element of the currently selected element wrapper 1311 * @param {Object} [attributes] An object with properties that will be added as attributes to the SVG element that is created. Attributes with undefined values will not be added. 1312 * @param {String} [className] This class or class list will be added to the SVG element 1313 * @param {Boolean} [insertFirst] If this param is set to true in conjunction with a parent element the newly created element will be added as first child element in the parent element 1314 * @returns {Chartist.Svg} Returns a Chartist.Svg wrapper object that can be used to modify the containing SVG data 1315 */ 1316 function elem(name, attributes, className, insertFirst) { 1317 return new Chartist.Svg(name, attributes, className, this, insertFirst); 1318 } 1319 1320 /** 1321 * Returns the parent Chartist.SVG wrapper object 1322 * 1323 * @returns {Chartist.Svg} Returns a Chartist.Svg wrapper around the parent node of the current node. If the parent node is not existing or it's not an SVG node then this function will return null. 1324 */ 1325 function parent() { 1326 return this._node.parentNode instanceof SVGElement ? new Chartist.Svg(this._node.parentNode) : null; 1327 } 1328 1329 /** 1330 * This method returns a Chartist.Svg wrapper around the root SVG element of the current tree. 1331 * 1332 * @returns {Chartist.Svg} The root SVG element wrapped in a Chartist.Svg element 1333 */ 1334 function root() { 1335 var node = this._node; 1336 while(node.nodeName !== 'svg') { 1337 node = node.parentNode; 1338 } 1339 return new Chartist.Svg(node); 1340 } 1341 1342 /** 1343 * Find the first child SVG element of the current element that matches a CSS selector. The returned object is a Chartist.Svg wrapper. 1344 * 1345 * @param {String} selector A CSS selector that is used to query for child SVG elements 1346 * @returns {Chartist.Svg} The SVG wrapper for the element found or null if no element was found 1347 */ 1348 function querySelector(selector) { 1349 var foundNode = this._node.querySelector(selector); 1350 return foundNode ? new Chartist.Svg(foundNode) : null; 1351 } 1352 1353 /** 1354 * Find the all child SVG elements of the current element that match a CSS selector. The returned object is a Chartist.Svg.List wrapper. 1355 * 1356 * @param {String} selector A CSS selector that is used to query for child SVG elements 1357 * @returns {Chartist.Svg.List} The SVG wrapper list for the element found or null if no element was found 1358 */ 1359 function querySelectorAll(selector) { 1360 var foundNodes = this._node.querySelectorAll(selector); 1361 return foundNodes.length ? new Chartist.Svg.List(foundNodes) : null; 1362 } 1363 1364 /** 1365 * This method creates a foreignObject (see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject) that allows to embed HTML content into a SVG graphic. With the help of foreignObjects you can enable the usage of regular HTML elements inside of SVG where they are subject for SVG positioning and transformation but the Browser will use the HTML rendering capabilities for the containing DOM. 1366 * 1367 * @memberof Chartist.Svg 1368 * @param {Node|String} content The DOM Node, or HTML string that will be converted to a DOM Node, that is then placed into and wrapped by the foreignObject 1369 * @param {String} [attributes] An object with properties that will be added as attributes to the foreignObject element that is created. Attributes with undefined values will not be added. 1370 * @param {String} [className] This class or class list will be added to the SVG element 1371 * @param {Boolean} [insertFirst] Specifies if the foreignObject should be inserted as first child 1372 * @returns {Chartist.Svg} New wrapper object that wraps the foreignObject element 1373 */ 1374 function foreignObject(content, attributes, className, insertFirst) { 1375 // If content is string then we convert it to DOM 1376 // TODO: Handle case where content is not a string nor a DOM Node 1377 if(typeof content === 'string') { 1378 var container = document.createElement('div'); 1379 container.innerHTML = content; 1380 content = container.firstChild; 1381 } 1382 1383 // Adding namespace to content element 1384 content.setAttribute('xmlns', xhtmlNs); 1385 1386 // Creating the foreignObject without required extension attribute (as described here 1387 // http://www.w3.org/TR/SVG/extend.html#ForeignObjectElement) 1388 var fnObj = this.elem('foreignObject', attributes, className, insertFirst); 1389 1390 // Add content to foreignObjectElement 1391 fnObj._node.appendChild(content); 1392 1393 return fnObj; 1394 } 1395 1396 /** 1397 * This method adds a new text element to the current Chartist.Svg wrapper. 1398 * 1399 * @memberof Chartist.Svg 1400 * @param {String} t The text that should be added to the text element that is created 1401 * @returns {Chartist.Svg} The same wrapper object that was used to add the newly created element 1402 */ 1403 function text(t) { 1404 this._node.appendChild(document.createTextNode(t)); 1405 return this; 1406 } 1407 1408 /** 1409 * This method will clear all child nodes of the current wrapper object. 1410 * 1411 * @memberof Chartist.Svg 1412 * @returns {Chartist.Svg} The same wrapper object that got emptied 1413 */ 1414 function empty() { 1415 while (this._node.firstChild) { 1416 this._node.removeChild(this._node.firstChild); 1417 } 1418 1419 return this; 1420 } 1421 1422 /** 1423 * This method will cause the current wrapper to remove itself from its parent wrapper. Use this method if you'd like to get rid of an element in a given DOM structure. 1424 * 1425 * @memberof Chartist.Svg 1426 * @returns {Chartist.Svg} The parent wrapper object of the element that got removed 1427 */ 1428 function remove() { 1429 this._node.parentNode.removeChild(this._node); 1430 return this.parent(); 1431 } 1432 1433 /** 1434 * This method will replace the element with a new element that can be created outside of the current DOM. 1435 * 1436 * @memberof Chartist.Svg 1437 * @param {Chartist.Svg} newElement The new Chartist.Svg object that will be used to replace the current wrapper object 1438 * @returns {Chartist.Svg} The wrapper of the new element 1439 */ 1440 function replace(newElement) { 1441 this._node.parentNode.replaceChild(newElement._node, this._node); 1442 return newElement; 1443 } 1444 1445 /** 1446 * This method will append an element to the current element as a child. 1447 * 1448 * @memberof Chartist.Svg 1449 * @param {Chartist.Svg} element The Chartist.Svg element that should be added as a child 1450 * @param {Boolean} [insertFirst] Specifies if the element should be inserted as first child 1451 * @returns {Chartist.Svg} The wrapper of the appended object 1452 */ 1453 function append(element, insertFirst) { 1454 if(insertFirst && this._node.firstChild) { 1455 this._node.insertBefore(element._node, this._node.firstChild); 1456 } else { 1457 this._node.appendChild(element._node); 1458 } 1459 1460 return this; 1461 } 1462 1463 /** 1464 * Returns an array of class names that are attached to the current wrapper element. This method can not be chained further. 1465 * 1466 * @memberof Chartist.Svg 1467 * @returns {Array} A list of classes or an empty array if there are no classes on the current element 1468 */ 1469 function classes() { 1470 return this._node.getAttribute('class') ? this._node.getAttribute('class').trim().split(/\s+/) : []; 1471 } 1472 1473 /** 1474 * Adds one or a space separated list of classes to the current element and ensures the classes are only existing once. 1475 * 1476 * @memberof Chartist.Svg 1477 * @param {String} names A white space separated list of class names 1478 * @returns {Chartist.Svg} The wrapper of the current element 1479 */ 1480 function addClass(names) { 1481 this._node.setAttribute('class', 1482 this.classes(this._node) 1483 .concat(names.trim().split(/\s+/)) 1484 .filter(function(elem, pos, self) { 1485 return self.indexOf(elem) === pos; 1486 }).join(' ') 1487 ); 1488 1489 return this; 1490 } 1491 1492 /** 1493 * Removes one or a space separated list of classes from the current element. 1494 * 1495 * @memberof Chartist.Svg 1496 * @param {String} names A white space separated list of class names 1497 * @returns {Chartist.Svg} The wrapper of the current element 1498 */ 1499 function removeClass(names) { 1500 var removedClasses = names.trim().split(/\s+/); 1501 1502 this._node.setAttribute('class', this.classes(this._node).filter(function(name) { 1503 return removedClasses.indexOf(name) === -1; 1504 }).join(' '));
1505 1506 return this; 1507 } 1508 1509 /** 1510 * Removes all classes from the current element. 1511 * 1512 * @memberof Chartist.Svg 1513 * @returns {Chartist.Svg} The wrapper of the current element 1514 */ 1515 function removeAllClasses() { 1516 this._node.setAttribute('class', ''); 1517 1518 return this; 1519 } 1520 1521 /** 1522 * Get element height with fallback to svg BoundingBox or parent container dimensions: 1523 * See [bugzilla.mozilla.org](https://bugzilla.mozilla.org/show_bug.cgi?id=530985) 1524 * 1525 * @memberof Chartist.Svg 1526 * @return {Number} The elements height in pixels 1527 */ 1528 function height() { 1529 return this._node.clientHeight || Math.round(this._node.getBBox().height) || this._node.parentNode.clientHeight; 1530 } 1531 1532 /** 1533 * Get element width with fallback to svg BoundingBox or parent container dimensions: 1534 * See [bugzilla.mozilla.org](https://bugzilla.mozilla.org/show_bug.cgi?id=530985) 1535 * 1536 * @memberof Chartist.Core 1537 * @return {Number} The elements width in pixels 1538 */ 1539 function width() { 1540 return this._node.clientWidth || Math.round(this._node.getBBox().width) || this._node.parentNode.clientWidth; 1541 } 1542 1543 /** 1544 * The animate function lets you animate the current element with SMIL animations. You can add animations for multiple attributes at the same time by using an animation definition object. This object should contain SMIL animation attributes. Please refer to http://www.w3.org/TR/SVG/animate.html for a detailed specification about the available animation attributes. Additionally an easing property can be passed in the animation definition object. This can be a string with a name of an easing function in `Chartist.Svg.Easing` or an array with four numbers specifying a cubic Bézier curve. 1545 * **An animations object could look like this:** 1546 * ```javascript 1547 * element.animate({ 1548 * opacity: { 1549 * dur: 1000, 1550 * from: 0, 1551 * to: 1 1552 * }, 1553 * x1: { 1554 * dur: '1000ms', 1555 * from: 100, 1556 * to: 200, 1557 * easing: 'easeOutQuart' 1558 * }, 1559 * y1: { 1560 * dur: '2s', 1561 * from: 0, 1562 * to: 100 1563 * } 1564 * }); 1565 * ``` 1566 * **Automatic unit conversion** 1567 * For the `dur` and the `begin` animate attribute you can also omit a unit by passing a number. The number will automatically be converted to milli seconds. 1568 * **Guided mode** 1569 * The default behavior of SMIL animations with offset using the `begin` attribute is that the attribute will keep it's original value until the animation starts. Mostly this behavior is not desired as you'd like to have your element attributes already initialized with the animation `from` value even before the animation starts. Also if you don't specify `fill="freeze"` on an animate element or if you delete the animation after it's done (which is done in guided mode) the attribute will switch back to the initial value. This behavior is also not desired when performing simple one-time animations. For one-time animations you'd want to trigger animations immediately instead of relative to the document begin time. That's why in guided mode Chartist.Svg will also use the `begin` property to schedule a timeout and manually start the animation after the timeout. If you're using multiple SMIL definition objects for an attribute (in an array), guided mode will be disabled for this attribute, even if you explicitly enabled it. 1570 * If guided mode is enabled the following behavior is added: 1571 * - Before the animation starts (even when delayed with `begin`) the animated attribute will be set already to the `from` value of the animation 1572 * - `begin` is explicitly set to `indefinite` so it can be started manually without relying on document begin time (creation) 1573 * - The animate element will be forced to use `fill="freeze"` 1574 * - The animation will be triggered with `beginElement()` in a timeout where `begin` of the definition object is interpreted in milli seconds. If no `begin` was specified the timeout is triggered immediately. 1575 * - After the animation the element attribute value will be set to the `to` value of the animation 1576 * - The animate element is deleted from the DOM 1577 * 1578 * @memberof Chartist.Svg 1579 * @param {Object} animations An animations object where the property keys are the attributes you'd like to animate. The properties should be objects again that contain the SMIL animation attributes (usually begin, dur, from, and to). The property begin and dur is auto converted (see Automatic unit conversion). You can also schedule multiple animations for the same attribute by passing an Array of SMIL definition objects. Attributes that contain an array of SMIL definition objects will not be executed in guided mode. 1580 * @param {Boolean} guided Specify if guided mode should be activated for this animation (see Guided mode). If not otherwise specified, guided mode will be activated. 1581 * @param {Object} eventEmitter If specified, this event emitter will be notified when an animation starts or ends. 1582 * @returns {Chartist.Svg} The current element where the animation was added 1583 */ 1584 function animate(animations, guided, eventEmitter) { 1585 if(guided === undefined) { 1586 guided = true; 1587 } 1588
1589 Object.keys(animations).forEach(function createAnimateForAttributes(attribute) { 1590 1591 function createAnimate(animationDefinition, guided) { 1592 var attributeProperties = {}, 1593 animate, 1594 timeout, 1595 easing; 1596 1597 // Check if an easing is specified in the definition object and delete it from the object as it will not 1598 // be part of the animate element attributes. 1599 if(animationDefinition.easing) { 1600 // If already an easing Bézier curve array we take it or we lookup a easing array in the Easing object 1601 easing = animationDefinition.easing instanceof Array ? 1602 animationDefinition.easing : 1603 Chartist.Svg.Easing[animationDefinition.easing]; 1604 delete animationDefinition.easing; 1605 } 1606 1607 // If numeric dur or begin was provided we assume milli seconds 1608 animationDefinition.begin = Chartist.ensureUnit(animationDefinition.begin, 'ms'); 1609 animationDefinition.dur = Chartist.ensureUnit(animationDefinition.dur, 'ms'); 1610 1611 if(easing) { 1612 animationDefinition.calcMode = 'spline'; 1613 animationDefinition.keySplines = easing.join(' '); 1614 animationDefinition.keyTimes = '0;1'; 1615 } 1616 1617 // Adding "fill: freeze" if we are in guided mode and set initial attribute values 1618 if(guided) { 1619 animationDefinition.fill = 'freeze'; 1620 // Animated property on our element should already be set to the animation from value in guided mode 1621 attributeProperties[attribute] = animationDefinition.from; 1622 this.attr(attributeProperties); 1623 1624 // In guided mode we also set begin to indefinite so we can trigger the start manually and put the begin 1625 // which needs to be in ms aside 1626 timeout = Chartist.stripUnit(animationDefinition.begin || 0); 1627 animationDefinition.begin = 'indefinite'; 1628 } 1629 1630 animate = this.elem('animate', Chartist.extend({ 1631 attributeName: attribute 1632 }, animationDefinition)); 1633 1634 if(guided) { 1635 // If guided we take the value that was put aside in timeout and trigger the animation manually with a timeout 1636 setTimeout(function() { 1637 // If beginElement fails we set the animated attribute to the end position and remove the animate element 1638 // This happens if the SMIL ElementTimeControl interface is not supported or any other problems occured in 1639 // the browser. (Currently FF 34 does not support animate elements in foreignObjects) 1640 try { 1641 animate._node.beginElement(); 1642 } catch(err) { 1643 // Set animated attribute to current animated value 1644 attributeProperties[attribute] = animationDefinition.to; 1645 this.attr(attributeProperties); 1646 // Remove the animate element as it's no longer required 1647 animate.remove(); 1648 } 1649 }.bind(this), timeout); 1650 } 1651 1652 if(eventEmitter) { 1653 animate._node.addEventListener('beginEvent', function handleBeginEvent() { 1654 eventEmitter.emit('animationBegin', { 1655 element: this, 1656 animate: animate._node, 1657 params: animationDefinition 1658 }); 1659 }.bind(this)); 1660 } 1661 1662 animate._node.addEventListener('endEvent', function handleEndEvent() { 1663 if(eventEmitter) { 1664 eventEmitter.emit('animationEnd', { 1665 element: this, 1666 animate: animate._node, 1667 params: animationDefinition 1668 }); 1669 } 1670 1671 if(guided) { 1672 // Set animated attribute to current animated value 1673 attributeProperties[attribute] = animationDefinition.to; 1674 this.attr(attributeProperties); 1675 // Remove the animate element as it's no longer required 1676 animate.remove(); 1677 } 1678 }.bind(this)); 1679 } 1680 1681 // If current attribute is an array of definition objects we create an animate for each and disable guided mode 1682 if(animations[attribute] instanceof Array) { 1683 animations[attribute].forEach(function(animationDefinition) { 1684 createAnimate.bind(this)(animationDefinition, false); 1685 }.bind(this)); 1686 } else { 1687 createAnimate.bind(this)(animations[attribute], guided); 1688 } 1689 1690 }.bind(this)); 1691 1692 return this; 1693 } 1694 1695 Chartist.Svg = Chartist.Class.extend({ 1696 constructor: Svg, 1697 attr: attr, 1698 elem: elem, 1699 parent: parent, 1700 root: root, 1701 querySelector: querySelector, 1702 querySelectorAll: querySelectorAll, 1703 foreignObject: foreignObject, 1704 text: text, 1705 empty: empty, 1706 remove: remove, 1707 replace: replace, 1708 append: append, 1709 classes: classes, 1710 addClass: addClass, 1711 removeClass: removeClass, 1712 removeAllClasses: removeAllClasses, 1713 height: height, 1714 width: width, 1715 animate: animate 1716 }); 1717 1718 /** 1719 * This method checks for support of a given SVG feature like Extensibility, SVG-animation or the like. Check http://www.w3.org/TR/SVG11/feature for a detailed list. 1720 * 1721 * @memberof Chartist.Svg 1722 * @param {String} feature The SVG 1.1 feature that should be checked for support. 1723 * @returns {Boolean} True of false if the feature is supported or not 1724 */ 1725 Chartist.Svg.isSupported = function(feature) { 1726 return document.implementation.hasFeature('www.http://w3.org/TR/SVG11/feature#' + feature, '1.1'); 1727 }; 1728 1729 /** 1730 * This Object contains some standard easing cubic bezier curves. Then can be used with their name in the `Chartist.Svg.animate`. You can also extend the list and use your own name in the `animate` function. Click the show code button to see the available bezier functions. 1731 * 1732 * @memberof Chartist.Svg 1733 */
1734 var easingCubicBeziers = { 1735 easeInSine: [0.47, 0, 0.745, 0.715], 1736 easeOutSine: [0.39, 0.575, 0.565, 1], 1737 easeInOutSine: [0.445, 0.05, 0.55, 0.95], 1738 easeInQuad: [0.55, 0.085, 0.68, 0.53], 1739 easeOutQuad: [0.25, 0.46, 0.45, 0.94], 1740 easeInOutQuad: [0.455, 0.03, 0.515, 0.955], 1741 easeInCubic: [0.55, 0.055, 0.675, 0.19], 1742 easeOutCubic: [0.215, 0.61, 0.355, 1], 1743 easeInOutCubic: [0.645, 0.045, 0.355, 1], 1744 easeInQuart: [0.895, 0.03, 0.685, 0.22], 1745 easeOutQuart: [0.165, 0.84, 0.44, 1], 1746 easeInOutQuart: [0.77, 0, 0.175, 1], 1747 easeInQuint: [0.755, 0.05, 0.855, 0.06], 1748 easeOutQuint: [0.23, 1, 0.32, 1], 1749 easeInOutQuint: [0.86, 0, 0.07, 1], 1750 easeInExpo: [0.95, 0.05, 0.795, 0.035], 1751 easeOutExpo: [0.19, 1, 0.22, 1], 1752 easeInOutExpo: [1, 0, 0, 1], 1753 easeInCirc: [0.6, 0.04, 0.98, 0.335], 1754 easeOutCirc: [0.075, 0.82, 0.165, 1], 1755 easeInOutCirc: [0.785, 0.135, 0.15, 0.86], 1756 easeInBack: [0.6, -0.28, 0.735, 0.045], 1757 easeOutBack: [0.175, 0.885, 0.32, 1.275], 1758 easeInOutBack: [0.68, -0.55, 0.265, 1.55] 1759 }; 1760 1761 Chartist.Svg.Easing = easingCubicBeziers; 1762 1763 /** 1764 * This helper class is to wrap multiple `Chartist.Svg` elements into a list where you can call the `Chartist.Svg` functions on all elements in the list with one call. This is helpful when you'd like to perform calls with `Chartist.Svg` on multiple elements. 1765 * An instance of this class is also returned by `Chartist.Svg.querySelectorAll`. 1766 * 1767 * @memberof Chartist.Svg 1768 * @param {Array<Node>|NodeList} nodeList An Array of SVG DOM nodes or a SVG DOM NodeList (as returned by document.querySelectorAll) 1769 * @constructor 1770 */ 1771 function SvgList(nodeList) { 1772 var list = this; 1773 1774 this.svgElements = []; 1775 for(var i = 0; i < nodeList.length; i++) { 1776 this.svgElements.push(new Chartist.Svg(nodeList[i])); 1777 } 1778 1779 // Add delegation methods for Chartist.Svg 1780 Object.keys(Chartist.Svg.prototype).filter(function(prototypeProperty) { 1781 return ['constructor', 1782 'parent', 1783 'querySelector', 1784 'querySelectorAll', 1785 'replace', 1786 'append', 1787 'classes', 1788 'height', 1789 'width'].indexOf(prototypeProperty) === -1; 1790 }).forEach(function(prototypeProperty) { 1791 list[prototypeProperty] = function() { 1792 var args = Array.prototype.slice.call(arguments, 0); 1793 list.svgElements.forEach(function(element) { 1794 Chartist.Svg.prototype[prototypeProperty].apply(element, args); 1795 }); 1796 return list; 1797 }; 1798 }); 1799 } 1800 1801 Chartist.Svg.List = Chartist.Class.extend({ 1802 constructor: SvgList 1803 }); 1804 1805 }(window, document, Chartist)); 1806 ;/** 1807 * The Chartist line chart can be used to draw Line or Scatter charts. If used in the browser you can access the global `Chartist` namespace where you find the `Line` function as a main entry point. 1808 * 1809 * For examples on how to use the line chart please check the examples of the `Chartist.Line` method. 1810 * 1811 * @module Chartist.Line 1812 */ 1813 /* global Chartist */ 1814 (function(window, document, Chartist){ 1815 'use strict'; 1816 1817 /** 1818 * Default options in line charts. Expand the code view to see a detailed list of options with comments. 1819 * 1820 * @memberof Chartist.Line 1821 */ 1822 var defaultOptions = { 1823 // Options for X-Axis 1824 axisX: { 1825 // The offset of the labels to the chart area 1826 offset: 30, 1827 // Allows you to correct label positioning on this axis by positive or negative x and y offset. 1828 labelOffset: { 1829 x: 0, 1830 y: 0 1831 }, 1832 // If labels should be shown or not 1833 showLabel: true, 1834 // If the axis grid should be drawn or not 1835 showGrid: true, 1836 // Interpolation function that allows you to intercept the value from the axis label 1837 labelInterpolationFnc: Chartist.noop 1838 }, 1839 // Options for Y-Axis 1840 axisY: { 1841 // The offset of the labels to the chart area 1842 offset: 40, 1843 // Allows you to correct label positioning on this axis by positive or negative x and y offset. 1844 labelOffset: { 1845 x: 0, 1846 y: 0 1847 }, 1848 // If labels should be shown or not 1849 showLabel: true, 1850 // If the axis grid should be drawn or not 1851 showGrid: true, 1852 // Interpolation function that allows you to intercept the value from the axis label 1853 labelInterpolationFnc: Chartist.noop, 1854 // This value specifies the minimum height in pixel of the scale steps 1855 scaleMinSpace: 20 1856 }, 1857 // Specify a fixed width for the chart as a string (i.e. '100px' or '50%') 1858 width: undefined, 1859 // Specify a fixed height for the chart as a string (i.e. '100px' or '50%') 1860 height: undefined, 1861 // If the line should be drawn or not 1862 showLine: true, 1863 // If dots should be drawn or not 1864 showPoint: true, 1865 // If the line chart should draw an area 1866 showArea: false, 1867 // The base for the area chart that will be used to close the area shape (is normally 0) 1868 areaBase: 0, 1869 // Specify if the lines should be smoothed (Catmull-Rom-Splines will be used) 1870 lineSmooth: true, 1871 // Overriding the natural low of the chart allows you to zoom in or limit the charts lowest displayed value 1872 low: undefined, 1873 // Overriding the natural high of the chart allows you to zoom in or limit the charts highest displayed value 1874 high: undefined, 1875 // Padding of the chart drawing area to the container element and labels 1876 chartPadding: 5, 1877 // When set to true, the last grid line on the x-axis is not drawn and the chart elements will expand to the full available width of the chart. For the last label to be drawn correctly you might need to add chart padding or offset the last label with a draw event handler. 1878 fullWidth: false, 1879 // Override the class names that get used to generate the SVG structure of the chart 1880 classNames: { 1881 chart: 'ct-chart-line', 1882 label: 'ct-label', 1883 labelGroup: 'ct-labels', 1884 series: 'ct-series', 1885 line: 'ct-line', 1886 point: 'ct-point', 1887 area: 'ct-area', 1888 grid: 'ct-grid', 1889 gridGroup: 'ct-grids', 1890 vertical: 'ct-vertical', 1891 horizontal: 'ct-horizontal' 1892 } 1893 }; 1894 1895 /** 1896 * Creates a new chart 1897 * 1898 */ 1899 function createChart(options) { 1900 var seriesGroups = [], 1901 bounds, 1902 normalizedData = Chartist.normalizeDataArray(Chartist.getDataArray(this.data), this.data.labels.length); 1903 1904 // Create new svg object 1905 this.svg = Chartist.createSvg(this.container, options.width, options.height, options.classNames.chart); 1906 1907 // initialize bounds 1908 bounds = Chartist.getBounds(this.svg, Chartist.getHighLow(normalizedData), options); 1909 1910 var chartRect = Chartist.createChartRect(this.svg, options); 1911 // Start drawing 1912 var labels = this.svg.elem('g').addClass(options.classNames.labelGroup), 1913 grid = this.svg.elem('g').addClass(options.classNames.gridGroup); 1914 1915 Chartist.createXAxis(chartRect, this.data, grid, labels, options, this.eventEmitter, this.supportsForeignObject); 1916 Chartist.createYAxis(chartRect, bounds, grid, labels, options, this.eventEmitter, this.supportsForeignObject); 1917 1918 // Draw the series 1919 // initialize series groups 1920 for (var i = 0; i < this.data.series.length; i++) { 1921 seriesGroups[i] = this.svg.elem('g'); 1922 1923 // If the series is an object and contains a name we add a custom attribute 1924 if(this.data.series[i].name) { 1925 seriesGroups[i].attr({ 1926 'series-name': this.data.series[i].name 1927 }, Chartist.xmlNs.uri); 1928 } 1929 1930 // Use series class from series data or if not set generate one 1931 seriesGroups[i].addClass([ 1932 options.classNames.series, 1933 (this.data.series[i].className || options.classNames.series + '-' + Chartist.alphaNumerate(i)) 1934 ].join(' '));
1935 1936 var p, 1937 pathCoordinates = [], 1938 point; 1939 1940 for (var j = 0; j < normalizedData[i].length; j++) { 1941 p = Chartist.projectPoint(chartRect, bounds, normalizedData[i], j, options); 1942 pathCoordinates.push(p.x, p.y); 1943 1944 //If we should show points we need to create them now to avoid secondary loop 1945 // Small offset for Firefox to render squares correctly 1946 if (options.showPoint) { 1947 point = seriesGroups[i].elem('line', { 1948 x1: p.x, 1949 y1: p.y, 1950 x2: p.x + 0.01, 1951 y2: p.y 1952 }, options.classNames.point).attr({ 1953 'value': normalizedData[i][j] 1954 }, Chartist.xmlNs.uri); 1955 1956 this.eventEmitter.emit('draw', { 1957 type: 'point', 1958 value: normalizedData[i][j], 1959 index: j, 1960 group: seriesGroups[i], 1961 element: point, 1962 x: p.x, 1963 y: p.y 1964 }); 1965 } 1966 } 1967 1968 // TODO: Nicer handling of conditions, maybe composition? 1969 if (options.showLine || options.showArea) { 1970 // TODO: We should add a path API in the SVG library for easier path creation 1971 var pathElements = ['M' + pathCoordinates[0] + ',' + pathCoordinates[1]]; 1972 1973 // If smoothed path and path has more than two points then use catmull rom to bezier algorithm 1974 if (options.lineSmooth && pathCoordinates.length > 4) { 1975 1976 var cr = Chartist.catmullRom2bezier(pathCoordinates); 1977 for(var k = 0; k < cr.length; k++) { 1978 pathElements.push('C' + cr[k].join()); 1979 } 1980 } else { 1981 for(var l = 3; l < pathCoordinates.length; l += 2) { 1982 pathElements.push('L' + pathCoordinates[l - 1] + ',' + pathCoordinates[l]); 1983 } 1984 } 1985 1986 if(options.showLine) { 1987 var line = seriesGroups[i].elem('path', { 1988 d: pathElements.join('') 1989 }, options.classNames.line, true).attr({ 1990 'values': normalizedData[i] 1991 }, Chartist.xmlNs.uri); 1992 1993 this.eventEmitter.emit('draw', { 1994 type: 'line', 1995 values: normalizedData[i], 1996 index: i, 1997 group: seriesGroups[i], 1998 element: line 1999 }); 2000 } 2001 2002 if(options.showArea) { 2003 // If areaBase is outside the chart area (< low or > high) we need to set it respectively so that 2004 // the area is not drawn outside the chart area. 2005 var areaBase = Math.max(Math.min(options.areaBase, bounds.max), bounds.min); 2006 2007 // If we need to draw area shapes we just make a copy of our pathElements SVG path array 2008 var areaPathElements = pathElements.slice(); 2009 2010 // We project the areaBase value into screen coordinates 2011 var areaBaseProjected = Chartist.projectPoint(chartRect, bounds, [areaBase], 0, options); 2012 // And splice our new area path array to add the missing path elements to close the area shape 2013 areaPathElements.splice(0, 0, 'M' + areaBaseProjected.x + ',' + areaBaseProjected.y); 2014 areaPathElements[1] = 'L' + pathCoordinates[0] + ',' + pathCoordinates[1]; 2015 areaPathElements.push('L' + pathCoordinates[pathCoordinates.length - 2] + ',' + areaBaseProjected.y); 2016 2017 // Create the new path for the area shape with the area class from the options 2018 var area = seriesGroups[i].elem('path', { 2019 d: areaPathElements.join('') 2020 }, options.classNames.area, true).attr({ 2021 'values': normalizedData[i] 2022 }, Chartist.xmlNs.uri); 2023 2024 this.eventEmitter.emit('draw', { 2025 type: 'area', 2026 values: normalizedData[i], 2027 index: i, 2028 group: seriesGroups[i], 2029 element: area 2030 }); 2031 } 2032 } 2033 } 2034 2035 this.eventEmitter.emit('created', { 2036 bounds: bounds, 2037 chartRect: chartRect, 2038 svg: this.svg, 2039 options: options 2040 }); 2041 } 2042 2043 /** 2044 * This method creates a new line chart. 2045 * 2046 * @memberof Chartist.Line 2047 * @param {String|Node} query A selector query string or directly a DOM element 2048 * @param {Object} data The data object that needs to consist of a labels and a series array 2049 * @param {Object} [options] The options object with options that override the default options. Check the examples for a detailed list. 2050 * @param {Array} [responsiveOptions] Specify an array of responsive option arrays which are a media query and options object pair => [[mediaQueryString, optionsObject],[more...]] 2051 * @return {Object} An object which exposes the API for the created chart 2052 * 2053 * @example 2054 * // Create a simple line chart 2055 * var data = { 2056 * // A labels array that can contain any sort of values 2057 * labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], 2058 * // Our series array that contains series objects or in this case series data arrays 2059 * series: [ 2060 * [5, 2, 4, 2, 0] 2061 * ] 2062 * }; 2063 * 2064 * // As options we currently only set a static size of 300x200 px 2065 * var options = { 2066 * width: '300px', 2067 * height: '200px' 2068 * }; 2069 * 2070 * // In the global name space Chartist we call the Line function to initialize a line chart. As a first parameter we pass in a selector where we would like to get our chart created. Second parameter is the actual data object and as a third parameter we pass in our options 2071 * new Chartist.Line('.ct-chart', data, options); 2072 * 2073 * @example 2074 * // Create a line chart with responsive options 2075 * 2076 * var data = { 2077 * // A labels array that can contain any sort of values 2078 * labels: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], 2079 * // Our series array that contains series objects or in this case series data arrays 2080 * series: [ 2081 * [5, 2, 4, 2, 0] 2082 * ] 2083 * }; 2084 * 2085 * // In adition to the regular options we specify responsive option overrides that will override the default configutation based on the matching media queries. 2086 * var responsiveOptions = [ 2087 * ['screen and (min-width: 641px) and (max-width: 1024px)', { 2088 * showPoint: false, 2089 * axisX: { 2090 * labelInterpolationFnc: function(value) {
2091 * // Will return Mon, Tue, Wed etc. on medium screens 2092 * return value.slice(0, 3); 2093 * } 2094 * } 2095 * }], 2096 * ['screen and (max-width: 640px)', { 2097 * showLine: false, 2098 * axisX: { 2099 * labelInterpolationFnc: function(value) { 2100 * // Will return M, T, W etc. on small screens 2101 * return value[0]; 2102 * } 2103 * } 2104 * }] 2105 * ]; 2106 * 2107 * new Chartist.Line('.ct-chart', data, null, responsiveOptions); 2108 * 2109 */ 2110 function Line(query, data, options, responsiveOptions) { 2111 Chartist.Line.super.constructor.call(this, 2112 query, 2113 data, 2114 Chartist.extend({}, defaultOptions, options), 2115 responsiveOptions); 2116 } 2117 2118 // Creating line chart type in Chartist namespace 2119 Chartist.Line = Chartist.Base.extend({ 2120 constructor: Line, 2121 createChart: createChart 2122 }); 2123 2124 }(window, document, Chartist)); 2125 ;/** 2126 * The bar chart module of Chartist that can be used to draw unipolar or bipolar bar and grouped bar charts. 2127 * 2128 * @module Chartist.Bar 2129 */ 2130 /* global Chartist */ 2131 (function(window, document, Chartist){ 2132 'use strict'; 2133 2134 /** 2135 * Default options in bar charts. Expand the code view to see a detailed list of options with comments. 2136 * 2137 * @memberof Chartist.Bar 2138 */ 2139 var defaultOptions = { 2140 // Options for X-Axis 2141 axisX: { 2142 // The offset of the chart drawing area to the border of the container 2143 offset: 30, 2144 // Allows you to correct label positioning on this axis by positive or negative x and y offset. 2145 labelOffset: { 2146 x: 0, 2147 y: 0 2148 }, 2149 // If labels should be shown or not 2150 showLabel: true, 2151 // If the axis grid should be drawn or not 2152 showGrid: true, 2153 // Interpolation function that allows you to intercept the value from the axis label 2154 labelInterpolationFnc: Chartist.noop 2155 }, 2156 // Options for Y-Axis 2157 axisY: { 2158 // The offset of the chart drawing area to the border of the container 2159 offset: 40, 2160 // Allows you to correct label positioning on this axis by positive or negative x and y offset. 2161 labelOffset: { 2162 x: 0, 2163 y: 0 2164 }, 2165 // If labels should be shown or not 2166 showLabel: true, 2167 // If the axis grid should be drawn or not 2168 showGrid: true, 2169 // Interpolation function that allows you to intercept the value from the axis label 2170 labelInterpolationFnc: Chartist.noop, 2171 // This value specifies the minimum height in pixel of the scale steps 2172 scaleMinSpace: 20 2173 }, 2174 // Specify a fixed width for the chart as a string (i.e. '100px' or '50%') 2175 width: undefined, 2176 // Specify a fixed height for the chart as a string (i.e. '100px' or '50%') 2177 height: undefined, 2178 // Overriding the natural high of the chart allows you to zoom in or limit the charts highest displayed value 2179 high: undefined, 2180 // Overriding the natural low of the chart allows you to zoom in or limit the charts lowest displayed value 2181 low: undefined, 2182 // Padding of the chart drawing area to the container element and labels 2183 chartPadding: 5, 2184 // Specify the distance in pixel of bars in a group 2185 seriesBarDistance: 15, 2186 // When set to true, the last grid line on the x-axis is not drawn and the chart elements will expand to the full available width of the chart. For the last label to be drawn correctly you might need to add chart padding or offset the last label with a draw event handler. For bar charts this might be used in conjunction with the centerBars property set to false. 2187 fullWidth: false, 2188 // This property will cause the bars of the bar chart to be drawn on the grid line rather than between two grid lines. This is useful for single series bar charts and might be used in conjunction with the fullWidth property. 2189 centerBars: true, 2190 // If set to true this property will cause the series bars to be stacked and form a total for each series point. This will also influence the y-axis and the overall bounds of the chart. In stacked mode the seriesBarDistance property will have no effect. 2191 stackBars: false, 2192 // Override the class names that get used to generate the SVG structure of the chart 2193 classNames: { 2194 chart: 'ct-chart-bar', 2195 label: 'ct-label', 2196 labelGroup: 'ct-labels', 2197 series: 'ct-series', 2198 bar: 'ct-bar', 2199 grid: 'ct-grid', 2200 gridGroup: 'ct-grids', 2201 vertical: 'ct-vertical', 2202 horizontal: 'ct-horizontal' 2203 } 2204 }; 2205 2206 /** 2207 * Creates a new chart 2208 * 2209 */ 2210 function createChart(options) { 2211 var seriesGroups = [], 2212 bounds, 2213 normalizedData = Chartist.normalizeDataArray(Chartist.getDataArray(this.data), this.data.labels.length), 2214 highLow; 2215 2216 // Create new svg element 2217 this.svg = Chartist.createSvg(this.container, options.width, options.height, options.classNames.chart); 2218 2219 if(options.stackBars) { 2220 // If stacked bars we need to calculate the high low from stacked values from each series 2221 var serialSums = Chartist.serialMap(normalizedData, function serialSums() { 2222 return Array.prototype.slice.call(arguments).reduce(Chartist.sum, 0); 2223 }); 2224 2225 highLow = Chartist.getHighLow([serialSums]); 2226 } else { 2227 highLow = Chartist.getHighLow(normalizedData); 2228 } 2229 2230 // initialize bounds 2231 bounds = Chartist.getBounds(this.svg, highLow, options, 0); 2232 2233 var chartRect = Chartist.createChartRect(this.svg, options); 2234 // Start drawing 2235 var labels = this.svg.elem('g').addClass(options.classNames.labelGroup), 2236 grid = this.svg.elem('g').addClass(options.classNames.gridGroup), 2237 // Projected 0 point 2238 zeroPoint = Chartist.projectPoint(chartRect, bounds, [0], 0, options), 2239 // Used to track the screen coordinates of stacked bars 2240 stackedBarValues = []; 2241 2242 Chartist.createXAxis(chartRect, this.data, grid, labels, options, this.eventEmitter, this.supportsForeignObject); 2243 Chartist.createYAxis(chartRect, bounds, grid, labels, options, this.eventEmitter, this.supportsForeignObject); 2244 2245 // Draw the series 2246 // initialize series groups 2247 for (var i = 0; i < this.data.series.length; i++) { 2248 // Calculating bi-polar value of index for seriesOffset. For i = 0..4 biPol will be -1.5, -0.5, 0.5, 1.5 etc. 2249 var biPol = i - (this.data.series.length - 1) / 2,
2250 // Half of the period width between vertical grid lines used to position bars 2251 periodHalfWidth = chartRect.width() / (normalizedData[i].length - (options.fullWidth ? 1 : 0)) / 2; 2252 2253 seriesGroups[i] = this.svg.elem('g'); 2254 2255 // If the series is an object and contains a name we add a custom attribute 2256 if(this.data.series[i].name) { 2257 seriesGroups[i].attr({ 2258 'series-name': this.data.series[i].name 2259 }, Chartist.xmlNs.uri); 2260 } 2261 2262 // Use series class from series data or if not set generate one 2263 seriesGroups[i].addClass([ 2264 options.classNames.series, 2265 (this.data.series[i].className || options.classNames.series + '-' + Chartist.alphaNumerate(i)) 2266 ].join(' ')); 2267 2268 for(var j = 0; j < normalizedData[i].length; j++) { 2269 var p = Chartist.projectPoint(chartRect, bounds, normalizedData[i], j, options), 2270 bar, 2271 previousStack, 2272 y1, 2273 y2; 2274 2275 // Offset to center bar between grid lines 2276 p.x += (options.centerBars ? periodHalfWidth : 0); 2277 // Using bi-polar offset for multiple series if no stacked bars are used
2278 p.x += options.stackBars ? 0 : biPol * options.seriesBarDistance; 2279 2280 // Enter value in stacked bar values used to remember previous screen value for stacking up bars 2281 previousStack = stackedBarValues[j] || zeroPoint.y; 2282 stackedBarValues[j] = previousStack - (zeroPoint.y - p.y); 2283 2284 // If bars are stacked we use the stackedBarValues reference and otherwise base all bars off the zero line 2285 y1 = options.stackBars ? previousStack : zeroPoint.y; 2286 y2 = options.stackBars ? stackedBarValues[j] : p.y; 2287 2288 bar = seriesGroups[i].elem('line', { 2289 x1: p.x, 2290 y1: y1, 2291 x2: p.x, 2292 y2: y2 2293 }, options.classNames.bar).attr({ 2294 'value': normalizedData[i][j] 2295 }, Chartist.xmlNs.uri); 2296 2297 this.eventEmitter.emit('draw', { 2298 type: 'bar', 2299 value: normalizedData[i][j], 2300 index: j, 2301 group: seriesGroups[i], 2302 element: bar, 2303 x1: p.x, 2304 y1: y1, 2305 x2: p.x, 2306 y2: y2 2307 }); 2308 } 2309 } 2310 2311 this.eventEmitter.emit('created', { 2312 bounds: bounds, 2313 chartRect: chartRect, 2314 svg: this.svg, 2315 options: options 2316 }); 2317 } 2318 2319 /** 2320 * This method creates a new bar chart and returns API object that you can use for later changes. 2321 * 2322 * @memberof Chartist.Bar 2323 * @param {String|Node} query A selector query string or directly a DOM element 2324 * @param {Object} data The data object that needs to consist of a labels and a series array 2325 * @param {Object} [options] The options object with options that override the default options. Check the examples for a detailed list. 2326 * @param {Array} [responsiveOptions] Specify an array of responsive option arrays which are a media query and options object pair => [[mediaQueryString, optionsObject],[more...]] 2327 * @return {Object} An object which exposes the API for the created chart 2328 * 2329 * @example 2330 * // Create a simple bar chart 2331 * var data = { 2332 * labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], 2333 * series: [ 2334 * [5, 2, 4, 2, 0] 2335 * ] 2336 * }; 2337 * 2338 * // In the global name space Chartist we call the Bar function to initialize a bar chart. As a first parameter we pass in a selector where we would like to get our chart created and as a second parameter we pass our data object. 2339 * new Chartist.Bar('.ct-chart', data); 2340 * 2341 * @example 2342 * // This example creates a bipolar grouped bar chart where the boundaries are limitted to -10 and 10 2343 * new Chartist.Bar('.ct-chart', { 2344 * labels: [1, 2, 3, 4, 5, 6, 7], 2345 * series: [ 2346 * [1, 3, 2, -5, -3, 1, -6], 2347 * [-5, -2, -4, -1, 2, -3, 1] 2348 * ] 2349 * }, { 2350 * seriesBarDistance: 12, 2351 * low: -10, 2352 * high: 10 2353 * }); 2354 * 2355 */ 2356 function Bar(query, data, options, responsiveOptions) { 2357 Chartist.Bar.super.constructor.call(this, 2358 query, 2359 data, 2360 Chartist.extend({}, defaultOptions, options), 2361 responsiveOptions); 2362 } 2363 2364 // Creating bar chart type in Chartist namespace 2365 Chartist.Bar = Chartist.Base.extend({ 2366 constructor: Bar, 2367 createChart: createChart 2368 }); 2369 2370 }(window, document, Chartist)); 2371 ;/** 2372 * The pie chart module of Chartist that can be used to draw pie, donut or gauge charts 2373 * 2374 * @module Chartist.Pie 2375 */ 2376 /* global Chartist */ 2377 (function(window, document, Chartist) { 2378 'use strict'; 2379 2380 /** 2381 * Default options in line charts. Expand the code view to see a detailed list of options with comments. 2382 * 2383 * @memberof Chartist.Pie 2384 */ 2385 var defaultOptions = { 2386 // Specify a fixed width for the chart as a string (i.e. '100px' or '50%') 2387 width: undefined, 2388 // Specify a fixed height for the chart as a string (i.e. '100px' or '50%') 2389 height: undefined, 2390 // Padding of the chart drawing area to the container element and labels 2391 chartPadding: 5, 2392 // Override the class names that get used to generate the SVG structure of the chart 2393 classNames: { 2394 chart: 'ct-chart-pie', 2395 series: 'ct-series', 2396 slice: 'ct-slice', 2397 donut: 'ct-donut', 2398 label: 'ct-label' 2399 }, 2400 // The start angle of the pie chart in degrees where 0 points n
2400orth. A higher value offsets the start angle clockwise. 2401 startAngle: 0, 2402 // An optional total you can specify. By specifying a total value, the sum of the values in the series must be this total in order to draw a full pie. You can use this parameter to draw only parts of a pie or gauge charts. 2403 total: undefined, 2404 // If specified the donut CSS classes will be used and strokes will be drawn instead of pie slices. 2405 donut: false, 2406 // Specify the donut stroke width, currently done in javascript for convenience. May move to CSS styles in the future. 2407 donutWidth: 60, 2408 // If a label should be shown or not 2409 showLabel: true, 2410 // Label position offset from the standard position which is half distance of the radius. This value can be either positive or negative. Positive values will position the label away from the center. 2411 labelOffset: 0, 2412 // An interpolation function for the label value 2413 labelInterpolationFnc: Chartist.noop, 2414 // Label direction can be 'neutral', 'explode' or 'implode'. The labels anchor will be positioned based on those settings as well as the fact if the labels are on the right or left side of the center of the chart. Usually explode is useful when labels are positioned far away from the center. 2415 labelDirection: 'neutral' 2416 }; 2417 2418 /** 2419 * Determines SVG anchor position based on direction and center parameter 2420 * 2421 * @param center 2422 * @param label 2423 * @param direction 2424 * @returns {string} 2425 */ 2426 function determineAnchorPosition(center, label, direction) { 2427 var toTheRight = label.x > center.x; 2428 2429 if(toTheRight && direction === 'explode' || 2430 !toTheRight && direction === 'implode') { 2431 return 'start'; 2432 } else if(toTheRight && direction === 'implode' || 2433 !toTheRight && direction === 'explode') { 2434 return 'end'; 2435 } else { 2436 return 'middle'; 2437 } 2438 } 2439 2440 /** 2441 * Creates the pie chart 2442 * 2443 * @param options 2444 */ 2445 function createChart(options) { 2446 var seriesGroups = [], 2447 chartRect, 2448 radius, 2449 labelRadius, 2450 totalDataSum, 2451 startAngle = options.startAngle, 2452 dataArray = Chartist.getDataArray(this.data); 2453 2454 // Create SVG.js draw 2455 this.svg = Chartist.createSvg(this.container, options.width, options.height, options.classNames.chart); 2456 // Calculate charting rect 2457 chartRect = Chartist.createChartRect(this.svg, options, 0, 0); 2458 // Get biggest circle radius possible within chartRect 2459 radius = Math.min(chartRect.width() / 2, chartRect.height() / 2); 2460 // Calculate total of all series to get reference value or use total reference from optional options 2461 totalDataSum = options.total || dataArray.reduce(function(previousValue, currentValue) { 2462 return previousValue + currentValue; 2463 }, 0); 2464 2465 // If this is a donut chart we need to adjust our radius to enable strokes to be drawn inside 2466 // Unfortunately this is not possible with the current SVG Spec 2467 // See this proposal for more details: http://lists.w3.org/Archives/Public/www-svg/2003Oct/0000.html 2468 radius -= options.donut ? options.donutWidth / 2 : 0; 2469 2470 // If a donut chart then the label position is at the radius, if regular pie chart it's half of the radius 2471 // see https://github.com/gionkunz/chartist-js/issues/21 2472 labelRadius = options.donut ? radius : radius / 2; 2473 // Add the offset to the labelRadius where a negative offset means closed to the center of the chart 2474 labelRadius += options.labelOffset; 2475 2476 // Calculate end angle based on total sum and current data value and offset with padding 2477 var center = { 2478 x: chartRect.x1 + chartRect.width() / 2, 2479 y: chartRect.y2 + chartRect.height() / 2 2480 }; 2481 2482 // Check if there is only one non-zero value in the series array. 2483 var hasSingleValInSeries = this.data.series.filter(function(val) { 2484 return val !== 0; 2485 }).length === 1; 2486 2487 // Draw the series 2488 // initialize series groups 2489 for (var i = 0; i < this.data.series.length; i++) { 2490 seriesGroups[i] = this.svg.elem('g', null, null, true); 2491 2492 // If the series is an object and contains a name we add a custom attribute 2493 if(this.data.series[i].name) { 2494 seriesGroups[i].attr({ 2495 'series-name': this.data.series[i].name 2496 }, Chartist.xmlNs.uri); 2497 } 2498 2499 // Use series class from series data or if not set generate one 2500 seriesGroups[i].addClass([ 2501 options.classNames.series, 2502 (this.data.series[i].className || options.classNames.series + '-' + Chartist.alphaNumerate(i)) 2503 ].join(' '));
2504 2505 var endAngle = startAngle + dataArray[i] / totalDataSum * 360; 2506 // If we need to draw the arc for all 360 degrees we need to add a hack where we close the circle 2507 // with Z and use 359.99 degrees 2508 if(endAngle - startAngle === 360) { 2509 endAngle -= 0.01; 2510 } 2511 2512 var start = Chartist.polarToCartesian(center.x, center.y, radius, startAngle - (i === 0 || hasSingleValInSeries ? 0 : 0.2)), 2513 end = Chartist.polarToCartesian(center.x, center.y, radius, endAngle), 2514 arcSweep = endAngle - startAngle <= 180 ? '0' : '1', 2515 d = [ 2516 // Start at the end point from the cartesian coordinates 2517 'M', end.x, end.y, 2518 // Draw arc 2519 'A', radius, radius, 0, arcSweep, 0, start.x, start.y 2520 ]; 2521 2522 // If regular pie chart (no donut) we add a line to the center of the circle for completing the pie 2523 if(options.donut === false) { 2524 d.push('L', center.x, center.y); 2525 } 2526 2527 // Create the SVG path 2528 // If this is a donut chart we add the donut class, otherwise just a regular slice 2529 var path = seriesGroups[i].elem('path', { 2530 d: d.join(' ') 2531 }, options.classNames.slice + (options.donut ? ' ' + options.classNames.donut : '')); 2532 2533 // Adding the pie series value to the path 2534 path.attr({ 2535 'value': dataArray[i] 2536 }, Chartist.xmlNs.uri); 2537 2538 // If this is a donut, we add the stroke-width as style attribute 2539 if(options.donut === true) { 2540 path.attr({ 2541 'style': 'stroke-width: ' + (+options.donutWidth) + 'px' 2542 }); 2543 } 2544 2545 // Fire off draw event 2546 this.eventEmitter.emit('draw', { 2547 type: 'slice', 2548 value: dataArray[i], 2549 totalDataSum: totalDataSum, 2550 index: i, 2551 group: seriesGroups[i], 2552 element: path, 2553 center: center, 2554 radius: radius, 2555 startAngle: startAngle, 2556 endAngle: endAngle 2557 }); 2558 2559 // If we need to show labels we need to add the label for this slice now 2560 if(options.showLabel) { 2561 // Position at the labelRadius distance from center and between start and end angle 2562 var labelPosition = Chartist.polarToCartesian(center.x, center.y, labelRadius, startAngle + (endAngle - startAngle) / 2), 2563 interpolatedValue = options.labelInterpolationFnc(this.data.labels ? this.data.labels[i] : dataArray[i], i); 2564 2565 var labelElement = seriesGroups[i].elem('text', { 2566 dx: labelPosition.x, 2567 dy: labelPosition.y, 2568 'text-anchor': determineAnchorPosition(center, labelPosition, options.labelDirection) 2569 }, options.classNames.label).text('' + interpolatedValue); 2570 2571 // Fire off draw event 2572 this.eventEmitter.emit('draw', { 2573 type: 'label', 2574 index: i, 2575 group: seriesGroups[i], 2576 element: labelElement, 2577 text: '' + interpolatedValue, 2578 x: labelPosition.x, 2579 y: labelPosition.y 2580 }); 2581 } 2582 2583 // Set next startAngle to current endAngle. Use slight offset so there are no transparent hairline issues 2584 // (except for last slice) 2585 startAngle = endAngle; 2586 } 2587 2588 this.eventEmitter.emit('created', { 2589 chartRect: chartRect, 2590 svg: this.svg, 2591 options: options 2592 }); 2593 } 2594 2595 /** 2596 * This method creates a new pie chart and returns an object that can be used to redraw the chart. 2597 * 2598 * @memberof Chartist.Pie 2599 * @param {String|Node} query A selector query string or directly a DOM element 2600 * @param {Object} data The data object in the pie chart needs to have a series property with a one dimensional data array. The values will be normalized against each other and don't necessarily need to be in percentage. The series property can also be an array of objects that contain a data property with the value and a className property to override the CSS class name for the series group. 2601 * @param {Object} [options] The options object with options that override the default options. Check the examples for a detailed list. 2602 * @param {Array} [responsiveOptions] Specify an array of responsive option arrays which are a media query and options object pair => [[mediaQueryString, optionsObject],[more...]] 2603 * @return {Object} An object with a version and an update method to manually redraw the chart 2604 * 2605 * @example
2606 * // Simple pie chart example with four series 2607 * new Chartist.Pie('.ct-chart', { 2608 * series: [10, 2, 4, 3] 2609 * }); 2610 * 2611 * @example 2612 * // Drawing a donut chart 2613 * new Chartist.Pie('.ct-chart', { 2614 * series: [10, 2, 4, 3] 2615 * }, { 2616 * donut: true 2617 * }); 2618 * 2619 * @example 2620 * // Using donut, startAngle and total to draw a gauge chart 2621 * new Chartist.Pie('.ct-chart', { 2622 * series: [20, 10, 30, 40] 2623 * }, { 2624 * donut: true, 2625 * donutWidth: 20, 2626 * startAngle: 270, 2627 * total: 200 2628 * }); 2629 * 2630 * @example 2631 * // Drawing a pie chart with padding and labels that are outside the pie 2632 * new Chartist.Pie('.ct-chart', { 2633 * series: [20, 10, 30, 40] 2634 * }, { 2635 * chartPadding: 30, 2636 * labelOffset: 50, 2637 * labelDirection: 'explode' 2638 * }); 2639 * 2640 * @example 2641 * // Overriding the class names for individual series 2642 * new Chartist.Pie('.ct-chart', { 2643 * series: [{ 2644 * data: 20, 2645 * className: 'my-custom-class-one' 2646 * }, { 2647 * data: 10, 2648 * className: 'my-custom-class-two' 2649 * }, { 2650 * data: 70, 2651 * className: 'my-custom-class-three' 2652 * }] 2653 * }); 2654 */ 2655 function Pie(query, data, options, responsiveOptions) { 2656 Chartist.Pie.super.constructor.call(this, 2657 query, 2658 data, 2659 Chartist.extend({}, defaultOptions, options), 2660 responsiveOptions); 2661 } 2662 2663 // Creating pie chart type in Chartist namespace 2664 Chartist.Pie = Chartist.Base.extend({ 2665 constructor: Pie, 2666 createChart: createChart, 2667 determineAnchorPosition: determineAnchorPosition 2668 }); 2669 2670 }(window, document, Chartist)); 2671 2672 return Chartist; 2673 2674}));
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.