Comparar commits
233 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 8d1f280771 | |||
| 63530ba66d | |||
| 088b4fcc55 | |||
| e3ffbb155f | |||
| 48b8a3a2c5 | |||
| 34f8bc99c6 | |||
| a5e4b38252 | |||
| 6065e8c801 | |||
| cd5a8d1a50 | |||
| 37b13265c9 | |||
| 0d5f282349 | |||
| db981807bb | |||
| 1f302f09bb | |||
| c0ccae1031 | |||
| 9023057906 | |||
| 2d5eb6d57e | |||
| 42648d0a83 | |||
| 4c749e78b2 | |||
| ef1acfa0e8 | |||
| 5759f419c0 | |||
| 8b3424bd09 | |||
| 76e060cf97 | |||
| 2aa489643a | |||
| 36de623401 | |||
| 74f161b548 | |||
| fa1b0db453 | |||
| 22c40fe752 | |||
| d3538a1fbd | |||
| b21061c164 | |||
| 8e3d3c2c22 | |||
| 9f9237905a | |||
| 872c9d5ebb | |||
| 15d9a044c6 | |||
| 051ef8178e | |||
| 76d2a782fc | |||
| 257bdb2dbb | |||
| b0ece8b516 | |||
| aaa2484e1b | |||
| 43f2c015f3 | |||
| d811812356 | |||
| d73dae09e7 | |||
| 4047440acc | |||
| 3f9afb1f31 | |||
| 81c66c8d85 | |||
| 0fa2ac4ea0 | |||
| 886c99623f | |||
| 68ae02263b | |||
| 9751e8f5b1 | |||
| 89574b61c5 | |||
| 253158cf1c | |||
| 8c34bdbcd7 | |||
| ecd3b58b28 | |||
| 41bac830c5 | |||
| 47b25d1c6d | |||
| 109de75c6a | |||
| 42856ec16a | |||
| faa43d51a9 | |||
| 3e4fce58fa | |||
| b0bddce017 | |||
| b5c69984e3 | |||
| a8375d0459 | |||
| 1f16b349d2 | |||
| 30019dfddd | |||
| 61eea7f4a1 | |||
| 6582e2ff06 | |||
| 8cfe21f6aa | |||
| 6815fc451f | |||
| 31643ee598 | |||
| 1a3bb25f5c | |||
| 181a61f16a | |||
| 763bde9ed1 | |||
| 0486db1e74 | |||
| 6185f156ea | |||
| d37e65c58c | |||
| 43efda72d4 | |||
| e55c0d4945 | |||
| a518833f96 | |||
| 49fe78643a | |||
| d2ef4c9831 | |||
| ec7b87d69c | |||
| 1e01e29f88 | |||
| e9f206606d | |||
| c5e4912319 | |||
| 63b7b18b17 | |||
| cfc008686b | |||
| e1237feb97 | |||
| cf88ec8333 | |||
| bba2dda59a | |||
| e2601f71e3 | |||
| 195c794e5d | |||
| 5732e0d72a | |||
| 187163763e | |||
| 15a32f4c78 | |||
| a76640c409 | |||
| 11773a5825 | |||
| 65de784be1 | |||
| 55cf07926c | |||
| bd39534c2f | |||
| 9dc63f0349 | |||
| edd93f27ce | |||
| 97a2232fbf | |||
| 29743e1d63 | |||
| 2bddccaaf0 | |||
| 8f5d702a86 | |||
| 24d32e9321 | |||
| 679fa4e72e | |||
| 9b4ebc3a05 | |||
| 7d4ead51f7 | |||
| bcd834c084 | |||
| 593a1eacc8 | |||
| d05d29aa66 | |||
| 4a3a9f1a21 | |||
| a89ede0d72 | |||
| 65ec618a48 | |||
| e63d5a3367 | |||
| 5b5463b6f9 | |||
| 205163641f | |||
| 09eae7ff28 | |||
| d46a174080 | |||
| 0c91bb96e0 | |||
| 9e9f436454 | |||
| cbd2fef80f | |||
| 996e471171 | |||
| 70f7dd4c7d | |||
| 9c106aa8ff | |||
| 7dcee4b40e | |||
| f12d1bcb56 | |||
| b9ab70f21b | |||
| c2d6e4c31f | |||
| a6c712323f | |||
| b93306788b | |||
| 2a62e0e04d | |||
| 8e5d8b8861 | |||
| 8b0b6194b2 | |||
| 675a9f827e | |||
| 235247eb12 | |||
| b3b5896329 | |||
| ecb1cde3fc | |||
| 9be48a73ea | |||
| 7e791e02f2 | |||
| 06d55e3501 | |||
| 37cfbdb802 | |||
| 8d1a857e75 | |||
| 42e2c237d4 | |||
| b22e1e3e9c | |||
| 635ec58616 | |||
| f44dd06826 | |||
| 02d7442e4f | |||
| 4ded634d75 | |||
| 80a25af502 | |||
| 9b2d2efc7a | |||
| 6b0e609935 | |||
| 74d3890ba9 | |||
| df4d2d2594 | |||
| b7dc2339f4 | |||
| edbb235668 | |||
| 92776ce0ac | |||
| e48c7f168b | |||
| 8a550852da | |||
| 6af94298ec | |||
| 1163efbcca | |||
| 3a32144d89 | |||
| e3519f5bd4 | |||
| 7d8b1712a7 | |||
| 54b51038ca | |||
| e774a893da | |||
| b03a057a76 | |||
| 48dd1cf024 | |||
| a2d477ac5e | |||
| 1352dfeb0f | |||
| d4e7765a50 | |||
| 73b579c962 | |||
| b67afcd727 | |||
| a10e245e5a | |||
| 181e866175 | |||
| d998c803d3 | |||
| a559ab85cd | |||
| c66edc9169 | |||
| b7606c7a8d | |||
| 12e2ace039 | |||
| ed4b9945f8 | |||
| 951e714e77 | |||
| 008bb1aab3 | |||
| 4cc24d475a | |||
| 4b8c9bc30d | |||
| 978ed1bc1e | |||
| 676765e808 | |||
| f2065265c8 | |||
| a6e2eec9e0 | |||
| 2e383da0d8 | |||
| e30a9d2a94 | |||
| 492292e94b | |||
| 454f519b6d | |||
| 56d53e3ad4 | |||
| 67b3d32218 | |||
| d7ad5b6340 | |||
| db1365c8ce | |||
| b5b0081108 | |||
| fff81c455d | |||
| ad972a0c5c | |||
| 872c92cd4b | |||
| 1d8d19d404 | |||
| ed6f234205 | |||
| 09c11440dd | |||
| e58421a08a | |||
| fb580a6e48 | |||
| f79ebdaa88 | |||
| b432a3d6db | |||
| ef4c46f2be | |||
| 69b1239aa1 | |||
| 4ef57315da | |||
| d26f37236d | |||
| 63e115e7a8 | |||
| 9f98334d6f | |||
| 15f724f146 | |||
| afb22d15ae | |||
| e709b13e77 | |||
| 460399f651 | |||
| 132272d140 | |||
| 9e4d90e110 | |||
| df91739b77 | |||
| d3f7071983 | |||
| d287a01b44 | |||
| 0eadc86d24 | |||
| c7b117631b | |||
| 0e29cfdfda | |||
| cf07df07d6 | |||
| 4b34bbadf9 | |||
| e13ada2b43 | |||
| 62b29282fd | |||
| 2814c8e44c | |||
| e67d198e69 | |||
| ae7552a916 |
@@ -1,19 +0,0 @@
|
||||
---
|
||||
engines:
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- javascript
|
||||
eslint:
|
||||
enabled: true
|
||||
fixme:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.js"
|
||||
exclude_paths:
|
||||
- dist/
|
||||
- node_modules/
|
||||
- test/
|
||||
- coverage/
|
||||
@@ -1 +0,0 @@
|
||||
**/*{.,-}min.js
|
||||
-213
@@ -1,213 +0,0 @@
|
||||
ecmaFeatures:
|
||||
modules: true
|
||||
jsx: true
|
||||
|
||||
env:
|
||||
amd: true
|
||||
browser: true
|
||||
es6: true
|
||||
jquery: true
|
||||
node: true
|
||||
|
||||
# http://eslint.org/docs/rules/
|
||||
rules:
|
||||
# Possible Errors
|
||||
comma-dangle: [2, never]
|
||||
no-cond-assign: 2
|
||||
no-console: 0
|
||||
no-constant-condition: 2
|
||||
no-control-regex: 2
|
||||
no-debugger: 2
|
||||
no-dupe-args: 2
|
||||
no-dupe-keys: 2
|
||||
no-duplicate-case: 2
|
||||
no-empty: 2
|
||||
no-empty-character-class: 2
|
||||
no-ex-assign: 2
|
||||
no-extra-boolean-cast: 2
|
||||
no-extra-parens: 0
|
||||
no-extra-semi: 2
|
||||
no-func-assign: 2
|
||||
no-inner-declarations: [2, functions]
|
||||
no-invalid-regexp: 2
|
||||
no-irregular-whitespace: 2
|
||||
no-negated-in-lhs: 2
|
||||
no-obj-calls: 2
|
||||
no-regex-spaces: 2
|
||||
no-sparse-arrays: 2
|
||||
no-unexpected-multiline: 2
|
||||
no-unreachable: 2
|
||||
use-isnan: 2
|
||||
valid-jsdoc: 0
|
||||
valid-typeof: 2
|
||||
|
||||
# Best Practices
|
||||
accessor-pairs: 2
|
||||
block-scoped-var: 0
|
||||
complexity: [2, 6]
|
||||
consistent-return: 0
|
||||
curly: 0
|
||||
default-case: 0
|
||||
dot-location: 0
|
||||
dot-notation: 0
|
||||
eqeqeq: 2
|
||||
guard-for-in: 2
|
||||
no-alert: 2
|
||||
no-caller: 2
|
||||
no-case-declarations: 2
|
||||
no-div-regex: 2
|
||||
no-else-return: 0
|
||||
no-empty-label: 2
|
||||
no-empty-pattern: 2
|
||||
no-eq-null: 2
|
||||
no-eval: 2
|
||||
no-extend-native: 2
|
||||
no-extra-bind: 2
|
||||
no-fallthrough: 2
|
||||
no-floating-decimal: 0
|
||||
no-implicit-coercion: 0
|
||||
no-implied-eval: 2
|
||||
no-invalid-this: 0
|
||||
no-iterator: 2
|
||||
no-labels: 0
|
||||
no-lone-blocks: 2
|
||||
no-loop-func: 2
|
||||
no-magic-number: 0
|
||||
no-multi-spaces: 0
|
||||
no-multi-str: 0
|
||||
no-native-reassign: 2
|
||||
no-new-func: 2
|
||||
no-new-wrappers: 2
|
||||
no-new: 2
|
||||
no-octal-escape: 2
|
||||
no-octal: 2
|
||||
no-proto: 2
|
||||
no-redeclare: 2
|
||||
no-return-assign: 2
|
||||
no-script-url: 2
|
||||
no-self-compare: 2
|
||||
no-sequences: 0
|
||||
no-throw-literal: 0
|
||||
no-unused-expressions: 2
|
||||
no-useless-call: 2
|
||||
no-useless-concat: 2
|
||||
no-void: 2
|
||||
no-warning-comments: 0
|
||||
no-with: 2
|
||||
radix: 2
|
||||
vars-on-top: 0
|
||||
wrap-iife: 2
|
||||
yoda: 0
|
||||
|
||||
# Strict
|
||||
strict: 0
|
||||
|
||||
# Variables
|
||||
init-declarations: 0
|
||||
no-catch-shadow: 2
|
||||
no-delete-var: 2
|
||||
no-label-var: 2
|
||||
no-shadow-restricted-names: 2
|
||||
no-shadow: 0
|
||||
no-undef-init: 2
|
||||
no-undef: 0
|
||||
no-undefined: 0
|
||||
no-unused-vars: 0
|
||||
no-use-before-define: 0
|
||||
|
||||
# Node.js and CommonJS
|
||||
callback-return: 2
|
||||
global-require: 2
|
||||
handle-callback-err: 2
|
||||
no-mixed-requires: 0
|
||||
no-new-require: 0
|
||||
no-path-concat: 2
|
||||
no-process-exit: 2
|
||||
no-restricted-modules: 0
|
||||
no-sync: 0
|
||||
|
||||
# Stylistic Issues
|
||||
array-bracket-spacing: 0
|
||||
block-spacing: 0
|
||||
brace-style: 0
|
||||
camelcase: 0
|
||||
comma-spacing: 0
|
||||
comma-style: 0
|
||||
computed-property-spacing: 0
|
||||
consistent-this: 0
|
||||
eol-last: 0
|
||||
func-names: 0
|
||||
func-style: 0
|
||||
id-length: 0
|
||||
id-match: 0
|
||||
indent: 0
|
||||
jsx-quotes: 0
|
||||
key-spacing: 0
|
||||
linebreak-style: 0
|
||||
lines-around-comment: 0
|
||||
max-depth: 0
|
||||
max-len: 0
|
||||
max-nested-callbacks: 0
|
||||
max-params: 0
|
||||
max-statements: [2, 30]
|
||||
new-cap: 0
|
||||
new-parens: 0
|
||||
newline-after-var: 0
|
||||
no-array-constructor: 0
|
||||
no-bitwise: 0
|
||||
no-continue: 0
|
||||
no-inline-comments: 0
|
||||
no-lonely-if: 0
|
||||
no-mixed-spaces-and-tabs: 0
|
||||
no-multiple-empty-lines: 0
|
||||
no-negated-condition: 0
|
||||
no-nested-ternary: 0
|
||||
no-new-object: 0
|
||||
no-plusplus: 0
|
||||
no-restricted-syntax: 0
|
||||
no-spaced-func: 0
|
||||
no-ternary: 0
|
||||
no-trailing-spaces: 0
|
||||
no-underscore-dangle: 0
|
||||
no-unneeded-ternary: 0
|
||||
object-curly-spacing: 0
|
||||
one-var: 0
|
||||
operator-assignment: 0
|
||||
operator-linebreak: 0
|
||||
padded-blocks: 0
|
||||
quote-props: 0
|
||||
quotes: 0
|
||||
require-jsdoc: 0
|
||||
semi-spacing: 0
|
||||
semi: 0
|
||||
sort-vars: 0
|
||||
space-after-keywords: 0
|
||||
space-before-blocks: 0
|
||||
space-before-function-paren: 0
|
||||
space-before-keywords: 0
|
||||
space-in-parens: 0
|
||||
space-infix-ops: 0
|
||||
space-return-throw-case: 0
|
||||
space-unary-ops: 0
|
||||
spaced-comment: 0
|
||||
wrap-regex: 0
|
||||
|
||||
# ECMAScript 6
|
||||
arrow-body-style: 0
|
||||
arrow-parens: 0
|
||||
arrow-spacing: 0
|
||||
constructor-super: 0
|
||||
generator-star-spacing: 0
|
||||
no-arrow-condition: 0
|
||||
no-class-assign: 0
|
||||
no-const-assign: 0
|
||||
no-dupe-class-members: 0
|
||||
no-this-before-super: 0
|
||||
no-var: 0
|
||||
object-shorthand: 0
|
||||
prefer-arrow-callback: 0
|
||||
prefer-const: 0
|
||||
prefer-reflect: 0
|
||||
prefer-spread: 0
|
||||
prefer-template: 0
|
||||
require-yield: 0
|
||||
+1
-2
@@ -3,8 +3,7 @@
|
||||
|
||||
node_modules/*
|
||||
custom/*
|
||||
coverage/*
|
||||
|
||||
docs/index.md
|
||||
|
||||
.settings/
|
||||
bower_components/
|
||||
|
||||
+1
-3
@@ -35,8 +35,6 @@ Pull requests
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Guidlines:
|
||||
|
||||
- Please create an issue first:
|
||||
@@ -47,7 +45,7 @@ Guidlines:
|
||||
- If adding new functionality, please also update the relevant `.md` file in [`/docs`](https://github.com/nnnick/Chart.js/tree/master/docs)
|
||||
- Please make your commits in logical sections with clear commit messages
|
||||
|
||||
Joining the project
|
||||
Joining the Project
|
||||
-------------
|
||||
- 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.
|
||||
|
||||
|
||||
externo
+4138
-2857
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+31
-3
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+1
-2
@@ -1,5 +1,4 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013-2016 Nick Downie
|
||||
Copyright (c) 2013-2015 Nick Downie
|
||||
|
||||
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:
|
||||
|
||||
|
||||
+6
-46
@@ -1,56 +1,16 @@
|
||||
[](http://www.chartjs.org/docs/)
|
||||
|
||||
# Chart.js
|
||||
|
||||
[](https://travis-ci.org/nnnick/Chart.js) [](https://codeclimate.com/github/nnnick/Chart.js) [](https://chartjs-slack-automation.herokuapp.com/)
|
||||
[](https://travis-ci.org/nnnick/Chart.js) [](https://codeclimate.com/github/nnnick/Chart.js)
|
||||
|
||||
# Version 2.0 is in early alpha. We are changing the internal data model to reduce copying, increase performance and expose a more flexible API.
|
||||
|
||||
*Simple HTML5 Charts using the canvas element* [chartjs.org](http://www.chartjs.org)
|
||||
|
||||
## v1.0.2 Stable
|
||||
## Documentation
|
||||
|
||||
- NPM: `npm install chart.js --save`
|
||||
- Bower: `bower install Chart.js --save`
|
||||
- CDN: https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js
|
||||
- Zip: [Download](https://github.com/nnnick/Chart.js/archive/master.zip)
|
||||
You can find documentation at [chartjs.org/docs](http://www.chartjs.org/docs/). 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.
|
||||
|
||||
[Examples](https://github.com/nnnick/Chart.js/tree/master/samples) | [Documentation](http://www.chartjs.org/docs/)
|
||||
|
||||
## v2.0 Beta
|
||||
|
||||
- Release: [2.0.0-beta](https://github.com/nnnick/Chart.js/releases/tag/2.0.0-beta)
|
||||
- Zip: [Download](https://github.com/nnnick/Chart.js/archive/2.0.0-beta2.zip)
|
||||
|
||||
Documentation for v2.0 is currently located [here](http://nnnick.github.io/Chart.js/docs-v2/).
|
||||
|
||||
## v2.0 Bleeding-Edge
|
||||
|
||||
- Branch: [v2.0-dev](https://github.com/nnnick/Chart.js/tree/v2.0-dev)
|
||||
- Zip: [Download](https://github.com/nnnick/Chart.js/archive/v2.0-dev.zip)
|
||||
|
||||
The next generation and release of Chart.js (v2.0) has been well under way this year and we are very close to releasing some amazing new features including, but not limited to:
|
||||
- Rewritten, optimized, and unit-tested
|
||||
- New and improved scales (log, time, linear, category, multiple scales)
|
||||
- Improved Tooltips and tooltip callbacks for customization
|
||||
- Improved responsiveness and resizing
|
||||
- Powerful support for adding, removing, changing, and updating data on the fly
|
||||
- Animations for everything, including all elements, colors and tooltips
|
||||
- Powerful customization when you need it. Automatic and dynamic when you don't.
|
||||
- Excellent support for modern frameworks and modular build systems.
|
||||
- Even more extensible via new element controllers, core scale classes, combo chart support, and hook systems
|
||||
- Bug fixes, stability improvements, etc.
|
||||
|
||||
#####Contributing to 2.0
|
||||
Submit PR's to the v2.0-dev branch.
|
||||
|
||||
#####Building and Testing
|
||||
`gulp build`, `gulp test`, `gulp watch --test`
|
||||
|
||||
#####v1.x Status: Feature Complete
|
||||
v1.x is now considered feature complete. PR's for bug fixes are still extremely welcome. Any open PR's for v1.x features will need to be reconsidered, refactored and resubmitted for v2.x (if the feature has not already been implemented). For questions on new features refer to the docs in the v2.0-dev branch
|
||||
|
||||
|
||||
## Bugs, Issues and Contributing
|
||||
## Bugs, issues and contributing
|
||||
|
||||
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.
|
||||
|
||||
@@ -58,4 +18,4 @@ For support using Chart.js, please post questions with the [`chartjs` tag on Sta
|
||||
|
||||
## License
|
||||
|
||||
Chart.js is available under the [MIT license](https://github.com/nnnick/Chart.js/blob/master/LICENSE.md).
|
||||
Chart.js is available under the [MIT license](http://opensource.org/licenses/MIT).
|
||||
|
||||
+11
-19
@@ -1,21 +1,13 @@
|
||||
{
|
||||
"name": "Chart.js",
|
||||
"description": "Simple HTML5 Charts using the canvas element",
|
||||
"homepage": "https://github.com/nnnick/Chart.js",
|
||||
"author": "nnnick",
|
||||
"main": [
|
||||
"Chart.js"
|
||||
],
|
||||
"ignore": [
|
||||
"**/*",
|
||||
".travis.yml",
|
||||
"CONTRIBUTING.md",
|
||||
"Chart.js",
|
||||
"LICENSE.md",
|
||||
"README.md",
|
||||
"gulpfile.js",
|
||||
"package.json"
|
||||
],
|
||||
"dependencies": {},
|
||||
"version": "1.1.0"
|
||||
"name": "Chart.js",
|
||||
"version": "2.0.0-alpha",
|
||||
"description": "Simple HTML5 Charts using the canvas element",
|
||||
"homepage": "https://github.com/nnnick/Chart.js",
|
||||
"author": "nnnick",
|
||||
"main": [
|
||||
"Chart.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"jquery": "~2.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "nnnick/chartjs",
|
||||
"type": "library",
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"keywords": [
|
||||
"chart",
|
||||
"js"
|
||||
],
|
||||
"homepage": "http://www.chartjs.org/",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "NICK DOWNIE",
|
||||
"email": "hello@nickdownie.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"release/2.0": "v2.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
+167
-127
@@ -11,14 +11,14 @@ First we need to include the Chart.js library on the page. The library occupies
|
||||
<script src="Chart.js"></script>
|
||||
```
|
||||
|
||||
Alternatively, if you're using an AMD loader for JavaScript modules, that is also supported in the Chart.js core. Please note: the library will still occupy a global variable of `Chart`, even if it detects `define` and `define.amd`. If this is a problem, you can call `noConflict` to restore the global Chart variable to its previous owner.
|
||||
Alternatively, if you're using an AMD loader for JavaScript modules, that is also supported in the Chart.js core. Please note: the library will still occupy a global variable of `Chart`, even if it detects `define` and `define.amd`. If this is a problem, you can call `noConflict` to restore the global Chart variable to it's previous owner.
|
||||
|
||||
```javascript
|
||||
// Using requirejs
|
||||
require(['path/to/Chartjs'], function(Chart){
|
||||
// Use Chart.js as normal here.
|
||||
|
||||
// Chart.noConflict restores the Chart global variable to its previous owner
|
||||
// Chart.noConflict restores the Chart global variable to it's previous owner
|
||||
// The function returns what was previously Chart, allowing you to reassign.
|
||||
var Chartjs = Chart.noConflict();
|
||||
|
||||
@@ -31,15 +31,9 @@ You can also grab Chart.js using bower:
|
||||
bower install Chart.js --save
|
||||
```
|
||||
|
||||
or NPM:
|
||||
|
||||
```bash
|
||||
npm install chart.js --save
|
||||
```
|
||||
|
||||
Also, Chart.js is available from CDN:
|
||||
|
||||
https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js
|
||||
https://cdnjs.com/libraries/chart.js
|
||||
|
||||
###Creating a chart
|
||||
|
||||
@@ -78,144 +72,209 @@ We call a method of the name of the chart we want to create. We pass in the data
|
||||
|
||||
This concept was introduced in Chart.js 1.0 to keep configuration DRY, and allow for changing options globally across chart types, avoiding the need to specify options for each instance, or the default for a particular chart type.
|
||||
|
||||
Templates are based on micro templating by John Resig:
|
||||
|
||||
http://ejohn.org/blog/javascript-micro-templating/
|
||||
|
||||
```javascript
|
||||
Chart.defaults.global = {
|
||||
// Boolean - Whether to animate the chart
|
||||
animation: true,
|
||||
// Animation settings
|
||||
animation: {
|
||||
// Length that animation should take in ms assuming 60fps.
|
||||
// Set to 0 to disable animation
|
||||
duration: 1000,
|
||||
|
||||
// Number - Number of animation steps
|
||||
animationSteps: 60,
|
||||
// Easing type. Possible values are:
|
||||
// [easeInOutQuart, linear, easeOutBounce, easeInBack, easeInOutQuad,
|
||||
// easeOutQuart, easeOutQuad, easeInOutBounce, easeOutSine, easeInOutCubic,
|
||||
// easeInExpo, easeInOutBack, easeInCirc, easeInOutElastic, easeOutBack,
|
||||
// easeInQuad, easeInOutExpo, easeInQuart, easeOutQuint, easeInOutCirc,
|
||||
// easeInSine, easeOutExpo, easeOutCirc, easeOutCubic, easeInQuint,
|
||||
// easeInElastic, easeInOutSine, easeInOutQuint, easeInBounce,
|
||||
// easeOutElastic, easeInCubic]
|
||||
easing: "easeOutQuart",
|
||||
|
||||
// String - Animation easing effect
|
||||
// Possible effects are:
|
||||
// [easeInOutQuart, linear, easeOutBounce, easeInBack, easeInOutQuad,
|
||||
// easeOutQuart, easeOutQuad, easeInOutBounce, easeOutSine, easeInOutCubic,
|
||||
// easeInExpo, easeInOutBack, easeInCirc, easeInOutElastic, easeOutBack,
|
||||
// easeInQuad, easeInOutExpo, easeInQuart, easeOutQuint, easeInOutCirc,
|
||||
// easeInSine, easeOutExpo, easeOutCirc, easeOutCubic, easeInQuint,
|
||||
// easeInElastic, easeInOutSine, easeInOutQuint, easeInBounce,
|
||||
// easeOutElastic, easeInCubic]
|
||||
animationEasing: "easeOutQuart",
|
||||
// Function - function to call each time an animation step occurs
|
||||
onProgress: function() {},
|
||||
|
||||
// Boolean - If we should show the scale at all
|
||||
showScale: true,
|
||||
// Function - function to call when animations finish
|
||||
onComplete: function() {},
|
||||
},
|
||||
|
||||
// Boolean - If we want to override with a hard coded scale
|
||||
scaleOverride: false,
|
||||
// Boolean - if true, resize the charts when the page resizes
|
||||
responsive: false,
|
||||
|
||||
// ** Required if scaleOverride is true **
|
||||
// Number - The number of steps in a hard coded scale
|
||||
scaleSteps: null,
|
||||
// Number - The value jump in the hard coded scale
|
||||
scaleStepWidth: null,
|
||||
// Number - The scale starting value
|
||||
scaleStartValue: null,
|
||||
// Boolean - if true, try to maintain the screen aspect ratio
|
||||
maintainAspectRatio: true,
|
||||
|
||||
// String - Colour of the scale line
|
||||
scaleLineColor: "rgba(0,0,0,.1)",
|
||||
// Array - events to bind tooltips to
|
||||
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
|
||||
|
||||
// Number - Pixel width of the scale line
|
||||
scaleLineWidth: 1,
|
||||
// Hover settings
|
||||
hover: {
|
||||
// Function - called when the user hovers over the items.
|
||||
// Parameters: array of active elements
|
||||
onHover: null,
|
||||
|
||||
// Boolean - Whether to show labels on the scale
|
||||
scaleShowLabels: true,
|
||||
// String - hover mode. Options are 'single', 'label', and 'dataset'
|
||||
// 'single' gets the nearest element
|
||||
// 'label' gets all of the elements at the given dataset index (do not use for scatter charts)
|
||||
// 'dataset' gets all the elements in the given dataset
|
||||
mode: 'single',
|
||||
|
||||
// Interpolated JS string - can access value
|
||||
scaleLabel: "<%=value%>",
|
||||
// Number - duration (in ms) of the tooltip animation. 0 to disable
|
||||
animationDuration: 400,
|
||||
},
|
||||
|
||||
// Boolean - Whether the scale should stick to integers, not floats even if drawing space is there
|
||||
scaleIntegersOnly: true,
|
||||
// Function - click handler to bind to chart area
|
||||
onClick: null,
|
||||
|
||||
// Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
||||
scaleBeginAtZero: false,
|
||||
// Tooltip configuration
|
||||
tooltips: {
|
||||
// Boolean - if true show tooltips
|
||||
enabled: true,
|
||||
|
||||
// String - Scale label font declaration for the scale label
|
||||
scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
// Function - custom tooltip function to use
|
||||
custom: null,
|
||||
|
||||
// Number - Scale label font size in pixels
|
||||
scaleFontSize: 12,
|
||||
// String - color of tooltip background
|
||||
backgroundColor: "rgba(0,0,0,0.8)",
|
||||
|
||||
// String - Scale label font weight style
|
||||
scaleFontStyle: "normal",
|
||||
// String - fonts to use
|
||||
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
|
||||
// String - Scale label font colour
|
||||
scaleFontColor: "#666",
|
||||
// Number - font size
|
||||
fontSize: 10,
|
||||
|
||||
// Boolean - whether or not the chart should be responsive and resize when the browser does.
|
||||
responsive: false,
|
||||
// String - font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container
|
||||
maintainAspectRatio: true,
|
||||
// String - font color
|
||||
fontColor: "#fff",
|
||||
|
||||
// Boolean - Determines whether to draw tooltips on the canvas or not
|
||||
showTooltips: true,
|
||||
// String - title fonts
|
||||
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
|
||||
// Function - Determines whether to execute the customTooltips function instead of drawing the built in tooltips (See [Advanced - External Tooltips](#advanced-usage-external-tooltips))
|
||||
customTooltips: false,
|
||||
// Number - title font size
|
||||
titleFontSize: 12,
|
||||
|
||||
// Array - Array of string names to attach tooltip events
|
||||
tooltipEvents: ["mousemove", "touchstart", "touchmove"],
|
||||
// String - title font style
|
||||
titleFontStyle: "bold",
|
||||
|
||||
// String - Tooltip background colour
|
||||
tooltipFillColor: "rgba(0,0,0,0.8)",
|
||||
// String - title font color
|
||||
titleFontColor: "#fff",
|
||||
|
||||
// String - Tooltip label font declaration for the scale label
|
||||
tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
// Number -
|
||||
yPadding: 6,
|
||||
|
||||
// Number - Tooltip label font size in pixels
|
||||
tooltipFontSize: 14,
|
||||
// Number -
|
||||
xPadding: 6,
|
||||
|
||||
// String - Tooltip font weight style
|
||||
tooltipFontStyle: "normal",
|
||||
// Number -
|
||||
caretSize: 8,
|
||||
|
||||
// String - Tooltip label font colour
|
||||
tooltipFontColor: "#fff",
|
||||
// Number - radius of rounded corners
|
||||
cornerRadius: 6,
|
||||
|
||||
// String - Tooltip title font declaration for the scale label
|
||||
tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
// Number -
|
||||
xOffset: 10,
|
||||
|
||||
// Number - Tooltip title font size in pixels
|
||||
tooltipTitleFontSize: 14,
|
||||
// String - template string to use for tooltips in single mode
|
||||
template: [
|
||||
'<% if(label){ %>',
|
||||
'<%=label %>:',
|
||||
'<% } %>',
|
||||
'<%=value %>',
|
||||
].join(''),
|
||||
|
||||
// String - Tooltip title font weight style
|
||||
tooltipTitleFontStyle: "bold",
|
||||
// String - template string to use for tooltips in label mode
|
||||
multiTemplate: [
|
||||
'<%if (datasetLabel){ %>',
|
||||
'<%=datasetLabel %>:',
|
||||
'<% } %>',
|
||||
'<%=value %>'
|
||||
].join(''),
|
||||
|
||||
// String - Tooltip title font colour
|
||||
tooltipTitleFontColor: "#fff",
|
||||
// String -
|
||||
multiKeyBackground: '#fff',
|
||||
},
|
||||
|
||||
// String - Tooltip title template
|
||||
tooltipTitleTemplate: "<%= label%>",
|
||||
// String - default grey color. 'rgba(0,0,0,0.1)'
|
||||
defaultColor: defaultColor,
|
||||
|
||||
// Number - pixel width of padding around tooltip text
|
||||
tooltipYPadding: 6,
|
||||
// Element defaults
|
||||
elements: {
|
||||
// Default settings for all line elements
|
||||
line: {
|
||||
// Number - Bezier curve tension. Set to 0 for no bezier curves
|
||||
tension: 0.4,
|
||||
|
||||
// Number - pixel width of padding around tooltip text
|
||||
tooltipXPadding: 6,
|
||||
// String - the fill color
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - Size of the caret on the tooltip
|
||||
tooltipCaretSize: 8,
|
||||
// Number - width of the line
|
||||
borderWidth: 3,
|
||||
|
||||
// Number - Pixel radius of the tooltip border
|
||||
tooltipCornerRadius: 6,
|
||||
// String = color of the line
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Boolean - if true fill in the area between the line and the x axis with the background color
|
||||
fill: true,
|
||||
|
||||
// Number - Pixel offset from point x to tooltip edge
|
||||
tooltipXOffset: 10,
|
||||
{% raw %}
|
||||
// String - Template string for single tooltips
|
||||
tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>",
|
||||
{% endraw %}
|
||||
// String - Template string for multiple tooltips
|
||||
multiTooltipTemplate: "<%= value %>",
|
||||
// Boolean -
|
||||
skipNull: true,
|
||||
|
||||
// Function - Will fire on animation progression.
|
||||
onAnimationProgress: function(){},
|
||||
// Boolean -
|
||||
drawNull: false,
|
||||
},
|
||||
// Settings for all point elements
|
||||
point: {
|
||||
// Number - radius of point circle
|
||||
radius: 3,
|
||||
|
||||
// Function - Will fire on animation completion.
|
||||
onAnimationComplete: function(){}
|
||||
// String - fill color of point
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - width of stroke of point circle
|
||||
borderWidth: 1,
|
||||
|
||||
// String - stroke color for point
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Number - extra radius added to radius for hit detection
|
||||
hitRadius: 6,
|
||||
|
||||
// Number - radius of point circle when hovered
|
||||
hoverRadius: 4,
|
||||
|
||||
// Number - radius of circle stroke when hovered
|
||||
hoverBorderWidth: 2,
|
||||
},
|
||||
// Settings for all bar elements
|
||||
bar: {
|
||||
// String - fill color of bar
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - width of stroke of line surrounding bar fill
|
||||
borderWidth: 0,
|
||||
|
||||
// String - Border color
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Number -
|
||||
valueSpacing: 5,
|
||||
|
||||
// Number -
|
||||
datasetSpacing: 1,
|
||||
},
|
||||
// Settings for all slice elements
|
||||
slice: {
|
||||
// String - fill color
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// String - border color
|
||||
borderColor: "#fff",
|
||||
|
||||
// Number - border stroke width
|
||||
borderWidth: 2,
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -226,22 +285,3 @@ Chart.defaults.global.responsive = true;
|
||||
```
|
||||
|
||||
Now, every time we create a chart, `options.responsive` will be `true`.
|
||||
|
||||
###Tooltip Templates
|
||||
For the `tooltipTemplate` and `multiTooltipTemplate` configuration options, you may use special template blocks. **Do not use user input in the tooltip template, as it may be executed as raw JavaScript.**
|
||||
|
||||
Anything in between `<%` and `%>` will be executed as JavaScript. As in the example shown above, `<% if (label) { %>` and later `<% } %>`. Using `<%= expr %>` will print the value of the variable named by `expr` into the tooltip.
|
||||
|
||||
The available variables to print into the tooltip depends on the chart type. In general, the following variables are available:
|
||||
|
||||
```javascript
|
||||
{
|
||||
value : "value of the data point",
|
||||
label : "label for the position the data point is at",
|
||||
datasetLabel: "label for the dataset the point is from",
|
||||
strokeColor : "stroke color for the dataset",
|
||||
fillColor : "fill color for the dataset",
|
||||
highlightFill : "highlight fill color for the dataset",
|
||||
highlightStroke : "highlight stroke color for the dataset"
|
||||
}
|
||||
```
|
||||
|
||||
+190
-53
@@ -13,7 +13,10 @@ Often, it is used to show trend data, and the comparison of two data sets.
|
||||
|
||||
###Example usage
|
||||
```javascript
|
||||
var myLineChart = new Chart(ctx).Line(data, options);
|
||||
var myLineChart = new Chart(ctx).Line({
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
```
|
||||
###Data structure
|
||||
|
||||
@@ -23,29 +26,64 @@ var data = {
|
||||
datasets: [
|
||||
{
|
||||
label: "My First dataset",
|
||||
fillColor: "rgba(220,220,220,0.2)",
|
||||
strokeColor: "rgba(220,220,220,1)",
|
||||
pointColor: "rgba(220,220,220,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(220,220,220,1)",
|
||||
data: [65, 59, 80, 81, 56, 55, 40]
|
||||
|
||||
// Boolean - if true fill the area under the line
|
||||
fill: false,
|
||||
|
||||
// String - the color to fill the area under the line with if fill is true
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
|
||||
// The properties below allow an array to be specified to change the value of the item at the given index
|
||||
|
||||
// String or array - Line color
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// String or array - Point stroke color
|
||||
pointBorderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// String or array - Point fill color
|
||||
pointBackgroundColor: "#fff",
|
||||
|
||||
// Number or array - Stroke width of point border
|
||||
pointBorderWidth: 1,
|
||||
|
||||
// Number or array - Radius of point when hovered
|
||||
pointHoverRadius: 5,
|
||||
|
||||
// String or array - point background color when hovered
|
||||
pointHoverBackgroundColor: "rgba(220,220,220,1)",
|
||||
|
||||
// Point border color when hovered
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// Number or array - border width of point when hovered
|
||||
pointBorderWidth: 2,
|
||||
|
||||
// The actual data
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
|
||||
// String - If specified, binds the dataset to a certain y-axis. If not specified, the first y-axis is used.
|
||||
yAxisID: "y-axis-1",
|
||||
},
|
||||
{
|
||||
label: "My Second dataset",
|
||||
fillColor: "rgba(151,187,205,0.2)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||
fill: false,
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
pointBorderColor: "rgba(220,220,220,1)",
|
||||
pointBackgroundColor: "#fff",
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: "rgba(220,220,220,1)",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointBorderWidth: 2,
|
||||
data: [28, 48, 40, 19, 86, 27, 90]
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
The line chart requires an array of labels. This labels are shown on the X axis. There must be one label for each data point. More labels than datapoints are allowed, in which case the line ends at the last data point.
|
||||
The line chart requires an array of labels for each of the data points. This is shown on the X axis.
|
||||
The data for line charts is broken up into an array of datasets. Each dataset has a colour for the fill, a colour for the line and colours for the points and strokes of the points. These colours are strings just like CSS. You can use RGBA, RGB, HEX or HSL notation.
|
||||
|
||||
The label key on each dataset is optional, and can be used when generating a scale for the chart.
|
||||
@@ -56,65 +94,164 @@ These are the customisation options specific to Line charts. These options are m
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Boolean - if true, line stack on top of each other along the y axis
|
||||
stacked: false,
|
||||
|
||||
///Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines : true,
|
||||
hover: {
|
||||
// String - We use a label hover mode since the x axis displays data by the index in the dataset
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor : "rgba(0,0,0,.05)",
|
||||
scales: {
|
||||
// The line 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.
|
||||
xAxes: [{
|
||||
// String - type of axis to use. Should not be changed from 'dataset'. To use a 'linear' axis on the x, use the scatter chart type
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth : 1,
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
// String - position of the scale. possible options are "top" and "bottom" for dataset scales
|
||||
position: "bottom",
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
// String - id of the axis so that data can bind to it
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
//Boolean - Whether the line is curved between points
|
||||
bezierCurve : true,
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
//Number - Tension of the bezier curve between points
|
||||
bezierCurveTension : 0.4,
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
//Boolean - Whether to show a dot for each point
|
||||
pointDot : true,
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointDotRadius : 4,
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
//Number - Pixel width of point dot stroke
|
||||
pointDotStrokeWidth : 1,
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
pointHitDetectionRadius : 20,
|
||||
// Number - width of the grid line for the first index (index 0)
|
||||
zeroLineWidth: 1,
|
||||
|
||||
//Boolean - Whether to show a stroke for datasets
|
||||
datasetStroke : true,
|
||||
// String - color of the grid line for the first index
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
|
||||
//Number - Pixel width of dataset stroke
|
||||
datasetStrokeWidth : 2,
|
||||
// Boolean - if true, offset labels from grid lines
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
//Boolean - Whether to fill the dataset with a colour
|
||||
datasetFill : true,
|
||||
{% raw %}
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
|
||||
{% endraw %}
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
//Boolean - Whether to horizontally center the label and point dot inside the grid
|
||||
offsetGridLines : false
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
// String - type of axis. 'linear' is the default but extensions may provide other types such as logarithmic
|
||||
scaleType: "linear",
|
||||
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
// String - position of axis. Vertical axes can have either "left" or "right"
|
||||
position: "left",
|
||||
|
||||
// ID of the axis for data binding
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
// Number - width of the grid line representing a numerical value of 0
|
||||
zeroLineWidth: 1,
|
||||
|
||||
// String - color of the grid line representing a numerical value of 0
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// Boolean - if true ensures that the scale always has a 0 point
|
||||
beginAtZero: false,
|
||||
|
||||
// Object - if specified, allows the user to override the step generation algorithm.
|
||||
// Contains the following values
|
||||
// start: // number to start at
|
||||
// stepWidth: // size of step
|
||||
// steps: // number of steps
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Function - if specified this is passed the tick value, index, and the array of all tick values. Returns a string that is used as the label for that value
|
||||
userCallback: null,
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can override these for your `Chart` instance by passing a second argument into the `Line` method as an object with the keys you want to override.
|
||||
You can override these for your `Chart` instance by passing a member `options` into the `Line` method.
|
||||
|
||||
For example, we could have a line chart without bezier curves between points by doing the following:
|
||||
For example, we could have a line chart display without an x axis by doing the following. The config merge is smart enough to handle arrays so that you do not need to specify all axis settings to change one thing.
|
||||
|
||||
```javascript
|
||||
new Chart(ctx).Line(data, {
|
||||
bezierCurve: false
|
||||
new Chart(ctx).Line({
|
||||
data: data,
|
||||
options: {
|
||||
xAxes: [{
|
||||
show: false
|
||||
}]
|
||||
}
|
||||
});
|
||||
// This will create a chart with all of the default options, merged from the global config,
|
||||
// and the Line chart defaults, but this particular instance will have `bezierCurve` set to false.
|
||||
|
||||
+171
-37
@@ -14,7 +14,10 @@ It is sometimes used to show trend data, and the comparison of multiple data set
|
||||
|
||||
### Example usage
|
||||
```javascript
|
||||
var myBarChart = new Chart(ctx).Bar(data, options);
|
||||
var myBarChart = new Chart(ctx).Bar({
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
```
|
||||
|
||||
### Data structure
|
||||
@@ -25,18 +28,36 @@ var data = {
|
||||
datasets: [
|
||||
{
|
||||
label: "My First dataset",
|
||||
fillColor: "rgba(220,220,220,0.5)",
|
||||
strokeColor: "rgba(220,220,220,0.8)",
|
||||
highlightFill: "rgba(220,220,220,0.75)",
|
||||
highlightStroke: "rgba(220,220,220,1)",
|
||||
data: [65, 59, 80, 81, 56, 55, 40]
|
||||
|
||||
// The properties below allow an array to be specified to change the value of the item at the given index
|
||||
// String or array - the bar color
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
|
||||
// String or array - bar stroke color
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// Number or array - bar border width
|
||||
borderWidth: 1,
|
||||
|
||||
// String or array - fill color when hovered
|
||||
hoverBackgroundColor: "rgba(220,220,220,0.2)",
|
||||
|
||||
// String or array - border color when hovered
|
||||
hoverBorderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// The actual data
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
|
||||
// String - If specified, binds the dataset to a certain y-axis. If not specified, the first y-axis is used.
|
||||
yAxisID: "y-axis-1",
|
||||
},
|
||||
{
|
||||
label: "My Second dataset",
|
||||
fillColor: "rgba(151,187,205,0.5)",
|
||||
strokeColor: "rgba(151,187,205,0.8)",
|
||||
highlightFill: "rgba(151,187,205,0.75)",
|
||||
highlightStroke: "rgba(151,187,205,1)",
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
borderWidth: 1,
|
||||
hoverBackgroundColor: "rgba(220,220,220,0.2)",
|
||||
hoverBorderColor: "rgba(220,220,220,1)",
|
||||
data: [28, 48, 40, 19, 86, 27, 90]
|
||||
}
|
||||
]
|
||||
@@ -53,40 +74,150 @@ These are the customisation options specific to Bar charts. These options are me
|
||||
|
||||
```javascript
|
||||
{
|
||||
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
||||
scaleBeginAtZero : true,
|
||||
// Boolean - if true, bars stack on top of each other
|
||||
stacked: false,
|
||||
|
||||
//Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines : true,
|
||||
hover: {
|
||||
// String - We use a label hover mode since the x axis displays data by the index in the dataset
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor : "rgba(0,0,0,.05)",
|
||||
scales: {
|
||||
// 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.
|
||||
xAxes: [{
|
||||
// String - type of axis to use. Should not be changed from 'dataset'.
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth : 1,
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
// String - position of the scale. possible options are "top" and "bottom" for dataset scales
|
||||
position: "bottom",
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
// String - id of the axis so that data can bind to it
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
//Boolean - If there is a stroke on each bar
|
||||
barShowStroke : true,
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
//Number - Pixel width of the bar stroke
|
||||
barStrokeWidth : 2,
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
//Number - Spacing between each of the X value sets
|
||||
barValueSpacing : 5,
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
//Number - Spacing between data sets within X values
|
||||
barDatasetSpacing : 1,
|
||||
{% raw %}
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
|
||||
{% endraw %}
|
||||
}
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
// Number - width of the grid line for the first index (index 0)
|
||||
zeroLineWidth: 1,
|
||||
|
||||
// String - color of the grid line for the first index
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
|
||||
// Boolean - if true, offset labels from grid lines
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
// String - type of axis. 'linear' is the default but extensions may provide other types such as logarithmic
|
||||
scaleType: "linear",
|
||||
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
// String - position of axis. Vertical axes can have either "left" or "right"
|
||||
position: "left",
|
||||
|
||||
// ID of the axis for data binding
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
// Number - width of the grid line representing a numerical value of 0
|
||||
zeroLineWidth: 1,
|
||||
|
||||
// String - color of the grid line representing a numerical value of 0
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// Boolean - if true ensures that the scale always has a 0 point
|
||||
beginAtZero: false,
|
||||
|
||||
// Object - if specified, allows the user to override the step generation algorithm.
|
||||
// Contains the following values
|
||||
// start: // number to start at
|
||||
// stepWidth: // size of step
|
||||
// steps: // number of steps
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Function - if specified this is passed the tick value, index, and the array of all tick values. Returns a string that is used as the label for that value
|
||||
userCallback: null,
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -94,8 +225,11 @@ You can override these for your `Chart` instance by passing a second argument in
|
||||
For example, we could have a bar chart without a stroke on each bar by doing the following:
|
||||
|
||||
```javascript
|
||||
new Chart(ctx).Bar(data, {
|
||||
barShowStroke: false
|
||||
new Chart(ctx).Bar({
|
||||
data: data,
|
||||
options: {
|
||||
barShowStroke: false
|
||||
}
|
||||
});
|
||||
// This will create a chart with all of the default options, merged from the global config,
|
||||
// and the Bar chart defaults but this particular instance will have `barShowStroke` set to false.
|
||||
|
||||
@@ -76,9 +76,6 @@ These are the customisation options specific to Radar charts. These options are
|
||||
//Number - Pixel width of the angle line
|
||||
angleLineWidth : 1,
|
||||
|
||||
//Number - Interval at which to draw angle lines ("every Nth point")
|
||||
angleLineInterval: 1,
|
||||
|
||||
//String - Point label font declaration
|
||||
pointLabelFontFamily : "'Arial'",
|
||||
|
||||
@@ -113,7 +110,7 @@ These are the customisation options specific to Radar charts. These options are
|
||||
datasetFill : true,
|
||||
{% raw %}
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
|
||||
{% endraw %}
|
||||
}
|
||||
```
|
||||
@@ -177,4 +174,4 @@ Calling `removeData()` on your Chart instance will remove the first value for al
|
||||
```javascript
|
||||
myRadarChart.removeData();
|
||||
// Other points will now animate to their correct positions.
|
||||
```
|
||||
```
|
||||
@@ -102,7 +102,7 @@ These are the customisation options specific to Polar Area charts. These options
|
||||
animateScale : false,
|
||||
{% raw %}
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
|
||||
{% endraw %}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -88,7 +88,7 @@ These are the customisation options specific to Pie & Doughnut charts. These opt
|
||||
animateScale : false,
|
||||
{% raw %}
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
|
||||
{% endraw %}
|
||||
}
|
||||
```
|
||||
@@ -137,8 +137,6 @@ myDoughnutChart.update();
|
||||
|
||||
Calling `addData(segmentData, index)` on your Chart instance passing an object in the same format as in the constructor. There is an optional second argument of 'index', this determines at what index the new segment should be inserted into the chart.
|
||||
|
||||
If you don't specify a color and highliht, one will be chosen from the global default array: segmentColorDefault and the corresponding segmentHighlightColorDefault. The index of the addded data is used to lookup a corresponding color from the defaults.
|
||||
|
||||
```javascript
|
||||
// An object in the same format as the original data source
|
||||
myDoughnutChart.addData({
|
||||
|
||||
@@ -54,7 +54,7 @@ myLineChart.destroy();
|
||||
|
||||
#### .toBase64Image()
|
||||
|
||||
This returns a base 64 encoded string of the chart in its current state.
|
||||
This returns a base 64 encoded string of the chart in it's current state.
|
||||
|
||||
```javascript
|
||||
myLineChart.toBase64Image();
|
||||
@@ -96,7 +96,7 @@ var myPieChart = new Chart(ctx).Pie(data, {
|
||||
// tooltip.y
|
||||
// etc...
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
@@ -176,7 +176,7 @@ npm install -g gulp
|
||||
|
||||
This will install the local development dependencies for Chart.js, along with a CLI for the JavaScript task runner <a href="http://gulpjs.com/" target="_blank">gulp</a>.
|
||||
|
||||
Now, we can run the `gulp build` task, and pass in a comma-separated list of types as an argument to build a custom version of Chart.js with only specified chart types.
|
||||
Now, we can run the `gulp build` task, and pass in a comma seperated list of types as an argument to build a custom version of Chart.js with only specified chart types.
|
||||
|
||||
Here we will create a version of Chart.js with only Line, Radar and Bar charts included:
|
||||
|
||||
|
||||
+97
-104
@@ -1,129 +1,122 @@
|
||||
var gulp = require('gulp'),
|
||||
concat = require('gulp-concat'),
|
||||
uglify = require('gulp-uglify'),
|
||||
util = require('gulp-util'),
|
||||
jshint = require('gulp-jshint'),
|
||||
size = require('gulp-size'),
|
||||
connect = require('gulp-connect'),
|
||||
replace = require('gulp-replace'),
|
||||
htmlv = require('gulp-html-validator'),
|
||||
inquirer = require('inquirer'),
|
||||
semver = require('semver'),
|
||||
exec = require('child_process').exec,
|
||||
fs = require('fs'),
|
||||
package = require('./package.json'),
|
||||
bower = require('./bower.json'),
|
||||
livereload = require('gulp-livereload');
|
||||
concat = require('gulp-concat'),
|
||||
uglify = require('gulp-uglify'),
|
||||
util = require('gulp-util'),
|
||||
jshint = require('gulp-jshint'),
|
||||
size = require('gulp-size'),
|
||||
connect = require('gulp-connect'),
|
||||
replace = require('gulp-replace'),
|
||||
htmlv = require('gulp-html-validator'),
|
||||
inquirer = require('inquirer'),
|
||||
semver = require('semver'),
|
||||
exec = require('child_process').exec,
|
||||
fs = require('fs'),
|
||||
package = require('./package.json'),
|
||||
bower = require('./bower.json');
|
||||
|
||||
var srcDir = './src/';
|
||||
/*
|
||||
* Usage : gulp build --types=Bar,Line,Doughnut
|
||||
* Output: - A built Chart.js file with Core and types Bar, Line and Doughnut concatenated together
|
||||
* - A minified version of this code, in Chart.min.js
|
||||
* Usage : gulp build --types=Bar,Line,Doughnut
|
||||
* Output: - A built Chart.js file with Core and types Bar, Line and Doughnut concatenated together
|
||||
* - A minified version of this code, in Chart.min.js
|
||||
*/
|
||||
|
||||
gulp.task('build', function(){
|
||||
gulp.task('build', function() {
|
||||
|
||||
// Default to all of the chart types, with Chart.Core first
|
||||
var srcFiles = [FileName('Core')],
|
||||
isCustom = !!(util.env.types),
|
||||
outputDir = (isCustom) ? 'custom' : '.';
|
||||
if (isCustom){
|
||||
util.env.types.split(',').forEach(function(type){ return srcFiles.push(FileName(type));});
|
||||
}
|
||||
else{
|
||||
// Seems gulp-concat remove duplicates - nice!
|
||||
// So we can use this to sort out dependency order - aka include Core first!
|
||||
srcFiles.push(srcDir+'*');
|
||||
}
|
||||
var srcFiles = [
|
||||
'./src/core/core.js',
|
||||
'./src/core/core.helpers.js',
|
||||
'./src/core/core.chart.js',
|
||||
'./src/core/core.element.js',
|
||||
'./src/core/**',
|
||||
'./src/controllers/**',
|
||||
'./src/scales/**',
|
||||
'./src/elements/**',
|
||||
'./src/charts/chart.bar.js',
|
||||
'./node_modules/color/dist/color.min.js'
|
||||
],
|
||||
isCustom = !!(util.env.types),
|
||||
outputDir = (isCustom) ? 'custom' : '.';
|
||||
|
||||
return gulp.src(srcFiles)
|
||||
.pipe(concat('Chart.js'))
|
||||
.pipe(replace('{{ version }}', package.version))
|
||||
.pipe(gulp.dest(outputDir))
|
||||
.pipe(uglify({preserveComments:'some'}))
|
||||
.pipe(concat('Chart.min.js'))
|
||||
.pipe(gulp.dest(outputDir));
|
||||
return gulp.src(srcFiles)
|
||||
.pipe(concat('Chart.js'))
|
||||
.pipe(replace('{{ version }}', package.version))
|
||||
.pipe(gulp.dest(outputDir))
|
||||
.pipe(uglify({
|
||||
preserveComments: 'some'
|
||||
}))
|
||||
.pipe(concat('Chart.min.js'))
|
||||
.pipe(gulp.dest(outputDir));
|
||||
|
||||
function FileName(moduleName){
|
||||
return srcDir+'Chart.'+moduleName+'.js';
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Usage : gulp bump
|
||||
* Prompts: Version increment to bump
|
||||
* Output: - New version number written into package.json & bower.json
|
||||
* Usage : gulp bump
|
||||
* Prompts: Version increment to bump
|
||||
* Output: - New version number written into package.json & bower.json
|
||||
*/
|
||||
|
||||
gulp.task('bump', function(complete){
|
||||
util.log('Current version:', util.colors.cyan(package.version));
|
||||
var choices = ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease'].map(function(versionType){
|
||||
return versionType + ' (v' + semver.inc(package.version, versionType) + ')';
|
||||
});
|
||||
inquirer.prompt({
|
||||
type: 'list',
|
||||
name: 'version',
|
||||
message: 'What version update would you like?',
|
||||
choices: choices
|
||||
}, function(res){
|
||||
var increment = res.version.split(' ')[0],
|
||||
newVersion = semver.inc(package.version, increment);
|
||||
gulp.task('bump', function(complete) {
|
||||
util.log('Current version:', util.colors.cyan(package.version));
|
||||
var choices = ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease'].map(function(versionType) {
|
||||
return versionType + ' (v' + semver.inc(package.version, versionType) + ')';
|
||||
});
|
||||
inquirer.prompt({
|
||||
type: 'list',
|
||||
name: 'version',
|
||||
message: 'What version update would you like?',
|
||||
choices: choices
|
||||
}, function(res) {
|
||||
var increment = res.version.split(' ')[0],
|
||||
newVersion = semver.inc(package.version, increment);
|
||||
|
||||
// Set the new versions into the bower/package object
|
||||
package.version = newVersion;
|
||||
bower.version = newVersion;
|
||||
// Set the new versions into the bower/package object
|
||||
package.version = newVersion;
|
||||
bower.version = newVersion;
|
||||
|
||||
// Write these to their own files, then build the output
|
||||
fs.writeFileSync('package.json', JSON.stringify(package, null, 2));
|
||||
fs.writeFileSync('bower.json', JSON.stringify(bower, null, 2));
|
||||
// Write these to their own files, then build the output
|
||||
fs.writeFileSync('package.json', JSON.stringify(package, null, 2));
|
||||
fs.writeFileSync('bower.json', JSON.stringify(bower, null, 2));
|
||||
|
||||
complete();
|
||||
});
|
||||
complete();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('release', ['build'], function(){
|
||||
exec('git tag -a v' + package.version);
|
||||
gulp.task('release', ['build'], function() {
|
||||
exec('git tag -a v' + package.version);
|
||||
});
|
||||
|
||||
gulp.task('jshint', function(){
|
||||
return gulp.src(srcDir + '*.js')
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter('default'));
|
||||
gulp.task('jshint', function() {
|
||||
return gulp.src(srcDir + '*.js')
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter('default'));
|
||||
});
|
||||
|
||||
gulp.task('valid', function(){
|
||||
return gulp.src('samples/*.html')
|
||||
.pipe(htmlv());
|
||||
gulp.task('valid', function() {
|
||||
return gulp.src('samples/*.html')
|
||||
.pipe(htmlv());
|
||||
});
|
||||
|
||||
gulp.task('library-size', function(){
|
||||
return gulp.src('Chart.min.js')
|
||||
.pipe(size({
|
||||
gzip: true
|
||||
}));
|
||||
gulp.task('library-size', function() {
|
||||
return gulp.src('Chart.min.js')
|
||||
.pipe(size({
|
||||
gzip: true
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('module-sizes', function(){
|
||||
return gulp.src(srcDir + '*.js')
|
||||
.pipe(uglify({preserveComments:'some'}))
|
||||
.pipe(size({
|
||||
showFiles: true,
|
||||
gzip: true
|
||||
}));
|
||||
gulp.task('module-sizes', function() {
|
||||
return gulp.src(srcDir + '*.js')
|
||||
.pipe(uglify({
|
||||
preserveComments: 'some'
|
||||
}))
|
||||
.pipe(size({
|
||||
showFiles: true,
|
||||
gzip: true
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('watch', function(){
|
||||
gulp.watch('./src/*', ['build']);
|
||||
|
||||
livereload.listen(35729);
|
||||
|
||||
var reloadPage = function (evt) {
|
||||
livereload.changed(evt.path);
|
||||
};
|
||||
|
||||
gulp.watch(['Chart.js', 'samples/*'], reloadPage);
|
||||
|
||||
gulp.task('watch', function() {
|
||||
gulp.watch('./src/**', ['build']);
|
||||
});
|
||||
|
||||
gulp.task('test', ['jshint', 'valid']);
|
||||
@@ -132,16 +125,16 @@ gulp.task('size', ['library-size', 'module-sizes']);
|
||||
|
||||
gulp.task('default', ['build', 'watch']);
|
||||
|
||||
gulp.task('server', function(){
|
||||
connect.server({
|
||||
port: 8000
|
||||
});
|
||||
gulp.task('server', function() {
|
||||
connect.server({
|
||||
port: 8000
|
||||
});
|
||||
});
|
||||
|
||||
// Convenience task for opening the project straight from the command line
|
||||
gulp.task('_open', function(){
|
||||
exec('open http://localhost:8000');
|
||||
exec('subl .');
|
||||
gulp.task('_open', function() {
|
||||
exec('open http://localhost:8000');
|
||||
exec('subl .');
|
||||
});
|
||||
|
||||
gulp.task('dev', ['server', 'default']);
|
||||
|
||||
+29
-28
@@ -1,30 +1,31 @@
|
||||
{
|
||||
"name": "chart.js",
|
||||
"homepage": "http://www.chartjs.org",
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"version": "1.1.0",
|
||||
"main": "Chart.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nnnick/Chart.js.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependences": {},
|
||||
"devDependencies": {
|
||||
"gulp": "3.9.x",
|
||||
"gulp-concat": "~2.1.x",
|
||||
"gulp-connect": "~2.0.5",
|
||||
"gulp-jshint": "~1.5.1",
|
||||
"gulp-replace": "^0.4.0",
|
||||
"gulp-size": "~0.4.0",
|
||||
"gulp-uglify": "~1.5.x",
|
||||
"gulp-util": "~2.2.x",
|
||||
"gulp-html-validator": "^0.0.2",
|
||||
"inquirer": "^0.5.1",
|
||||
"semver": "^3.0.1",
|
||||
"gulp-livereload": "^3.8.0"
|
||||
},
|
||||
"spm": {
|
||||
"main": "Chart.js"
|
||||
}
|
||||
"name": "chart.js",
|
||||
"homepage": "http://www.chartjs.org",
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"version": "2.0.0-alpha",
|
||||
"main": "Chart.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nnnick/Chart.js.git"
|
||||
},
|
||||
"dependences": {},
|
||||
"devDependencies": {
|
||||
"color": "git://github.com/chartjs/color",
|
||||
"gulp": "3.5.x",
|
||||
"gulp-concat": "~2.1.x",
|
||||
"gulp-connect": "~2.0.5",
|
||||
"gulp-html-validator": "^0.0.2",
|
||||
"gulp-jshint": "~1.5.1",
|
||||
"gulp-replace": "^0.4.0",
|
||||
"gulp-size": "~0.4.0",
|
||||
"gulp-uglify": "~0.2.x",
|
||||
"gulp-util": "~2.2.x",
|
||||
"inquirer": "^0.5.1",
|
||||
"jquery": "^2.1.4",
|
||||
"onecolor": "^2.5.0",
|
||||
"semver": "^3.0.1"
|
||||
},
|
||||
"spm": {
|
||||
"main": "Chart.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Bar Chart Multi Axis</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function() {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
yAxisID: "y-axis-1",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
yAxisID: "y-axis-2",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: [randomColor(), randomColor(), randomColor(), randomColor(), randomColor(), randomColor(), randomColor()],
|
||||
yAxisID: "y-axis-1",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
hoverAnimationDuration: 400,
|
||||
stacked: false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
}, {
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
id: "y-axis-2",
|
||||
gridLines: {
|
||||
drawOnChartArea: false
|
||||
}
|
||||
}],
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
if (Chart.helpers.isArray(dataset.backgroundColor)) {
|
||||
dataset.backgroundColor= [randomColor(), randomColor(), randomColor(), randomColor(), randomColor(), randomColor(), randomColor()];
|
||||
} else {
|
||||
dataset.backgroundColor= randomColor();
|
||||
}
|
||||
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
});
|
||||
window.myBar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,70 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Stacked Bar Chart</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
stacked: true,
|
||||
}],
|
||||
yAxes: [{
|
||||
stacked: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
});
|
||||
window.myBar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+55
-37
@@ -1,45 +1,63 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bar Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
|
||||
<head>
|
||||
<title>Bar Chart</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
var randomScalingFactor = function(){ return Math.round(Math.random()*100)};
|
||||
<body>
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
labels : ["January","February","March","April","May","June","July"],
|
||||
datasets : [
|
||||
{
|
||||
fillColor : "rgba(220,220,220,0.5)",
|
||||
strokeColor : "rgba(220,220,220,0.8)",
|
||||
highlightFill: "rgba(220,220,220,0.75)",
|
||||
highlightStroke: "rgba(220,220,220,1)",
|
||||
data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
|
||||
},
|
||||
{
|
||||
fillColor : "rgba(151,187,205,0.5)",
|
||||
strokeColor : "rgba(151,187,205,0.8)",
|
||||
highlightFill : "rgba(151,187,205,0.75)",
|
||||
highlightStroke : "rgba(151,187,205,1)",
|
||||
data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
|
||||
}
|
||||
]
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
|
||||
}
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx).Bar(barChartData, {
|
||||
responsive : true
|
||||
});
|
||||
}
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
var zero = Math.random() < 0.2 ? [0, 0, 0, 0, 0, 0, 0] : false;
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
dataset.data = zero || [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
});
|
||||
window.myBar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Bar Chart</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
borderColor: 'white',
|
||||
borderWidth: 2
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
borderColor: 'white',
|
||||
borderWidth: 2
|
||||
}, {
|
||||
type: 'line',
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, ]
|
||||
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
});
|
||||
window.myBar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -48,7 +48,12 @@
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myDoughnut = new Chart(ctx).Doughnut(doughnutData, {responsive : true});
|
||||
window.myDoughnut = new Chart(ctx).Doughnut({
|
||||
data: doughnutData,
|
||||
options: {
|
||||
responsive : true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
+103
-58
@@ -1,67 +1,112 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Doughnut Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#canvas-holder{
|
||||
width:30%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area" width="500" height="500"/>
|
||||
</div>
|
||||
|
||||
<head>
|
||||
<title>Doughnut Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#canvas-holder {
|
||||
width: 30%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
<body>
|
||||
<div id="canvas-holder" style="width:100%">
|
||||
<canvas id="chart-area" width="500" height="500" />
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var doughnutData = [
|
||||
{
|
||||
value: 300,
|
||||
color:"#F7464A",
|
||||
highlight: "#FF5A5E",
|
||||
label: "Red"
|
||||
},
|
||||
{
|
||||
value: 50,
|
||||
color: "#46BFBD",
|
||||
highlight: "#5AD3D1",
|
||||
label: "Green"
|
||||
},
|
||||
{
|
||||
value: 100,
|
||||
color: "#FDB45C",
|
||||
highlight: "#FFC870",
|
||||
label: "Yellow"
|
||||
},
|
||||
{
|
||||
value: 40,
|
||||
color: "#949FB1",
|
||||
highlight: "#A8B3C5",
|
||||
label: "Grey"
|
||||
},
|
||||
{
|
||||
value: 120,
|
||||
color: "#4D5360",
|
||||
highlight: "#616774",
|
||||
label: "Dark Grey"
|
||||
}
|
||||
var config = {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}, {
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}, {
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}],
|
||||
labels: [
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Grey",
|
||||
"Dark Grey"
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
];
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myDoughnut = new Chart(ctx, config);
|
||||
console.log(window.myDoughnut);
|
||||
};
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myDoughnut = new Chart(ctx).Doughnut(doughnutData, {responsive : true});
|
||||
};
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, piece) {
|
||||
$.each(piece.data, function(j, value) {
|
||||
config.data.datasets[i].data[j] = randomScalingFactor();
|
||||
});
|
||||
});
|
||||
window.myDoughnut.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -112,10 +112,13 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx1 = document.getElementById("chart1").getContext("2d");
|
||||
window.myLine = new Chart(ctx1).Line(lineChartData, {
|
||||
showScale: false,
|
||||
pointDot : true,
|
||||
responsive: true
|
||||
window.myLine = new Chart(ctx1).Line({
|
||||
data: lineChartData,
|
||||
options: {
|
||||
showScale: false,
|
||||
pointDot : true,
|
||||
responsive: true
|
||||
}
|
||||
});
|
||||
|
||||
var ctx2 = document.getElementById("chart2").getContext("2d");
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Line Chart Multiple Axes</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%;">
|
||||
<canvas id="canvas" style="width:100%;height:100%"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var lineChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
yAxisID: "y-axis-1",
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
yAxisID: "y-axis-2"
|
||||
}]
|
||||
};
|
||||
|
||||
$.each(lineChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(lineChartData);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx).Line({
|
||||
data: lineChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
stacked: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
gridLines: {
|
||||
offsetGridLines: false
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
}, {
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
id: "y-axis-2",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||
},
|
||||
}],
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
lineChartData.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
lineChartData.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
window.myLine.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+72
-45
@@ -1,54 +1,81 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width:30%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
var randomScalingFactor = function(){ return Math.round(Math.random()*100)};
|
||||
var lineChartData = {
|
||||
labels : ["January","February","March","April","May","June","July"],
|
||||
datasets : [
|
||||
{
|
||||
label: "My First dataset",
|
||||
fillColor : "rgba(220,220,220,0.2)",
|
||||
strokeColor : "rgba(220,220,220,1)",
|
||||
pointColor : "rgba(220,220,220,1)",
|
||||
pointStrokeColor : "#fff",
|
||||
pointHighlightFill : "#fff",
|
||||
pointHighlightStroke : "rgba(220,220,220,1)",
|
||||
data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
|
||||
},
|
||||
{
|
||||
label: "My Second dataset",
|
||||
fillColor : "rgba(151,187,205,0.2)",
|
||||
strokeColor : "rgba(151,187,205,1)",
|
||||
pointColor : "rgba(151,187,205,1)",
|
||||
pointStrokeColor : "#fff",
|
||||
pointHighlightFill : "#fff",
|
||||
pointHighlightStroke : "rgba(151,187,205,1)",
|
||||
data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
|
||||
}
|
||||
]
|
||||
<body>
|
||||
<div style="width:50%;">
|
||||
<canvas id="canvas" style="width:100%;height:100%"></canvas>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
}
|
||||
var config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true
|
||||
}],
|
||||
yAxes: [{
|
||||
display: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx).Line(lineChartData, {
|
||||
responsive: true
|
||||
});
|
||||
}
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx, config);
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
config.data.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
config.data.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
window.myLine.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -145,10 +145,14 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx1 = document.getElementById("chart-area1").getContext("2d");
|
||||
window.myPie = new Chart(ctx1).Pie(pieData);
|
||||
window.myPie = new Chart(ctx1).Pie({
|
||||
data: pieData
|
||||
});
|
||||
|
||||
var ctx2 = document.getElementById("chart-area2").getContext("2d");
|
||||
window.myPie = new Chart(ctx2).Pie(pieData);
|
||||
window.myPie = new Chart(ctx2).Pie({
|
||||
data: pieData
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
+93
-49
@@ -1,58 +1,102 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Pie Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area" width="300" height="300"/>
|
||||
</div>
|
||||
|
||||
<head>
|
||||
<title>Pie Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
<body>
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area" width="300" height="300" />
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var pieData = [
|
||||
{
|
||||
value: 300,
|
||||
color:"#F7464A",
|
||||
highlight: "#FF5A5E",
|
||||
label: "Red"
|
||||
},
|
||||
{
|
||||
value: 50,
|
||||
color: "#46BFBD",
|
||||
highlight: "#5AD3D1",
|
||||
label: "Green"
|
||||
},
|
||||
{
|
||||
value: 100,
|
||||
color: "#FDB45C",
|
||||
highlight: "#FFC870",
|
||||
label: "Yellow"
|
||||
},
|
||||
{
|
||||
value: 40,
|
||||
color: "#949FB1",
|
||||
highlight: "#A8B3C5",
|
||||
label: "Grey"
|
||||
},
|
||||
{
|
||||
value: 120,
|
||||
color: "#4D5360",
|
||||
highlight: "#616774",
|
||||
label: "Dark Grey"
|
||||
}
|
||||
var config = {
|
||||
type: 'pie',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}, {
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}, {
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}],
|
||||
labels: [
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Grey",
|
||||
"Dark Grey"
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
];
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPie = new Chart(ctx, config);
|
||||
};
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPie = new Chart(ctx).Pie(pieData);
|
||||
};
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, piece) {
|
||||
$.each(piece.data, function(j, value) {
|
||||
config.data.datasets[i].data[j] = randomScalingFactor();
|
||||
//config.data.datasets.backgroundColor[i] = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
});
|
||||
});
|
||||
window.myPie.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+63
-51
@@ -1,60 +1,72 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Polar Area Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-holder" style="width:30%">
|
||||
<canvas id="chart-area" width="300" height="300"/>
|
||||
</div>
|
||||
|
||||
<head>
|
||||
<title>Polar Area Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
<body>
|
||||
<div id="canvas-holder" style="width:100%">
|
||||
<canvas id="chart-area" width="300" height="300" />
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var polarData = [
|
||||
{
|
||||
value: 300,
|
||||
color:"#F7464A",
|
||||
highlight: "#FF5A5E",
|
||||
label: "Red"
|
||||
},
|
||||
{
|
||||
value: 50,
|
||||
color: "#46BFBD",
|
||||
highlight: "#5AD3D1",
|
||||
label: "Green"
|
||||
},
|
||||
{
|
||||
value: 100,
|
||||
color: "#FDB45C",
|
||||
highlight: "#FFC870",
|
||||
label: "Yellow"
|
||||
},
|
||||
{
|
||||
value: 40,
|
||||
color: "#949FB1",
|
||||
highlight: "#A8B3C5",
|
||||
label: "Grey"
|
||||
},
|
||||
{
|
||||
value: 120,
|
||||
color: "#4D5360",
|
||||
highlight: "#616774",
|
||||
label: "Dark Grey"
|
||||
}
|
||||
var config = {
|
||||
type: 'polarArea',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
],
|
||||
backgroundColor: [
|
||||
"#F7464A",
|
||||
"#46BFBD",
|
||||
"#FDB45C",
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
}],
|
||||
labels: [
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Grey",
|
||||
"Dark Grey"
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
];
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area");
|
||||
window.myPolarArea = new Chart(ctx, config);
|
||||
};
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPolarArea = new Chart(ctx).PolarArea(polarData, {
|
||||
responsive:true
|
||||
});
|
||||
};
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, piece) {
|
||||
$.each(piece.data, function(j, value) {
|
||||
config.data.datasets[i].data[j] = randomScalingFactor();
|
||||
//config.data.datasets.backgroundColor[i] = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
});
|
||||
});
|
||||
window.myPolarArea.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+52
-46
@@ -1,53 +1,59 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Radar Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<meta name = "viewport" content = "initial-scale = 1, user-scalable = no">
|
||||
<style>
|
||||
canvas{
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width:30%">
|
||||
<canvas id="canvas" height="450" width="450"></canvas>
|
||||
</div>
|
||||
|
||||
<head>
|
||||
<title>Radar Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
var radarChartData = {
|
||||
labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"],
|
||||
datasets: [
|
||||
{
|
||||
label: "My First dataset",
|
||||
fillColor: "rgba(220,220,220,0.2)",
|
||||
strokeColor: "rgba(220,220,220,1)",
|
||||
pointColor: "rgba(220,220,220,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(220,220,220,1)",
|
||||
data: [65,59,90,81,56,55,40]
|
||||
},
|
||||
{
|
||||
label: "My Second dataset",
|
||||
fillColor: "rgba(151,187,205,0.2)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||
data: [28,48,40,19,96,27,100]
|
||||
}
|
||||
]
|
||||
};
|
||||
<body>
|
||||
<div style="width:100%">
|
||||
<canvas id="canvas" height="450" width="450"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
window.onload = function(){
|
||||
window.myRadar = new Chart(document.getElementById("canvas").getContext("2d")).Radar(radarChartData, {
|
||||
responsive: true
|
||||
});
|
||||
}
|
||||
var config = {
|
||||
type: 'radar',
|
||||
data: {
|
||||
labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
backgroundColor: "rgba(220,220,220,0.2)",
|
||||
pointBackgroundColor: "rgba(220,220,220,1)",
|
||||
data: [null, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
backgroundColor: "rgba(151,187,205,0.2)",
|
||||
pointBackgroundColor: "rgba(151,187,205,1)",
|
||||
hoverPointBackgroundColor: "#fff",
|
||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||
data: [null, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
window.myRadar = new Chart(document.getElementById("canvas"), config);
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
config.data.datasets[0].backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.3)';
|
||||
config.data.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
config.data.datasets[1].backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.3)';
|
||||
config.data.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
window.myRadar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Scatter Chart Multi Axis</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var scatterChartData = {
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
xAxisID: "x-axis-1",
|
||||
yAxisID: "y-axis-1",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
xAxisID: "x-axis-1",
|
||||
yAxisID: "y-axis-2",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
$.each(scatterChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(scatterChartData);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myScatter = new Chart(ctx).Scatter({
|
||||
data: scatterChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'single',
|
||||
scales: {
|
||||
xAxes: [{
|
||||
position: "bottom",
|
||||
gridLines: {
|
||||
zeroLineColor: "rgba(0,0,0,1)"
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
}, {
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
id: "y-axis-2",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||
},
|
||||
}],
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
scatterChartData.datasets[0].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}];
|
||||
scatterChartData.datasets[1].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
window.myScatter.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,155 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Scatter Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var scatterChartData = {
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
$.each(scatterChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(scatterChartData);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myScatter = new Chart(ctx).Scatter({
|
||||
data: scatterChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'single', // should always use single for a scatter chart
|
||||
scales: {
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
zeroLineColor: "rgba(0,0,0,1)"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
scatterChartData.datasets[0].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}];
|
||||
scatterChartData.datasets[1].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
window.myScatter.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,305 +0,0 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
var defaultConfig = {
|
||||
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
||||
scaleBeginAtZero : true,
|
||||
|
||||
//Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines : true,
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor : "rgba(0,0,0,.05)",
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth : 1,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
|
||||
//Boolean - If there is a stroke on each bar
|
||||
barShowStroke : true,
|
||||
|
||||
//Number - Pixel width of the bar stroke
|
||||
barStrokeWidth : 2,
|
||||
|
||||
//Number - Spacing between each of the X value sets
|
||||
barValueSpacing : 5,
|
||||
|
||||
//Number - Spacing between data sets within X values
|
||||
barDatasetSpacing : 1,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
|
||||
|
||||
};
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
name: "Bar",
|
||||
defaults : defaultConfig,
|
||||
initialize: function(data){
|
||||
|
||||
//Expose options as a scope variable here so we can access it in the ScaleClass
|
||||
var options = this.options;
|
||||
|
||||
this.ScaleClass = Chart.Scale.extend({
|
||||
offsetGridLines : true,
|
||||
calculateBarX : function(datasetCount, datasetIndex, barIndex){
|
||||
//Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
|
||||
var xWidth = this.calculateBaseWidth(),
|
||||
xAbsolute = this.calculateX(barIndex) - (xWidth/2),
|
||||
barWidth = this.calculateBarWidth(datasetCount);
|
||||
|
||||
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
|
||||
},
|
||||
calculateBaseWidth : function(){
|
||||
return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
|
||||
},
|
||||
calculateBarWidth : function(datasetCount){
|
||||
//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
|
||||
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
|
||||
|
||||
return (baseWidth / datasetCount);
|
||||
}
|
||||
});
|
||||
|
||||
this.datasets = [];
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips){
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
|
||||
var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
|
||||
|
||||
this.eachBars(function(bar){
|
||||
bar.restore(['fillColor', 'strokeColor']);
|
||||
});
|
||||
helpers.each(activeBars, function(activeBar){
|
||||
if (activeBar) {
|
||||
activeBar.fillColor = activeBar.highlightFill;
|
||||
activeBar.strokeColor = activeBar.highlightStroke;
|
||||
}
|
||||
});
|
||||
this.showTooltip(activeBars);
|
||||
});
|
||||
}
|
||||
|
||||
//Declare the extension of the default point, to cater for the options passed in to the constructor
|
||||
this.BarClass = Chart.Rectangle.extend({
|
||||
strokeWidth : this.options.barStrokeWidth,
|
||||
showStroke : this.options.barShowStroke,
|
||||
ctx : this.chart.ctx
|
||||
});
|
||||
|
||||
//Iterate through each of the datasets, and build this into a property of the chart
|
||||
helpers.each(data.datasets,function(dataset,datasetIndex){
|
||||
|
||||
var datasetObject = {
|
||||
label : dataset.label || null,
|
||||
fillColor : dataset.fillColor,
|
||||
strokeColor : dataset.strokeColor,
|
||||
bars : []
|
||||
};
|
||||
|
||||
this.datasets.push(datasetObject);
|
||||
|
||||
helpers.each(dataset.data,function(dataPoint,index){
|
||||
//Add a new point for each piece of data, passing any required data to draw.
|
||||
datasetObject.bars.push(new this.BarClass({
|
||||
value : dataPoint,
|
||||
label : data.labels[index],
|
||||
datasetLabel: dataset.label,
|
||||
strokeColor : (typeof dataset.strokeColor != 'string') ? dataset.strokeColor[index] : dataset.strokeColor,
|
||||
fillColor : (typeof dataset.fillColor != 'string') ? dataset.fillColor[index] : dataset.fillColor,
|
||||
highlightFill : (dataset.highlightFill) ? (typeof dataset.highlightFill != 'string') ? dataset.highlightFill[index] : dataset.highlightFill : (typeof dataset.fillColor != 'string') ? dataset.fillColor[index] : dataset.fillColor,
|
||||
highlightStroke : (dataset.highlightStroke) ? (typeof dataset.highlightStroke != 'string') ? dataset.highlightStroke[index] : dataset.highlightStroke : (typeof dataset.strokeColor != 'string') ? dataset.strokeColor[index] : dataset.strokeColor
|
||||
}));
|
||||
},this);
|
||||
|
||||
},this);
|
||||
|
||||
this.buildScale(data.labels);
|
||||
|
||||
this.BarClass.prototype.base = this.scale.endPoint;
|
||||
|
||||
this.eachBars(function(bar, index, datasetIndex){
|
||||
helpers.extend(bar, {
|
||||
width : this.scale.calculateBarWidth(this.datasets.length),
|
||||
x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
|
||||
y: this.scale.endPoint
|
||||
});
|
||||
bar.save();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
update : function(){
|
||||
this.scale.update();
|
||||
// Reset any highlight colours before updating.
|
||||
helpers.each(this.activeElements, function(activeElement){
|
||||
activeElement.restore(['fillColor', 'strokeColor']);
|
||||
});
|
||||
|
||||
this.eachBars(function(bar){
|
||||
bar.save();
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
eachBars : function(callback){
|
||||
helpers.each(this.datasets,function(dataset, datasetIndex){
|
||||
helpers.each(dataset.bars, callback, this, datasetIndex);
|
||||
},this);
|
||||
},
|
||||
getBarsAtEvent : function(e){
|
||||
var barsArray = [],
|
||||
eventPosition = helpers.getRelativePosition(e),
|
||||
datasetIterator = function(dataset){
|
||||
barsArray.push(dataset.bars[barIndex]);
|
||||
},
|
||||
barIndex;
|
||||
|
||||
for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
|
||||
for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
|
||||
if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
|
||||
helpers.each(this.datasets, datasetIterator);
|
||||
return barsArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return barsArray;
|
||||
},
|
||||
buildScale : function(labels){
|
||||
var self = this;
|
||||
|
||||
var dataTotal = function(){
|
||||
var values = [];
|
||||
self.eachBars(function(bar){
|
||||
values.push(bar.value);
|
||||
});
|
||||
return values;
|
||||
};
|
||||
|
||||
var scaleOptions = {
|
||||
templateString : this.options.scaleLabel,
|
||||
height : this.chart.height,
|
||||
width : this.chart.width,
|
||||
ctx : this.chart.ctx,
|
||||
textColor : this.options.scaleFontColor,
|
||||
fontSize : this.options.scaleFontSize,
|
||||
fontStyle : this.options.scaleFontStyle,
|
||||
fontFamily : this.options.scaleFontFamily,
|
||||
valuesCount : labels.length,
|
||||
beginAtZero : this.options.scaleBeginAtZero,
|
||||
integersOnly : this.options.scaleIntegersOnly,
|
||||
calculateYRange: function(currentHeight){
|
||||
var updatedRanges = helpers.calculateScaleRange(
|
||||
dataTotal(),
|
||||
currentHeight,
|
||||
this.fontSize,
|
||||
this.beginAtZero,
|
||||
this.integersOnly
|
||||
);
|
||||
helpers.extend(this, updatedRanges);
|
||||
},
|
||||
xLabels : labels,
|
||||
font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
|
||||
lineWidth : this.options.scaleLineWidth,
|
||||
lineColor : this.options.scaleLineColor,
|
||||
showHorizontalLines : this.options.scaleShowHorizontalLines,
|
||||
showVerticalLines : this.options.scaleShowVerticalLines,
|
||||
gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
|
||||
gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
|
||||
padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
|
||||
showLabels : this.options.scaleShowLabels,
|
||||
display : this.options.showScale
|
||||
};
|
||||
|
||||
if (this.options.scaleOverride){
|
||||
helpers.extend(scaleOptions, {
|
||||
calculateYRange: helpers.noop,
|
||||
steps: this.options.scaleSteps,
|
||||
stepValue: this.options.scaleStepWidth,
|
||||
min: this.options.scaleStartValue,
|
||||
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
||||
});
|
||||
}
|
||||
|
||||
this.scale = new this.ScaleClass(scaleOptions);
|
||||
},
|
||||
addData : function(valuesArray,label){
|
||||
//Map the values array for each of the datasets
|
||||
helpers.each(valuesArray,function(value,datasetIndex){
|
||||
//Add a new point for each piece of data, passing any required data to draw.
|
||||
this.datasets[datasetIndex].bars.push(new this.BarClass({
|
||||
value : value,
|
||||
label : label,
|
||||
datasetLabel: this.datasets[datasetIndex].label,
|
||||
x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
|
||||
y: this.scale.endPoint,
|
||||
width : this.scale.calculateBarWidth(this.datasets.length),
|
||||
base : this.scale.endPoint,
|
||||
strokeColor : this.datasets[datasetIndex].strokeColor,
|
||||
fillColor : this.datasets[datasetIndex].fillColor
|
||||
}));
|
||||
},this);
|
||||
|
||||
this.scale.addXLabel(label);
|
||||
//Then re-render the chart.
|
||||
this.update();
|
||||
},
|
||||
removeData : function(){
|
||||
this.scale.removeXLabel();
|
||||
//Then re-render the chart.
|
||||
helpers.each(this.datasets,function(dataset){
|
||||
dataset.bars.shift();
|
||||
},this);
|
||||
this.update();
|
||||
},
|
||||
reflow : function(){
|
||||
helpers.extend(this.BarClass.prototype,{
|
||||
y: this.scale.endPoint,
|
||||
base : this.scale.endPoint
|
||||
});
|
||||
var newScaleProps = helpers.extend({
|
||||
height : this.chart.height,
|
||||
width : this.chart.width
|
||||
});
|
||||
this.scale.update(newScaleProps);
|
||||
},
|
||||
draw : function(ease){
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
var ctx = this.chart.ctx;
|
||||
|
||||
this.scale.draw(easingDecimal);
|
||||
|
||||
//Draw all the bars for each dataset
|
||||
helpers.each(this.datasets,function(dataset,datasetIndex){
|
||||
helpers.each(dataset.bars,function(bar,index){
|
||||
if (bar.hasValue()){
|
||||
bar.base = this.scale.endPoint;
|
||||
//Transition then draw
|
||||
bar.transition({
|
||||
x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
|
||||
y : this.scale.calculateY(bar.value),
|
||||
width : this.scale.calculateBarWidth(this.datasets.length)
|
||||
}, easingDecimal).draw();
|
||||
}
|
||||
},this);
|
||||
|
||||
},this);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
-2251
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -1,194 +0,0 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
//Cache a local reference to Chart.helpers
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
//Boolean - Whether we should show a stroke on each segment
|
||||
segmentShowStroke : true,
|
||||
|
||||
//String - The colour of each segment stroke
|
||||
segmentStrokeColor : "#fff",
|
||||
|
||||
//Number - The width of each segment stroke
|
||||
segmentStrokeWidth : 2,
|
||||
|
||||
//The percentage of the chart that we cut out of the middle.
|
||||
percentageInnerCutout : 50,
|
||||
|
||||
//Number - Amount of animation steps
|
||||
animationSteps : 100,
|
||||
|
||||
//String - Animation easing effect
|
||||
animationEasing : "easeOutBounce",
|
||||
|
||||
//Boolean - Whether we animate the rotation of the Doughnut
|
||||
animateRotate : true,
|
||||
|
||||
//Boolean - Whether we animate scaling the Doughnut from the centre
|
||||
animateScale : false,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
|
||||
|
||||
};
|
||||
|
||||
Chart.Type.extend({
|
||||
//Passing in a name registers this chart in the Chart namespace
|
||||
name: "Doughnut",
|
||||
//Providing a defaults will also register the defaults in the chart namespace
|
||||
defaults : defaultConfig,
|
||||
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
|
||||
//Config is automatically merged by the core of Chart.js, and is available at this.options
|
||||
initialize: function(data){
|
||||
|
||||
//Declare segments as a static property to prevent inheriting across the Chart type prototype
|
||||
this.segments = [];
|
||||
this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
|
||||
|
||||
this.SegmentArc = Chart.Arc.extend({
|
||||
ctx : this.chart.ctx,
|
||||
x : this.chart.width/2,
|
||||
y : this.chart.height/2
|
||||
});
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips){
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
|
||||
var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
|
||||
|
||||
helpers.each(this.segments,function(segment){
|
||||
segment.restore(["fillColor"]);
|
||||
});
|
||||
helpers.each(activeSegments,function(activeSegment){
|
||||
activeSegment.fillColor = activeSegment.highlightColor;
|
||||
});
|
||||
this.showTooltip(activeSegments);
|
||||
});
|
||||
}
|
||||
this.calculateTotal(data);
|
||||
|
||||
helpers.each(data,function(datapoint, index){
|
||||
if (!datapoint.color) {
|
||||
datapoint.color = 'hsl(' + (360 * index / data.length) + ', 100%, 50%)';
|
||||
}
|
||||
this.addData(datapoint, index, true);
|
||||
},this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
getSegmentsAtEvent : function(e){
|
||||
var segmentsArray = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
helpers.each(this.segments,function(segment){
|
||||
if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
|
||||
},this);
|
||||
return segmentsArray;
|
||||
},
|
||||
addData : function(segment, atIndex, silent){
|
||||
var index = atIndex !== undefined ? atIndex : this.segments.length;
|
||||
if ( typeof(segment.color) === "undefined" ) {
|
||||
segment.color = Chart.defaults.global.segmentColorDefault[index % Chart.defaults.global.segmentColorDefault.length];
|
||||
segment.highlight = Chart.defaults.global.segmentHighlightColorDefaults[index % Chart.defaults.global.segmentHighlightColorDefaults.length];
|
||||
}
|
||||
this.segments.splice(index, 0, new this.SegmentArc({
|
||||
value : segment.value,
|
||||
outerRadius : (this.options.animateScale) ? 0 : this.outerRadius,
|
||||
innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout,
|
||||
fillColor : segment.color,
|
||||
highlightColor : segment.highlight || segment.color,
|
||||
showStroke : this.options.segmentShowStroke,
|
||||
strokeWidth : this.options.segmentStrokeWidth,
|
||||
strokeColor : this.options.segmentStrokeColor,
|
||||
startAngle : Math.PI * 1.5,
|
||||
circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),
|
||||
label : segment.label
|
||||
}));
|
||||
if (!silent){
|
||||
this.reflow();
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
calculateCircumference : function(value) {
|
||||
if ( this.total > 0 ) {
|
||||
return (Math.PI*2)*(value / this.total);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
calculateTotal : function(data){
|
||||
this.total = 0;
|
||||
helpers.each(data,function(segment){
|
||||
this.total += Math.abs(segment.value);
|
||||
},this);
|
||||
},
|
||||
update : function(){
|
||||
this.calculateTotal(this.segments);
|
||||
|
||||
// Reset any highlight colours before updating.
|
||||
helpers.each(this.activeElements, function(activeElement){
|
||||
activeElement.restore(['fillColor']);
|
||||
});
|
||||
|
||||
helpers.each(this.segments,function(segment){
|
||||
segment.save();
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
|
||||
removeData: function(atIndex){
|
||||
var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
|
||||
this.segments.splice(indexToDelete, 1);
|
||||
this.reflow();
|
||||
this.update();
|
||||
},
|
||||
|
||||
reflow : function(){
|
||||
helpers.extend(this.SegmentArc.prototype,{
|
||||
x : this.chart.width/2,
|
||||
y : this.chart.height/2
|
||||
});
|
||||
this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
|
||||
helpers.each(this.segments, function(segment){
|
||||
segment.update({
|
||||
outerRadius : this.outerRadius,
|
||||
innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
draw : function(easeDecimal){
|
||||
var animDecimal = (easeDecimal) ? easeDecimal : 1;
|
||||
this.clear();
|
||||
helpers.each(this.segments,function(segment,index){
|
||||
segment.transition({
|
||||
circumference : this.calculateCircumference(segment.value),
|
||||
outerRadius : this.outerRadius,
|
||||
innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
|
||||
},animDecimal);
|
||||
|
||||
segment.endAngle = segment.startAngle + segment.circumference;
|
||||
|
||||
segment.draw();
|
||||
if (index === 0){
|
||||
segment.startAngle = Math.PI * 1.5;
|
||||
}
|
||||
//Check to see if it's the last segment, if not get the next and update the start angle
|
||||
if (index < this.segments.length-1){
|
||||
this.segments[index+1].startAngle = segment.endAngle;
|
||||
}
|
||||
},this);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
Chart.types.Doughnut.extend({
|
||||
name : "Pie",
|
||||
defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0})
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -1,383 +0,0 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
///Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines : true,
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor : "rgba(0,0,0,.05)",
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth : 1,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
|
||||
//Boolean - Whether the line is curved between points
|
||||
bezierCurve : true,
|
||||
|
||||
//Number - Tension of the bezier curve between points
|
||||
bezierCurveTension : 0.4,
|
||||
|
||||
//Boolean - Whether to show a dot for each point
|
||||
pointDot : true,
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointDotRadius : 4,
|
||||
|
||||
//Number - Pixel width of point dot stroke
|
||||
pointDotStrokeWidth : 1,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
pointHitDetectionRadius : 20,
|
||||
|
||||
//Boolean - Whether to show a stroke for datasets
|
||||
datasetStroke : true,
|
||||
|
||||
//Number - Pixel width of dataset stroke
|
||||
datasetStrokeWidth : 2,
|
||||
|
||||
//Boolean - Whether to fill the dataset with a colour
|
||||
datasetFill : true,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>",
|
||||
|
||||
//Boolean - Whether to horizontally center the label and point dot inside the grid
|
||||
offsetGridLines : false
|
||||
|
||||
};
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
name: "Line",
|
||||
defaults : defaultConfig,
|
||||
initialize: function(data){
|
||||
//Declare the extension of the default point, to cater for the options passed in to the constructor
|
||||
this.PointClass = Chart.Point.extend({
|
||||
offsetGridLines : this.options.offsetGridLines,
|
||||
strokeWidth : this.options.pointDotStrokeWidth,
|
||||
radius : this.options.pointDotRadius,
|
||||
display: this.options.pointDot,
|
||||
hitDetectionRadius : this.options.pointHitDetectionRadius,
|
||||
ctx : this.chart.ctx,
|
||||
inRange : function(mouseX){
|
||||
return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
|
||||
}
|
||||
});
|
||||
|
||||
this.datasets = [];
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips){
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
|
||||
var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
|
||||
this.eachPoints(function(point){
|
||||
point.restore(['fillColor', 'strokeColor']);
|
||||
});
|
||||
helpers.each(activePoints, function(activePoint){
|
||||
activePoint.fillColor = activePoint.highlightFill;
|
||||
activePoint.strokeColor = activePoint.highlightStroke;
|
||||
});
|
||||
this.showTooltip(activePoints);
|
||||
});
|
||||
}
|
||||
|
||||
//Iterate through each of the datasets, and build this into a property of the chart
|
||||
helpers.each(data.datasets,function(dataset){
|
||||
|
||||
var datasetObject = {
|
||||
label : dataset.label || null,
|
||||
fillColor : dataset.fillColor,
|
||||
strokeColor : dataset.strokeColor,
|
||||
pointColor : dataset.pointColor,
|
||||
pointStrokeColor : dataset.pointStrokeColor,
|
||||
points : []
|
||||
};
|
||||
|
||||
this.datasets.push(datasetObject);
|
||||
|
||||
|
||||
helpers.each(dataset.data,function(dataPoint,index){
|
||||
//Add a new point for each piece of data, passing any required data to draw.
|
||||
datasetObject.points.push(new this.PointClass({
|
||||
value : dataPoint,
|
||||
label : data.labels[index],
|
||||
datasetLabel: dataset.label,
|
||||
strokeColor : dataset.pointStrokeColor,
|
||||
fillColor : dataset.pointColor,
|
||||
highlightFill : dataset.pointHighlightFill || dataset.pointColor,
|
||||
highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
|
||||
}));
|
||||
},this);
|
||||
|
||||
this.buildScale(data.labels);
|
||||
|
||||
|
||||
this.eachPoints(function(point, index){
|
||||
helpers.extend(point, {
|
||||
x: this.scale.calculateX(index),
|
||||
y: this.scale.endPoint
|
||||
});
|
||||
point.save();
|
||||
}, this);
|
||||
|
||||
},this);
|
||||
|
||||
|
||||
this.render();
|
||||
},
|
||||
update : function(){
|
||||
this.scale.update();
|
||||
// Reset any highlight colours before updating.
|
||||
helpers.each(this.activeElements, function(activeElement){
|
||||
activeElement.restore(['fillColor', 'strokeColor']);
|
||||
});
|
||||
this.eachPoints(function(point){
|
||||
point.save();
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
eachPoints : function(callback){
|
||||
helpers.each(this.datasets,function(dataset){
|
||||
helpers.each(dataset.points,callback,this);
|
||||
},this);
|
||||
},
|
||||
getPointsAtEvent : function(e){
|
||||
var pointsArray = [],
|
||||
eventPosition = helpers.getRelativePosition(e);
|
||||
helpers.each(this.datasets,function(dataset){
|
||||
helpers.each(dataset.points,function(point){
|
||||
if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);
|
||||
});
|
||||
},this);
|
||||
return pointsArray;
|
||||
},
|
||||
buildScale : function(labels){
|
||||
var self = this;
|
||||
|
||||
var dataTotal = function(){
|
||||
var values = [];
|
||||
self.eachPoints(function(point){
|
||||
values.push(point.value);
|
||||
});
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
var scaleOptions = {
|
||||
templateString : this.options.scaleLabel,
|
||||
height : this.chart.height,
|
||||
width : this.chart.width,
|
||||
ctx : this.chart.ctx,
|
||||
textColor : this.options.scaleFontColor,
|
||||
offsetGridLines : this.options.offsetGridLines,
|
||||
fontSize : this.options.scaleFontSize,
|
||||
fontStyle : this.options.scaleFontStyle,
|
||||
fontFamily : this.options.scaleFontFamily,
|
||||
valuesCount : labels.length,
|
||||
beginAtZero : this.options.scaleBeginAtZero,
|
||||
integersOnly : this.options.scaleIntegersOnly,
|
||||
calculateYRange : function(currentHeight){
|
||||
var updatedRanges = helpers.calculateScaleRange(
|
||||
dataTotal(),
|
||||
currentHeight,
|
||||
this.fontSize,
|
||||
this.beginAtZero,
|
||||
this.integersOnly
|
||||
);
|
||||
helpers.extend(this, updatedRanges);
|
||||
},
|
||||
xLabels : labels,
|
||||
font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
|
||||
lineWidth : this.options.scaleLineWidth,
|
||||
lineColor : this.options.scaleLineColor,
|
||||
showHorizontalLines : this.options.scaleShowHorizontalLines,
|
||||
showVerticalLines : this.options.scaleShowVerticalLines,
|
||||
gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
|
||||
gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
|
||||
padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
|
||||
showLabels : this.options.scaleShowLabels,
|
||||
display : this.options.showScale
|
||||
};
|
||||
|
||||
if (this.options.scaleOverride){
|
||||
helpers.extend(scaleOptions, {
|
||||
calculateYRange: helpers.noop,
|
||||
steps: this.options.scaleSteps,
|
||||
stepValue: this.options.scaleStepWidth,
|
||||
min: this.options.scaleStartValue,
|
||||
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.scale = new Chart.Scale(scaleOptions);
|
||||
},
|
||||
addData : function(valuesArray,label){
|
||||
//Map the values array for each of the datasets
|
||||
|
||||
helpers.each(valuesArray,function(value,datasetIndex){
|
||||
//Add a new point for each piece of data, passing any required data to draw.
|
||||
this.datasets[datasetIndex].points.push(new this.PointClass({
|
||||
value : value,
|
||||
label : label,
|
||||
datasetLabel: this.datasets[datasetIndex].label,
|
||||
x: this.scale.calculateX(this.scale.valuesCount+1),
|
||||
y: this.scale.endPoint,
|
||||
strokeColor : this.datasets[datasetIndex].pointStrokeColor,
|
||||
fillColor : this.datasets[datasetIndex].pointColor
|
||||
}));
|
||||
},this);
|
||||
|
||||
this.scale.addXLabel(label);
|
||||
//Then re-render the chart.
|
||||
this.update();
|
||||
},
|
||||
removeData : function(){
|
||||
this.scale.removeXLabel();
|
||||
//Then re-render the chart.
|
||||
helpers.each(this.datasets,function(dataset){
|
||||
dataset.points.shift();
|
||||
},this);
|
||||
this.update();
|
||||
},
|
||||
reflow : function(){
|
||||
var newScaleProps = helpers.extend({
|
||||
height : this.chart.height,
|
||||
width : this.chart.width
|
||||
});
|
||||
this.scale.update(newScaleProps);
|
||||
},
|
||||
draw : function(ease){
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
var ctx = this.chart.ctx;
|
||||
|
||||
// Some helper methods for getting the next/prev points
|
||||
var hasValue = function(item){
|
||||
return item.value !== null;
|
||||
},
|
||||
nextPoint = function(point, collection, index){
|
||||
return helpers.findNextWhere(collection, hasValue, index) || point;
|
||||
},
|
||||
previousPoint = function(point, collection, index){
|
||||
return helpers.findPreviousWhere(collection, hasValue, index) || point;
|
||||
};
|
||||
|
||||
if (!this.scale) return;
|
||||
this.scale.draw(easingDecimal);
|
||||
|
||||
|
||||
helpers.each(this.datasets,function(dataset){
|
||||
var pointsWithValues = helpers.where(dataset.points, hasValue);
|
||||
|
||||
//Transition each point first so that the line and point drawing isn't out of sync
|
||||
//We can use this extra loop to calculate the control points of this dataset also in this loop
|
||||
|
||||
helpers.each(dataset.points, function(point, index){
|
||||
if (point.hasValue()){
|
||||
point.transition({
|
||||
y : this.scale.calculateY(point.value),
|
||||
x : this.scale.calculateX(index)
|
||||
}, easingDecimal);
|
||||
}
|
||||
},this);
|
||||
|
||||
|
||||
// Control points need to be calculated in a separate loop, because we need to know the current x/y of the point
|
||||
// This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
|
||||
if (this.options.bezierCurve){
|
||||
helpers.each(pointsWithValues, function(point, index){
|
||||
var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;
|
||||
point.controlPoints = helpers.splineCurve(
|
||||
previousPoint(point, pointsWithValues, index),
|
||||
point,
|
||||
nextPoint(point, pointsWithValues, index),
|
||||
tension
|
||||
);
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (point.controlPoints.outer.y > this.scale.endPoint){
|
||||
point.controlPoints.outer.y = this.scale.endPoint;
|
||||
}
|
||||
else if (point.controlPoints.outer.y < this.scale.startPoint){
|
||||
point.controlPoints.outer.y = this.scale.startPoint;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (point.controlPoints.inner.y > this.scale.endPoint){
|
||||
point.controlPoints.inner.y = this.scale.endPoint;
|
||||
}
|
||||
else if (point.controlPoints.inner.y < this.scale.startPoint){
|
||||
point.controlPoints.inner.y = this.scale.startPoint;
|
||||
}
|
||||
},this);
|
||||
}
|
||||
|
||||
|
||||
//Draw the line between all the points
|
||||
ctx.lineWidth = this.options.datasetStrokeWidth;
|
||||
ctx.strokeStyle = dataset.strokeColor;
|
||||
ctx.beginPath();
|
||||
|
||||
helpers.each(pointsWithValues, function(point, index){
|
||||
if (index === 0){
|
||||
ctx.moveTo(point.x, point.y);
|
||||
}
|
||||
else{
|
||||
if(this.options.bezierCurve){
|
||||
var previous = previousPoint(point, pointsWithValues, index);
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
previous.controlPoints.outer.x,
|
||||
previous.controlPoints.outer.y,
|
||||
point.controlPoints.inner.x,
|
||||
point.controlPoints.inner.y,
|
||||
point.x,
|
||||
point.y
|
||||
);
|
||||
}
|
||||
else{
|
||||
ctx.lineTo(point.x,point.y);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (this.options.datasetStroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
if (this.options.datasetFill && pointsWithValues.length > 0){
|
||||
//Round off the line by going to the base of the chart, back to the start, then fill.
|
||||
ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);
|
||||
ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);
|
||||
ctx.fillStyle = dataset.fillColor;
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
//Now draw the points over the line
|
||||
//A little inefficient double looping, but better than the line
|
||||
//lagging behind the point positions
|
||||
helpers.each(pointsWithValues,function(point){
|
||||
point.draw();
|
||||
});
|
||||
},this);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,250 +0,0 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
//Cache a local reference to Chart.helpers
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
scaleShowLabelBackdrop : true,
|
||||
|
||||
//String - The colour of the label backdrop
|
||||
scaleBackdropColor : "rgba(255,255,255,0.75)",
|
||||
|
||||
// Boolean - Whether the scale should begin at zero
|
||||
scaleBeginAtZero : true,
|
||||
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
scaleBackdropPaddingY : 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
scaleBackdropPaddingX : 2,
|
||||
|
||||
//Boolean - Show line for each value in the scale
|
||||
scaleShowLine : true,
|
||||
|
||||
//Boolean - Stroke a line around each segment in the chart
|
||||
segmentShowStroke : true,
|
||||
|
||||
//String - The colour of the stroke on each segment.
|
||||
segmentStrokeColor : "#fff",
|
||||
|
||||
//Number - The width of the stroke value in pixels
|
||||
segmentStrokeWidth : 2,
|
||||
|
||||
//Number - Amount of animation steps
|
||||
animationSteps : 100,
|
||||
|
||||
//String - Animation easing effect.
|
||||
animationEasing : "easeOutBounce",
|
||||
|
||||
//Boolean - Whether to animate the rotation of the chart
|
||||
animateRotate : true,
|
||||
|
||||
//Boolean - Whether to animate scaling the chart from the centre
|
||||
animateScale : false,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
|
||||
};
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
//Passing in a name registers this chart in the Chart namespace
|
||||
name: "PolarArea",
|
||||
//Providing a defaults will also register the defaults in the chart namespace
|
||||
defaults : defaultConfig,
|
||||
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
|
||||
//Config is automatically merged by the core of Chart.js, and is available at this.options
|
||||
initialize: function(data){
|
||||
this.segments = [];
|
||||
//Declare segment class as a chart instance specific class, so it can share props for this instance
|
||||
this.SegmentArc = Chart.Arc.extend({
|
||||
showStroke : this.options.segmentShowStroke,
|
||||
strokeWidth : this.options.segmentStrokeWidth,
|
||||
strokeColor : this.options.segmentStrokeColor,
|
||||
ctx : this.chart.ctx,
|
||||
innerRadius : 0,
|
||||
x : this.chart.width/2,
|
||||
y : this.chart.height/2
|
||||
});
|
||||
this.scale = new Chart.RadialScale({
|
||||
display: this.options.showScale,
|
||||
fontStyle: this.options.scaleFontStyle,
|
||||
fontSize: this.options.scaleFontSize,
|
||||
fontFamily: this.options.scaleFontFamily,
|
||||
fontColor: this.options.scaleFontColor,
|
||||
showLabels: this.options.scaleShowLabels,
|
||||
showLabelBackdrop: this.options.scaleShowLabelBackdrop,
|
||||
backdropColor: this.options.scaleBackdropColor,
|
||||
backdropPaddingY : this.options.scaleBackdropPaddingY,
|
||||
backdropPaddingX: this.options.scaleBackdropPaddingX,
|
||||
lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
|
||||
lineColor: this.options.scaleLineColor,
|
||||
lineArc: true,
|
||||
width: this.chart.width,
|
||||
height: this.chart.height,
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2,
|
||||
ctx : this.chart.ctx,
|
||||
templateString: this.options.scaleLabel,
|
||||
valuesCount: data.length
|
||||
});
|
||||
|
||||
this.updateScaleRange(data);
|
||||
|
||||
this.scale.update();
|
||||
|
||||
helpers.each(data,function(segment,index){
|
||||
this.addData(segment,index,true);
|
||||
},this);
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips){
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
|
||||
var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
|
||||
helpers.each(this.segments,function(segment){
|
||||
segment.restore(["fillColor"]);
|
||||
});
|
||||
helpers.each(activeSegments,function(activeSegment){
|
||||
activeSegment.fillColor = activeSegment.highlightColor;
|
||||
});
|
||||
this.showTooltip(activeSegments);
|
||||
});
|
||||
}
|
||||
|
||||
this.render();
|
||||
},
|
||||
getSegmentsAtEvent : function(e){
|
||||
var segmentsArray = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
helpers.each(this.segments,function(segment){
|
||||
if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
|
||||
},this);
|
||||
return segmentsArray;
|
||||
},
|
||||
addData : function(segment, atIndex, silent){
|
||||
var index = atIndex || this.segments.length;
|
||||
|
||||
this.segments.splice(index, 0, new this.SegmentArc({
|
||||
fillColor: segment.color,
|
||||
highlightColor: segment.highlight || segment.color,
|
||||
label: segment.label,
|
||||
value: segment.value,
|
||||
outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value),
|
||||
circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(),
|
||||
startAngle: Math.PI * 1.5
|
||||
}));
|
||||
if (!silent){
|
||||
this.reflow();
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
removeData: function(atIndex){
|
||||
var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
|
||||
this.segments.splice(indexToDelete, 1);
|
||||
this.reflow();
|
||||
this.update();
|
||||
},
|
||||
calculateTotal: function(data){
|
||||
this.total = 0;
|
||||
helpers.each(data,function(segment){
|
||||
this.total += segment.value;
|
||||
},this);
|
||||
this.scale.valuesCount = this.segments.length;
|
||||
},
|
||||
updateScaleRange: function(datapoints){
|
||||
var valuesArray = [];
|
||||
helpers.each(datapoints,function(segment){
|
||||
valuesArray.push(segment.value);
|
||||
});
|
||||
|
||||
var scaleSizes = (this.options.scaleOverride) ?
|
||||
{
|
||||
steps: this.options.scaleSteps,
|
||||
stepValue: this.options.scaleStepWidth,
|
||||
min: this.options.scaleStartValue,
|
||||
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
||||
} :
|
||||
helpers.calculateScaleRange(
|
||||
valuesArray,
|
||||
helpers.min([this.chart.width, this.chart.height])/2,
|
||||
this.options.scaleFontSize,
|
||||
this.options.scaleBeginAtZero,
|
||||
this.options.scaleIntegersOnly
|
||||
);
|
||||
|
||||
helpers.extend(
|
||||
this.scale,
|
||||
scaleSizes,
|
||||
{
|
||||
size: helpers.min([this.chart.width, this.chart.height]),
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
update : function(){
|
||||
this.calculateTotal(this.segments);
|
||||
|
||||
helpers.each(this.segments,function(segment){
|
||||
segment.save();
|
||||
});
|
||||
|
||||
this.reflow();
|
||||
this.render();
|
||||
},
|
||||
reflow : function(){
|
||||
helpers.extend(this.SegmentArc.prototype,{
|
||||
x : this.chart.width/2,
|
||||
y : this.chart.height/2
|
||||
});
|
||||
this.updateScaleRange(this.segments);
|
||||
this.scale.update();
|
||||
|
||||
helpers.extend(this.scale,{
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2
|
||||
});
|
||||
|
||||
helpers.each(this.segments, function(segment){
|
||||
segment.update({
|
||||
outerRadius : this.scale.calculateCenterOffset(segment.value)
|
||||
});
|
||||
}, this);
|
||||
|
||||
},
|
||||
draw : function(ease){
|
||||
var easingDecimal = ease || 1;
|
||||
//Clear & draw the canvas
|
||||
this.clear();
|
||||
helpers.each(this.segments,function(segment, index){
|
||||
segment.transition({
|
||||
circumference : this.scale.getCircumference(),
|
||||
outerRadius : this.scale.calculateCenterOffset(segment.value)
|
||||
},easingDecimal);
|
||||
|
||||
segment.endAngle = segment.startAngle + segment.circumference;
|
||||
|
||||
// If we've removed the first segment we need to set the first one to
|
||||
// start at the top.
|
||||
if (index === 0){
|
||||
segment.startAngle = Math.PI * 1.5;
|
||||
}
|
||||
|
||||
//Check to see if it's the last segment, if not get the next and update the start angle
|
||||
if (index < this.segments.length - 1){
|
||||
this.segments[index+1].startAngle = segment.endAngle;
|
||||
}
|
||||
segment.draw();
|
||||
}, this);
|
||||
this.scale.draw();
|
||||
}
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -1,350 +0,0 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
name: "Radar",
|
||||
defaults:{
|
||||
//Boolean - Whether to show lines for each scale point
|
||||
scaleShowLine : true,
|
||||
|
||||
//Boolean - Whether we show the angle lines out of the radar
|
||||
angleShowLineOut : true,
|
||||
|
||||
//Boolean - Whether to show labels on the scale
|
||||
scaleShowLabels : false,
|
||||
|
||||
// Boolean - Whether the scale should begin at zero
|
||||
scaleBeginAtZero : true,
|
||||
|
||||
//String - Colour of the angle line
|
||||
angleLineColor : "rgba(0,0,0,.1)",
|
||||
|
||||
//Number - Pixel width of the angle line
|
||||
angleLineWidth : 1,
|
||||
|
||||
//Number - Interval at which to draw angle lines ("every Nth point")
|
||||
angleLineInterval: 1,
|
||||
|
||||
//String - Point label font declaration
|
||||
pointLabelFontFamily : "'Arial'",
|
||||
|
||||
//String - Point label font weight
|
||||
pointLabelFontStyle : "normal",
|
||||
|
||||
//Number - Point label font size in pixels
|
||||
pointLabelFontSize : 10,
|
||||
|
||||
//String - Point label font colour
|
||||
pointLabelFontColor : "#666",
|
||||
|
||||
//Boolean - Whether to show a dot for each point
|
||||
pointDot : true,
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointDotRadius : 3,
|
||||
|
||||
//Number - Pixel width of point dot stroke
|
||||
pointDotStrokeWidth : 1,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
pointHitDetectionRadius : 20,
|
||||
|
||||
//Boolean - Whether to show a stroke for datasets
|
||||
datasetStroke : true,
|
||||
|
||||
//Number - Pixel width of dataset stroke
|
||||
datasetStrokeWidth : 2,
|
||||
|
||||
//Boolean - Whether to fill the dataset with a colour
|
||||
datasetFill : true,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
|
||||
|
||||
},
|
||||
|
||||
initialize: function(data){
|
||||
this.PointClass = Chart.Point.extend({
|
||||
strokeWidth : this.options.pointDotStrokeWidth,
|
||||
radius : this.options.pointDotRadius,
|
||||
display: this.options.pointDot,
|
||||
hitDetectionRadius : this.options.pointHitDetectionRadius,
|
||||
ctx : this.chart.ctx
|
||||
});
|
||||
|
||||
this.datasets = [];
|
||||
|
||||
this.buildScale(data);
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips){
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
|
||||
var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
|
||||
|
||||
this.eachPoints(function(point){
|
||||
point.restore(['fillColor', 'strokeColor']);
|
||||
});
|
||||
helpers.each(activePointsCollection, function(activePoint){
|
||||
activePoint.fillColor = activePoint.highlightFill;
|
||||
activePoint.strokeColor = activePoint.highlightStroke;
|
||||
});
|
||||
|
||||
this.showTooltip(activePointsCollection);
|
||||
});
|
||||
}
|
||||
|
||||
//Iterate through each of the datasets, and build this into a property of the chart
|
||||
helpers.each(data.datasets,function(dataset){
|
||||
|
||||
var datasetObject = {
|
||||
label: dataset.label || null,
|
||||
fillColor : dataset.fillColor,
|
||||
strokeColor : dataset.strokeColor,
|
||||
pointColor : dataset.pointColor,
|
||||
pointStrokeColor : dataset.pointStrokeColor,
|
||||
points : []
|
||||
};
|
||||
|
||||
this.datasets.push(datasetObject);
|
||||
|
||||
helpers.each(dataset.data,function(dataPoint,index){
|
||||
//Add a new point for each piece of data, passing any required data to draw.
|
||||
var pointPosition;
|
||||
if (!this.scale.animation){
|
||||
pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint));
|
||||
}
|
||||
datasetObject.points.push(new this.PointClass({
|
||||
value : dataPoint,
|
||||
label : data.labels[index],
|
||||
datasetLabel: dataset.label,
|
||||
x: (this.options.animation) ? this.scale.xCenter : pointPosition.x,
|
||||
y: (this.options.animation) ? this.scale.yCenter : pointPosition.y,
|
||||
strokeColor : dataset.pointStrokeColor,
|
||||
fillColor : dataset.pointColor,
|
||||
highlightFill : dataset.pointHighlightFill || dataset.pointColor,
|
||||
highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
|
||||
}));
|
||||
},this);
|
||||
|
||||
},this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
eachPoints : function(callback){
|
||||
helpers.each(this.datasets,function(dataset){
|
||||
helpers.each(dataset.points,callback,this);
|
||||
},this);
|
||||
},
|
||||
|
||||
getPointsAtEvent : function(evt){
|
||||
var mousePosition = helpers.getRelativePosition(evt),
|
||||
fromCenter = helpers.getAngleFromPoint({
|
||||
x: this.scale.xCenter,
|
||||
y: this.scale.yCenter
|
||||
}, mousePosition);
|
||||
|
||||
var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount,
|
||||
pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex),
|
||||
activePointsCollection = [];
|
||||
|
||||
// If we're at the top, make the pointIndex 0 to get the first of the array.
|
||||
if (pointIndex >= this.scale.valuesCount || pointIndex < 0){
|
||||
pointIndex = 0;
|
||||
}
|
||||
|
||||
if (fromCenter.distance <= this.scale.drawingArea){
|
||||
helpers.each(this.datasets, function(dataset){
|
||||
activePointsCollection.push(dataset.points[pointIndex]);
|
||||
});
|
||||
}
|
||||
|
||||
return activePointsCollection;
|
||||
},
|
||||
|
||||
buildScale : function(data){
|
||||
this.scale = new Chart.RadialScale({
|
||||
display: this.options.showScale,
|
||||
fontStyle: this.options.scaleFontStyle,
|
||||
fontSize: this.options.scaleFontSize,
|
||||
fontFamily: this.options.scaleFontFamily,
|
||||
fontColor: this.options.scaleFontColor,
|
||||
showLabels: this.options.scaleShowLabels,
|
||||
showLabelBackdrop: this.options.scaleShowLabelBackdrop,
|
||||
backdropColor: this.options.scaleBackdropColor,
|
||||
backgroundColors: this.options.scaleBackgroundColors,
|
||||
backdropPaddingY : this.options.scaleBackdropPaddingY,
|
||||
backdropPaddingX: this.options.scaleBackdropPaddingX,
|
||||
lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
|
||||
lineColor: this.options.scaleLineColor,
|
||||
angleLineColor : this.options.angleLineColor,
|
||||
angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0,
|
||||
angleLineInterval: (this.options.angleLineInterval) ? this.options.angleLineInterval : 1,
|
||||
// Point labels at the edge of each line
|
||||
pointLabelFontColor : this.options.pointLabelFontColor,
|
||||
pointLabelFontSize : this.options.pointLabelFontSize,
|
||||
pointLabelFontFamily : this.options.pointLabelFontFamily,
|
||||
pointLabelFontStyle : this.options.pointLabelFontStyle,
|
||||
height : this.chart.height,
|
||||
width: this.chart.width,
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2,
|
||||
ctx : this.chart.ctx,
|
||||
templateString: this.options.scaleLabel,
|
||||
labels: data.labels,
|
||||
valuesCount: data.datasets[0].data.length
|
||||
});
|
||||
|
||||
this.scale.setScaleSize();
|
||||
this.updateScaleRange(data.datasets);
|
||||
this.scale.buildYLabels();
|
||||
},
|
||||
updateScaleRange: function(datasets){
|
||||
var valuesArray = (function(){
|
||||
var totalDataArray = [];
|
||||
helpers.each(datasets,function(dataset){
|
||||
if (dataset.data){
|
||||
totalDataArray = totalDataArray.concat(dataset.data);
|
||||
}
|
||||
else {
|
||||
helpers.each(dataset.points, function(point){
|
||||
totalDataArray.push(point.value);
|
||||
});
|
||||
}
|
||||
});
|
||||
return totalDataArray;
|
||||
})();
|
||||
|
||||
|
||||
var scaleSizes = (this.options.scaleOverride) ?
|
||||
{
|
||||
steps: this.options.scaleSteps,
|
||||
stepValue: this.options.scaleStepWidth,
|
||||
min: this.options.scaleStartValue,
|
||||
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
||||
} :
|
||||
helpers.calculateScaleRange(
|
||||
valuesArray,
|
||||
helpers.min([this.chart.width, this.chart.height])/2,
|
||||
this.options.scaleFontSize,
|
||||
this.options.scaleBeginAtZero,
|
||||
this.options.scaleIntegersOnly
|
||||
);
|
||||
|
||||
helpers.extend(
|
||||
this.scale,
|
||||
scaleSizes
|
||||
);
|
||||
|
||||
},
|
||||
addData : function(valuesArray,label){
|
||||
//Map the values array for each of the datasets
|
||||
this.scale.valuesCount++;
|
||||
helpers.each(valuesArray,function(value,datasetIndex){
|
||||
var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value));
|
||||
this.datasets[datasetIndex].points.push(new this.PointClass({
|
||||
value : value,
|
||||
label : label,
|
||||
datasetLabel: this.datasets[datasetIndex].label,
|
||||
x: pointPosition.x,
|
||||
y: pointPosition.y,
|
||||
strokeColor : this.datasets[datasetIndex].pointStrokeColor,
|
||||
fillColor : this.datasets[datasetIndex].pointColor
|
||||
}));
|
||||
},this);
|
||||
|
||||
this.scale.labels.push(label);
|
||||
|
||||
this.reflow();
|
||||
|
||||
this.update();
|
||||
},
|
||||
removeData : function(){
|
||||
this.scale.valuesCount--;
|
||||
this.scale.labels.shift();
|
||||
helpers.each(this.datasets,function(dataset){
|
||||
dataset.points.shift();
|
||||
},this);
|
||||
this.reflow();
|
||||
this.update();
|
||||
},
|
||||
update : function(){
|
||||
this.eachPoints(function(point){
|
||||
point.save();
|
||||
});
|
||||
this.reflow();
|
||||
this.render();
|
||||
},
|
||||
reflow: function(){
|
||||
helpers.extend(this.scale, {
|
||||
width : this.chart.width,
|
||||
height: this.chart.height,
|
||||
size : helpers.min([this.chart.width, this.chart.height]),
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2
|
||||
});
|
||||
this.updateScaleRange(this.datasets);
|
||||
this.scale.setScaleSize();
|
||||
this.scale.buildYLabels();
|
||||
},
|
||||
draw : function(ease){
|
||||
var easeDecimal = ease || 1,
|
||||
ctx = this.chart.ctx;
|
||||
this.clear();
|
||||
this.scale.draw();
|
||||
|
||||
helpers.each(this.datasets,function(dataset){
|
||||
|
||||
//Transition each point first so that the line and point drawing isn't out of sync
|
||||
helpers.each(dataset.points,function(point,index){
|
||||
if (point.hasValue()){
|
||||
point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);
|
||||
}
|
||||
},this);
|
||||
|
||||
|
||||
|
||||
//Draw the line between all the points
|
||||
ctx.lineWidth = this.options.datasetStrokeWidth;
|
||||
ctx.strokeStyle = dataset.strokeColor;
|
||||
ctx.beginPath();
|
||||
helpers.each(dataset.points,function(point,index){
|
||||
if (index === 0){
|
||||
ctx.moveTo(point.x,point.y);
|
||||
}
|
||||
else{
|
||||
ctx.lineTo(point.x,point.y);
|
||||
}
|
||||
},this);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
||||
ctx.fillStyle = dataset.fillColor;
|
||||
if(this.options.datasetFill){
|
||||
ctx.fill();
|
||||
}
|
||||
//Now draw the points over the line
|
||||
//A little inefficient double looping, but better than the line
|
||||
//lagging behind the point positions
|
||||
helpers.each(dataset.points,function(point){
|
||||
if (point.hasValue()){
|
||||
point.draw();
|
||||
}
|
||||
});
|
||||
|
||||
},this);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,153 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.bar = {
|
||||
hover: {
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "category",
|
||||
categorySpacing: 10,
|
||||
spacing: 1,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
offsetGridLines: true,
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear",
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
Chart.controllers.bar = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.controllers.bar.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
if (!this.getDataset().xAxisID) {
|
||||
this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
|
||||
}
|
||||
|
||||
if (!this.getDataset().yAxisID) {
|
||||
this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
|
||||
}
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Rectangle({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
|
||||
var xScale = this.getScaleForId(this.getDataset().xAxisID);
|
||||
var yScale = this.getScaleForId(this.getDataset().yAxisID);
|
||||
helpers.each(this.getDataset().metaData, function(rectangle, index) {
|
||||
|
||||
var yScalePoint;
|
||||
|
||||
if (yScale.min < 0 && yScale.max < 0) {
|
||||
// all less than 0. use the top
|
||||
yScalePoint = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
yScalePoint = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
yScalePoint = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
helpers.extend(rectangle, {
|
||||
// Utility
|
||||
_chart: this.chart.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.calculateBarX(this.chart.data.datasets.length, this.index, index),
|
||||
y: reset ? yScalePoint : yScale.calculateBarY(this.index, index),
|
||||
|
||||
// Tooltip
|
||||
label: this.chart.data.labels[index],
|
||||
datasetLabel: this.getDataset().label,
|
||||
|
||||
// Appearance
|
||||
base: yScale.calculateBarBase(this.index, index),
|
||||
width: xScale.calculateBarWidth(this.chart.data.datasets.length),
|
||||
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
|
||||
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
|
||||
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth),
|
||||
},
|
||||
});
|
||||
rectangle.pivot();
|
||||
}, this);
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
helpers.each(this.getDataset().metaData, function(rectangle, index) {
|
||||
rectangle.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
},
|
||||
|
||||
setHoverStyle: function(rectangle) {
|
||||
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
|
||||
var index = rectangle._index;
|
||||
|
||||
rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.hoverBackgroundColor ? rectangle.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(rectangle._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
rectangle._model.borderColor = rectangle.custom && rectangle.custom.hoverBorderColor ? rectangle.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(rectangle._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
rectangle._model.borderWidth = rectangle.custom && rectangle.custom.hoverBorderWidth ? rectangle.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangle._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(rectangle) {
|
||||
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
|
||||
var index = rectangle._index;
|
||||
|
||||
rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor);
|
||||
rectangle._model.borderColor = rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor);
|
||||
rectangle._model.borderWidth = rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,177 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
//Cache a local reference to Chart.helpers
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.doughnut = {
|
||||
animation: {
|
||||
//Boolean - Whether we animate the rotation of the Doughnut
|
||||
animateRotate: true,
|
||||
//Boolean - Whether we animate scaling the Doughnut from the centre
|
||||
animateScale: false,
|
||||
},
|
||||
hover: {
|
||||
mode: 'single'
|
||||
},
|
||||
//The percentage of the chart that we cut out of the middle.
|
||||
cutoutPercentage: 50,
|
||||
};
|
||||
|
||||
Chart.defaults.pie = helpers.clone(Chart.defaults.doughnut);
|
||||
helpers.extend(Chart.defaults.pie, {
|
||||
cutoutPercentage: 0
|
||||
});
|
||||
|
||||
|
||||
Chart.controllers.doughnut = Chart.controllers.pie = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.controllers.doughnut.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
// no scales for doughnut
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
|
||||
this.chart.outerRadius = (helpers.min([this.chart.chart.width, this.chart.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2;
|
||||
this.chart.innerRadius = this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1;
|
||||
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.chart.data.datasets.length;
|
||||
|
||||
|
||||
this.getDataset().total = 0;
|
||||
helpers.each(this.getDataset().data, function(value) {
|
||||
this.getDataset().total += Math.abs(value);
|
||||
}, this);
|
||||
|
||||
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index);
|
||||
this.innerRadius = this.outerRadius - this.chart.radiusLength;
|
||||
|
||||
helpers.each(this.getDataset().metaData, function(arc, index) {
|
||||
|
||||
var resetModel = {
|
||||
x: this.chart.chart.width / 2,
|
||||
y: this.chart.chart.height / 2,
|
||||
startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
|
||||
circumference: (this.chart.options.animation.animateRotate) ? 0 : this.calculateCircumference(this.getDataset().data[index]),
|
||||
outerRadius: (this.chart.options.animation.animateScale) ? 0 : this.outerRadius,
|
||||
innerRadius: (this.chart.options.animation.animateScale) ? 0 : this.innerRadius,
|
||||
};
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: reset ? resetModel : {
|
||||
x: this.chart.chart.width / 2,
|
||||
y: this.chart.chart.height / 2,
|
||||
circumference: this.calculateCircumference(this.getDataset().data[index]),
|
||||
outerRadius: this.outerRadius,
|
||||
innerRadius: this.innerRadius,
|
||||
|
||||
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
|
||||
hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
|
||||
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
|
||||
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.getDataset().label, index, this.chart.data.labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
if (!reset) {
|
||||
|
||||
if (index === 0) {
|
||||
arc._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
|
||||
} else {
|
||||
arc._model.startAngle = this.getDataset().metaData[index - 1]._model.endAngle;
|
||||
}
|
||||
|
||||
arc._model.endAngle = arc._model.startAngle + arc._model.circumference;
|
||||
|
||||
|
||||
//Check to see if it's the last arc, if not get the next and update its start angle
|
||||
if (index < this.getDataset().data.length - 1) {
|
||||
this.getDataset().metaData[index + 1]._model.startAngle = arc._model.endAngle;
|
||||
}
|
||||
}
|
||||
|
||||
arc.pivot();
|
||||
}, this);
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
helpers.each(this.getDataset().metaData, function(arc, index) {
|
||||
arc.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
|
||||
setHoverStyle: function(arc) {
|
||||
var dataset = this.chart.data.datasets[arc._datasetIndex];
|
||||
var index = arc._index;
|
||||
|
||||
arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, arc._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(arc) {
|
||||
var dataset = this.chart.data.datasets[arc._datasetIndex];
|
||||
var index = arc._index;
|
||||
|
||||
arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor);
|
||||
arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor);
|
||||
arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth);
|
||||
},
|
||||
|
||||
calculateCircumference: function(value) {
|
||||
if (this.getDataset().total > 0) {
|
||||
return (Math.PI * 1.999999) * (value / this.getDataset().total);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,231 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.line = {
|
||||
hover: {
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "category",
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear",
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Chart.controllers.line = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.controllers.line.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
if (!this.getDataset().xAxisID) {
|
||||
this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
|
||||
}
|
||||
|
||||
if (!this.getDataset().yAxisID) {
|
||||
this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
|
||||
}
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
|
||||
this.getDataset().metaDataset = this.getDataset().metaDataset || new Chart.elements.Line({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_points: this.getDataset().metaData,
|
||||
});
|
||||
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
|
||||
var line = this.getDataset().metaDataset;
|
||||
var points = this.getDataset().metaData;
|
||||
|
||||
var yScale = this.getScaleForId(this.getDataset().yAxisID);
|
||||
var xScale = this.getScaleForId(this.getDataset().xAxisID);
|
||||
var scaleBase;
|
||||
|
||||
if (yScale.min < 0 && yScale.max < 0) {
|
||||
scaleBase = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
scaleBase = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
scaleBase = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
|
||||
// Update Line
|
||||
helpers.extend(line, {
|
||||
// Utility
|
||||
_scale: yScale,
|
||||
_datasetIndex: this.index,
|
||||
// Data
|
||||
_children: points,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: line.custom && line.custom.tension ? line.custom.tension : (this.getDataset().tension || this.chart.options.elements.line.tension),
|
||||
backgroundColor: line.custom && line.custom.backgroundColor ? line.custom.backgroundColor : (this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor),
|
||||
borderWidth: line.custom && line.custom.borderWidth ? line.custom.borderWidth : (this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth),
|
||||
borderColor: line.custom && line.custom.borderColor ? line.custom.borderColor : (this.getDataset().borderColor || this.chart.options.elements.line.borderColor),
|
||||
fill: line.custom && line.custom.fill ? line.custom.fill : (this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill),
|
||||
skipNull: this.getDataset().skipNull !== undefined ? this.getDataset().skipNull : this.chart.options.elements.line.skipNull,
|
||||
drawNull: this.getDataset().drawNull !== undefined ? this.getDataset().drawNull : this.chart.options.elements.line.drawNull,
|
||||
// Scale
|
||||
scaleTop: yScale.top,
|
||||
scaleBottom: yScale.bottom,
|
||||
scaleZero: scaleBase,
|
||||
},
|
||||
});
|
||||
line.pivot();
|
||||
|
||||
// Update Points
|
||||
helpers.each(points, function(point, index) {
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPointPixelForValue(this.getDataset().data[index], index, this.index),
|
||||
y: reset ? scaleBase : yScale.getPointPixelForValue(this.getDataset().data[index], index, this.index),
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.chart.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth),
|
||||
skip: this.getDataset().data[index] === null,
|
||||
|
||||
// Tooltip
|
||||
hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius),
|
||||
},
|
||||
|
||||
});
|
||||
}, this);
|
||||
|
||||
// Update bezier control points
|
||||
helpers.each(this.getDataset().metaData, function(point, index) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(this.getDataset().metaData, index)._model,
|
||||
point._model,
|
||||
helpers.nextItem(this.getDataset().metaData, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chart.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chart.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chart.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chart.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chart.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chart.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chart.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chart.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(this.getDataset().metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
}, this);
|
||||
|
||||
// Transition and Draw the line
|
||||
this.getDataset().metaDataset.transition(easingDecimal).draw();
|
||||
|
||||
// Draw the points
|
||||
helpers.each(this.getDataset().metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
},
|
||||
|
||||
setHoverStyle: function(point) {
|
||||
// Point
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var index = point._index;
|
||||
|
||||
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, point._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(point) {
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var index = point._index;
|
||||
|
||||
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius);
|
||||
point._model.backgroundColor = point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor);
|
||||
point._model.borderColor = point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor);
|
||||
point._model.borderWidth = point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,185 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
//Cache a local reference to Chart.helpers
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
Chart.defaults.polarArea = {
|
||||
|
||||
scale: {
|
||||
type: "radialLinear",
|
||||
lineArc: true, // so that lines are circular
|
||||
},
|
||||
|
||||
//Boolean - Whether to animate the rotation of the chart
|
||||
animateRotate: true,
|
||||
};
|
||||
|
||||
Chart.controllers.polarArea = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.controllers.polarArea.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
// no scales for doughnut
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
//this.chart.scale.setScaleSize();
|
||||
this.chart.scale.calculateRange();
|
||||
this.chart.scale.generateTicks();
|
||||
this.chart.scale.buildYLabels();
|
||||
|
||||
this.chart.outerRadius = (helpers.min([this.chart.chart.width, this.chart.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2;
|
||||
this.chart.innerRadius = this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1;
|
||||
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.chart.data.datasets.length;
|
||||
|
||||
this.getDataset().total = 0;
|
||||
helpers.each(this.getDataset().data, function(value) {
|
||||
this.getDataset().total += Math.abs(value);
|
||||
}, this);
|
||||
|
||||
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index);
|
||||
this.innerRadius = this.outerRadius - this.chart.radiusLength;
|
||||
|
||||
helpers.each(this.getDataset().metaData, function(arc, index) {
|
||||
|
||||
var resetModel = {
|
||||
x: this.chart.chart.width / 2,
|
||||
y: this.chart.chart.height / 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: 0,
|
||||
startAngle: Math.PI * -0.5,
|
||||
endAngle: Math.PI * -0.5,
|
||||
|
||||
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
|
||||
hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
|
||||
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
|
||||
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.chart.data.labels, index, this.chart.data.labels[index])
|
||||
};
|
||||
|
||||
var circumference = 1 / this.getDataset().data.length * 2;
|
||||
var startAngle = (-0.5 * Math.PI) + (Math.PI * circumference) * index;
|
||||
var endAngle = startAngle + (circumference * Math.PI);
|
||||
|
||||
console.log()
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: reset ? resetModel : {
|
||||
x: this.chart.chart.width / 2,
|
||||
y: this.chart.chart.height / 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]),
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle,
|
||||
|
||||
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
|
||||
hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
|
||||
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
|
||||
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.chart.data.labels, index, this.chart.data.labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
arc.pivot();
|
||||
}, this);
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
helpers.each(this.getDataset().metaData, function(arc, index) {
|
||||
arc.transition(easingDecimal).draw();
|
||||
console.log(arc);
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
|
||||
setHoverStyle: function(arc) {
|
||||
var dataset = this.chart.data.datasets[arc._datasetIndex];
|
||||
var index = arc._index;
|
||||
|
||||
arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, arc._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(arc) {
|
||||
var dataset = this.chart.data.datasets[arc._datasetIndex];
|
||||
var index = arc._index;
|
||||
|
||||
arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor);
|
||||
arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor);
|
||||
arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth);
|
||||
},
|
||||
|
||||
calculateCircumference: function(value) {
|
||||
if (this.getDataset().total > 0) {
|
||||
return (Math.PI * 2) * (value / this.getDataset().total);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
updateScaleRange: function() {
|
||||
helpers.extend(this.chart.scale, {
|
||||
size: helpers.min([this.chart.width, this.chart.height]),
|
||||
xCenter: this.chart.width / 2,
|
||||
yCenter: this.chart.height / 2
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return;
|
||||
|
||||
|
||||
Chart.Type.extend({});
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,230 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
|
||||
Chart.defaults.radar = {
|
||||
scale: {
|
||||
type: "radialLinear",
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0, // no bezier in radar
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Chart.controllers.radar = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
|
||||
helpers.extend(Chart.controllers.radar.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
// No need. Single scale only
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
|
||||
this.getDataset().metaDataset = this.getDataset().metaDataset || new Chart.elements.Line({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_points: this.getDataset().metaData,
|
||||
_loop: true
|
||||
});
|
||||
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
_model: {
|
||||
x: 0, //xScale.getPixelForValue(null, index, true),
|
||||
y: 0, //this.chartArea.bottom,
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
|
||||
var line = this.getDataset().metaDataset;
|
||||
var points = this.getDataset().metaData;
|
||||
|
||||
var scale = this.chart.scale;
|
||||
var scaleBase;
|
||||
|
||||
scale.setScaleSize();
|
||||
scale.calculateRange();
|
||||
scale.generateTicks();
|
||||
scale.buildYLabels();
|
||||
|
||||
if (scale.min < 0 && scale.max < 0) {
|
||||
scaleBase = scale.getPointPositionForValue(0, scale.max);
|
||||
} else if (scale.min > 0 && scale.max > 0) {
|
||||
scaleBase = scale.getPointPositionForValue(0, scale.min);
|
||||
} else {
|
||||
scaleBase = scale.getPointPositionForValue(0, 0);
|
||||
}
|
||||
|
||||
helpers.extend(this.getDataset().metaDataset, {
|
||||
// Utility
|
||||
_datasetIndex: this.index,
|
||||
// Data
|
||||
_children: this.getDataset().metaData,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: this.getDataset().tension || this.chart.options.elements.line.tension,
|
||||
backgroundColor: this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor,
|
||||
borderWidth: this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth,
|
||||
borderColor: this.getDataset().borderColor || this.chart.options.elements.line.borderColor,
|
||||
fill: this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill, // use the value from the this.getDataset() if it was provided. else fall back to the default
|
||||
skipNull: this.getDataset().skipNull !== undefined ? this.getDataset().skipNull : this.chart.options.elements.line.skipNull,
|
||||
drawNull: this.getDataset().drawNull !== undefined ? this.getDataset().drawNull : this.chart.options.elements.line.drawNull,
|
||||
|
||||
// Scale
|
||||
scaleTop: scale.top,
|
||||
scaleBottom: scale.bottom,
|
||||
scaleZero: scaleBase,
|
||||
},
|
||||
});
|
||||
|
||||
this.getDataset().metaDataset.pivot();
|
||||
|
||||
// Update Points
|
||||
helpers.each(points, function(point, index) {
|
||||
var pointPosition = scale.getPointPositionForValue(index, this.getDataset().data[index]);
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: reset ? scale.yCenter : pointPosition.y,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.chart.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.getDataset().pointRadius, index, this.chart.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth),
|
||||
skip: this.getDataset().data[index] === null,
|
||||
|
||||
// Tooltip
|
||||
hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
|
||||
// Update bezier control points
|
||||
helpers.each(this.getDataset().metaData, function(point, index) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(this.getDataset().metaData, index, true)._model,
|
||||
point._model,
|
||||
helpers.nextItem(this.getDataset().metaData, index, true)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chart.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chart.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chart.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chart.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chart.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chart.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chart.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chart.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(this.getDataset().metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
}, this);
|
||||
|
||||
// Transition and Draw the line
|
||||
this.getDataset().metaDataset.transition(easingDecimal).draw();
|
||||
|
||||
// Draw the points
|
||||
helpers.each(this.getDataset().metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
},
|
||||
|
||||
setHoverStyle: function(point) {
|
||||
// Point
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var index = point._index;
|
||||
|
||||
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, point._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(point) {
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var index = point._index;
|
||||
|
||||
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius);
|
||||
point._model.backgroundColor = point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor);
|
||||
point._model.borderColor = point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor);
|
||||
point._model.borderWidth = point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,43 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
return;
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
hover: {
|
||||
mode: 'single',
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "linear", // scatter should not use a category axis
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear",
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
}],
|
||||
},
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].borderColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>",
|
||||
|
||||
tooltips: {
|
||||
template: "(<%= value.x %>, <%= value.y %>)",
|
||||
multiTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%>(<%= value.x %>, <%= value.y %>)",
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
Chart.types.Line.extend({
|
||||
name: "Scatter",
|
||||
defaults: defaultConfig,
|
||||
});
|
||||
}).call(this);
|
||||
@@ -0,0 +1,116 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.animation = {
|
||||
duration: 1000,
|
||||
easing: "easeOutQuart",
|
||||
onProgress: function() {},
|
||||
onComplete: function() {},
|
||||
};
|
||||
|
||||
Chart.Animation = Chart.Element.extend({
|
||||
currentStep: null, // the current animation step
|
||||
numSteps: 60, // default number of steps
|
||||
easing: "", // the easing to use for this animation
|
||||
render: null, // render function used by the animation service
|
||||
|
||||
onAnimationProgress: null, // user specified callback to fire on each step of the animation
|
||||
onAnimationComplete: null, // user specified callback to fire when the animation finishes
|
||||
});
|
||||
|
||||
Chart.animationService = {
|
||||
frameDuration: 17,
|
||||
animations: [],
|
||||
dropFrames: 0,
|
||||
addAnimation: function(chartInstance, animationObject, duration) {
|
||||
|
||||
if (typeof duration !== 'number' || duration === 0) {
|
||||
chartInstance.animating = true;
|
||||
}
|
||||
|
||||
for (var index = 0; index < this.animations.length; ++index) {
|
||||
if (this.animations[index].chartInstance === chartInstance) {
|
||||
// replacing an in progress animation
|
||||
this.animations[index].animationObject = animationObject;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.animations.push({
|
||||
chartInstance: chartInstance,
|
||||
animationObject: animationObject
|
||||
});
|
||||
|
||||
// If there are no animations queued, manually kickstart a digest, for lack of a better word
|
||||
if (this.animations.length == 1) {
|
||||
helpers.requestAnimFrame.call(window, this.digestWrapper);
|
||||
}
|
||||
},
|
||||
// Cancel the animation for a given chart instance
|
||||
cancelAnimation: function(chartInstance) {
|
||||
var index = helpers.findNextWhere(this.animations, function(animationWrapper) {
|
||||
return animationWrapper.chartInstance === chartInstance;
|
||||
});
|
||||
|
||||
if (index) {
|
||||
this.animations.splice(index, 1);
|
||||
chartInstance.animating = false;
|
||||
}
|
||||
},
|
||||
// calls startDigest with the proper context
|
||||
digestWrapper: function() {
|
||||
Chart.animationService.startDigest.call(Chart.animationService);
|
||||
},
|
||||
startDigest: function() {
|
||||
|
||||
var startTime = Date.now();
|
||||
var framesToDrop = 0;
|
||||
|
||||
if (this.dropFrames > 1) {
|
||||
framesToDrop = Math.floor(this.dropFrames);
|
||||
this.dropFrames -= framesToDrop;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.animations.length; i++) {
|
||||
|
||||
if (this.animations[i].animationObject.currentStep === null) {
|
||||
this.animations[i].animationObject.currentStep = 0;
|
||||
}
|
||||
|
||||
this.animations[i].animationObject.currentStep += 1 + framesToDrop;
|
||||
if (this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps) {
|
||||
this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
|
||||
}
|
||||
|
||||
this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
|
||||
|
||||
if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps) {
|
||||
// executed the last frame. Remove the animation.
|
||||
this.animations[i].chartInstance.animating = false;
|
||||
this.animations.splice(i, 1);
|
||||
// Keep the index in place to offset the splice
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
var endTime = Date.now();
|
||||
var delay = endTime - startTime - this.frameDuration;
|
||||
var frameDelay = delay / this.frameDuration;
|
||||
|
||||
if (frameDelay > 1) {
|
||||
this.dropFrames += frameDelay;
|
||||
}
|
||||
|
||||
// Do we have more stuff to animate?
|
||||
if (this.animations.length > 0) {
|
||||
helpers.requestAnimFrame.call(window, this.digestWrapper);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,437 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
//Create a dictionary of chart types, to allow for extension of existing types
|
||||
Chart.types = {};
|
||||
|
||||
//Store a reference to each instance - allowing us to globally resize chart instances on window resize.
|
||||
//Destroy method on the chart will remove the instance of the chart from this reference.
|
||||
Chart.instances = {};
|
||||
|
||||
// Controllers available for dataset visualization eg. bar, line, slice, etc.
|
||||
Chart.controllers = {};
|
||||
|
||||
// The main controller of a chart
|
||||
Chart.Controller = function(instance) {
|
||||
|
||||
this.chart = instance;
|
||||
this.config = instance.config;
|
||||
this.data = this.config.data;
|
||||
this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {});
|
||||
this.id = helpers.uid();
|
||||
|
||||
//Add the chart instance to the global namespace
|
||||
Chart.instances[this.id] = this;
|
||||
|
||||
if (this.options.responsive) {
|
||||
// Silent resize before chart draws
|
||||
this.resize(true);
|
||||
}
|
||||
|
||||
this.initialize.call(this);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
helpers.extend(Chart.Controller.prototype, {
|
||||
|
||||
initialize: function initialize() {
|
||||
|
||||
// TODO
|
||||
// If BeforeInit(this) doesn't return false, proceed
|
||||
|
||||
this.bindEvents();
|
||||
|
||||
// Make sure controllers are built first so that each dataset is bound to an axis before the scales
|
||||
// are built
|
||||
this.buildControllers();
|
||||
this.buildScales();
|
||||
this.resetElements();
|
||||
this.initToolTip();
|
||||
this.update();
|
||||
|
||||
// TODO
|
||||
// If AfterInit(this) doesn't return false, proceed
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
clear: function clear() {
|
||||
helpers.clear(this.chart);
|
||||
return this;
|
||||
},
|
||||
|
||||
stop: function stop() {
|
||||
// Stops any current animation loop occuring
|
||||
Chart.animationService.cancelAnimation(this);
|
||||
return this;
|
||||
},
|
||||
|
||||
resize: function resize(silent) {
|
||||
this.stop();
|
||||
var canvas = this.chart.canvas,
|
||||
newWidth = helpers.getMaximumWidth(this.chart.canvas),
|
||||
newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : helpers.getMaximumHeight(this.chart.canvas);
|
||||
|
||||
canvas.width = this.chart.width = newWidth;
|
||||
canvas.height = this.chart.height = newHeight;
|
||||
|
||||
helpers.retinaScale(this.chart);
|
||||
|
||||
if (!silent) {
|
||||
this.update(this.options.responsiveAnimationDuration);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
buildScales: function buildScales() {
|
||||
// Map of scale ID to scale object so we can lookup later
|
||||
this.scales = {};
|
||||
|
||||
// Build the x axes
|
||||
if (this.options.scales) {
|
||||
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
|
||||
helpers.each(this.options.scales.xAxes, function(xAxisOptions) {
|
||||
var ScaleClass = Chart.scaleService.getScaleConstructor(xAxisOptions.type);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: xAxisOptions,
|
||||
data: this.data,
|
||||
id: xAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
|
||||
// Build the y axes
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
|
||||
var ScaleClass = Chart.scaleService.getScaleConstructor(yAxisOptions.type);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: yAxisOptions,
|
||||
data: this.data,
|
||||
id: yAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
if (this.options.scale) {
|
||||
// Build radial axes
|
||||
var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scale.type);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: this.options.scale,
|
||||
data: this.data,
|
||||
chart: this.chart,
|
||||
});
|
||||
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
},
|
||||
|
||||
buildControllers: function() {
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
var type = dataset.type || this.config.type;
|
||||
if (dataset.controller) {
|
||||
dataset.controller.updateIndex(datasetIndex);
|
||||
return;
|
||||
}
|
||||
dataset.controller = new Chart.controllers[type](this, datasetIndex);
|
||||
}, this);
|
||||
},
|
||||
|
||||
resetElements: function resetElements() {
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.controller.reset();
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
update: function update(animationDuration) {
|
||||
// This will loop through any data and do the appropriate element update for the type
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.controller.update();
|
||||
}, this);
|
||||
this.render(animationDuration);
|
||||
},
|
||||
|
||||
render: function render(duration) {
|
||||
|
||||
if (this.options.animation.duration !== 0 || duration) {
|
||||
var animation = new Chart.Animation();
|
||||
animation.numSteps = (duration || this.options.animation.duration) / 16.66; //60 fps
|
||||
animation.easing = this.options.animation.easing;
|
||||
|
||||
// render function
|
||||
animation.render = function(chartInstance, animationObject) {
|
||||
var easingFunction = helpers.easingEffects[animationObject.easing];
|
||||
var stepDecimal = animationObject.currentStep / animationObject.numSteps;
|
||||
var easeDecimal = easingFunction(stepDecimal);
|
||||
|
||||
chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
|
||||
};
|
||||
|
||||
// user events
|
||||
animation.onAnimationProgress = this.options.onAnimationProgress;
|
||||
animation.onAnimationComplete = this.options.onAnimationComplete;
|
||||
|
||||
Chart.animationService.addAnimation(this, animation, duration);
|
||||
} else {
|
||||
this.draw();
|
||||
this.options.onAnimationComplete.call(this);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
if (this.scale) {
|
||||
this.scale.draw();
|
||||
}
|
||||
|
||||
// Draw each dataset via its respective controller (reversed to support proper line stacking)
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.controller.draw(ease);
|
||||
}, this, true);
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Get the single element that was clicked on
|
||||
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
|
||||
getElementAtEvent: function(e) {
|
||||
|
||||
var eventPosition = helpers.getRelativePosition(e);
|
||||
var elementsArray = [];
|
||||
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
helpers.each(dataset.metaData, function(element, index) {
|
||||
if (element.inRange(eventPosition.x, eventPosition.y)) {
|
||||
elementsArray.push(element);
|
||||
return elementsArray;
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
return elementsArray;
|
||||
},
|
||||
|
||||
getElementsAtEvent: function(e) {
|
||||
var eventPosition = helpers.getRelativePosition(e);
|
||||
var elementsArray = [];
|
||||
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
helpers.each(dataset.metaData, function(element, index) {
|
||||
if (element.inLabelRange(eventPosition.x, eventPosition.y)) {
|
||||
elementsArray.push(element);
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
return elementsArray;
|
||||
},
|
||||
|
||||
getDatasetAtEvent: function(e) {
|
||||
var eventPosition = helpers.getRelativePosition(e);
|
||||
var elementsArray = [];
|
||||
|
||||
for (var datasetIndex = 0; datasetIndex < this.chart.data.datasets.length; datasetIndex++) {
|
||||
for (elementIndex = 0; elementIndex < this.chart.data.datasets[datasetIndex].metaData.length; elementIndex++) {
|
||||
if (this.chart.data.datasets[datasetIndex].metaData[elementIndex].inLabelRange(eventPosition.x, eventPosition.y)) {
|
||||
helpers.each(this.chart.data.datasets, datasetIterator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return elementsArray.length ? elementsArray : [];
|
||||
},
|
||||
|
||||
generateLegend: function generateLegend() {
|
||||
return template(this.options.legendTemplate, this);
|
||||
},
|
||||
|
||||
destroy: function destroy() {
|
||||
this.clear();
|
||||
helpers.unbindEvents(this, this.events);
|
||||
var canvas = this.chart.canvas;
|
||||
|
||||
// Reset canvas height/width attributes starts a fresh with the canvas context
|
||||
canvas.width = this.chart.width;
|
||||
canvas.height = this.chart.height;
|
||||
|
||||
// < IE9 doesn't support removeProperty
|
||||
if (canvas.style.removeProperty) {
|
||||
canvas.style.removeProperty('width');
|
||||
canvas.style.removeProperty('height');
|
||||
} else {
|
||||
canvas.style.removeAttribute('width');
|
||||
canvas.style.removeAttribute('height');
|
||||
}
|
||||
|
||||
delete Chart.instances[this.id];
|
||||
},
|
||||
|
||||
toBase64Image: function toBase64Image() {
|
||||
return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
|
||||
},
|
||||
|
||||
initToolTip: function initToolTip() {
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
},
|
||||
|
||||
bindEvents: function bindEvents() {
|
||||
helpers.bindEvents(this, this.options.events, function(evt) {
|
||||
this.eventHandler(evt);
|
||||
});
|
||||
},
|
||||
eventHandler: function eventHandler(e) {
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
if (e.type == 'mouseout') {
|
||||
this.active = [];
|
||||
} else {
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
}
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
this.data.datasets[this.lastActive[0]._datasetIndex].controller.removeHoverStyle(this.lastActive[0], this.lastActive[0]._datasetIndex, this.lastActive[0]._index);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
this.data.datasets[this.lastActive[i]._datasetIndex].controller.removeHoverStyle(this.lastActive[i], this.lastActive[i]._datasetIndex, this.lastActive[i]._index);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
this.data.datasets[this.active[0]._datasetIndex].controller.setHoverStyle(this.active[0]);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
this.data.datasets[this.active[i]._datasetIndex].controller.setHoverStyle(this.active[i]);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hover.animationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,92 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.elements = {};
|
||||
|
||||
Chart.Element = function(configuration) {
|
||||
helpers.extend(this, configuration);
|
||||
this.initialize.apply(this, arguments);
|
||||
};
|
||||
helpers.extend(Chart.Element.prototype, {
|
||||
initialize: function() {},
|
||||
pivot: function() {
|
||||
if (!this._view) {
|
||||
this._view = helpers.clone(this._model);
|
||||
}
|
||||
this._start = helpers.clone(this._view);
|
||||
return this;
|
||||
},
|
||||
transition: function(ease) {
|
||||
if (!this._view) {
|
||||
this._view = helpers.clone(this._model);
|
||||
}
|
||||
if (!this._start) {
|
||||
this.pivot();
|
||||
}
|
||||
|
||||
helpers.each(this._model, function(value, key) {
|
||||
|
||||
if (key[0] === '_' || !this._model.hasOwnProperty(key)) {
|
||||
// Only non-underscored properties
|
||||
}
|
||||
|
||||
// Init if doesn't exist
|
||||
else if (!this._view[key]) {
|
||||
if (typeof value === 'number') {
|
||||
this._view[key] = value * ease;
|
||||
} else {
|
||||
this._view[key] = value || null;
|
||||
}
|
||||
}
|
||||
|
||||
// No unnecessary computations
|
||||
else if (this._model[key] === this._view[key]) {
|
||||
// It's the same! Woohoo!
|
||||
}
|
||||
|
||||
// Color transitions if possible
|
||||
else if (typeof value === 'string') {
|
||||
try {
|
||||
var color = helpers.color(this._start[key]).mix(helpers.color(this._model[key]), ease);
|
||||
this._view[key] = color.rgbString();
|
||||
} catch (err) {
|
||||
this._view[key] = value;
|
||||
}
|
||||
}
|
||||
// Number transitions
|
||||
else if (typeof value === 'number') {
|
||||
var startVal = this._start[key] !== undefined ? this._start[key] : 0;
|
||||
this._view[key] = ((this._model[key] - startVal) * ease) + startVal;
|
||||
}
|
||||
// Everything else
|
||||
else {
|
||||
this._view[key] = value;
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
if (ease === 1) {
|
||||
delete this._start;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
return {
|
||||
x: this._model.x,
|
||||
y: this._model.y
|
||||
};
|
||||
},
|
||||
hasValue: function() {
|
||||
return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
|
||||
}
|
||||
});
|
||||
|
||||
Chart.Element.extend = helpers.inherits;
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,864 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart;
|
||||
|
||||
//Global Chart helpers object for utility methods and classes
|
||||
var helpers = Chart.helpers = {};
|
||||
|
||||
//-- Basic js utility methods
|
||||
var each = helpers.each = function(loopable, callback, self, reverse) {
|
||||
var additionalArgs = Array.prototype.slice.call(arguments, 3);
|
||||
// Check to see if null or undefined firstly.
|
||||
if (loopable) {
|
||||
if (loopable.length === +loopable.length) {
|
||||
var i;
|
||||
if (reverse) {
|
||||
for (i = loopable.length - 1; i >= 0; i--) {
|
||||
callback.apply(self, [loopable[i], i].concat(additionalArgs));
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < loopable.length; i++) {
|
||||
callback.apply(self, [loopable[i], i].concat(additionalArgs));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var item in loopable) {
|
||||
callback.apply(self, [loopable[item], item].concat(additionalArgs));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
clone = helpers.clone = function(obj) {
|
||||
var objClone = {};
|
||||
each(obj, function(value, key) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (helpers.isArray(value)) {
|
||||
objClone[key] = value.slice(0);
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
objClone[key] = clone(value);
|
||||
} else {
|
||||
objClone[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
return objClone;
|
||||
},
|
||||
extend = helpers.extend = function(base) {
|
||||
each(Array.prototype.slice.call(arguments, 1), function(extensionObject) {
|
||||
each(extensionObject, function(value, key) {
|
||||
if (extensionObject.hasOwnProperty(key)) {
|
||||
base[key] = value;
|
||||
}
|
||||
});
|
||||
});
|
||||
return base;
|
||||
},
|
||||
merge = helpers.merge = function(base, master) {
|
||||
//Merge properties in left object over to a shallow clone of object right.
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args.unshift({});
|
||||
return extend.apply(null, args);
|
||||
},
|
||||
// Need a special merge function to chart configs since they are now grouped
|
||||
configMerge = helpers.configMerge = function(_base) {
|
||||
var base = clone(_base);
|
||||
helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
|
||||
helpers.each(extension, function(value, key) {
|
||||
if (extension.hasOwnProperty(key)) {
|
||||
if (key === 'scales') {
|
||||
// Scale config merging is complex. Add out own function here for that
|
||||
base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value);
|
||||
|
||||
} else if (key === 'scale') {
|
||||
// Used in polar area & radar charts since there is only one scale
|
||||
base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value);
|
||||
} else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) {
|
||||
// In this case we have an array of objects replacing another array. Rather than doing a strict replace,
|
||||
// merge. This allows easy scale option merging
|
||||
var baseArray = base[key];
|
||||
|
||||
helpers.each(value, function(valueObj, index) {
|
||||
|
||||
if (index < baseArray.length) {
|
||||
baseArray[index] = helpers.configMerge(baseArray[index], valueObj);
|
||||
} else {
|
||||
baseArray.push(valueObj); // nothing to merge
|
||||
}
|
||||
});
|
||||
|
||||
} else if (base.hasOwnProperty(key) && typeof base[key] == "object" && base[key] !== null && typeof value == "object") {
|
||||
// If we are overwriting an object with an object, do a merge of the properties.
|
||||
base[key] = helpers.configMerge(base[key], value);
|
||||
|
||||
} else {
|
||||
// can just overwrite the value in this case
|
||||
base[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return base;
|
||||
},
|
||||
scaleMerge = helpers.scaleMerge = function(_base, extension) {
|
||||
var base = clone(_base);
|
||||
|
||||
helpers.each(extension, function(value, key) {
|
||||
if (extension.hasOwnProperty(key)) {
|
||||
if (key === 'xAxes' || key === 'yAxes') {
|
||||
// These properties are arrays of items
|
||||
if (base.hasOwnProperty(key)) {
|
||||
helpers.each(value, function(valueObj, index) {
|
||||
if (index >= base[key].length || !base[key][index].type) {
|
||||
base[key].push(helpers.configMerge(valueObj.type ? Chart.scaleService.getScaleDefaults(valueObj.type) : {}, valueObj));
|
||||
} else if (valueObj.type !== base[key][index].type) {
|
||||
// Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults
|
||||
base[key][index] = helpers.configMerge(base[key][index], valueObj.type ? Chart.scaleService.getScaleDefaults(valueObj.type) : {}, valueObj)
|
||||
} else {
|
||||
// Type is the same
|
||||
base[key][index] = helpers.configMerge(base[key][index], valueObj);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
base[key] = [];
|
||||
helpers.each(value, function(valueObj) {
|
||||
base[key].push(helpers.configMerge(valueObj.type ? Chart.scaleService.getScaleDefaults(valueObj.type) : {}, valueObj));
|
||||
});
|
||||
}
|
||||
} else if (base.hasOwnProperty(key) && typeof base[key] == "object" && base[key] !== null && typeof value == "object") {
|
||||
// If we are overwriting an object with an object, do a merge of the properties.
|
||||
base[key] = helpers.configMerge(base[key], value);
|
||||
|
||||
} else {
|
||||
// can just overwrite the value in this case
|
||||
base[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return base;
|
||||
},
|
||||
getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
|
||||
if (!value) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (helpers.isArray(value) && index < value.length) {
|
||||
return value[index];
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
indexOf = helpers.indexOf = function(arrayToSearch, item) {
|
||||
if (Array.prototype.indexOf) {
|
||||
return arrayToSearch.indexOf(item);
|
||||
} else {
|
||||
for (var i = 0; i < arrayToSearch.length; i++) {
|
||||
if (arrayToSearch[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
where = helpers.where = function(collection, filterCallback) {
|
||||
var filtered = [];
|
||||
|
||||
helpers.each(collection, function(item) {
|
||||
if (filterCallback(item)) {
|
||||
filtered.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return filtered;
|
||||
},
|
||||
findNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
|
||||
// Default to start of the array
|
||||
if (!startIndex) {
|
||||
startIndex = -1;
|
||||
}
|
||||
for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
|
||||
var currentItem = arrayToSearch[i];
|
||||
if (filterCallback(currentItem)) {
|
||||
return currentItem;
|
||||
}
|
||||
}
|
||||
},
|
||||
findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
|
||||
// Default to end of the array
|
||||
if (!startIndex) {
|
||||
startIndex = arrayToSearch.length;
|
||||
}
|
||||
for (var i = startIndex - 1; i >= 0; i--) {
|
||||
var currentItem = arrayToSearch[i];
|
||||
if (filterCallback(currentItem)) {
|
||||
return currentItem;
|
||||
}
|
||||
}
|
||||
},
|
||||
inherits = helpers.inherits = function(extensions) {
|
||||
//Basic javascript inheritance based on the model created in Backbone.js
|
||||
var parent = this;
|
||||
var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() {
|
||||
return parent.apply(this, arguments);
|
||||
};
|
||||
|
||||
var Surrogate = function() {
|
||||
this.constructor = ChartElement;
|
||||
};
|
||||
Surrogate.prototype = parent.prototype;
|
||||
ChartElement.prototype = new Surrogate();
|
||||
|
||||
ChartElement.extend = inherits;
|
||||
|
||||
if (extensions) extend(ChartElement.prototype, extensions);
|
||||
|
||||
ChartElement.__super__ = parent.prototype;
|
||||
|
||||
return ChartElement;
|
||||
},
|
||||
noop = helpers.noop = function() {},
|
||||
uid = helpers.uid = (function() {
|
||||
var id = 0;
|
||||
return function() {
|
||||
return "chart-" + id++;
|
||||
};
|
||||
})(),
|
||||
warn = helpers.warn = function(str) {
|
||||
//Method for warning of errors
|
||||
if (window.console && typeof window.console.warn === "function") console.warn(str);
|
||||
},
|
||||
amd = helpers.amd = (typeof define === 'function' && define.amd),
|
||||
//-- Math methods
|
||||
isNumber = helpers.isNumber = function(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
},
|
||||
max = helpers.max = function(array) {
|
||||
return Math.max.apply(Math, array);
|
||||
},
|
||||
min = helpers.min = function(array) {
|
||||
return Math.min.apply(Math, array);
|
||||
},
|
||||
sign = helpers.sign = function(x) {
|
||||
if (Math.sign) {
|
||||
return Math.sign(x);
|
||||
} else {
|
||||
x = +x; // convert to a number
|
||||
if (x === 0 || isNaN(x)) {
|
||||
return x;
|
||||
}
|
||||
return x > 0 ? 1 : -1;
|
||||
}
|
||||
},
|
||||
log10 = helpers.log10 = function(x) {
|
||||
if (Math.log10) {
|
||||
return Math.log10(x)
|
||||
} else {
|
||||
return Math.log(x) / Math.LN10;
|
||||
}
|
||||
},
|
||||
cap = helpers.cap = function(valueToCap, maxValue, minValue) {
|
||||
if (isNumber(maxValue)) {
|
||||
if (valueToCap > maxValue) {
|
||||
return maxValue;
|
||||
}
|
||||
} else if (isNumber(minValue)) {
|
||||
if (valueToCap < minValue) {
|
||||
return minValue;
|
||||
}
|
||||
}
|
||||
return valueToCap;
|
||||
},
|
||||
getDecimalPlaces = helpers.getDecimalPlaces = function(num) {
|
||||
if (num % 1 !== 0 && isNumber(num)) {
|
||||
var s = num.toString();
|
||||
if (s.indexOf("e-") < 0) {
|
||||
// no exponent, e.g. 0.01
|
||||
return s.split(".")[1].length;
|
||||
} else if (s.indexOf(".") < 0) {
|
||||
// no decimal point, e.g. 1e-9
|
||||
return parseInt(s.split("e-")[1]);
|
||||
} else {
|
||||
// exponent and decimal point, e.g. 1.23e-9
|
||||
var parts = s.split(".")[1].split("e-");
|
||||
return parts[0].length + parseInt(parts[1]);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
toRadians = helpers.toRadians = function(degrees) {
|
||||
return degrees * (Math.PI / 180);
|
||||
},
|
||||
toDegrees = helpers.toDegrees = function(radians) {
|
||||
return radians * (180 / Math.PI);
|
||||
},
|
||||
// Gets the angle from vertical upright to the point about a centre.
|
||||
getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
|
||||
var distanceFromXCenter = anglePoint.x - centrePoint.x,
|
||||
distanceFromYCenter = anglePoint.y - centrePoint.y,
|
||||
radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
|
||||
|
||||
var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
|
||||
|
||||
if (angle < (-0.5 * Math.PI)) {
|
||||
angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
|
||||
}
|
||||
|
||||
return {
|
||||
angle: angle,
|
||||
distance: radialDistanceFromCenter
|
||||
};
|
||||
},
|
||||
aliasPixel = helpers.aliasPixel = function(pixelWidth) {
|
||||
return (pixelWidth % 2 === 0) ? 0 : 0.5;
|
||||
},
|
||||
splineCurve = helpers.splineCurve = function(FirstPoint, MiddlePoint, AfterPoint, t) {
|
||||
//Props to Rob Spencer at scaled innovation for his post on splining between points
|
||||
//http://scaledinnovation.com/analytics/splines/aboutSplines.html
|
||||
var d01 = Math.sqrt(Math.pow(MiddlePoint.x - FirstPoint.x, 2) + Math.pow(MiddlePoint.y - FirstPoint.y, 2)),
|
||||
d12 = Math.sqrt(Math.pow(AfterPoint.x - MiddlePoint.x, 2) + Math.pow(AfterPoint.y - MiddlePoint.y, 2)),
|
||||
fa = t * d01 / (d01 + d12), // scaling factor for triangle Ta
|
||||
fb = t * d12 / (d01 + d12);
|
||||
return {
|
||||
previous: {
|
||||
x: MiddlePoint.x - fa * (AfterPoint.x - FirstPoint.x),
|
||||
y: MiddlePoint.y - fa * (AfterPoint.y - FirstPoint.y)
|
||||
},
|
||||
next: {
|
||||
x: MiddlePoint.x + fb * (AfterPoint.x - FirstPoint.x),
|
||||
y: MiddlePoint.y + fb * (AfterPoint.y - FirstPoint.y)
|
||||
}
|
||||
};
|
||||
},
|
||||
nextItem = helpers.nextItem = function(collection, index, loop) {
|
||||
if (loop) {
|
||||
return collection[index + 1] || collection[0];
|
||||
}
|
||||
return collection[index + 1] || collection[collection.length - 1];
|
||||
},
|
||||
previousItem = helpers.previousItem = function(collection, index, loop) {
|
||||
if (loop) {
|
||||
return collection[index - 1] || collection[collection.length - 1];
|
||||
}
|
||||
return collection[index - 1] || collection[0];
|
||||
},
|
||||
calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val) {
|
||||
return Math.floor(Math.log(val) / Math.LN10);
|
||||
},
|
||||
calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly) {
|
||||
|
||||
//Set a minimum step of two - a point at the top of the graph, and a point at the base
|
||||
var minSteps = 2,
|
||||
maxSteps = Math.floor(drawingSize / (textSize * 1.5)),
|
||||
skipFitting = (minSteps >= maxSteps);
|
||||
|
||||
var maxValue = max(valuesArray),
|
||||
minValue = min(valuesArray);
|
||||
|
||||
// We need some degree of seperation here to calculate the scales if all the values are the same
|
||||
// Adding/minusing 0.5 will give us a range of 1.
|
||||
if (maxValue === minValue) {
|
||||
maxValue += 0.5;
|
||||
// So we don't end up with a graph with a negative start value if we've said always start from zero
|
||||
if (minValue >= 0.5 && !startFromZero) {
|
||||
minValue -= 0.5;
|
||||
} else {
|
||||
// Make up a whole number above the values
|
||||
maxValue += 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
var valueRange = Math.abs(maxValue - minValue),
|
||||
rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange),
|
||||
graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
|
||||
graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
|
||||
graphRange = graphMax - graphMin,
|
||||
stepValue = Math.pow(10, rangeOrderOfMagnitude),
|
||||
numberOfSteps = Math.round(graphRange / stepValue);
|
||||
|
||||
//If we have more space on the graph we'll use it to give more definition to the data
|
||||
while ((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) {
|
||||
if (numberOfSteps > maxSteps) {
|
||||
stepValue *= 2;
|
||||
numberOfSteps = Math.round(graphRange / stepValue);
|
||||
// Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps.
|
||||
if (numberOfSteps % 1 !== 0) {
|
||||
skipFitting = true;
|
||||
}
|
||||
}
|
||||
//We can fit in double the amount of scale points on the scale
|
||||
else {
|
||||
//If user has declared ints only, and the step value isn't a decimal
|
||||
if (integersOnly && rangeOrderOfMagnitude >= 0) {
|
||||
//If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float
|
||||
if (stepValue / 2 % 1 === 0) {
|
||||
stepValue /= 2;
|
||||
numberOfSteps = Math.round(graphRange / stepValue);
|
||||
}
|
||||
//If it would make it a float break out of the loop
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
//If the scale doesn't have to be an int, make the scale more granular anyway.
|
||||
else {
|
||||
stepValue /= 2;
|
||||
numberOfSteps = Math.round(graphRange / stepValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (skipFitting) {
|
||||
numberOfSteps = minSteps;
|
||||
stepValue = graphRange / numberOfSteps;
|
||||
}
|
||||
return {
|
||||
steps: numberOfSteps,
|
||||
stepValue: stepValue,
|
||||
min: graphMin,
|
||||
max: graphMin + (numberOfSteps * stepValue)
|
||||
};
|
||||
|
||||
},
|
||||
// Implementation of the nice number algorithm used in determining where axis labels will go
|
||||
niceNum = helpers.niceNum = function(range, round) {
|
||||
var exponent = Math.floor(helpers.log10(range));
|
||||
var fraction = range / Math.pow(10, exponent);
|
||||
var niceFraction;
|
||||
|
||||
if (round) {
|
||||
if (fraction < 1.5) {
|
||||
niceFraction = 1;
|
||||
} else if (fraction < 3) {
|
||||
niceFraction = 2;
|
||||
} else if (fraction < 7) {
|
||||
niceFraction = 5;
|
||||
} else {
|
||||
niceFraction = 10;
|
||||
}
|
||||
} else {
|
||||
if (fraction <= 1.0) {
|
||||
niceFraction = 1;
|
||||
} else if (fraction <= 2) {
|
||||
niceFraction = 2;
|
||||
} else if (fraction <= 5) {
|
||||
niceFraction = 5;
|
||||
} else {
|
||||
niceFraction = 10;
|
||||
}
|
||||
}
|
||||
|
||||
return niceFraction * Math.pow(10, exponent);
|
||||
},
|
||||
/* jshint ignore:start */
|
||||
// Blows up jshint errors based on the new Function constructor
|
||||
//Templating methods
|
||||
//Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
|
||||
template = helpers.template = function(templateString, valuesObject) {
|
||||
|
||||
// If templateString is function rather than string-template - call the function for valuesObject
|
||||
|
||||
if (templateString instanceof Function) {
|
||||
return templateString(valuesObject);
|
||||
}
|
||||
|
||||
var cache = {};
|
||||
|
||||
function tmpl(str, data) {
|
||||
// Figure out if we're getting a template, or if we need to
|
||||
// load the template - and be sure to cache the result.
|
||||
var fn = !/\W/.test(str) ?
|
||||
cache[str] = cache[str] :
|
||||
|
||||
// Generate a reusable function that will serve as a template
|
||||
// generator (and which will be cached).
|
||||
new Function("obj",
|
||||
"var p=[],print=function(){p.push.apply(p,arguments);};" +
|
||||
|
||||
// Introduce the data as local variables using with(){}
|
||||
"with(obj){p.push('" +
|
||||
|
||||
// Convert the template into pure JavaScript
|
||||
str
|
||||
.replace(/[\r\t\n]/g, " ")
|
||||
.split("<%").join("\t")
|
||||
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
|
||||
.replace(/\t=(.*?)%>/g, "',$1,'")
|
||||
.split("\t").join("');")
|
||||
.split("%>").join("p.push('")
|
||||
.split("\r").join("\\'") +
|
||||
"');}return p.join('');"
|
||||
);
|
||||
|
||||
// Provide some basic currying to the user
|
||||
return data ? fn(data) : fn;
|
||||
}
|
||||
return tmpl(templateString, valuesObject);
|
||||
},
|
||||
/* jshint ignore:end */
|
||||
generateLabels = helpers.generateLabels = function(templateString, numberOfSteps, graphMin, stepValue) {
|
||||
var labelsArray = new Array(numberOfSteps);
|
||||
if (templateString) {
|
||||
each(labelsArray, function(val, index) {
|
||||
labelsArray[index] = template(templateString, {
|
||||
value: (graphMin + (stepValue * (index + 1)))
|
||||
});
|
||||
});
|
||||
}
|
||||
return labelsArray;
|
||||
},
|
||||
//--Animation methods
|
||||
//Easing functions adapted from Robert Penner's easing equations
|
||||
//http://www.robertpenner.com/easing/
|
||||
easingEffects = helpers.easingEffects = {
|
||||
linear: function(t) {
|
||||
return t;
|
||||
},
|
||||
easeInQuad: function(t) {
|
||||
return t * t;
|
||||
},
|
||||
easeOutQuad: function(t) {
|
||||
return -1 * t * (t - 2);
|
||||
},
|
||||
easeInOutQuad: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t;
|
||||
}
|
||||
return -1 / 2 * ((--t) * (t - 2) - 1);
|
||||
},
|
||||
easeInCubic: function(t) {
|
||||
return t * t * t;
|
||||
},
|
||||
easeOutCubic: function(t) {
|
||||
return 1 * ((t = t / 1 - 1) * t * t + 1);
|
||||
},
|
||||
easeInOutCubic: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t;
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * t + 2);
|
||||
},
|
||||
easeInQuart: function(t) {
|
||||
return t * t * t * t;
|
||||
},
|
||||
easeOutQuart: function(t) {
|
||||
return -1 * ((t = t / 1 - 1) * t * t * t - 1);
|
||||
},
|
||||
easeInOutQuart: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t * t;
|
||||
}
|
||||
return -1 / 2 * ((t -= 2) * t * t * t - 2);
|
||||
},
|
||||
easeInQuint: function(t) {
|
||||
return 1 * (t /= 1) * t * t * t * t;
|
||||
},
|
||||
easeOutQuint: function(t) {
|
||||
return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
|
||||
},
|
||||
easeInOutQuint: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t * t * t;
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
|
||||
},
|
||||
easeInSine: function(t) {
|
||||
return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
|
||||
},
|
||||
easeOutSine: function(t) {
|
||||
return 1 * Math.sin(t / 1 * (Math.PI / 2));
|
||||
},
|
||||
easeInOutSine: function(t) {
|
||||
return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
|
||||
},
|
||||
easeInExpo: function(t) {
|
||||
return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
|
||||
},
|
||||
easeOutExpo: function(t) {
|
||||
return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
|
||||
},
|
||||
easeInOutExpo: function(t) {
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * Math.pow(2, 10 * (t - 1));
|
||||
}
|
||||
return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
|
||||
},
|
||||
easeInCirc: function(t) {
|
||||
if (t >= 1) {
|
||||
return t;
|
||||
}
|
||||
return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
|
||||
},
|
||||
easeOutCirc: function(t) {
|
||||
return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
|
||||
},
|
||||
easeInOutCirc: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
|
||||
}
|
||||
return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
|
||||
},
|
||||
easeInElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1) == 1) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * 0.3;
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
|
||||
},
|
||||
easeOutElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1) == 1) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * 0.3;
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
|
||||
},
|
||||
easeInOutElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1 / 2) == 2) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * (0.3 * 1.5);
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
if (t < 1) {
|
||||
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
|
||||
}
|
||||
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
|
||||
},
|
||||
easeInBack: function(t) {
|
||||
var s = 1.70158;
|
||||
return 1 * (t /= 1) * t * ((s + 1) * t - s);
|
||||
},
|
||||
easeOutBack: function(t) {
|
||||
var s = 1.70158;
|
||||
return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
|
||||
},
|
||||
easeInOutBack: function(t) {
|
||||
var s = 1.70158;
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
|
||||
},
|
||||
easeInBounce: function(t) {
|
||||
return 1 - easingEffects.easeOutBounce(1 - t);
|
||||
},
|
||||
easeOutBounce: function(t) {
|
||||
if ((t /= 1) < (1 / 2.75)) {
|
||||
return 1 * (7.5625 * t * t);
|
||||
} else if (t < (2 / 2.75)) {
|
||||
return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
|
||||
} else if (t < (2.5 / 2.75)) {
|
||||
return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
|
||||
} else {
|
||||
return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
|
||||
}
|
||||
},
|
||||
easeInOutBounce: function(t) {
|
||||
if (t < 1 / 2) {
|
||||
return easingEffects.easeInBounce(t * 2) * 0.5;
|
||||
}
|
||||
return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
|
||||
}
|
||||
},
|
||||
//Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
|
||||
requestAnimFrame = helpers.requestAnimFrame = (function() {
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function(callback) {
|
||||
return window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})(),
|
||||
cancelAnimFrame = helpers.cancelAnimFrame = (function() {
|
||||
return window.cancelAnimationFrame ||
|
||||
window.webkitCancelAnimationFrame ||
|
||||
window.mozCancelAnimationFrame ||
|
||||
window.oCancelAnimationFrame ||
|
||||
window.msCancelAnimationFrame ||
|
||||
function(callback) {
|
||||
return window.clearTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})(),
|
||||
//-- DOM methods
|
||||
getRelativePosition = helpers.getRelativePosition = function(evt) {
|
||||
var mouseX, mouseY;
|
||||
var e = evt.originalEvent || evt,
|
||||
canvas = evt.currentTarget || evt.srcElement,
|
||||
boundingRect = canvas.getBoundingClientRect();
|
||||
|
||||
if (e.touches) {
|
||||
mouseX = e.touches[0].clientX - boundingRect.left;
|
||||
mouseY = e.touches[0].clientY - boundingRect.top;
|
||||
|
||||
} else {
|
||||
mouseX = e.clientX - boundingRect.left;
|
||||
mouseY = e.clientY - boundingRect.top;
|
||||
}
|
||||
|
||||
return {
|
||||
x: mouseX,
|
||||
y: mouseY
|
||||
};
|
||||
|
||||
},
|
||||
addEvent = helpers.addEvent = function(node, eventType, method) {
|
||||
if (node.addEventListener) {
|
||||
node.addEventListener(eventType, method);
|
||||
} else if (node.attachEvent) {
|
||||
node.attachEvent("on" + eventType, method);
|
||||
} else {
|
||||
node["on" + eventType] = method;
|
||||
}
|
||||
},
|
||||
removeEvent = helpers.removeEvent = function(node, eventType, handler) {
|
||||
if (node.removeEventListener) {
|
||||
node.removeEventListener(eventType, handler, false);
|
||||
} else if (node.detachEvent) {
|
||||
node.detachEvent("on" + eventType, handler);
|
||||
} else {
|
||||
node["on" + eventType] = noop;
|
||||
}
|
||||
},
|
||||
bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) {
|
||||
// Create the events object if it's not already present
|
||||
if (!chartInstance.events) chartInstance.events = {};
|
||||
|
||||
each(arrayOfEvents, function(eventName) {
|
||||
chartInstance.events[eventName] = function() {
|
||||
handler.apply(chartInstance, arguments);
|
||||
};
|
||||
addEvent(chartInstance.chart.canvas, eventName, chartInstance.events[eventName]);
|
||||
});
|
||||
},
|
||||
unbindEvents = helpers.unbindEvents = function(chartInstance, arrayOfEvents) {
|
||||
each(arrayOfEvents, function(handler, eventName) {
|
||||
removeEvent(chartInstance.chart.canvas, eventName, handler);
|
||||
});
|
||||
},
|
||||
getMaximumWidth = helpers.getMaximumWidth = function(domNode) {
|
||||
var container = domNode.parentNode,
|
||||
padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right'));
|
||||
// TODO = check cross browser stuff with this.
|
||||
return container.clientWidth - padding;
|
||||
},
|
||||
getMaximumHeight = helpers.getMaximumHeight = function(domNode) {
|
||||
var container = domNode.parentNode,
|
||||
padding = parseInt(getStyle(container, 'padding-bottom')) + parseInt(getStyle(container, 'padding-top'));
|
||||
// TODO = check cross browser stuff with this.
|
||||
return container.clientHeight - padding;
|
||||
},
|
||||
getStyle = helpers.getStyle = function(el, property) {
|
||||
return el.currentStyle ?
|
||||
el.currentStyle[property] :
|
||||
document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
|
||||
},
|
||||
getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support
|
||||
retinaScale = helpers.retinaScale = function(chart) {
|
||||
var ctx = chart.ctx,
|
||||
width = chart.canvas.width,
|
||||
height = chart.canvas.height;
|
||||
|
||||
if (window.devicePixelRatio) {
|
||||
ctx.canvas.style.width = width + "px";
|
||||
ctx.canvas.style.height = height + "px";
|
||||
ctx.canvas.height = height * window.devicePixelRatio;
|
||||
ctx.canvas.width = width * window.devicePixelRatio;
|
||||
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
}
|
||||
},
|
||||
//-- Canvas methods
|
||||
clear = helpers.clear = function(chart) {
|
||||
chart.ctx.clearRect(0, 0, chart.width, chart.height);
|
||||
},
|
||||
fontString = helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
|
||||
return fontStyle + " " + pixelSize + "px " + fontFamily;
|
||||
},
|
||||
longestText = helpers.longestText = function(ctx, font, arrayOfStrings) {
|
||||
ctx.font = font;
|
||||
var longest = 0;
|
||||
each(arrayOfStrings, function(string) {
|
||||
var textWidth = ctx.measureText(string).width;
|
||||
longest = (textWidth > longest) ? textWidth : longest;
|
||||
});
|
||||
return longest;
|
||||
},
|
||||
drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + radius, y);
|
||||
ctx.lineTo(x + width - radius, y);
|
||||
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
||||
ctx.lineTo(x + width, y + height - radius);
|
||||
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
||||
ctx.lineTo(x + radius, y + height);
|
||||
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||||
ctx.lineTo(x, y + radius);
|
||||
ctx.quadraticCurveTo(x, y, x + radius, y);
|
||||
ctx.closePath();
|
||||
},
|
||||
color = helpers.color = function(color) {
|
||||
if (!window.Color) {
|
||||
console.log('Color.js not found!');
|
||||
return color;
|
||||
}
|
||||
return window.Color(color);
|
||||
},
|
||||
isArray = helpers.isArray = function(obj) {
|
||||
if (!Array.isArray) {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]';
|
||||
}
|
||||
return Array.isArray(obj);
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
Arquivo executável
+106
@@ -0,0 +1,106 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: {{ version }}
|
||||
*
|
||||
* Copyright 2015 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart;
|
||||
|
||||
//Occupy the global variable of Chart, and create a simple base class
|
||||
var Chart = function(context, config) {
|
||||
var chart = this;
|
||||
this.config = config;
|
||||
|
||||
// Support a jQuery'd canvas element
|
||||
if (context.length && context[0].getContext) {
|
||||
context = context[0];
|
||||
}
|
||||
|
||||
// Support a canvas domnode
|
||||
if (context.getContext) {
|
||||
context = context.getContext("2d");
|
||||
}
|
||||
|
||||
this.canvas = context.canvas;
|
||||
|
||||
this.ctx = context;
|
||||
|
||||
//Variables global to the chart
|
||||
var computeDimension = function(element, dimension) {
|
||||
if (element['offset' + dimension]) {
|
||||
return element['offset' + dimension];
|
||||
} else {
|
||||
return document.defaultView.getComputedStyle(element).getPropertyValue(dimension);
|
||||
}
|
||||
};
|
||||
|
||||
var width = this.width = computeDimension(context.canvas, 'Width') || context.canvas.width;
|
||||
var height = this.height = computeDimension(context.canvas, 'Height') || context.canvas.height;
|
||||
|
||||
// Firefox requires this to work correctly
|
||||
context.canvas.width = width;
|
||||
context.canvas.height = height;
|
||||
|
||||
width = this.width = context.canvas.width;
|
||||
height = this.height = context.canvas.height;
|
||||
this.aspectRatio = this.width / this.height;
|
||||
//High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
|
||||
Chart.helpers.retinaScale(this);
|
||||
|
||||
if (config) {
|
||||
this.controller = new Chart.Controller(this);
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
//Globally expose the defaults to allow for user updating/changing
|
||||
Chart.defaults = {
|
||||
global: {
|
||||
responsive: true,
|
||||
responsiveAnimationDuration: 0,
|
||||
maintainAspectRatio: true,
|
||||
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
|
||||
hover: {
|
||||
onHover: null,
|
||||
mode: 'single',
|
||||
animationDuration: 400,
|
||||
},
|
||||
onClick: null,
|
||||
defaultColor: 'rgba(0,0,0,0.1)',
|
||||
|
||||
// Element defaults defined in element extensions
|
||||
elements: {}
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
if (typeof amd !== 'undefined') {
|
||||
define(function() {
|
||||
return Chart;
|
||||
});
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
module.exports = Chart;
|
||||
}
|
||||
|
||||
root.Chart = Chart;
|
||||
|
||||
Chart.noConflict = function() {
|
||||
root.Chart = previous;
|
||||
return Chart;
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,29 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
// Attach global event to resize each chart instance when the browser resizes
|
||||
helpers.addEvent(window, "resize", (function() {
|
||||
// Basic debounce of resize function so it doesn't hurt performance when resizing browser.
|
||||
var timeout;
|
||||
return function() {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function() {
|
||||
helpers.each(Chart.instances, function(instance) {
|
||||
// If the responsive flag is set in the chart instance config
|
||||
// Cascade the resize event down to the chart.
|
||||
if (instance.options.responsive) {
|
||||
instance.resize();
|
||||
}
|
||||
});
|
||||
}, 16);
|
||||
};
|
||||
})());
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,269 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
// The scale service is used to resize charts along with all of their axes. We make this as
|
||||
// a service where scales are registered with their respective charts so that changing the
|
||||
// scales does not require
|
||||
Chart.scaleService = {
|
||||
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
|
||||
// use the new chart options to grab the correct scale
|
||||
constructors: {},
|
||||
// Use a registration function so that we can move to an ES6 map when we no longer need to support
|
||||
// old browsers
|
||||
// Scale config defaults
|
||||
defaults: {},
|
||||
registerScaleType: function(type, scaleConstructor, defaults) {
|
||||
this.constructors[type] = scaleConstructor;
|
||||
this.defaults[type] = defaults;
|
||||
},
|
||||
getScaleConstructor: function(type) {
|
||||
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
|
||||
},
|
||||
getScaleDefaults: function(type) {
|
||||
return this.defaults.hasOwnProperty(type) ? this.defaults[type] : {};
|
||||
},
|
||||
// The interesting function
|
||||
fitScalesForChart: function(chartInstance, width, height) {
|
||||
var xPadding = width > 30 ? 5 : 2;
|
||||
var yPadding = height > 30 ? 5 : 2;
|
||||
|
||||
if (chartInstance) {
|
||||
var leftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "left";
|
||||
});
|
||||
var rightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "right";
|
||||
});
|
||||
var topScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "top";
|
||||
});
|
||||
var bottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "bottom";
|
||||
});
|
||||
|
||||
// Essentially we now have any number of scales on each of the 4 sides.
|
||||
// Our canvas looks like the following.
|
||||
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
|
||||
// B1 is the bottom axis
|
||||
// |------------------------------------------------------|
|
||||
// | | T1 | |
|
||||
// |----|-----|-------------------------------------|-----|
|
||||
// | | | | |
|
||||
// | L1 | L2 | Chart area | R1 |
|
||||
// | | | | |
|
||||
// | | | | |
|
||||
// |----|-----|-------------------------------------|-----|
|
||||
// | | B1 | |
|
||||
// | | | |
|
||||
// |------------------------------------------------------|
|
||||
|
||||
// What we do to find the best sizing, we do the following
|
||||
// 1. Determine the minimum size of the chart area.
|
||||
// 2. Split the remaining width equally between each vertical axis
|
||||
// 3. Split the remaining height equally between each horizontal axis
|
||||
// 4. Give each scale the maximum size it can be. The scale will return it's minimum size
|
||||
// 5. Adjust the sizes of each axis based on it's minimum reported size.
|
||||
// 6. Refit each axis
|
||||
// 7. Position each axis in the final location
|
||||
// 8. Tell the chart the final location of the chart area
|
||||
|
||||
// Step 1
|
||||
var chartWidth = width / 2; // min 50%
|
||||
var chartHeight = height / 2; // min 50%
|
||||
|
||||
chartWidth -= (2 * xPadding);
|
||||
chartHeight -= (2 * yPadding);
|
||||
|
||||
|
||||
// Step 2
|
||||
var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
|
||||
|
||||
// Step 3
|
||||
var horizontalScaleHeight = (height - chartHeight) / (topScales.length + bottomScales.length);
|
||||
|
||||
// Step 4;
|
||||
var minimumScaleSizes = [];
|
||||
|
||||
var verticalScaleMinSizeFunction = function(scaleInstance) {
|
||||
var minSize = scaleInstance.fit(verticalScaleWidth, chartHeight);
|
||||
minimumScaleSizes.push({
|
||||
horizontal: false,
|
||||
minSize: minSize,
|
||||
scale: scaleInstance,
|
||||
});
|
||||
};
|
||||
|
||||
var horizontalScaleMinSizeFunction = function(scaleInstance) {
|
||||
var minSize = scaleInstance.fit(chartWidth, horizontalScaleHeight);
|
||||
minimumScaleSizes.push({
|
||||
horizontal: true,
|
||||
minSize: minSize,
|
||||
scale: scaleInstance,
|
||||
});
|
||||
};
|
||||
|
||||
// vertical scales
|
||||
helpers.each(leftScales, verticalScaleMinSizeFunction);
|
||||
helpers.each(rightScales, verticalScaleMinSizeFunction);
|
||||
|
||||
// horizontal scales
|
||||
helpers.each(topScales, horizontalScaleMinSizeFunction);
|
||||
helpers.each(bottomScales, horizontalScaleMinSizeFunction);
|
||||
|
||||
// Step 5
|
||||
var maxChartHeight = height - (2 * yPadding);
|
||||
var maxChartWidth = width - (2 * xPadding);
|
||||
|
||||
helpers.each(minimumScaleSizes, function(wrapper) {
|
||||
if (wrapper.horizontal) {
|
||||
maxChartHeight -= wrapper.minSize.height;
|
||||
} else {
|
||||
maxChartWidth -= wrapper.minSize.width;
|
||||
}
|
||||
});
|
||||
|
||||
// At this point, maxChartHeight and maxChartWidth are the size the chart area could
|
||||
// be if the axes are drawn at their minimum sizes.
|
||||
|
||||
// Step 6
|
||||
var verticalScaleFitFunction = function(scaleInstance) {
|
||||
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
|
||||
return wrapper.scale === scaleInstance;
|
||||
});
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.fit(wrapper.minSize.width, maxChartHeight);
|
||||
}
|
||||
};
|
||||
|
||||
var horizontalScaleFitFunction = function(scaleInstance) {
|
||||
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
|
||||
return wrapper.scale === scaleInstance;
|
||||
});
|
||||
|
||||
var scaleMargin = {
|
||||
left: totalLeftWidth,
|
||||
right: totalRightWidth,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
|
||||
}
|
||||
};
|
||||
|
||||
var totalLeftWidth = xPadding;
|
||||
var totalRightWidth = xPadding;
|
||||
var totalTopHeight = yPadding;
|
||||
var totalBottomHeight = yPadding;
|
||||
|
||||
helpers.each(leftScales, verticalScaleFitFunction);
|
||||
helpers.each(rightScales, verticalScaleFitFunction);
|
||||
|
||||
// Figure out how much margin is on the left and right of the horizontal axes
|
||||
helpers.each(leftScales, function(scaleInstance) {
|
||||
totalLeftWidth += scaleInstance.width;
|
||||
});
|
||||
|
||||
helpers.each(rightScales, function(scaleInstance) {
|
||||
totalRightWidth += scaleInstance.width;
|
||||
});
|
||||
|
||||
helpers.each(topScales, horizontalScaleFitFunction);
|
||||
helpers.each(bottomScales, horizontalScaleFitFunction);
|
||||
|
||||
helpers.each(topScales, function(scaleInstance) {
|
||||
totalTopHeight += scaleInstance.height;
|
||||
});
|
||||
helpers.each(bottomScales, function(scaleInstance) {
|
||||
totalBottomHeight += scaleInstance.height;
|
||||
});
|
||||
|
||||
// Let the left scale know the final margin
|
||||
helpers.each(leftScales, function(scaleInstance) {
|
||||
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
|
||||
return wrapper.scale === scaleInstance;
|
||||
});
|
||||
|
||||
var scaleMargin = {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: totalTopHeight,
|
||||
bottom: totalBottomHeight
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
|
||||
}
|
||||
});
|
||||
|
||||
helpers.each(rightScales, function(scaleInstance) {
|
||||
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
|
||||
return wrapper.scale === scaleInstance;
|
||||
});
|
||||
|
||||
var scaleMargin = {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: totalTopHeight,
|
||||
bottom: totalBottomHeight
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
|
||||
}
|
||||
});
|
||||
|
||||
// Step 7
|
||||
// Position the scales
|
||||
var left = xPadding;
|
||||
var top = yPadding;
|
||||
var right = 0;
|
||||
var bottom = 0;
|
||||
|
||||
var verticalScalePlacer = function(scaleInstance) {
|
||||
scaleInstance.left = left;
|
||||
scaleInstance.right = left + scaleInstance.width;
|
||||
scaleInstance.top = totalTopHeight;
|
||||
scaleInstance.bottom = totalTopHeight + maxChartHeight;
|
||||
|
||||
// Move to next point
|
||||
left = scaleInstance.right;
|
||||
};
|
||||
|
||||
var horizontalScalePlacer = function(scaleInstance) {
|
||||
scaleInstance.left = totalLeftWidth;
|
||||
scaleInstance.right = totalLeftWidth + maxChartWidth;
|
||||
scaleInstance.top = top;
|
||||
scaleInstance.bottom = top + scaleInstance.height;
|
||||
|
||||
// Move to next point
|
||||
top = scaleInstance.bottom;
|
||||
};
|
||||
|
||||
helpers.each(leftScales, verticalScalePlacer);
|
||||
helpers.each(topScales, horizontalScalePlacer);
|
||||
|
||||
// Account for chart width and height
|
||||
left += maxChartWidth;
|
||||
top += maxChartHeight;
|
||||
|
||||
helpers.each(rightScales, verticalScalePlacer);
|
||||
helpers.each(bottomScales, horizontalScalePlacer);
|
||||
|
||||
// Step 8
|
||||
chartInstance.chartArea = {
|
||||
left: totalLeftWidth,
|
||||
top: totalTopHeight,
|
||||
right: totalLeftWidth + maxChartWidth,
|
||||
bottom: totalTopHeight + maxChartHeight,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}).call(this);
|
||||
@@ -0,0 +1,333 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.tooltips = {
|
||||
enabled: true,
|
||||
custom: null,
|
||||
backgroundColor: "rgba(0,0,0,0.8)",
|
||||
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
fontSize: 10,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#fff",
|
||||
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
titleFontSize: 12,
|
||||
titleFontStyle: "bold",
|
||||
titleFontColor: "#fff",
|
||||
yPadding: 6,
|
||||
xPadding: 6,
|
||||
caretSize: 8,
|
||||
cornerRadius: 6,
|
||||
xOffset: 10,
|
||||
template: [
|
||||
'<% if(label){ %>',
|
||||
'<%=label %>: ',
|
||||
'<% } %>',
|
||||
'<%=value %>',
|
||||
].join(''),
|
||||
multiTemplate: [
|
||||
'<%if (datasetLabel){ %>',
|
||||
'<%=datasetLabel %>: ',
|
||||
'<% } %>',
|
||||
'<%=value %>'
|
||||
].join(''),
|
||||
multiKeyBackground: '#fff',
|
||||
};
|
||||
|
||||
Chart.Tooltip = Chart.Element.extend({
|
||||
initialize: function() {
|
||||
var options = this._options;
|
||||
helpers.extend(this, {
|
||||
_model: {
|
||||
// Positioning
|
||||
xPadding: options.tooltips.xPadding,
|
||||
yPadding: options.tooltips.yPadding,
|
||||
xOffset: options.tooltips.xOffset,
|
||||
|
||||
// Labels
|
||||
textColor: options.tooltips.fontColor,
|
||||
_fontFamily: options.tooltips.fontFamily,
|
||||
_fontStyle: options.tooltips.fontStyle,
|
||||
fontSize: options.tooltips.fontSize,
|
||||
|
||||
// Title
|
||||
titleTextColor: options.tooltips.titleFontColor,
|
||||
_titleFontFamily: options.tooltips.titleFontFamily,
|
||||
_titleFontStyle: options.tooltips.titleFontStyle,
|
||||
titleFontSize: options.tooltips.titleFontSize,
|
||||
|
||||
// Appearance
|
||||
caretHeight: options.tooltips.caretSize,
|
||||
cornerRadius: options.tooltips.cornerRadius,
|
||||
backgroundColor: options.tooltips.backgroundColor,
|
||||
opacity: 0,
|
||||
legendColorBackground: options.tooltips.multiKeyBackground,
|
||||
},
|
||||
});
|
||||
},
|
||||
update: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
|
||||
switch (this._options.hover.mode) {
|
||||
case 'single':
|
||||
helpers.extend(this._model, {
|
||||
text: helpers.template(this._options.tooltips.template, {
|
||||
// These variables are available in the template function. Add others here
|
||||
element: this._active[0],
|
||||
value: this._data.datasets[this._active[0]._datasetIndex].data[this._active[0]._index],
|
||||
label: this._data.labels ? this._data.labels[this._active[0]._index] : '',
|
||||
}),
|
||||
});
|
||||
|
||||
var tooltipPosition = this._active[0].tooltipPosition();
|
||||
helpers.extend(this._model, {
|
||||
x: Math.round(tooltipPosition.x),
|
||||
y: Math.round(tooltipPosition.y),
|
||||
caretPadding: tooltipPosition.padding
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case 'label':
|
||||
|
||||
// Tooltip Content
|
||||
|
||||
var dataArray,
|
||||
dataIndex;
|
||||
|
||||
var labels = [],
|
||||
colors = [];
|
||||
|
||||
for (var i = this._data.datasets.length - 1; i >= 0; i--) {
|
||||
dataArray = this._data.datasets[i].metaData;
|
||||
dataIndex = helpers.indexOf(dataArray, this._active[0]);
|
||||
if (dataIndex !== -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var medianPosition = (function(index) {
|
||||
// Get all the points at that particular index
|
||||
var elements = [],
|
||||
dataCollection,
|
||||
xPositions = [],
|
||||
yPositions = [],
|
||||
xMax,
|
||||
yMax,
|
||||
xMin,
|
||||
yMin;
|
||||
helpers.each(this._data.datasets, function(dataset) {
|
||||
dataCollection = dataset.metaData;
|
||||
if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()) {
|
||||
elements.push(dataCollection[dataIndex]);
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Reverse labels if stacked
|
||||
helpers.each(this._options.stacked ? elements.reverse() : elements, function(element) {
|
||||
xPositions.push(element._view.x);
|
||||
yPositions.push(element._view.y);
|
||||
|
||||
//Include any colour information about the element
|
||||
labels.push(helpers.template(this._options.tooltips.multiTemplate, {
|
||||
// These variables are available in the template function. Add others here
|
||||
element: element,
|
||||
datasetLabel: this._data.datasets[element._datasetIndex].label,
|
||||
value: this._data.datasets[element._datasetIndex].data[element._index],
|
||||
}));
|
||||
colors.push({
|
||||
fill: element._view.backgroundColor,
|
||||
stroke: element._view.borderColor
|
||||
});
|
||||
|
||||
}, this);
|
||||
|
||||
yMin = helpers.min(yPositions);
|
||||
yMax = helpers.max(yPositions);
|
||||
|
||||
xMin = helpers.min(xPositions);
|
||||
xMax = helpers.max(xPositions);
|
||||
|
||||
return {
|
||||
x: (xMin > this._chart.width / 2) ? xMin : xMax,
|
||||
y: (yMin + yMax) / 2,
|
||||
};
|
||||
}).call(this, dataIndex);
|
||||
|
||||
// Apply for now
|
||||
helpers.extend(this._model, {
|
||||
x: medianPosition.x,
|
||||
y: medianPosition.y,
|
||||
labels: labels,
|
||||
title: this._data.labels && this._data.labels.length ? this._data.labels[this._active[0]._index] : '',
|
||||
legendColors: colors,
|
||||
legendBackgroundColor: this._options.tooltips.multiKeyBackground,
|
||||
});
|
||||
|
||||
|
||||
// Calculate Appearance Tweaks
|
||||
|
||||
this._model.height = (labels.length * this._model.fontSize) + ((labels.length - 1) * (this._model.fontSize / 2)) + (this._model.yPadding * 2) + this._model.titleFontSize * 1.5;
|
||||
|
||||
var titleWidth = ctx.measureText(this.title).width,
|
||||
//Label has a legend square as well so account for this.
|
||||
labelWidth = helpers.longestText(ctx, this.font, labels) + this._model.fontSize + 3,
|
||||
longestTextWidth = helpers.max([labelWidth, titleWidth]);
|
||||
|
||||
this._model.width = longestTextWidth + (this._model.xPadding * 2);
|
||||
|
||||
|
||||
var halfHeight = this._model.height / 2;
|
||||
|
||||
//Check to ensure the height will fit on the canvas
|
||||
if (this._model.y - halfHeight < 0) {
|
||||
this._model.y = halfHeight;
|
||||
} else if (this._model.y + halfHeight > this._chart.height) {
|
||||
this._model.y = this._chart.height - halfHeight;
|
||||
}
|
||||
|
||||
//Decide whether to align left or right based on position on canvas
|
||||
if (this._model.x > this._chart.width / 2) {
|
||||
this._model.x -= this._model.xOffset + this._model.width;
|
||||
} else {
|
||||
this._model.x += this._model.xOffset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
|
||||
switch (this._options.hover.mode) {
|
||||
case 'single':
|
||||
|
||||
ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
|
||||
|
||||
vm.xAlign = "center";
|
||||
vm.yAlign = "above";
|
||||
|
||||
//Distance between the actual element.y position and the start of the tooltip caret
|
||||
var caretPadding = vm.caretPadding || 2;
|
||||
|
||||
var tooltipWidth = ctx.measureText(vm.text).width + 2 * vm.xPadding,
|
||||
tooltipRectHeight = vm.fontSize + 2 * vm.yPadding,
|
||||
tooltipHeight = tooltipRectHeight + vm.caretHeight + caretPadding;
|
||||
|
||||
if (vm.x + tooltipWidth / 2 > this._chart.width) {
|
||||
vm.xAlign = "left";
|
||||
} else if (vm.x - tooltipWidth / 2 < 0) {
|
||||
vm.xAlign = "right";
|
||||
}
|
||||
|
||||
if (vm.y - tooltipHeight < 0) {
|
||||
vm.yAlign = "below";
|
||||
}
|
||||
|
||||
var tooltipX = vm.x - tooltipWidth / 2,
|
||||
tooltipY = vm.y - tooltipHeight;
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
|
||||
|
||||
// Custom Tooltips
|
||||
if (this._custom) {
|
||||
this._custom(this._view);
|
||||
} else {
|
||||
switch (vm.yAlign) {
|
||||
case "above":
|
||||
//Draw a caret above the x/y
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vm.x, vm.y - caretPadding);
|
||||
ctx.lineTo(vm.x + vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
|
||||
ctx.lineTo(vm.x - vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
case "below":
|
||||
tooltipY = vm.y + caretPadding + vm.caretHeight;
|
||||
//Draw a caret below the x/y
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vm.x, vm.y + caretPadding);
|
||||
ctx.lineTo(vm.x + vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
|
||||
ctx.lineTo(vm.x - vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vm.xAlign) {
|
||||
case "left":
|
||||
tooltipX = vm.x - tooltipWidth + (vm.cornerRadius + vm.caretHeight);
|
||||
break;
|
||||
case "right":
|
||||
tooltipX = vm.x - (vm.cornerRadius + vm.caretHeight);
|
||||
break;
|
||||
}
|
||||
|
||||
helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipRectHeight, vm.cornerRadius);
|
||||
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillText(vm.text, tooltipX + tooltipWidth / 2, tooltipY + tooltipRectHeight / 2);
|
||||
|
||||
}
|
||||
break;
|
||||
case 'label':
|
||||
|
||||
helpers.drawRoundedRectangle(ctx, vm.x, vm.y - vm.height / 2, vm.width, vm.height, vm.cornerRadius);
|
||||
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
|
||||
ctx.textAlign = "left";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = helpers.color(vm.titleTextColor).alpha(vm.opacity).rgbString();
|
||||
ctx.font = helpers.fontString(vm.fontSize, vm._titleFontStyle, vm._titleFontFamily);
|
||||
ctx.fillText(vm.title, vm.x + vm.xPadding, this.getLineHeight(0));
|
||||
|
||||
ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
|
||||
helpers.each(vm.labels, function(label, index) {
|
||||
ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
|
||||
ctx.fillText(label, vm.x + vm.xPadding + vm.fontSize + 3, this.getLineHeight(index + 1));
|
||||
|
||||
//A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
|
||||
//ctx.clearRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize/2, vm.fontSize, vm.fontSize);
|
||||
//Instead we'll make a white filled block to put the legendColour palette over.
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.legendColors[index].stroke).alpha(vm.opacity).rgbString();
|
||||
ctx.fillRect(vm.x + vm.xPadding - 1, this.getLineHeight(index + 1) - vm.fontSize / 2 - 1, vm.fontSize + 2, vm.fontSize + 2);
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.legendColors[index].fill).alpha(vm.opacity).rgbString();
|
||||
ctx.fillRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize / 2, vm.fontSize, vm.fontSize);
|
||||
|
||||
|
||||
}, this);
|
||||
break;
|
||||
}
|
||||
},
|
||||
getLineHeight: function(index) {
|
||||
var baseLineHeight = this._view.y - (this._view.height / 2) + this._view.yPadding,
|
||||
afterTitleIndex = index - 1;
|
||||
|
||||
//If the index is zero, we're getting the title
|
||||
if (index === 0) {
|
||||
return baseLineHeight + this._view.titleFontSize / 2;
|
||||
} else {
|
||||
return baseLineHeight + ((this._view.fontSize * 1.5 * afterTitleIndex) + this._view.fontSize / 2) + this._view.titleFontSize * 1.5;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,93 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: {{ version }}
|
||||
*
|
||||
* Copyright 2015 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.elements.arc = {
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderColor: "#fff",
|
||||
borderWidth: 2
|
||||
};
|
||||
|
||||
Chart.elements.Arc = Chart.Element.extend({
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
inRange: function(chartX, chartY) {
|
||||
|
||||
var vm = this._view;
|
||||
|
||||
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
|
||||
x: chartX,
|
||||
y: chartY
|
||||
});
|
||||
|
||||
// Put into the range of (-PI/2, 3PI/2]
|
||||
var startAngle = vm.startAngle < (-0.5 * Math.PI) ? vm.startAngle + (2.0 * Math.PI) : vm.startAngle > (1.5 * Math.PI) ? vm.startAngle - (2.0 * Math.PI) : vm.startAngle;
|
||||
var endAngle = vm.endAngle < (-0.5 * Math.PI) ? vm.endAngle + (2.0 * Math.PI) : vm.endAngle > (1.5 * Math.PI) ? vm.endAngle - (2.0 * Math.PI) : vm.endAngle
|
||||
|
||||
//Check if within the range of the open/close angle
|
||||
var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle),
|
||||
withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius);
|
||||
|
||||
return (betweenAngles && withinRadius);
|
||||
//Ensure within the outside of the arc centre, but inside arc outer
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
|
||||
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
|
||||
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
|
||||
return {
|
||||
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
|
||||
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
|
||||
};
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(vm.x, vm.y, vm.outerRadius, vm.startAngle, vm.endAngle);
|
||||
|
||||
ctx.arc(vm.x, vm.y, vm.innerRadius, vm.endAngle, vm.startAngle, true);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = vm.borderWidth;
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor;
|
||||
|
||||
ctx.fill();
|
||||
ctx.lineJoin = 'bevel';
|
||||
|
||||
if (vm.borderWidth) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,178 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: {{ version }}
|
||||
*
|
||||
* Copyright 2015 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.elements.line = {
|
||||
tension: 0.4,
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 3,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
fill: true, // do we fill in the area between the line and its base axis
|
||||
skipNull: true,
|
||||
drawNull: false,
|
||||
};
|
||||
|
||||
|
||||
Chart.elements.Line = Chart.Element.extend({
|
||||
draw: function() {
|
||||
|
||||
var vm = this._view;
|
||||
var ctx = this._chart.ctx;
|
||||
var first = this._children[0];
|
||||
var last = this._children[this._children.length - 1];
|
||||
|
||||
// Draw the background first (so the border is always on top)
|
||||
helpers.each(this._children, function(point, index) {
|
||||
var previous = helpers.previousItem(this._children, index);
|
||||
var next = helpers.nextItem(this._children, index);
|
||||
|
||||
// First point only
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Skip and drag along scale baseline
|
||||
if (point._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.lineTo(previous._view.x, point._view.y);
|
||||
ctx.moveTo(next._view.x, point._view.y);
|
||||
}
|
||||
// End Skip Stright line from the base line
|
||||
else if (previous._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(point._view.x, previous._view.y);
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
|
||||
if (previous._view.skip && vm.skipNull) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
}
|
||||
// Normal Bezier Curve
|
||||
else {
|
||||
if (vm.tension > 0) {
|
||||
ctx.bezierCurveTo(
|
||||
previous._view.controlPointNextX,
|
||||
previous._view.controlPointNextY,
|
||||
point._view.controlPointPreviousX,
|
||||
point._view.controlPointPreviousY,
|
||||
point._view.x,
|
||||
point._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
// For radial scales, loop back around to the first point
|
||||
if (this._loop) {
|
||||
if (vm.tension > 0 && !first._view.skip) {
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
last._view.controlPointNextX,
|
||||
last._view.controlPointNextY,
|
||||
first._view.controlPointPreviousX,
|
||||
first._view.controlPointPreviousY,
|
||||
first._view.x,
|
||||
first._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(first._view.x, first._view.y);
|
||||
}
|
||||
}
|
||||
|
||||
// If we had points and want to fill this line, do so.
|
||||
if (this._children.length > 0 && vm.fill) {
|
||||
//Round off the line by going to the base of the chart, back to the start, then fill.
|
||||
ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero);
|
||||
ctx.lineTo(this._children[0]._view.x, vm.scaleZero);
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
|
||||
// Now draw the line between all the points with any borders
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.defaultColor;
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
|
||||
ctx.beginPath();
|
||||
|
||||
helpers.each(this._children, function(point, index) {
|
||||
var previous = helpers.previousItem(this._children, index);
|
||||
var next = helpers.nextItem(this._children, index);
|
||||
|
||||
// First point only
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Skip and drag along scale baseline
|
||||
if (point._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(previous._view.x, point._view.y);
|
||||
ctx.moveTo(next._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
// End Skip Stright line from the base line
|
||||
if (previous._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(point._view.x, previous._view.y);
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (previous._view.skip && vm.skipNull) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
// Normal Bezier Curve
|
||||
if (vm.tension > 0) {
|
||||
ctx.bezierCurveTo(
|
||||
previous._view.controlPointNextX,
|
||||
previous._view.controlPointNextY,
|
||||
point._view.controlPointPreviousX,
|
||||
point._view.controlPointPreviousY,
|
||||
point._view.x,
|
||||
point._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (this._loop && !first._view.skip) {
|
||||
if (vm.tension > 0) {
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
last._view.controlPointNextX,
|
||||
last._view.controlPointNextY,
|
||||
first._view.controlPointPreviousX,
|
||||
first._view.controlPointPreviousY,
|
||||
first._view.x,
|
||||
first._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(first._view.x, first._view.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ctx.stroke();
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,84 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: {{ version }}
|
||||
*
|
||||
* Copyright 2015 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.elements.point = {
|
||||
radius: 3,
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
// Hover
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1,
|
||||
};
|
||||
|
||||
|
||||
Chart.elements.Point = Chart.Element.extend({
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._view;
|
||||
var hoverRange = vm.hitRadius + vm.radius;
|
||||
return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2));
|
||||
},
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y,
|
||||
padding: vm.radius + vm.borderWidth
|
||||
};
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var vm = this._view;
|
||||
var ctx = this._chart.ctx;
|
||||
|
||||
|
||||
if (vm.skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vm.radius > 0 || vm.borderWidth > 0) {
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(vm.x, vm.y, vm.radius || Chart.defaults.global.elements.point.radius, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.point.borderWidth;
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,86 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.elements.rectangle = {
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 0,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
};
|
||||
|
||||
Chart.elements.Rectangle = Chart.Element.extend({
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
|
||||
var halfWidth = vm.width / 2,
|
||||
leftX = vm.x - halfWidth,
|
||||
rightX = vm.x + halfWidth,
|
||||
top = vm.base - (vm.base - vm.y),
|
||||
halfStroke = vm.borderWidth / 2;
|
||||
|
||||
// Canvas doesn't allow us to stroke inside the width so we can
|
||||
// adjust the sizes to fit if we're setting a stroke on the line
|
||||
if (vm.borderWidth) {
|
||||
leftX += halfStroke;
|
||||
rightX -= halfStroke;
|
||||
top += halfStroke;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor;
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = vm.borderWidth;
|
||||
|
||||
// It'd be nice to keep this class totally generic to any rectangle
|
||||
// and simply specify which border to miss out.
|
||||
ctx.moveTo(leftX, vm.base);
|
||||
ctx.lineTo(leftX, top);
|
||||
ctx.lineTo(rightX, top);
|
||||
ctx.lineTo(rightX, vm.base);
|
||||
ctx.fill();
|
||||
if (vm.borderWidth) {
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
height: function() {
|
||||
var vm = this._view;
|
||||
return vm.base - vm.y;
|
||||
},
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._view;
|
||||
if (vm.y < vm.base) {
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
|
||||
} else {
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y);
|
||||
}
|
||||
},
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
if (vm.y < vm.base) {
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.base
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,266 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
// Default config for a category scale
|
||||
var defaultConfig = {
|
||||
display: true,
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
};
|
||||
|
||||
var DatasetScale = Chart.Element.extend({
|
||||
isHorizontal: function() {
|
||||
return this.options.position == "top" || this.options.position == "bottom";
|
||||
},
|
||||
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
|
||||
// This must be called after fit has been run so that
|
||||
// this.left, this.top, this.right, and this.bottom have been defined
|
||||
if (this.isHorizontal()) {
|
||||
var isRotated = (this.labelRotation > 0);
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var valueWidth = innerWidth / Math.max((this.data.labels.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
|
||||
var valueOffset = (valueWidth * index) + this.paddingLeft;
|
||||
|
||||
if (this.options.gridLines.offsetGridLines && includeOffset) {
|
||||
valueOffset += (valueWidth / 2);
|
||||
}
|
||||
|
||||
return this.left + Math.round(valueOffset);
|
||||
} else {
|
||||
return this.top + (index * (this.height / this.data.labels.length));
|
||||
}
|
||||
},
|
||||
getPointPixelForValue: function(value, index, datasetIndex) {
|
||||
return this.getPixelForValue(value, index, datasetIndex, true);
|
||||
},
|
||||
|
||||
// Functions needed for bar charts
|
||||
calculateBaseWidth: function() {
|
||||
return (this.getPixelForValue(null, 1, 0, true) - this.getPixelForValue(null, 0, 0, true)) - (2 * this.options.categorySpacing);
|
||||
},
|
||||
calculateBarWidth: function(datasetCount) {
|
||||
//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
|
||||
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * this.options.spacing);
|
||||
|
||||
if (this.options.stacked) {
|
||||
return baseWidth;
|
||||
}
|
||||
return (baseWidth / datasetCount);
|
||||
},
|
||||
calculateBarX: function(datasetCount, datasetIndex, elementIndex) {
|
||||
var xWidth = this.calculateBaseWidth(),
|
||||
xAbsolute = this.getPixelForValue(null, elementIndex, datasetIndex, true) - (xWidth / 2),
|
||||
barWidth = this.calculateBarWidth(datasetCount);
|
||||
|
||||
if (this.options.stacked) {
|
||||
return xAbsolute + barWidth / 2;
|
||||
}
|
||||
|
||||
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * this.options.spacing) + barWidth / 2;
|
||||
},
|
||||
|
||||
calculateLabelRotation: function(maxHeight, margins) {
|
||||
//Get the width of each grid by calculating the difference
|
||||
//between x offsets between 0 and 1.
|
||||
var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
|
||||
this.ctx.font = labelFont;
|
||||
|
||||
var firstWidth = this.ctx.measureText(this.data.labels[0]).width;
|
||||
var lastWidth = this.ctx.measureText(this.data.labels[this.data.labels.length - 1]).width;
|
||||
var firstRotated;
|
||||
var lastRotated;
|
||||
|
||||
this.paddingRight = lastWidth / 2 + 3;
|
||||
this.paddingLeft = firstWidth / 2 + 3;
|
||||
|
||||
this.labelRotation = 0;
|
||||
|
||||
if (this.options.display) {
|
||||
var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.data.labels);
|
||||
var cosRotation;
|
||||
var sinRotation;
|
||||
var firstRotatedWidth;
|
||||
|
||||
this.labelWidth = originalLabelWidth;
|
||||
|
||||
//Allow 3 pixels x2 padding either side for label readability
|
||||
// only the index matters for a dataset scale, but we want a consistent interface between scales
|
||||
var gridWidth = Math.floor(this.getPixelForValue(0, 1) - this.getPixelForValue(0, 0)) - 6;
|
||||
|
||||
//Max label rotate should be 90 - also act as a loop counter
|
||||
while ((this.labelWidth > gridWidth && this.labelRotation === 0) || (this.labelWidth > gridWidth && this.labelRotation <= 90 && this.labelRotation > 0)) {
|
||||
cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
|
||||
sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
|
||||
|
||||
firstRotated = cosRotation * firstWidth;
|
||||
lastRotated = cosRotation * lastWidth;
|
||||
|
||||
// We're right aligning the text now.
|
||||
if (firstRotated + this.options.labels.fontSize / 2 > this.yLabelWidth) {
|
||||
this.paddingLeft = firstRotated + this.options.labels.fontSize / 2;
|
||||
}
|
||||
|
||||
this.paddingRight = this.options.labels.fontSize / 2;
|
||||
|
||||
if (sinRotation * originalLabelWidth > maxHeight) {
|
||||
// go back one step
|
||||
this.labelRotation--;
|
||||
break;
|
||||
}
|
||||
|
||||
this.labelRotation++;
|
||||
this.labelWidth = cosRotation * originalLabelWidth;
|
||||
|
||||
}
|
||||
} else {
|
||||
this.labelWidth = 0;
|
||||
this.paddingRight = 0;
|
||||
this.paddingLeft = 0;
|
||||
}
|
||||
|
||||
if (margins) {
|
||||
this.paddingLeft -= margins.left;
|
||||
this.paddingRight -= margins.right;
|
||||
|
||||
this.paddingLeft = Math.max(this.paddingLeft, 0);
|
||||
this.paddingRight = Math.max(this.paddingRight, 0);
|
||||
}
|
||||
|
||||
},
|
||||
// Fit this axis to the given size
|
||||
// @param {number} maxWidth : the max width the axis can be
|
||||
// @param {number} maxHeight: the max height the axis can be
|
||||
// @return {object} minSize : the minimum size needed to draw the axis
|
||||
fit: function(maxWidth, maxHeight, margins) {
|
||||
this.calculateLabelRotation(maxHeight, margins);
|
||||
|
||||
var minSize = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
|
||||
var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
|
||||
var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.data.labels);
|
||||
|
||||
// Width
|
||||
if (this.isHorizontal()) {
|
||||
minSize.width = maxWidth;
|
||||
this.width = maxWidth;
|
||||
} else if (this.options.display) {
|
||||
minSize.width = Math.min(longestLabelWidth + 6, maxWidth);
|
||||
}
|
||||
|
||||
// Height
|
||||
if (this.isHorizontal() && this.options.display) {
|
||||
var labelHeight = (Math.cos(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.labels.fontSize;
|
||||
minSize.height = Math.min(labelHeight, maxHeight);
|
||||
} else if (this.options.display) {
|
||||
minSize.width = Math.min(longestLabelWidth + 6, maxWidth);
|
||||
}
|
||||
|
||||
this.width = minSize.width;
|
||||
this.height = minSize.height;
|
||||
return minSize;
|
||||
},
|
||||
// Actualy draw the scale on the canvas
|
||||
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
|
||||
draw: function(chartArea) {
|
||||
if (this.options.display) {
|
||||
|
||||
var setContextLineSettings;
|
||||
|
||||
// Make sure we draw text in the correct color
|
||||
this.ctx.fillStyle = this.options.labels.fontColor;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
setContextLineSettings = true;
|
||||
var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 10;
|
||||
var yTickEnd = this.options.position == "bottom" ? this.top + 10 : this.bottom;
|
||||
var isRotated = this.labelRotation !== 0;
|
||||
|
||||
helpers.each(this.data.labels, function(label, index) {
|
||||
var xLineValue = this.getPixelForValue(label, index, null, false); // xvalues for grid lines
|
||||
var xLabelValue = this.getPixelForValue(label, index, null, true); // x values for labels (need to consider offsetLabel option)
|
||||
|
||||
if (this.options.gridLines.show) {
|
||||
if (index === 0) {
|
||||
// Draw the first index specially
|
||||
this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
|
||||
setContextLineSettings = true; // reset next time
|
||||
} else if (setContextLineSettings) {
|
||||
this.ctx.lineWidth = this.options.gridLines.lineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.color;
|
||||
setContextLineSettings = false;
|
||||
}
|
||||
|
||||
xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
|
||||
// Draw the label area
|
||||
this.ctx.beginPath();
|
||||
|
||||
if (this.options.gridLines.drawTicks) {
|
||||
this.ctx.moveTo(xLineValue, yTickStart);
|
||||
this.ctx.lineTo(xLineValue, yTickEnd);
|
||||
}
|
||||
|
||||
// Draw the chart area
|
||||
if (this.options.gridLines.drawOnChartArea) {
|
||||
this.ctx.moveTo(xLineValue, chartArea.top);
|
||||
this.ctx.lineTo(xLineValue, chartArea.bottom);
|
||||
}
|
||||
|
||||
// Need to stroke in the loop because we are potentially changing line widths & colours
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
if (this.options.labels.show) {
|
||||
this.ctx.save();
|
||||
this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.top + 8);
|
||||
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
|
||||
this.ctx.font = this.font;
|
||||
this.ctx.textAlign = (isRotated) ? "right" : "center";
|
||||
this.ctx.textBaseline = (isRotated) ? "middle" : "top";
|
||||
this.ctx.fillText(label, 0, 0);
|
||||
this.ctx.restore();
|
||||
}
|
||||
}, this);
|
||||
} else {
|
||||
// Vertical
|
||||
if (this.options.gridLines.show) {}
|
||||
|
||||
if (this.options.labels.show) {
|
||||
// Draw the labels
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
|
||||
}).call(this);
|
||||
@@ -0,0 +1,574 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
reverse: false,
|
||||
beginAtZero: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value.toLocaleString()%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
};
|
||||
|
||||
var LinearScale = Chart.Element.extend({
|
||||
isHorizontal: function() {
|
||||
return this.options.position == "top" || this.options.position == "bottom";
|
||||
},
|
||||
generateTicks: function(width, height) {
|
||||
// We need to decide how many ticks we are going to have. Each tick draws a grid line.
|
||||
// There are two possibilities. The first is that the user has manually overridden the scale
|
||||
// calculations in which case the job is easy. The other case is that we have to do it ourselves
|
||||
//
|
||||
// We assume at this point that the scale object has been updated with the following values
|
||||
// by the chart.
|
||||
// min: this is the minimum value of the scale
|
||||
// max: this is the maximum value of the scale
|
||||
// options: contains the options for the scale. This is referenced from the user settings
|
||||
// rather than being cloned. This ensures that updates always propogate to a redraw
|
||||
|
||||
// Reset the ticks array. Later on, we will draw a grid line at these positions
|
||||
// The array simply contains the numerical value of the spots where ticks will be
|
||||
this.ticks = [];
|
||||
|
||||
if (this.options.override) {
|
||||
// The user has specified the manual override. We use <= instead of < so that
|
||||
// we get the final line
|
||||
for (var i = 0; i <= this.options.override.steps; ++i) {
|
||||
var value = this.options.override.start + (i * this.options.override.stepWidth);
|
||||
ticks.push(value);
|
||||
}
|
||||
} else {
|
||||
// Figure out what the max number of ticks we can support it is based on the size of
|
||||
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
|
||||
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
|
||||
// the graph
|
||||
|
||||
var maxTicks;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
maxTicks = Math.min(11, Math.ceil(width / 50));
|
||||
} else {
|
||||
// The factor of 2 used to scale the font size has been experimentally determined.
|
||||
maxTicks = Math.min(11, Math.ceil(height / (2 * this.options.labels.fontSize)));
|
||||
}
|
||||
|
||||
// Make sure we always have at least 2 ticks
|
||||
maxTicks = Math.max(2, maxTicks);
|
||||
|
||||
// To get a "nice" value for the tick spacing, we will use the appropriately named
|
||||
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
|
||||
// for details.
|
||||
|
||||
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
|
||||
// do nothing since that would make the chart weird. If the user really wants a weird chart
|
||||
// axis, they can manually override it
|
||||
if (this.options.beginAtZero) {
|
||||
var minSign = helpers.sign(this.min);
|
||||
var maxSign = helpers.sign(this.max);
|
||||
|
||||
if (minSign < 0 && maxSign < 0) {
|
||||
// move the top up to 0
|
||||
this.max = 0;
|
||||
} else if (minSign > 0 && maxSign > 0) {
|
||||
// move the botttom down to 0
|
||||
this.min = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var niceRange = helpers.niceNum(this.max - this.min, false);
|
||||
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
|
||||
var niceMin = Math.floor(this.min / spacing) * spacing;
|
||||
var niceMax = Math.ceil(this.max / spacing) * spacing;
|
||||
|
||||
// Put the values into the ticks array
|
||||
for (var j = niceMin; j <= niceMax; j += spacing) {
|
||||
this.ticks.push(j);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.position == "left" || this.options.position == "right") {
|
||||
// We are in a vertical orientation. The top value is the highest. So reverse the array
|
||||
this.ticks.reverse();
|
||||
}
|
||||
|
||||
// At this point, we need to update our max and min given the tick values since we have expanded the
|
||||
// range of the scale
|
||||
this.max = helpers.max(this.ticks);
|
||||
this.min = helpers.min(this.ticks);
|
||||
|
||||
if (this.options.reverse) {
|
||||
this.ticks.reverse();
|
||||
|
||||
this.start = this.max;
|
||||
this.end = this.min;
|
||||
} else {
|
||||
this.start = this.min;
|
||||
this.end = this.max;
|
||||
}
|
||||
},
|
||||
buildLabels: function() {
|
||||
// We assume that this has been run after ticks have been generated. We try to figure out
|
||||
// a label for each tick.
|
||||
this.labels = [];
|
||||
|
||||
helpers.each(this.ticks, function(tick, index, ticks) {
|
||||
var label;
|
||||
|
||||
if (this.options.labels.userCallback) {
|
||||
// If the user provided a callback for label generation, use that as first priority
|
||||
label = this.options.labels.userCallback(tick, index, ticks);
|
||||
} else if (this.options.labels.template) {
|
||||
// else fall back to the template string
|
||||
label = helpers.template(this.options.labels.template, {
|
||||
value: tick
|
||||
});
|
||||
}
|
||||
|
||||
this.labels.push(label ? label : ""); // empty string will not render so we're good
|
||||
}, this);
|
||||
},
|
||||
// Get the correct value. If the value type is object get the x or y based on whether we are horizontal or not
|
||||
getRightValue: function(rawValue) {
|
||||
return typeof rawValue === "object" ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
|
||||
},
|
||||
getPixelForValue: function(value) {
|
||||
// This must be called after fit has been run so that
|
||||
// this.left, this.top, this.right, and this.bottom have been defined
|
||||
var pixel;
|
||||
var range = this.end - this.start;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
pixel = this.left + (this.width / range * (value - this.start));
|
||||
} else {
|
||||
// Bottom - top since pixels increase downard on a screen
|
||||
pixel = this.bottom - (this.height / range * (value - this.start));
|
||||
}
|
||||
|
||||
return pixel;
|
||||
},
|
||||
|
||||
// Functions needed for line charts
|
||||
calculateRange: function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
var positiveValues = [];
|
||||
var negativeValues = [];
|
||||
|
||||
if (this.options.stacked) {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(rawValue, index) {
|
||||
|
||||
var value = this.getRightValue(rawValue);
|
||||
|
||||
positiveValues[index] = positiveValues[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
|
||||
if (this.options.relativePoints) {
|
||||
positiveValues[index] = 100;
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negativeValues[index] += value;
|
||||
} else {
|
||||
positiveValues[index] += value;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
|
||||
var values = positiveValues.concat(negativeValues);
|
||||
this.min = helpers.min(values);
|
||||
this.max = helpers.max(values);
|
||||
|
||||
} else {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(rawValue, index) {
|
||||
var value = this.getRightValue(rawValue);
|
||||
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.min === this.max) {
|
||||
this.min--;
|
||||
this.max++;
|
||||
}
|
||||
},
|
||||
|
||||
getPointPixelForValue: function(rawValue, index, datasetIndex) {
|
||||
var value = this.getRightValue(rawValue);
|
||||
|
||||
if (this.options.stacked) {
|
||||
var offsetPos = 0;
|
||||
var offsetNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; ++i) {
|
||||
if (this.data.datasets[i].data[index] < 0) {
|
||||
offsetNeg += this.data.datasets[i].data[index];
|
||||
} else {
|
||||
offsetPos += this.data.datasets[i].data[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return this.getPixelForValue(offsetNeg + value);
|
||||
} else {
|
||||
return this.getPixelForValue(offsetPos + value);
|
||||
}
|
||||
} else {
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
},
|
||||
|
||||
// Functions needed for bar charts
|
||||
calculateBarBase: function(datasetIndex, index) {
|
||||
var base = 0;
|
||||
|
||||
if (this.options.stacked) {
|
||||
|
||||
var value = this.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (value < 0) {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (this.data.datasets[i].yAxisID === this.id) {
|
||||
base += this.data.datasets[i].data[index] < 0 ? this.data.datasets[i].data[index] : 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < datasetIndex; j++) {
|
||||
if (this.data.datasets[j].yAxisID === this.id) {
|
||||
base += this.data.datasets[j].data[index] > 0 ? this.data.datasets[j].data[index] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.getPixelForValue(base);
|
||||
}
|
||||
|
||||
base = this.getPixelForValue(this.min);
|
||||
|
||||
if (this.beginAtZero || ((this.min <= 0 && this.max >= 0) || (this.min >= 0 && this.max <= 0))) {
|
||||
base = this.getPixelForValue(0);
|
||||
base += this.options.gridLines.lineWidth;
|
||||
} else if (this.min < 0 && this.max < 0) {
|
||||
// All values are negative. Use the top as the base
|
||||
base = this.getPixelForValue(this.max);
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
},
|
||||
calculateBarY: function(datasetIndex, index) {
|
||||
var value = this.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (this.options.stacked) {
|
||||
|
||||
var sumPos = 0,
|
||||
sumNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (this.data.datasets[i].data[index] < 0) {
|
||||
sumNeg += this.data.datasets[i].data[index] || 0;
|
||||
} else {
|
||||
sumPos += this.data.datasets[i].data[index] || 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return this.getPixelForValue(sumNeg + value);
|
||||
} else {
|
||||
return this.getPixelForValue(sumPos + value);
|
||||
}
|
||||
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
|
||||
var offset = 0;
|
||||
|
||||
for (var j = datasetIndex; j < this.data.datasets.length; j++) {
|
||||
if (j === datasetIndex && value) {
|
||||
offset += value;
|
||||
} else {
|
||||
offset = offset + value;
|
||||
}
|
||||
}
|
||||
|
||||
return this.getPixelForValue(value);
|
||||
},
|
||||
|
||||
// Fit this axis to the given size
|
||||
// @param {number} maxWidth : the max width the axis can be
|
||||
// @param {number} maxHeight: the max height the axis can be
|
||||
// @return {object} minSize : the minimum size needed to draw the axis
|
||||
fit: function(maxWidth, maxHeight) {
|
||||
this.calculateRange();
|
||||
this.generateTicks(maxWidth, maxHeight);
|
||||
this.buildLabels();
|
||||
|
||||
var minSize = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
|
||||
// In a horizontal axis, we need some room for the scale to be drawn
|
||||
//
|
||||
// -----------------------------------------------------
|
||||
// | | | | |
|
||||
//
|
||||
// In a vertical axis, we need some room for the scale to be drawn.
|
||||
// The actual grid lines will be drawn on the chart area, however, we need to show
|
||||
// ticks where the axis actually is.
|
||||
// We will allocate 25px for this width
|
||||
// |
|
||||
// -|
|
||||
// |
|
||||
// |
|
||||
// -|
|
||||
// |
|
||||
// |
|
||||
// -|
|
||||
|
||||
|
||||
// Width
|
||||
if (this.isHorizontal()) {
|
||||
minSize.width = maxWidth; // fill all the width
|
||||
} else {
|
||||
minSize.width = this.options.gridLines.show && this.options.display ? 10 : 0;
|
||||
}
|
||||
|
||||
// height
|
||||
if (this.isHorizontal()) {
|
||||
minSize.height = this.options.gridLines.show && this.options.display ? 10 : 0;
|
||||
} else {
|
||||
minSize.height = maxHeight; // fill all the height
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (this.options.labels.show && this.options.display) {
|
||||
// Don't bother fitting the labels if we are not showing them
|
||||
var labelFont = helpers.fontString(this.options.labels.fontSize,
|
||||
this.options.labels.fontStyle, this.options.labels.fontFamily);
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
// A horizontal axis is more constrained by the height.
|
||||
var maxLabelHeight = maxHeight - minSize.height;
|
||||
var labelHeight = 1.5 * this.options.labels.fontSize;
|
||||
minSize.height = Math.min(maxHeight, minSize.height + labelHeight);
|
||||
} else {
|
||||
// A vertical axis is more constrained by the width. Labels are the dominant factor
|
||||
// here, so get that length first
|
||||
var maxLabelWidth = maxWidth - minSize.width;
|
||||
var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.labels);
|
||||
|
||||
if (largestTextWidth < maxLabelWidth) {
|
||||
// We don't need all the room
|
||||
minSize.width += largestTextWidth;
|
||||
} else {
|
||||
// Expand to max size
|
||||
minSize.width = maxWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.width = minSize.width;
|
||||
this.height = minSize.height;
|
||||
return minSize;
|
||||
},
|
||||
// Actualy draw the scale on the canvas
|
||||
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
|
||||
draw: function(chartArea) {
|
||||
if (this.options.display) {
|
||||
|
||||
var setContextLineSettings;
|
||||
var hasZero;
|
||||
|
||||
// Make sure we draw text in the correct color
|
||||
this.ctx.fillStyle = this.options.labels.fontColor;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
if (this.options.gridLines.show) {
|
||||
// Draw the horizontal line
|
||||
setContextLineSettings = true;
|
||||
hasZero = helpers.findNextWhere(this.ticks, function(tick) {
|
||||
return tick === 0;
|
||||
}) !== undefined;
|
||||
var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 5;
|
||||
var yTickEnd = this.options.position == "bottom" ? this.top + 5 : this.bottom;
|
||||
|
||||
helpers.each(this.ticks, function(tick, index) {
|
||||
// Grid lines are vertical
|
||||
var xValue = this.getPixelForValue(tick);
|
||||
|
||||
if (tick === 0 || (!hasZero && index === 0)) {
|
||||
// Draw the 0 point specially or the left if there is no 0
|
||||
this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
|
||||
setContextLineSettings = true; // reset next time
|
||||
} else if (setContextLineSettings) {
|
||||
this.ctx.lineWidth = this.options.gridLines.lineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.color;
|
||||
setContextLineSettings = false;
|
||||
}
|
||||
|
||||
xValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
|
||||
// Draw the label area
|
||||
this.ctx.beginPath();
|
||||
|
||||
if (this.options.gridLines.drawTicks) {
|
||||
this.ctx.moveTo(xValue, yTickStart);
|
||||
this.ctx.lineTo(xValue, yTickEnd);
|
||||
}
|
||||
|
||||
// Draw the chart area
|
||||
if (this.options.gridLines.drawOnChartArea) {
|
||||
this.ctx.moveTo(xValue, chartArea.top);
|
||||
this.ctx.lineTo(xValue, chartArea.bottom);
|
||||
}
|
||||
|
||||
// Need to stroke in the loop because we are potentially changing line widths & colours
|
||||
this.ctx.stroke();
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.options.labels.show) {
|
||||
// Draw the labels
|
||||
|
||||
var labelStartY;
|
||||
|
||||
if (this.options.position == "top") {
|
||||
labelStartY = this.bottom - 10;
|
||||
this.ctx.textBaseline = "bottom";
|
||||
} else {
|
||||
// bottom side
|
||||
labelStartY = this.top + 10;
|
||||
this.ctx.textBaseline = "top";
|
||||
}
|
||||
|
||||
this.ctx.textAlign = "center";
|
||||
this.ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
|
||||
|
||||
helpers.each(this.labels, function(label, index) {
|
||||
var xValue = this.getPixelForValue(this.ticks[index]);
|
||||
this.ctx.fillText(label, xValue, labelStartY);
|
||||
}, this);
|
||||
}
|
||||
} else {
|
||||
// Vertical
|
||||
if (this.options.gridLines.show) {
|
||||
|
||||
// Draw the vertical line
|
||||
setContextLineSettings = true;
|
||||
hasZero = helpers.findNextWhere(this.ticks, function(tick) {
|
||||
return tick === 0;
|
||||
}) !== undefined;
|
||||
var xTickStart = this.options.position == "right" ? this.left : this.right - 5;
|
||||
var xTickEnd = this.options.position == "right" ? this.left + 5 : this.right;
|
||||
|
||||
helpers.each(this.ticks, function(tick, index) {
|
||||
// Grid lines are horizontal
|
||||
var yValue = this.getPixelForValue(tick);
|
||||
|
||||
if (tick === 0 || (!hasZero && index === 0)) {
|
||||
// Draw the 0 point specially or the bottom if there is no 0
|
||||
this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
|
||||
setContextLineSettings = true; // reset next time
|
||||
} else if (setContextLineSettings) {
|
||||
this.ctx.lineWidth = this.options.gridLines.lineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.color;
|
||||
setContextLineSettings = false; // use boolean to indicate that we only want to do this once
|
||||
}
|
||||
|
||||
yValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
|
||||
// Draw the label area
|
||||
this.ctx.beginPath();
|
||||
|
||||
if (this.options.gridLines.drawTicks) {
|
||||
this.ctx.moveTo(xTickStart, yValue);
|
||||
this.ctx.lineTo(xTickEnd, yValue);
|
||||
}
|
||||
|
||||
// Draw the chart area
|
||||
if (this.options.gridLines.drawOnChartArea) {
|
||||
this.ctx.moveTo(chartArea.left, yValue);
|
||||
this.ctx.lineTo(chartArea.right, yValue);
|
||||
}
|
||||
|
||||
this.ctx.stroke();
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.options.labels.show) {
|
||||
// Draw the labels
|
||||
|
||||
var labelStartX;
|
||||
|
||||
if (this.options.position == "left") {
|
||||
labelStartX = this.right - 10;
|
||||
this.ctx.textAlign = "right";
|
||||
} else {
|
||||
// right side
|
||||
labelStartX = this.left + 5;
|
||||
this.ctx.textAlign = "left";
|
||||
}
|
||||
|
||||
this.ctx.textBaseline = "middle";
|
||||
this.ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
|
||||
|
||||
helpers.each(this.labels, function(label, index) {
|
||||
var yValue = this.getPixelForValue(this.ticks[index]);
|
||||
this.ctx.fillText(label, labelStartX, yValue);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig);
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,451 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to animate scaling the chart from the centre
|
||||
animate: true,
|
||||
|
||||
lineArc: false,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1,
|
||||
},
|
||||
|
||||
angleLines: {
|
||||
show: true,
|
||||
color: "rgba(0,0,0, 0.1)",
|
||||
lineWidth: 1
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: true,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value.toLocaleString()%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
showLabelBackdrop: true,
|
||||
|
||||
//String - The colour of the label backdrop
|
||||
backdropColor: "rgba(255,255,255,0.75)",
|
||||
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
backdropPaddingY: 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
backdropPaddingX: 2,
|
||||
},
|
||||
|
||||
pointLabels: {
|
||||
//String - Point label font declaration
|
||||
fontFamily: "'Arial'",
|
||||
|
||||
//String - Point label font weight
|
||||
fontStyle: "normal",
|
||||
|
||||
//Number - Point label font size in pixels
|
||||
fontSize: 10,
|
||||
|
||||
//String - Point label font colour
|
||||
fontColor: "#666",
|
||||
},
|
||||
};
|
||||
|
||||
var LinearRadialScale = Chart.Element.extend({
|
||||
initialize: function() {
|
||||
this.height = this.chart.height;
|
||||
this.width = this.chart.width;
|
||||
this.xCenter = this.chart.width / 2;
|
||||
this.yCenter = this.chart.height / 2;
|
||||
this.size = helpers.min([this.height, this.width]);
|
||||
this.valuesCount = this.data.labels.length;
|
||||
this.labels = this.data.labels;
|
||||
this.drawingArea = (this.options.display) ? (this.size / 2) - (this.options.labels.fontSize / 2 + this.options.labels.backdropPaddingY) : (this.size / 2);
|
||||
},
|
||||
update: function() {
|
||||
if (!this.options.lineArc) {
|
||||
this.setScaleSize();
|
||||
} else {
|
||||
this.drawingArea = (this.options.display) ? (this.size / 2) - (this.fontSize / 2 + this.backdropPaddingY) : (this.size / 2);
|
||||
}
|
||||
|
||||
this.buildYLabels();
|
||||
},
|
||||
calculateRange: function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
},
|
||||
generateTicks: function() {
|
||||
// We need to decide how many ticks we are going to have. Each tick draws a grid line.
|
||||
// There are two possibilities. The first is that the user has manually overridden the scale
|
||||
// calculations in which case the job is easy. The other case is that we have to do it ourselves
|
||||
//
|
||||
// We assume at this point that the scale object has been updated with the following values
|
||||
// by the chart.
|
||||
// min: this is the minimum value of the scale
|
||||
// max: this is the maximum value of the scale
|
||||
// options: contains the options for the scale. This is referenced from the user settings
|
||||
// rather than being cloned. This ensures that updates always propogate to a redraw
|
||||
|
||||
// Reset the ticks array. Later on, we will draw a grid line at these positions
|
||||
// The array simply contains the numerical value of the spots where ticks will be
|
||||
this.ticks = [];
|
||||
|
||||
if (this.options.override) {
|
||||
// The user has specified the manual override. We use <= instead of < so that
|
||||
// we get the final line
|
||||
for (var i = 0; i <= this.options.override.steps; ++i) {
|
||||
var value = this.options.override.start + (i * this.options.override.stepWidth);
|
||||
ticks.push(value);
|
||||
}
|
||||
} else {
|
||||
// Figure out what the max number of ticks we can support it is based on the size of
|
||||
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
|
||||
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
|
||||
// the graph
|
||||
|
||||
var maxTicks = Math.min(11, Math.ceil(this.drawingArea / (2 * this.options.labels.fontSize)));
|
||||
|
||||
// Make sure we always have at least 2 ticks
|
||||
maxTicks = Math.max(2, maxTicks);
|
||||
|
||||
// To get a "nice" value for the tick spacing, we will use the appropriately named
|
||||
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
|
||||
// for details.
|
||||
|
||||
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
|
||||
// do nothing since that would make the chart weird. If the user really wants a weird chart
|
||||
// axis, they can manually override it
|
||||
if (this.options.beginAtZero) {
|
||||
var minSign = helpers.sign(this.min);
|
||||
var maxSign = helpers.sign(this.max);
|
||||
|
||||
if (minSign < 0 && maxSign < 0) {
|
||||
// move the top up to 0
|
||||
this.max = 0;
|
||||
} else if (minSign > 0 && maxSign > 0) {
|
||||
// move the botttom down to 0
|
||||
this.min = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var niceRange = helpers.niceNum(this.max - this.min, false);
|
||||
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
|
||||
var niceMin = Math.floor(this.min / spacing) * spacing;
|
||||
var niceMax = Math.ceil(this.max / spacing) * spacing;
|
||||
|
||||
// Put the values into the ticks array
|
||||
for (var j = niceMin; j <= niceMax; j += spacing) {
|
||||
this.ticks.push(j);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.position == "left" || this.options.position == "right") {
|
||||
// We are in a vertical orientation. The top value is the highest. So reverse the array
|
||||
this.ticks.reverse();
|
||||
}
|
||||
|
||||
// At this point, we need to update our max and min given the tick values since we have expanded the
|
||||
// range of the scale
|
||||
this.max = helpers.max(this.ticks);
|
||||
this.min = helpers.min(this.ticks);
|
||||
},
|
||||
buildYLabels: function() {
|
||||
this.yLabels = [];
|
||||
|
||||
helpers.each(this.ticks, function(tick, index, ticks) {
|
||||
var label;
|
||||
|
||||
if (this.options.labels.userCallback) {
|
||||
// If the user provided a callback for label generation, use that as first priority
|
||||
label = this.options.labels.userCallback(tick, index, ticks);
|
||||
} else if (this.options.labels.template) {
|
||||
// else fall back to the template string
|
||||
label = helpers.template(this.options.labels.template, {
|
||||
value: tick
|
||||
});
|
||||
}
|
||||
|
||||
this.yLabels.push(label ? label : "");
|
||||
}, this);
|
||||
},
|
||||
getCircumference: function() {
|
||||
return ((Math.PI * 2) / this.valuesCount);
|
||||
},
|
||||
setScaleSize: function() {
|
||||
/*
|
||||
* Right, this is really confusing and there is a lot of maths going on here
|
||||
* The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
|
||||
*
|
||||
* Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
|
||||
*
|
||||
* Solution:
|
||||
*
|
||||
* We assume the radius of the polygon is half the size of the canvas at first
|
||||
* at each index we check if the text overlaps.
|
||||
*
|
||||
* Where it does, we store that angle and that index.
|
||||
*
|
||||
* After finding the largest index and angle we calculate how much we need to remove
|
||||
* from the shape radius to move the point inwards by that x.
|
||||
*
|
||||
* We average the left and right distances to get the maximum shape radius that can fit in the box
|
||||
* along with labels.
|
||||
*
|
||||
* Once we have that, we can find the centre point for the chart, by taking the x text protrusion
|
||||
* on each side, removing that from the size, halving it and adding the left x protrusion width.
|
||||
*
|
||||
* This will mean we have a shape fitted to the canvas, as large as it can be with the labels
|
||||
* and position it in the most space efficient manner
|
||||
*
|
||||
* https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
|
||||
*/
|
||||
|
||||
|
||||
// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
|
||||
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
|
||||
var largestPossibleRadius = helpers.min([(this.height / 2 - this.options.pointLabels.fontSize - 5), this.width / 2]),
|
||||
pointPosition,
|
||||
i,
|
||||
textWidth,
|
||||
halfTextWidth,
|
||||
furthestRight = this.width,
|
||||
furthestRightIndex,
|
||||
furthestRightAngle,
|
||||
furthestLeft = 0,
|
||||
furthestLeftIndex,
|
||||
furthestLeftAngle,
|
||||
xProtrusionLeft,
|
||||
xProtrusionRight,
|
||||
radiusReductionRight,
|
||||
radiusReductionLeft,
|
||||
maxWidthRadius;
|
||||
this.ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily);
|
||||
for (i = 0; i < this.valuesCount; i++) {
|
||||
// 5px to space the text slightly out - similar to what we do in the draw function.
|
||||
pointPosition = this.getPointPosition(i, largestPossibleRadius);
|
||||
textWidth = this.ctx.measureText(helpers.template(this.options.labels.template, {
|
||||
value: this.labels[i]
|
||||
})).width + 5;
|
||||
if (i === 0 || i === this.valuesCount / 2) {
|
||||
// If we're at index zero, or exactly the middle, we're at exactly the top/bottom
|
||||
// of the radar chart, so text will be aligned centrally, so we'll half it and compare
|
||||
// w/left and right text sizes
|
||||
halfTextWidth = textWidth / 2;
|
||||
if (pointPosition.x + halfTextWidth > furthestRight) {
|
||||
furthestRight = pointPosition.x + halfTextWidth;
|
||||
furthestRightIndex = i;
|
||||
}
|
||||
if (pointPosition.x - halfTextWidth < furthestLeft) {
|
||||
furthestLeft = pointPosition.x - halfTextWidth;
|
||||
furthestLeftIndex = i;
|
||||
}
|
||||
} else if (i < this.valuesCount / 2) {
|
||||
// Less than half the values means we'll left align the text
|
||||
if (pointPosition.x + textWidth > furthestRight) {
|
||||
furthestRight = pointPosition.x + textWidth;
|
||||
furthestRightIndex = i;
|
||||
}
|
||||
} else if (i > this.valuesCount / 2) {
|
||||
// More than half the values means we'll right align the text
|
||||
if (pointPosition.x - textWidth < furthestLeft) {
|
||||
furthestLeft = pointPosition.x - textWidth;
|
||||
furthestLeftIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xProtrusionLeft = furthestLeft;
|
||||
|
||||
xProtrusionRight = Math.ceil(furthestRight - this.width);
|
||||
|
||||
furthestRightAngle = this.getIndexAngle(furthestRightIndex);
|
||||
|
||||
furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
|
||||
|
||||
radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
|
||||
|
||||
radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
|
||||
|
||||
// Ensure we actually need to reduce the size of the chart
|
||||
radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
|
||||
radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
|
||||
|
||||
this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2;
|
||||
|
||||
//this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])
|
||||
this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
|
||||
|
||||
},
|
||||
setCenterPoint: function(leftMovement, rightMovement) {
|
||||
|
||||
var maxRight = this.width - rightMovement - this.drawingArea,
|
||||
maxLeft = leftMovement + this.drawingArea;
|
||||
|
||||
this.xCenter = (maxLeft + maxRight) / 2;
|
||||
// Always vertically in the centre as the text height doesn't change
|
||||
this.yCenter = (this.height / 2);
|
||||
},
|
||||
|
||||
getIndexAngle: function(index) {
|
||||
var angleMultiplier = (Math.PI * 2) / this.valuesCount;
|
||||
// Start from the top instead of right, so remove a quarter of the circle
|
||||
|
||||
return index * angleMultiplier - (Math.PI / 2);
|
||||
},
|
||||
getDistanceFromCenterForValue: function(value) {
|
||||
// Take into account half font size + the yPadding of the top value
|
||||
var scalingFactor = this.drawingArea / (this.max - this.min);
|
||||
return (value - this.min) * scalingFactor;
|
||||
},
|
||||
getPointPosition: function(index, distanceFromCenter) {
|
||||
var thisAngle = this.getIndexAngle(index);
|
||||
return {
|
||||
x: (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
|
||||
y: (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
|
||||
};
|
||||
},
|
||||
getPointPositionForValue: function(index, value) {
|
||||
return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
|
||||
},
|
||||
draw: function() {
|
||||
if (this.options.display) {
|
||||
var ctx = this.ctx;
|
||||
helpers.each(this.yLabels, function(label, index) {
|
||||
// Don't draw a centre value
|
||||
if (index > 0) {
|
||||
var yCenterOffset = this.getDistanceFromCenterForValue(this.ticks[index]);
|
||||
var yHeight = this.yCenter - yCenterOffset;
|
||||
|
||||
// Draw circular lines around the scale
|
||||
if (this.options.gridLines.show) {
|
||||
ctx.strokeStyle = this.options.gridLines.color;
|
||||
ctx.lineWidth = this.options.gridLines.lineWidth;
|
||||
|
||||
if (this.options.lineArc) {
|
||||
// Draw circular arcs between the points
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
} else {
|
||||
// Draw straight lines connecting each index
|
||||
ctx.beginPath();
|
||||
for (var i = 0; i < this.valuesCount; i++) {
|
||||
var pointPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.ticks[index]));
|
||||
if (i === 0) {
|
||||
ctx.moveTo(pointPosition.x, pointPosition.y);
|
||||
} else {
|
||||
ctx.lineTo(pointPosition.x, pointPosition.y);
|
||||
}
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.labels.show) {
|
||||
ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
|
||||
|
||||
if (this.options.labels.showLabelBackdrop) {
|
||||
var labelWidth = ctx.measureText(label).width;
|
||||
ctx.fillStyle = this.options.labels.backdropColor;
|
||||
ctx.fillRect(
|
||||
this.xCenter - labelWidth / 2 - this.options.labels.backdropPaddingX,
|
||||
yHeight - this.fontSize / 2 - this.options.labels.backdropPaddingY,
|
||||
labelWidth + this.options.labels.backdropPaddingX * 2,
|
||||
this.options.labels.fontSize + this.options.labels.backdropPaddingY * 2
|
||||
);
|
||||
}
|
||||
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = this.options.labels.fontColor;
|
||||
ctx.fillText(label, this.xCenter, yHeight);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (!this.options.lineArc) {
|
||||
ctx.lineWidth = this.options.angleLines.lineWidth;
|
||||
ctx.strokeStyle = this.options.angleLines.color;
|
||||
|
||||
for (var i = this.valuesCount - 1; i >= 0; i--) {
|
||||
if (this.options.angleLines.show) {
|
||||
var outerPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.max));
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.xCenter, this.yCenter);
|
||||
ctx.lineTo(outerPosition.x, outerPosition.y);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
// Extra 3px out for some label spacing
|
||||
var pointLabelPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.max) + 5);
|
||||
ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily);
|
||||
ctx.fillStyle = this.options.pointLabels.fontColor;
|
||||
|
||||
var labelsCount = this.labels.length,
|
||||
halfLabelsCount = this.labels.length / 2,
|
||||
quarterLabelsCount = halfLabelsCount / 2,
|
||||
upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
|
||||
exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
|
||||
if (i === 0) {
|
||||
ctx.textAlign = 'center';
|
||||
} else if (i === halfLabelsCount) {
|
||||
ctx.textAlign = 'center';
|
||||
} else if (i < halfLabelsCount) {
|
||||
ctx.textAlign = 'left';
|
||||
} else {
|
||||
ctx.textAlign = 'right';
|
||||
}
|
||||
|
||||
// Set the correct text baseline based on outer positioning
|
||||
if (exactQuarter) {
|
||||
ctx.textBaseline = 'middle';
|
||||
} else if (upperHalf) {
|
||||
ctx.textBaseline = 'bottom';
|
||||
} else {
|
||||
ctx.textBaseline = 'top';
|
||||
}
|
||||
|
||||
ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig);
|
||||
|
||||
|
||||
}).call(this);
|
||||
Referência em uma Nova Issue
Bloquear um usuário