├── .eslintignore
├── config.jshintrc
├── .gitignore
├── src
├── charts
│ ├── Chart.Bar.js
│ ├── Chart.Bubble.js
│ ├── Chart.Line.js
│ ├── Chart.Doughnut.js
│ ├── Chart.PolarArea.js
│ ├── Chart.Radar.js
│ └── Chart.Scatter.js
├── core
│ ├── core.scaleService.js
│ ├── core.plugin.js
│ ├── core.datasetController.js
│ ├── core.element.js
│ ├── core.js
│ ├── core.animation.js
│ └── core.title.js
├── chart.js
├── elements
│ ├── element.arc.js
│ ├── element.rectangle.js
│ ├── element.point.js
│ └── element.line.js
└── scales
│ └── scale.category.js
├── karma.conf.js
├── bower.json
├── .codeclimate.yml
├── karma.conf.ci.js
├── .travis.yml
├── composer.json
├── karma.coverage.conf.js
├── LICENSE.md
├── test
├── core.plugin.tests.js
├── core.element.tests.js
├── mockContext.js
├── element.arc.tests.js
└── core.title.tests.js
├── package.json
├── README.md
├── docs
├── 08-Notes.md
├── 05-Polar-Area-Chart.md
├── 04-Radar-Chart.md
├── 06-Pie-Doughnut-Chart.md
└── 03-Bar-Chart.md
├── samples
├── combo-bar-line.html
├── bar-stacked.html
├── data_label_combo-bar-line.html
├── polar-area.html
├── line-multi-axis.html
├── pie.html
├── bar-multi-axis.html
├── timeScale
│ ├── line-time-point-data.html
│ ├── combo-time-scale.html
│ └── line-time-scale.html
├── scatter-logX.html
├── line-customTooltips.html
├── radar.html
├── radar-skip-points.html
├── pie-customTooltips.html
├── line-logarithmic.html
├── line-stacked-area.html
├── scatter-multi-axis.html
├── bar.html
├── line-x-axis-filter.html
├── doughnut.html
├── line-skip-points.html
└── AnimationCallbacks
│ └── progress-bar.html
├── CONTRIBUTING.md
├── .eslintrc
└── gulpfile.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*{.,-}min.js
2 |
--------------------------------------------------------------------------------
/config.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "predef": [ "require", "module" ]
4 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 |
4 | node_modules/*
5 | custom/*
6 |
7 | docs/index.md
8 |
9 | bower_components/
10 |
11 | coverage/*
12 |
13 | nbproject/*
14 |
--------------------------------------------------------------------------------
/src/charts/Chart.Bar.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | Chart.Bar = function(context, config) {
6 | config.type = 'bar';
7 |
8 | return new Chart(context, config);
9 | };
10 |
11 | };
--------------------------------------------------------------------------------
/src/charts/Chart.Bubble.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | Chart.Bubble = function(context, config) {
6 | config.type = 'bubble';
7 | return new Chart(context, config);
8 | };
9 |
10 | };
--------------------------------------------------------------------------------
/src/charts/Chart.Line.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | Chart.Line = function(context, config) {
6 | config.type = 'line';
7 |
8 | return new Chart(context, config);
9 | };
10 |
11 | };
--------------------------------------------------------------------------------
/src/charts/Chart.Doughnut.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | Chart.Doughnut = function(context, config) {
6 | config.type = 'doughnut';
7 |
8 | return new Chart(context, config);
9 | };
10 |
11 | };
--------------------------------------------------------------------------------
/src/charts/Chart.PolarArea.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | Chart.PolarArea = function(context, config) {
6 | config.type = 'polarArea';
7 |
8 | return new Chart(context, config);
9 | };
10 |
11 | };
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | browsers: ['Chrome', 'Firefox'],
4 | frameworks: ['browserify', 'jasmine'],
5 | reporters: ['progress', 'html'],
6 |
7 | preprocessors: {
8 | 'src/**/*.js': ['browserify']
9 | },
10 | browserify: {
11 | debug: true
12 | }
13 | });
14 | };
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Chart.js",
3 | "version": "2.0.2",
4 | "description": "Simple HTML5 Charts using the canvas element",
5 | "homepage": "https://github.com/nnnick/Chart.js",
6 | "author": "nnnick",
7 | "main": [
8 | "dist/Chart.js"
9 | ],
10 | "devDependencies": {
11 | "jquery": "~2.1.4"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | engines:
2 | duplication:
3 | enabled: true
4 | config:
5 | languages:
6 | - javascript
7 | eslint:
8 | enabled: true
9 | fixme:
10 | enabled: true
11 | ratings:
12 | paths:
13 | - "src/**/*.js"
14 | exclude_paths:
15 | - dist/**/*
16 | - node_modules/**/*
17 | - test/**/*
18 | - coverage/**/*
19 |
--------------------------------------------------------------------------------
/src/charts/Chart.Radar.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | var helpers = Chart.helpers;
6 |
7 | var defaultConfig = {
8 | aspectRatio: 1
9 | };
10 |
11 | Chart.Radar = function(context, config) {
12 | config.options = helpers.configMerge(defaultConfig, config.options);
13 | config.type = 'radar';
14 |
15 | return new Chart(context, config);
16 | };
17 |
18 | };
19 |
--------------------------------------------------------------------------------
/karma.conf.ci.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | var configuration = {
3 | browsers: ['Firefox'],
4 | customLaunchers: {
5 | Chrome_travis_ci: {
6 | base: 'Chrome',
7 | flags: ['--no-sandbox']
8 | }
9 | },
10 | frameworks: ['browserify', 'jasmine'],
11 | reporters: ['progress', 'html'],
12 | preprocessors: {
13 | 'src/**/*.js': ['browserify']
14 | },
15 | browserify: {
16 | debug: true
17 | }
18 | };
19 |
20 | if (process.env.TRAVIS) {
21 | configuration.browsers.push('Chrome_travis_ci');
22 | }
23 |
24 | config.set(configuration);
25 | };
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "5.6"
4 | - "4.3"
5 |
6 | before_install:
7 | - "export CHROME_BIN=/usr/bin/google-chrome"
8 | - "export DISPLAY=:99.0"
9 | - "sh -e /etc/init.d/xvfb start"
10 |
11 | before_script:
12 | - npm install
13 |
14 | script:
15 | - gulp test
16 | - gulp coverage
17 | - cat ./coverage/lcov.info | ./node_modules/.bin/coveralls
18 |
19 | notifications:
20 | slack: chartjs:pcfCZR6ugg5TEcaLtmIfQYuA
21 |
22 | sudo: required
23 | dist: trusty
24 |
25 | addons:
26 | firefox: latest
27 | apt:
28 | sources:
29 | - google-chrome
30 | packages:
31 | - google-chrome-stable
32 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nnnick/chartjs",
3 | "type": "library",
4 | "description": "Simple HTML5 charts using the canvas element.",
5 | "keywords": [
6 | "chart",
7 | "js"
8 | ],
9 | "homepage": "http://www.chartjs.org/",
10 | "license": "MIT",
11 | "authors": [
12 | {
13 | "name": "NICK DOWNIE",
14 | "email": "hello@nickdownie.com"
15 | }
16 | ],
17 | "require": {
18 | "php": ">=5.3.3"
19 | },
20 | "minimum-stability": "stable",
21 | "extra": {
22 | "branch-alias": {
23 | "release/2.0": "v2.0-dev"
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/karma.coverage.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | var configuration = {
3 | browsers: ['Firefox'],
4 |
5 | frameworks: ['browserify', 'jasmine'],
6 |
7 | preprocessors: {
8 | 'src/**/*.js': ['browserify']
9 | },
10 | browserify: {
11 | debug: true,
12 | transform: [['browserify-istanbul', {
13 | instrumenterConfig: {
14 | embed: true
15 | }
16 | }]]
17 | },
18 |
19 | reporters: ['progress', 'coverage'],
20 | coverageReporter: {
21 | dir: 'coverage/',
22 | reporters: [
23 | { type: 'html', subdir: 'report-html' },
24 | { type: 'lcovonly', subdir: '.', file: 'lcov.info' }
25 | ]
26 | }
27 | };
28 |
29 | // If on the CI, use the CI chrome launcher
30 | if (process.env.TRAVIS) {
31 | configuration.browsers.push('Chrome_travis_ci');
32 | configuration.customLaunchers = {
33 | Chrome_travis_ci: {
34 | base: 'Chrome',
35 | flags: ['--no-sandbox']
36 | }
37 | };
38 | } else {
39 | configuration.browsers.push('Chrome');
40 | }
41 |
42 | config.set(configuration);
43 | };
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2013-2016 Nick Downie
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 |
--------------------------------------------------------------------------------
/src/charts/Chart.Scatter.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | var defaultConfig = {
6 | hover: {
7 | mode: 'single'
8 | },
9 |
10 | scales: {
11 | xAxes: [{
12 | type: "linear", // scatter should not use a category axis
13 | position: "bottom",
14 | id: "x-axis-1" // need an ID so datasets can reference the scale
15 | }],
16 | yAxes: [{
17 | type: "linear",
18 | position: "left",
19 | id: "y-axis-1"
20 | }]
21 | },
22 |
23 | tooltips: {
24 | callbacks: {
25 | title: function(tooltipItems, data) {
26 | // Title doesn't make sense for scatter since we format the data as a point
27 | return '';
28 | },
29 | label: function(tooltipItem, data) {
30 | return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')';
31 | }
32 | }
33 | }
34 | };
35 |
36 | // Register the default config for this type
37 | Chart.defaults.scatter = defaultConfig;
38 |
39 | // Scatter charts use line controllers
40 | Chart.controllers.scatter = Chart.controllers.line;
41 |
42 | Chart.Scatter = function(context, config) {
43 | config.type = 'scatter';
44 | return new Chart(context, config);
45 | };
46 |
47 | };
--------------------------------------------------------------------------------
/test/core.plugin.tests.js:
--------------------------------------------------------------------------------
1 | // Plugin tests
2 | describe('Test the plugin system', function() {
3 | beforeEach(function() {
4 | Chart.plugins = [];
5 | });
6 |
7 | it ('Should register plugins', function() {
8 | var myplugin = {};
9 | Chart.pluginService.register(myplugin);
10 | expect(Chart.plugins.length).toBe(1);
11 |
12 | // Should only add plugin once
13 | Chart.pluginService.register(myplugin);
14 | expect(Chart.plugins.length).toBe(1);
15 | });
16 |
17 | it ('Should allow unregistering plugins', function() {
18 | var myplugin = {};
19 | Chart.pluginService.register(myplugin);
20 | expect(Chart.plugins.length).toBe(1);
21 |
22 | // Should only add plugin once
23 | Chart.pluginService.remove(myplugin);
24 | expect(Chart.plugins.length).toBe(0);
25 |
26 | // Removing a plugin that doesn't exist should not error
27 | Chart.pluginService.remove(myplugin);
28 | });
29 |
30 | it ('Should allow notifying plugins', function() {
31 | var myplugin = {
32 | count: 0,
33 | trigger: function(chart) {
34 | myplugin.count = chart.count;
35 | }
36 | };
37 | Chart.pluginService.register(myplugin);
38 |
39 | Chart.pluginService.notifyPlugins('trigger', [{ count: 10 }]);
40 |
41 | expect(myplugin.count).toBe(10);
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/src/core/core.scaleService.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | var helpers = Chart.helpers;
6 |
7 | Chart.scaleService = {
8 | // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
9 | // use the new chart options to grab the correct scale
10 | constructors: {},
11 | // Use a registration function so that we can move to an ES6 map when we no longer need to support
12 | // old browsers
13 |
14 | // Scale config defaults
15 | defaults: {},
16 | registerScaleType: function(type, scaleConstructor, defaults) {
17 | this.constructors[type] = scaleConstructor;
18 | this.defaults[type] = helpers.clone(defaults);
19 | },
20 | getScaleConstructor: function(type) {
21 | return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
22 | },
23 | getScaleDefaults: function(type) {
24 | // Return the scale defaults merged with the global settings so that we always use the latest ones
25 | return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {};
26 | },
27 | addScalesToLayout: function(chartInstance) {
28 | // Adds each scale to the chart.boxes array to be sized accordingly
29 | helpers.each(chartInstance.scales, function(scale) {
30 | Chart.layoutService.addBox(chartInstance, scale);
31 | });
32 | }
33 | };
34 | };
--------------------------------------------------------------------------------
/test/core.element.tests.js:
--------------------------------------------------------------------------------
1 | // Test the core element functionality
2 | describe('Core element tests', function() {
3 | it ('should transition model properties', function() {
4 | var element = new Chart.Element({
5 | _model: {
6 | numberProp: 0,
7 | numberProp2: 100,
8 | _underscoreProp: 0,
9 | stringProp: 'abc',
10 | objectProp: {
11 | myObject: true
12 | },
13 | colorProp: 'rgb(0, 0, 0)'
14 | }
15 | });
16 |
17 | // First transition clones model into view
18 | element.transition(0.25);
19 | expect(element._view).toEqual(element._model);
20 | expect(element._start).toEqual(element._model); // also cloned
21 |
22 | expect(element._view.objectProp).toBe(element._model.objectProp); // not cloned
23 | expect(element._start.objectProp).toEqual(element._model.objectProp); // not cloned
24 |
25 | element._model.numberProp = 100;
26 | element._model.numberProp2 = 250;
27 | element._model._underscoreProp = 200;
28 | element._model.stringProp = 'def'
29 | element._model.newStringProp = 'newString';
30 | element._model.colorProp = 'rgb(255, 255, 0)'
31 |
32 | element.transition(0.25);
33 | expect(element._view).toEqual({
34 | numberProp: 25,
35 | numberProp2: 137.5,
36 | _underscoreProp: 0, // underscore props are not transition to a new value
37 | stringProp: 'def',
38 | newStringProp: 'newString',
39 | objectProp: {
40 | myObject: true
41 | },
42 | colorProp: 'rgb(64, 64, 0)',
43 | });
44 | });
45 | });
--------------------------------------------------------------------------------
/src/core/core.plugin.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 | var helpers = Chart.helpers;
5 |
6 | // Plugins are stored here
7 | Chart.plugins = [];
8 | Chart.pluginService = {
9 | // Register a new plugin
10 | register: function(plugin) {
11 | if (Chart.plugins.indexOf(plugin) === -1) {
12 | Chart.plugins.push(plugin);
13 | }
14 | },
15 |
16 | // Remove a registered plugin
17 | remove: function(plugin) {
18 | var idx = Chart.plugins.indexOf(plugin);
19 | if (idx !== -1) {
20 | Chart.plugins.splice(idx, 1);
21 | }
22 | },
23 |
24 | // Iterate over all plugins
25 | notifyPlugins: function(method, args, scope) {
26 | helpers.each(Chart.plugins, function(plugin) {
27 | if (plugin[method] && typeof plugin[method] === 'function') {
28 | plugin[method].apply(scope, args);
29 | }
30 | }, scope);
31 | }
32 | };
33 |
34 | Chart.PluginBase = Chart.Element.extend({
35 | // Plugin methods. All functions are passed the chart instance
36 |
37 | // Called at start of chart init
38 | beforeInit: helpers.noop,
39 |
40 | // Called at end of chart init
41 | afterInit: helpers.noop,
42 |
43 | // Called at start of update
44 | beforeUpdate: helpers.noop,
45 |
46 | // Called at end of update
47 | afterUpdate: helpers.noop,
48 |
49 | // Called at start of draw
50 | beforeDraw: helpers.noop,
51 |
52 | // Called at end of draw
53 | afterDraw: helpers.noop,
54 |
55 | // Called during destroy
56 | destory: helpers.noop,
57 | });
58 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chart.js",
3 | "homepage": "http://www.chartjs.org",
4 | "description": "Simple HTML5 charts using the canvas element.",
5 | "version": "2.0.2",
6 | "main": "src/chart.js",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/nnnick/Chart.js.git"
10 | },
11 | "devDependencies": {
12 | "browserify": "^13.0.0",
13 | "browserify-istanbul": "^0.2.1",
14 | "coveralls": "^2.11.6",
15 | "gulp": "3.9.x",
16 | "gulp-concat": "~2.1.x",
17 | "gulp-connect": "~2.0.5",
18 | "gulp-html-validator": "^0.0.2",
19 | "gulp-jshint": "~1.5.1",
20 | "gulp-karma": "0.0.4",
21 | "gulp-replace": "^0.4.0",
22 | "gulp-size": "~0.4.0",
23 | "gulp-streamify": "^1.0.2",
24 | "gulp-uglify": "~0.2.x",
25 | "gulp-umd": "~0.2.0",
26 | "gulp-util": "~2.2.x",
27 | "inquirer": "^0.5.1",
28 | "jasmine": "^2.3.2",
29 | "jasmine-core": "^2.3.4",
30 | "jquery": "^2.1.4",
31 | "jshint-stylish": "~2.1.0",
32 | "karma": "^0.12.37",
33 | "karma-browserify": "^5.0.1",
34 | "karma-chrome-launcher": "^0.2.0",
35 | "karma-coverage": "^0.5.1",
36 | "karma-firefox-launcher": "^0.1.6",
37 | "karma-jasmine": "^0.3.6",
38 | "karma-jasmine-html-reporter": "^0.1.8",
39 | "merge-stream": "^1.0.0",
40 | "semver": "^3.0.1",
41 | "vinyl-source-stream": "^1.1.0",
42 | "watchify": "^3.7.0"
43 | },
44 | "spm": {
45 | "main": "Chart.js"
46 | },
47 | "dependencies": {
48 | "chartjs-color": "^1.0.2",
49 | "moment": "^2.10.6"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chart.js
2 |
3 | [](https://travis-ci.org/nnnick/Chart.js) [](https://codeclimate.com/github/nnnick/Chart.js)[](https://coveralls.io/r/nnnick/Chart.js?branch=master)
4 |
5 | [](https://chartjs-slack-automation.herokuapp.com/)
6 |
7 | *Simple HTML5 Charts using the canvas element* [chartjs.org](http://www.chartjs.org)
8 |
9 | ## Installation
10 |
11 | To download a zip, go to the Chart.js on Github
12 |
13 | To install via npm / bower:
14 |
15 | ```bash
16 | npm install chart.js --save
17 | bower install Chart.js --save
18 | ```
19 | CDN: https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0/Chart.js
20 |
21 | ## Documentation
22 |
23 | You can find documentation at [nnnick.github.io/Chart.js/docs-v2/](http://nnnick.github.io/Chart.js/docs-v2/). The markdown files that build the site are available under `/docs`. Please note - in some of the json examples of configuration you might notice some liquid tags - this is just for the generating the site html, please disregard.
24 |
25 | ## Contributing
26 |
27 | Before submitting an issue or a pull request to the project, please take a moment to look over the [contributing guidelines](https://github.com/nnnick/Chart.js/blob/master/CONTRIBUTING.md) first.
28 |
29 | For support using Chart.js, please post questions with the [`chartjs` tag on Stack Overflow](http://stackoverflow.com/questions/tagged/chartjs).
30 |
31 | ## Building and Testing
32 | `gulp build`, `gulp test`
33 |
34 | ## License
35 |
36 | Chart.js is available under the [MIT license](http://opensource.org/licenses/MIT).
37 |
--------------------------------------------------------------------------------
/src/chart.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Chart.js
3 | * http://chartjs.org/
4 | * Version: {{ version }}
5 | *
6 | * Copyright 2015 Nick Downie
7 | * Released under the MIT license
8 | * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
9 | */
10 |
11 |
12 | var Chart = require('./core/core.js')();
13 |
14 | require('./core/core.helpers')(Chart);
15 | require('./core/core.element')(Chart);
16 | require('./core/core.animation')(Chart);
17 | require('./core/core.controller')(Chart);
18 | require('./core/core.datasetController')(Chart);
19 | require('./core/core.layoutService')(Chart);
20 | require('./core/core.legend')(Chart);
21 | require('./core/core.plugin.js')(Chart);
22 | require('./core/core.scale')(Chart);
23 | require('./core/core.scaleService')(Chart);
24 | require('./core/core.title')(Chart);
25 | require('./core/core.tooltip')(Chart);
26 |
27 | require('./controllers/controller.bar')(Chart);
28 | require('./controllers/controller.bubble')(Chart);
29 | require('./controllers/controller.doughnut')(Chart);
30 | require('./controllers/controller.line')(Chart);
31 | require('./controllers/controller.polarArea')(Chart);
32 | require('./controllers/controller.radar')(Chart);
33 |
34 | require('./scales/scale.category')(Chart);
35 | require('./scales/scale.linear')(Chart);
36 | require('./scales/scale.logarithmic')(Chart);
37 | require('./scales/scale.radialLinear')(Chart);
38 | require('./scales/scale.time')(Chart);
39 |
40 | require('./elements/element.arc')(Chart);
41 | require('./elements/element.line')(Chart);
42 | require('./elements/element.point')(Chart);
43 | require('./elements/element.rectangle')(Chart);
44 |
45 | require('./charts/Chart.Bar')(Chart);
46 | require('./charts/Chart.Bubble')(Chart);
47 | require('./charts/Chart.Doughnut')(Chart);
48 | require('./charts/Chart.Line')(Chart);
49 | require('./charts/Chart.PolarArea')(Chart);
50 | require('./charts/Chart.Radar')(Chart);
51 | require('./charts/Chart.Scatter')(Chart);
52 |
53 | window.Chart = module.exports = Chart;
54 |
--------------------------------------------------------------------------------
/src/core/core.datasetController.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | var helpers = Chart.helpers;
6 |
7 | // Base class for all dataset controllers (line, bar, etc)
8 | Chart.DatasetController = function(chart, datasetIndex) {
9 | this.initialize.call(this, chart, datasetIndex);
10 | };
11 |
12 | helpers.extend(Chart.DatasetController.prototype, {
13 | initialize: function(chart, datasetIndex) {
14 | this.chart = chart;
15 | this.index = datasetIndex;
16 | this.linkScales();
17 | this.addElements();
18 | },
19 | updateIndex: function(datasetIndex) {
20 | this.index = datasetIndex;
21 | },
22 |
23 | linkScales: function() {
24 | if (!this.getDataset().xAxisID) {
25 | this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
26 | }
27 |
28 | if (!this.getDataset().yAxisID) {
29 | this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
30 | }
31 | },
32 |
33 | getDataset: function() {
34 | return this.chart.data.datasets[this.index];
35 | },
36 |
37 | getScaleForId: function(scaleID) {
38 | return this.chart.scales[scaleID];
39 | },
40 |
41 | reset: function() {
42 | this.update(true);
43 | },
44 |
45 | buildOrUpdateElements: function buildOrUpdateElements() {
46 | // Handle the number of data points changing
47 | var numData = this.getDataset().data.length;
48 | var numMetaData = this.getDataset().metaData.length;
49 |
50 | // Make sure that we handle number of datapoints changing
51 | if (numData < numMetaData) {
52 | // Remove excess bars for data points that have been removed
53 | this.getDataset().metaData.splice(numData, numMetaData - numData);
54 | } else if (numData > numMetaData) {
55 | // Add new elements
56 | for (var index = numMetaData; index < numData; ++index) {
57 | this.addElementAndReset(index);
58 | }
59 | }
60 | },
61 |
62 | // Controllers should implement the following
63 | addElements: helpers.noop,
64 | addElementAndReset: helpers.noop,
65 | draw: helpers.noop,
66 | removeHoverStyle: helpers.noop,
67 | setHoverStyle: helpers.noop,
68 | update: helpers.noop
69 | });
70 |
71 | Chart.DatasetController.extend = helpers.inherits;
72 |
73 | };
--------------------------------------------------------------------------------
/src/core/core.element.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | var helpers = Chart.helpers;
6 |
7 | Chart.elements = {};
8 |
9 | Chart.Element = function(configuration) {
10 | helpers.extend(this, configuration);
11 | this.initialize.apply(this, arguments);
12 | };
13 | helpers.extend(Chart.Element.prototype, {
14 | initialize: function() {},
15 | pivot: function() {
16 | if (!this._view) {
17 | this._view = helpers.clone(this._model);
18 | }
19 | this._start = helpers.clone(this._view);
20 | return this;
21 | },
22 | transition: function(ease) {
23 | if (!this._view) {
24 | this._view = helpers.clone(this._model);
25 | }
26 |
27 | // No animation -> No Transition
28 | if (ease === 1) {
29 | this._view = this._model;
30 | this._start = null;
31 | return this;
32 | }
33 |
34 | if (!this._start) {
35 | this.pivot();
36 | }
37 |
38 | helpers.each(this._model, function(value, key) {
39 |
40 | if (key[0] === '_' || !this._model.hasOwnProperty(key)) {
41 | // Only non-underscored properties
42 | }
43 |
44 | // Init if doesn't exist
45 | else if (!this._view.hasOwnProperty(key)) {
46 | if (typeof value === 'number' && !isNaN(this._view[key])) {
47 | this._view[key] = value * ease;
48 | } else {
49 | this._view[key] = value;
50 | }
51 | }
52 |
53 | // No unnecessary computations
54 | else if (value === this._view[key]) {
55 | // It's the same! Woohoo!
56 | }
57 |
58 | // Color transitions if possible
59 | else if (typeof value === 'string') {
60 | try {
61 | var color = helpers.color(this._start[key]).mix(helpers.color(this._model[key]), ease);
62 | this._view[key] = color.rgbString();
63 | } catch (err) {
64 | this._view[key] = value;
65 | }
66 | }
67 | // Number transitions
68 | else if (typeof value === 'number') {
69 | var startVal = this._start[key] !== undefined && isNaN(this._start[key]) === false ? this._start[key] : 0;
70 | this._view[key] = ((this._model[key] - startVal) * ease) + startVal;
71 | }
72 | // Everything else
73 | else {
74 | this._view[key] = value;
75 | }
76 | }, this);
77 |
78 | return this;
79 | },
80 | tooltipPosition: function() {
81 | return {
82 | x: this._model.x,
83 | y: this._model.y
84 | };
85 | },
86 | hasValue: function() {
87 | return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
88 | }
89 | });
90 |
91 | Chart.Element.extend = helpers.inherits;
92 |
93 | };
94 |
--------------------------------------------------------------------------------
/src/elements/element.arc.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart, moment) {
4 |
5 | var helpers = Chart.helpers;
6 |
7 | Chart.defaults.global.elements.arc = {
8 | backgroundColor: Chart.defaults.global.defaultColor,
9 | borderColor: "#fff",
10 | borderWidth: 2
11 | };
12 |
13 | Chart.elements.Arc = Chart.Element.extend({
14 | inLabelRange: function(mouseX) {
15 | var vm = this._view;
16 |
17 | if (vm) {
18 | return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
19 | } else {
20 | return false;
21 | }
22 | },
23 | inRange: function(chartX, chartY) {
24 |
25 | var vm = this._view;
26 |
27 | if (vm) {
28 | var pointRelativePosition = helpers.getAngleFromPoint(vm, {
29 | x: chartX,
30 | y: chartY
31 | });
32 |
33 | //Sanitise angle range
34 | var startAngle = vm.startAngle;
35 | var endAngle = vm.endAngle;
36 | while (endAngle < startAngle) {
37 | endAngle += 2.0 * Math.PI;
38 | }
39 | while (pointRelativePosition.angle > endAngle) {
40 | pointRelativePosition.angle -= 2.0 * Math.PI;
41 | }
42 | while (pointRelativePosition.angle < startAngle) {
43 | pointRelativePosition.angle += 2.0 * Math.PI;
44 | }
45 |
46 | //Check if within the range of the open/close angle
47 | var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle),
48 | withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius);
49 |
50 | return (betweenAngles && withinRadius);
51 | } else {
52 | return false;
53 | }
54 | },
55 | tooltipPosition: function() {
56 | var vm = this._view;
57 |
58 | var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
59 | rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
60 | return {
61 | x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
62 | y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
63 | };
64 | },
65 | draw: function() {
66 |
67 | var ctx = this._chart.ctx;
68 | var vm = this._view;
69 |
70 | ctx.beginPath();
71 |
72 | ctx.arc(vm.x, vm.y, vm.outerRadius, vm.startAngle, vm.endAngle);
73 |
74 | ctx.arc(vm.x, vm.y, vm.innerRadius, vm.endAngle, vm.startAngle, true);
75 |
76 | ctx.closePath();
77 | ctx.strokeStyle = vm.borderColor;
78 | ctx.lineWidth = vm.borderWidth;
79 |
80 | ctx.fillStyle = vm.backgroundColor;
81 |
82 | ctx.fill();
83 | ctx.lineJoin = 'bevel';
84 |
85 | if (vm.borderWidth) {
86 | ctx.stroke();
87 | }
88 | }
89 | });
90 | };
91 |
--------------------------------------------------------------------------------
/docs/08-Notes.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Notes
3 | anchor: notes
4 | ---
5 |
6 | ### Browser support
7 | Browser support for the canvas element is available in all modern & major mobile browsers (caniuse.com/canvas).
8 |
9 | For IE8 & below, I would recommend using the polyfill ExplorerCanvas - available at https://code.google.com/p/explorercanvas/. It falls back to Internet explorer's format VML when canvas support is not available. Example use:
10 |
11 | ```html
12 |
13 |
16 |
17 | ```
18 |
19 | Usually I would recommend feature detection to choose whether or not to load a polyfill, rather than IE conditional comments, however in this case, VML is a Microsoft proprietary format, so it will only work in IE.
20 |
21 | Some important points to note in my experience using ExplorerCanvas as a fallback.
22 |
23 | - Initialise charts on load rather than DOMContentReady when using the library, as sometimes a race condition will occur, and it will result in an error when trying to get the 2d context of a canvas.
24 | - New VML DOM elements are being created for each animation frame and there is no hardware acceleration. As a result animation is usually slow and jerky, with flashing text. It is a good idea to dynamically turn off animation based on canvas support. I recommend using the excellent Modernizr to do this.
25 | - When declaring fonts, the library explorercanvas requires the font name to be in single quotes inside the string. For example, instead of your scaleFontFamily property being simply "Arial", explorercanvas support, use "'Arial'" instead. Chart.js does this for default values.
26 |
27 | ### Bugs & issues
28 |
29 | Please report these on the GitHub page - at github.com/nnnick/Chart.js. If you could include a link to a simple jsbin or similar to demonstrate the issue, that'd be really helpful.
30 |
31 |
32 | ### Contributing
33 | New contributions to the library are welcome, just a couple of guidelines:
34 |
35 | - Tabs for indentation, not spaces please.
36 | - Please ensure you're changing the individual files in `/src`, not the concatenated output in the `Chart.js` file in the root of the repo.
37 | - Please check that your code will pass `jshint` code standards, `gulp jshint` will run this for you.
38 | - Please keep pull requests concise, and document new functionality in the relevant `.md` file.
39 | - Consider whether your changes are useful for all users, or if creating a Chart.js extension would be more appropriate.
40 |
41 | ### License
42 | Chart.js is open source and available under the MIT license.
--------------------------------------------------------------------------------
/src/scales/scale.category.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | var helpers = Chart.helpers;
6 | // Default config for a category scale
7 | var defaultConfig = {
8 | position: "bottom"
9 | };
10 |
11 | var DatasetScale = Chart.Scale.extend({
12 | // Implement this so that
13 | determineDataLimits: function() {
14 | this.minIndex = 0;
15 | this.maxIndex = this.chart.data.labels.length;
16 | var findIndex;
17 |
18 | if (this.options.ticks.min !== undefined) {
19 | // user specified min value
20 | findIndex = helpers.indexOf(this.chart.data.labels, this.options.ticks.min);
21 | this.minIndex = findIndex !== -1 ? findIndex : this.minIndex;
22 | }
23 |
24 | if (this.options.ticks.max !== undefined) {
25 | // user specified max value
26 | findIndex = helpers.indexOf(this.chart.data.labels, this.options.ticks.max);
27 | this.maxIndex = findIndex !== -1 ? findIndex : this.maxIndex;
28 | }
29 |
30 | this.min = this.chart.data.labels[this.minIndex];
31 | this.max = this.chart.data.labels[this.maxIndex];
32 | },
33 |
34 | buildTicks: function(index) {
35 | // If we are viewing some subset of labels, slice the original array
36 | this.ticks = (this.minIndex === 0 && this.maxIndex === this.chart.data.labels.length) ? this.chart.data.labels : this.chart.data.labels.slice(this.minIndex, this.maxIndex + 1);
37 | },
38 |
39 | getLabelForIndex: function(index, datasetIndex) {
40 | return this.ticks[index];
41 | },
42 |
43 | // Used to get data value locations. Value can either be an index or a numerical value
44 | getPixelForValue: function(value, index, datasetIndex, includeOffset) {
45 | // 1 is added because we need the length but we have the indexes
46 | var offsetAmt = Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
47 |
48 | if (this.isHorizontal()) {
49 | var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
50 | var valueWidth = innerWidth / offsetAmt;
51 | var widthOffset = (valueWidth * (index - this.minIndex)) + this.paddingLeft;
52 |
53 | if (this.options.gridLines.offsetGridLines && includeOffset) {
54 | widthOffset += (valueWidth / 2);
55 | }
56 |
57 | return this.left + Math.round(widthOffset);
58 | } else {
59 | var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
60 | var valueHeight = innerHeight / offsetAmt;
61 | var heightOffset = (valueHeight * (index - this.minIndex)) + this.paddingTop;
62 |
63 | if (this.options.gridLines.offsetGridLines && includeOffset) {
64 | heightOffset += (valueHeight / 2);
65 | }
66 |
67 | return this.top + Math.round(heightOffset);
68 | }
69 | },
70 | getPixelForTick: function(index, includeOffset) {
71 | return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
72 | }
73 | });
74 |
75 | Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
76 |
77 | };
--------------------------------------------------------------------------------
/src/elements/element.rectangle.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(Chart) {
4 |
5 | var helpers = Chart.helpers;
6 |
7 | Chart.defaults.global.elements.rectangle = {
8 | backgroundColor: Chart.defaults.global.defaultColor,
9 | borderWidth: 0,
10 | borderColor: Chart.defaults.global.defaultColor,
11 | borderSkipped: 'bottom'
12 | };
13 |
14 | Chart.elements.Rectangle = Chart.Element.extend({
15 | draw: function() {
16 |
17 | var ctx = this._chart.ctx;
18 | var vm = this._view;
19 |
20 | var halfWidth = vm.width / 2,
21 | leftX = vm.x - halfWidth,
22 | rightX = vm.x + halfWidth,
23 | top = vm.base - (vm.base - vm.y),
24 | halfStroke = vm.borderWidth / 2;
25 |
26 | // Canvas doesn't allow us to stroke inside the width so we can
27 | // adjust the sizes to fit if we're setting a stroke on the line
28 | if (vm.borderWidth) {
29 | leftX += halfStroke;
30 | rightX -= halfStroke;
31 | top += halfStroke;
32 | }
33 |
34 | ctx.beginPath();
35 |
36 | ctx.fillStyle = vm.backgroundColor;
37 | ctx.strokeStyle = vm.borderColor;
38 | ctx.lineWidth = vm.borderWidth;
39 |
40 | // Corner points, from bottom-left to bottom-right clockwise
41 | // | 1 2 |
42 | // | 0 3 |
43 | var corners = [
44 | [leftX, vm.base],
45 | [leftX, top],
46 | [rightX, top],
47 | [rightX, vm.base]
48 | ];
49 |
50 | // Find first (starting) corner with fallback to 'bottom'
51 | var borders = ['bottom', 'left', 'top', 'right'];
52 | var startCorner = borders.indexOf(vm.borderSkipped, 0);
53 | if (startCorner === -1)
54 | startCorner = 0;
55 |
56 | function cornerAt(index) {
57 | return corners[(startCorner + index) % 4];
58 | }
59 |
60 | // Draw rectangle from 'startCorner'
61 | ctx.moveTo.apply(ctx, cornerAt(0));
62 | for (var i = 1; i < 4; i++)
63 | ctx.lineTo.apply(ctx, cornerAt(i));
64 |
65 | ctx.fill();
66 | if (vm.borderWidth) {
67 | ctx.stroke();
68 | }
69 | },
70 | height: function() {
71 | var vm = this._view;
72 | return vm.base - vm.y;
73 | },
74 | inRange: function(mouseX, mouseY) {
75 | var vm = this._view;
76 | var inRange = false;
77 |
78 | if (vm) {
79 | if (vm.y < vm.base) {
80 | inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
81 | } else {
82 | inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y);
83 | }
84 | }
85 |
86 | return inRange;
87 | },
88 | inLabelRange: function(mouseX) {
89 | var vm = this._view;
90 |
91 | if (vm) {
92 | return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
93 | } else {
94 | return false;
95 | }
96 | },
97 | tooltipPosition: function() {
98 | var vm = this._view;
99 | return {
100 | x: vm.x,
101 | y: vm.y
102 | };
103 | }
104 | });
105 |
106 | };
--------------------------------------------------------------------------------
/samples/combo-bar-line.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Combo Bar-Line Chart
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/05-Polar-Area-Chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Polar Area Chart
3 | anchor: polar-area-chart
4 | ---
5 | ### Introduction
6 | Polar area charts are similar to pie charts, but each segment has the same angle - the radius of the segment differs depending on the value.
7 |
8 | This type of chart is often useful when we want to show a comparison data similar to a pie chart, but also show a scale of values for context.
9 |
10 |
11 |
12 |
13 |
14 | ### Example usage
15 |
16 | ```javascript
17 | new Chart(ctx, {
18 | data: data,
19 | type: 'polarArea',
20 | options: options
21 | });
22 | ```
23 |
24 | ### Data structure
25 |
26 | ```javascript
27 | var data = {
28 | datasets: [{
29 | data: [
30 | 10,
31 | 32,
32 | 53,
33 | 14,
34 | 22,
35 | ],
36 | backgroundColor: [
37 | "#F7464A",
38 | "#46BFBD",
39 | "#FDB45C",
40 | "#949FB1",
41 | "#4D5360",
42 | ],
43 | label: 'My dataset' // for legend
44 | }],
45 | labels: [
46 | "Red",
47 | "Green",
48 | "Yellow",
49 | "Grey",
50 | "Dark Grey"
51 | ]
52 | };
53 | ```
54 | As you can see, for the chart data you pass in an array of objects, with a value and a colour. The value attribute should be a number, while the color attribute should be a string. Similar to CSS, for this string you can use HEX notation, RGB, RGBA or HSL.
55 |
56 | ### Chart options
57 |
58 | These are the customisation options specific to Polar Area charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart.
59 |
60 | Name | Type | Default | Description
61 | --- | --- | --- | ---
62 | scale | Array | [See Scales](#scales) and [Defaults for Radial Linear Scale](#getting-started-radial-linear-scale) | Options for the one scale used on the chart. Use this to style the ticks, labels, and grid.
63 | *scale*.type | String |"radialLinear" | As defined in ["Radial Linear"](#scales-radial-linear-scale).
64 | *scale*.lineArc | Boolean | true | When true, lines are circular.
65 | animateRotate | Boolean |true | If true, will animate the rotation of the chart.
66 | animateScale | Boolean | true | If true, will animate scaling the chart.
67 | *legend*.*labels*.generateLabels | Function | `function(data) {} ` | Returns labels for each the legend
68 | *legend*.onClick | Function | function(event, legendItem) {} ` | Handles clicking an individual legend item
69 | legendCallback | Function | `function(chart) ` | Generates the HTML legend via calls to `generateLegend`
70 |
71 | You can override these for your `Chart` instance by passing a second argument into the `PolarArea` method as an object with the keys you want to override.
72 |
73 | For example, we could have a polar area chart with a black stroke on each segment like so:
74 |
75 | ```javascript
76 | new Chart(ctx, {
77 | data: data,
78 | type: "polarArea",
79 | options: {
80 | elements: {
81 | arc: {
82 | borderColor: "#000000"
83 | }
84 | }
85 | }
86 | });
87 | // This will create a chart with all of the default options, merged from the global config,
88 | // and the PolarArea chart defaults but this particular instance will have `elements.arc.borderColor` set to `"#000000"`.
89 | ```
90 |
91 | We can also change these defaults values for each PolarArea type that is created, this object is available at `Chart.defaults.polarArea`.
92 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Contributing to Chart.js
2 | ========================
3 |
4 | Contributions to Chart.js are welcome and encouraged, but please have a look through the guidelines in this document before raising an issue, or writing code for the project.
5 |
6 |
7 | Using issues
8 | ------------
9 |
10 | The [issue tracker](https://github.com/nnnick/Chart.js/issues) is the preferred channel for reporting bugs, requesting new features and submitting pull requests.
11 |
12 | If you're suggesting a new chart type, please take a look at [writing new chart types](https://github.com/nnnick/Chart.js/blob/master/docs/06-Advanced.md#writing-new-chart-types) in the documentation, and some of the [community extensions](https://github.com/nnnick/Chart.js/blob/master/docs/06-Advanced.md#community-extensions) that have been created already.
13 |
14 | To keep the library lightweight for everyone, it's unlikely we'll add many more chart types to the core of Chart.js, but issues are a good medium to design and spec out how new chart types could work and look.
15 |
16 | Please do not use issues for support requests. For help using Chart.js, please take a look at the [`chartjs`](http://stackoverflow.com/questions/tagged/chartjs) tag on Stack Overflow.
17 |
18 |
19 | Reporting bugs
20 | --------------
21 |
22 | Well structured, detailed bug reports are hugely valuable for the project.
23 |
24 | Guidlines for reporting bugs:
25 |
26 | - Check the issue search to see if it has already been reported
27 | - Isolate the problem to a simple test case
28 | - Provide a demonstration of the problem on [JS Bin](http://jsbin.com) or similar
29 |
30 | Please provide any additional details associated with the bug, if it's browser or screen density specific, or only happens with a certain configuration or data.
31 |
32 |
33 | Local development
34 | -----------------
35 |
36 | Run `npm install` to install all the libraries, then run `gulp dev --test` to build and run tests as you make changes.
37 |
38 |
39 | Pull requests
40 | -------------
41 |
42 | Clear, concise pull requests are excellent at continuing the project's community driven growth. But please review [these guidelines](https://github.com/blog/1943-how-to-write-the-perfect-pull-request) and the guidelines below before starting work on the project.
43 |
44 | Be advised that **Chart.js 1.0.2 is in feature-complete status**. Pull requests adding new features to the 1.x branch will be disregarded.
45 |
46 | Guidlines:
47 |
48 | - Please create an issue first:
49 | - For bugs, we can discuss the fixing approach
50 | - For enhancements, we can discuss if it is within the project scope and avoid duplicate effort
51 | - Please make changes to the files in [`/src`](https://github.com/nnnick/Chart.js/tree/master/src), not `Chart.js` or `Chart.min.js` in the repo root directory, this avoids merge conflicts
52 | - Tabs for indentation, not spaces please
53 | - If adding new functionality, please also update the relevant `.md` file in [`/docs`](https://github.com/nnnick/Chart.js/tree/master/docs)
54 | - Please make your commits in logical sections with clear commit messages
55 |
56 | Joining the project
57 | -------------
58 | - Active committers and contributors are invited to introduce yourself and request commit access to this project. Please send an email to hello@nickdownie.com or file an issue.
59 |
60 | License
61 | -------
62 |
63 | By contributing your code, you agree to license your contribution under the [MIT license](https://github.com/nnnick/Chart.js/blob/master/LICENSE.md).
64 |
--------------------------------------------------------------------------------
/samples/bar-stacked.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Stacked Bar Chart
6 |
7 |
8 |
15 |
16 |
17 |
18 |
14 |
15 | ### Example usage
16 |
17 | ```javascript
18 | var myRadarChart = new Chart(ctx, {
19 | type: 'radar',
20 | data: data,
21 | options: options
22 | });
23 | ```
24 |
25 | ### Data structure
26 | ```javascript
27 | var data = {
28 | labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"],
29 | datasets: [
30 | {
31 | label: "My First dataset",
32 | backgroundColor: "rgba(220,220,220,0.2)",
33 | borderColor: "rgba(220,220,220,1)",
34 | pointBackgroundColor: "rgba(220,220,220,1)",
35 | pointBorderColor: "#fff",
36 | pointHoverBackgroundColor: "#fff",
37 | pointHoverBorderColor: "rgba(220,220,220,1)",
38 | data: [65, 59, 90, 81, 56, 55, 40]
39 | },
40 | {
41 | label: "My Second dataset",
42 | backgroundColor: "rgba(151,187,205,0.2)",
43 | borderColor: "rgba(151,187,205,1)",
44 | pointBackgroundColor: "rgba(151,187,205,1)",
45 | pointBorderColor: "#fff",
46 | pointHoverBackgroundColor: "#fff",
47 | pointHoverBorderColor: "rgba(151,187,205,1)",
48 | data: [28, 48, 40, 19, 96, 27, 100]
49 | }
50 | ]
51 | };
52 | ```
53 | For a radar chart, to provide context of what each point means, we include an array of strings that show around each point in the chart.
54 | For the radar chart data, we have an array of datasets. Each of these is an object, with a fill colour, a stroke colour, a colour for the fill of each point, and a colour for the stroke of each point. We also have an array of data values.
55 | The label key on each dataset is optional, and can be used when generating a scale for the chart.
56 |
57 |
58 | ### Chart Options
59 |
60 | These are the customisation options specific to Radar charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart.
61 |
62 | The default options for radar chart are defined in `Chart.defaults.radar`.
63 |
64 | Name | Type | Default | Description
65 | --- | --- | --- | ---
66 | scale | Array | [See Scales](#scales) and [Defaults for Radial Linear Scale](#scales-radial-linear-scale) | Options for the one scale used on the chart. Use this to style the ticks, labels, and grid lines.
67 | *scale*.type | String |"radialLinear" | As defined in ["Radial Linear"](#scales-radial-linear-scale).
68 | *elements*.line | Array | | Options for all line elements used on the chart, as defined in the global elements, duplicated here to show Radar chart specific defaults.
69 | *elements.line*.tension | Number | 0 | Tension exhibited by lines when calculating splineCurve. Setting to 0 creates straight lines.
70 |
71 | You can override these for your `Chart` instance by passing a second argument into the `Radar` method as an object with the keys you want to override.
72 |
73 | For example, we could have a radar chart without a point for each on piece of data by doing the following:
74 |
75 | ```javascript
76 | new Chart(ctx, {
77 | type: "radar",
78 | data: data,
79 | options: {
80 | scale: {
81 | reverse: true,
82 | ticks: {
83 | beginAtZero: true
84 | }
85 | }
86 | }
87 | });
88 | // This will create a chart with all of the default options, merged from the global config,
89 | // and the Radar chart defaults but this particular instance's scale will be reversed as
90 | // well as the ticks beginning at zero.
91 | ```
92 |
93 | We can also change these defaults values for each Radar type that is created, this object is available at `Chart.defaults.radar`.
94 |
--------------------------------------------------------------------------------
/src/core/core.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function() {
4 |
5 | //Occupy the global variable of Chart, and create a simple base class
6 | var Chart = function(context, config) {
7 | this.config = config;
8 |
9 | // Support a jQuery'd canvas element
10 | if (context.length && context[0].getContext) {
11 | context = context[0];
12 | }
13 |
14 | // Support a canvas domnode
15 | if (context.getContext) {
16 | context = context.getContext("2d");
17 | }
18 |
19 | this.ctx = context;
20 | this.canvas = context.canvas;
21 |
22 | // Figure out what the size of the chart will be.
23 | // If the canvas has a specified width and height, we use those else
24 | // we look to see if the canvas node has a CSS width and height.
25 | // If there is still no height, fill the parent container
26 | this.width = context.canvas.width || parseInt(Chart.helpers.getStyle(context.canvas, 'width')) || Chart.helpers.getMaximumWidth(context.canvas);
27 | this.height = context.canvas.height || parseInt(Chart.helpers.getStyle(context.canvas, 'height')) || Chart.helpers.getMaximumHeight(context.canvas);
28 |
29 | this.aspectRatio = this.width / this.height;
30 |
31 | if (isNaN(this.aspectRatio) || isFinite(this.aspectRatio) === false) {
32 | // If the canvas has no size, try and figure out what the aspect ratio will be.
33 | // Some charts prefer square canvases (pie, radar, etc). If that is specified, use that
34 | // else use the canvas default ratio of 2
35 | this.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2;
36 | }
37 |
38 | // Store the original style of the element so we can set it back
39 | this.originalCanvasStyleWidth = context.canvas.style.width;
40 | this.originalCanvasStyleHeight = context.canvas.style.height;
41 |
42 | // High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
43 | Chart.helpers.retinaScale(this);
44 |
45 | if (config) {
46 | this.controller = new Chart.Controller(this);
47 | }
48 |
49 | // Always bind this so that if the responsive state changes we still work
50 | var _this = this;
51 | Chart.helpers.addResizeListener(context.canvas.parentNode, function() {
52 | if (_this.controller && _this.controller.config.options.responsive) {
53 | _this.controller.resize();
54 | }
55 | });
56 |
57 | return this.controller ? this.controller : this;
58 |
59 | };
60 |
61 | //Globally expose the defaults to allow for user updating/changing
62 | Chart.defaults = {
63 | global: {
64 | responsive: true,
65 | responsiveAnimationDuration: 0,
66 | maintainAspectRatio: true,
67 | events: ["mousemove", "mouseout", "click", "touchstart", "touchmove"],
68 | hover: {
69 | onHover: null,
70 | mode: 'single',
71 | animationDuration: 400
72 | },
73 | onClick: null,
74 | defaultColor: 'rgba(0,0,0,0.1)',
75 | defaultFontColor: '#666',
76 | defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
77 | defaultFontSize: 12,
78 | defaultFontStyle: 'normal',
79 | showLines: true,
80 |
81 | // Element defaults defined in element extensions
82 | elements: {},
83 |
84 | // Legend callback string
85 | legendCallback: function(chart) {
86 | var text = [];
87 | text.push('
');
88 | for (var i = 0; i < chart.data.datasets.length; i++) {
89 | text.push('
21 |
22 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/docs/06-Pie-Doughnut-Chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Pie & Doughnut Charts
3 | anchor: doughnut-pie-chart
4 | ---
5 | ### Introduction
6 | Pie and doughnut charts are probably the most commonly used chart there are. They are divided into segments, the arc of each segment shows the proportional value of each piece of data.
7 |
8 | They are excellent at showing the relational proportions between data.
9 |
10 | Pie and doughnut charts are effectively the same class in Chart.js, but have one different default value - their `percentageInnerCutout`. This equates what percentage of the inner should be cut out. This defaults to `0` for pie charts, and `50` for doughnuts.
11 |
12 | They are also registered under two aliases in the `Chart` core. Other than their different default value, and different alias, they are exactly the same.
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ### Example usage
24 |
25 | ```javascript
26 | // For a pie chart
27 | var myPieChart = new Chart(ctx,{
28 | type: 'pie',
29 | data: data,
30 | options: options
31 | });
32 | ```
33 |
34 | ```javascript
35 | // And for a doughnut chart
36 | var myDoughnutChart = new Chart(ctx, {
37 | type: 'doughnut',
38 | data: data,
39 | options: options
40 | });
41 | ```
42 |
43 | ### Data structure
44 |
45 | ```javascript
46 | var data = {
47 | labels: [
48 | "Red",
49 | "Green",
50 | "Yellow"
51 | ],
52 | datasets: [
53 | {
54 | data: [300, 50, 100],
55 | backgroundColor: [
56 | "#F7464A",
57 | "#46BFBD",
58 | "#FDB45C"
59 | ],
60 | hoverBackgroundColor: [
61 | "#FF5A5E",
62 | "#5AD3D1",
63 | "#FFC870"
64 | ]
65 | }]
66 | };
67 | ```
68 |
69 | For a pie chart, you must pass in an array of objects with a value and an optional color property. The value attribute should be a number, Chart.js will total all of the numbers and calculate the relative proportion of each. The color attribute should be a string. Similar to CSS, for this string you can use HEX notation, RGB, RGBA or HSL.
70 |
71 | ### Chart options
72 |
73 | These are the customisation options specific to Pie & Doughnut charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart.
74 |
75 | Name | Type | Default | Description
76 | --- | --- | --- | ---
77 | cutoutPercentage | Number | 50 - for doughnut, 0 - for pie | The percentage of the chart that is cut out of the middle.
78 | rotation | Number | -0.5 * Math.PI | Starting angle to draw arcs from
79 | circumference | Number | 2 * Math.PI | Sweep to allow arcs to cover
80 | scale | Array | [See Scales](#scales) and [Defaults for Radial Linear Scale](#getting-started-radial-linear-scale) | Options for the one scale used on the chart. Use this to style the ticks, labels, and grid.
81 | *scale*.type | String |"radialLinear" | As defined in ["Radial Linear"](#scales-radial-linear-scale).
82 | *scale*.lineArc | Boolean | true | When true, lines are arced compared to straight when false.
83 | *animation*.animateRotate | Boolean |true | If true, will animate the rotation of the chart.
84 | *animation*.animateScale | Boolean | false | If true, will animate scaling the Doughnut from the centre.
85 | *legend*.*labels*.generateLabels | Function | `function(data) {} ` | Returns labels for each the legend
86 | *legend*.onClick | Function | function(event, legendItem) {} ` | Handles clicking an individual legend item
87 |
88 | You can override these for your `Chart` instance by passing a second argument into the `Doughnut` method as an object with the keys you want to override.
89 |
90 | For example, we could have a doughnut chart that animates by scaling out from the centre like so:
91 |
92 | ```javascript
93 | new Chart(ctx,{
94 | type:"doughnut",
95 | animation:{
96 | animateScale:true
97 | }
98 | });
99 | // This will create a chart with all of the default options, merged from the global config,
100 | // and the Doughnut chart defaults but this particular instance will have `animateScale` set to `true`.
101 | ```
102 |
103 | We can also change these default values for each Doughnut type that is created, this object is available at `Chart.defaults.doughnut`. Pie charts also have a clone of these defaults available to change at `Chart.defaults.pie`, with the only difference being `percentageInnerCutout` being set to 0.
--------------------------------------------------------------------------------
/samples/timeScale/line-time-point-data.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Time Scale Point Data
6 |
7 |
8 |
9 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/src/core/core.animation.js:
--------------------------------------------------------------------------------
1 | /*global window: false */
2 | "use strict";
3 |
4 | module.exports = function(Chart) {
5 |
6 | var helpers = Chart.helpers;
7 |
8 | Chart.defaults.global.animation = {
9 | duration: 1000,
10 | easing: "easeOutQuart",
11 | onProgress: helpers.noop,
12 | onComplete: helpers.noop
13 | };
14 |
15 | Chart.Animation = Chart.Element.extend({
16 | currentStep: null, // the current animation step
17 | numSteps: 60, // default number of steps
18 | easing: "", // the easing to use for this animation
19 | render: null, // render function used by the animation service
20 |
21 | onAnimationProgress: null, // user specified callback to fire on each step of the animation
22 | onAnimationComplete: null // user specified callback to fire when the animation finishes
23 | });
24 |
25 | Chart.animationService = {
26 | frameDuration: 17,
27 | animations: [],
28 | dropFrames: 0,
29 | request: null,
30 | addAnimation: function(chartInstance, animationObject, duration, lazy) {
31 |
32 | if (!lazy) {
33 | chartInstance.animating = true;
34 | }
35 |
36 | for (var index = 0; index < this.animations.length; ++index) {
37 | if (this.animations[index].chartInstance === chartInstance) {
38 | // replacing an in progress animation
39 | this.animations[index].animationObject = animationObject;
40 | return;
41 | }
42 | }
43 |
44 | this.animations.push({
45 | chartInstance: chartInstance,
46 | animationObject: animationObject
47 | });
48 |
49 | // If there are no animations queued, manually kickstart a digest, for lack of a better word
50 | if (this.animations.length === 1) {
51 | this.requestAnimationFrame();
52 | }
53 | },
54 | // Cancel the animation for a given chart instance
55 | cancelAnimation: function(chartInstance) {
56 | var index = helpers.findIndex(this.animations, function(animationWrapper) {
57 | return animationWrapper.chartInstance === chartInstance;
58 | });
59 |
60 | if (index !== -1) {
61 | this.animations.splice(index, 1);
62 | chartInstance.animating = false;
63 | }
64 | },
65 | requestAnimationFrame: function() {
66 | var me = this;
67 | if (me.request === null) {
68 | // Skip animation frame requests until the active one is executed.
69 | // This can happen when processing mouse events, e.g. 'mousemove'
70 | // and 'mouseout' events will trigger multiple renders.
71 | me.request = helpers.requestAnimFrame.call(window, function() {
72 | me.request = null;
73 | me.startDigest();
74 | });
75 | }
76 | },
77 | startDigest: function() {
78 |
79 | var startTime = Date.now();
80 | var framesToDrop = 0;
81 |
82 | if (this.dropFrames > 1) {
83 | framesToDrop = Math.floor(this.dropFrames);
84 | this.dropFrames = this.dropFrames % 1;
85 | }
86 |
87 | var i = 0;
88 | while (i < this.animations.length) {
89 | if (this.animations[i].animationObject.currentStep === null) {
90 | this.animations[i].animationObject.currentStep = 0;
91 | }
92 |
93 | this.animations[i].animationObject.currentStep += 1 + framesToDrop;
94 |
95 | if (this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps) {
96 | this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
97 | }
98 |
99 | this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
100 | if (this.animations[i].animationObject.onAnimationProgress && this.animations[i].animationObject.onAnimationProgress.call) {
101 | this.animations[i].animationObject.onAnimationProgress.call(this.animations[i].chartInstance, this.animations[i]);
102 | }
103 |
104 | if (this.animations[i].animationObject.currentStep === this.animations[i].animationObject.numSteps) {
105 | if (this.animations[i].animationObject.onAnimationComplete && this.animations[i].animationObject.onAnimationComplete.call) {
106 | this.animations[i].animationObject.onAnimationComplete.call(this.animations[i].chartInstance, this.animations[i]);
107 | }
108 |
109 | // executed the last frame. Remove the animation.
110 | this.animations[i].chartInstance.animating = false;
111 |
112 | this.animations.splice(i, 1);
113 | } else {
114 | ++i;
115 | }
116 | }
117 |
118 | var endTime = Date.now();
119 | var dropFrames = (endTime - startTime) / this.frameDuration;
120 |
121 | this.dropFrames += dropFrames;
122 |
123 | // Do we have more stuff to animate?
124 | if (this.animations.length > 0) {
125 | this.requestAnimationFrame();
126 | }
127 | }
128 | };
129 | };
--------------------------------------------------------------------------------
/samples/scatter-logX.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Scatter Chart
6 |
7 |
8 |
15 |
16 |
17 |
18 |
14 |
15 | ### Example usage
16 | ```javascript
17 | var myBarChart = new Chart(ctx, {
18 | type: 'bar',
19 | data: data,
20 | options: options
21 | });
22 | ```
23 |
24 | ### Data structure
25 |
26 | ```javascript
27 | var data = {
28 | labels: ["January", "February", "March", "April", "May", "June", "July"],
29 | datasets: [
30 | {
31 | label: "My First dataset",
32 |
33 | // The properties below allow an array to be specified to change the value of the item at the given index
34 | // String or array - the bar color
35 | backgroundColor: "rgba(220,220,220,0.2)",
36 |
37 | // String or array - bar stroke color
38 | borderColor: "rgba(220,220,220,1)",
39 |
40 | // Number or array - bar border width
41 | borderWidth: 1,
42 |
43 | // String or array - fill color when hovered
44 | hoverBackgroundColor: "rgba(220,220,220,0.2)",
45 |
46 | // String or array - border color when hovered
47 | hoverBorderColor: "rgba(220,220,220,1)",
48 |
49 | // The actual data
50 | data: [65, 59, 80, 81, 56, 55, 40],
51 |
52 | // String - If specified, binds the dataset to a certain y-axis. If not specified, the first y-axis is used.
53 | yAxisID: "y-axis-0",
54 | },
55 | {
56 | label: "My Second dataset",
57 | backgroundColor: "rgba(220,220,220,0.2)",
58 | borderColor: "rgba(220,220,220,1)",
59 | borderWidth: 1,
60 | hoverBackgroundColor: "rgba(220,220,220,0.2)",
61 | hoverBorderColor: "rgba(220,220,220,1)",
62 | data: [28, 48, 40, 19, 86, 27, 90]
63 | }
64 | ]
65 | };
66 | ```
67 | The bar chart has the a very similar data structure to the line chart, and has an array of datasets, each with colours and an array of data. Again, colours are in CSS format.
68 | We have an array of labels too for display. In the example, we are showing the same data as the previous line chart example.
69 |
70 | The label key on each dataset is optional, and can be used when generating a scale for the chart.
71 |
72 | ### Chart Options
73 |
74 | These are the customisation options specific to Bar charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart.
75 |
76 | The default options for bar chart are defined in `Chart.defaults.Bar`.
77 |
78 | Name | Type | Default | Description
79 | --- |:---:| --- | ---
80 | stacked | Boolean | false |
81 | *hover*.mode | String | "label" | Label's hover mode. "label" is used since the x axis displays data by the index in the dataset.
82 | scales | Array | - | -
83 | *scales*.xAxes | Array | | The bar chart officially supports only 1 x-axis but uses an array to keep the API consistent. Use a scatter chart if you need multiple x axes.
84 | *Options for xAxes* | | |
85 | type | String | "Category" | As defined in [Scales](#scales-category-scale).
86 | display | Boolean | true | If true, show the scale.
87 | position | String | "bottom" | Position of the scale. Options are "top" and "bottom" for dataset scales.
88 | id | String | "x-axis-1" | Id of the axis so that data can bind to it
89 | categoryPercentage | Number | 0.8 | Percent (0-1) of the available width (the space between the gridlines for small datasets) for each data-point to use for the bars. [Read More](#bar-chart-barpercentage-vs-categorypercentage)
90 | barPercentage | Number | 0.9 | Percent (0-1) of the available width each bar should be within the category percentage. 1.0 will take the whole category width and put the bars right next to each other. [Read More](#bar-chart-barpercentage-vs-categorypercentage)
91 | gridLines | Array | [See Scales](#scales) |
92 | *gridLines*.offsetGridLines | Boolean | true | If true, the bars for a particular data point fall between the grid lines. If false, the grid line will go right down the middle of the bars.
93 | scaleLabel | Array | [See Scales](#scales) |
94 | ticks | Array | [See Scales](#scales) |
95 | | | |
96 | *scales*.yAxes | Array | `[{ type: "linear" }]` |
97 | *Options for xAxes* | | |
98 | type | String | "linear" | As defined in [Scales](#scales-linear-scale).
99 | display | Boolean | true | If true, show the scale.
100 | position | String | "left" | Position of the scale. Options are "left" and "right" for dataset scales.
101 | id | String | "y-axis-1" | Id of the axis so that data can bind to it.
102 | gridLines | Array | [See Scales](#scales) |
103 | scaleLabel | Array | [See Scales](#scales) |
104 | ticks | Array | [See Scales](#scales) |
105 |
106 | You can override these for your `Chart` instance by passing a second argument into the `Bar` method as an object with the keys you want to override.
107 |
108 | For example, we could have a bar chart without a stroke on each bar by doing the following:
109 |
110 | ```javascript
111 | new Chart(ctx, {
112 | type: "bar",
113 | data: data,
114 | options: {
115 | scales: {
116 | xAxes: [{
117 | stacked: true
118 | }],
119 | yAxes: [{
120 | stacked: true
121 | }]
122 | }
123 | }
124 | }
125 | });
126 | // This will create a chart with all of the default options, merged from the global config,
127 | // and the Bar chart defaults but this particular instance will have `stacked` set to true
128 | // for both x and y axes.
129 | ```
130 |
131 | We can also change these defaults values for each Bar type that is created, this object is available at `Chart.defaults.Bar`.
132 |
133 | #### barPercentage vs categoryPercentage
134 |
135 | The following shows the relationship between the bar percentage option and the category percentage option.
136 |
137 | ```text
138 | // categoryPercentage: 1.0
139 | // barPercentage: 1.0
140 | Bar: | 1.0 | 1.0 |
141 | Category: | 1.0 |
142 | Sample: |===========|
143 |
144 | // categoryPercentage: 1.0
145 | // barPercentage: 0.5
146 | Bar: |.5| |.5|
147 | Category: | 1.0 |
148 | Sample: |==============|
149 |
150 | // categoryPercentage: 0.5
151 | // barPercentage: 1.0
152 | Bar: |1.||1.|
153 | Category: | .5 |
154 | Sample: |==============|
155 | ```
--------------------------------------------------------------------------------
/samples/AnimationCallbacks/progress-bar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Animation Callbacks
5 |
6 |
7 |
14 |
15 |
16 |
17 |