diff --git a/.codeclimate.yml b/.codeclimate.yml index 99e66913c..fcc885c82 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -4,8 +4,6 @@ engines: config: languages: - javascript - exclude_paths: - - "samples/samples.js" eslint: enabled: true channel: "eslint-3" @@ -15,7 +13,13 @@ ratings: paths: - "src/**/*.js" exclude_paths: -- dist/**/* -- node_modules/**/* -- test/**/* -- coverage/**/* +- '.github/' +- 'dist/' +- 'test/' +- 'docs/' +- 'samples/' +- 'scripts/' +- '**.md' +- '**.json' +- 'gulpfile.js' +- 'karma.conf.js' diff --git a/.editorconfig b/.editorconfig index 4e0d877ef..8cea3b4f0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,3 +8,11 @@ end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = false + +[gulpfile.js] +indent_style = space +indent_size = 2 + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.eslintrc b/.eslintrc index 7acbf600a..6a31e4b6e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -44,7 +44,7 @@ rules: accessor-pairs: 2 array-callback-return: 0 block-scoped-var: 0 - complexity: [2, 6] + complexity: [2, 10] consistent-return: 0 curly: [2, all] default-case: 2 @@ -183,7 +183,7 @@ rules: object-curly-spacing: [2, never] object-property-newline: 0 one-var-declaration-per-line: 2 - one-var: 0 + one-var: [2, {initialized: never}] operator-assignment: 0 operator-linebreak: 0 padded-blocks: 0 @@ -197,8 +197,8 @@ rules: space-before-blocks: [2, always] space-before-function-paren: [2, never] space-in-parens: [2, never] - space-infix-ops: 0 - space-unary-ops: 0 + space-infix-ops: 2 + space-unary-ops: [2, {words: true, nonwords: false}] spaced-comment: [2, always] unicode-bom: 0 wrap-regex: 2 diff --git a/.gitignore b/.gitignore index 0853b7efd..53ce8fedb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /dist /docs/index.md /gh-pages +/jsdoc /node_modules .DS_Store .idea diff --git a/.travis.yml b/.travis.yml index 34e8a8b3b..862d8b044 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,12 @@ language: node_js node_js: - - "5.10" + - "6" before_install: - "export CHROME_BIN=/usr/bin/google-chrome" - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" -before_script: - - npm install - script: - gulp build - gulp test --coverage @@ -25,13 +22,8 @@ sudo: required dist: trusty addons: + chrome: stable firefox: latest - apt: - sources: - - google-chrome - packages: - - google-chrome-stable - # IMPORTANT: scripts require GITHUB_AUTH_TOKEN and GITHUB_AUTH_EMAIL environment variables # IMPORTANT: scripts has to be set executables in the Git repository (error 127) diff --git a/README.md b/README.md index d2ed99631..767950b88 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # Chart.js -[![Build Status](https://travis-ci.org/chartjs/Chart.js.svg?branch=master)](https://travis-ci.org/chartjs/Chart.js) [![Code Climate](https://codeclimate.com/github/nnnick/Chart.js/badges/gpa.svg)](https://codeclimate.com/github/nnnick/Chart.js) [![Coverage Status](https://coveralls.io/repos/github/chartjs/Chart.js/badge.svg?branch=master)](https://coveralls.io/github/chartjs/Chart.js?branch=master) - -[![Chart.js on Slack](https://img.shields.io/badge/slack-Chart.js-blue.svg)](https://chart-js-automation.herokuapp.com/) +[![travis](https://img.shields.io/travis/chartjs/Chart.js.svg?style=flat-square&maxAge=60)](https://travis-ci.org/chartjs/Chart.js) [![codeclimate](https://img.shields.io/codeclimate/github/chartjs/Chart.js.svg?style=flat-square&maxAge=600)](https://codeclimate.com/github/chartjs/Chart.js) [![coveralls](https://img.shields.io/coveralls/chartjs/Chart.js.svg?style=flat-square&maxAge=600)](https://coveralls.io/github/chartjs/Chart.js?branch=master) [![slack](https://img.shields.io/badge/slack-Chart.js-blue.svg?style=flat-square&maxAge=3600)](https://chart-js-automation.herokuapp.com/) *Simple HTML5 Charts using the canvas element* [chartjs.org](http://www.chartjs.org) @@ -29,7 +27,7 @@ The `Chart.bundle.js` and `Chart.bundle.min.js` builds include Moment.js in a si ## Documentation -You can find documentation at [www.chartjs.org/docs](http://www.chartjs.org/docs). The markdown files that build the site are available under `/docs`. Previous version documentation is available at [www.chartjs.org/docs/#notes-previous-versions](http://www.chartjs.org/docs/#notes-previous-versions). +You can find documentation at [www.chartjs.org/docs](http://www.chartjs.org/docs). The markdown files that build the site are available under `/docs`. Previous version documentation is available at [www.chartjs.org/docs/latest/developers/#previous-versions](http://www.chartjs.org/docs/latest/developers/#previous-versions). ## Contributing diff --git a/docs/README.md b/docs/README.md index aa5872211..5a0e9f355 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Chart.js -[![Chart.js on Slack](https://img.shields.io/badge/slack-Chart.js-blue.svg)](https://chart-js-automation.herokuapp.com/) +[![slack](https://img.shields.io/badge/slack-Chart.js-blue.svg?style=flat-square&maxAge=600)](https://chart-js-automation.herokuapp.com/) ## Installation diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 041be7062..606300900 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -10,6 +10,7 @@ * [Interactions](general/interactions/README.md) * [Events](general/interactions/events.md) * [Modes](general/interactions/modes.md) + * [Options](general/options.md) * [Colors](general/colors.md) * [Fonts](general/fonts.md) * [Configuration](configuration/README.md) @@ -34,7 +35,7 @@ * [Category](axes/cartesian/category.md) * [Linear](axes/cartesian/linear.md) * [Logarithmic](axes/cartesian/logarithmic.md) - * [Time](axes/cartesian/time.md) + * [Time](axes/cartesian/time.md) * [Radial](axes/radial/README.md) * [Linear](axes/radial/linear.md) * [Labelling](axes/labelling.md) diff --git a/docs/axes/cartesian/README.md b/docs/axes/cartesian/README.md index c54571619..64ce32073 100644 --- a/docs/axes/cartesian/README.md +++ b/docs/axes/cartesian/README.md @@ -15,6 +15,7 @@ All of the included cartesian axes support a number of common options. | -----| ---- | --------| ----------- | `type` | `String` | | Type of scale being employed. Custom scales can be created and registered with a string key. This allows changing the type of an axis for a chart. | `position` | `String` | | Position of the axis in the chart. Possible values are: `'top'`, `'left'`, `'bottom'`, `'right'` +| `offset` | `Boolean` | `false` | If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to `true` in the bar chart by default. | `id` | `String` | | The ID is used to link datasets and scale axes together. [more...](#axis-id) | `gridLines` | `Object` | | Grid line configuration. [more...](../styling.md#grid-line-configuration) | `scaleLabel` | `Object` | | Scale title configuration. [more...](../labelling.md#scale-title-configuration) @@ -31,7 +32,7 @@ The following options are common to all cartesian axes but do not apply to other | `maxRotation` | `Number` | `90` | Maximum rotation for tick labels when rotating to condense labels. Note: Rotation doesn't occur until necessary. *Note: Only applicable to horizontal scales.* | `minRotation` | `Number` | `0` | Minimum rotation for tick labels. *Note: Only applicable to horizontal scales.* | `mirror` | `Boolean` | `false` | Flips tick labels around axis, displaying the labels inside the chart instead of outside. *Note: Only applicable to vertical scales.* -| `padding` | `Number` | `10` | Padding between the tick label and the axis. *Note: Only applicable to horizontal scales.* +| `padding` | `Number` | `10` | Padding between the tick label and the axis. When set on a vertical axis, this applies in the horizontal (X) direction. When set on a horizontal axis, this applies in the vertical (Y) direction. ## Axis ID The properties `dataset.xAxisID` or `dataset.yAxisID` have to match the scale properties `scales.xAxes.id` or `scales.yAxes.id`. This is especially needed if multi-axes charts are used. @@ -101,4 +102,4 @@ var myChart = new Chart(ctx, { } } }); -``` \ No newline at end of file +``` diff --git a/docs/axes/cartesian/category.md b/docs/axes/cartesian/category.md index 41bfd74d7..38c306712 100644 --- a/docs/axes/cartesian/category.md +++ b/docs/axes/cartesian/category.md @@ -1,6 +1,38 @@ # Category Cartesian Axis -The category scale will be familiar to those who have used v1.0. Labels are drawn from one of the label arrays included in the chart data. If only `data.labels` is defined, this will be used. If `data.xLabels` is defined and the axis is horizontal, this will be used. Similarly, if `data.yLabels` is defined and the axis is vertical, this property will be used. Using both `xLabels` and `yLabels` together can create a chart that uses strings for both the X and Y axes. +If global configuration is used, labels are drawn from one of the label arrays included in the chart data. If only `data.labels` is defined, this will be used. If `data.xLabels` is defined and the axis is horizontal, this will be used. Similarly, if `data.yLabels` is defined and the axis is vertical, this property will be used. Using both `xLabels` and `yLabels` together can create a chart that uses strings for both the X and Y axes. + +Specifying any of the settings above defines the x axis as `type: category` if not defined otherwise. For more fine-grained control of category labels it is also possible to add `labels` as part of the category axis definition. Doing so does not apply the global defaults. + +## Category Axis Definition + +Globally: + +```javascript +let chart = new Chart(ctx, { + type: ... + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June'], + datasets: ... + }, +}); +``` +As part of axis definition: + +```javascript +let chart = new Chart(ctx, { + type: ... + data: ... + options: { + scales: { + xAxes: [{ + type: 'category', + labels: ['January', 'February', 'March', 'April', 'May', 'June'], + }] + } + } +}); +``` ## Tick Configuration Options @@ -8,6 +40,7 @@ The category scale provides the following options for configuring tick marks. Th | Name | Type | Default | Description | -----| ---- | --------| ----------- +| labels | Array[String] | - | An array of labels to display. | `min` | `String` | | The minimum item to display. [more...](#min-max-configuration) | `max` | `String` | | The maximum item to display. [more...](#min-max-configuration) diff --git a/docs/axes/cartesian/time.md b/docs/axes/cartesian/time.md index 67830e9aa..e087d1868 100644 --- a/docs/axes/cartesian/time.md +++ b/docs/axes/cartesian/time.md @@ -2,28 +2,46 @@ The time scale is used to display times and dates. When building its ticks, it will automatically calculate the most comfortable unit base on the size of the scale. -## Configuration Options +## Data Sets -The following options are provided by the time scale. They are all located in the `time` sub options. These options extend the [common tick configuration](README.md#tick-configuration). +### Input Data -| Name | Type | Default | Description -| -----| ---- | --------| ----------- -| `displayFormats` | `Object` | | Sets how different time units are displayed. [more...](#display-formats) -| `isoWeekday` | `Boolean` | `false` | If true and the unit is set to 'week', iso weekdays will be used. -| `max` | [Time](#date-formats) | | If defined, this will override the data maximum -| `min` | [Time](#date-formats) | | If defined, this will override the data minimum -| `parser` | `String` or `Function` | | Custom parser for dates. [more...](#parser) -| `round` | `String` | `false` | If defined, dates will be rounded to the start of this unit. See [Time Units](#scales-time-units) below for the allowed units. -| `tooltipFormat` | `String` | | The moment js format string to use for the tooltip. -| `unit` | `String` | `false` | If defined, will force the unit to be a certain type. See [Time Units](#scales-time-units) section below for details. -| `unitStepSize` | `Number` | `1` | The number of units between grid lines. -| `minUnit` | `String` | `millisecond` | The minimum display format to be used for a time unit. +The x-axis data points may additionally be specified via the `t` attribute when using the time scale. -## Date Formats + data: [{ + x: new Date(), + y: 1 + }, { + t: new Date(), + y: 10 + }] + + +### Date Formats When providing data for the time scale, Chart.js supports all of the formats that Moment.js accepts. See [Moment.js docs](http://momentjs.com/docs/#/parsing/) for details. -## Time Units +## Configuration Options + +The following options are provided by the time scale. You may also set options provided by the [common tick configuration](README.md#tick-configuration). + +| Name | Type | Default | Description +| -----| ---- | --------| ----------- +| `distribution` | `String` | `linear` | How data is plotted. [more...](#scale-distribution) +| `bounds` | `String` | `data` | Determines the scale bounds. [more...](#scale-bounds) +| `ticks.source` | `String` | `auto` | How ticks are generated. [more...](#ticks-source) +| `time.displayFormats` | `Object` | | Sets how different time units are displayed. [more...](#display-formats) +| `time.isoWeekday` | `Boolean` | `false` | If true and the unit is set to 'week', then the first day of the week will be Monday. Otherwise, it will be Sunday. +| `time.max` | [Time](#date-formats) | | If defined, this will override the data maximum +| `time.min` | [Time](#date-formats) | | If defined, this will override the data minimum +| `time.parser` | `String` or `Function` | | Custom parser for dates. [more...](#parser) +| `time.round` | `String` | `false` | If defined, dates will be rounded to the start of this unit. See [Time Units](#time-units) below for the allowed units. +| `time.tooltipFormat` | `String` | | The moment js format string to use for the tooltip. +| `time.unit` | `String` | `false` | If defined, will force the unit to be a certain type. See [Time Units](#time-units) section below for details. +| `time.stepSize` | `Number` | `1` | The number of units between grid lines. +| `time.minUnit` | `String` | `'millisecond'` | The minimum display format to be used for a time unit. + +### Time Units The following time measurements are supported. The names can be passed as strings to the `time.unit` config option to force a certain unit. @@ -55,20 +73,20 @@ var chart = new Chart(ctx, { }) ``` -## Display Formats +### Display Formats The following display formats are used to configure how different time units are formed into strings for the axis tick marks. See [moment.js](http://momentjs.com/docs/#/displaying/format/) for the allowable format strings. -Name | Default ---- | --- -millisecond | 'SSS [ms]' -second | 'h:mm:ss a' -minute | 'h:mm:ss a' -hour | 'MMM D, hA' -day | 'll' -week | 'll' -month | 'MMM YYYY' -quarter | '[Q]Q - YYYY' -year | 'YYYY' +Name | Default | Example +--- | --- | --- +millisecond | 'h:mm:ss.SSS a' | 11:20:01.123 AM +second | 'h:mm:ss a' | 11:20:01 AM +minute | 'h:mm a' | 11:20 AM +hour | 'hA' | 11AM +day | 'MMM D' | Sep 4 +week | 'll' | Sep 4 2015 +month | 'MMM YYYY' | Sep 2015 +quarter | '[Q]Q - YYYY' | Q3 - 2015 +year | 'YYYY' | 2015 For example, to set the display format for the 'quarter' unit to show the month and year, the following config would be passed to the chart constructor. @@ -91,7 +109,44 @@ var chart = new Chart(ctx, { }) ``` -## Parser +### Scale Distribution + +The `distribution` property controls the data distribution along the scale: + + * `'linear'`: data are spread according to their time (distances can vary) + * `'series'`: data are spread at the same distance from each other + +```javascript +var chart = new Chart(ctx, { + type: 'line', + data: data, + options: { + scales: { + xAxes: [{ + type: 'time', + distribution: 'series' + }] + } + } +}) +``` + +### Scale Bounds + +The `bounds` property controls the scale boundary strategy (bypassed by min/max time options) + +* `'data'`: make sure data are fully visible, labels outside are removed +* `'ticks'`: make sure ticks are fully visible, data outside are truncated + +### Ticks Source + +The `ticks.source` property controls the ticks generation + +* `'auto'`: generates "optimal" ticks based on scale size and time options. +* `'data'`: generates ticks from data (including labels from data `{t|x|y}` objects) +* `'labels'`: generates ticks from user given `data.labels` values ONLY + +### Parser If this property is defined as a string, it is interpreted as a custom format to be used by moment to parse the date. -If this is a function, it must return a moment.js object given the appropriate data value. \ No newline at end of file +If this is a function, it must return a moment.js object given the appropriate data value. diff --git a/docs/axes/labelling.md b/docs/axes/labelling.md index c0dda84a9..aec8d990f 100644 --- a/docs/axes/labelling.md +++ b/docs/axes/labelling.md @@ -9,11 +9,13 @@ The scale label configuration is nested under the scale configuration in the `sc | Name | Type | Default | Description | -----| ---- | --------| ----------- | `display` | `Boolean` | `false` | If true, display the axis title. -| `labelString` | `String` | `''` | The text for the title. (i.e. "# of People" or "Respone Choices"). +| `labelString` | `String` | `''` | The text for the title. (i.e. "# of People" or "Response Choices"). +| `lineHeight` | `Number|String` | `1.2` | Height of an individual line of text (see [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height)) | `fontColor` | Color | `'#666'` | Font color for scale title. | `fontFamily` | `String` | `"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"` | Font family for the scale title, follows CSS font-family options. | `fontSize` | `Number` | `12` | Font size for scale title. | `fontStyle` | `String` | `'normal'` | Font style for the scale title, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit). +| `padding` | `Number` or `Object` | `4` | Padding to apply around scale labels. Only `top` and `bottom` are implemented. ## Creating Custom Tick Formats @@ -39,4 +41,4 @@ var chart = new Chart(ctx, { } } }); -``` \ No newline at end of file +``` diff --git a/docs/axes/styling.md b/docs/axes/styling.md index 48a4d7d05..79cb4e9d6 100644 --- a/docs/axes/styling.md +++ b/docs/axes/styling.md @@ -21,7 +21,7 @@ The grid line configuration is nested under the scale configuration in the `grid | `zeroLineColor` | Color | `'rgba(0, 0, 0, 0.25)'` | Stroke color of the grid line for the first index (index 0). | `zeroLineBorderDash` | `Number[]` | `[]` | Length and spacing of dashes of the grid line for the first index (index 0). See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash) | `zeroLineBorderDashOffset` | `Number` | `0` | Offset for line dashes of the grid line for the first index (index 0). See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset) -| `offsetGridLines` | `Boolean` | `false` | If true, labels are shifted to be between grid lines. This is used in the bar chart and should not generally be used. +| `offsetGridLines` | `Boolean` | `false` | If true, grid lines will be shifted to be between labels. This is set to `true` in the bar chart by default. ## Tick Configuration The tick configuration is nested under the scale configuration in the `ticks` key. It defines options for the tick marks that are generated by the axis. @@ -35,3 +35,27 @@ The tick configuration is nested under the scale configuration in the `ticks` ke | `fontSize` | `Number` | `12` | Font size for the tick labels. | `fontStyle` | `String` | `'normal'` | Font style for the tick labels, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit). | `reverse` | `Boolean` | `false` | Reverses order of tick labels. +| `minor` | `object` | `{}` | Minor ticks configuration. Ommited options are inherited from options above. +| `major` | `object` | `{}` | Major ticks configuration. Ommited options are inherited from options above. + +## Minor Tick Configuration +The minorTick configuration is nested under the ticks configuration in the `minor` key. It defines options for the minor tick marks that are generated by the axis. Omitted options are inherited from `ticks` configuration. + +| Name | Type | Default | Description +| -----| ---- | --------| ----------- +| `callback` | `Function` | | Returns the string representation of the tick value as it should be displayed on the chart. See [callback](../axes/labelling.md#creating-custom-tick-formats). +| `fontColor` | Color | `'#666'` | Font color for tick labels. +| `fontFamily` | `String` | `"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"` | Font family for the tick labels, follows CSS font-family options. +| `fontSize` | `Number` | `12` | Font size for the tick labels. +| `fontStyle` | `String` | `'normal'` | Font style for the tick labels, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit). + +## Major Tick Configuration +The majorTick configuration is nested under the ticks configuration in the `major` key. It defines options for the major tick marks that are generated by the axis. Omitted options are inherited from `ticks` configuration. + +| Name | Type | Default | Description +| -----| ---- | --------| ----------- +| `callback` | `Function` | | Returns the string representation of the tick value as it should be displayed on the chart. See [callback](../axes/labelling.md#creating-custom-tick-formats). +| `fontColor` | Color | `'#666'` | Font color for tick labels. +| `fontFamily` | `String` | `"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"` | Font family for the tick labels, follows CSS font-family options. +| `fontSize` | `Number` | `12` | Font size for the tick labels. +| `fontStyle` | `String` | `'normal'` | Font style for the tick labels, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit). diff --git a/docs/charts/bar.md b/docs/charts/bar.md index 32cc32e9e..524c79521 100644 --- a/docs/charts/bar.md +++ b/docs/charts/bar.md @@ -73,7 +73,7 @@ Some properties can be specified as an array. If these are set to an array value | `backgroundColor` | `Color/Color[]` | The fill color of the bar. See [Colors](../general/colors.md#colors) | `borderColor` | `Color/Color[]` | The color of the bar border. See [Colors](../general/colors.md#colors) | `borderWidth` | `Number/Number[]` | The stroke width of the bar in pixels. -| `borderSkipped` | `String` | Which edge to skip drawing the border for. [more...](#borderSkipped) +| `borderSkipped` | `String` | Which edge to skip drawing the border for. [more...](#borderskipped) | `hoverBackgroundColor` | `Color/Color[]` | The fill colour of the bars when hovered. | `hoverBorderColor` | `Color/Color[]` | The stroke colour of the bars when hovered. | `hoverBorderWidth` | `Number/Number[]` | The stroke width of the bars when hovered. @@ -93,16 +93,16 @@ The bar chart defines the following configuration options. These options are mer | Name | Type | Default | Description | ---- | ---- | ------- | ----------- -| `barPercentage` | `Number` | `0.9` | Percent (0-1) of the available width each bar should be within the category percentage. 1.0 will take the whole category width and put the bars right next to each other. [more...](#bar-chart-barpercentage-vs-categorypercentage) -| `categoryPercentage` | `Number` | `0.8` | Percent (0-1) of the available width (the space between the gridlines for small datasets) for each data-point to use for the bars. [more...](#bar-chart-barpercentage-vs-categorypercentage) -| `barThickness` | `Number` | | Manually set width of each bar in pixels. If not set, the bars are sized automatically using `barPercentage` and `categoryPercentage`; -| `maxBarThickness` | `Number` | | Set this to ensure that the automatically sized bars are not sized thicker than this. Only works if barThickness is not set (automatic sizing is enabled). -| `gridLines.offsetGridLines` | `Boolean` | `true` | If true, the bars for a particular data point fall between the grid lines. If false, the grid line will go right down the middle of the bars. [more...](#offsetGridLines) +| `barPercentage` | `Number` | `0.9` | Percent (0-1) of the available width each bar should be within the category width. 1.0 will take the whole category width and put the bars right next to each other. [more...](#barpercentage-vs-categorypercentage) +| `categoryPercentage` | `Number` | `0.8` | Percent (0-1) of the available width each category should be within the sample width. [more...](#barpercentage-vs-categorypercentage) +| `barThickness` | `Number` | | Manually set width of each bar in pixels. If not set, the base sample widths are calculated automatically so that they take the full available widths without overlap. Then, the bars are sized using `barPercentage` and `categoryPercentage`. +| `maxBarThickness` | `Number` | | Set this to ensure that bars are not sized thicker than this. +| `gridLines.offsetGridLines` | `Boolean` | `true` | If true, the bars for a particular data point fall between the grid lines. The grid line will move to the left by one half of the tick interval. If false, the grid line will go right down the middle of the bars. [more...](#offsetgridlines) ### offsetGridLines -If true, the bars for a particular data point fall between the grid lines. If false, the grid line will go right down the middle of the bars. It is unlikely that this will ever need to be changed in practice. It exists more as a way to reuse the axis code by configuring the existing axis slightly differently. +If true, the bars for a particular data point fall between the grid lines. The grid line will move to the left by one half of the tick interval, which is the space between the grid lines. If false, the grid line will go right down the middle of the bars. This is set to true for a bar chart while false for other charts by default. -This setting applies to the axis configuration for a bar chart. If axes are added to the chart, this setting will need to be set for each new axis. +This setting applies to the axis configuration. If axes are added to the chart, this setting will need to be set for each new axis. ```javascript options = { @@ -152,6 +152,12 @@ The `data` property of a dataset for a bar chart is specified as a an array of n data: [20, 10] ``` +You can also specify the dataset as x/y coordinates. + +```javascript +data: [{x:'2016-12-25', y:20}, {x:'2016-12-26', y:10}] +``` + # Stacked Bar Chart Bar charts can be configured into stacked bar charts by changing the settings on the X and Y axes to enable stacking. Stacked bar charts can be used to show how one data series is made up of a number of smaller pieces. @@ -235,6 +241,6 @@ var myBarChart = new Chart(ctx, { ``` ## Config Options -The configuration options for the horizontal bar chart are the same as for the [bar chart](../bar/config-options.md#config-options). However, any options specified on the x axis in a bar chart, are applied to the y axis in a horizontal bar chart. +The configuration options for the horizontal bar chart are the same as for the [bar chart](#configuration-options). However, any options specified on the x axis in a bar chart, are applied to the y axis in a horizontal bar chart. -The default horizontal bar configuration is specified in `Chart.defaults.horizontalBar`. \ No newline at end of file +The default horizontal bar configuration is specified in `Chart.defaults.horizontalBar`. diff --git a/docs/charts/bubble.md b/docs/charts/bubble.md index d36f7eea4..4cb2ee699 100644 --- a/docs/charts/bubble.md +++ b/docs/charts/bubble.md @@ -1,6 +1,6 @@ # Bubble Chart -A bubble chart is used to display three dimensions of data at the same time. The location of the bubble is determined by the first two dimensions and the corresponding horizontal and vertical axes. The third dimension is represented by the size of the individual bubbles. +A bubble chart is used to display three dimensions of data at the same time. The location of the bubble is determined by the first two dimensions and the corresponding horizontal and vertical axes. The third dimension is represented by the size of the individual bubbles. {% chartjs %} { @@ -38,22 +38,52 @@ var myBubbleChart = new Chart(ctx,{ The bubble chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of the bubbles is generally set this way. -All properties, except `label` can be specified as an array. If these are set to an array value, the first value applies to the first bubble in the dataset, the second value to the second bubble, and so on. +| Name | Type | [Scriptable](../general/options.md#scriptable-options) | [Indexable](../general/options.md#indexable-options) | Default +| ---- | ---- | :----: | :----: | ---- +| [`backgroundColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0,0,0,0.1)'` +| [`borderColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0,0,0,0.1)'` +| [`borderWidth`](#styling) | `Number` | Yes | Yes | `3` +| [`data`](#data-structure) | `Object[]` | - | - | **required** +| [`hoverBackgroundColor`](#interactions) | [`Color`](../general/colors.md) | Yes | Yes | `undefined` +| [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | Yes | Yes | `undefined` +| [`hoverBorderWidth`](#interactions) | `Number` | Yes | Yes | `1` +| [`hoverRadius`](#interactions) | `Number` | Yes | Yes | `4` +| [`hitRadius`](#interactions) | `Number` | Yes | Yes | `1` +| [`label`](#labeling) | `String` | - | - | `undefined` +| [`pointStyle`](#styling) | `String` | Yes | Yes | `circle` +| [`radius`](#styling) | `Number` | Yes | Yes | `3` -| Name | Type | Description -| ---- | ---- | ----------- -| `label` | `String` | The label for the dataset which appears in the legend and tooltips. -| `backgroundColor` | `Color/Color[]` | The fill color for bubbles. -| `borderColor` | `Color/Color[]` | The border color for bubbles. -| `borderWidth` | `Number/Number[]` | The width of the point bubbles in pixels. -| `hoverBackgroundColor` | `Color/Color[]` | Bubble background color when hovered. -| `hoverBorderColor` | `Color/Color[]` | Bubble border color when hovered. -| `hoverBorderWidth` | `Number/Number[]` | Border width of point when hovered. -| `hoverRadius` | `Number/Number[]` | Additional radius to add to data radius on hover. +### Labeling -## Config Options +`label` defines the text associated to the dataset and which appears in the legend and tooltips. -The bubble chart has no unique configuration options. To configure options common to all of the bubbles, the [point element options](../configuration/elements/point.md#point-configuration) are used. +### Styling + +The style of each bubble can be controlled with the following properties: + +| Name | Description +| ---- | ---- +| `backgroundColor` | bubble background color +| `borderColor` | bubble border color +| `borderWidth` | bubble border width (in pixels) +| `pointStyle` | bubble [shape style](../configuration/elements#point-styles) +| `radius` | bubble radius (in pixels) + +All these values, if `undefined`, fallback to the associated [`elements.point.*`](../configuration/elements.md#point-configuration) options. + +### Interactions + +The interaction with each bubble can be controlled with the following properties: + +| Name | Description +| ---- | ----------- +| `hoverBackgroundColor` | bubble background color when hovered +| `hoverBorderColor` | bubble border color hovered +| `hoverBorderWidth` | bubble border width when hovered (in pixels) +| `hoverRadius` | bubble **additional** radius when hovered (in pixels) +| `hitRadius` | bubble **additional** radius for hit detection (in pixels) + +All these values, if `undefined`, fallback to the associated [`elements.point.*`](../configuration/elements.md#point-configuration) options. ## Default Options @@ -61,11 +91,7 @@ We can also change the default values for the Bubble chart type. Doing so will g ## Data Structure -For a bubble chart, datasets need to contain an array of data points. Each point must implement the [bubble data object](#bubble-data-object) interface. - -### Bubble Data Object - -Data for the bubble chart is passed in the form of an object. The object must implement the following interface. It is important to note that the radius property, `r` is **not** scaled by the chart. It is the raw radius in pixels of the bubble that is drawn on the canvas. +Bubble chart datasets need to contain a `data` array of points, each points represented by an object containing the following properties: ```javascript { @@ -75,7 +101,9 @@ Data for the bubble chart is passed in the form of an object. The object must im // Y Value y: , - // Radius of bubble. This is not scaled. + // Bubble radius in pixels (not scaled). r: } -``` \ No newline at end of file +``` + +**Important:** the radius property, `r` is **not** scaled by the chart, it is the raw radius in pixels of the bubble that is drawn on the canvas. diff --git a/docs/charts/line.md b/docs/charts/line.md index ba7911fe7..cd4141a49 100644 --- a/docs/charts/line.md +++ b/docs/charts/line.md @@ -6,11 +6,11 @@ A line chart is a way of plotting data points on a line. Often, it is used to sh "type": "line", "data": { "labels": [ - "January", - "February", - "March", - "April", - "May", + "January", + "February", + "March", + "April", + "May", "June", "July" ], @@ -48,21 +48,21 @@ All point* properties can be specified as an array. If these are set to an array | `label` | `String` | The label for the dataset which appears in the legend and tooltips. | `xAxisID` | `String` | The ID of the x axis to plot this dataset on. If not specified, this defaults to the ID of the first found x axis | `yAxisID` | `String` | The ID of the y axis to plot this dataset on. If not specified, this defaults to the ID of the first found y axis. -| `backgroundColor` | `Color/Color[]` | The fill color under the line. See [Colors](../general/colors.md#colors) -| `borderColor` | `Color/Color[]` | The color of the line. See [Colors](../general/colors.md#colors) -| `borderWidth` | `Number/Number[]` | The width of the line in pixels. +| `backgroundColor` | `Color` | The fill color under the line. See [Colors](../general/colors.md#colors) +| `borderColor` | `Color` | The color of the line. See [Colors](../general/colors.md#colors) +| `borderWidth` | `Number/` | The width of the line in pixels. | `borderDash` | `Number[]` | Length and spacing of dashes. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash) | `borderDashOffset` | `Number` | Offset for line dashes. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset) | `borderCapStyle` | `String` | Cap style of the line. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap) | `borderJoinStyle` | `String` | Line joint style. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin) -| `cubicInterpolationMode` | `String` | Algorithm used to interpolate a smooth curve from the discrete data points. [more...](#cubicInterpolationMode) +| `cubicInterpolationMode` | `String` | Algorithm used to interpolate a smooth curve from the discrete data points. [more...](#cubicinterpolationmode) | `fill` | `Boolean/String` | How to fill the area under the line. See [area charts](area.md) | `lineTension` | `Number` | Bezier curve tension of the line. Set to 0 to draw straightlines. This option is ignored if monotone cubic interpolation is used. | `pointBackgroundColor` | `Color/Color[]` | The fill color for points. | `pointBorderColor` | `Color/Color[]` | The border color for points. | `pointBorderWidth` | `Number/Number[]` | The width of the point border in pixels. | `pointRadius` | `Number/Number[]` | The radius of the point shape. If set to 0, the point is not rendered. -| `pointStyle` | `String/String[]/Image/Image[]` | Style of the point. [more...](#pointStyle) +| `pointStyle` | `String/String[]/Image/Image[]` | Style of the point. [more...](../configuration/elements#point-styles) | `pointHitRadius` | `Number/Number[]` | The pixel size of the non-displayed point that reacts to mouse events. | `pointHoverBackgroundColor` | `Color/Color[]` | Point background color when hovered. | `pointHoverBorderColor` | `Color/Color[]` | Point border color when hovered. @@ -75,7 +75,7 @@ All point* properties can be specified as an array. If these are set to an array ### cubicInterpolationMode The following interpolation modes are supported: * 'default' -* 'monotone'. +* 'monotone'. The 'default' algorithm uses a custom weighted cubic interpolation, which produces pleasant curves for all types of datasets. @@ -83,21 +83,6 @@ The 'monotone' algorithm is more suited to `y = f(x)` datasets : it preserves mo If left untouched (`undefined`), the global `options.elements.line.cubicInterpolationMode` property is used. -### pointStyle -The style of point. Options are: -* 'circle' -* 'cross' -* 'crossRot' -* 'dash'. -* 'line' -* 'rect' -* 'rectRounded' -* 'rectRot' -* 'star' -* 'triangle' - -If the option is an image, that image is drawn on the canvas using [drawImage](https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/drawImage). - ### Stepped Line The following values are supported for `steppedLine`: * `false`: No Step Interpolation (default) @@ -127,7 +112,7 @@ Chart.defaults.line.spanGaps = true; ## Data Structure -The `data` property of a dataset for a line chart can be passed in two formats. +The `data` property of a dataset for a line chart can be passed in two formats. ### Number[] ```javascript @@ -198,7 +183,7 @@ new Chart(ctx, { }); ``` -## Draw Line Drawing +## Disable Line Drawing If you have a lot of data points, it can be more performant to disable rendering of the line for a dataset and only draw points. Doing this means that there is less to draw on the canvas which will improve render performance. diff --git a/docs/charts/polar.md b/docs/charts/polar.md index 4c55556f1..29952cff6 100644 --- a/docs/charts/polar.md +++ b/docs/charts/polar.md @@ -56,7 +56,7 @@ The following options can be included in a polar area chart dataset to configure ## Config Options -These are the customisation options specific to Polar Area charts. These options are merged with the [global chart configuration options](#global-chart-configuration), and form the options of the chart. +These are the customisation options specific to Polar Area charts. These options are merged with the [global chart default options](#default-options), and form the options of the chart. | Name | Type | Default | Description | ---- | ---- | ------- | ----------- @@ -92,4 +92,4 @@ data = { 'Blue' ] }; -``` \ No newline at end of file +``` diff --git a/docs/charts/radar.md b/docs/charts/radar.md index dd5d47b27..ac908315c 100644 --- a/docs/charts/radar.md +++ b/docs/charts/radar.md @@ -69,9 +69,9 @@ All point* properties can be specified as an array. If these are set to an array | Name | Type | Description | ---- | ---- | ----------- | `label` | `String` | The label for the dataset which appears in the legend and tooltips. -| `backgroundColor` | `Color/Color[]` | The fill color under the line. See [Colors](../general/colors.md#colors) -| `borderColor` | `Color/Color[]` | The color of the line. See [Colors](../general/colors.md#colors) -| `borderWidth` | `Number/Number[]` | The width of the line in pixels. +| `backgroundColor` | `Color` | The fill color under the line. See [Colors](../general/colors.md#colors) +| `borderColor` | `Color` | The color of the line. See [Colors](../general/colors.md#colors) +| `borderWidth` | `Number` | The width of the line in pixels. | `borderDash` | `Number[]` | Length and spacing of dashes. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash) | `borderDashOffset` | `Number` | Offset for line dashes. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset) | `borderCapStyle` | `String` | Cap style of the line. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap) @@ -82,7 +82,7 @@ All point* properties can be specified as an array. If these are set to an array | `pointBorderColor` | `Color/Color[]` | The border color for points. | `pointBorderWidth` | `Number/Number[]` | The width of the point border in pixels. | `pointRadius` | `Number/Number[]` | The radius of the point shape. If set to 0, the point is not rendered. -| `pointStyle` | `String/String[]/Image/Image[]` | Style of the point. [more...](#pointStyle) +| `pointStyle` | `String/String[]/Image/Image[]` | Style of the point. [more...](#pointstyle) | `pointHitRadius` | `Number/Number[]` | The pixel size of the non-displayed point that reacts to mouse events. | `pointHoverBackgroundColor` | `Color/Color[]` | Point background color when hovered. | `pointHoverBorderColor` | `Color/Color[]` | Point border color when hovered. diff --git a/docs/configuration/animations.md b/docs/configuration/animations.md index 92e273bb6..77d469ef1 100644 --- a/docs/configuration/animations.md +++ b/docs/configuration/animations.md @@ -93,4 +93,4 @@ var chart = new Chart(ctx, { }); ``` -Another example usage of these callbacks can be found on [Github](https://github.com/chartjs/Chart.js/blob/master/samples/animation/progress-bar.html): this sample displays a progress bar showing how far along the animation is. +Another example usage of these callbacks can be found on [Github](https://github.com/chartjs/Chart.js/blob/master/samples/advanced/progress-bar.html): this sample displays a progress bar showing how far along the animation is. diff --git a/docs/configuration/elements.md b/docs/configuration/elements.md index 0586a14b2..5375a7e9f 100644 --- a/docs/configuration/elements.md +++ b/docs/configuration/elements.md @@ -18,7 +18,7 @@ Global point options: `Chart.defaults.global.elements.point` | Name | Type | Default | Description | -----| ---- | --------| ----------- | `radius` | `Number` | `3` | Point radius. -| `pointStyle` | `String` | `circle` | Point style. +| [`pointStyle`](#point-styles) | `String` | `circle` | Point style. | `backgroundColor` | `Color` | `'rgba(0,0,0,0.1)'` | Point fill color. | `borderWidth` | `Number` | `1` | Point stroke width. | `borderColor` | `Color` | `'rgba(0,0,0,0.1)'` | Point stroke color. @@ -26,6 +26,22 @@ Global point options: `Chart.defaults.global.elements.point` | `hoverRadius` | `Number` | `4` | Point radius when hovered. | `hoverBorderWidth` | `Number` | `1` | Stroke width when hovered. +### Point Styles + +The following values are supported: +- `'circle'` +- `'cross'` +- `'crossRot'` +- `'dash'` +- `'line'` +- `'rect'` +- `'rectRounded'` +- `'rectRot'` +- `'star'` +- `'triangle'` + +If the value is an image, that image is drawn on the canvas using [drawImage](https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/drawImage). + ## Line Configuration Line elements are used to represent the line in a line chart. diff --git a/docs/configuration/legend.md b/docs/configuration/legend.md index c398908b4..4cad48b55 100644 --- a/docs/configuration/legend.md +++ b/docs/configuration/legend.md @@ -34,8 +34,8 @@ The legend label configuration is nested below the legend configuration using th | `fontColor` | Color | `'#666'` | Color of text | `fontFamily` | `String` | `"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"` | Font family of legend text. | `padding` | `Number` | `10` | Padding between rows of colored boxes. -| `generateLabels` | `Function` | | Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See [Legend Item](#chart-configuration-legend-item-interface) for details. -| `filter` | `Function` | `null` | Filters legend items out of the legend. Receives 2 parameters, a [Legend Item](#chart-configuration-legend-item-interface) and the chart data. +| `generateLabels` | `Function` | | Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See [Legend Item](#legend-item-interface) for details. +| `filter` | `Function` | `null` | Filters legend items out of the legend. Receives 2 parameters, a [Legend Item](#legend-item-interface) and the chart data. | `usePointStyle` | `Boolean` | `false` | Label style will match corresponding point style (size is based on fontSize, boxWidth is not used in this case). ## Legend Item Interface @@ -163,4 +163,4 @@ var chart = new Chart(ctx, { } } }); -``` \ No newline at end of file +``` diff --git a/docs/configuration/title.md b/docs/configuration/title.md index a02e6b620..b2c04cd8f 100644 --- a/docs/configuration/title.md +++ b/docs/configuration/title.md @@ -14,7 +14,8 @@ The title configuration is passed into the `options.title` namespace. The global | `fontColor` | Color | `'#666'` | Font color | `fontStyle` | `String` | `'bold'` | Font style | `padding` | `Number` | `10` | Number of pixels to add above and below the title text. -| `text` | `String` | `''` | Title text ti display +| `lineHeight` | `Number|String` | `1.2` | Height of an individual line of text (see [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height)) +| `text` | `String/String[]` | `''` | Title text to display. If specified as an array, text is rendered on multiple lines. ### Position Possible title position values are: diff --git a/docs/configuration/tooltip.md b/docs/configuration/tooltip.md index 21387f140..bb3b7a6ae 100644 --- a/docs/configuration/tooltip.md +++ b/docs/configuration/tooltip.md @@ -7,7 +7,7 @@ The tooltip configuration is passed into the `options.tooltips` namespace. The g | Name | Type | Default | Description | -----| ---- | --------| ----------- | `enabled` | `Boolean` | `true` | Are tooltips enabled -| `custom` | `Function` | `null` | See [custom tooltip](#custom-tooltips) section. +| `custom` | `Function` | `null` | See [custom tooltip](#external-custom-tooltips) section. | `mode` | `String` | `'nearest'` | Sets which elements appear in the tooltip. [more...](../general/interactions/modes.md#interaction-modes). | `intersect` | `Boolean` | `true` | if true, the tooltip mode applies only when the mouse position intersects with an element. If false, the mode will be applied at all times. | `position` | `String` | `'average'` | The mode for positioning the tooltip. [more...](#position-modes) @@ -53,17 +53,17 @@ New modes can be defined by adding functions to the Chart.Tooltip.positioners ma ### Sort Callback -Allows sorting of [tooltip items](#chart-configuration-tooltip-item-interface). Must implement at minimum a function that can be passed to [Array.prototype.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort). This function can also accept a third parameter that is the data object passed to the chart. +Allows sorting of [tooltip items](#tooltip-item-interface). Must implement at minimum a function that can be passed to [Array.prototype.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort). This function can also accept a third parameter that is the data object passed to the chart. ### Filter Callback -Allows filtering of [tooltip items](#chart-configuration-tooltip-item-interface). Must implement at minimum a function that can be passed to [Array.prototype.filter](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter). This function can also accept a second parameter that is the data object passed to the chart. +Allows filtering of [tooltip items](#tooltip-item-interface). Must implement at minimum a function that can be passed to [Array.prototype.filter](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter). This function can also accept a second parameter that is the data object passed to the chart. ## Tooltip Callbacks The tooltip label configuration is nested below the tooltip configuration using the `callbacks` key. The tooltip has the following callbacks for providing text. For all functions, 'this' will be the tooltip object created from the Chart.Tooltip constructor. -All functions are called with the same arguments: a [tooltip item](#chart-configuration-tooltip-item-interface) and the data object passed to the chart. All functions must return either a string or an array of strings. Arrays of strings are treated as multiple lines of text. +All functions are called with the same arguments: a [tooltip item](#tooltip-item-interface) and the data object passed to the chart. All functions must return either a string or an array of strings. Arrays of strings are treated as multiple lines of text. | Name | Arguments | Description | ---- | --------- | ----------- @@ -74,6 +74,7 @@ All functions are called with the same arguments: a [tooltip item](#chart-config | `beforeLabel` | `tooltipItem, data` | Returns text to render before an individual label. This will be called for each item in the tooltip. | `label` | `tooltipItem, data` | Returns text to render for an individual item in the tooltip. | `labelColor` | `tooltipItem, chart` | Returns the colors to render for the tooltip item. [more...](#label-color-callback) +| `labelTextColor` | `tooltipItem, chart` | Returns the colors for the text of the label for the tooltip item. | `afterLabel` | `tooltipItem, data` | Returns text to render after an individual label. | `afterBody` | `Array[tooltipItem], data` | Returns text to render after the body section | `beforeFooter` | `Array[tooltipItem], data` | Returns text to render before the footer section. @@ -95,6 +96,9 @@ var chart = new Chart(ctx, { borderColor: 'rgb(255, 0, 0)', backgroundColor: 'rgb(255, 0, 0)' } + }, + labelTextColor:function(tooltipItem, chart){ + return '#543453'; } } } diff --git a/docs/developers/README.md b/docs/developers/README.md index f8d6e9742..d2b0635b0 100644 --- a/docs/developers/README.md +++ b/docs/developers/README.md @@ -1,6 +1,25 @@ # Developers + Developer features allow extending and enhancing Chart.js in many different ways. +# Latest resources + +Latest documentation and samples, including unreleased features, are available at: + + - http://www.chartjs.org/docs/master/ + - http://www.chartjs.org/samples/master/ + +# Development releases + +Latest builds are available for testing at: + + - http://www.chartjs.org/dist/master/Chart.min.js + - http://www.chartjs.org/dist/master/Chart.bundle.min.js + +> Note: Development builds are currently only available via HTTP, so in order to include them in [JSFiddle](http://jsfiddle.net) or [CodePen](http://codepen.io), you need to access these tools via HTTP as well. + +**WARNING: Development builds MUST not be used for production purposes or as replacement for CDN.** + # Browser support Chart.js offers support for all browsers where canvas is supported. @@ -19,4 +38,4 @@ Please use the documentation that is available on [chartjs.org](http://www.chart Please note - documentation for previous versions are available on the GitHub repo. -- [1.x Documentation](https://github.com/chartjs/Chart.js/tree/v1.1.1/docs) \ No newline at end of file +- [1.x Documentation](https://github.com/chartjs/Chart.js/tree/v1.1.1/docs) diff --git a/docs/developers/api.md b/docs/developers/api.md index 714cb580a..a5a0a5133 100644 --- a/docs/developers/api.md +++ b/docs/developers/api.md @@ -17,7 +17,7 @@ This must be called before the canvas is reused for a new chart. myLineChart.destroy(); ``` -## .update(duration, lazy) +## .update(config) Triggers an update of the chart. This can be safely called after updating the data object. This will update all scales, legends, and then re-render the chart. @@ -30,6 +30,21 @@ myLineChart.update(); // Calling update now animates the position of March from > **Note:** replacing the data reference (e.g. `myLineChart.data = {datasets: [...]}` only works starting **version 2.6**. Prior that, replacing the entire data object could be achieved with the following workaround: `myLineChart.config.data = {datasets: [...]}`. +A `config` object can be provided with additional configuration for the update process. This is useful when `update` is manually called inside an event handler and some different animation is desired. + +The following properties are supported: +* **duration** (number): Time for the animation of the redraw in milliseconds +* **lazy** (boolean): If true, the animation can be interrupted by other animations +* **easing** (string): The animation easing function. See [Animation Easing](../configuration/animations.md) for possible values. + +Example: +```javascript +myChart.update({ + duration: 800, + easing: 'easeOutBounce' +}) +``` + See [Updating Charts](updates.md) for more details. ## .reset() @@ -40,14 +55,20 @@ Reset the chart to it's state before the initial animation. A new animation can myLineChart.reset(); ``` -## .render(duration, lazy) +## .render(config) Triggers a redraw of all chart elements. Note, this does not update elements for new data. Use `.update()` in that case. +See `.update(config)` for more details on the config object. + ```javascript // duration is the time for the animation of the redraw in milliseconds // lazy is a boolean. if true, the animation can be interrupted by other animations -myLineChart.render(duration, lazy); +myLineChart.render({ + duration: 800, + lazy: false, + easing: 'easeOutBounce' +}); ``` ## .stop() @@ -107,6 +128,19 @@ myLineChart.getElementAtEvent(e); // => returns the first element at the event point. ``` +To get an item that was clicked on, `getElementAtEvent` can be used. + +```javascript +function clickHandler(evt) { + var item = myChart.getElementAtEvent(evt)[0]; + + if (item) { + var label = myChart.data.labels[firstPoint._index]; + var value = myChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index]; + } +} +``` + ## .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. diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md index ee08b4dd7..e6551d590 100644 --- a/docs/developers/contributing.md +++ b/docs/developers/contributing.md @@ -8,6 +8,7 @@ New contributions to the library are welcome, but we ask that you please follow - Check that your code will pass tests, `gulp test` will run tests for you. - Keep pull requests concise, and document new functionality in the relevant `.md` file. - Consider whether your changes are useful for all users, or if creating a Chart.js [plugin](plugins.md) would be more appropriate. +- Avoid breaking changes unless there is an upcoming major release, which are infrequent. We encourage people to write plugins for most new advanced features, so care a lot about backwards compatibility. # Joining the project diff --git a/docs/developers/plugins.md b/docs/developers/plugins.md index 467759f28..cc4284f8f 100644 --- a/docs/developers/plugins.md +++ b/docs/developers/plugins.md @@ -1,6 +1,6 @@ # Plugins -Plugins are the most efficient way to customize or change the default behavior of a chart. They have been introduced at [version 2.1.0](https://github.com/chartjs/Chart.js/releases/tag/2.1.0) (global plugins only) and extended at [version 2.5.0](https://github.com/chartjs/Chart.js/releases/tag/2.5.0) (per chart plugins and options). +Plugins are the most efficient way to customize or change the default behavior of a chart. They have been introduced at [version 2.1.0](https://github.com/chartjs/Chart.js/releases/tag/2.1.0) (global plugins only) and extended at [version 2.5.0](https://github.com/chartjs/Chart.js/releases/tag/v2.5.0) (per chart plugins and options). ## Using plugins @@ -106,7 +106,7 @@ var chart = new Chart(ctx, { ## Plugin Core API -Available hooks (as of version 2.5): +Available hooks (as of version 2.6): * beforeInit * afterInit @@ -116,12 +116,16 @@ Available hooks (as of version 2.5): * afterLayout * beforeDatasetsUpdate *(cancellable)* * afterDatasetsUpdate +* beforeDatasetUpdate *(cancellable)* +* afterDatasetUpdate * beforeRender *(cancellable)* * afterRender * beforeDraw *(cancellable)* * afterDraw * beforeDatasetsDraw *(cancellable)* * afterDatasetsDraw +* beforeDatasetDraw *(cancellable)* +* afterDatasetDraw * beforeEvent *(cancellable)* * afterEvent * resize diff --git a/docs/general/README.md b/docs/general/README.md index f075bca21..950188ff1 100644 --- a/docs/general/README.md +++ b/docs/general/README.md @@ -1,8 +1,10 @@ -# General Chart.js Configuration +# General Configuration -These sections describe general configuration options that can apply elsewhere in the documentation. +These sections describe general configuration options that can apply elsewhere in the documentation. -* [Colors](./colors.md) defines acceptable color values -* [Font](./fonts.md) defines various font options -* [Interactions](./interactions/README.md) defines options that reflect how hovering chart elements works -* [Responsive](./responsive.md) defines responsive chart options that apply to all charts. \ No newline at end of file +* [Responsive](./responsive.md) defines responsive chart options that apply to all charts. +* [Device Pixel Ratio](./device-pixel-ratio.md) defines the ratio between display pixels and rendered pixels. +* [Interactions](./interactions/README.md) defines options that reflect how hovering chart elements works. +* [Options](./options.md) scriptable and indexable options syntax. +* [Colors](./colors.md) defines acceptable color values. +* [Font](./fonts.md) defines various font options. diff --git a/docs/general/device-pixel-ratio.md b/docs/general/device-pixel-ratio.md new file mode 100644 index 000000000..ae82e7a6e --- /dev/null +++ b/docs/general/device-pixel-ratio.md @@ -0,0 +1,13 @@ +# Device Pixel Ratio + +By default the chart's canvas will use a 1:1 pixel ratio, unless the physical display has a higher pixel ratio (e.g. Retina displays). + +For applications where a chart will be converted to a bitmap, or printed to a higher DPI medium it can be desirable to render the chart at a higher resolution than the default. + +Setting `devicePixelRatio` to a value other than 1 will force the canvas size to be scaled by that amount, relative to the container size. There should be no visible difference on screen; the difference will only be visible when the image is zoomed or printed. + +## Configuration Options + +| Name | Type | Default | Description +| ---- | ---- | ------- | ----------- +| `devicePixelRatio` | `Number` | window.devicePixelRatio | Override the window's default devicePixelRatio. diff --git a/docs/general/fonts.md b/docs/general/fonts.md index dd4752942..082456f3d 100644 --- a/docs/general/fonts.md +++ b/docs/general/fonts.md @@ -26,3 +26,7 @@ let chart = new Chart(ctx, { | `defaultFontFamily` | `String` | `"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"` | Default font family for all text. | `defaultFontSize` | `Number` | `12` | Default font size (in px) for text. Does not apply to radialLinear scale point labels. | `defaultFontStyle` | `String` | `'normal'` | Default font style. Does not apply to tooltip title or footer. Does not apply to chart title. + +## Non-Existant Fonts + +If a font is specified for a chart that does exist on the system, the browser will not apply the font when it is set. If you notice odd fonts appearing in your charts, check that the font you are applying exists on your system. See [issue 3318](https://github.com/chartjs/Chart.js/issues/3318) for more details. diff --git a/docs/general/interactions/README.md b/docs/general/interactions/README.md index 1ff1d55d8..585e65582 100644 --- a/docs/general/interactions/README.md +++ b/docs/general/interactions/README.md @@ -6,4 +6,5 @@ The hover configuration is passed into the `options.hover` namespace. The global | ---- | ---- | ------- | ----------- | `mode` | `String` | `'nearest'` | Sets which elements appear in the tooltip. See [Interaction Modes](./modes.md#interaction-modes) for details. | `intersect` | `Boolean` | `true` | if true, the hover mode only applies when the mouse position intersects an item on the chart. +| `axis` | `String` | `'x'` | Can be set to `'x'`, `'y'`, or `'xy'` to define which directions are used in calculating distances. Defaults to `'x'` for `index` mode and `'xy'` in `dataset` and `nearest` modes. | `animationDuration` | `Number` | `400` | Duration in milliseconds it takes to animate hover style changes. \ No newline at end of file diff --git a/docs/general/interactions/modes.md b/docs/general/interactions/modes.md index 6775c590c..a46f047e7 100644 --- a/docs/general/interactions/modes.md +++ b/docs/general/interactions/modes.md @@ -41,7 +41,7 @@ Finds the first item that intersects the point and returns it. Behaves like 'nea See `'index'` mode ## index -Finds item at the same index. If the `intersect` setting is true, the first intersecting item is used to determine the index in the data. If `intersect` false the nearest item is used to determine the index. +Finds item at the same index. If the `intersect` setting is true, the first intersecting item is used to determine the index in the data. If `intersect` false the nearest item, in the x direction, is used to determine the index. ```javascript var chart = new Chart(ctx, { @@ -55,6 +55,21 @@ var chart = new Chart(ctx, { }) ``` +To use index mode in a chart like the horizontal bar chart, where we search along the y direction, you can use the `axis` setting introduced in v2.7.0. By setting this value to `'y'` on the y direction is used. + +```javascript +var chart = new Chart(ctx, { + type: 'horizontalBar', + data: data, + options: { + tooltips: { + mode: 'index', + axis: 'y' + } + } +}) +``` + ## x-axis (deprecated) Behaves like `'index'` mode with `intersect = false`. diff --git a/docs/general/options.md b/docs/general/options.md new file mode 100644 index 000000000..e05cd33ee --- /dev/null +++ b/docs/general/options.md @@ -0,0 +1,48 @@ +# Options + +## Scriptable Options + +Scriptable options also accept a function which is called for each data and that takes the unique argument `context` representing contextual information (see [option context](options.md#option-context)). + +Example: + +```javascript +color: function(context) { + var index = context.dataIndex; + var value = context.dataset.data[index]; + return value < 0 ? 'red' : // draw negative values in red + index % 2 ? 'blue' : // else, alternate values in blue and green + 'green'; +} +``` + +> **Note:** scriptable options are only supported by a few bubble chart options. + +## Indexable Options + +Indexable options also accept an array in which each item corresponds to the element at the same index. Note that this method requires to provide as many items as data, so, in most cases, using a [function](#scriptable-options) is more appropriated if supported. + +Example: + +```javascript +color: [ + 'red', // color for data at index 0 + 'blue', // color for data at index 1 + 'green', // color for data at index 2 + 'black', // color for data at index 3 + //... +] +``` + +## Option Context + +The option context is used to give contextual information when resolving options and currently only applies to [scriptable options](#scriptable-options). + +The context object contains the following properties: + +- `chart`: the associated chart +- `dataIndex`: index of the current data +- `dataset`: dataset at index `datasetIndex` +- `datasetIndex`: index of the current dataset + +**Important**: since the context can represent different types of entities (dataset, data, etc.), some properties may be `undefined` so be sure to test any context property before using it. diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index bb3e17f1f..9266b3099 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -11,7 +11,7 @@ First, we need to have a canvas in our page. Now that we have a canvas we can use, we need to include Chart.js in our page. ```html - ``` Now, we can create a chart. We add a script to our page: diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 4a3df6eab..dd8cfa4a7 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -2,22 +2,27 @@ Chart.js can be installed via npm or bower. It is recommended to get Chart.js this way. ## npm +[![npm](https://img.shields.io/npm/v/chart.js.svg?style=flat-square&maxAge=600)](https://npmjs.com/package/chart.js) ```bash npm install chart.js --save ``` ## Bower +[![bower](https://img.shields.io/bower/v/chartjs.svg?style=flat-square&maxAge=600)](https://libraries.io/bower/chartjs) ```bash bower install chart.js --save ``` ## CDN +[![cdn](https://img.shields.io/cdnjs/v/Chart.js.svg?label=cdn&style=flat-square&maxAge=600)](https://cdnjs.com/libraries/Chart.js) + or just use these [Chart.js CDN](https://cdnjs.com/libraries/Chart.js) links. - ## Github +[![github](https://img.shields.io/github/release/chartjs/Chart.js.svg?style=flat-square&maxAge=600)](https://github.com/chartjs/Chart.js/releases/latest) + You can download the latest version of [Chart.js on GitHub](https://github.com/chartjs/Chart.js/releases/latest). If you download or clone the repository, you must [build](../developers/contributing.md#building-chartjs) Chart.js to generate the dist files. Chart.js no longer comes with prebuilt release versions, so an alternative option to downloading the repo is **strongly** advised. @@ -38,4 +43,4 @@ Files: * `dist/Chart.bundle.js` * `dist/Chart.bundle.min.js` -The bundled version includes Moment.js built into the same file. This version should be used if you wish to use time axes and want a single file to include. Do not use this build if your application already includes Moment.js. If you do, Moment.js will be included twice, increasing the page load time and potentially introducing version issues. \ No newline at end of file +The bundled version includes Moment.js built into the same file. This version should be used if you wish to use time axes and want a single file to include. Do not use this build if your application already includes Moment.js. If you do, Moment.js will be included twice, increasing the page load time and potentially introducing version issues. diff --git a/docs/notes/comparison.md b/docs/notes/comparison.md index aa7e25eec..3cb9c99af 100644 --- a/docs/notes/comparison.md +++ b/docs/notes/comparison.md @@ -12,7 +12,7 @@ Library Features | Extendable to Custom Charts | ✓ | ✓ | | | | Supports Modern Browsers | ✓ | ✓ | ✓ | ✓ | | Extensive Documentation | ✓ | ✓ | ✓ | ✓ | -| Open Source | ✓ | ✓ | ✓ | ✓ | +| Open Source | ✓ | ✓ | | ✓ | Built in Chart Types diff --git a/docs/notes/extensions.md b/docs/notes/extensions.md index 43991102e..33e0e647e 100644 --- a/docs/notes/extensions.md +++ b/docs/notes/extensions.md @@ -13,11 +13,12 @@ In addition, many charts can be found on the [npm registry](https://www.npmjs.co ## Plugins - - chartjs-plugin-annotation.js - Draw lines and boxes on chart area. - - chartjs-plugin-deferred.js - Defer initial chart update until chart scrolls into viewport. - - chartjs-plugin-draggable.js - Makes select chart elements draggable with the mouse. - - chartjs-plugin-stacked100.js - Draw 100% stacked bar chart. - - chartjs-plugin-zoom.js - Enable zooming and panning on charts. + - chartjs-plugin-annotation - Draws lines and boxes on chart area. + - chartjs-plugin-datalabels - Displays labels on data for any type of charts. + - chartjs-plugin-deferred - Defers initial chart update until chart scrolls into viewport. + - chartjs-plugin-draggable - Makes select chart elements draggable with the mouse. + - chartjs-plugin-stacked100 - Draws 100% stacked bar chart. + - chartjs-plugin-zoom - Enables zooming and panning on charts. In addition, many plugins can be found on the [npm registry](https://www.npmjs.com/search?q=chartjs-plugin-). @@ -45,3 +46,6 @@ In addition, many plugins can be found on the [npm registry](https://www.npmjs.c ### Vue.js - vue-chartjs + +### Java + - Chart.java diff --git a/gulpfile.js b/gulpfile.js index 884242f05..bb42ea9d3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -23,7 +23,6 @@ var package = require('./package.json'); var srcDir = './src/'; var outDir = './dist/'; -var testDir = './test/'; var header = "/*!\n" + " * Chart.js\n" + @@ -128,8 +127,9 @@ function packageTask() { function lintTask() { var files = [ - srcDir + '**/*.js', - testDir + '**/*.js' + 'samples/**/*.js', + 'src/**/*.js', + 'test/**/*.js' ]; // NOTE(SB) codeclimate has 'complexity' and 'max-statements' eslint rules way too strict @@ -137,25 +137,9 @@ function lintTask() { // to fix, let's turn them as warnings and rewrite code later progressively. var options = { rules: { - 'complexity': [1, 6], + 'complexity': [1, 10], 'max-statements': [1, 30] - }, - globals: [ - 'Chart', - 'acquireChart', - 'afterAll', - 'afterEach', - 'beforeAll', - 'beforeEach', - 'describe', - 'expect', - 'fail', - 'it', - 'jasmine', - 'moment', - 'spyOn', - 'xit' - ] + } }; return gulp.src(files) @@ -190,8 +174,8 @@ function startTest() { './test/jasmine.index.js', './src/**/*.js', ].concat( - argv.inputs? - argv.inputs.split(';'): + argv.inputs ? + argv.inputs.split(';') : ['./test/specs/**/*.js'] ); } @@ -204,7 +188,12 @@ function unittestTask(done) { args: { coverage: !!argv.coverage } - }, done).start(); + }, + // https://github.com/karma-runner/gulp-karma/issues/18 + function(error) { + error = error ? new Error('Karma returned with the error code: ' + error) : undefined; + done(error); + }).start(); } function librarySizeTask() { @@ -216,9 +205,7 @@ function librarySizeTask() { function moduleSizesTask() { return gulp.src(srcDir + '**/*.js') - .pipe(uglify({ - preserveComments: 'some' - })) + .pipe(uglify()) .pipe(size({ showFiles: true, gzip: true diff --git a/package.json b/package.json index 7963c6e58..445dc877a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "chart.js", "homepage": "http://www.chartjs.org", "description": "Simple HTML5 charts using the canvas element.", - "version": "2.6.0", + "version": "2.7.0", "license": "MIT", "main": "src/chart.js", "repository": { @@ -10,45 +10,45 @@ "url": "https://github.com/chartjs/Chart.js.git" }, "devDependencies": { - "browserify": "^13.0.0", - "browserify-istanbul": "^0.2.1", + "browserify": "^14.3.0", + "browserify-istanbul": "^2.0.0", "bundle-collapser": "^1.2.1", - "child-process-promise": "^2.2.0", - "coveralls": "^2.11.6", + "child-process-promise": "^2.2.1", + "coveralls": "^2.13.1", "gitbook-cli": "^2.3.0", "gulp": "3.9.x", - "gulp-concat": "~2.1.x", - "gulp-connect": "~2.0.5", - "gulp-eslint": "^3.0.0", + "gulp-concat": "~2.6.x", + "gulp-connect": "~5.0.0", + "gulp-eslint": "^3.0.1", "gulp-file": "^0.3.0", - "gulp-html-validator": "^0.0.2", + "gulp-html-validator": "^0.0.5", "gulp-insert": "~0.5.0", "gulp-replace": "^0.5.4", - "gulp-size": "~0.4.0", + "gulp-size": "~2.1.0", "gulp-streamify": "^1.0.2", - "gulp-uglify": "~2.0.x", - "gulp-util": "~2.2.x", - "gulp-zip": "~3.2.0", - "jasmine": "^2.5.0", - "jasmine-core": "^2.5.0", - "karma": "^1.5.0", - "karma-browserify": "^5.1.0", - "karma-chrome-launcher": "^2.0.0", - "karma-coverage": "^1.1.0", - "karma-firefox-launcher": "^1.0.0", + "gulp-uglify": "~3.0.x", + "gulp-util": "~3.0.x", + "gulp-zip": "~4.0.0", + "jasmine": "^2.6.0", + "jasmine-core": "^2.6.2", + "karma": "^1.7.0", + "karma-browserify": "^5.1.1", + "karma-chrome-launcher": "^2.1.1", + "karma-coverage": "^1.1.1", + "karma-firefox-launcher": "^1.0.1", "karma-jasmine": "^1.1.0", "karma-jasmine-html-reporter": "^0.2.2", - "merge-stream": "^1.0.0", + "merge-stream": "^1.0.1", "pixelmatch": "^4.0.2", "vinyl-source-stream": "^1.1.0", - "watchify": "^3.7.0", - "yargs": "^5.0.0" + "watchify": "^3.9.0", + "yargs": "^8.0.1" }, "spm": { "main": "Chart.js" }, "dependencies": { - "chartjs-color": "^2.1.0", - "moment": "^2.10.6" + "chartjs-color": "~2.2.0", + "moment": "~2.18.0" } } diff --git a/samples/charts/area/analyser.js b/samples/charts/area/analyser.js index f9d001af5..e4ed8e90b 100644 --- a/samples/charts/area/analyser.js +++ b/samples/charts/area/analyser.js @@ -20,7 +20,7 @@ return; } - for (i=0, ilen=datasets.length; i + Chart.js samples diff --git a/samples/samples.js b/samples/samples.js index 235685bf2..2d11e9d48 100644 --- a/samples/samples.js +++ b/samples/samples.js @@ -112,6 +112,9 @@ }, { title: 'Line (point data)', path: 'scales/time/line-point-data.html' + }, { + title: 'Time Series', + path: 'scales/time/financial.html' }, { title: 'Combo', path: 'scales/time/combo.html' @@ -167,6 +170,12 @@ title: 'HTML tooltips (points)', path: 'tooltips/custom-points.html' }] + }, { + title: 'Scriptable', + items: [{ + title: 'Bubble Chart', + path: 'scriptable/bubble.html' + }] }, { title: 'Advanced', items: [{ diff --git a/samples/scales/time/financial.html b/samples/scales/time/financial.html new file mode 100644 index 000000000..3d91dfa56 --- /dev/null +++ b/samples/scales/time/financial.html @@ -0,0 +1,104 @@ + + + + + Line Chart + + + + + + + +
+ +
+
+
+ Chart Type: + + + + + + diff --git a/samples/scales/time/line-point-data.html b/samples/scales/time/line-point-data.html index 0a221624b..3483c4545 100644 --- a/samples/scales/time/line-point-data.html +++ b/samples/scales/time/line-point-data.html @@ -77,10 +77,10 @@ }, options: { responsive: true, - title:{ - display:true, - text:"Chart.js Time Point Data" - }, + title:{ + display:true, + text:"Chart.js Time Point Data" + }, scales: { xAxes: [{ type: "time", @@ -88,7 +88,13 @@ scaleLabel: { display: true, labelString: 'Date' - } + }, + ticks: { + major: { + fontStyle: "bold", + fontColor: "#FF0000" + } + } }], yAxes: [{ display: true, @@ -104,7 +110,6 @@ window.onload = function() { var ctx = document.getElementById("canvas").getContext("2d"); window.myLine = new Chart(ctx, config); - }; document.getElementById('randomizeData').addEventListener('click', function() { diff --git a/samples/scales/time/line.html b/samples/scales/time/line.html index 221354f9e..0cca931e0 100644 --- a/samples/scales/time/line.html +++ b/samples/scales/time/line.html @@ -28,7 +28,7 @@ + + + +
+
+
+ + + +
+
+ + + + diff --git a/samples/utils.js b/samples/utils.js index dc5bd12ae..50bb81c0c 100644 --- a/samples/utils.js +++ b/samples/utils.js @@ -1,5 +1,3 @@ -/* global Chart */ - 'use strict'; window.chartColors = { @@ -12,10 +10,6 @@ window.chartColors = { grey: 'rgb(201, 203, 207)' }; -window.randomScalingFactor = function() { - return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100); -}; - (function(global) { var Months = [ 'January', @@ -32,7 +26,21 @@ window.randomScalingFactor = function() { 'December' ]; + var COLORS = [ + '#4dc9f6', + '#f67019', + '#f53794', + '#537bc4', + '#acc236', + '#166a8f', + '#00a950', + '#58595b', + '#8549ba' + ]; + var Samples = global.Samples || (global.Samples = {}); + var Color = global.Color; + Samples.utils = { // Adapted from http://indiegamr.com/generate-repeatable-random-numbers-in-js/ srand: function(seed) { @@ -41,8 +49,8 @@ window.randomScalingFactor = function() { rand: function(min, max) { var seed = this._seed; - min = min === undefined? 0 : min; - max = max === undefined? 1 : max; + min = min === undefined ? 0 : min; + max = max === undefined ? 1 : max; this._seed = (seed * 9301 + 49297) % 233280; return min + (this._seed / 233280) * (max - min); }, @@ -59,7 +67,7 @@ window.randomScalingFactor = function() { var data = []; var i, value; - for (i=0; i 0) { + if (item[0].yLabel) { + title = item[0].yLabel; + } else if (data.labels.length > 0 && item[0].index < data.labels.length) { + title = data.labels[item[0].index]; + } + } + + return title; + }, + + label: function(item, data) { + var datasetLabel = data.datasets[item.datasetIndex].label || ''; + return datasetLabel + ': ' + item.xLabel; + } + }, + mode: 'index', + axis: 'y' + } +}); + module.exports = function(Chart) { - var helpers = Chart.helpers; - - Chart.defaults.bar = { - hover: { - mode: 'label' - }, - - scales: { - xAxes: [{ - type: 'category', - - // Specific to Bar Controller - categoryPercentage: 0.8, - barPercentage: 0.9, - - // grid line settings - gridLines: { - offsetGridLines: true - } - }], - yAxes: [{ - type: 'linear' - }] - } - }; - Chart.controllers.bar = Chart.DatasetController.extend({ - dataElementType: Chart.elements.Rectangle, + dataElementType: elements.Rectangle, initialize: function() { var me = this; @@ -45,13 +114,13 @@ module.exports = function(Chart) { update: function(reset) { var me = this; - var elements = me.getMeta().data; + var rects = me.getMeta().data; var i, ilen; me._ruler = me.getRuler(); - for (i = 0, ilen = elements.length; i < ilen; ++i) { - me.updateElement(elements[i], i, reset); + for (i = 0, ilen = rects.length; i < ilen; ++i) { + me.updateElement(rects[i], i, reset); } }, @@ -72,9 +141,9 @@ module.exports = function(Chart) { datasetLabel: dataset.label, label: chart.data.labels[index], borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, - backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), - borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), - borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), + borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) }; me.updateElementGeometry(rectangle, index, reset); @@ -96,11 +165,11 @@ module.exports = function(Chart) { var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); model.horizontal = horizontal; - model.base = reset? base : vpixels.base; - model.x = horizontal? reset? base : vpixels.head : ipixels.center; - model.y = horizontal? ipixels.center : reset? base : vpixels.head; - model.height = horizontal? ipixels.size : undefined; - model.width = horizontal? undefined : ipixels.size; + model.base = reset ? base : vpixels.base; + model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; + model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; + model.height = horizontal ? ipixels.size : undefined; + model.width = horizontal ? undefined : ipixels.size; }, /** @@ -140,7 +209,7 @@ module.exports = function(Chart) { var chart = me.chart; var scale = me.getIndexScale(); var stacked = scale.options.stacked; - var ilen = last === undefined? chart.data.datasets.length : last + 1; + var ilen = last === undefined ? chart.data.datasets.length : last + 1; var stacks = []; var i, meta; @@ -171,26 +240,23 @@ module.exports = function(Chart) { getRuler: function() { var me = this; var scale = me.getIndexScale(); - var options = scale.options; var stackCount = me.getStackCount(); - var fullSize = scale.isHorizontal()? scale.width : scale.height; - var tickSize = fullSize / scale.ticks.length; - var categorySize = tickSize * options.categoryPercentage; - var fullBarSize = categorySize / stackCount; - var barSize = fullBarSize * options.barPercentage; + var datasetIndex = me.index; + var pixels = []; + var isHorizontal = scale.isHorizontal(); + var start = isHorizontal ? scale.left : scale.top; + var end = start + (isHorizontal ? scale.width : scale.height); + var i, ilen; - barSize = Math.min( - helpers.getValueOrDefault(options.barThickness, barSize), - helpers.getValueOrDefault(options.maxBarThickness, Infinity)); + for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { + pixels.push(scale.getPixelForValue(null, i, datasetIndex)); + } return { + pixels: pixels, + start: start, + end: end, stackCount: stackCount, - tickSize: tickSize, - categorySize: categorySize, - categorySpacing: tickSize - categorySize, - fullBarSize: fullBarSize, - barSize: barSize, - barSpacing: fullBarSize - barSize, scale: scale }; }, @@ -205,7 +271,7 @@ module.exports = function(Chart) { var meta = me.getMeta(); var scale = me.getValueScale(); var datasets = chart.data.datasets; - var value = Number(datasets[datasetIndex].data[index]); + var value = scale.getRightValue(datasets[datasetIndex].data[index]); var stacked = scale.options.stacked; var stack = meta.stack; var start = 0; @@ -220,7 +286,7 @@ module.exports = function(Chart) { imeta.controller.getValueScaleId() === scale.id && chart.isDatasetVisible(i)) { - ivalue = Number(datasets[i].data[index]); + ivalue = scale.getRightValue(datasets[i].data[index]); if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { start += ivalue; } @@ -245,16 +311,45 @@ module.exports = function(Chart) { */ calculateBarIndexPixels: function(datasetIndex, index, ruler) { var me = this; - var scale = ruler.scale; - var isCombo = me.chart.isCombo; + var options = ruler.scale.options; var stackIndex = me.getStackIndex(datasetIndex); - var base = scale.getPixelForValue(null, index, datasetIndex, isCombo); - var size = ruler.barSize; + var pixels = ruler.pixels; + var base = pixels[index]; + var length = pixels.length; + var start = ruler.start; + var end = ruler.end; + var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size; - base -= isCombo? ruler.tickSize / 2 : 0; - base += ruler.fullBarSize * stackIndex; - base += ruler.categorySpacing / 2; - base += ruler.barSpacing / 2; + if (length === 1) { + leftSampleSize = base > start ? base - start : end - base; + rightSampleSize = base < end ? end - base : base - start; + } else { + if (index > 0) { + leftSampleSize = (base - pixels[index - 1]) / 2; + if (index === length - 1) { + rightSampleSize = leftSampleSize; + } + } + if (index < length - 1) { + rightSampleSize = (pixels[index + 1] - base) / 2; + if (index === 0) { + leftSampleSize = rightSampleSize; + } + } + } + + leftCategorySize = leftSampleSize * options.categoryPercentage; + rightCategorySize = rightSampleSize * options.categoryPercentage; + fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount; + size = fullBarSize * options.barPercentage; + + size = Math.min( + helpers.valueOrDefault(options.barThickness, size), + helpers.valueOrDefault(options.maxBarThickness, Infinity)); + + base -= leftCategorySize; + base += fullBarSize * stackIndex; + base += (fullBarSize - size) / 2; return { size: size, @@ -267,18 +362,17 @@ module.exports = function(Chart) { draw: function() { var me = this; var chart = me.chart; - var elements = me.getMeta().data; + var scale = me.getValueScale(); + var rects = me.getMeta().data; var dataset = me.getDataset(); - var ilen = elements.length; + var ilen = rects.length; var i = 0; - var d; helpers.canvas.clipArea(chart.ctx, chart.chartArea); - for (; i 0) { - if (tooltipItems[0].yLabel) { - title = tooltipItems[0].yLabel; - } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { - title = data.labels[tooltipItems[0].index]; - } - } - - return title; - }, - label: function(tooltipItem, data) { - var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; - return datasetLabel + ': ' + tooltipItem.xLabel; - } - } - } - }; - Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ /** * @private diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index abe807b3b..65c6fae01 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -1,46 +1,54 @@ 'use strict'; +var defaults = require('../core/core.defaults'); +var elements = require('../elements/index'); +var helpers = require('../helpers/index'); + +defaults._set('bubble', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // bubble should probably use a linear scale by default + position: 'bottom', + id: 'x-axis-0' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-0' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(item, data) { + var datasetLabel = data.datasets[item.datasetIndex].label || ''; + var dataPoint = data.datasets[item.datasetIndex].data[item.index]; + return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; + } + } + } +}); + + module.exports = function(Chart) { - var helpers = Chart.helpers; - - Chart.defaults.bubble = { - hover: { - mode: 'single' - }, - - scales: { - xAxes: [{ - type: 'linear', // bubble should probably use a linear scale by default - position: 'bottom', - id: 'x-axis-0' // need an ID so datasets can reference the scale - }], - yAxes: [{ - type: 'linear', - position: 'left', - id: 'y-axis-0' - }] - }, - - tooltips: { - callbacks: { - title: function() { - // Title doesn't make sense for scatter since we format the data as a point - return ''; - }, - label: function(tooltipItem, data) { - var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; - var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; - return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')'; - } - } - } - }; - Chart.controllers.bubble = Chart.DatasetController.extend({ + /** + * @protected + */ + dataElementType: elements.Point, - dataElementType: Chart.elements.Point, - + /** + * @protected + */ update: function(reset) { var me = this; var meta = me.getMeta(); @@ -52,71 +60,121 @@ module.exports = function(Chart) { }); }, + /** + * @protected + */ updateElement: function(point, index, reset) { var me = this; var meta = me.getMeta(); + var custom = point.custom || {}; var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); - - var custom = point.custom || {}; - var dataset = me.getDataset(); - var data = dataset.data[index]; - var pointElementOptions = me.chart.options.elements.point; + var options = me._resolveElementOptions(point, index); + var data = me.getDataset().data[index]; var dsIndex = me.index; - helpers.extend(point, { - // Utility - _xScale: xScale, - _yScale: yScale, - _datasetIndex: dsIndex, - _index: index, + var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); + var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); - // Desired view properties - _model: { - x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), - y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), - // Appearance - radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), - - // Tooltip - hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) - } - }); - - // Trick to reset the styles of the point - Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions); - - var model = point._model; - model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y)); + point._xScale = xScale; + point._yScale = yScale; + point._options = options; + point._datasetIndex = dsIndex; + point._index = index; + point._model = { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + hitRadius: options.hitRadius, + pointStyle: options.pointStyle, + radius: reset ? 0 : options.radius, + skip: custom.skip || isNaN(x) || isNaN(y), + x: x, + y: y, + }; point.pivot(); }, - getRadius: function(value) { - return value.r || this.chart.options.elements.point.radius; - }, - + /** + * @protected + */ setHoverStyle: function(point) { - var me = this; - Chart.DatasetController.prototype.setHoverStyle.call(me, point); - - // Radius - var dataset = me.chart.data.datasets[point._datasetIndex]; - var index = point._index; - var custom = point.custom || {}; var model = point._model; - model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]); + var options = point._options; + + model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor)); + model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); + model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); + model.radius = options.radius + options.hoverRadius; }, + /** + * @protected + */ removeHoverStyle: function(point) { - var me = this; - Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point); - - var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index]; - var custom = point.custom || {}; var model = point._model; + var options = point._options; - model.radius = custom.radius ? custom.radius : me.getRadius(dataVal); + model.backgroundColor = options.backgroundColor; + model.borderColor = options.borderColor; + model.borderWidth = options.borderWidth; + model.radius = options.radius; + }, + + /** + * @private + */ + _resolveElementOptions: function(point, index) { + var me = this; + var chart = me.chart; + var datasets = chart.data.datasets; + var dataset = datasets[me.index]; + var custom = point.custom || {}; + var options = chart.options.elements.point; + var resolve = helpers.options.resolve; + var data = dataset.data[index]; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + var keys = [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + 'hoverRadius', + 'hitRadius', + 'pointStyle' + ]; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve([ + custom[key], + dataset[key], + options[key] + ], context, index); + } + + // Custom radius resolution + values.radius = resolve([ + custom.radius, + data ? data.r : undefined, + dataset.radius, + options.radius + ], context, index); + + return values; } }); }; diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index 3d4780582..6028602c4 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -1,134 +1,133 @@ 'use strict'; +var defaults = require('../core/core.defaults'); +var elements = require('../elements/index'); +var helpers = require('../helpers/index'); + +defaults._set('doughnut', { + animation: { + // Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + // Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false + }, + hover: { + mode: 'single' + }, + legendCallback: function(chart) { + var text = []; + text.push('
    '); + + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + + if (datasets.length) { + for (var i = 0; i < datasets[0].data.length; ++i) { + text.push('
  • '); + if (labels[i]) { + text.push(labels[i]); + } + text.push('
  • '); + } + } + + text.push('
'); + return text.join(''); + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var ds = data.datasets[0]; + var arc = meta.data[i]; + var custom = arc && arc.custom || {}; + var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + var arcOpts = chart.options.elements.arc; + var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); + var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); + var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); + + return { + text: label, + fillStyle: fill, + strokeStyle: stroke, + lineWidth: bw, + hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + // toggle visibility of index if exists + if (meta.data[index]) { + meta.data[index].hidden = !meta.data[index].hidden; + } + } + + chart.update(); + } + }, + + // The percentage of the chart that we cut out of the middle. + cutoutPercentage: 50, + + // The rotation of the chart, where the first data arc begins. + rotation: Math.PI * -0.5, + + // The total circumference of the chart. + circumference: Math.PI * 2.0, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(tooltipItem, data) { + var dataLabel = data.labels[tooltipItem.index]; + var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + + if (helpers.isArray(dataLabel)) { + // show value on first line of multiline label + // need to clone because we are changing the value + dataLabel = dataLabel.slice(); + dataLabel[0] += value; + } else { + dataLabel += value; + } + + return dataLabel; + } + } + } +}); + +defaults._set('pie', helpers.clone(defaults.doughnut)); +defaults._set('pie', { + cutoutPercentage: 0 +}); + module.exports = function(Chart) { - var helpers = Chart.helpers, - defaults = Chart.defaults; - - defaults.doughnut = { - animation: { - // Boolean - Whether we animate the rotation of the Doughnut - animateRotate: true, - // Boolean - Whether we animate scaling the Doughnut from the centre - animateScale: false - }, - aspectRatio: 1, - hover: { - mode: 'single' - }, - legendCallback: function(chart) { - var text = []; - text.push('
    '); - - var data = chart.data; - var datasets = data.datasets; - var labels = data.labels; - - if (datasets.length) { - for (var i = 0; i < datasets[0].data.length; ++i) { - text.push('
  • '); - if (labels[i]) { - text.push(labels[i]); - } - text.push('
  • '); - } - } - - text.push('
'); - return text.join(''); - }, - legend: { - labels: { - generateLabels: function(chart) { - var data = chart.data; - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var meta = chart.getDatasetMeta(0); - var ds = data.datasets[0]; - var arc = meta.data[i]; - var custom = arc && arc.custom || {}; - var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; - var arcOpts = chart.options.elements.arc; - var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); - var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); - var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); - - return { - text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(ds.data[i]) || meta.data[i].hidden, - - // Extra data used for toggling the correct item - index: i - }; - }); - } - return []; - } - }, - - onClick: function(e, legendItem) { - var index = legendItem.index; - var chart = this.chart; - var i, ilen, meta; - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - // toggle visibility of index if exists - if (meta.data[index]) { - meta.data[index].hidden = !meta.data[index].hidden; - } - } - - chart.update(); - } - }, - - // The percentage of the chart that we cut out of the middle. - cutoutPercentage: 50, - - // The rotation of the chart, where the first data arc begins. - rotation: Math.PI * -0.5, - - // The total circumference of the chart. - circumference: Math.PI * 2.0, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(tooltipItem, data) { - var dataLabel = data.labels[tooltipItem.index]; - var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; - - if (helpers.isArray(dataLabel)) { - // show value on first line of multiline label - // need to clone because we are changing the value - dataLabel = dataLabel.slice(); - dataLabel[0] += value; - } else { - dataLabel += value; - } - - return dataLabel; - } - } - } - }; - - defaults.pie = helpers.clone(defaults.doughnut); - helpers.extend(defaults.pie, { - cutoutPercentage: 0 - }); - - Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ - dataElementType: Chart.elements.Arc, + dataElementType: elements.Arc, linkScales: helpers.noop, @@ -147,20 +146,17 @@ module.exports = function(Chart) { update: function(reset) { var me = this; - var chart = me.chart, - chartArea = chart.chartArea, - opts = chart.options, - arcOpts = opts.elements.arc, - availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth, - availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth, - minSize = Math.min(availableWidth, availableHeight), - offset = { - x: 0, - y: 0 - }, - meta = me.getMeta(), - cutoutPercentage = opts.cutoutPercentage, - circumference = opts.circumference; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var arcOpts = opts.elements.arc; + var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; + var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; + var minSize = Math.min(availableWidth, availableHeight); + var offset = {x: 0, y: 0}; + var meta = me.getMeta(); + var cutoutPercentage = opts.cutoutPercentage; + var circumference = opts.circumference; // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc if (circumference < Math.PI * 2.0) { @@ -169,7 +165,7 @@ module.exports = function(Chart) { var endAngle = startAngle + circumference; var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; - var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); + var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); @@ -200,19 +196,19 @@ module.exports = function(Chart) { updateElement: function(arc, index, reset) { var me = this; - var chart = me.chart, - chartArea = chart.chartArea, - opts = chart.options, - animationOpts = opts.animation, - centerX = (chartArea.left + chartArea.right) / 2, - centerY = (chartArea.top + chartArea.bottom) / 2, - startAngle = opts.rotation, // non reset case handled later - endAngle = opts.rotation, // non reset case handled later - dataset = me.getDataset(), - circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), - innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, - outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, - valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var animationOpts = opts.animation; + var centerX = (chartArea.left + chartArea.right) / 2; + var centerY = (chartArea.top + chartArea.bottom) / 2; + var startAngle = opts.rotation; // non reset case handled later + var endAngle = opts.rotation; // non reset case handled later + var dataset = me.getDataset(); + var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); + var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; + var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; + var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; helpers.extend(arc, { // Utility @@ -283,16 +279,16 @@ module.exports = function(Chart) { }, // gets the max border or hover width to properly scale pie charts - getMaxBorderWidth: function(elements) { - var max = 0, - index = this.index, - length = elements.length, - borderWidth, - hoverWidth; + getMaxBorderWidth: function(arcs) { + var max = 0; + var index = this.index; + var length = arcs.length; + var borderWidth; + var hoverWidth; for (var i = 0; i < length; i++) { - borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; - hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; + borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; + hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; max = borderWidth > max ? borderWidth : max; max = hoverWidth > max ? hoverWidth : max; diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 1a26a04b7..9d9206d5c 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -1,38 +1,40 @@ 'use strict'; +var defaults = require('../core/core.defaults'); +var elements = require('../elements/index'); +var helpers = require('../helpers/index'); + +defaults._set('line', { + showLines: true, + spanGaps: false, + + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + id: 'x-axis-0' + }], + yAxes: [{ + type: 'linear', + id: 'y-axis-0' + }] + } +}); + module.exports = function(Chart) { - var helpers = Chart.helpers; - - Chart.defaults.line = { - showLines: true, - spanGaps: false, - - hover: { - mode: 'label' - }, - - scales: { - xAxes: [{ - type: 'category', - id: 'x-axis-0' - }], - yAxes: [{ - type: 'linear', - id: 'y-axis-0' - }] - } - }; - function lineEnabled(dataset, options) { - return helpers.getValueOrDefault(dataset.showLine, options.showLines); + return helpers.valueOrDefault(dataset.showLine, options.showLines); } Chart.controllers.line = Chart.DatasetController.extend({ - datasetElementType: Chart.elements.Line, + datasetElementType: elements.Line, - dataElementType: Chart.elements.Point, + dataElementType: elements.Point, update: function(reset) { var me = this; @@ -67,7 +69,7 @@ module.exports = function(Chart) { // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 // This option gives lines the ability to span gaps spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, - tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), + tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), @@ -76,15 +78,15 @@ module.exports = function(Chart) { borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), - steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), - cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), + steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), + cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), }; line.pivot(); } // Update Points - for (i=0, ilen=points.length; i'); + + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + + if (datasets.length) { + for (var i = 0; i < datasets[0].data.length; ++i) { + text.push('
  • '); + if (labels[i]) { + text.push(labels[i]); + } + text.push('
  • '); + } + } + + text.push(''); + return text.join(''); + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var ds = data.datasets[0]; + var arc = meta.data[i]; + var custom = arc.custom || {}; + var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + var arcOpts = chart.options.elements.arc; + var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); + var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); + var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); + + return { + text: label, + fillStyle: fill, + strokeStyle: stroke, + lineWidth: bw, + hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + meta.data[index].hidden = !meta.data[index].hidden; + } + + chart.update(); + } + }, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(item, data) { + return data.labels[item.index] + ': ' + item.yLabel; + } + } + } +}); + module.exports = function(Chart) { - var helpers = Chart.helpers; - - Chart.defaults.polarArea = { - - scale: { - type: 'radialLinear', - angleLines: { - display: false - }, - gridLines: { - circular: true - }, - pointLabels: { - display: false - }, - ticks: { - beginAtZero: true - } - }, - - // Boolean - Whether to animate the rotation of the chart - animation: { - animateRotate: true, - animateScale: true - }, - - startAngle: -0.5 * Math.PI, - aspectRatio: 1, - legendCallback: function(chart) { - var text = []; - text.push('
      '); - - var data = chart.data; - var datasets = data.datasets; - var labels = data.labels; - - if (datasets.length) { - for (var i = 0; i < datasets[0].data.length; ++i) { - text.push('
    • '); - if (labels[i]) { - text.push(labels[i]); - } - text.push('
    • '); - } - } - - text.push('
    '); - return text.join(''); - }, - legend: { - labels: { - generateLabels: function(chart) { - var data = chart.data; - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var meta = chart.getDatasetMeta(0); - var ds = data.datasets[0]; - var arc = meta.data[i]; - var custom = arc.custom || {}; - var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; - var arcOpts = chart.options.elements.arc; - var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); - var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); - var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); - - return { - text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(ds.data[i]) || meta.data[i].hidden, - - // Extra data used for toggling the correct item - index: i - }; - }); - } - return []; - } - }, - - onClick: function(e, legendItem) { - var index = legendItem.index; - var chart = this.chart; - var i, ilen, meta; - - for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - meta.data[index].hidden = !meta.data[index].hidden; - } - - chart.update(); - } - }, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(tooltipItem, data) { - return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; - } - } - } - }; - Chart.controllers.polarArea = Chart.DatasetController.extend({ - dataElementType: Chart.elements.Arc, + dataElementType: elements.Arc, linkScales: helpers.noop, @@ -145,7 +145,6 @@ module.exports = function(Chart) { var opts = chart.options; var animationOpts = opts.animation; var scale = chart.scale; - var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var labels = chart.data.labels; var circumference = me.calculateCircumference(dataset.data[index]); @@ -184,7 +183,7 @@ module.exports = function(Chart) { outerRadius: reset ? resetRadius : distance, startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, - label: getValueAtIndexOrDefault(labels, index, labels[index]) + label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) } }); diff --git a/src/controllers/controller.radar.js b/src/controllers/controller.radar.js index 2f73b3cc1..5de4e4ede 100644 --- a/src/controllers/controller.radar.js +++ b/src/controllers/controller.radar.js @@ -1,26 +1,27 @@ 'use strict'; +var defaults = require('../core/core.defaults'); +var elements = require('../elements/index'); +var helpers = require('../helpers/index'); + +defaults._set('radar', { + scale: { + type: 'radialLinear' + }, + elements: { + line: { + tension: 0 // no bezier in radar + } + } +}); + module.exports = function(Chart) { - var helpers = Chart.helpers; - - Chart.defaults.radar = { - aspectRatio: 1, - scale: { - type: 'radialLinear' - }, - elements: { - line: { - tension: 0 // no bezier in radar - } - } - }; - Chart.controllers.radar = Chart.DatasetController.extend({ - datasetElementType: Chart.elements.Line, + datasetElementType: elements.Line, - dataElementType: Chart.elements.Point, + dataElementType: elements.Point, linkScales: helpers.noop, @@ -49,7 +50,7 @@ module.exports = function(Chart) { // Model _model: { // Appearance - tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), + tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), @@ -99,15 +100,15 @@ module.exports = function(Chart) { y: reset ? scale.yCenter : pointPosition.y, // Appearance - tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), - radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), - backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), - borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), - borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), - pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), + tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), + radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), + borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), + pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), // Tooltip - hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) + hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) } }); @@ -145,10 +146,10 @@ module.exports = function(Chart) { var index = point._index; var model = point._model; - model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); - model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); - model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); - model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); + model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); + model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(point) { @@ -158,10 +159,10 @@ module.exports = function(Chart) { var model = point._model; var pointElementOptions = this.chart.options.elements.point; - model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); - model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); - model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); - model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); + model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); + model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); + model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); + model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); } }); }; diff --git a/src/controllers/controller.scatter.js b/src/controllers/controller.scatter.js new file mode 100644 index 000000000..b2e2cf1f7 --- /dev/null +++ b/src/controllers/controller.scatter.js @@ -0,0 +1,42 @@ +'use strict'; + +var defaults = require('../core/core.defaults'); + +defaults._set('scatter', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + id: 'x-axis-1', // need an ID so datasets can reference the scale + type: 'linear', // scatter should not use a category axis + position: 'bottom' + }], + yAxes: [{ + id: 'y-axis-1', + type: 'linear', + position: 'left' + }] + }, + + showLines: false, + + tooltips: { + callbacks: { + title: function() { + return ''; // doesn't make sense for scatter since data are formatted as a point + }, + label: function(item) { + return '(' + item.xLabel + ', ' + item.yLabel + ')'; + } + } + } +}); + +module.exports = function(Chart) { + + // Scatter charts use line controllers + Chart.controllers.scatter = Chart.controllers.line; + +}; diff --git a/src/core/core.animation.js b/src/core/core.animation.js index ad450df8e..af746588e 100644 --- a/src/core/core.animation.js +++ b/src/core/core.animation.js @@ -1,18 +1,22 @@ /* global window: false */ 'use strict'; -module.exports = function(Chart) { +var defaults = require('./core.defaults'); +var Element = require('./core.element'); +var helpers = require('../helpers/index'); - var helpers = Chart.helpers; - - Chart.defaults.global.animation = { +defaults._set('global', { + animation: { duration: 1000, easing: 'easeOutQuart', onProgress: helpers.noop, onComplete: helpers.noop - }; + } +}); - Chart.Animation = Chart.Element.extend({ +module.exports = function(Chart) { + + Chart.Animation = Element.extend({ chart: null, // the animation associated chart instance currentStep: 0, // the current animation step numSteps: 60, // default number of steps @@ -45,7 +49,7 @@ module.exports = function(Chart) { chart.animating = true; } - for (i=0, ilen=animations.length; i < ilen; ++i) { + for (i = 0, ilen = animations.length; i < ilen; ++i) { if (animations[i].chart === chart) { animations[i] = animation; return; diff --git a/src/core/core.canvasHelpers.js b/src/core/core.canvasHelpers.js deleted file mode 100644 index 610095122..000000000 --- a/src/core/core.canvasHelpers.js +++ /dev/null @@ -1,150 +0,0 @@ -'use strict'; - -module.exports = function(Chart) { - // Global Chart canvas helpers object for drawing items to canvas - var helpers = Chart.canvasHelpers = {}; - - helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { - var type, edgeLength, xOffset, yOffset, height, size; - - if (typeof pointStyle === 'object') { - type = pointStyle.toString(); - if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { - ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2, pointStyle.width, pointStyle.height); - return; - } - } - - if (isNaN(radius) || radius <= 0) { - return; - } - - switch (pointStyle) { - // Default includes circle - default: - ctx.beginPath(); - ctx.arc(x, y, radius, 0, Math.PI * 2); - ctx.closePath(); - ctx.fill(); - break; - case 'triangle': - ctx.beginPath(); - edgeLength = 3 * radius / Math.sqrt(3); - height = edgeLength * Math.sqrt(3) / 2; - ctx.moveTo(x - edgeLength / 2, y + height / 3); - ctx.lineTo(x + edgeLength / 2, y + height / 3); - ctx.lineTo(x, y - 2 * height / 3); - ctx.closePath(); - ctx.fill(); - break; - case 'rect': - size = 1 / Math.SQRT2 * radius; - ctx.beginPath(); - ctx.fillRect(x - size, y - size, 2 * size, 2 * size); - ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); - break; - case 'rectRounded': - var offset = radius / Math.SQRT2; - var leftX = x - offset; - var topY = y - offset; - var sideSize = Math.SQRT2 * radius; - Chart.helpers.drawRoundedRectangle(ctx, leftX, topY, sideSize, sideSize, radius / 2); - ctx.fill(); - break; - case 'rectRot': - size = 1 / Math.SQRT2 * radius; - ctx.beginPath(); - ctx.moveTo(x - size, y); - ctx.lineTo(x, y + size); - ctx.lineTo(x + size, y); - ctx.lineTo(x, y - size); - ctx.closePath(); - ctx.fill(); - break; - case 'cross': - ctx.beginPath(); - ctx.moveTo(x, y + radius); - ctx.lineTo(x, y - radius); - ctx.moveTo(x - radius, y); - ctx.lineTo(x + radius, y); - ctx.closePath(); - break; - case 'crossRot': - ctx.beginPath(); - xOffset = Math.cos(Math.PI / 4) * radius; - yOffset = Math.sin(Math.PI / 4) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x - xOffset, y + yOffset); - ctx.lineTo(x + xOffset, y - yOffset); - ctx.closePath(); - break; - case 'star': - ctx.beginPath(); - ctx.moveTo(x, y + radius); - ctx.lineTo(x, y - radius); - ctx.moveTo(x - radius, y); - ctx.lineTo(x + radius, y); - xOffset = Math.cos(Math.PI / 4) * radius; - yOffset = Math.sin(Math.PI / 4) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x - xOffset, y + yOffset); - ctx.lineTo(x + xOffset, y - yOffset); - ctx.closePath(); - break; - case 'line': - ctx.beginPath(); - ctx.moveTo(x - radius, y); - ctx.lineTo(x + radius, y); - ctx.closePath(); - break; - case 'dash': - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(x + radius, y); - ctx.closePath(); - break; - } - - ctx.stroke(); - }; - - helpers.clipArea = function(ctx, clipArea) { - ctx.save(); - ctx.beginPath(); - ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top); - ctx.clip(); - }; - - helpers.unclipArea = function(ctx) { - ctx.restore(); - }; - - helpers.lineTo = function(ctx, previous, target, flip) { - if (target.steppedLine) { - if (target.steppedLine === 'after') { - ctx.lineTo(previous.x, target.y); - } else { - ctx.lineTo(target.x, previous.y); - } - ctx.lineTo(target.x, target.y); - return; - } - - if (!target.tension) { - ctx.lineTo(target.x, target.y); - return; - } - - ctx.bezierCurveTo( - flip? previous.controlPointPreviousX : previous.controlPointNextX, - flip? previous.controlPointPreviousY : previous.controlPointNextY, - flip? target.controlPointNextX : target.controlPointPreviousX, - flip? target.controlPointNextY : target.controlPointPreviousY, - target.x, - target.y); - }; - - Chart.helpers.canvas = helpers; -}; diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 71eb3bb62..03dfedb71 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -1,10 +1,12 @@ 'use strict'; -module.exports = function(Chart) { +var defaults = require('./core.defaults'); +var helpers = require('../helpers/index'); +var Interaction = require('./core.interaction'); +var platform = require('../platforms/platform'); - var helpers = Chart.helpers; +module.exports = function(Chart) { var plugins = Chart.plugins; - var platform = Chart.platform; // Create a dictionary of chart types, to allow for extension of existing types Chart.types = {}; @@ -29,8 +31,8 @@ module.exports = function(Chart) { data.labels = data.labels || []; config.options = helpers.configMerge( - Chart.defaults.global, - Chart.defaults[config.type], + defaults.global, + defaults[config.type], config.options || {}); return config; @@ -80,7 +82,7 @@ module.exports = function(Chart) { me.config = config; me.width = width; me.height = height; - me.aspectRatio = height? width / height : null; + me.aspectRatio = height ? width / height : null; me.options = config.options; me._bufferedRender = false; @@ -93,7 +95,7 @@ module.exports = function(Chart) { * @private */ me.chart = me; - me.controller = me; // chart.chart.controller #inception + me.controller = me; // chart.chart.controller #inception // Add the chart instance to the global namespace Chart.instances[me.id] = me; @@ -130,7 +132,7 @@ module.exports = function(Chart) { // Before init plugin notification plugins.notify(me, 'beforeInit'); - helpers.retinaScale(me); + helpers.retinaScale(me, me.options.devicePixelRatio); me.bindEvents(); @@ -151,7 +153,7 @@ module.exports = function(Chart) { }, clear: function() { - helpers.clear(this); + helpers.canvas.clear(this); return this; }, @@ -169,8 +171,10 @@ module.exports = function(Chart) { // the canvas render width and height will be casted to integers so make sure that // the canvas display style uses the same integer values to avoid blurring effect. - var newWidth = Math.floor(helpers.getMaximumWidth(canvas)); - var newHeight = Math.floor(aspectRatio? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)); + + // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased + var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); + var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); if (me.width === newWidth && me.height === newHeight) { return; @@ -181,7 +185,7 @@ module.exports = function(Chart) { canvas.style.width = newWidth + 'px'; canvas.style.height = newHeight + 'px'; - helpers.retinaScale(me); + helpers.retinaScale(me, options.devicePixelRatio); if (!silent) { // Notify any plugins about the resize @@ -247,7 +251,7 @@ module.exports = function(Chart) { helpers.each(items, function(item) { var scaleOptions = item.options; - var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); + var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); if (!scaleClass) { return; @@ -265,6 +269,7 @@ module.exports = function(Chart) { }); scales[scale.id] = scale; + scale.mergeTicksOptions(); // TODO(SB): I think we should be able to remove this custom case (options.scale) // and consider it as a regular scale part of the "scales"" map only! This would @@ -284,9 +289,13 @@ module.exports = function(Chart) { helpers.each(me.data.datasets, function(dataset, datasetIndex) { var meta = me.getDatasetMeta(datasetIndex); - if (!meta.type) { - meta.type = dataset.type || me.config.type; + var type = dataset.type || me.config.type; + + if (meta.type && meta.type !== type) { + me.destroyDatasetMeta(datasetIndex); + meta = me.getDatasetMeta(datasetIndex); } + meta.type = type; types.push(meta.type); @@ -303,15 +312,6 @@ module.exports = function(Chart) { } }, me); - if (types.length > 1) { - for (var i = 1; i < types.length; i++) { - if (types[i] !== types[i - 1]) { - me.isCombo = true; - break; - } - } - } - return newControllers; }, @@ -334,9 +334,17 @@ module.exports = function(Chart) { this.tooltip.initialize(); }, - update: function(animationDuration, lazy) { + update: function(config) { var me = this; + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + updateConfig(me); if (plugins.notify(me, 'beforeUpdate') === false) { @@ -368,11 +376,12 @@ module.exports = function(Chart) { if (me._bufferedRender) { me._bufferedRequest = { - lazy: lazy, - duration: animationDuration + duration: config.duration, + easing: config.easing, + lazy: config.lazy }; } else { - me.render(animationDuration, lazy); + me.render(config); } }, @@ -442,9 +451,20 @@ module.exports = function(Chart) { plugins.notify(me, 'afterDatasetUpdate', [args]); }, - render: function(duration, lazy) { + render: function(config) { var me = this; + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + var duration = config.duration; + var lazy = config.lazy; + if (plugins.notify(me, 'beforeRender') === false) { return; } @@ -458,10 +478,10 @@ module.exports = function(Chart) { if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { var animation = new Chart.Animation({ numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps - easing: animationOptions.easing, + easing: config.easing || animationOptions.easing, render: function(chart, animationObject) { - var easingFunction = helpers.easingEffects[animationObject.easing]; + var easingFunction = helpers.easing.effects[animationObject.easing]; var currentStep = animationObject.currentStep; var stepDecimal = currentStep / animationObject.numSteps; @@ -488,7 +508,7 @@ module.exports = function(Chart) { me.clear(); - if (easingValue === undefined || easingValue === null) { + if (helpers.isNullOrUndef(easingValue)) { easingValue = 1; } @@ -521,7 +541,7 @@ module.exports = function(Chart) { transition: function(easingValue) { var me = this; - for (var i=0, ilen=(me.data.datasets || []).length; i= 0; --i) { + for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { if (me.isDatasetVisible(i)) { me.drawDataset(i, easingValue); } @@ -578,19 +598,19 @@ module.exports = function(Chart) { // Get the single element that was clicked on // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw getElementAtEvent: function(e) { - return Chart.Interaction.modes.single(this, e); + return Interaction.modes.single(this, e); }, getElementsAtEvent: function(e) { - return Chart.Interaction.modes.label(this, e, {intersect: true}); + return Interaction.modes.label(this, e, {intersect: true}); }, getElementsAtXAxis: function(e) { - return Chart.Interaction.modes['x-axis'](this, e, {intersect: true}); + return Interaction.modes['x-axis'](this, e, {intersect: true}); }, getElementsAtEventForMode: function(e, mode, options) { - var method = Chart.Interaction.modes[mode]; + var method = Interaction.modes[mode]; if (typeof method === 'function') { return method(this, e, options); } @@ -599,7 +619,7 @@ module.exports = function(Chart) { }, getDatasetAtEvent: function(e) { - return Chart.Interaction.modes.dataset(this, e, {intersect: true}); + return Interaction.modes.dataset(this, e, {intersect: true}); }, getDatasetMeta: function(datasetIndex) { @@ -627,7 +647,7 @@ module.exports = function(Chart) { getVisibleDatasetCount: function() { var count = 0; - for (var i = 0, ilen = this.data.datasets.length; i