Comparar commits

..

134 Commits

Autor SHA1 Mensagem Data
Evert Timberg 5a5d698b11 Fix some errors with docs that were noticed when building 2015-12-28 08:29:54 -05:00
Evert Timberg e2206ab269 Merge pull request #1835 from nnnick/fix/release-v2-beta2
Update build & bump version
2015-12-27 11:50:05 -05:00
etimberg ff11975648 Fix issues identified in PR 2015-12-27 11:48:14 -05:00
etimberg 421e610150 Update build & bump version 2015-12-27 10:46:08 -05:00
Evert Timberg 94a8c9a27f Merge pull request #1832 from hallatore/Bug_SuggestedMinVsMin
min/max should have precedence over suggestedMin/suggestedMax
2015-12-25 10:44:29 -05:00
Tore Lervik a0c359ee80 min/max should have precedence over suggestedMin/suggestedMax 2015-12-25 13:53:09 +01:00
Tanner Linsley 31721a353c Merge pull request #1829 from nnnick/fix/1716-1731
Better tooltip positioning
2015-12-23 10:22:34 -07:00
Evert Timberg bbc6f5aa5f Fix broken test 2015-12-23 12:00:56 -05:00
Evert Timberg 968a11a631 Merge pull request #1826 from hallatore/Bug_BezierCurveAtZero
Fixed bug with bezier still being used with tension set to 0
2015-12-23 09:39:21 -05:00
Tore Lervik a1940caf91 Fixed build error bug with getting tension 2015-12-23 15:29:25 +01:00
Evert Timberg d9e1b2250e Merge pull request #1827 from hallatore/Bug_AutoSkipCheck
Fixed bug with skipRatio check not being triggered correctly.
2015-12-23 09:09:41 -05:00
Tore Lervik ad67d1ecf8 Fixed bug with skipRatio check not being triggered correctly. 2015-12-23 13:42:24 +01:00
Tore Lervik 2c178a5670 Fixed bug with bezier still being used with tension set to 0 2015-12-23 13:36:44 +01:00
Evert Timberg f170fd1b8d Improvements to tooltip alignment to avoid the canvas edges 2015-12-22 21:35:23 -05:00
Evert Timberg a423542775 Fix tests 2015-12-22 09:13:02 -05:00
Evert Timberg d5727d7bc0 Merge pull request #1819 from xriss/v2.0-dev
gulp-umd
2015-12-22 08:44:04 -05:00
Kriss 1221d8311a gulp-umd
Explicitly pass root to the factory function.
2015-12-22 13:21:23 +00:00
Evert Timberg eb61f5c3fc Merge pull request #1817 from prashcr/patch-2
Add missing comma
2015-12-22 08:19:45 -05:00
Evert Timberg fd668086b2 Merge pull request #1816 from hallatore/Bug_AutoSkip
Refactored skipRatio to use the longest label text for calculation.
2015-12-22 08:19:25 -05:00
Prashanth Chandra 8e7162ca91 Add missing comma 2015-12-22 17:50:11 +08:00
Evert Timberg 923378e2af Merge pull request #1815 from hallatore/Bug_firstTickUndefined
Bug first tick undefined
2015-12-21 19:59:12 -05:00
Tore Lervik 0437968a4e Fixed broken tests 2015-12-21 23:47:38 +01:00
Tore Lervik d134394a1d Refactored out property 2015-12-21 23:37:09 +01:00
Tore Lervik cd02434943 Refactored skipRatio to use the longest label text for calculation. 2015-12-21 23:32:52 +01:00
Tore Lervik c078ae15f0 Fixed a typo 2015-12-21 22:43:20 +01:00
Tore Lervik 86ca455cb6 Refactored firstTick null check 2015-12-21 22:40:33 +01:00
Tore Lervik 477fc72693 firstTick and lastTick can be null if the chart is empty. 2015-12-21 22:34:31 +01:00
Evert Timberg d7643bf865 Initial refactor of tooltip code 2015-12-21 15:52:33 -05:00
Evert Timberg d26ab24e3c Show rectangle points at end of bars, even negative ones. 2015-12-21 15:52:19 -05:00
Evert Timberg fc3a3aedaf Merge pull request #1814 from hallatore/Bug_SuggestedMinMax
Have to check for undefined because "if (0.0)" would equal false.
2015-12-21 14:43:41 -05:00
Tore Lervik 3577d959d0 Have to check for undefined because "if (0.0)" would equal false. 2015-12-21 20:28:20 +01:00
Evert Timberg 63b14dc2a9 Merge pull request #1810 from olyckne/fix/ticks_callback
Fix ticks callback and auto skip
2015-12-21 08:23:55 -05:00
Mattias Lyckne 888a8dd797 Update docs 2015-12-21 14:21:51 +01:00
Mattias Lyckne 7363c6fcbe Dont set autoSkip false if userCallback is defined
”The reason being is that the user could replace either callback or
userCallbac”
2015-12-21 13:59:29 +01:00
Mattias Lyckne dec4a5f57a Fix tests 2015-12-21 08:43:48 +01:00
Evert Timberg d163122536 Merge pull request #1808 from nnnick/fix/1737
Fix sizing of tooltip when beforeBody or afterBody is very long
2015-12-20 18:51:24 -05:00
Evert Timberg bafd36b5ec Fix sizing of tooltip when beforeBody or afterBody is very line 2015-12-20 18:48:44 -05:00
Evert Timberg ae643d3d76 Update built files 2015-12-20 18:31:49 -05:00
Evert Timberg 5f4e8a4c13 Merge pull request #1794 from nnnick/update-scale-lifecycle
Update Scale Lifcycle
2015-12-19 14:29:49 -05:00
Evert Timberg 29770d3e67 Merge pull request #1802 from akagroundhog/v2.0-dev
xPadding/yPadding fix in 00-Getting-Started.md
2015-12-18 17:43:07 -05:00
akagroundhog 421e294e2c xPadding/yPadding fix in 00-Getting-Started.md
xPadding is horizontal padding (left & right), yPadding is vertical padding (top & bottom).
2015-12-19 00:19:32 +03:00
Mattias Lyckne 23b2d991f7 disable autoSkip when there is a userCallback or autoSkip is set to false 2015-12-18 16:18:49 +01:00
Evert Timberg d295e49907 Merge pull request #1797 from xriss/patch-1
maxRotation test is off by one
2015-12-17 17:04:42 -05:00
Kriss d9363d637a maxRotation test is off by one
Most notable when maxRotation=0 and you end up with the text becoming non centred as it has rotated it by 1 and switched to right alligned mode.
2015-12-17 22:00:12 +00:00
Evert Timberg f7d3ea3841 Merge pull request #1788 from nnnick/fix/extended-bars
Better extensions of bar controllers
2015-12-17 09:36:15 -05:00
Evert Timberg 283f1d7387 Update check to new system + fix tests 2015-12-17 09:31:01 -05:00
Evert Timberg c8d3831152 Split out data limits step of scales into it's own step. Wire up callbacks from the options to those in the scale life-cycle. Updated the docs accordingly 2015-12-17 09:21:04 -05:00
etimberg 9aee15fe6c Updated docs 2015-12-15 20:19:32 -05:00
etimberg 8efa577ef5 Bar dataset controller needs a better way to know which datasets are bars since users could make their own bar types. 2015-12-15 20:13:37 -05:00
Evert Timberg 3ccf3fd249 Merge pull request #1787 from nnnick/fix/min-max
Updates new min/max settings
2015-12-15 19:35:46 -05:00
Evert Timberg cddfba1676 Better test 2015-12-15 19:33:38 -05:00
Evert Timberg 274897fc24 Fix tests 2015-12-15 19:30:09 -05:00
Evert Timberg f51d7753bd Update logarithmic algorithm 2015-12-15 19:18:18 -05:00
Evert Timberg 3e3912973c Slight improvement to new min/max options so that linear scales look right 2015-12-15 18:58:05 -05:00
Evert Timberg 6d490fbbbb Merge pull request #1779 from olyckne/adds_min_and_max_to_y_axes
Adds min and max setting to axes
2015-12-14 21:55:17 -05:00
Mattias Lyckne 968db4e783 Adds min and max setting to axes 2015-12-14 09:22:34 +01:00
Evert Timberg 1170ab5481 Merge pull request #1778 from nnnick/fix/1582
Update docs for polar area / doughnut charts + extending chart.js
2015-12-13 16:54:20 -05:00
Evert Timberg c2ee14dec9 Update docs. Improved pie / polar area docs. Updated advanced docs regarding extending scales / chart types 2015-12-13 15:29:57 -05:00
Evert Timberg 139cbe1c64 All dataset controllers now inherit from a common base class Chart.DatasetController. This makes writing external controllers much easier 2015-12-13 14:35:40 -05:00
Evert Timberg 1854ea086c Merge pull request #1775 from nnnick/fix/1711
Call animation callbacks.
2015-12-12 10:12:49 -05:00
Evert Timberg 716e303230 Call animation callbacks. Created a sample file that shows their use by updating a progress bar during the animation. Updated the documentation to show how they are called. 2015-12-12 10:01:08 -05:00
Evert Timberg 47d04ced8f Merge pull request #1755 from nnnick/fix/1725
Build controllers before scales are updated to ensure scale IDs are correct
2015-12-12 09:05:39 -05:00
Evert Timberg 95332e39f5 Merge branch 'v2.0-dev' into fix/1725
Conflicts:
	src/core/core.controller.js
2015-12-12 09:01:59 -05:00
Evert Timberg ae3025a2e1 Merge pull request #1774 from nnnick/fix/1762
Handle tension being set to 0 properly
2015-12-12 08:56:10 -05:00
Evert Timberg 39e5daaca8 Better property picking. Added a helper function so we don't have to write a complicated ternary everywhere. Updated tests accordingly. 2015-12-12 08:52:06 -05:00
Derek Perkins 4add69432f Merge pull request #1750 from nnnick/v2.0-legend
Legend and Title Support
2015-12-11 18:52:26 -07:00
Derek Perkins 7825ee5ef2 Merge pull request #1770 from dchesterton/patch-1
Lowercase NPM package name
2015-12-11 18:51:21 -07:00
Daniel Chesterton 4eed79ee52 Lowercase NPM package name 2015-12-11 23:58:56 +00:00
Evert Timberg 571120d650 Merge pull request #1756 from nnnick/fix/code-climate
Fix up issues in core scale
2015-12-08 21:49:33 -05:00
Evert Timberg 7999aab26a Fix up issues in core scale 2015-12-08 20:22:02 -05:00
Evert Timberg d689e0fc47 When only the dataset array changes scales did not update correctly. This was due to the fact that the new datasets did not have scale IDs yet. By building the controllers first, the IDs are mapped if necessary. Then we can correctly update scales. 2015-12-08 19:57:58 -05:00
Evert Timberg 819f865723 Merge branch 'v2.0-dev' into v2.0-legend
Conflicts:
	src/controllers/controller.doughnut.js
2015-12-07 22:10:38 -05:00
Evert Timberg 4f8f182d21 Better sizing when not displaying 2015-12-07 21:58:48 -05:00
Evert Timberg a2e1a97382 Update docs + remove unused config option since we don't need 2 different callbacks. 2015-12-07 21:52:15 -05:00
Evert Timberg 60920cd75a Polar area legend toggles data 2015-12-07 21:42:58 -05:00
Evert Timberg 59b7927994 Toggling of doughnut items now works 2015-12-07 21:23:20 -05:00
Evert Timberg 8befbbe8f0 Use config onClick function to toggle datasets for bars/lines/radar charts. 2015-12-07 20:36:44 -05:00
Evert Timberg b0f69a1092 Generate nice legend boxes for all current chart types 2015-12-07 19:11:54 -05:00
Evert Timberg 3b750e7f27 Merge pull request #1749 from nnnick/fix/code-climate
Fix some issues found by code climate
2015-12-07 18:26:51 -05:00
Evert Timberg ffa2870b56 Fix a few more issues in the tooltips 2015-12-07 18:23:51 -05:00
Evert Timberg 1cb7dde0d5 Start fixing code climate issues 2015-12-07 18:19:56 -05:00
Evert Timberg 20b9395585 Merge pull request #1745 from nnnick/fix/1710
Remove unused tooltip options
2015-12-07 17:46:49 -05:00
Tanner Linsley 73e9ff44fe Merge pull request #1744 from nnnick/fix/1466
Time Scale Updates for Tooltips
2015-12-07 12:10:12 -07:00
Evert Timberg bffbaf6b37 Generate label strings using an option function. This allows new chart types to override 2015-12-06 12:53:45 -05:00
Evert Timberg eba2fce923 Docs + move defaults to correct place. 2015-12-06 11:19:55 -05:00
Evert Timberg 792ee587ae Split legend and title block functionality into 2 separate blocks. This allows the title to be on the top of the chart while the legend is on the bottom 2015-12-06 10:20:38 -05:00
Evert Timberg eaf183352c Polar are controller needs a similar update to the doughnut controller 2015-12-06 10:19:09 -05:00
Evert Timberg 7ab2a5ed9a Remove unused xOffset property from the tooltip. Use the multiKeyBackground. Update documentation accordingly. 2015-12-06 09:38:19 -05:00
Evert Timberg 28fc6841f3 Add tooltipFormat option to time scale options.
Fix bars when displayed on a time scale.
Updated sample files.
2015-12-05 19:33:33 -05:00
Evert Timberg 5df23df291 Move time sample files to sub folder 2015-12-05 10:54:45 -05:00
Evert Timberg 52ae1f7d3c Merge pull request #1743 from nnnick/fix/1576
Gracefully handle setting `options.animation` to `false`
2015-12-05 10:44:24 -05:00
Evert Timberg 0c247dde28 Gracefully handle setting options.animation to false which occurs when migrating configs from v1. 2015-12-05 10:40:05 -05:00
Evert Timberg 784fa78267 Merge pull request #1738 from nnnick/fix/1735
Call `beforeBody` and `afterBody` using the correct function.
2015-12-05 10:01:15 -05:00
Evert Timberg d21b3c6d7e Merge pull request #1741 from nnnick/fix/1706
Highlight correct items in dataset mode
2015-12-05 10:01:03 -05:00
Evert Timberg a1e35ade49 Merge pull request #1742 from nnnick/fix/1715
Fixes rotational animation of new arcs
2015-12-05 10:00:45 -05:00
Evert Timberg 89da20354c Fix tests 2015-12-05 09:55:17 -05:00
Evert Timberg 2f1006c6de Make sure we always have a valid end angle when reseting the model. Previously this was undefined and interpreted as 0 in the animation code. 2015-12-05 09:39:25 -05:00
Evert Timberg 744e1429b9 Label polar area and doughnut datasets 2015-12-04 20:32:18 -05:00
Evert Timberg 59fb1696a8 Highlight correct items when hover mode is set to 'dataset' 2015-12-04 20:01:04 -05:00
Evert Timberg 38afefde25 Doughnut controller needs to obey chart area size 2015-12-04 19:22:48 -05:00
Evert Timberg d905f19355 Fix draw issue when radar chart dataset has 0 points and hence no children 2015-12-03 22:04:00 -05:00
Evert Timberg d8704ee37d Add legends to sample files 2015-12-03 22:03:39 -05:00
Evert Timberg 0b260d57cc Fix JSHint issue 2015-12-03 21:25:36 -05:00
Evert Timberg dcc06eb3bb Merge branch 'v2.0-dev' into v2.0-legend
Conflicts:
	src/core/core.scaleService.js
2015-12-03 21:21:10 -05:00
Evert Timberg 0a92cf1b40 Call beforeBody and afterBody using the correct function. 2015-12-03 18:46:21 -05:00
Tanner Linsley 62bc4e42ec Merge pull request #1736 from nnnick/umd-build
Umd build
2015-12-02 20:37:04 -07:00
etimberg 95a172df06 Remove old and stuff. Use the correct color variable instead of window.color 2015-12-02 22:15:03 -05:00
etimberg 40c8289ce8 Time scale uses moment loaded through UMD 2015-12-02 21:10:51 -05:00
etimberg b388a1e873 Pipe the build through UMD to get proper UMD headers 2015-12-02 21:08:52 -05:00
Evert Timberg 14c2cf56d1 Merge pull request #1728 from ashayas/bugfix/1722
Realized minRotation not needed, removed as an option and removed from tests
2015-12-02 07:30:55 -05:00
npsecoding 164671ffeb Realized minRotaion not needed, removed as an option and removed from tests 2015-12-01 22:29:19 -08:00
Tanner Linsley 05043d4771 Merge pull request #1724 from nnnick/fix/1717
Allow time scale to use multiple of base units
2015-12-01 19:42:31 -07:00
etimberg 25f061f340 Fix test failures 2015-12-01 21:07:53 -05:00
etimberg bf277473c6 Update scale fitting to prevent horizontal scales from jumping around. 2015-12-01 21:01:13 -05:00
etimberg 6b3fd19fa9 Time scale will now scale units. For example, it will try 1hr then 2hrs then 3 hrs then 6 hrs then 12 hrs ... until it finds a unit that fits. 2015-12-01 20:02:44 -05:00
etimberg 37abe74d43 Improve drawing when title is placed below legend boxes 2015-11-23 22:12:34 -05:00
etimberg ea15aaaec2 Fix draw issue 2015-11-23 21:54:45 -05:00
Evert Timberg 10d056a4d1 Adds strikethrough of legend text when dataset is hidden 2015-11-23 21:04:41 -05:00
Evert Timberg 0d1f63e601 Update after legend toggle since scales may need to change 2015-11-23 19:03:24 -05:00
Evert Timberg afc6d9094b Allow clicking on legend text 2015-11-23 18:58:51 -05:00
Evert Timberg 60c1609ba9 Initial toggling of datasets from the legend.
Still need to do:
- clicking on text in legend
- toggling style of legend based on dataset style
- figure out if we need an
2015-11-22 22:31:08 -05:00
etimberg 0a17edd1ae Minor improvement to code that gets active elements 2015-11-21 22:44:11 -05:00
etimberg 2efb974189 ensure that fullwidth boxes are at very top / bottom 2015-11-21 22:10:39 -05:00
Evert Timberg 7b719e6432 Modify layout service to be able to place horizontal boxes as either full width or not full width. Currently the legend is the only full width box we have. Scales were modified so as not to be full width, however Core.Scale can handle boxes that are full width.
Updated unit tests to use correct 'boxes' property of chart instance. Tests did not require numerical updates!

Added a test to cover placing a full width box.
2015-11-21 10:51:32 -05:00
Evert Timberg 2a9b3e5221 Merge branch 'v2.0-dev' into v2.0-legend
Conflicts:
	src/core/core.controller.js
2015-11-21 09:23:33 -05:00
Evert Timberg 29002b2efb Fix old scaleService tests that are now testing the layoutService 2015-10-31 13:10:06 -04:00
Evert Timberg e8f80ca211 Rename test file 2015-10-31 13:08:36 -04:00
Tanner Linsley a1fb98c79f Line Sample Updated 2015-10-27 02:34:31 -06:00
Tanner Linsley 872b7b6161 Legend now centers labels and draws their box colors/styles 2015-10-27 02:34:23 -06:00
Tanner Linsley ab2c3ce4e9 Linear Scale now plays nice with full width 2015-10-27 02:33:45 -06:00
Tanner Linsley 2bc386ee81 Category Scale now plays nice with full width 2015-10-27 02:33:34 -06:00
Tanner Linsley 15ff3f9edc Core Scale now plays nice with layoutService and full width 2015-10-27 02:33:19 -06:00
Tanner Linsley 9ab505caec Layout Service readability & reorganization
The layout service now treats all horizontal boxes as full width.
Scales are expected to use the margins to know where to line up with
the chartArea.
2015-10-27 02:32:55 -06:00
Tanner Linsley a91dba3e8a 50% Canvas Legend Support 2015-10-26 13:57:59 -06:00
60 arquivos alterados com 5032 adições e 3205 exclusões
externo
+1758 -1138
Ver Arquivo
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+13 -12
Ver Arquivo
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+1 -1
Ver Arquivo
@@ -1,6 +1,6 @@
{
"name": "Chart.js",
"version": "2.0.0-beta",
"version": "2.0.0-beta2",
"description": "Simple HTML5 Charts using the canvas element",
"homepage": "https://github.com/nnnick/Chart.js",
"author": "nnnick",
+38 -8
Ver Arquivo
@@ -31,7 +31,7 @@ You can also grab Chart.js using bower, npm, or CDN:
bower install Chart.js --save
```
```bash
npm install Chart.js --save
npm install chart.js --save
```
https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0-beta/Chart.js
@@ -105,6 +105,37 @@ onClick | Function | null | Called if the event is of type 'mouseup' or 'click'.
defaultColor | Color | 'rgba(0,0,0,0.1)' |
legendCallback | Function | ` function (chart) { // the chart object to generate a legend from. }` | Function to generate a legend. Default implementation returns an HTML string.
The global options for the chart title is defined in `Chart.defaults.global.title`
Name | Type | Default | Description
--- | --- | --- | ---
display | Boolean | true | Show the title block
position | String | 'top' | Position of the title. 'top' or 'bottom' are allowed
fullWidth | Boolean | true | Marks that this box should take the full width of the canvas (pushing down other boxes)
fontColor | Color | '#666' | Text color
fontFamily | String | 'Helvetica Neue' |
fontSize | Number | 12 |
fontStyle | String | 'bold' |
padding | Number | 10 | Number of pixels to add above and below the title text
text | String | '' | Title text
The global options for the chart legend is defined in `Chart.defaults.global.legend`
Name | Type | Default | Description
--- | --- | --- | ---
display | Boolean | true | Is the legend displayed
position | String | 'top' | Position of the legend. Options are 'top' or 'bottom'
fullWidth | Boolean | true | Marks that this box should take the full width of the canvas (pushing down other boxes)
onClick | Function | `function(event, legendItem) {}` | A callback that is called when a click is registered on top of a label item
labels |-|-|-
*labels*boxWidth | Number | 40 | Width of coloured box
*labels*fontSize | Number | 12 | Font size
*labels*fontStyle | String | "normal" |
*labels*fontColor | Color | "#666" |
*labels*fontFamily | String | "Helvetica Neue" |
*labels*padding | Number | 10 | Padding between rows of colored boxes
*labels*generateLabels: | Function | `function(data) { } | Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. Styles that can be returned are `fillStyle`, `strokeStyle`, `lineCap`, `lineDash`, `lineDashOffset`, `lineWidth`, `lineJoin`. Return a `hidden` attribute to indicate that the label refers to something that is not visible. A strikethrough style will be given to the text in this case.
The global options for tooltips are defined in `Chart.defaults.global.tooltips`.
Name | Type | Default | Description
@@ -123,12 +154,11 @@ Label | | | There are three labels you can control. `title`, `body`, `footer` th
\*Align | String | "left" | text alignment. See [MDN Canvas Documentation](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textAlign)
titleMarginBottom | Number | 6 | Margin to add on bottom of title section
footerMarginTop | Number | 6 | Margin to add before drawing the footer
xPadding | Number | 6 | Padding to add on top and bottom of tooltip
yPadding | Number | 6 | Padding to add on left and right of tooltip
xPadding | Number | 6 | Padding to add on left and right of tooltip
yPadding | Number | 6 | Padding to add on top and bottom of tooltip
caretSize | Number | 5 | Size, in px, of the tooltip arrow
cornerRadius | Number | 6 | Radius of tooltip corner curves
xOffset | Number | 10 |
multiKeyBackground | Color | "#fff" |
multiKeyBackground | Color | "#fff" | Color to draw behind the colored boxes when multiple items are in the tooltip
| | |
callbacks | - | - | V2.0 introduces callback functions as a replacement for the template engine in v1. The tooltip has the following callbacks for providing text. For all functions, 'this' will be the tooltip object create from the Chart.Tooltip constructor
**Callback Functions** | | | All functions are called with the same arguments
@@ -156,9 +186,9 @@ Name | Type | Default | Description
--- |:---:| --- | ---
duration | Number | 1000 | The number of milliseconds an animation takes.
easing | String | "easeOutQuart" | Easing function to use.
onProgress | Function | none |
onComplete | Function |none |
onProgress | Function | none | Callback called on each step of an animation. Passed a single argument, an object, containing the chart instance and an object with details of the animation.
onComplete | Function | none | Callback called at the end of an animation. Passed the same arguments as `onProgress
`
The global options for elements are defined in `Chart.defaults.global.elements`.
Name | Type | Default | Description
+39 -21
Ver Arquivo
@@ -1,14 +1,13 @@
---
title: Getting started
title: Scales
anchor: scales
---
###Scales
Scales in v2.0 of Chart.js are significantly more powerful, but also different than those of v1.0.
- Multiple x & y axes are now supported.
- A built-in label auto-skip feature now detects would-be overlapping ticks and labels and removes every nth label to keep things displaying normally.
- Scale titles are now supported
* Multiple x & y axes are now supported.
* A built-in label auto-skip feature now detects would-be overlapping ticks and labels and removes every nth label to keep things displaying normally.
* Scale titles are now supported
* New scale types can be extended without writing an entirely new chart type
Every scale extends a core scale class with the following options:
@@ -16,6 +15,20 @@ Name | Type | Default | Description
--- |:---:| --- | ---
type | String | Chart specific. | Type of scale being employed. Custom scales can be created and registered with a string key. Options: ["category"](#scales-category-scale), ["linear"](#scales-linear-scale), ["logarithmic"](#scales-logarithmic-scale), ["time"](#scales-time-scale), ["radialLinear"](#scales-radial-linear-scale)
display | Boolean | true | If true, show the scale including gridlines, ticks, and labels. Overrides *gridLines.display*, *scaleLabel.display*, and *ticks.display*.
beforeUpdate | Function | undefined | Callback called before the update process starts. Passed a single argument, the scale instance.
beforeSetDimensions | Function | undefined | Callback that runs before dimensions are set. Passed a single argument, the scale instance.
afterSetDimensions | Function | undefined | Callback that runs after dimensions are set. Passed a single argument, the scale instance.
beforeDataLimits | Function | undefined | Callback that runs before data limits are determined. Passed a single argument, the scale instance.
afterDataLimits | Function | undefined | Callback that runs after data limits are determined. Passed a single argument, the scale instance.
beforeBuildTicks | Function | undefined | Callback that runs before ticks are created. Passed a single argument, the scale instance.
afterBuildTicks | Function | undefined | Callback that runs after ticks are created. Useful for filtering ticks. Passed a single argument, the scale instance.
beforeTickToLabelConversion | Function | undefined | Callback that runs before ticks are converted into strings. Passed a single argument, the scale instance.
afterTickToLabelConversion | Function | undefined | Callback that runs after ticks are converted into strings. Passed a single argument, the scale instance.
beforeCalculateTickRotation | Function | undefined | Callback that runs before tick rotation is determined. Passed a single argument, the scale instance.
afterCalculateTickRotation | Function | undefined | Callback that runs after tick rotation is determined. Passed a single argument, the scale instance.
beforeFit | Function | undefined | Callback that runs before the scale fits to the canvas. Passed a single argument, the scale instance.
afterFit | Function | undefined | Callback that runs after the scale fits to the canvas. Passed a single argument, the scale instance.
afterUpdate | Function | undefined | Callback that runs at the end of the update process. Passed a single argument, the scale instance.
**gridLines** | Array | - | Options for the grid lines that run perpendicular to the axis.
*gridLines*.display | Boolean | true |
*gridLines*.color | Color | "rgba(0, 0, 0, 0.1)" | Color of the grid lines.
@@ -46,6 +59,9 @@ display | Boolean | true | If true, show the scale including gridlines, ticks, a
*ticks*.display | Boolean | true | If true, show the ticks.
*ticks*.suggestedMin | Number | - | User defined minimum number for the scale, overrides minimum value *except for if* it is higher than the minimum value.
*ticks*.suggestedMax | Number | - | User defined maximum number for the scale, overrides maximum value *except for if* it is lower than the maximum value.
*ticks*.min | Number | - | User defined minimum number for the scale, overrides minimum value
*ticks*.max | Number | - | User defined minimum number for the scale, overrides maximum value
*ticks*.autoSkip | Boolean | true | If true, automatically calculates how many labels that can be shown and hides labels accordingly. Turn it off to show all labels no matter what
*ticks*.callback | Function | `function(value) { return '' + value; } ` | Returns the string representation of the tick value as it should be displayed on the chart.
The `callback` method may be used for advanced tick customization. The following callback would display every label in scientific notation
@@ -65,7 +81,7 @@ The `callback` method may be used for advanced tick customization. The following
}
```
#### Category Scale
### Category Scale
The category scale will be familiar to those who have used v1.0. Labels are drawn in from the labels array included in the chart data.
The category scale extends the core scale class with the following tick template:
@@ -76,7 +92,7 @@ The category scale extends the core scale class with the following tick template
}
```
#### Linear Scale
### Linear Scale
The linear scale can be used to display numerical data. It can be placed on either the x or y axis. The scatter chart type automatically configures a line chart to use one of these scales for the x axis.
The linear scale extends the core scale class with the following tick template:
@@ -113,7 +129,7 @@ The linear scale extends the core scale class with the following tick template:
}
```
#### Logarithmic Scale
### Logarithmic Scale
The logarithmic scale is used to display logarithmic data of course. It can be placed on either the x or y axis.
The log scale extends the core scale class with the following tick template:
@@ -135,7 +151,7 @@ The log scale extends the core scale class with the following tick template:
}
```
#### Time Scale
### Time Scale
The time scale is used to display times and dates. It can be placed on the x axis. When building its ticks, it will automatically calculate the most comfortable unit base on the size of the scale.
The time scale extends the core scale class with the following tick template:
@@ -153,16 +169,18 @@ The time scale extends the core scale class with the following tick template:
// Moment js for each of the units. Replaces `displayFormat`
// To override, use a pattern string from http://momentjs.com/docs/#/displaying/format/
displayFormats: {
'millisecond': 'SSS [ms]',
'second': 'h:mm:ss a', // 11:20:01 AM
'minute': 'h:mm:ss a', // 11:20:01 AM
'hour': 'MMM D, hA', // Sept 4, 5PM
'day': 'll', // Sep 4 2015
'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ?
'month': 'MMM YYYY', // Sept 2015
'quarter': '[Q]Q - YYYY', // Q3
'year': 'YYYY', // 2015
},
'millisecond': 'SSS [ms]',
'second': 'h:mm:ss a', // 11:20:01 AM
'minute': 'h:mm:ss a', // 11:20:01 AM
'hour': 'MMM D, hA', // Sept 4, 5PM
'day': 'll', // Sep 4 2015
'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ?
'month': 'MMM YYYY', // Sept 2015
'quarter': '[Q]Q - YYYY', // Q3
'year': 'YYYY', // 2015
},
// Sets the display format used in tooltip generation
tooltipFormat: ''
},
}
```
@@ -210,7 +228,7 @@ The following time measurements are supported:
}
```
#### Radial Linear Scale
### Radial Linear Scale
The radial linear scale is used specifically for the radar chart type.
The radial linear scale extends the core scale class with the following tick template:
+5 -32
Ver Arquivo
@@ -80,6 +80,9 @@ var data = {
// Number or array - border width of point when hovered
pointHoverBorderWidth: 2,
// Tension - bezier curve tension of the line. Set to 0 to draw straight Wlines connecting points
tension: 0.1,
// The actual data
data: [65, 59, 80, 81, 56, 55, 40],
@@ -145,37 +148,7 @@ new Chart(ctx, {
}
});
// 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.
// and the Line chart defaults, but this particular instance will have the x axis not displaying.
```
We can also change these defaults values for each Line type that is created, this object is available at `Chart.defaults.Line`.
### Prototype methods
#### .getElementsAtEvent( event )
Calling `getElementsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event.
```javascript
canvas.onclick = function(evt){
var activePoints = myLineChart.getElementsAtEvent(evt);
// => activePoints is an array of points on the canvas that are at the same position as the click event.
};
```
This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application.
#### .getElementAtEvent( event )
Calling `getElementAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the single element at the event position. If there are multiple items within range, only the first is returned
#### .update( )
Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop. You can safely call `update()` after changing the entire data object on the chart.
```javascript
myLineChart.data.datasets[0].data[2] = 50;
// Would update the first dataset's value of 'March' to be 50
myLineChart.update();
// Calling update now animates the position of March from 90 to 50.
```
We can also change these defaults values for each Line type that is created, this object is available at `Chart.defaults.line`.
-28
Ver Arquivo
@@ -152,32 +152,4 @@ Sample: |==============|
Bar: |1.||1.|
Category: | .5 |
Sample: |==============|
```
### Prototype methods
#### .getElementsAtEvent( event )
Calling `getElementsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event.
```javascript
canvas.onclick = function(evt){
var activePoints = myLineChart.getElementsAtEvent(evt);
// => activePoints is an array of points on the canvas that are at the same position as the click event.
};
```
This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application.
#### .getElementAtEvent( event )
Calling `getElementAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the single element at the event position. If there are multiple items within range, only the first is returned
#### .update( )
Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop.
```javascript
myBarChart.data.datasets[0].data[2] = 50;
// Would update the first dataset's value of 'March' to be 50
myBarChart.update();
// Calling update now animates the position of March from 90 to 50.
```
+8 -54
Ver Arquivo
@@ -3,7 +3,7 @@ title: Radar Chart
anchor: radar-chart
---
###Introduction
### Introduction
A radar chart is a way of showing multiple data points and the variation between them.
They are often useful for comparing the points of two or more different data sets.
@@ -12,17 +12,17 @@ They are often useful for comparing the points of two or more different data set
<canvas width="250" height="125"></canvas>
</div>
###Example usage
### Example usage
```javascript
var myRadarChart = new Chart(ctx,{
var myRadarChart = new Chart(ctx, {
type:'radar',
data: data,
options: options
});
```
###Data structure
### Data structure
```javascript
var data = {
labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"],
@@ -52,10 +52,10 @@ var data = {
```
For a radar chart, to provide context of what each point means, we include an array of strings that show around each point in the chart.
For the radar chart data, we have an array of datasets. Each of these is an object, with a fill colour, a stroke colour, a colour for the fill of each point, and a colour for the stroke of each point. We also have an array of data values.
The label key on each dataset is optional, and can be used when generating a scale for the chart.
### Chart options
### Chart Options
These are the customisation options specific to Radar charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart.
@@ -66,7 +66,7 @@ Name | Type | Default | Description
scale | Array | [See Scales](#scales) and [Defaults for Radial Linear Scale](#scales-radial-linear-scale) | Options for the one scale used on the chart. Use this to style the ticks, labels, and grid lines.
*scale*.type | String |"radialLinear" | As defined in ["Radial Linear"](#scales-radial-linear-scale).
*elements*.line | Array | | Options for all line elements used on the chart, as defined in the global elements, duplicated here to show Radar chart specific defaults.
*elements.line*.tension | Number | 0 | Tension exhibited by lines when calculating splineCurve.
*elements.line*.tension | Number | 0 | Tension exhibited by lines when calculating splineCurve. Setting to 0 creates straight lines.
You can override these for your `Chart` instance by passing a second argument into the `Radar` method as an object with the keys you want to override.
@@ -90,50 +90,4 @@ new Chart(ctx, {
// well as the ticks beginning at zero.
```
We can also change these defaults values for each Radar type that is created, this object is available at `Chart.defaults.Radar`.
### Prototype methods
#### .getPointsAtEvent( event )
Calling `getPointsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event.
```javascript
canvas.onclick = function(evt){
var activePoints = myRadarChart.getPointsAtEvent(evt);
// => activePoints is an array of points on the canvas that are at the same position as the click event.
};
```
This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application.
#### .update( )
Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop.
```javascript
myRadarChart.datasets[0].points[2].value = 50;
// Would update the first dataset's value of 'Sleeping' to be 50
myRadarChart.update();
// Calling update now animates the position of Sleeping from 90 to 50.
```
#### .addData( valuesArray, label )
Calling `addData(valuesArray, label)` on your Chart instance passing an array of values for each dataset, along with a label for those points.
```javascript
// The values array passed into addData should be one for each dataset in the chart
myRadarChart.addData([40, 60], "Dancing");
// The new data will now animate at the end of the chart.
```
#### .removeData( )
Calling `removeData()` on your Chart instance will remove the first value for all datasets on the chart.
```javascript
myRadarChart.removeData();
// Other points will now animate to their correct positions.
```
We can also change these defaults values for each Radar type that is created, this object is available at `Chart.defaults.radar`.
+48 -90
Ver Arquivo
@@ -14,45 +14,42 @@ This type of chart is often useful when we want to show a comparison data simila
### Example usage
```javascript
new Chart(ctx).PolarArea(data, options);
new Chart(ctx, {
data:data,
type: 'polarArea',
options: options
});
```
### Data structure
```javascript
var data = [
{
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 data = {
datasets: [{
data: [
10,
32,
53,
14,
22,
],
backgroundColor: [
"#F7464A",
"#46BFBD",
"#FDB45C",
"#949FB1",
"#4D5360",
],
label: 'My dataset' // for legend
}],
labels: [
"Red",
"Green",
"Yellow",
"Grey",
"Dark Grey"
]
};
```
As you can see, for the chart data you pass in an array of objects, with a value and a colour. The value attribute should be a number, while the color attribute should be a string. Similar to CSS, for this string you can use HEX notation, RGB, RGBA or HSL.
@@ -65,69 +62,30 @@ Name | Type | Default | Description
scale | Array | [See Scales](#scales) and [Defaults for Radial Linear Scale](#getting-started-radial-linear-scale) | Options for the one scale used on the chart. Use this to style the ticks, labels, and grid.
*scale*.type | String |"radialLinear" | As defined in ["Radial Linear"](#scales-radial-linear-scale).
*scale*.lineArc | Boolean | true | When true, lines are circular.
*animation*.animateRotate | Boolean |true | If true, will animate the rotation of the chart.
*animation*.animateScale | Boolean | true | If true, will animate scaling the chart.
animateRotate | Boolean |true | If true, will animate the rotation of the chart.
animateScale | Boolean | true | If true, will animate scaling the chart.
*legend*.*labels*.generateLabels | Function | `function(data) {} ` | Returns labels for each the legend
*legend*.onClick | Function | function(event, legendItem) {} ` | Handles clicking an individual legend item
legendCallback | Function | `function(chart) ` | Generates the HTML legend via calls to `generateLegend`
You can override these for your `Chart` instance by passing a second argument into the `PolarArea` method as an object with the keys you want to override.
For example, we could have a polar area chart with a black stroke on each segment like so:
```javascript
new Chart(ctx).PolarArea(data, {
segmentStrokeColor: "#000000"
new Chart(ctx, {
data: data,
type: 'polarArea',
options: {
elements: {
arc: {
borderColor: "#000000"
}
}
}
});
// This will create a chart with all of the default options, merged from the global config,
// and the PolarArea chart defaults but this particular instance will have `segmentStrokeColor` set to `"#000000"`.
// and the PolarArea chart defaults but this particular instance will have `elements.arc.borderColor` set to `"#000000"`.
```
We can also change these defaults values for each PolarArea type that is created, this object is available at `Chart.defaults.PolarArea`.
### Prototype methods
#### .getSegmentsAtEvent( event )
Calling `getSegmentsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the segment elements that are at that the same position of that event.
```javascript
canvas.onclick = function(evt){
var activePoints = myPolarAreaChart.getSegmentsAtEvent(evt);
// => activePoints is an array of segments on the canvas that are at the same position as the click event.
};
```
This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application.
#### .update( )
Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop.
```javascript
myPolarAreaChart.segments[1].value = 10;
// Would update the first dataset's value of 'Green' to be 10
myPolarAreaChart.update();
// Calling update now animates the position of Green from 50 to 10.
```
#### .addData( segmentData, index )
Calling `addData(segmentData, index)` on your Chart instance passing an object in the same format as in the constructor. There is an option second argument of 'index', this determines at what index the new segment should be inserted into the chart.
```javascript
// An object in the same format as the original data source
myPolarAreaChart.addData({
value: 130,
color: "#B48EAD",
highlight: "#C69CBE",
label: "Purple"
});
// The new segment will now animate in.
```
#### .removeData( index )
Calling `removeData(index)` on your Chart instance will remove segment at that particular index. If none is provided, it will default to the last segment.
```javascript
myPolarAreaChart.removeData();
// Other segments will update to fill the empty space left.
```
We can also change these defaults values for each PolarArea type that is created, this object is available at `Chart.defaults.polarArea`.
+3 -52
Ver Arquivo
@@ -78,6 +78,8 @@ scale | Array | [See Scales](#scales) and [Defaults for Radial Linear Scale](#ge
*scale*.lineArc | Boolean | true | When true, lines are arced compared to straight when false.
*animation*.animateRotate | Boolean |true | If true, will animate the rotation of the chart.
*animation*.animateScale | Boolean | false | If true, will animate scaling the Doughnut from the centre.
*legend*.*labels*.generateLabels | Function | `function(data) {} ` | Returns labels for each the legend
*legend*.onClick | Function | function(event, legendItem) {} ` | Handles clicking an individual legend item
You can override these for your `Chart` instance by passing a second argument into the `Doughnut` method as an object with the keys you want to override.
@@ -94,55 +96,4 @@ new Chart(ctx,{
// and the Doughnut chart defaults but this particular instance will have `animateScale` set to `true`.
```
We can also change these default values for each Doughnut type that is created, this object is available at `Chart.defaults.Doughnut`. Pie charts also have a clone of these defaults available to change at `Chart.defaults.Pie`, with the only difference being `percentageInnerCutout` being set to 0.
### Prototype methods
#### .getSegmentsAtEvent( event )
Calling `getSegmentsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the segment elements that are at the same position of that event.
```javascript
canvas.onclick = function(evt){
var activePoints = myDoughnutChart.getSegmentsAtEvent(evt);
// => activePoints is an array of segments on the canvas that are at the same position as the click event.
};
```
This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application.
#### .update( )
Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop.
```javascript
myDoughnutChart.segments[1].value = 10;
// Would update the first dataset's value of 'Green' to be 10
myDoughnutChart.update();
// Calling update now animates the circumference of the segment 'Green' from 50 to 10.
// and transitions other segment widths
```
#### .addData( segmentData, index )
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.
```javascript
// An object in the same format as the original data source
myDoughnutChart.addData({
value: 130,
color: "#B48EAD",
highlight: "#C69CBE",
label: "Purple"
});
// The new segment will now animate in.
```
#### .removeData( index )
Calling `removeData(index)` on your Chart instance will remove segment at that particular index. If none is provided, it will default to the last segment.
```javascript
myDoughnutChart.removeData();
// Other segments will update to fill the empty space left.
```
We can also change these default values for each Doughnut type that is created, this object is available at `Chart.defaults.Doughnut`. Pie charts also have a clone of these defaults available to change at `Chart.defaults.Pie`, with the only difference being `percentageInnerCutout` being set to 0.
+286 -87
Ver Arquivo
@@ -10,17 +10,38 @@ For each chart, there are a set of global prototype methods on the shared `Chart
```javascript
// For example:
var myLineChart = new Chart(ctx).Line(data);
var myLineChart = new Chart(ctx, config);
```
#### .clear()
#### .destroy()
Will clear the chart canvas. Used extensively internally between animation frames, but you might find it useful.
Use this to destroy any chart instances that are created. This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js.
This must be called before the canvas is reused for a new chart.
```javascript
// Will clear the canvas that myLineChart is drawn on
myLineChart.clear();
// => returns 'this' for chainability
// Destroys a specific chart instance
myLineChart.destroy();
```
#### .update(duration, lazy)
Triggers an update of the chart. This can be safely called after replacing the entire data object. This will update all scales, legends, and then re-render the chart.
```javascript
// duration is the time for the animation of the redraw in miliseconds
// lazy is a boolean. if true, the animation can be interupted by other animations
myLineChart.data.datasets[0].data[2] = 50; // Would update the first dataset's value of 'March' to be 50
myLineChart.update(); // Calling update now animates the position of March from 90 to 50.
```
#### .render(duration, lazy)
Triggers a redraw of all chart elements. Note, this does not update elements for new data. Use `.update()` in that case.
```javascript
// duration is the time for the animation of the redraw in miliseconds
// lazy is a boolean. if true, the animation can be interupted by other animations
myLineChart.render(duration, lazy);
```
#### .stop()
@@ -35,7 +56,7 @@ myLineChart.stop();
#### .resize()
Use this to manually resize the canvas element. This is run each time the browser is resized, but you can call this method manually if you change the size of the canvas nodes container element.
Use this to manually resize the canvas element. This is run each time the canvas container is resized, but you can call this method manually if you change the size of the canvas nodes container element.
```javascript
// Resizes & redraws to fill its container element
@@ -43,13 +64,14 @@ myLineChart.resize();
// => returns 'this' for chainability
```
#### .destroy()
#### .clear()
Use this to destroy any chart instances that are created. This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js.
Will clear the chart canvas. Used extensively internally between animation frames, but you might find it useful.
```javascript
// Destroys a specific chart instance
myLineChart.destroy();
// Will clear the canvas that myLineChart is drawn on
myLineChart.clear();
// => returns 'this' for chainability
```
#### .toBase64Image()
@@ -63,109 +85,290 @@ myLineChart.toBase64Image();
#### .generateLegend()
Returns an HTML string of a legend for that chart. The template for this legend is at `legendTemplate` in the chart options.
Returns an HTML string of a legend for that chart. The legend is generated from the `legendCallback` in the options.
```javascript
myLineChart.generateLegend();
// => returns HTML string of a legend for this chart
```
#### .getElementAtEvent(e)
Calling `getElementAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the single element at the event position. If there are multiple items within range, only the first is returned
```javascript
myLineChart.getElementAtEvent(e);
// => returns the first element at the event point.
```
#### .getElementsAtEvent(e)
Looks for the element under the event point, then returns all elements at the same data index. This is used internally for 'label' mode highlighting.
Calling `getElementsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event.
```javascript
canvas.onclick = function(evt){
var activePoints = myLineChart.getElementsAtEvent(evt);
// => activePoints is an array of points on the canvas that are at the same position as the click event.
};
```
This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application.
#### .getDatasetAtEvent(e)
Looks for the element under the event point, then returns all elements from that dataset. This is used internally for 'dataset' mode highlighting
```javascript
myLineChart.getDatasetAtEvent(e);
// => returns an array of elements
```
### External Tooltips
You can enable custom tooltips in the global or chart configuration like so:
```javascript
var myPieChart = new Chart(ctx).Pie(data, {
customTooltips: function(tooltip) {
var myPieChart = new Chart(ctx, {
type: 'pie',
data: data,
options: {
tooltips: {
custom: function(tooltip) {
// tooltip will be false if tooltip is not visible or should be hidden
if (!tooltip) {
return;
}
// tooltip will be false if tooltip is not visible or should be hidden
if (!tooltip) {
return;
}
// Otherwise, tooltip will be an object with all tooltip properties like:
// Otherwise, tooltip will be an object with all tooltip properties like:
// tooltip.caretHeight
// tooltip.caretPadding
// tooltip.chart
// tooltip.cornerRadius
// tooltip.fillColor
// tooltip.font...
// tooltip.text
// tooltip.x
// tooltip.y
// etc...
};
// tooltip.caretHeight
// tooltip.caretPadding
// tooltip.chart
// tooltip.cornerRadius
// tooltip.fillColor
// tooltip.font...
// tooltip.text
// tooltip.x
// tooltip.y
// etc...
}
}
}
});
```
See files `sample/pie-customTooltips.html` and `sample/line-customTooltips.html` for examples on how to get started.
See `sample/line-customTooltips.html` for examples on how to get started.
### Writing new scale types
Starting with Chart.js 2.0 scales can be individually extended. Scales should always derive from Chart.Scale.
```javascript
var MyScale = Chart.Scale.extend({
/* extensions ... */
});
// MyScale is now derived from Chart.Scale
```
Once you have created your scale class, you need to register it with the global chart object so that it can be used. A default config for the scale may be provided when registering the constructor. The first parameter to the register function is a string key that is used later to identify which scale type to use for a chart.
```javascript
Chart.scaleService.registerScaleType('myScale', MyScale, defaultConfigObject);
```
To use the new scale, simply pass in the string key to the config when creating a chart.
```javascript
var lineChart = new Chart(ctx, {
data: data,
type: 'line',
options: {
scales: {
yAxes: [{
type: 'myScale' // this is the same key that was passed to the registerScaleType function
}]
}
}
})
```
#### Scale Properties
Scale instances are given the following properties during the fitting process.
```javascript
{
left: Number, // left edge of the scale bounding box
right: Number, // right edge of the bounding box'
top: Number,
bottom: Number,
width: Number, // the same as right - left
height: Number, // the same as bottom - top
// Margin on each side. Like css, this is outside the bounding box.
margins: {
left: Number,
right: Number,
top: Number,
bottom: Number,
},
// Amount of padding on the inside of the bounding box (like CSS)
paddingLeft: Number,
paddingRight: Number,
paddingTop: Number,
paddingBottom: Number,
}
```
#### Scale Interface
To work with Chart.js, custom scale types must implement the following interface.
```javascript
{
// Determines the data limits. Should set this.min and this.max to be the data max/min
determineDataLimits: function() {},
// Generate tick marks. this.chart is the chart instance. The data object can be accessed as this.chart.data
// buildTicks() should create a ticks array on the scale instance, if you intend to use any of the implementations from the base class
buildTicks: function() {},
// Get the value to show for the data at the given index of the the given dataset, ie this.chart.data.datasets[datasetIndex].data[index]
getLabelForIndex: function(index, datasetIndex) {},
// Get the pixel (x coordinate for horizontal scale, y coordinate for vertical scales) for a given value
// @param index: index into the ticks array
// @param includeOffset: if true, get the pixel halway between the given tick and the next
getPixelForTick: function(index, includeOffset) {},
// Get the pixel (x coordinate for horizontal scale, y coordinate for vertical scales) for a given value
// @param value : the value to get the pixel for
// @param index : index into the data array of the value
// @param datasetIndex : index of the dataset the value comes from
// @param includeOffset : if true, get the pixel halway between the given tick and the next
getPixelForValue: function(value, index, datasetIndex, includeOffset) {}
}
```
Optionally, the following methods may also be overwritten, but an implementation is already provided by the `Chart.Scale` base class.
```javascript
// Transform the ticks array of the scale instance into strings. The default implementation simply calls this.options.ticks.callback(numericalTick, index, ticks);
convertTicksToLabels: function() {},
// Determine how much the labels will rotate by. The default implementation will only rotate labels if the scale is horizontal.
calculateTickRotation: function() {},
// Fits the scale into the canvas.
// this.maxWidth and this.maxHeight will tell you the maximum dimensions the scale instance can be. Scales should endeavour to be as efficient as possible with canvas space.
// this.margins is the amount of space you have on either side of your scale that you may expand in to. This is used already for calculating the best label rotation
// You must set this.minSize to be the size of your scale. It must be an object containing 2 properties: width and height.
// You must set this.width to be the width and this.height to be the height of the scale
fit: function() {},
// Draws the scale onto the canvas. this.(left|right|top|bottom) will have been populated to tell you the area on the canvas to draw in
// @param chartArea : an object containing four properties: left, right, top, bottom. This is the rectangle that lines, bars, etc will be drawn in. It may be used, for example, to draw grid lines.
draw: function(chartArea) {},
```
The Core.Scale base class also has some utility functions that you may find useful.
```javascript
{
// Returns true if the scale instance is horizontal
isHorizontal: function(){},
// Get the correct value from the value from this.chart.data.datasets[x].data[]
// If dataValue is an object, returns .x or .y depending on the return of isHorizontal()
// If the value is undefined, returns NaN
// Otherwise returns the value.
// Note that in all cases, the returned value is not guaranteed to be a Number
getRightValue: function(dataValue){},
}
```
### Writing new chart types
Chart.js 1.0 has been rewritten to provide a platform for developers to create their own custom chart types, and be able to share and utilise them through the Chart.js API.
The format is relatively simple, there are a set of utility helper methods under `Chart.helpers`, including things such as looping over collections, requesting animation frames, and easing equations.
On top of this, there are also some simple base classes of Chart elements, these all extend from `Chart.Element`, and include things such as points, bars and scales.
Chart.js 2.0 introduces the concept of controllers for each dataset. Like scales, new controllers can be written as needed.
```javascript
Chart.Type.extend({
// Passing in a name registers this chart in the Chart namespace
name: "Scatter",
// Providing a defaults will also register the deafults in the chart namespace
defaults : {
options: "Here",
available: "at this.options"
},
// 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.chart.ctx // The drawing context for this chart
this.chart.canvas // the canvas node for this chart
},
// Used to draw something on the canvas
draw: function() {
}
Chart.controllers.MyType = Chart.DatasetController.extend({
});
// Now we can create a new instance of our chart, using the Chart.js API
new Chart(ctx).Scatter(data);
// initialize is now run
new Chart(ctx, {
// this is the string the constructor was registered at, ie Chart.controllers.MyType
type: 'MyType',
data: data,
options: options
});
```
#### Dataset Controller Interface
Dataset controllers must implement the following interface.
```javascript
{
// Create elements for each piece of data in the dataset. Store elements in an array on the dataset as dataset.metaData
addElements: function() {},
// Create a single element for the data at the given index and reset its state
addElementAndReset: function(index) {},
// Draw the representation of the dataset
// @param ease : if specified, this number represents how far to transition elements. See the implementation of draw() in any of the provided controllers to see how this should be used
draw: function(ease) {},
// Remove hover styling from the given element
removeHoverStyle: function(element) {},
// Add hover styling to the given element
setHoverStyle: function(element) {},
// Update the elements in response to new data
// @param reset : if true, put the elements into a reset state so they can animate to their final values
update: function(reset) {},
}
```
The following methods may optionally be overridden by derived dataset controllers
```javascript
{
// Initializes the controller
initialize: function(chart, datasetIndex) {},
// Ensures that the dataset represented by this controller is linked to a scale. Overridden to helpers.noop in the polar area and doughnut controllers as these
// chart types using a single scale
linkScales: function() {},
// Called by the main chart controller when an update is triggered. The default implementation handles the number of data points changing and creating elements appropriately.
buildOrUpdateElements: function() {}
}
```
### Extending existing chart types
We can also extend existing chart types, and expose them to the API in the same way. Let's say for example, we might want to run some more code when we initialize every Line chart.
Extending or replacing an existing controller type is easy. Simply replace the constructor for one of the built in types with your own.
```javascript
// Notice now we're extending the particular Line chart type, rather than the base class.
Chart.types.Line.extend({
// Passing in a name registers this chart in the Chart namespace in the same way
name: "LineAlt",
initialize: function(data){
console.log('My Line chart extension');
Chart.types.Line.prototype.initialize.apply(this, arguments);
}
});
The built in controller types are:
* `Chart.controllers.line`
* `Chart.controllers.bar`
* `Chart.controllers.radar`
* `Chart.controllers.doughnut`
* `Chart.controllers.polarArea`
* `Chart.controllers.bubble`
// Creates a line chart in the same way
new Chart(ctx).LineAlt(data);
// but this logs 'My Line chart extension' in the console.
```
#### Bar Controller
The bar controller has a special property that you should be aware of. To correctly calculate the width of a bar, the controller must determine the number of datasets that map to bars. To do this, the bar controller attaches a property `bar` to the dataset during initialization. If you are creating a replacement or updated bar controller, you should do the same. This will ensure that charts with regular bars and your new derived bars will work seamlessly.
### Community extensions
### Building Chart.js
- <a href="https://github.com/Regaddi/Chart.StackedBar.js" target="_blank">Stacked Bar Chart</a> by <a href="https://twitter.com/Regaddi" target="_blank">@Regaddi</a>
- <a href="https://github.com/tannerlinsley/Chart.StackedArea.js" target="_blank">Stacked Bar Chart</a> by <a href="https://twitter.com/tannerlinsley" target="_blank">@tannerlinsley</a>
- <a href="https://github.com/CAYdenberg/Chart.js" target="_blank">Error bars (bar and line charts)</a> by <a href="https://twitter.com/CAYdenberg" target="_blank">@CAYdenberg</a>
- <a href="http://dima117.github.io/Chart.Scatter/" target="_blank">Scatter chart (number & date scales are supported)</a> by <a href="https://github.com/dima117" target="_blank">@dima117</a>
### Creating custom builds
Chart.js uses <a href="http://gulpjs.com/" target="_blank">gulp</a> to build the library into a single JavaScript file. We can use this same build script with custom parameters in order to build a custom version.
Chart.js uses <a href="http://gulpjs.com/" target="_blank">gulp</a> to build the library into a single JavaScript file.
Firstly, we need to ensure development dependencies are installed. With node and npm installed, after cloning the Chart.js repo to a local directory, and navigating to that directory in the command line, we can run the following:
@@ -176,12 +379,8 @@ 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 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:
Now, we can run the `gulp build` task.
```bash
gulp build --types=Line,Radar,Bar
gulp build
```
This will output to the `/custom` directory, and write two files, Chart.js, and Chart.min.js with only those chart types included.
+21 -1
Ver Arquivo
@@ -13,7 +13,8 @@ var gulp = require('gulp'),
fs = require('fs'),
package = require('./package.json'),
bower = require('./bower.json'),
karma = require('gulp-karma');
karma = require('gulp-karma'),
umd = require('gulp-umd');
var srcDir = './src/';
var testDir = './test/';
@@ -72,6 +73,25 @@ function buildTask() {
return gulp.src(srcFiles)
.pipe(concat('Chart.js'))
.pipe(replace('{{ version }}', package.version))
.pipe(umd({
// We want a global always to ensure that we match previous behaviour
templateSource:
";(function(root, factory) {\n"+
" if (typeof define === 'function' && define.amd) {\n"+
" define(<%= amd %>, factory);\n"+
" } else if (typeof exports === 'object') {\n"+
" module.exports = factory.call(root,<%= cjs %>);\n"+
" } else {\n"+
" root.<%= namespace %> = factory.call(root,<%= global %>);\n"+
" }\n"+
"}(this, function(<%= param %>) {\n"+
"<%= contents %>\n"+
"return <%= exports %>;\n"+
"}));\n",
dependencies: function() {
return ['moment']
}
}))
.pipe(gulp.dest(outputDir))
.pipe(uglify({
preserveComments: 'some'
+3 -2
Ver Arquivo
@@ -2,7 +2,7 @@
"name": "chart.js",
"homepage": "http://www.chartjs.org",
"description": "Simple HTML5 charts using the canvas element.",
"version": "2.0.0-beta",
"version": "2.0.0-beta2",
"main": "Chart.js",
"repository": {
"type": "git",
@@ -30,7 +30,8 @@
"karma-firefox-launcher": "^0.1.6",
"karma-jasmine": "^0.3.6",
"karma-jasmine-html-reporter": "^0.1.8",
"semver": "^3.0.1"
"semver": "^3.0.1",
"gulp-umd": "~0.2.0"
},
"spm": {
"main": "Chart.js"
@@ -1,11 +1,9 @@
<!doctype html>
<html>
<head>
<title>Line Chart</title>
<script src="../node_modules/moment/min/moment.min.js"></script>
<script src="../Chart.js"></script>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<title> Animation Callbacks </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);
@@ -14,8 +12,9 @@
</head>
<body>
<div style="width:100%;">
<canvas id="canvas" style="width:100%;height:100%"></canvas>
<div style="width: 100 %;">
<canvas id="canvas" style="width: 100 % ;height: 100 %"></canvas>
<progress id="animationProgress" max="1" value="0" style="width: 100%"></progress>
</div>
<br>
<br>
@@ -24,14 +23,9 @@
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<div>
<h3>Legend</h3>
<div id="legendContainer">
</div>
</div>
<script>
var randomScalingFactor = function() {
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
return Math.round(Math.random() * 100);
};
var randomColorFactor = function() {
return Math.round(Math.random() * 255);
@@ -39,63 +33,56 @@
var randomColor = function(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
};
var newDate = function(days) {
var date = new Date();
return date.setDate(date.getDate() + days);
};
var newTimestamp = function(days) {
return Date.now() - days * 100000;
};
var $progress = $('#animationProgress');
var config = {
type: 'bar',
type: 'line',
data: {
//labels: [newTimestamp(0), newTimestamp(1), newTimestamp(2), newTimestamp(3), newTimestamp(4), newTimestamp(5), newTimestamp(6)], // unix timestamps
// labels: [newDate(0), newDate(1), newDate(2), newDate(3), newDate(4), newDate(5), newDate(6)], // Date Objects
labels: ["01/01/2015 20:00", "01/02/2015 20:00", "01/03/2015 22:00", "01/05/2015 23:00", "01/07/2015 03:00", "01/08/2015 10:00", "01/10/2015"], // Hours
// labels: ["01/01/2015", "01/02/2015", "01/03/2015", "01/06/2015", "01/15/2015", "01/17/2015", "01/30/2015"], // Days
// labels: ["12/25/2014", "01/08/2015", "01/15/2015", "01/22/2015", "01/29/2015", "02/05/2015", "02/12/2015"], // Weeks
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
type: 'bar',
label: 'Dataset 1',
backgroundColor: "rgba(151,187,205,0.5)",
label: "My First dataset ",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
borderColor: 'white',
borderWidth: 2
fill: false,
borderDash: [5, 5],
}, {
type: 'bar',
label: 'Dataset 2',
backgroundColor: "rgba(151,187,205,0.5)",
label: "My Second dataset ",
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()]
}, ]
}]
},
options: {
responsive: true,
scales: {
xAxes: [{
type: "time",
display: true,
time: {
format: 'MM/DD/YYYY HH:mm',
// round: 'day'
}
}, ],
yAxes: [{
display: true
}]
},
elements: {
line: {
tension: 0.3
animation: {
duration: 2000,
onProgress: function(animation) {
$progress.attr({
value: animation.animationObject.currentStep / animation.animationObject.numSteps,
});
},
onComplete: function(animation) {
window.setTimeout(function() {
$progress.attr({
value: 0
});
}, 2000);
}
},
tooltips: {
mode: 'label',
},
scales: {
xAxes: [{
scaleLabel: {
show: true,
labelString: 'Month'
}
}],
yAxes: [{
scaleLabel: {
show: true,
labelString: 'Value'
},
}]
}
}
};
@@ -107,30 +94,20 @@
dataset.pointBorderWidth = 1;
});
console.log(config.data);
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
window.myLine = new Chart(ctx, config);
updateLegend();
};
function updateLegend() {
$legendContainer = $('#legendContainer');
$legendContainer.empty();
$legendContainer.append(window.myLine.generateLegend());
}
$('#randomizeData').click(function() {
$.each(config.data.datasets, function(i, dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});
window.myLine.update();
updateLegend();
});
$('#addDataset').click(function() {
@@ -150,42 +127,35 @@
config.data.datasets.push(newDataset);
window.myLine.update();
updateLegend();
});
$('#addData').click(function() {
if (config.data.datasets.length > 0) {
config.data.labels.push(
myLine.scales['x-axis-0'].labelMoments[myLine.scales['x-axis-0'].labelMoments.length - 1].add(1, 'day')
.format('MM/DD/YYYY')
);
config.data.labels.push('dataset #' + config.data.labels.length);
for (var index = 0; index < config.data.datasets.length; ++index) {
config.data.datasets[index].data.push(randomScalingFactor());
}
$.each(config.data.datasets, function(i, dataset) {
dataset.data.push(randomScalingFactor());
});
window.myLine.update();
updateLegend();
}
});
$('#removeDataset').click(function() {
config.data.datasets.splice(0, 1);
window.myLine.update();
updateLegend();
});
$('#removeData').click(function() {
config.data.labels.splice(-1, 1); // remove the label first
config.data.datasets.forEach(function(dataset, datasetIndex) {
config.data.datasets[datasetIndex].data.pop();
dataset.data.pop();
});
window.myLine.update();
updateLegend();
});
</script>
</body>
</html>
</html>
+7
Ver Arquivo
@@ -70,6 +70,13 @@
data: barChartData,
options: {
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: 'Our 3 Favorite Datasets'
}
}
});
+14 -2
Ver Arquivo
@@ -44,6 +44,7 @@
};
var config = {
type: 'doughnut',
data: {
datasets: [{
data: [
@@ -60,6 +61,7 @@
"#949FB1",
"#4D5360",
],
label: 'Dataset 1'
}, {
hidden: true,
data: [
@@ -76,6 +78,7 @@
"#949FB1",
"#4D5360",
],
label: 'Dataset 2'
}, {
data: [
randomScalingFactor(),
@@ -91,6 +94,7 @@
"#949FB1",
"#4D5360",
],
label: 'Dataset 3'
}],
labels: [
"Red",
@@ -101,7 +105,14 @@
]
},
options: {
responsive: true
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: 'Our Favorite Datasets'
}
}
};
@@ -113,7 +124,7 @@
window.onload = function() {
var ctx = document.getElementById("chart-area").getContext("2d");
window.myDoughnut = Chart.Doughnut(ctx, config);
window.myDoughnut = new Chart(ctx, config);
console.log(window.myDoughnut);
updateLegend();
@@ -138,6 +149,7 @@
var newDataset = {
backgroundColor: [],
data: [],
label: 'New dataset ' + config.data.datasets.length,
};
for (var index = 0; index < config.data.labels.length; ++index) {
@@ -3,7 +3,6 @@
<head>
<title>Line Chart</title>
<script src="../node_modules/moment/min/moment.min.js"></script>
<script src="../Chart.js"></script>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<style>
@@ -24,11 +23,6 @@
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<div>
<h3>Legend</h3>
<div id="legendContainer">
</div>
</div>
<script>
var randomScalingFactor = function() {
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
@@ -39,22 +33,11 @@
var randomColor = function(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
};
var newDate = function(days) {
var date = new Date();
return date.setDate(date.getDate() + days);
};
var newTimestamp = function(days) {
return Date.now() - days * 100000;
};
var config = {
type: 'line',
data: {
//labels: [newTimestamp(0), newTimestamp(1), newTimestamp(2), newTimestamp(3), newTimestamp(4), newTimestamp(5), newTimestamp(6)], // unix timestamps
// labels: [newDate(0), newDate(1), newDate(2), newDate(3), newDate(4), newDate(5), newDate(6)], // Date Objects
labels: ["01/01/2015 20:00", "01/02/2015 21:00", "01/03/2015 22:00", "01/05/2015 23:00", "01/07/2015 03:00", "01/08/2015 10:00", "01/10/2015"], // Hours
// labels: ["01/01/2015", "01/02/2015", "01/03/2015", "01/06/2015", "01/15/2015", "01/17/2015", "01/30/2015"], // Days
// labels: ["12/25/2014", "01/08/2015", "01/15/2015", "01/22/2015", "01/29/2015", "02/05/2015", "02/12/2015"], // Weeks
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
@@ -63,60 +46,55 @@
}, {
label: "My Second dataset",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
fill: false,
borderDash: [5, 5],
}, {
label: "Dataset with point data",
data: [{
x: "12/31/2014 06:00",
y: randomScalingFactor()
}, {
x: "01/04/2015 13:00",
y: randomScalingFactor()
}, {
x: "01/07/2015 01:15",
y: randomScalingFactor()
}, {
x: "01/15/2015 01:15",
y: randomScalingFactor()
}],
fill: false
label: "My Third dataset",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
fill: false,
}, {
label: "My Fourth dataset",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
fill: false,
}]
},
options: {
responsive: true,
legend: {
position: 'bottom',
},
hover: {
mode: 'label'
},
scales: {
xAxes: [{
type: "time",
display: true,
time: {
format: 'MM/DD/YYYY HH:mm',
// round: 'day'
},
scaleLabel: {
display: true,
labelString: 'Date'
show: true,
labelString: 'Month'
}
}, ],
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'value'
show: true,
labelString: 'Value'
}
}]
},
elements: {
line: {
tension: 0.3
}
},
title: {
display: true,
text: 'Our 4 Favorite Datasets'
}
}
};
$.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);
var background = randomColor(0.5);
dataset.borderColor = background;
dataset.backgroundColor = background;
dataset.pointBorderColor = background;
dataset.pointBackgroundColor = background;
dataset.pointBorderWidth = 1;
});
@@ -140,6 +118,7 @@
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});
window.myLine.update();
@@ -147,13 +126,15 @@
});
$('#addDataset').click(function() {
var background = randomColor(0.5);
var newDataset = {
label: 'Dataset ' + config.data.datasets.length,
borderColor: randomColor(0.4),
backgroundColor: randomColor(0.5),
pointBorderColor: randomColor(0.7),
pointBackgroundColor: randomColor(0.5),
borderColor: background,
backgroundColor: background,
pointBorderColor: background,
pointBackgroundColor: background,
pointBorderWidth: 1,
fill: false,
data: [],
};
@@ -168,16 +149,11 @@
$('#addData').click(function() {
if (config.data.datasets.length > 0) {
config.data.labels.push(
myLine.scales['x-axis-0'].labelMoments[myLine.scales['x-axis-0'].labelMoments.length - 1]
.clone()
.add(1, 'day')
.format('MM/DD/YYYY HH:mm')
);
config.data.labels.push('dataset #' + config.data.labels.length);
for (var index = 0; index < config.data.datasets.length; ++index) {
config.data.datasets[index].data.push(randomScalingFactor());
}
$.each(config.data.datasets, function(i, dataset) {
dataset.data.push(randomScalingFactor());
});
window.myLine.update();
updateLegend();
-164
Ver Arquivo
@@ -1,164 +0,0 @@
<!doctype html>
<html>
<head>
<title>Time Scale Point Data</title>
<script src="../node_modules/moment/min/moment.min.js"></script>
<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>
<body>
<div style="width:100%;">
<canvas id="canvas" style="width:100%;height:100%"></canvas>
</div>
<br>
<br>
<button id="randomizeData">Randomize Data</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<div>
<h3>Legend</h3>
<div id="legendContainer">
</div>
</div>
<script>
var randomScalingFactor = function() {
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
};
var randomColorFactor = function() {
return Math.round(Math.random() * 255);
};
var randomColor = function(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
};
var newDate = function(days) {
var date = new Date();
return date.setDate(date.getDate() + days);
};
var newTimestamp = function(days) {
return Date.now() - days * 100000;
};
var config = {
type: 'line',
data: {
datasets: [{
label: "Dataset with point data",
data: [{
x: "12/31/2014 06:00",
y: randomScalingFactor()
}, {
x: "01/04/2015 13:00",
y: randomScalingFactor()
}, {
x: "01/07/2015 01:15",
y: randomScalingFactor()
}, {
x: "01/15/2015 01:15",
y: randomScalingFactor()
}],
fill: false
}]
},
options: {
responsive: true,
scales: {
xAxes: [{
type: "time",
display: true,
time: {
format: 'MM/DD/YYYY HH:mm',
// round: 'day'
},
scaleLabel: {
display: true,
labelString: 'Date'
}
}, ],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'value'
}
}]
},
elements: {
line: {
tension: 0.3
}
},
}
};
$.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);
updateLegend();
};
function updateLegend() {
$legendContainer = $('#legendContainer');
$legendContainer.empty();
$legendContainer.append(window.myLine.generateLegend());
}
$('#randomizeData').click(function() {
$.each(config.data.datasets, function(i, dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});
window.myLine.update();
updateLegend();
});
$('#addData').click(function() {
if (config.data.datasets.length > 0) {
var newTime = myLine.scales['x-axis-0'].labelMoments[0][myLine.scales['x-axis-0'].labelMoments[0].length - 1]
.clone()
.add(1, 'day')
.format('MM/DD/YYYY HH:mm');
for (var index = 0; index < config.data.datasets.length; ++index) {
config.data.datasets[index].data.push({
x: newTime,
y: randomScalingFactor()
});
}
window.myLine.update();
updateLegend();
}
});
$('#removeData').click(function() {
config.data.datasets.forEach(function(dataset, datasetIndex) {
dataset.data.pop();
});
window.myLine.update();
updateLegend();
});
</script>
</body>
</html>
+1 -1
Ver Arquivo
@@ -88,7 +88,7 @@
}
},
hover: {
mode: 'label'
mode: 'dataset'
},
scales: {
xAxes: [{
+8
Ver Arquivo
@@ -48,6 +48,7 @@
"#949FB1",
"#4D5360",
],
label: 'My dataset' // for legend
}],
labels: [
"Red",
@@ -59,6 +60,13 @@
},
options: {
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: 'Our Favorite Dataset'
},
scale: {
ticks: {
beginAtZero: true
+8 -1
Ver Arquivo
@@ -56,8 +56,15 @@
},]
},
options: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Our 3 Favorite Datasets'
},
scale: {
reverse: true,
reverse: false,
ticks: {
beginAtZero: true
}
+180
Ver Arquivo
@@ -0,0 +1,180 @@
<!doctype html>
<html>
<head>
<title>Line Chart</title>
<script src="../../node_modules/moment/min/moment.min.js"></script>
<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>
<body>
<div style="width:100%;">
<canvas id="canvas" style="width:100%;height:100%"></canvas>
</div>
<br>
<br>
<button id="randomizeData">Randomize Data</button>
<button id="addDataset">Add Dataset</button>
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<div>
<h3>Legend</h3>
<div id="legendContainer">
</div>
</div>
<script>
var timeFormat = 'MM/DD/YYYY HH:mm';
function randomScalingFactor() {
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
}
function randomColorFactor() {
return Math.round(Math.random() * 255);
}
function randomColor(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
}
function newDateString(days) {
return moment().add(days, 'd').format(timeFormat);
}
var config = {
type: 'bar',
data: {
labels: [newDateString(0), newDateString(1), newDateString(2), newDateString(3), newDateString(4), newDateString(5), newDateString(6)],
datasets: [{
type: 'bar',
label: 'Dataset 1',
backgroundColor: "rgba(151,187,205,0.5)",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
borderColor: 'white',
borderWidth: 2
}, {
type: 'bar',
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()]
}, ]
},
options: {
responsive: true,
scales: {
xAxes: [{
type: "time",
display: true,
time: {
format: timeFormat,
// round: 'day'
}
}],
},
}
};
$.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);
updateLegend();
};
function updateLegend() {
$legendContainer = $('#legendContainer');
$legendContainer.empty();
$legendContainer.append(window.myLine.generateLegend());
}
$('#randomizeData').click(function() {
$.each(config.data.datasets, function(i, dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});
window.myLine.update();
updateLegend();
});
$('#addDataset').click(function() {
var newDataset = {
label: 'Dataset ' + config.data.datasets.length,
borderColor: randomColor(0.4),
backgroundColor: randomColor(0.5),
pointBorderColor: randomColor(0.7),
pointBackgroundColor: randomColor(0.5),
pointBorderWidth: 1,
data: [],
};
for (var index = 0; index < config.data.labels.length; ++index) {
newDataset.data.push(randomScalingFactor());
}
config.data.datasets.push(newDataset);
window.myLine.update();
updateLegend();
});
$('#addData').click(function() {
if (config.data.datasets.length > 0) {
config.data.labels.push(
myLine.scales['x-axis-0'].labelMoments[myLine.scales['x-axis-0'].labelMoments.length - 1].add(1, 'day')
.format('MM/DD/YYYY')
);
for (var index = 0; index < config.data.datasets.length; ++index) {
config.data.datasets[index].data.push(randomScalingFactor());
}
window.myLine.update();
updateLegend();
}
});
$('#removeDataset').click(function() {
config.data.datasets.splice(0, 1);
window.myLine.update();
updateLegend();
});
$('#removeData').click(function() {
config.data.labels.splice(-1, 1); // remove the label first
config.data.datasets.forEach(function(dataset, datasetIndex) {
config.data.datasets[datasetIndex].data.pop();
});
window.myLine.update();
updateLegend();
});
</script>
</body>
</html>
+172
Ver Arquivo
@@ -0,0 +1,172 @@
<!doctype html>
<html>
<head>
<title>Time Scale Point Data</title>
<script src="../../node_modules/moment/min/moment.min.js"></script>
<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>
<body>
<div style="width:100%;">
<canvas id="canvas" style="width:100%;height:100%"></canvas>
</div>
<br>
<br>
<button id="randomizeData">Randomize Data</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<div>
<h3>Legend</h3>
<div id="legendContainer">
</div>
</div>
<script>
function randomScalingFactor() {
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
}
function randomColorFactor() {
return Math.round(Math.random() * 255);
}
function randomColor(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
}
function newDate(days) {
return moment().add(days, 'd').toDate();
}
function newDateString(days) {
return moment().add(days, 'd').format();
}
var config = {
type: 'line',
data: {
datasets: [{
label: "Dataset with string point data",
data: [{
x: newDateString(0),
y: randomScalingFactor()
}, {
x: newDateString(2),
y: randomScalingFactor()
}, {
x: newDateString(4),
y: randomScalingFactor()
}, {
x: newDateString(5),
y: randomScalingFactor()
}],
fill: false
}, {
label: "Dataset with date object point data",
data: [{
x: newDate(0),
y: randomScalingFactor()
}, {
x: newDate(2),
y: randomScalingFactor()
}, {
x: newDate(4),
y: randomScalingFactor()
}, {
x: newDate(5),
y: randomScalingFactor()
}],
fill: false
}]
},
options: {
responsive: true,
scales: {
xAxes: [{
type: "time",
display: true,
scaleLabel: {
display: true,
labelString: 'Date'
}
}, ],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'value'
}
}]
}
}
};
$.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;
});
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
window.myLine = new Chart(ctx, config);
updateLegend();
};
function updateLegend() {
$legendContainer = $('#legendContainer');
$legendContainer.empty();
$legendContainer.append(window.myLine.generateLegend());
}
$('#randomizeData').click(function() {
$.each(config.data.datasets, function(i, dataset) {
$.each(dataset.data, function(j, dataObj) {
dataObj.y = randomScalingFactor();
});
});
window.myLine.update();
updateLegend();
});
$('#addData').click(function() {
if (config.data.datasets.length > 0) {
var newTime = myLine.scales['x-axis-0'].labelMoments[0][myLine.scales['x-axis-0'].labelMoments[0].length - 1]
.clone()
.add(1, 'day')
.format('MM/DD/YYYY HH:mm');
for (var index = 0; index < config.data.datasets.length; ++index) {
config.data.datasets[index].data.push({
x: newTime,
y: randomScalingFactor()
});
}
window.myLine.update();
updateLegend();
}
});
$('#removeData').click(function() {
config.data.datasets.forEach(function(dataset, datasetIndex) {
dataset.data.pop();
});
window.myLine.update();
updateLegend();
});
</script>
</body>
</html>
+211
Ver Arquivo
@@ -0,0 +1,211 @@
<!doctype html>
<html>
<head>
<title>Line Chart</title>
<script src="../../node_modules/moment/min/moment.min.js"></script>
<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>
<body>
<div style="width:100%;">
<canvas id="canvas" style="width:100%;height:100%"></canvas>
</div>
<br>
<br>
<button id="randomizeData">Randomize Data</button>
<button id="addDataset">Add Dataset</button>
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<div>
<h3>Legend</h3>
<div id="legendContainer">
</div>
</div>
<script>
var timeFormat = 'MM/DD/YYYY HH:mm';
function randomScalingFactor() {
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
}
function randomColorFactor() {
return Math.round(Math.random() * 255);
}
function randomColor(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
}
function newDate(days) {
return moment().add(days, 'd').toDate();
}
function newDateString(days) {
return moment().add(days, 'd').format(timeFormat);
}
function newTimestamp(days) {
return moment().add(days, 'd').unix();
}
var config = {
type: 'line',
data: {
labels: [newDate(0), newDate(1), newDate(2), newDate(3), newDate(4), newDate(5), newDate(6)], // Date Objects
datasets: [{
label: "My First dataset",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
fill: false,
borderDash: [5, 5],
}, {
label: "My Second dataset",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
}, {
label: "Dataset with point data",
data: [{
x: newDateString(0),
y: randomScalingFactor()
}, {
x: newDateString(5),
y: randomScalingFactor()
}, {
x: newDateString(7),
y: randomScalingFactor()
}, {
x: newDateString(15),
y: randomScalingFactor()
}],
fill: false
}]
},
options: {
responsive: true,
scales: {
xAxes: [{
type: "time",
time: {
format: timeFormat,
// round: 'day'
tooltipFormat: 'll HH:mm'
},
scaleLabel: {
display: true,
labelString: 'Date'
}
}, ],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'value'
}
}]
},
}
};
$.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);
updateLegend();
};
function updateLegend() {
$legendContainer = $('#legendContainer');
$legendContainer.empty();
$legendContainer.append(window.myLine.generateLegend());
}
$('#randomizeData').click(function() {
$.each(config.data.datasets, function(i, dataset) {
$.each(dataset.data, function(j, dataObj) {
if (typeof dataObj === 'object') {
dataObj.y = randomScalingFactor();
} else {
dataset.data[j] = randomScalingFactor();
}
});
});
window.myLine.update();
updateLegend();
});
$('#addDataset').click(function() {
var newDataset = {
label: 'Dataset ' + config.data.datasets.length,
borderColor: randomColor(0.4),
backgroundColor: randomColor(0.5),
pointBorderColor: randomColor(0.7),
pointBackgroundColor: randomColor(0.5),
pointBorderWidth: 1,
data: [],
};
for (var index = 0; index < config.data.labels.length; ++index) {
newDataset.data.push(randomScalingFactor());
}
config.data.datasets.push(newDataset);
window.myLine.update();
updateLegend();
});
$('#addData').click(function() {
if (config.data.datasets.length > 0) {
config.data.labels.push(newDate(config.data.labels.length));
for (var index = 0; index < config.data.datasets.length; ++index) {
if (typeof config.data.datasets[index].data[0] === "object") {
config.data.datasets[index].data.push({
x: newDate(config.data.datasets[index].data.length),
y: randomScalingFactor(),
})
} else {
config.data.datasets[index].data.push(randomScalingFactor());
}
}
window.myLine.update();
updateLegend();
}
});
$('#removeDataset').click(function() {
config.data.datasets.splice(0, 1);
window.myLine.update();
updateLegend();
});
$('#removeData').click(function() {
config.data.labels.splice(-1, 1); // remove the label first
config.data.datasets.forEach(function(dataset, datasetIndex) {
dataset.data.pop();
});
window.myLine.update();
updateLegend();
});
</script>
</body>
</html>
-22
Ver Arquivo
@@ -5,29 +5,7 @@
var Chart = root.Chart;
var helpers = Chart.helpers;
var defaultConfig = {
aspectRatio: 1,
legendCallback: function(chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend">');
if (chart.data.datasets.length) {
for (var i = 0; i < chart.data.datasets[0].data.length; ++i) {
text.push('<li><span style="background-color:' + chart.data.datasets[0].backgroundColor[i] + '">');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push('</span></li>');
}
}
text.push('</ul>');
return text.join("");
}
};
Chart.Doughnut = function(context, config) {
config.options = helpers.configMerge(defaultConfig, config.options);
config.type = 'doughnut';
return new Chart(context, config);
-22
Ver Arquivo
@@ -5,29 +5,7 @@
var Chart = root.Chart;
var helpers = Chart.helpers;
var defaultConfig = {
aspectRatio: 1,
legendCallback: function(chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend">');
if (chart.data.datasets.length) {
for (var i = 0; i < chart.data.datasets[0].data.length; ++i) {
text.push('<li><span style="background-color:' + chart.data.datasets[0].backgroundColor[i] + '">');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push('</span></li>');
}
}
text.push('</ul>');
return text.join("");
}
};
Chart.PolarArea = function(context, config) {
config.options = helpers.configMerge(defaultConfig, config.options);
config.type = 'polarArea';
return new Chart(context, config);
+21 -79
Ver Arquivo
@@ -30,49 +30,19 @@
},
};
Chart.controllers.bar = function(chart, datasetIndex) {
this.initialize.call(this, chart, datasetIndex);
};
helpers.extend(Chart.controllers.bar.prototype, {
Chart.controllers.bar = Chart.DatasetController.extend({
initialize: function(chart, datasetIndex) {
this.chart = chart;
this.index = datasetIndex;
this.linkScales();
this.addElements();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
linkScales: function() {
if (!this.getDataset().xAxisID) {
this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
}
Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex);
if (!this.getDataset().yAxisID) {
this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
}
// Use this to indicate that this is a bar dataset.
this.getDataset().bar = true;
},
getDataset: function() {
return this.chart.data.datasets[this.index];
},
getScaleForID: function(scaleID) {
return this.chart.scales[scaleID];
},
// Get the number of datasets that display bars. We use this to correctly calculate the bar width
getBarCount: function getBarCount() {
var barCount = 0;
helpers.each(this.chart.data.datasets, function(dataset) {
if (helpers.isDatasetVisible(dataset)) {
if (dataset.type === 'bar') {
++barCount;
} else if (dataset.type === undefined && this.chart.config.type === 'bar') {
++barCount;
}
if (helpers.isDatasetVisible(dataset) && dataset.bar) {
++barCount;
}
}, this);
return barCount;
@@ -102,30 +72,6 @@
this.getDataset().metaData.splice(index, 0, rectangle);
},
removeElement: function(index) {
this.getDataset().metaData.splice(index, 1);
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
var numData = this.getDataset().data.length;
var numRectangles = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numRectangles) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numRectangles - numData);
} else if (numData > numRectangles) {
// Add new elements
for (var index = numRectangles; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
update: function update(reset) {
var numBars = this.getBarCount();
@@ -136,8 +82,8 @@
updateElement: function updateElement(rectangle, index, reset, numBars) {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var yScalePoint;
@@ -181,8 +127,8 @@
calculateBarBase: function(datasetIndex, index) {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var base = 0;
@@ -225,17 +171,14 @@
getRuler: function() {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForID(this.getDataset().yAxisID);
/*var datasetCount = !this.chart.isCombo ? this.chart.data.datasets.length : helpers.where(this.chart.data.datasets, function(ds) {
return ds.type == 'bar';
}).length;*/
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var datasetCount = this.getBarCount();
var tickWidth = (function() {
var min = xScale.getPixelForValue(null, 1) - xScale.getPixelForValue(null, 0);
var min = xScale.getPixelForTick(1) - xScale.getPixelForTick(0);
for (var i = 2; i < this.getDataset().data.length; i++) {
min = Math.min(xScale.getPixelForValue(null, i) - xScale.getPixelForValue(null, i - 1), min);
min = Math.min(xScale.getPixelForTick(i) - xScale.getPixelForTick(i - 1), min);
}
return min;
}).call(this);
@@ -258,7 +201,7 @@
calculateBarWidth: function() {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var ruler = this.getRuler();
if (xScale.options.stacked) {
@@ -274,8 +217,7 @@
var barIndex = 0;
for (var j = 0; j < datasetIndex; ++j) {
if (helpers.isDatasetVisible(this.chart.data.datasets[j]) &&
(this.chart.data.datasets[j].type === 'bar' || (this.chart.data.datasets[j].type === undefined && this.chart.config.type === 'bar'))) {
if (helpers.isDatasetVisible(this.chart.data.datasets[j]) && this.chart.data.datasets[j].bar) {
++barIndex;
}
}
@@ -285,12 +227,12 @@
calculateBarX: function(index, datasetIndex) {
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var barIndex = this.getBarIndex(datasetIndex);
var ruler = this.getRuler();
var leftTick = xScale.getPixelForValue(null, index, barIndex, this.chart.isCombo);
var leftTick = xScale.getPixelForValue(null, index, datasetIndex, this.chart.isCombo);
leftTick -= this.chart.isCombo ? (ruler.tickWidth / 2) : 0;
if (xScale.options.stacked) {
@@ -307,8 +249,8 @@
calculateBarY: function(index, datasetIndex) {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var value = this.getDataset().data[index];
+1 -59
Ver Arquivo
@@ -31,40 +31,7 @@
};
Chart.controllers.bubble = function(chart, datasetIndex) {
this.initialize.call(this, chart, datasetIndex);
};
helpers.extend(Chart.controllers.bubble.prototype, {
initialize: function(chart, datasetIndex) {
this.chart = chart;
this.index = datasetIndex;
this.linkScales();
this.addElements();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
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];
},
Chart.controllers.bubble = Chart.DatasetController.extend({
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
@@ -90,31 +57,6 @@
// Add to the points array
this.getDataset().metaData.splice(index, 0, point);
},
removeElement: function(index) {
this.getDataset().metaData.splice(index, 1);
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
// Handle the number of data points changing
var numData = this.getDataset().data.length;
var numPoints = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numPoints) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numPoints - numData);
} else if (numData > numPoints) {
// Add new elements
for (var index = numPoints; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
update: function update(reset) {
+66 -50
Ver Arquivo
@@ -13,9 +13,59 @@
//Boolean - Whether we animate scaling the Doughnut from the centre
animateScale: false,
},
aspectRatio: 1,
hover: {
mode: 'single'
},
legendCallback: function(chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend">');
if (chart.data.datasets.length) {
for (var i = 0; i < chart.data.datasets[0].data.length; ++i) {
text.push('<li><span style="background-color:' + chart.data.datasets[0].backgroundColor[i] + '">');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push('</span></li>');
}
}
text.push('</ul>');
return text.join("");
},
legend: {
labels: {
generateLabels: function(data) {
return data.labels.map(function(label, i) {
return {
text: label,
fillStyle: data.datasets[0].backgroundColor[i],
hidden: isNaN(data.datasets[0].data[i]),
// Extra data used for toggling the correct item
index: i
};
});
}
},
onClick: function(e, legendItem) {
helpers.each(this.chart.data.datasets, function(dataset) {
dataset.metaHiddenData = dataset.metaHiddenData || [];
var idx = legendItem.index;
if (!isNaN(dataset.data[idx])) {
dataset.metaHiddenData[idx] = dataset.data[idx];
dataset.data[idx] = NaN;
} else if (!isNaN(dataset.metaHiddenData[idx])) {
dataset.data[idx] = dataset.metaHiddenData[idx];
}
});
this.chart.update();
}
},
//The percentage of the chart that we cut out of the middle.
cutoutPercentage: 50,
@@ -36,29 +86,11 @@
});
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();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
linkScales: function() {
// no scales for doughnut
},
getDataset: function() {
return this.chart.data.datasets[this.index];
},
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
helpers.each(this.getDataset().data, function(value, index) {
@@ -87,29 +119,6 @@
// Add to the points array
this.getDataset().metaData.splice(index, 0, arc);
},
removeElement: function(index) {
this.getDataset().metaData.splice(index, 1);
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
// Make sure we have metaData for each data point
var numData = this.getDataset().data.length;
var numArcs = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numArcs) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numArcs - numData);
} else if (numData > numArcs) {
// Add new elements
for (var index = numArcs; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
getVisibleDatasetCount: function getVisibleDatasetCount() {
return helpers.where(this.chart.data.datasets, function(ds) { return helpers.isDatasetVisible(ds); }).length;
@@ -129,14 +138,17 @@
},
update: function update(reset) {
var minSize = Math.min(this.chart.chartArea.right - this.chart.chartArea.left, this.chart.chartArea.bottom - this.chart.chartArea.top);
this.chart.outerRadius = Math.max((helpers.min([this.chart.chart.width, this.chart.chart.height]) / 2) - this.chart.options.elements.arc.borderWidth / 2, 0);
this.chart.outerRadius = Math.max((minSize / 2) - this.chart.options.elements.arc.borderWidth / 2, 0);
this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0);
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.getVisibleDatasetCount();
this.getDataset().total = 0;
helpers.each(this.getDataset().data, function(value) {
this.getDataset().total += Math.abs(value);
if (!isNaN(value)) {
this.getDataset().total += Math.abs(value);
}
}, this);
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.getRingIndex(this.index));
@@ -147,13 +159,17 @@
}, this);
},
updateElement: function(arc, index, reset) {
var centerX = (this.chart.chartArea.left + this.chart.chartArea.right) / 2;
var centerY = (this.chart.chartArea.top + this.chart.chartArea.bottom) / 2;
var resetModel = {
x: this.chart.chart.width / 2,
y: this.chart.chart.height / 2,
x: centerX,
y: centerY,
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
endAngle: Math.PI * -0.5,
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,
innerRadius: (this.chart.options.animation.animateScale) ? 0 : this.innerRadius
};
helpers.extend(arc, {
@@ -164,8 +180,8 @@
// Desired view properties
_model: reset ? resetModel : {
x: this.chart.chart.width / 2,
y: this.chart.chart.height / 2,
x: centerX,
y: centerY,
circumference: this.calculateCircumference(this.getDataset().data[index]),
outerRadius: this.outerRadius,
innerRadius: this.innerRadius,
@@ -225,7 +241,7 @@
},
calculateCircumference: function(value) {
if (this.getDataset().total > 0) {
if (this.getDataset().total > 0 && !isNaN(value)) {
return (Math.PI * 1.999999) * (value / this.getDataset().total);
} else {
return 0;
+3 -60
Ver Arquivo
@@ -24,40 +24,7 @@
};
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();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
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];
},
Chart.controllers.line = Chart.DatasetController.extend({
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
@@ -93,30 +60,6 @@
// Make sure bezier control points are updated
this.updateBezierControlPoints();
},
removeElement: function(index) {
this.getDataset().metaData.splice(index, 1);
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
// Handle the number of data points changing
var numData = this.getDataset().data.length;
var numPoints = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numPoints) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numPoints - numData);
} else if (numData > numPoints) {
// Add new elements
for (var index = numPoints; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
update: function update(reset) {
var line = this.getDataset().metaDataset;
@@ -144,7 +87,7 @@
// Model
_model: {
// Appearance
tension: line.custom && line.custom.tension ? line.custom.tension : (this.getDataset().tension || this.chart.options.elements.line.tension),
tension: line.custom && line.custom.tension ? line.custom.tension : helpers.getValueOrDefault(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),
@@ -238,7 +181,7 @@
x: xScale.getPixelForValue(this.getDataset().data[index], index, this.index, this.chart.isCombo),
y: reset ? scaleBase : this.calculatePointY(this.getDataset().data[index], index, this.index, this.chart.isCombo),
// Appearance
tension: point.custom && point.custom.tension ? point.custom.tension : (this.getDataset().tension || this.chart.options.elements.line.tension),
tension: point.custom && point.custom.tension ? point.custom.tension : helpers.getValueOrDefault(this.getDataset().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: this.getPointBackgroundColor(point, index),
borderColor: this.getPointBorderColor(point, index),
+82 -70
Ver Arquivo
@@ -18,6 +18,56 @@
animateRotate: true,
animateScale: true,
aspectRatio: 1,
legendCallback: function(chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend">');
if (chart.data.datasets.length) {
for (var i = 0; i < chart.data.datasets[0].data.length; ++i) {
text.push('<li><span style="background-color:' + chart.data.datasets[0].backgroundColor[i] + '">');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push('</span></li>');
}
}
text.push('</ul>');
return text.join("");
},
legend: {
labels: {
generateLabels: function(data) {
return data.labels.map(function(label, i) {
return {
text: label,
fillStyle: data.datasets[0].backgroundColor[i],
hidden: isNaN(data.datasets[0].data[i]),
// Extra data used for toggling the correct item
index: i
};
});
}
},
onClick: function(e, legendItem) {
helpers.each(this.chart.data.datasets, function(dataset) {
dataset.metaHiddenData = dataset.metaHiddenData || [];
var idx = legendItem.index;
if (!isNaN(dataset.data[idx])) {
dataset.metaHiddenData[idx] = dataset.data[idx];
dataset.data[idx] = NaN;
} else if (!isNaN(dataset.metaHiddenData[idx])) {
dataset.data[idx] = dataset.metaHiddenData[idx];
}
});
this.chart.update();
}
},
// Need to override these to give a nice default
tooltips: {
callbacks: {
@@ -29,34 +79,10 @@
}
};
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();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
Chart.controllers.polarArea = Chart.DatasetController.extend({
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) {
@@ -81,37 +107,13 @@
// Add to the points array
this.getDataset().metaData.splice(index, 0, arc);
},
removeElement: function(index) {
this.getDataset().metaData.splice(index, 1);
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
// Handle the number of data points changing
var numData = this.getDataset().data.length;
var numPoints = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numPoints) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numPoints - numData);
} else if (numData > numPoints) {
// Add new elements
for (var index = numPoints; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
getVisibleDatasetCount: function getVisibleDatasetCount() {
return helpers.where(this.chart.data.datasets, function(ds) { return helpers.isDatasetVisible(ds); }).length;
},
update: function update(reset) {
this.chart.outerRadius = Math.max((helpers.min([this.chart.chart.width, this.chart.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2, 0);
var minSize = Math.min(this.chart.chartArea.right - this.chart.chartArea.left, this.chart.chartArea.bottom - this.chart.chartArea.top);
this.chart.outerRadius = Math.max((minSize - this.chart.options.elements.arc.borderWidth / 2) / 2, 0);
this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0);
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.getVisibleDatasetCount();
@@ -127,14 +129,27 @@
this.updateElement(arc, index, reset);
}, this);
},
updateElement: function(arc, index, reset) {
var circumference = 1 / this.getDataset().data.length * 2;
var startAngle = (-0.5 * Math.PI) + (Math.PI * circumference) * index;
var endAngle = startAngle + (circumference * Math.PI);
var circumference = this.calculateCircumference(this.getDataset().data[index]);
var centerX = (this.chart.chartArea.left + this.chart.chartArea.right) / 2;
var centerY = (this.chart.chartArea.top + this.chart.chartArea.bottom) / 2;
// If there is NaN data before us, we need to calculate the starting angle correctly.
// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
var notNullIndex = 0;
for (var i = 0; i < index; ++i) {
if (!isNaN(this.getDataset().data[i])) {
++notNullIndex;
}
}
var startAngle = (-0.5 * Math.PI) + (circumference * notNullIndex);
var endAngle = startAngle + circumference;
var resetModel = {
x: this.chart.chart.width / 2,
y: this.chart.chart.height / 2,
x: centerX,
y: centerY,
innerRadius: 0,
outerRadius: this.chart.options.animateScale ? 0 : this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]),
startAngle: this.chart.options.animateRotate ? Math.PI * -0.5 : startAngle,
@@ -157,8 +172,8 @@
// Desired view properties
_model: reset ? resetModel : {
x: this.chart.chart.width / 2,
y: this.chart.chart.height / 2,
x: centerX,
y: centerY,
innerRadius: 0,
outerRadius: this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]),
startAngle: startAngle,
@@ -202,19 +217,16 @@
},
calculateCircumference: function(value) {
if (this.getDataset().total > 0) {
return (Math.PI * 2) * (value / this.getDataset().total);
} else {
if (isNaN(value)) {
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
});
},
} else {
// Count the number of NaN values
var numNaN = helpers.where(this.getDataset().data, function(data) {
return isNaN(data);
}).length;
return (2 * Math.PI) / (this.getDataset().data.length - numNaN);
}
}
});
}).call(this);
+3 -55
Ver Arquivo
@@ -18,35 +18,11 @@
},
};
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();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
Chart.controllers.radar = Chart.DatasetController.extend({
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 || [];
@@ -87,30 +63,6 @@
// Make sure bezier control points are updated
this.updateBezierControlPoints();
},
removeElement: function(index) {
this.getDataset().metaData.splice(index, 1);
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
// Handle the number of data points changing
var numData = this.getDataset().data.length;
var numPoints = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numPoints) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numPoints - numData);
} else if (numData > numPoints) {
// Add new elements
for (var index = numPoints; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
update: function update(reset) {
@@ -136,7 +88,7 @@
// Model
_model: {
// Appearance
tension: this.getDataset().tension || this.chart.options.elements.line.tension,
tension: helpers.getValueOrDefault(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,
@@ -175,7 +127,7 @@
y: reset ? this.chart.scale.yCenter : pointPosition.y,
// Appearance
tension: point.custom && point.custom.tension ? point.custom.tension : this.chart.options.elements.line.tension,
tension: point.custom && point.custom.tension ? point.custom.tension : helpers.getValueOrDefault(this.getDataset().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),
@@ -246,9 +198,5 @@
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);
+11 -3
Ver Arquivo
@@ -9,8 +9,8 @@
Chart.defaults.global.animation = {
duration: 1000,
easing: "easeOutQuart",
onProgress: function() {},
onComplete: function() {},
onProgress: helpers.noop,
onComplete: helpers.noop,
};
Chart.Animation = Chart.Element.extend({
@@ -77,18 +77,26 @@
}
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.onAnimationProgress && this.animations[i].animationObject.onAnimationProgress.call) {
this.animations[i].animationObject.onAnimationProgress.call(this.animations[i].chartInstance, this.animations[i]);
}
if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps) {
if (this.animations[i].animationObject.onAnimationComplete && this.animations[i].animationObject.onAnimationComplete.call) {
this.animations[i].animationObject.onAnimationComplete.call(this.animations[i].chartInstance, this.animations[i]);
}
// executed the last frame. Remove the animation.
this.animations[i].chartInstance.animating = false;
this.animations.splice(i, 1);
+64 -48
Ver Arquivo
@@ -59,6 +59,8 @@
this.ensureScalesHaveIDs();
this.buildOrUpdateControllers();
this.buildScales();
this.buildSurroundingItems();
this.updateLayout();
this.resetElements();
this.initToolTip();
this.update();
@@ -165,7 +167,33 @@
this.scales.radialScale = scale;
}
Chart.scaleService.update(this, this.chart.width, this.chart.height);
Chart.scaleService.addScalesToLayout(this);
},
buildSurroundingItems: function() {
if (this.options.title) {
this.titleBlock = new Chart.Title({
ctx: this.chart.ctx,
options: this.options.title,
chart: this
});
Chart.layoutService.addBox(this, this.titleBlock);
}
if (this.options.legend) {
this.legend = new Chart.Legend({
ctx: this.chart.ctx,
options: this.options.legend,
chart: this,
});
Chart.layoutService.addBox(this, this.legend);
}
},
updateLayout: function() {
Chart.layoutService.update(this, this.chart.width, this.chart.height);
},
buildOrUpdateControllers: function buildOrUpdateControllers(resetNewControllers) {
@@ -180,13 +208,12 @@
if (dataset.controller) {
dataset.controller.updateIndex(datasetIndex);
return;
}
} else {
dataset.controller = new Chart.controllers[type](this, datasetIndex);
dataset.controller = new Chart.controllers[type](this, datasetIndex);
if (resetNewControllers) {
dataset.controller.reset();
if (resetNewControllers) {
dataset.controller.reset();
}
}
}, this);
@@ -210,11 +237,11 @@
// In case the entire data object changed
this.tooltip._data = this.data;
Chart.scaleService.update(this, this.chart.width, this.chart.height);
// Make sure dataset controllers are updated and new controllers are reset
this.buildOrUpdateControllers(true);
Chart.layoutService.update(this, this.chart.width, this.chart.height);
// Make sure all dataset controllers have correct meta data counts
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.controller.buildOrUpdateElements();
@@ -229,7 +256,7 @@
render: function render(duration, lazy) {
if ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration == 'undefined' && this.options.animation.duration !== 0)) {
if (this.options.animation && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration == 'undefined' && this.options.animation.duration !== 0))) {
var animation = new Chart.Animation();
animation.numSteps = (duration || this.options.animation.duration) / 16.66; //60 fps
animation.easing = this.options.animation.easing;
@@ -244,14 +271,14 @@
};
// user events
animation.onAnimationProgress = this.options.onAnimationProgress;
animation.onAnimationComplete = this.options.onAnimationComplete;
animation.onAnimationProgress = this.options.animation.onProgress;
animation.onAnimationComplete = this.options.animation.onComplete;
Chart.animationService.addAnimation(this, animation, duration, lazy);
} else {
this.draw();
if (this.options.onAnimationComplete && this.options.onAnimationComplete.call) {
this.options.onAnimationComplete.call(this);
if (this.options.animation && this.options.animation.onComplete && this.options.animation.onComplete.call) {
this.options.animation.onComplete.call(this);
}
}
return this;
@@ -262,8 +289,8 @@
this.clear();
// Draw all the scales
helpers.each(this.scales, function(scale) {
scale.draw(this.chartArea);
helpers.each(this.boxes, function(box) {
box.draw(this.chartArea);
}, this);
if (this.scale) {
this.scale.draw();
@@ -331,22 +358,13 @@
},
getDatasetAtEvent: function(e) {
var eventPosition = helpers.getRelativePosition(e, this.chart);
var elementsArray = [];
var elementsArray = this.getElementAtEvent(e);
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
if (helpers.isDatasetVisible(dataset)) {
helpers.each(dataset.metaData, function(element, elementIndex) {
if (element.inLabelRange(eventPosition.x, eventPosition.y)) {
helpers.each(dataset.metaData, function(element, index) {
elementsArray.push(element);
}, this);
}
}, this);
}
}, this);
if (elementsArray.length > 0) {
elementsArray = this.data.datasets[elementsArray[0]._datasetIndex].metaData;
}
return elementsArray.length ? elementsArray : [];
return elementsArray;
},
generateLegend: function generateLegend() {
@@ -382,6 +400,7 @@
initToolTip: function initToolTip() {
this.tooltip = new Chart.Tooltip({
_chart: this.chart,
_chartInstance: this,
_data: this.data,
_options: this.options,
}, this);
@@ -401,30 +420,23 @@
this.active = [];
this.tooltipActive = [];
} else {
this.active = function() {
switch (this.options.hover.mode) {
var _this = this;
var getItemsForMode = function(mode) {
switch (mode) {
case 'single':
return this.getElementAtEvent(e);
return _this.getElementAtEvent(e);
case 'label':
return this.getElementsAtEvent(e);
return _this.getElementsAtEvent(e);
case 'dataset':
return this.getDatasetAtEvent(e);
return _this.getDatasetAtEvent(e);
default:
return e;
}
}.call(this);
this.tooltipActive = function() {
switch (this.options.tooltips.mode) {
case 'single':
return this.getElementAtEvent(e);
case 'label':
return this.getElementsAtEvent(e);
case 'dataset':
return this.getDatasetAtEvent(e);
default:
return e;
}
}.call(this);
};
this.active = getItemsForMode(this.options.hover.mode);
this.tooltipActive = getItemsForMode(this.options.tooltips.mode);
}
// On Hover hook
@@ -436,6 +448,10 @@
if (this.options.onClick) {
this.options.onClick.call(this, e, this.active);
}
if (this.legend && this.legend.handleEvent) {
this.legend.handleEvent(e);
}
}
var dataset;
+77
Ver Arquivo
@@ -0,0 +1,77 @@
(function() {
"use strict";
//Declare root variable - window in the browser, global on the server
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
// Base class for all dataset controllers (line, bar, etc)
Chart.DatasetController = function(chart, datasetIndex) {
this.initialize.call(this, chart, datasetIndex);
};
helpers.extend(Chart.DatasetController.prototype, {
initialize: function(chart, datasetIndex) {
this.chart = chart;
this.index = datasetIndex;
this.linkScales();
this.addElements();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
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];
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
// Handle the number of data points changing
var numData = this.getDataset().data.length;
var numMetaData = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numMetaData) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numMetaData - numData);
} else if (numData > numMetaData) {
// Add new elements
for (var index = numMetaData; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
// Controllers should implement the following
addElements: helpers.noop,
addElementAndReset: helpers.noop,
draw: helpers.noop,
removeHoverStyle: helpers.noop,
setHoverStyle: helpers.noop,
update: helpers.noop,
});
Chart.DatasetController.extend = helpers.inherits;
}).call(this);
+14 -4
Ver Arquivo
@@ -171,6 +171,9 @@
return value;
},
getValueOrDefault = helpers.getValueOrDefault = function(value, defaultValue) {
return value === undefined ? defaultValue : value;
},
indexOf = helpers.indexOf = function(arrayToSearch, item) {
if (Array.prototype.indexOf) {
return arrayToSearch.indexOf(item);
@@ -246,9 +249,10 @@
})(),
warn = helpers.warn = function(str) {
//Method for warning of errors
if (window.console && typeof window.console.warn === "function") console.warn(str);
if (console && typeof 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);
@@ -787,11 +791,11 @@
ctx.closePath();
},
color = helpers.color = function(color) {
if (!window.Color) {
if (!root.Color) {
console.log('Color.js not found!');
return color;
}
return window.Color(color);
return root.Color(color);
},
addResizeListener = helpers.addResizeListener = function(node, callback) {
// Hide an iframe before the node
@@ -844,4 +848,10 @@
isDatasetVisible = helpers.isDatasetVisible = function(dataset) {
return !dataset.hidden;
};
helpers.callCallback = function(fn, args, _tArg) {
if (fn && typeof fn.call === 'function') {
fn.apply(_tArg, args);
}
}
}).call(this);
-8
Ver Arquivo
@@ -109,14 +109,6 @@
},
};
if (typeof amd !== 'undefined') {
define(function() {
return Chart;
});
} else if (typeof module === 'object' && module.exports) {
module.exports = Chart;
}
root.Chart = Chart;
Chart.noConflict = function() {
+324
Ver Arquivo
@@ -0,0 +1,324 @@
(function() {
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
// The layout service is ver self explanatory. It's responsible for the layout within a chart.
// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
// It is this service's responsibility of carrying out that layout.
Chart.layoutService = {
defaults: {},
// Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins.
addBox: function(chartInstance, box) {
if (!chartInstance.boxes) {
chartInstance.boxes = [];
}
chartInstance.boxes.push(box);
},
removeBox: function(chartInstance, box) {
if (!chartInstance.boxes) {
return;
}
chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1);
},
// The most important function
update: function(chartInstance, width, height) {
if (!chartInstance) {
return;
}
var xPadding = width > 30 ? 5 : 2;
var yPadding = height > 30 ? 5 : 2;
var leftBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position == "left";
});
var rightBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position == "right";
});
var topBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position == "top";
});
var bottomBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position == "bottom";
});
// Boxes that overlay the chartarea such as the radialLinear scale
var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position == "chartArea";
});
function fullWidthSorter(a, b) {
}
// Ensure that full width boxes are at the very top / bottom
topBoxes.sort(function(a, b) {
return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0);
});
bottomBoxes.sort(function(a, b) {
return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0);
});
// Essentially we now have any number of boxes 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
// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
// an error will be thrown.
//
// |----------------------------------------------------|
// | T1 (Full Width) |
// |----------------------------------------------------|
// | | | T2 | |
// | |----|-------------------------------------|----|
// | | | C1 | | C2 | |
// | | |----| |----| |
// | | | | |
// | L1 | L2 | ChartArea (C0) | R1 |
// | | | | |
// | | |----| |----| |
// | | | C3 | | C4 | |
// | |----|-------------------------------------|----|
// | | | B1 | |
// |----------------------------------------------------|
// | B2 (Full Width) |
// |----------------------------------------------------|
//
// 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 layout the maximum size it can be. The layout 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
// 9. Tell any axes that overlay the chart area the positions of the chart area
// Step 1
var chartWidth = width - (2 * xPadding);
var chartHeight = height - (2 * yPadding);
var chartAreaWidth = chartWidth / 2; // min 50%
var chartAreaHeight = chartHeight / 2; // min 50%
// Step 2
var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
// Step 3
var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
// Step 4
var maxChartAreaWidth = chartWidth;
var maxChartAreaHeight = chartHeight;
var minBoxSizes = [];
helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
function getMinimumBoxSize(box) {
var minSize;
var isHorizontal = box.isHorizontal();
if (isHorizontal) {
minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
maxChartAreaHeight -= minSize.height;
} else {
minSize = box.update(verticalBoxWidth, chartAreaHeight);
maxChartAreaWidth -= minSize.width;
}
minBoxSizes.push({
horizontal: isHorizontal,
minSize: minSize,
box: box,
});
}
// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
// be if the axes are drawn at their minimum sizes.
// Steps 5 & 6
var totalLeftBoxesWidth = xPadding;
var totalRightBoxesWidth = xPadding;
var totalTopBoxesHeight = yPadding;
var totalBottomBoxesHeight = yPadding;
// Update, and calculate the left and right margins for the horizontal boxes
helpers.each(leftBoxes.concat(rightBoxes), fitBox);
helpers.each(leftBoxes, function(box) {
totalLeftBoxesWidth += box.width;
});
helpers.each(rightBoxes, function(box) {
totalRightBoxesWidth += box.width;
});
// Set the Left and Right margins for the horizontal boxes
helpers.each(topBoxes.concat(bottomBoxes), fitBox);
// Function to fit a box
function fitBox(box) {
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
return minBoxSize.box === box;
});
if (minBoxSize) {
if (box.isHorizontal()) {
var scaleMargin = {
left: totalLeftBoxesWidth,
right: totalRightBoxesWidth,
top: 0,
bottom: 0,
};
box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, minBoxSize.minSize.height, scaleMargin);
} else {
box.update(minBoxSize.minSize.width, maxChartAreaHeight);
}
}
}
// Figure out how much margin is on the top and bottom of the vertical boxes
helpers.each(topBoxes, function(box) {
totalTopBoxesHeight += box.height;
});
helpers.each(bottomBoxes, function(box) {
totalBottomBoxesHeight += box.height;
});
// Let the left layout know the final margin
helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
function finalFitVerticalBox(box) {
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
return minBoxSize.box === box;
});
var scaleMargin = {
left: 0,
right: 0,
top: totalTopBoxesHeight,
bottom: totalBottomBoxesHeight
};
if (minBoxSize) {
box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
}
}
// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
totalLeftBoxesWidth = xPadding;
totalRightBoxesWidth = xPadding;
totalTopBoxesHeight = yPadding;
totalBottomBoxesHeight = yPadding;
helpers.each(leftBoxes, function(box) {
totalLeftBoxesWidth += box.width;
});
helpers.each(rightBoxes, function(box) {
totalRightBoxesWidth += box.width;
});
helpers.each(topBoxes, function(box) {
totalTopBoxesHeight += box.height;
});
helpers.each(bottomBoxes, function(box) {
totalBottomBoxesHeight += box.height;
});
// Figure out if our chart area changed. This would occur if the dataset layout label rotation
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
// without calling `fit` again
var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
helpers.each(leftBoxes, function(box) {
box.height = newMaxChartAreaHeight;
});
helpers.each(rightBoxes, function(box) {
box.height = newMaxChartAreaHeight;
});
helpers.each(topBoxes, function(box) {
box.width = newMaxChartAreaWidth;
});
helpers.each(bottomBoxes, function(box) {
box.width = newMaxChartAreaWidth;
});
maxChartAreaHeight = newMaxChartAreaHeight;
maxChartAreaWidth = newMaxChartAreaWidth;
}
// Step 7 - Position the boxes
var left = xPadding;
var top = yPadding;
var right = 0;
var bottom = 0;
helpers.each(leftBoxes.concat(topBoxes), placeBox);
// Account for chart width and height
left += maxChartAreaWidth;
top += maxChartAreaHeight;
helpers.each(rightBoxes, placeBox);
helpers.each(bottomBoxes, placeBox);
function placeBox(box) {
if (box.isHorizontal()) {
box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth;
box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth;
box.top = top;
box.bottom = top + box.height;
// Move to next point
top = box.bottom;
} else {
box.left = left;
box.right = left + box.width;
box.top = totalTopBoxesHeight;
box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
// Move to next point
left = box.right;
}
}
// Step 8
chartInstance.chartArea = {
left: totalLeftBoxesWidth,
top: totalTopBoxesHeight,
right: totalLeftBoxesWidth + maxChartAreaWidth,
bottom: totalTopBoxesHeight + maxChartAreaHeight,
};
// Step 9
helpers.each(chartAreaBoxes, function(box) {
box.left = chartInstance.chartArea.left;
box.top = chartInstance.chartArea.top;
box.right = chartInstance.chartArea.right;
box.bottom = chartInstance.chartArea.bottom;
box.update(maxChartAreaWidth, maxChartAreaHeight);
});
}
};
}).call(this);
+325
Ver Arquivo
@@ -0,0 +1,325 @@
(function() {
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.global.legend = {
display: true,
position: 'top',
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
// a callback that will handle
onClick: function(e, legendItem) {
var dataset = this.chart.data.datasets[legendItem.datasetIndex];
dataset.hidden = !dataset.hidden;
// We hid a dataset ... rerender the chart
this.chart.update();
},
labels: {
boxWidth: 40,
fontSize: 12,
fontStyle: "normal",
fontColor: "#666",
fontFamily: "Helvetica Neue",
padding: 10,
// Generates labels shown in the legend
// Valid properties to return:
// text : text to display
// fillStyle : fill of coloured box
// strokeStyle: stroke of coloured box
// hidden : if this legend item refers to a hidden item
// lineCap : cap style for line
// lineDash
// lineDashOffset :
// lineJoin :
// lineWidth :
generateLabels: function(data) {
return data.datasets.map(function(dataset, i) {
return {
text: dataset.label,
fillStyle: dataset.backgroundColor,
hidden: dataset.hidden,
lineCap: dataset.borderCapStyle,
lineDash: dataset.borderDash,
lineDashOffset: dataset.borderDashOffset,
lineJoin: dataset.borderJoinStyle,
lineWidth: dataset.borderWidth,
strokeStyle: dataset.borderColor,
// Below is extra data used for toggling the datasets
datasetIndex: i
};
}, this);
}
},
};
Chart.Legend = Chart.Element.extend({
initialize: function(config) {
helpers.extend(this, config);
// Contains hit boxes for each dataset (in dataset order)
this.legendHitBoxes = [];
// Are we in doughnut mode which has a different data type
this.doughnutMode = false;
},
// These methods are ordered by lifecyle. Utilities then follow.
// Any function defined here is inherited by all legend types.
// Any function can be extended by the legend type
beforeUpdate: helpers.noop,
update: function(maxWidth, maxHeight, margins) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this.beforeUpdate();
// Absorb the master measurements
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.margins = margins;
// Dimensions
this.beforeSetDimensions();
this.setDimensions();
this.afterSetDimensions();
// Labels
this.beforeBuildLabels();
this.buildLabels();
this.afterBuildLabels();
// Fit
this.beforeFit();
this.fit();
this.afterFit();
//
this.afterUpdate();
return this.minSize;
},
afterUpdate: helpers.noop,
//
beforeSetDimensions: helpers.noop,
setDimensions: function() {
// Set the unconstrained dimension before label rotation
if (this.isHorizontal()) {
// Reset position before calculating rotation
this.width = this.maxWidth;
this.left = 0;
this.right = this.width;
} else {
this.height = this.maxHeight;
// Reset position before calculating rotation
this.top = 0;
this.bottom = this.height;
}
// Reset padding
this.paddingLeft = 0;
this.paddingTop = 0;
this.paddingRight = 0;
this.paddingBottom = 0;
// Reset minSize
this.minSize = {
width: 0,
height: 0,
};
},
afterSetDimensions: helpers.noop,
//
beforeBuildLabels: helpers.noop,
buildLabels: function() {
this.legendItems = this.options.labels.generateLabels.call(this, this.chart.data);
},
afterBuildLabels: helpers.noop,
//
beforeFit: helpers.noop,
fit: function() {
var ctx = this.ctx;
var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
// Reset hit boxes
this.legendHitBoxes = [];
// Width
if (this.isHorizontal()) {
this.minSize.width = this.maxWidth; // fill all the width
} else {
this.minSize.width = this.options.display ? 10 : 0;
}
// height
if (this.isHorizontal()) {
this.minSize.height = this.options.display ? 10 : 0;
} else {
this.minSize.height = this.maxHeight; // fill all the height
}
// Increase sizes here
if (this.options.display) {
if (this.isHorizontal()) {
// Labels
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
this.lineWidths = [0];
var totalHeight = this.legendItems.length ? this.options.labels.fontSize + (this.options.labels.padding) : 0;
ctx.textAlign = "left";
ctx.textBaseline = 'top';
ctx.font = labelFont;
helpers.each(this.legendItems, function(legendItem, i) {
var width = this.options.labels.boxWidth + (this.options.labels.fontSize / 2) + ctx.measureText(legendItem.text).width;
if (this.lineWidths[this.lineWidths.length - 1] + width >= this.width) {
totalHeight += this.options.labels.fontSize + (this.options.labels.padding);
this.lineWidths[this.lineWidths.length] = this.left;
}
// Store the hitbox width and height here. Final position will be updated in `draw`
this.legendHitBoxes[i] = {
left: 0,
top: 0,
width: width,
height: this.options.labels.fontSize,
};
this.lineWidths[this.lineWidths.length - 1] += width + this.options.labels.padding;
}, this);
this.minSize.height += totalHeight;
} else {
// TODO vertical
}
}
this.width = this.minSize.width;
this.height = this.minSize.height;
},
afterFit: helpers.noop,
// Shared Methods
isHorizontal: function() {
return this.options.position == "top" || this.options.position == "bottom";
},
// Actualy draw the legend on the canvas
draw: function() {
if (this.options.display) {
var ctx = this.ctx;
var cursor = {
x: this.left + ((this.width - this.lineWidths[0]) / 2),
y: this.top + this.options.labels.padding,
line: 0,
};
var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
// Horizontal
if (this.isHorizontal()) {
// Labels
ctx.textAlign = "left";
ctx.textBaseline = 'top';
ctx.lineWidth = 0.5;
ctx.strokeStyle = this.options.labels.fontColor; // for strikethrough effect
ctx.fillStyle = this.options.labels.fontColor; // render in correct colour
ctx.font = labelFont;
helpers.each(this.legendItems, function(legendItem, i) {
var textWidth = ctx.measureText(legendItem.text).width;
var width = this.options.labels.boxWidth + (this.options.labels.fontSize / 2) + textWidth;
if (cursor.x + width >= this.width) {
cursor.y += this.options.labels.fontSize + (this.options.labels.padding);
cursor.line++;
cursor.x = this.left + ((this.width - this.lineWidths[cursor.line]) / 2);
}
// Set the ctx for the box
ctx.save();
var itemOrDefault = function(item, defaulVal) {
return item !== undefined ? item : defaulVal;
};
ctx.fillStyle = itemOrDefault(legendItem.fillStyle, Chart.defaults.global.defaultColor);
ctx.lineCap = itemOrDefault(legendItem.lineCap, Chart.defaults.global.elements.line.borderCapStyle);
ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, Chart.defaults.global.elements.line.borderDashOffset);
ctx.lineJoin = itemOrDefault(legendItem.lineJoin, Chart.defaults.global.elements.line.borderJoinStyle);
ctx.lineWidth = itemOrDefault(legendItem.lineWidth, Chart.defaults.global.elements.line.borderWidth);
ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, Chart.defaults.global.defaultColor);
if (ctx.setLineDash) {
// IE 9 and 10 do not support line dash
ctx.setLineDash(itemOrDefault(legendItem.lineDash, Chart.defaults.global.elements.line.borderDash));
}
// Draw the box
ctx.strokeRect(cursor.x, cursor.y, this.options.labels.boxWidth, this.options.labels.fontSize);
ctx.fillRect(cursor.x, cursor.y, this.options.labels.boxWidth, this.options.labels.fontSize);
ctx.restore();
this.legendHitBoxes[i].left = cursor.x;
this.legendHitBoxes[i].top = cursor.y;
// Fill the actual label
ctx.fillText(legendItem.text, this.options.labels.boxWidth + (this.options.labels.fontSize / 2) + cursor.x, cursor.y);
if (legendItem.hidden) {
// Strikethrough the text if hidden
ctx.beginPath();
ctx.lineWidth = 2;
ctx.moveTo(this.options.labels.boxWidth + (this.options.labels.fontSize / 2) + cursor.x, cursor.y + (this.options.labels.fontSize / 2));
ctx.lineTo(this.options.labels.boxWidth + (this.options.labels.fontSize / 2) + cursor.x + textWidth, cursor.y + (this.options.labels.fontSize / 2));
ctx.stroke();
}
cursor.x += width + (this.options.labels.padding);
}, this);
} else {
}
}
},
// Handle an event
handleEvent: function(e) {
var position = helpers.getRelativePosition(e, this.chart.chart);
if (position.x >= this.left && position.x <= this.right && position.y >= this.top && position.y <= this.bottom) {
// See if we are touching one of the dataset boxes
for (var i = 0; i < this.legendHitBoxes.length; ++i) {
var hitBox = this.legendHitBoxes[i];
if (position.x >= hitBox.left && position.x <= hitBox.left + hitBox.width && position.y >= hitBox.top && position.y <= hitBox.top + hitBox.height) {
// Touching an element
if (this.options.onClick) {
this.options.onClick.call(this, e, this.legendItems[i]);
}
break;
}
}
}
}
});
}).call(this);
+101 -36
Ver Arquivo
@@ -42,11 +42,12 @@
fontColor: "#666",
fontFamily: "Helvetica Neue",
maxRotation: 90,
minRotation: 20,
mirror: false,
padding: 10,
reverse: false,
display: true,
autoSkip: true,
autoSkipPadding: 20,
callback: function(value) {
return '' + value;
},
@@ -59,7 +60,9 @@
// Any function defined here is inherited by all scale types.
// Any function can be extended by the scale type
beforeUpdate: helpers.noop,
beforeUpdate: function() {
helpers.callCallback(this.options.beforeUpdate, [this]);
},
update: function(maxWidth, maxHeight, margins) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
@@ -68,12 +71,23 @@
// Absorb the master measurements
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.margins = margins;
this.margins = helpers.extend({
left: 0,
right: 0,
top: 0,
bottom: 0
}, margins);
// Dimensions
this.beforeSetDimensions();
this.setDimensions();
this.afterSetDimensions();
// Data min/max
this.beforeDataLimits();
this.determineDataLimits();
this.afterDataLimits();
// Ticks
this.beforeBuildTicks();
this.buildTicks();
@@ -97,11 +111,15 @@
return this.minSize;
},
afterUpdate: helpers.noop,
afterUpdate: function() {
helpers.callCallback(this.options.afterUpdate, [this]);
},
//
beforeSetDimensions: helpers.noop,
beforeSetDimensions: function() {
helpers.callCallback(this.options.beforeSetDimensions, [this]);
},
setDimensions: function() {
// Set the unconstrained dimension before label rotation
if (this.isHorizontal()) {
@@ -123,15 +141,31 @@
this.paddingRight = 0;
this.paddingBottom = 0;
},
afterSetDimensions: helpers.noop,
afterSetDimensions: function() {
helpers.callCallback(this.options.afterSetDimensions, [this]);
},
// Data limits
beforeDataLimits: function() {
helpers.callCallback(this.options.beforeDataLimits, [this]);
},
determineDataLimits: helpers.noop,
afterDataLimits: function() {
helpers.callCallback(this.options.afterDataLimits, [this]);
},
//
beforeBuildTicks: helpers.noop,
beforeBuildTicks: function() {
helpers.callCallback(this.options.beforeBuildTicks, [this]);
},
buildTicks: helpers.noop,
afterBuildTicks: helpers.noop,
afterBuildTicks: function() {
helpers.callCallback(this.options.afterBuildTicks, [this]);
},
beforeTickToLabelConversion: helpers.noop,
beforeTickToLabelConversion: function() {
helpers.callCallback(this.options.beforeTickToLabelConversion, [this]);
},
convertTicksToLabels: function() {
// Convert ticks to strings
this.ticks = this.ticks.map(function(numericalTick, index, ticks) {
@@ -142,11 +176,15 @@
},
this);
},
afterTickToLabelConversion: helpers.noop,
afterTickToLabelConversion: function() {
helpers.callCallback(this.options.afterTickToLabelConversion, [this]);
},
//
beforeCalculateTickRotation: helpers.noop,
beforeCalculateTickRotation: function() {
helpers.callCallback(this.options.beforeCalculateTickRotation, [this]);
},
calculateTickRotation: function() {
//Get the width of each grid by calculating the difference
//between x offsets between 0 and 1.
@@ -167,7 +205,6 @@
var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
var cosRotation;
var sinRotation;
var firstRotatedWidth;
this.labelWidth = originalLabelWidth;
@@ -177,7 +214,7 @@
var tickWidth = this.getPixelForTick(1) - this.getPixelForTick(0) - 6;
//Max label rotation can be set or default to 90 - also act as a loop counter
while (this.labelWidth > tickWidth && this.labelRotation <= this.options.ticks.maxRotation) {
while (this.labelWidth > tickWidth && this.labelRotation < this.options.ticks.maxRotation) {
cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
@@ -215,11 +252,15 @@
this.paddingRight = Math.max(this.paddingRight, 0);
}
},
afterCalculateTickRotation: helpers.noop,
afterCalculateTickRotation: function() {
helpers.callCallback(this.options.afterCalculateTickRotation, [this]);
},
//
beforeFit: helpers.noop,
beforeFit: function() {
helpers.callCallback(this.options.beforeFit, [this]);
},
fit: function() {
this.minSize = {
@@ -229,7 +270,8 @@
// Width
if (this.isHorizontal()) {
this.minSize.width = this.maxWidth; // fill all the width
// subtract the margins to line up with the chartArea if we are a full width scale
this.minSize.width = this.isFullWidth() ? this.maxWidth - this.margins.left - this.margins.right : this.maxWidth;
} else {
this.minSize.width = this.options.gridLines.display && this.options.display ? 10 : 0;
}
@@ -257,11 +299,10 @@
if (this.isHorizontal()) {
// A horizontal axis is more constrained by the height.
var maxLabelHeight = this.maxHeight - this.minSize.height;
var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
this.longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
// TODO - improve this calculation
var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.ticks.fontSize;
var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * this.longestLabelWidth) + 1.5 * this.options.ticks.fontSize;
this.minSize.height = Math.min(this.maxHeight, this.minSize.height + labelHeight);
@@ -316,11 +357,16 @@
this.height = this.minSize.height;
},
afterFit: helpers.noop,
afterFit: function() {
helpers.callCallback(this.options.afterFit, [this]);
},
// Shared Methods
isHorizontal: function() {
return this.options.position == "top" || this.options.position == "bottom";
return this.options.position === "top" || this.options.position === "bottom";
},
isFullWidth: function() {
return (this.options.fullWidth);
},
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
@@ -335,7 +381,11 @@
}
// If it is in fact an object, dive in one more level
if (typeof(rawValue) === "object") {
return getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
if (rawValue instanceof Date) {
return rawValue;
} else {
return getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
}
}
// Value is good, return it
@@ -359,7 +409,10 @@
if (includeOffset) {
pixel += tickWidth / 2;
}
return this.left + Math.round(pixel);
var finalVal = this.left + Math.round(pixel);
finalVal += this.isFullWidth() ? this.margins.left : 0;
return finalVal;
} else {
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
return this.top + (index * (innerHeight / (this.ticks.length - 1)));
@@ -367,12 +420,14 @@
},
// Utility for getting the pixel location of a percentage of scale
getPixelForDecimal: function(decimal, includeOffset) {
getPixelForDecimal: function(decimal/*, includeOffset*/) {
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
var valueOffset = (innerWidth * decimal) + this.paddingLeft;
return this.left + Math.round(valueOffset);
var finalVal = this.left + Math.round(valueOffset);
finalVal += this.isFullWidth() ? this.margins.left : 0;
return finalVal;
} else {
return this.top + (decimal * this.height);
}
@@ -388,21 +443,31 @@
var skipRatio;
var scaleLabelX;
var scaleLabelY;
var useAutoskipper = this.options.ticks.autoSkip;
// Make sure we draw text in the correct color and font
this.ctx.fillStyle = this.options.ticks.fontColor;
var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
var cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
var sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
var longestRotatedLabel = this.longestLabelWidth * cosRotation;
var rotatedLabelHeight = this.options.ticks.fontSize * sinRotation;
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 yTickStart = this.options.position === "bottom" ? this.top : this.bottom - 10;
var yTickEnd = this.options.position === "bottom" ? this.top + 10 : this.bottom;
skipRatio = false;
if ((this.options.ticks.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
skipRatio = 1 + Math.floor(((this.options.ticks.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
if (((longestRotatedLabel / 2) + this.options.ticks.autoSkipPadding) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
skipRatio = 1 + Math.floor((((longestRotatedLabel / 2) + this.options.ticks.autoSkipPadding) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
}
if (!useAutoskipper) {
skipRatio = false;
}
helpers.each(this.ticks, function(label, index) {
// Blank ticks
if ((skipRatio > 1 && index % skipRatio > 0) || (label === undefined || label === null)) {
@@ -463,15 +528,15 @@
this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
scaleLabelY = this.options.position === 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
}
} else {
setContextLineSettings = true;
var xTickStart = this.options.position == "right" ? this.left : this.right - 5;
var xTickEnd = this.options.position == "right" ? this.left + 5 : this.right;
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(label, index) {
// If the callback returned a null or undefined value, do not draw this line
@@ -519,7 +584,7 @@
this.ctx.save();
if (this.options.position == "left") {
if (this.options.position === "left") {
if (this.options.ticks.mirror) {
xLabelValue = this.right + this.options.ticks.padding;
this.ctx.textAlign = "left";
@@ -550,9 +615,9 @@
if (this.options.scaleLabel.display) {
// Draw the scale label
scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
scaleLabelX = this.options.position === 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
scaleLabelY = this.top + ((this.bottom - this.top) / 2);
var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
var rotation = this.options.position === 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
this.ctx.save();
this.ctx.translate(scaleLabelX, scaleLabelY);
+6 -305
Ver Arquivo
@@ -5,9 +5,6 @@
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
@@ -28,308 +25,12 @@
// Return the scale defaults merged with the global settings so that we always use the latest ones
return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {};
},
// The interesting function
update: 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";
});
// Scales that overlay the chartarea such as the radialLinear scale
var chartAreaScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "chartArea";
});
// 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
// 9. Tell any axes that overlay the chart area the positions 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.update(verticalScaleWidth, chartHeight);
minimumScaleSizes.push({
horizontal: false,
minSize: minSize,
scale: scaleInstance,
});
};
var horizontalScaleMinSizeFunction = function(scaleInstance) {
var minSize = scaleInstance.update(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.update(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.update(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.update(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.update(wrapper.minSize.width, maxChartHeight, scaleMargin);
}
});
// Recalculate because the size of each scale might have changed slightly due to the margins (label rotation for instance)
totalLeftWidth = xPadding;
totalRightWidth = xPadding;
totalTopHeight = yPadding;
totalBottomHeight = yPadding;
helpers.each(leftScales, function(scaleInstance) {
totalLeftWidth += scaleInstance.width;
});
helpers.each(rightScales, function(scaleInstance) {
totalRightWidth += scaleInstance.width;
});
helpers.each(topScales, function(scaleInstance) {
totalTopHeight += scaleInstance.height;
});
helpers.each(bottomScales, function(scaleInstance) {
totalBottomHeight += scaleInstance.height;
});
// Figure out if our chart area changed. This would occur if the dataset scale label rotation
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
// without calling `fit` again
var newMaxChartHeight = height - totalTopHeight - totalBottomHeight;
var newMaxChartWidth = width - totalLeftWidth - totalRightWidth;
if (newMaxChartWidth !== maxChartWidth || newMaxChartHeight !== maxChartHeight) {
helpers.each(leftScales, function(scale) {
scale.height = newMaxChartHeight;
});
helpers.each(rightScales, function(scale) {
scale.height = newMaxChartHeight;
});
helpers.each(topScales, function(scale) {
scale.width = newMaxChartWidth;
});
helpers.each(bottomScales, function(scale) {
scale.width = newMaxChartWidth;
});
maxChartHeight = newMaxChartHeight;
maxChartWidth = newMaxChartWidth;
}
// 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,
};
// Step 9
helpers.each(chartAreaScales, function(scaleInstance) {
scaleInstance.left = chartInstance.chartArea.left;
scaleInstance.top = chartInstance.chartArea.top;
scaleInstance.right = chartInstance.chartArea.right;
scaleInstance.bottom = chartInstance.chartArea.bottom;
scaleInstance.update(maxChartWidth, maxChartHeight);
});
}
}
addScalesToLayout: function(chartInstance) {
// Adds each scale to the chart.boxes array to be sized accordingly
helpers.each(chartInstance.scales, function(scale) {
Chart.layoutService.addBox(chartInstance, scale);
});
},
};
+195
Ver Arquivo
@@ -0,0 +1,195 @@
(function() {
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.global.title = {
display: false,
position: 'top',
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
fontColor: '#666',
fontFamily: 'Helvetica Neue',
fontSize: 12,
fontStyle: 'bold',
padding: 10,
// actual title
text: '',
};
Chart.Title = Chart.Element.extend({
initialize: function(config) {
helpers.extend(this, config);
this.options = helpers.configMerge(Chart.defaults.global.title, config.options);
// Contains hit boxes for each dataset (in dataset order)
this.legendHitBoxes = [];
},
// These methods are ordered by lifecyle. Utilities then follow.
beforeUpdate: helpers.noop,
update: function(maxWidth, maxHeight, margins) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this.beforeUpdate();
// Absorb the master measurements
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.margins = margins;
// Dimensions
this.beforeSetDimensions();
this.setDimensions();
this.afterSetDimensions();
// Labels
this.beforeBuildLabels();
this.buildLabels();
this.afterBuildLabels();
// Fit
this.beforeFit();
this.fit();
this.afterFit();
//
this.afterUpdate();
return this.minSize;
},
afterUpdate: helpers.noop,
//
beforeSetDimensions: helpers.noop,
setDimensions: function() {
// Set the unconstrained dimension before label rotation
if (this.isHorizontal()) {
// Reset position before calculating rotation
this.width = this.maxWidth;
this.left = 0;
this.right = this.width;
} else {
this.height = this.maxHeight;
// Reset position before calculating rotation
this.top = 0;
this.bottom = this.height;
}
// Reset padding
this.paddingLeft = 0;
this.paddingTop = 0;
this.paddingRight = 0;
this.paddingBottom = 0;
// Reset minSize
this.minSize = {
width: 0,
height: 0,
};
},
afterSetDimensions: helpers.noop,
//
beforeBuildLabels: helpers.noop,
buildLabels: helpers.noop,
afterBuildLabels: helpers.noop,
//
beforeFit: helpers.noop,
fit: function() {
var ctx = this.ctx;
var titleFont = helpers.fontString(this.options.fontSize, this.options.fontStyle, this.options.fontFamily);
// Width
if (this.isHorizontal()) {
this.minSize.width = this.maxWidth; // fill all the width
} else {
this.minSize.width = 0;
}
// height
if (this.isHorizontal()) {
this.minSize.height = 0;
} else {
this.minSize.height = this.maxHeight; // fill all the height
}
// Increase sizes here
if (this.isHorizontal()) {
// Title
if (this.options.display) {
this.minSize.height += this.options.fontSize + (this.options.padding * 2);
}
} else {
// TODO vertical
}
this.width = this.minSize.width;
this.height = this.minSize.height;
},
afterFit: helpers.noop,
// Shared Methods
isHorizontal: function() {
return this.options.position == "top" || this.options.position == "bottom";
},
// Actualy draw the title block on the canvas
draw: function() {
if (this.options.display) {
var ctx = this.ctx;
var titleX, titleY;
// Horizontal
if (this.isHorizontal()) {
// Title
if (this.options.display) {
ctx.textAlign = "center";
ctx.textBaseline = 'middle';
ctx.fillStyle = this.options.fontColor; // render in correct colour
ctx.font = helpers.fontString(this.options.fontSize, this.options.fontStyle, this.options.fontFamily);
titleX = this.left + ((this.right - this.left) / 2); // midpoint of the width
titleY = this.top + ((this.bottom - this.top) / 2); // midpoint of the height
ctx.fillText(this.options.text, titleX, titleY);
}
} else {
// Title
if (this.options.display) {
titleX = this.options.position == 'left' ? this.left + (this.options.fontSize / 2) : this.right - (this.options.fontSize / 2);
titleY = this.top + ((this.bottom - this.top) / 2);
var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
ctx.save();
ctx.translate(titleX, titleY);
ctx.rotate(rotation);
ctx.textAlign = "center";
ctx.fillStyle = this.options.fontColor; // render in correct colour
ctx.font = helpers.fontString(this.options.fontSize, this.options.fontStyle, this.options.fontFamily);
ctx.textBaseline = 'middle';
ctx.fillText(this.options.text, 0, 0);
ctx.restore();
}
}
}
}
});
}).call(this);
+267 -203
Ver Arquivo
@@ -35,7 +35,6 @@
xPadding: 6,
caretSize: 5,
cornerRadius: 6,
xOffset: 10,
multiKeyBackground: '#fff',
callbacks: {
// Args are: (tooltipItems, data)
@@ -98,7 +97,6 @@
// Positioning
xPadding: options.tooltips.xPadding,
yPadding: options.tooltips.yPadding,
xOffset: options.tooltips.xOffset,
// Body
bodyColor: options.tooltips.bodyColor,
@@ -153,8 +151,8 @@
// Args are: (tooltipItem, data)
getBeforeBody: function() {
var lines = this._options.tooltips.callbacks.beforeBody.call(this, arguments);
return helpers.isArray(lines) ? lines : [lines];
var lines = this._options.tooltips.callbacks.beforeBody.apply(this, arguments);
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
},
// Args are: (tooltipItem, data)
@@ -174,8 +172,8 @@
// Args are: (tooltipItem, data)
getAfterBody: function() {
var lines = this._options.tooltips.callbacks.afterBody.call(this, arguments);
return helpers.isArray(lines) ? lines : [lines];
var lines = this._options.tooltips.callbacks.afterBody.apply(this, arguments);
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
},
// Get the footer and beforeFooter and afterFooter lines
@@ -224,10 +222,7 @@
},
update: function(changed) {
var ctx = this._chart.ctx;
if(this._active.length){
if (this._active.length){
this._model.opacity = 1;
var element = this._active[0],
@@ -236,7 +231,7 @@
var tooltipItems = [];
if (this._options.tooltips.mode == 'single') {
if (this._options.tooltips.mode === 'single') {
var yScale = element._yScale || element._scale; // handle radar || polarArea charts
tooltipItems.push({
xLabel: element._xScale ? element._xScale.getLabelForIndex(element._index, element._datasetIndex) : '',
@@ -263,7 +258,7 @@
}
});
helpers.each(this._active, function(active, i) {
helpers.each(this._active, function(active) {
if (active) {
labelColors.push({
borderColor: active._view.borderColor,
@@ -288,9 +283,15 @@
helpers.extend(this._model, {
x: Math.round(tooltipPosition.x),
y: Math.round(tooltipPosition.y),
caretPadding: tooltipPosition.padding,
caretPadding: helpers.getValueOrDefault(tooltipPosition.padding, 2),
labelColors: labelColors,
});
// We need to determine alignment of
var tooltipSize = this.getTooltipSize(this._model);
this.determineAlignment(tooltipSize); // Smart Tooltip placement to stay on the canvas
helpers.extend(this._model, this.getBackgroundPoint(this._model, tooltipSize));
}
else{
this._model.opacity = 0;
@@ -302,219 +303,282 @@
return this;
},
draw: function() {
getTooltipSize: function getTooltipSize(vm) {
var ctx = this._chart.ctx;
var size = {
height: vm.yPadding * 2, // Tooltip Padding
width: 0
};
var combinedBodyLength = vm.body.length + vm.beforeBody.length + vm.afterBody.length;
size.height += vm.title.length * vm.titleFontSize; // Title Lines
size.height += (vm.title.length - 1) * vm.titleSpacing; // Title Line Spacing
size.height += vm.title.length ? vm.titleMarginBottom : 0; // Title's bottom Margin
size.height += combinedBodyLength * vm.bodyFontSize; // Body Lines
size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing
size.height += vm.footer.length ? vm.footerMarginTop : 0; // Footer Margin
size.height += vm.footer.length * (vm.footerFontSize); // Footer Lines
size.height += vm.footer.length ? (vm.footer.length - 1) * vm.footerSpacing : 0; // Footer Line Spacing
// Width
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
helpers.each(vm.title, function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width);
});
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
helpers.each(vm.beforeBody.concat(vm.afterBody), function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width);
}, this);
helpers.each(vm.body, function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0));
}, this);
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(vm.footer, function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width);
});
size.width += 2 * vm.xPadding;
return size;
},
determineAlignment: function determineAlignment(size) {
this._model.xAlign = this._model.yAlign = "center";
if (this._model.y < size.height) {
this._model.yAlign = 'top';
} else if (this._model.y > (this._chart.height - size.height)) {
this._model.yAlign = 'bottom';
}
var lf, rf;
var _this = this;
var midX = (this._chartInstance.chartArea.left + this._chartInstance.chartArea.right) / 2;
if (this._model.yAlign === 'center') {
lf = function(x) { return x <= midX; };
rf = function(x) { return x > midX; };
} else {
lf = function(x) { return x <= (size.width / 2); };
rf = function(x) { return x >= (_this._chart.width - (size.width / 2)); };
}
if (lf(this._model.x)) {
this._model.xAlign = 'left';
} else if (rf(this._model.x)) {
this._model.xAlign = 'right';
}
},
getBackgroundPoint: function getBackgroundPoint(vm, size) {
// Background Position
var pt = {
x: vm.x,
y: vm.y
};
if (vm.xAlign === 'right') {
pt.x -= size.width;
} else if (vm.xAlign === 'center') {
pt.x -= (size.width / 2);
}
if (vm.yAlign === 'top') {
pt.y += vm.caretPadding + vm.caretSize;
} else if (vm.yAlign === 'bottom') {
pt.y -= size.height + vm.caretPadding + vm.caretSize;
} else {
pt.y -= (size.height / 2);
}
if (vm.yAlign == 'center') {
if (vm.xAlign === 'left') {
pt.x += vm.caretPadding + vm.caretSize;
} else if (vm.xAlign === 'right') {
pt.x -= vm.caretPadding + vm.caretSize;
}
} else {
if (vm.xAlign === 'left') {
pt.x -= vm.cornerRadius + vm.caretPadding;
} else if (vm.xAlign === 'right') {
pt.x += vm.cornerRadius + vm.caretPadding;
}
}
return pt;
},
drawCaret: function drawCaret(tooltipPoint, size, opacity, caretPadding) {
var vm = this._view;
var ctx = this._chart.ctx;
var x1, x2, x3;
var y1, y2, y3;
if (vm.yAlign === 'center') {
// Left or right side
if (vm.xAlign === 'left') {
x1 = tooltipPoint.x;
x2 = x1 - vm.caretSize;
x3 = x1;
} else {
x1 = tooltipPoint.x + size.width;
x2 = x1 + vm.caretSize;
x3 = x1;
}
y2 = tooltipPoint.y + (size.height / 2);
y1 = y2 - vm.caretSize;
y3 = y2 + vm.caretSize;
} else {
if (vm.xAlign === 'left') {
x1 = tooltipPoint.x + vm.cornerRadius;
x2 = x1 + vm.caretSize;
x3 = x2 + vm.caretSize;
} else if (vm.xAlign === 'right') {
x1 = tooltipPoint.x + size.width - vm.cornerRadius;
x2 = x1 - vm.caretSize;
x3 = x2 - vm.caretSize;
} else {
x2 = tooltipPoint.x + (size.width / 2);
x1 = x2 - vm.caretSize;
x3 = x2 + vm.caretSize;
}
if (vm.yAlign === 'top') {
y1 = tooltipPoint.y;
y2 = y1 - vm.caretSize;
y3 = y1;
} else {
y1 = tooltipPoint.y + size.height;
y2 = y1 + vm.caretSize;
y3 = y1;
}
}
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
ctx.fill();
},
drawTitle: function drawTitle(pt, vm, ctx, opacity) {
if (vm.title.length) {
ctx.textAlign = vm._titleAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.titleColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
helpers.each(vm.title, function(title, i) {
ctx.fillText(title, pt.x, pt.y);
pt.y += vm.titleFontSize + vm.titleSpacing; // Line Height and spacing
if (i + 1 === vm.title.length) {
pt.y += vm.titleMarginBottom - vm.titleSpacing; // If Last, add margin, remove spacing
}
}, this);
}
},
drawBody: function drawBody(pt, vm, ctx, opacity) {
ctx.textAlign = vm._bodyAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
// Before Body
helpers.each(vm.beforeBody, function(beforeBody) {
ctx.fillText(beforeBody, pt.x, pt.y);
pt.y += vm.bodyFontSize + vm.bodySpacing;
});
helpers.each(vm.body, function(body, i) {
// Draw Legend-like boxes if needed
if (this._options.tooltips.mode !== 'single') {
// Fill a white rect so that colours merge nicely if the opacity is < 1
ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString();
ctx.fillRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize);
// Border
ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString();
ctx.strokeRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize);
// Inner square
ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString();
ctx.fillRect(pt.x + 1, pt.y + 1, vm.bodyFontSize - 2, vm.bodyFontSize - 2);
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbaString(); // Return fill style for text
}
// Body Line
ctx.fillText(body, pt.x + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0), pt.y);
pt.y += vm.bodyFontSize + vm.bodySpacing;
}, this);
// After Body
helpers.each(vm.afterBody, function(afterBody) {
ctx.fillText(afterBody, pt.x, pt.y);
pt.y += vm.bodyFontSize;
});
pt.y -= vm.bodySpacing; // Remove last body spacing
},
drawFooter: function drawFooter(pt, vm, ctx, opacity) {
if (vm.footer.length) {
pt.y += vm.footerMarginTop;
ctx.textAlign = vm._footerAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.footerColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(vm.footer, function(footer) {
ctx.fillText(footer, pt.x, pt.y);
pt.y += vm.footerFontSize + vm.footerSpacing;
}, this);
}
},
draw: function draw() {
var ctx = this._chart.ctx;
var vm = this._view;
if (this._view.opacity === 0) {
if (vm.opacity === 0) {
return;
}
// Get Dimensions
vm.position = "top";
var caretPadding = vm.caretPadding || 2;
var combinedBodyLength = vm.body.length + vm.beforeBody.length + vm.afterBody.length;
// Height
var tooltipHeight = vm.yPadding * 2; // Tooltip Padding
tooltipHeight += vm.title.length * vm.titleFontSize; // Title Lines
tooltipHeight += (vm.title.length - 1) * vm.titleSpacing; // Title Line Spacing
tooltipHeight += vm.title.length ? vm.titleMarginBottom : 0; // Title's bottom Margin
tooltipHeight += combinedBodyLength * vm.bodyFontSize; // Body Lines
tooltipHeight += (combinedBodyLength - 1) * vm.bodySpacing; // Body Line Spacing
tooltipHeight += vm.footer.length ? vm.footerMarginTop : 0; // Footer Margin
tooltipHeight += vm.footer.length * (vm.footerFontSize); // Footer Lines
tooltipHeight += (vm.footer.length - 1) * vm.footerSpacing; // Footer Line Spacing
// Width
var tooltipWidth = 0;
helpers.each(vm.title, function(line, i) {
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width);
});
helpers.each(vm.body, function(line, i) {
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width + (this._options.tooltips.mode != 'single' ? (vm.bodyFontSize + 2) : 0));
}, this);
helpers.each(vm.footer, function(line, i) {
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
tooltipWidth = Math.max(tooltipWidth, ctx.measureText(line).width);
});
tooltipWidth += 2 * vm.xPadding;
var tooltipTotalWidth = tooltipWidth + vm.caretSize + caretPadding;
// Smart Tooltip placement to stay on the canvas
// Top, center, or bottom
vm.yAlign = "center";
if (vm.y - (tooltipHeight / 2) < 0) {
vm.yAlign = "top";
} else if (vm.y + (tooltipHeight / 2) > this._chart.height) {
vm.yAlign = "bottom";
}
// Left or Right
vm.xAlign = "right";
if (vm.x + tooltipTotalWidth > this._chart.width) {
vm.xAlign = "left";
}
// Background Position
var tooltipX = vm.x,
tooltipY = vm.y;
if (vm.yAlign == 'top') {
tooltipY = vm.y - vm.caretSize - vm.cornerRadius;
} else if (vm.yAlign == 'bottom') {
tooltipY = vm.y - tooltipHeight + vm.caretSize + vm.cornerRadius;
} else {
tooltipY = vm.y - (tooltipHeight / 2);
}
if (vm.xAlign == 'left') {
tooltipX = vm.x - tooltipTotalWidth;
} else if (vm.xAlign == 'right') {
tooltipX = vm.x + caretPadding + vm.caretSize;
} else {
tooltipX = vm.x + (tooltipTotalWidth / 2);
}
// Draw Background
var caretPadding = vm.caretPadding;
var tooltipSize = this.getTooltipSize(vm);
var pt = {
x: vm.x,
y: vm.y
};
// IE11/Edge does not like very small opacities, so snap to 0
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
if (this._options.tooltips.enabled) {
// Draw Background
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString();
helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipHeight, vm.cornerRadius);
helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
ctx.fill();
}
// Draw Caret
if (this._options.tooltips.enabled) {
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(opacity).rgbString();
if (vm.xAlign == 'left') {
ctx.beginPath();
ctx.moveTo(vm.x - caretPadding, vm.y);
ctx.lineTo(vm.x - caretPadding - vm.caretSize, vm.y - vm.caretSize);
ctx.lineTo(vm.x - caretPadding - vm.caretSize, vm.y + vm.caretSize);
ctx.closePath();
ctx.fill();
} else {
ctx.beginPath();
ctx.moveTo(vm.x + caretPadding, vm.y);
ctx.lineTo(vm.x + caretPadding + vm.caretSize, vm.y - vm.caretSize);
ctx.lineTo(vm.x + caretPadding + vm.caretSize, vm.y + vm.caretSize);
ctx.closePath();
ctx.fill();
}
}
// Draw Title, Body, and Footer
if (this._options.tooltips.enabled) {
var yBase = tooltipY + vm.yPadding;
var xBase = tooltipX + vm.xPadding;
// Draw Caret
this.drawCaret(pt, tooltipSize, opacity, caretPadding);
// Draw Title, Body, and Footer
pt.x += vm.xPadding;
pt.y += vm.yPadding;
// Titles
if (vm.title.length) {
ctx.textAlign = vm._titleAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.titleColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
helpers.each(vm.title, function(title, i) {
ctx.fillText(title, xBase, yBase);
yBase += vm.titleFontSize + vm.titleSpacing; // Line Height and spacing
if (i + 1 == vm.title.length) {
yBase += vm.titleMarginBottom - vm.titleSpacing; // If Last, add margin, remove spacing
}
}, this);
}
this.drawTitle(pt, vm, ctx, opacity);
// Body
ctx.textAlign = vm._bodyAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
// Before Body
helpers.each(vm.beforeBody, function(beforeBody, i) {
ctx.fillText(vm.beforeBody, xBase, yBase);
yBase += vm.bodyFontSize + vm.bodySpacing;
});
helpers.each(vm.body, function(body, i) {
// Draw Legend-like boxes if needed
if (this._options.tooltips.mode != 'single') {
// Fill a white rect so that colours merge nicely if the opacity is < 1
ctx.fillStyle = helpers.color('#FFFFFF').alpha(opacity).rgbaString();
ctx.fillRect(xBase, yBase, vm.bodyFontSize, vm.bodyFontSize);
// Border
ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString();
ctx.strokeRect(xBase, yBase, vm.bodyFontSize, vm.bodyFontSize);
// Inner square
ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString();
ctx.fillRect(xBase + 1, yBase + 1, vm.bodyFontSize - 2, vm.bodyFontSize - 2);
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbaString(); // Return fill style for text
}
// Body Line
ctx.fillText(body, xBase + (this._options.tooltips.mode != 'single' ? (vm.bodyFontSize + 2) : 0), yBase);
yBase += vm.bodyFontSize + vm.bodySpacing;
}, this);
// After Body
helpers.each(vm.afterBody, function(afterBody, i) {
ctx.fillText(vm.afterBody, xBase, yBase);
yBase += vm.bodyFontSize;
});
yBase -= vm.bodySpacing; // Remove last body spacing
this.drawBody(pt, vm, ctx, opacity);
// Footer
if (vm.footer.length) {
yBase += vm.footerMarginTop;
ctx.textAlign = vm._footerAlign;
ctx.textBaseline = "top";
ctx.fillStyle = helpers.color(vm.footerColor).alpha(opacity).rgbString();
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(vm.footer, function(footer, i) {
ctx.fillText(footer, xBase, yBase);
yBase += vm.footerFontSize + vm.footerSpacing;
}, this);
}
this.drawFooter(pt, vm, ctx, opacity);
}
},
}
});
}).call(this);
+5 -3
Ver Arquivo
@@ -37,6 +37,8 @@
skipHandler.call(this, previousPoint, point, nextPoint);
} else if (previousPoint._view.skip) {
previousSkipHandler.call(this, previousPoint, point, nextPoint);
} else if (point._view.tension === 0) {
ctx.lineTo(point._view.x, point._view.y);
} else {
// Line between points
ctx.bezierCurveTo(
@@ -110,7 +112,7 @@
ctx.lineTo(previousPoint._view.x, this._view.scaleZero);
ctx.moveTo(nextPoint._view.x, this._view.scaleZero);
}
}, function(previousPoint, point, nextPoint) {
}, function(previousPoint, point) {
// If we skipped the last point, draw a line to ourselves so that the fill is nice
ctx.lineTo(point._view.x, point._view.y);
});
@@ -154,14 +156,14 @@
} else {
this.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) {
ctx.moveTo(nextPoint._view.x, nextPoint._view.y);
}, function(previousPoint, point, nextPoint) {
}, function(previousPoint, point) {
// If we skipped the last point, move up to our point preventing a line from being drawn
ctx.moveTo(point._view.x, point._view.y);
});
}
}, this);
if (this._loop) {
if (this._loop && this._children.length > 0) {
loopBackToStart();
}
+4 -11
Ver Arquivo
@@ -78,17 +78,10 @@
},
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
};
}
return {
x: vm.x,
y: vm.y
};
},
});
+43 -33
Ver Arquivo
@@ -36,8 +36,7 @@
};
var LinearScale = Chart.Scale.extend({
buildTicks: function() {
determineDataLimits: function() {
// First Calculate the range
this.min = null;
this.max = null;
@@ -114,6 +113,41 @@
}, this);
}
// 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.ticks.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;
}
}
if (this.options.ticks.min !== undefined) {
this.min = this.options.ticks.min;
} else if (this.options.ticks.suggestedMin !== undefined) {
this.min = Math.min(this.min, this.options.ticks.suggestedMin);
}
if (this.options.ticks.max !== undefined) {
this.max = this.options.ticks.max;
} else if (this.options.ticks.suggestedMax !== undefined) {
this.max = Math.max(this.max, this.options.ticks.suggestedMax);
}
if (this.min === this.max) {
this.min--;
this.max++;
}
},
buildTicks: function() {
// Then calulate the ticks
this.ticks = [];
@@ -140,35 +174,6 @@
// "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.ticks.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;
}
}
if (this.options.ticks.suggestedMin) {
this.min = Math.min(this.min, this.options.ticks.suggestedMin);
}
if (this.options.ticks.suggestedMax) {
this.max = Math.max(this.max, this.options.ticks.suggestedMax);
}
if (this.min === this.max) {
this.min--;
this.max++;
}
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;
@@ -177,9 +182,11 @@
var numSpaces = Math.ceil((niceMax - niceMin) / spacing);
// Put the values into the ticks array
for (var j = 0; j <= numSpaces; ++j) {
this.ticks.push(this.options.ticks.min !== undefined ? this.options.ticks.min : niceMin);
for (var j = 1; j < numSpaces; ++j) {
this.ticks.push(niceMin + (j * spacing));
}
this.ticks.push(this.options.ticks.max !== undefined ? this.options.ticks.max : niceMax);
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
@@ -190,6 +197,7 @@
// range of the scale
this.max = helpers.max(this.ticks);
this.min = helpers.min(this.ticks);
this.ticksAsNumbers = this.ticks.slice();
if (this.options.ticks.reverse) {
this.ticks.reverse();
@@ -217,7 +225,6 @@
var range = this.end - this.start;
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
pixel = this.left + (innerWidth / range * (rightValue - this.start));
return Math.round(pixel + this.paddingLeft);
@@ -227,6 +234,9 @@
return Math.round(pixel);
}
},
getPixelForTick: function(index, includeOffset) {
return this.getPixelForValue(this.ticksAsNumbers[index], null, null, includeOffset);
},
});
Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig);
+23 -14
Ver Arquivo
@@ -10,10 +10,10 @@
// label settings
ticks: {
callback: function(value) {
callback: function(value, index, arr) {
var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));
if (remain === 1 || remain === 2 || remain === 5) {
if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) {
return value.toExponential();
} else {
return '';
@@ -23,10 +23,8 @@
};
var LogarithmicScale = Chart.Scale.extend({
buildTicks: function() {
// Calculate Range (we may break this out into it's own lifecycle function)
determineDataLimits: function() {
// Calculate Range
this.min = null;
this.max = null;
@@ -90,6 +88,9 @@
}, this);
}
this.min = this.options.ticks.min !== undefined ? this.options.ticks.min : this.min;
this.max = this.options.ticks.max !== undefined ? this.options.ticks.max : this.max;
if (this.min === this.max) {
if (this.min !== 0 && this.min !== null) {
this.min = Math.pow(10, Math.floor(helpers.log10(this.min)) - 1);
@@ -99,8 +100,8 @@
this.max = 10;
}
}
},
buildTicks: function() {
// 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.tickValues = [];
@@ -110,16 +111,24 @@
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
// the graph
var minExponent = Math.floor(helpers.log10(this.min));
var maxExponent = Math.ceil(helpers.log10(this.max));
var tickVal = this.options.ticks.min !== undefined ? this.options.ticks.min : Math.pow(10, Math.floor(helpers.log10(this.min)));
for (var exponent = minExponent; exponent < maxExponent; ++exponent) {
for (var i = 1; i < 10; ++i) {
this.tickValues.push(i * Math.pow(10, exponent));
while (tickVal < this.max) {
this.tickValues.push(tickVal);
var exp = Math.floor(helpers.log10(tickVal));
var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
if (significand === 10) {
significand = 1;
++exp;
}
tickVal = significand * Math.pow(10, exp);
}
this.tickValues.push(1.0 * Math.pow(10, maxExponent));
var lastTick = this.options.ticks.max !== undefined ? this.options.ticks.max : tickVal;
this.tickValues.push(lastTick);
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
+18 -15
Ver Arquivo
@@ -63,7 +63,7 @@
var minSize = helpers.min([this.height, this.width]);
this.drawingArea = (this.options.display) ? (minSize / 2) - (this.options.ticks.fontSize / 2 + this.options.ticks.backdropPaddingY) : (minSize / 2);
},
buildTicks: function() {
determineDataLimits: function() {
this.min = null;
this.max = null;
@@ -95,20 +95,6 @@
this.max++;
}
this.ticks = [];
// 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(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
Math.ceil(this.drawingArea / (1.5 * this.options.ticks.fontSize)));
maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks
// 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
@@ -124,6 +110,23 @@
this.min = 0;
}
}
},
buildTicks: function() {
this.ticks = [];
// 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(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
Math.ceil(this.drawingArea / (1.5 * this.options.ticks.fontSize)));
maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks
// 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.
var niceRange = helpers.niceNum(this.max - this.min, false);
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
+85 -33
Ver Arquivo
@@ -1,7 +1,7 @@
(function() {
(function(moment) {
"use strict";
if (!window.moment) {
if (!moment) {
console.warn('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at http://momentjs.com/');
return;
}
@@ -12,15 +12,34 @@
var time = {
units: [
'millisecond',
'second',
'minute',
'hour',
'day',
'week',
'month',
'quarter',
'year',
{
name: 'millisecond',
steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
}, {
name: 'second',
steps: [1, 2, 5, 10, 30]
}, {
name: 'minute',
steps: [1, 2, 5, 10, 30]
}, {
name: 'hour',
steps: [1, 2, 3, 6, 12]
}, {
name: 'day',
steps: [1, 2, 5]
}, {
name: 'week',
maxStep: 4
}, {
name: 'month',
maxStep: 3
}, {
name: 'quarter',
maxStep: 4,
}, {
name: 'year',
maxStep: false
},
],
};
@@ -52,8 +71,9 @@
getLabelMoment: function(datasetIndex, index) {
return this.labelMoments[datasetIndex][index];
},
buildLabelMoments: function() {
determineDataLimits: function() {
this.labelMoments = [];
// Only parse these once. If the dataset does not have data as x,y pairs, we will use
// these
var scaleLabelMoments = [];
@@ -106,16 +126,13 @@
}, this);
// We will modify these, so clone for later
this.firstTick = this.firstTick.clone();
this.lastTick = this.lastTick.clone();
this.firstTick = (this.firstTick || moment()).clone();
this.lastTick = (this.lastTick || moment()).clone();
},
buildTicks: function(index) {
this.ticks = [];
this.labelMoments = [];
this.buildLabelMoments();
this.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step
// Set unit override if applicable
if (this.options.time.unit) {
@@ -125,7 +142,7 @@
} else {
// Determine the smallest needed unit of the time
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
var labelCapacity = innerWidth / this.options.ticks.fontSize + 4;
var labelCapacity = innerWidth / (this.options.ticks.fontSize + 10);
var buffer = this.options.time.round ? 0 : 2;
// Start as small as possible
@@ -133,16 +150,39 @@
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit, true) + buffer);
this.displayFormat = this.options.time.displayFormats[this.tickUnit];
// Work our way up to comfort
helpers.each(time.units, function(format) {
if (this.tickRange <= labelCapacity) {
return;
}
this.tickUnit = format;
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit) + buffer);
this.displayFormat = this.options.time.displayFormats[format];
var unitDefinitionIndex = 0;
var unitDefinition = time.units[unitDefinitionIndex];
}, this);
// While we aren't ideal and we don't have units left
while (unitDefinitionIndex < time.units.length) {
// Can we scale this unit. If `false` we can scale infinitely
//var canScaleUnit = ;
this.unitScale = 1;
if (helpers.isArray(unitDefinition.steps) && Math.ceil(this.tickRange / labelCapacity) < helpers.max(unitDefinition.steps)) {
// Use one of the prefedined steps
for (var idx = 0; idx < unitDefinition.steps.length; ++idx) {
if (unitDefinition.steps[idx] > Math.ceil(this.tickRange / labelCapacity)) {
this.unitScale = unitDefinition.steps[idx];
break;
}
}
break;
} else if ((unitDefinition.maxStep === false) || (Math.ceil(this.tickRange / labelCapacity) < unitDefinition.maxStep)) {
// We have a max step. Scale this unit
this.unitScale = Math.ceil(this.tickRange / labelCapacity);
break;
} else {
// Move to the next unit up
++unitDefinitionIndex;
unitDefinition = time.units[unitDefinitionIndex];
this.tickUnit = unitDefinition.name;
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit) + buffer);
this.displayFormat = this.options.time.displayFormats[unitDefinition.name];
}
}
}
this.firstTick.startOf(this.tickUnit);
@@ -162,7 +202,15 @@
// For every unit in between the first and last moment, create a moment and add it to the ticks tick
for (var i = 0; i <= this.tickRange; ++i) {
this.ticks.push(this.firstTick.clone().add(i, this.tickUnit));
if (i % this.unitScale === 0) {
this.ticks.push(this.firstTick.clone().add(i, this.tickUnit));
} else if (i === this.tickRange) {
// Expand out the last one if not an exact multiple
this.tickRange = Math.ceil(this.tickRange / this.unitScale) * this.unitScale;
this.ticks.push(this.firstTick.clone().add(this.tickRange, this.tickUnit));
this.lastTick = this.ticks[this.ticks.length - 1].clone();
break;
}
}
},
// Get tooltip label
@@ -173,11 +221,16 @@
label = this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
}
// Format nicely
if (this.options.time.tooltipFormat) {
label = this.parseTime(label).format(this.options.time.tooltipFormat);
}
return label;
},
convertTicksToLabels: function() {
this.ticks = this.ticks.map(function(tick, index, ticks) {
var formattedTick = tick.format(this.options.time.displayFormat ? this.options.time.displayFormat : this.options.time.displayFormats[this.tickUnit]);
var formattedTick = tick.format(this.displayFormat);
if (this.options.ticks.userCallback) {
return this.options.ticks.userCallback(formattedTick, index, ticks);
@@ -199,7 +252,6 @@
return this.left + Math.round(valueOffset);
} else {
//return this.top + (decimal * (this.height / this.ticks.length));
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
var valueHeight = innerHeight / Math.max(this.ticks.length - 1, 1);
var heightOffset = (innerHeight * decimal) + this.paddingTop;
@@ -226,4 +278,4 @@
});
Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig);
}).call(this);
}).call(this, moment);
+7 -34
Ver Arquivo
@@ -52,11 +52,10 @@ describe('Bar controller tests', function() {
var chart = {
data: {
datasets: [{
type: 'line'
}, {
type: 'bar'
bar: true
}, {
// no type, defaults to bar
bar: true
}]
},
config: {
@@ -82,13 +81,13 @@ describe('Bar controller tests', function() {
var chart = {
data: {
datasets: [{
bar: true,
}, {
bar: true,
hidden: true
}, {
type: 'line'
}, {
bar: true,
}]
},
config: {
@@ -142,40 +141,14 @@ describe('Bar controller tests', function() {
expect(chart.data.datasets[1].metaData[3] instanceof Chart.elements.Rectangle).toBe(true);
});
it('should remove elements', function() {
var chart = {
data: {
datasets: [{}, {
data: [10, 15, 0, -4]
}]
},
config: {
type: 'bar'
},
options: {
scales: {
xAxes: [{
id: 'firstXScaleID'
}],
yAxes: [{
id: 'firstYScaleID'
}]
}
}
};
var controller = new Chart.controllers.bar(chart, 1);
controller.removeElement(1);
expect(chart.data.datasets[1].metaData.length).toBe(3);
});
it('should update elements', function() {
var data = {
datasets: [{
data: [1, 2],
label: 'dataset1',
xAxisID: 'firstXScaleID',
yAxisID: 'firstYScaleID'
yAxisID: 'firstYScaleID',
bar: true
}, {
data: [10, 15, 0, -4],
label: 'dataset2'
+28 -35
Ver Arquivo
@@ -41,30 +41,13 @@ describe('Doughnut controller tests', function() {
expect(chart.data.datasets[0].metaData[3] instanceof Chart.elements.Arc).toBe(true);
});
it ('Should remove elements', function() {
var chart = {
data: {
datasets: [{
data: [10, 15, 0, 4]
}]
},
config: {
type: 'doughnut'
},
options: {
}
};
var controller = new Chart.controllers.doughnut(chart, 0);
controller.removeElement(1);
expect(chart.data.datasets[0].metaData.length).toBe(3);
});
it ('Should reset and update elements', function() {
var chart = {
chart: {
width: 100,
height: 200,
chartArea: {
left: 0,
top: 0,
right: 100,
bottom: 200,
},
data: {
datasets: [{
@@ -103,36 +86,40 @@ describe('Doughnut controller tests', function() {
x: 50,
y: 100,
startAngle: Math.PI * -0.5,
endAngle: Math.PI * -0.5,
circumference: 2.166614539857563,
outerRadius: 49,
innerRadius: 36.75,
innerRadius: 36.75
});
expect(chart.data.datasets[1].metaData[1]._model).toEqual({
x: 50,
y: 100,
startAngle: Math.PI * -0.5,
endAngle: Math.PI * -0.5,
circumference: 3.2499218097863447,
outerRadius: 49,
innerRadius: 36.75,
innerRadius: 36.75
});
expect(chart.data.datasets[1].metaData[2]._model).toEqual({
x: 50,
y: 100,
startAngle: Math.PI * -0.5,
endAngle: Math.PI * -0.5,
circumference: 0,
outerRadius: 49,
innerRadius: 36.75,
innerRadius: 36.75
});
expect(chart.data.datasets[1].metaData[3]._model).toEqual({
x: 50,
y: 100,
startAngle: Math.PI * -0.5,
endAngle: Math.PI * -0.5,
circumference: 0.8666458159430251,
outerRadius: 49,
innerRadius: 36.75,
innerRadius: 36.75
});
controller.update();
@@ -228,9 +215,11 @@ describe('Doughnut controller tests', function() {
it ('should draw all arcs', function() {
var chart = {
chart: {
width: 100,
height: 200,
chartArea: {
left: 0,
top: 0,
right: 100,
bottom: 200,
},
data: {
datasets: [{
@@ -275,9 +264,11 @@ describe('Doughnut controller tests', function() {
it ('should set the hover style of an arc', function() {
var chart = {
chart: {
width: 100,
height: 200,
chartArea: {
left: 0,
top: 0,
right: 100,
bottom: 200,
},
data: {
datasets: [{
@@ -354,9 +345,11 @@ describe('Doughnut controller tests', function() {
it ('should unset the hover style of an arc', function() {
var chart = {
chart: {
width: 100,
height: 200,
chartArea: {
left: 0,
top: 0,
right: 100,
bottom: 200,
},
data: {
datasets: [{
+18 -45
Ver Arquivo
@@ -76,33 +76,6 @@ describe('Line controller tests', function() {
expect(chart.data.datasets[0].metaDataset instanceof Chart.elements.Line).toBe(true); // 1 line element
});
it('should remove elements', function() {
var chart = {
data: {
datasets: [{
data: [10, 15, 0, -4]
}]
},
config: {
type: 'line'
},
options: {
scales: {
xAxes: [{
id: 'firstXScaleID'
}],
yAxes: [{
id: 'firstYScaleID'
}]
}
}
};
var controller = new Chart.controllers.line(chart, 0);
controller.removeElement(0);
expect(chart.data.datasets[0].metaData.length).toBe(3);
});
it('should draw all elements', function() {
var chart = {
data: {
@@ -342,7 +315,7 @@ describe('Line controller tests', function() {
});
// Use dataset level styles for lines & points
chart.data.datasets[0].tension = 0.2;
chart.data.datasets[0].tension = 0;
chart.data.datasets[0].backgroundColor = 'rgb(98, 98, 98)';
chart.data.datasets[0].borderColor = 'rgb(8, 8, 8)';
chart.data.datasets[0].borderWidth = 0.55;
@@ -370,7 +343,7 @@ describe('Line controller tests', function() {
borderJoinStyle: 'miter',
borderWidth: 0.55,
fill: false,
tension: 0.2,
tension: 0,
scaleTop: 0,
scaleBottom: 200,
@@ -384,7 +357,7 @@ describe('Line controller tests', function() {
hitRadius: 3.3,
radius: 22,
skip: false,
tension: 0.2,
tension: 0,
// Point
x: 81,
@@ -393,8 +366,8 @@ describe('Line controller tests', function() {
// Control points
controlPointPreviousX: 81,
controlPointPreviousY: 62,
controlPointNextX: 91,
controlPointNextY: 52.6,
controlPointNextX: 81,
controlPointNextY: 62,
});
expect(chart.data.datasets[0].metaData[1]._model).toEqual({
@@ -404,17 +377,17 @@ describe('Line controller tests', function() {
hitRadius: 3.3,
radius: 22,
skip: false,
tension: 0.2,
tension: 0,
// Point
x: 131,
y: 15,
// Control points
controlPointPreviousX: 124.65778768378175,
controlPointPreviousY: 9.097346953222619,
controlPointNextX: 144.85778768378177,
controlPointNextY: 27.897346953222623,
controlPointPreviousX: 131,
controlPointPreviousY: 15,
controlPointNextX: 131,
controlPointNextY: 15,
});
expect(chart.data.datasets[0].metaData[2]._model).toEqual({
@@ -424,17 +397,17 @@ describe('Line controller tests', function() {
hitRadius: 3.3,
radius: 22,
skip: false,
tension: 0.2,
tension: 0,
// Point
x: 182,
y: 156,
// Control points
controlPointPreviousX: 167.76304506745115,
controlPointPreviousY: 130.76816898092827,
controlPointNextX: 187.96304506745116,
controlPointNextY: 166.56816898092828,
controlPointPreviousX: 182,
controlPointPreviousY: 156,
controlPointNextX: 182,
controlPointNextY: 156,
});
expect(chart.data.datasets[0].metaData[3]._model).toEqual({
@@ -444,15 +417,15 @@ describe('Line controller tests', function() {
hitRadius: 3.3,
radius: 22,
skip: false,
tension: 0.2,
tension: 0,
// Point
x: 232,
y: 194,
// Control points
controlPointPreviousX: 222,
controlPointPreviousY: 186.4,
controlPointPreviousX: 232,
controlPointPreviousY: 194,
controlPointNextX: 232,
controlPointNextY: 194,
});
+4 -2
Ver Arquivo
@@ -239,12 +239,13 @@ describe('Core helper tests', function() {
fontSize: 12,
fontStyle: "normal",
maxRotation: 90,
minRotation: 20,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: merged.scales.yAxes[1].ticks.callback, // make it nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
},
type: 'linear'
}, {
@@ -276,12 +277,13 @@ describe('Core helper tests', function() {
fontSize: 12,
fontStyle: "normal",
maxRotation: 90,
minRotation: 20,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: merged.scales.yAxes[2].ticks.callback, // make it nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
},
type: 'linear'
}]
@@ -1,8 +1,8 @@
// Tests of the scale service
describe('Test the scale service', function() {
describe('Test the layout service', function() {
it('should fit a simple chart with 2 scales', function() {
var chartInstance = {
scales: [],
boxes: [],
};
var xScaleID = 'xScale';
@@ -38,12 +38,12 @@ describe('Test the scale service', function() {
id: yScaleID
});
chartInstance.scales.push(xScale);
chartInstance.scales.push(yScale);
chartInstance.boxes.push(xScale);
chartInstance.boxes.push(yScale);
var canvasWidth = 250;
var canvasHeight = 150;
Chart.scaleService.update(chartInstance, canvasWidth, canvasHeight);
Chart.layoutService.update(chartInstance, canvasWidth, canvasHeight);
expect(chartInstance.chartArea).toEqual({
left: 55,
@@ -68,7 +68,7 @@ describe('Test the scale service', function() {
it('should fit scales that are in the top and right positions', function() {
var chartInstance = {
scales: [],
boxes: [],
};
var xScaleID = 'xScale';
@@ -106,12 +106,12 @@ describe('Test the scale service', function() {
id: yScaleID
});
chartInstance.scales.push(xScale);
chartInstance.scales.push(yScale);
chartInstance.boxes.push(xScale);
chartInstance.boxes.push(yScale);
var canvasWidth = 250;
var canvasHeight = 150;
Chart.scaleService.update(chartInstance, canvasWidth, canvasHeight);
Chart.layoutService.update(chartInstance, canvasWidth, canvasHeight);
expect(chartInstance.chartArea).toEqual({
left: 5,
@@ -136,7 +136,7 @@ describe('Test the scale service', function() {
it('should fit multiple axes in the same position', function() {
var chartInstance = {
scales: [],
boxes: [],
};
var xScaleID = 'xScale';
@@ -184,13 +184,13 @@ describe('Test the scale service', function() {
id: yScaleID2
});
chartInstance.scales.push(xScale);
chartInstance.scales.push(yScale1);
chartInstance.scales.push(yScale2);
chartInstance.boxes.push(xScale);
chartInstance.boxes.push(yScale1);
chartInstance.boxes.push(yScale2);
var canvasWidth = 250;
var canvasHeight = 150;
Chart.scaleService.update(chartInstance, canvasWidth, canvasHeight);
Chart.layoutService.update(chartInstance, canvasWidth, canvasHeight);
expect(chartInstance.chartArea).toEqual({
left: 115,
@@ -223,7 +223,7 @@ describe('Test the scale service', function() {
// due to the lack of label rotation
it('should fit scales that overlap the chart area', function() {
var chartInstance = {
scales: [],
boxes: [],
};
var scaleID = 'scaleID';
@@ -250,11 +250,11 @@ describe('Test the scale service', function() {
id: scaleID
});
chartInstance.scales.push(scale);
chartInstance.boxes.push(scale);
var canvasWidth = 300;
var canvasHeight = 350;
Chart.scaleService.update(chartInstance, canvasWidth, canvasHeight);
Chart.layoutService.update(chartInstance, canvasWidth, canvasHeight);
expect(chartInstance.chartArea).toEqual({
left: 5,
@@ -270,4 +270,91 @@ describe('Test the scale service', function() {
expect(scale.width).toBe(290);
expect(scale.height).toBe(340)
});
it ('should fix a full width box correctly', function() {
var chartInstance = {
boxes: [],
};
var xScaleID1= 'xScale1';
var xScaleID2 = 'xScale2';
var yScaleID = 'yScale2';
var mockData = {
datasets: [{
xAxisID: xScaleID1,
data: [10, 5, 0, 25, 78, -10]
}, {
xAxisID: xScaleID2,
data: [-19, -20, 0, -99, -50, 0]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5']
};
var mockContext = window.createMockContext();
var xScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
var XConstructor = Chart.scaleService.getScaleConstructor('category');
var xScale1 = new XConstructor({
ctx: mockContext,
options: xScaleConfig,
chart: {
data: mockData
},
id: xScaleID1
});
var xScale2 = new XConstructor({
ctx: mockContext,
options: Chart.helpers.extend(Chart.helpers.clone(xScaleConfig), {
position: 'top',
fullWidth: true
}),
chart: {
data: mockData,
},
id: xScaleID2
});
var yScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
var YConstructor = Chart.scaleService.getScaleConstructor('linear');
var yScale = new YConstructor({
ctx: mockContext,
options: yScaleConfig,
chart: {
data: mockData
},
id: yScaleID
});
chartInstance.boxes.push(xScale1);
chartInstance.boxes.push(xScale2);
chartInstance.boxes.push(yScale);
var canvasWidth = 250;
var canvasHeight = 150;
Chart.layoutService.update(chartInstance, canvasWidth, canvasHeight);
expect(chartInstance.chartArea).toEqual({
left: 45,
right: 245,
top: 45,
bottom: 105,
});
// Are xScales at the right spot
expect(xScale1.left).toBe(45);
expect(xScale1.right).toBe(245);
expect(xScale1.top).toBe(105);
expect(xScale1.bottom).toBe(145);
expect(xScale2.left).toBe(5);
expect(xScale2.right).toBe(245);
expect(xScale2.top).toBe(5);
expect(xScale2.bottom).toBe(45);
// Is yScale at the right spot
expect(yScale.left).toBe(5);
expect(yScale.right).toBe(45);
expect(yScale.top).toBe(45);
expect(yScale.bottom).toBe(105);
});
});
+1 -1
Ver Arquivo
@@ -109,7 +109,7 @@ describe('Rectangle element tests', function() {
expect(rectangle.tooltipPosition()).toEqual({
x: 10,
y: 0,
y: 15,
});
// Test when the y is below the base (negative bar)
+2 -1
Ver Arquivo
@@ -38,12 +38,13 @@ describe('Category scale tests', function() {
fontSize: 12,
fontStyle: "normal",
maxRotation: 90,
minRotation: 20,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
}
});
+123 -73
Ver Arquivo
@@ -37,12 +37,13 @@ describe('Linear Scale', function() {
fontSize: 12,
fontStyle: "normal",
maxRotation: 90,
minRotation: 20,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this work nicer, then check below
autoSkip: true,
autoSkipPadding: 20
}
});
@@ -83,7 +84,7 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.buildTicks();
scale.determineDataLimits();
expect(scale.min).toBe(-100);
expect(scale.max).toBe(150);
});
@@ -122,7 +123,7 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.buildTicks();
scale.determineDataLimits();
expect(scale.min).toBe(-100);
expect(scale.max).toBe(150);
});
@@ -162,6 +163,7 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-100);
expect(scale.max).toBe(80);
@@ -205,6 +207,7 @@ describe('Linear Scale', function() {
verticalScale.width = 50;
verticalScale.height = 400;
verticalScale.determineDataLimits();
verticalScale.buildTicks();
expect(verticalScale.min).toBe(0);
expect(verticalScale.max).toBe(100);
@@ -224,6 +227,7 @@ describe('Linear Scale', function() {
horizontalScale.width = 400;
horizontalScale.height = 50;
horizontalScale.determineDataLimits();
horizontalScale.buildTicks();
expect(horizontalScale.min).toBe(-20);
expect(horizontalScale.max).toBe(100);
@@ -268,6 +272,7 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-150);
expect(scale.max).toBe(200);
@@ -310,6 +315,7 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-150);
expect(scale.max).toBe(200);
@@ -337,6 +343,7 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-1);
expect(scale.max).toBe(1);
@@ -370,11 +377,48 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-10);
expect(scale.max).toBe(10);
});
it('Should use the min and max options', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [{
yAxisID: scaleID,
data: [1, 1, 1, 2, 1, 0]
}]
};
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
config.ticks.min = -1010;
config.ticks.max = 1010;
var Constructor = Chart.scaleService.getScaleConstructor('linear');
var scale = new Constructor({
ctx: {},
options: config,
chart: {
data: mockData
},
id: scaleID
});
// Set arbitrary width and height for now
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-1010);
expect(scale.max).toBe(1010);
expect(scale.ticks[0]).toBe(1010);
expect(scale.ticks[scale.ticks.length - 1]).toBe(-1010);
});
it('should forcibly include 0 in the range if the beginAtZero option is used', function() {
var scaleID = 'myScale';
@@ -402,18 +446,22 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual([50, 45, 40, 35, 30, 25, 20]);
config.ticks.beginAtZero = true;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual([50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0]);
mockData.datasets[0].data = [-20, -30, -40, -50];
scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual([0, -5, -10, -15, -20, -25, -30, -35, -40, -45, -50]);
config.ticks.beginAtZero = false;
scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual([-20, -25, -30, -35, -40, -45, -50]);
});
@@ -443,6 +491,7 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
// Counts down because the lines are drawn top to bottom
@@ -477,6 +526,7 @@ describe('Linear Scale', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
// Reverse mode makes this count up
@@ -1093,16 +1143,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 30.2]
"args": [25, 30.5]
}, {
"name": "lineTo",
"args": [30, 30.2]
"args": [30, 30.5]
}, {
"name": "moveTo",
"args": [30, 30.2]
"args": [30, 30.5]
}, {
"name": "lineTo",
"args": [130, 30.2]
"args": [130, 30.5]
}, {
"name": "stroke",
"args": []
@@ -1111,7 +1161,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 29.7]
"args": [20, 30]
}, {
"name": "rotate",
"args": [-0]
@@ -1126,16 +1176,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 59.9]
"args": [25, 59.5]
}, {
"name": "lineTo",
"args": [30, 59.9]
"args": [30, 59.5]
}, {
"name": "moveTo",
"args": [30, 59.9]
"args": [30, 59.5]
}, {
"name": "lineTo",
"args": [130, 59.9]
"args": [130, 59.5]
}, {
"name": "stroke",
"args": []
@@ -1144,7 +1194,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 59.4]
"args": [20, 59]
}, {
"name": "rotate",
"args": [-0]
@@ -1159,16 +1209,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 89.6]
"args": [25, 89.5]
}, {
"name": "lineTo",
"args": [30, 89.6]
"args": [30, 89.5]
}, {
"name": "moveTo",
"args": [30, 89.6]
"args": [30, 89.5]
}, {
"name": "lineTo",
"args": [130, 89.6]
"args": [130, 89.5]
}, {
"name": "stroke",
"args": []
@@ -1177,7 +1227,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 89.1]
"args": [20, 89]
}, {
"name": "rotate",
"args": [-0]
@@ -1192,16 +1242,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 119.3]
"args": [25, 119.5]
}, {
"name": "lineTo",
"args": [30, 119.3]
"args": [30, 119.5]
}, {
"name": "moveTo",
"args": [30, 119.3]
"args": [30, 119.5]
}, {
"name": "lineTo",
"args": [130, 119.3]
"args": [130, 119.5]
}, {
"name": "stroke",
"args": []
@@ -1210,7 +1260,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 118.8]
"args": [20, 119]
}, {
"name": "rotate",
"args": [-0]
@@ -1231,16 +1281,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 149]
"args": [25, 149.5]
}, {
"name": "lineTo",
"args": [30, 149]
"args": [30, 149.5]
}, {
"name": "moveTo",
"args": [30, 149]
"args": [30, 149.5]
}, {
"name": "lineTo",
"args": [130, 149]
"args": [130, 149.5]
}, {
"name": "stroke",
"args": []
@@ -1249,7 +1299,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 148.5]
"args": [20, 149]
}, {
"name": "rotate",
"args": [-0]
@@ -1270,16 +1320,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 178.7]
"args": [25, 178.5]
}, {
"name": "lineTo",
"args": [30, 178.7]
"args": [30, 178.5]
}, {
"name": "moveTo",
"args": [30, 178.7]
"args": [30, 178.5]
}, {
"name": "lineTo",
"args": [130, 178.7]
"args": [130, 178.5]
}, {
"name": "stroke",
"args": []
@@ -1288,7 +1338,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 178.2]
"args": [20, 178]
}, {
"name": "rotate",
"args": [-0]
@@ -1303,16 +1353,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 208.4]
"args": [25, 208.5]
}, {
"name": "lineTo",
"args": [30, 208.4]
"args": [30, 208.5]
}, {
"name": "moveTo",
"args": [30, 208.4]
"args": [30, 208.5]
}, {
"name": "lineTo",
"args": [130, 208.4]
"args": [130, 208.5]
}, {
"name": "stroke",
"args": []
@@ -1321,7 +1371,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 207.9]
"args": [20, 208]
}, {
"name": "rotate",
"args": [-0]
@@ -1336,16 +1386,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 238.1]
"args": [25, 238.5]
}, {
"name": "lineTo",
"args": [30, 238.1]
"args": [30, 238.5]
}, {
"name": "moveTo",
"args": [30, 238.1]
"args": [30, 238.5]
}, {
"name": "lineTo",
"args": [130, 238.1]
"args": [130, 238.5]
}, {
"name": "stroke",
"args": []
@@ -1354,7 +1404,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 237.6]
"args": [20, 238]
}, {
"name": "rotate",
"args": [-0]
@@ -1369,16 +1419,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 267.8]
"args": [25, 267.5]
}, {
"name": "lineTo",
"args": [30, 267.8]
"args": [30, 267.5]
}, {
"name": "moveTo",
"args": [30, 267.8]
"args": [30, 267.5]
}, {
"name": "lineTo",
"args": [130, 267.8]
"args": [130, 267.5]
}, {
"name": "stroke",
"args": []
@@ -1387,7 +1437,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 267.3]
"args": [20, 267]
}, {
"name": "rotate",
"args": [-0]
@@ -1616,16 +1666,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 30.2]
"args": [25, 30.5]
}, {
"name": "lineTo",
"args": [30, 30.2]
"args": [30, 30.5]
}, {
"name": "moveTo",
"args": [30, 30.2]
"args": [30, 30.5]
}, {
"name": "lineTo",
"args": [130, 30.2]
"args": [130, 30.5]
}, {
"name": "stroke",
"args": []
@@ -1634,7 +1684,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 29.7]
"args": [20, 30]
}, {
"name": "rotate",
"args": [-0]
@@ -1649,16 +1699,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 89.6]
"args": [25, 89.5]
}, {
"name": "lineTo",
"args": [30, 89.6]
"args": [30, 89.5]
}, {
"name": "moveTo",
"args": [30, 89.6]
"args": [30, 89.5]
}, {
"name": "lineTo",
"args": [130, 89.6]
"args": [130, 89.5]
}, {
"name": "stroke",
"args": []
@@ -1667,7 +1717,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 89.1]
"args": [20, 89]
}, {
"name": "rotate",
"args": [-0]
@@ -1688,16 +1738,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 149]
"args": [25, 149.5]
}, {
"name": "lineTo",
"args": [30, 149]
"args": [30, 149.5]
}, {
"name": "moveTo",
"args": [30, 149]
"args": [30, 149.5]
}, {
"name": "lineTo",
"args": [130, 149]
"args": [130, 149.5]
}, {
"name": "stroke",
"args": []
@@ -1706,7 +1756,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 148.5]
"args": [20, 149]
}, {
"name": "rotate",
"args": [-0]
@@ -1727,16 +1777,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 208.4]
"args": [25, 208.5]
}, {
"name": "lineTo",
"args": [30, 208.4]
"args": [30, 208.5]
}, {
"name": "moveTo",
"args": [30, 208.4]
"args": [30, 208.5]
}, {
"name": "lineTo",
"args": [130, 208.4]
"args": [130, 208.5]
}, {
"name": "stroke",
"args": []
@@ -1745,7 +1795,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 207.9]
"args": [20, 208]
}, {
"name": "rotate",
"args": [-0]
@@ -1760,16 +1810,16 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "moveTo",
"args": [25, 267.8]
"args": [25, 267.5]
}, {
"name": "lineTo",
"args": [30, 267.8]
"args": [30, 267.5]
}, {
"name": "moveTo",
"args": [30, 267.8]
"args": [30, 267.5]
}, {
"name": "lineTo",
"args": [130, 267.8]
"args": [130, 267.5]
}, {
"name": "stroke",
"args": []
@@ -1778,7 +1828,7 @@ describe('Linear Scale', function() {
"args": []
}, {
"name": "translate",
"args": [20, 267.3]
"args": [20, 267]
}, {
"name": "rotate",
"args": [-0]
+55 -17
Ver Arquivo
@@ -36,12 +36,13 @@ describe('Logarithmic Scale tests', function() {
fontSize: 12,
fontStyle: "normal",
maxRotation: 90,
minRotation: 20,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
},
});
@@ -82,7 +83,7 @@ describe('Logarithmic Scale tests', function() {
scale.update(400, 400);
expect(scale.min).toBe(1);
expect(scale.max).toBe(10000);
expect(scale.max).toBe(5000);
});
it('Should correctly determine the max & min of string data values', function() {
@@ -118,7 +119,7 @@ describe('Logarithmic Scale tests', function() {
scale.update(400, 400);
expect(scale.min).toBe(1);
expect(scale.max).toBe(10000);
expect(scale.max).toBe(5000);
});
it('Should correctly determine the max & min data values when there are hidden datasets', function() {
@@ -155,7 +156,7 @@ describe('Logarithmic Scale tests', function() {
scale.update(400, 400);
expect(scale.min).toBe(1);
expect(scale.max).toBe(10000);
expect(scale.max).toBe(5000);
});
it('Should correctly determine the max & min for scatter data', function() {
@@ -195,7 +196,7 @@ describe('Logarithmic Scale tests', function() {
verticalScale.update(400, 400);
expect(verticalScale.min).toBe(1);
expect(verticalScale.max).toBe(1000);
expect(verticalScale.max).toBe(200);
var horizontalConfig = Chart.helpers.clone(config);
horizontalConfig.position = 'bottom';
@@ -251,7 +252,7 @@ describe('Logarithmic Scale tests', function() {
scale.update(400, 400);
expect(scale.min).toBe(10);
expect(scale.max).toBe(1000);
expect(scale.max).toBe(200);
});
it('Should correctly determine the min and max data values when stacked mode is turned on ignoring hidden datasets', function() {
@@ -294,7 +295,7 @@ describe('Logarithmic Scale tests', function() {
scale.update(400, 400);
expect(scale.min).toBe(10);
expect(scale.max).toBe(1000);
expect(scale.max).toBe(200);
});
it('Should ensure that the scale has a max and min that are not equal', function() {
@@ -330,6 +331,41 @@ describe('Logarithmic Scale tests', function() {
expect(scale.max).toBe(1);
});
it('Should use the min and max options', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [{
yAxisID: scaleID,
data: [1, 1, 1, 2, 1, 0]
}]
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
config.ticks.min = 10;
config.ticks.max = 1010;
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
var scale = new Constructor({
ctx: mockContext,
options: config,
chart: {
data: mockData
},
id: scaleID
});
scale.update(400, 00);
scale.buildTicks();
expect(scale.min).toBe(10);
expect(scale.max).toBe(1010);
expect(scale.ticks[0]).toBe(1010);
expect(scale.ticks[scale.ticks.length - 1]).toBe(10);
});
it('Should generate tick marks', function() {
var scaleID = 'myScale';
@@ -355,12 +391,13 @@ describe('Logarithmic Scale tests', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual([100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
expect(scale.ticks).toEqual([80, 70, 60, 50, 40, 30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
expect(scale.start).toBe(1);
expect(scale.end).toBe(100);
expect(scale.end).toBe(80);
});
it('Should generate tick marks in the correct order in reversed mode', function() {
@@ -390,11 +427,12 @@ describe('Logarithmic Scale tests', function() {
scale.width = 50;
scale.height = 400;
scale.determineDataLimits();
scale.buildTicks();
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
expect(scale.start).toBe(100);
expect(scale.ticks).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80]);
expect(scale.start).toBe(80);
expect(scale.end).toBe(1);
});
@@ -422,7 +460,7 @@ describe('Logarithmic Scale tests', function() {
scale.update(400, 400);
expect(scale.ticks).toEqual(['1e+2', '', '', '', '', '5e+1', '', '', '2e+1', '1e+1', '', '', '', '', '5e+0', '', '', '2e+0', '1e+0']);
expect(scale.ticks).toEqual(['8e+1', '', '', '5e+1', '', '', '2e+1', '1e+1', '', '', '', '', '5e+0', '', '', '2e+0', '1e+0']);
});
it('Should build labels using the user supplied callback', function() {
@@ -454,7 +492,7 @@ describe('Logarithmic Scale tests', function() {
scale.update(400, 400);
// Just the index
expect(scale.ticks).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18']);
expect(scale.ticks).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16']);
});
it('Should get the correct pixel value for a point', function() {
@@ -492,9 +530,9 @@ describe('Logarithmic Scale tests', function() {
verticalScale.width = 50;
verticalScale.height = 110;
expect(verticalScale.getPixelForValue(100, 0, 0)).toBe(5); // top + paddingTop
expect(verticalScale.getPixelForValue(80, 0, 0)).toBe(5); // top + paddingTop
expect(verticalScale.getPixelForValue(1, 0, 0)).toBe(105); // bottom - paddingBottom
expect(verticalScale.getPixelForValue(10, 0, 0)).toBe(55); // halfway
expect(verticalScale.getPixelForValue(10, 0, 0)).toBeCloseTo(52.4, 1e-4); // halfway
var horizontalConfig = Chart.helpers.clone(config);
horizontalConfig.position = 'bottom';
@@ -519,8 +557,8 @@ describe('Logarithmic Scale tests', function() {
horizontalScale.width = 110;
horizontalScale.height = 50;
expect(horizontalScale.getPixelForValue(100, 0, 0)).toBe(105); // right - paddingRight
expect(horizontalScale.getPixelForValue(80, 0, 0)).toBe(105); // right - paddingRight
expect(horizontalScale.getPixelForValue(1, 0, 0)).toBe(5); // left + paddingLeft
expect(horizontalScale.getPixelForValue(10, 0, 0)).toBe(55); // halfway
expect(horizontalScale.getPixelForValue(10, 0, 0)).toBeCloseTo(57.5, 1e-4); // halfway
});
});
+2 -2
Ver Arquivo
@@ -52,14 +52,14 @@ describe('Test the radial linear scale', function() {
fontSize: 12,
fontStyle: "normal",
maxRotation: 90,
minRotation: 20,
mirror: false,
padding: 10,
reverse: false,
showLabelBackdrop: true,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 20
},
});
+32 -3
Ver Arquivo
@@ -41,12 +41,13 @@ describe('Time scale tests', function() {
fontSize: 12,
fontStyle: "normal",
maxRotation: 90,
minRotation: 20,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below,
autoSkip: true,
autoSkipPadding: 20
},
time: {
format: false,
@@ -93,7 +94,35 @@ describe('Time scale tests', function() {
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual(['Jan 1, 2015', 'Jan 2, 2015', 'Jan 3, 2015', 'Jan 4, 2015', 'Jan 5, 2015', 'Jan 6, 2015', 'Jan 7, 2015', 'Jan 8, 2015', 'Jan 9, 2015', 'Jan 10, 2015', 'Jan 11, 2015']);
expect(scale.ticks).toEqual(['Jan 1, 2015', 'Jan 3, 2015', 'Jan 5, 2015', 'Jan 7, 2015', 'Jan 9, 2015', 'Jan 11, 2015']);
});
it('should build ticks using date objects', function() {
// Helper to build date objects
function newDateFromRef(days) {
return moment('01/01/2015 12:00', 'DD/MM/YYYY HH:mm').add(days, 'd').toDate();
}
var scaleID = 'myScale';
var mockData = {
labels: [newDateFromRef(0), newDateFromRef(1), newDateFromRef(2), newDateFromRef(4), newDateFromRef(6), newDateFromRef(7), newDateFromRef(9)], // days
};
var mockContext = window.createMockContext();
var Constructor = Chart.scaleService.getScaleConstructor('time');
var scale = new Constructor({
ctx: mockContext,
options: Chart.scaleService.getScaleDefaults('time'), // use default config for scale
chart: {
data: mockData
},
id: scaleID
});
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual(['Jan 1, 2015', 'Jan 3, 2015', 'Jan 5, 2015', 'Jan 7, 2015', 'Jan 9, 2015', 'Jan 11, 2015', 'Jan 13, 2015']);
});
it('should build ticks using the config unit', function() {