mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-04 23:44:05 +01:00
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/dist
|
||||
/docs/index.md
|
||||
/gh-pages
|
||||
/jsdoc
|
||||
/node_modules
|
||||
.DS_Store
|
||||
.idea
|
||||
|
||||
12
.travis.yml
12
.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)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Chart.js
|
||||
|
||||
[](https://travis-ci.org/chartjs/Chart.js) [](https://codeclimate.com/github/nnnick/Chart.js) [](https://coveralls.io/github/chartjs/Chart.js?branch=master)
|
||||
|
||||
[](https://chart-js-automation.herokuapp.com/)
|
||||
[](https://travis-ci.org/chartjs/Chart.js) [](https://codeclimate.com/github/chartjs/Chart.js) [](https://coveralls.io/github/chartjs/Chart.js?branch=master) [](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
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Chart.js
|
||||
|
||||
[](https://chart-js-automation.herokuapp.com/)
|
||||
[](https://chart-js-automation.herokuapp.com/)
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, {
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
If this is a function, it must return a moment.js object given the appropriate data value.
|
||||
|
||||
@@ -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, {
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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`.
|
||||
The default horizontal bar configuration is specified in `Chart.defaults.horizontalBar`.
|
||||
|
||||
@@ -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: <Number>,
|
||||
|
||||
// Radius of bubble. This is not scaled.
|
||||
// Bubble radius in pixels (not scaled).
|
||||
r: <Number>
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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'
|
||||
]
|
||||
};
|
||||
```
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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, {
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
- [1.x Documentation](https://github.com/chartjs/Chart.js/tree/v1.1.1/docs)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
* [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.
|
||||
|
||||
13
docs/general/device-pixel-ratio.md
Normal file
13
docs/general/device-pixel-ratio.md
Normal file
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
|
||||
|
||||
48
docs/general/options.md
Normal file
48
docs/general/options.md
Normal file
@@ -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.
|
||||
@@ -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
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"></script>
|
||||
```
|
||||
|
||||
Now, we can create a chart. We add a script to our page:
|
||||
|
||||
@@ -2,22 +2,27 @@
|
||||
Chart.js can be installed via npm or bower. It is recommended to get Chart.js this way.
|
||||
|
||||
## npm
|
||||
[](https://npmjs.com/package/chart.js)
|
||||
|
||||
```bash
|
||||
npm install chart.js --save
|
||||
```
|
||||
|
||||
## Bower
|
||||
[](https://libraries.io/bower/chartjs)
|
||||
|
||||
```bash
|
||||
bower install chart.js --save
|
||||
```
|
||||
|
||||
## CDN
|
||||
[](https://cdnjs.com/libraries/Chart.js)
|
||||
|
||||
or just use these [Chart.js CDN](https://cdnjs.com/libraries/Chart.js) links.
|
||||
|
||||
|
||||
## Github
|
||||
[](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.
|
||||
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.
|
||||
|
||||
@@ -12,7 +12,7 @@ Library Features
|
||||
| Extendable to Custom Charts | ✓ | ✓ | | |
|
||||
| Supports Modern Browsers | ✓ | ✓ | ✓ | ✓ |
|
||||
| Extensive Documentation | ✓ | ✓ | ✓ | ✓ |
|
||||
| Open Source | ✓ | ✓ | ✓ | ✓ |
|
||||
| Open Source | ✓ | ✓ | | ✓ |
|
||||
|
||||
Built in Chart Types
|
||||
|
||||
|
||||
@@ -13,11 +13,12 @@ In addition, many charts can be found on the [npm registry](https://www.npmjs.co
|
||||
|
||||
## Plugins
|
||||
|
||||
- <a href="https://github.com/chartjs/chartjs-plugin-annotation" target="_blank">chartjs-plugin-annotation.js</a> - Draw lines and boxes on chart area.
|
||||
- <a href="https://github.com/chartjs/chartjs-plugin-deferred" target="_blank">chartjs-plugin-deferred.js</a> - Defer initial chart update until chart scrolls into viewport.
|
||||
- <a href="https://github.com/compwright/chartjs-plugin-draggable" target="_blank">chartjs-plugin-draggable.js</a> - Makes select chart elements draggable with the mouse.
|
||||
- <a href="https://github.com/y-takey/chartjs-plugin-stacked100" target="_blank">chartjs-plugin-stacked100.js</a> - Draw 100% stacked bar chart.
|
||||
- <a href="https://github.com/chartjs/chartjs-plugin-zoom" target="_blank">chartjs-plugin-zoom.js</a> - Enable zooming and panning on charts.
|
||||
- <a href="https://github.com/chartjs/chartjs-plugin-annotation" target="_blank">chartjs-plugin-annotation</a> - Draws lines and boxes on chart area.
|
||||
- <a href="https://github.com/chartjs/chartjs-plugin-datalabels" target="_blank">chartjs-plugin-datalabels</a> - Displays labels on data for any type of charts.
|
||||
- <a href="https://github.com/chartjs/chartjs-plugin-deferred" target="_blank">chartjs-plugin-deferred</a> - Defers initial chart update until chart scrolls into viewport.
|
||||
- <a href="https://github.com/compwright/chartjs-plugin-draggable" target="_blank">chartjs-plugin-draggable</a> - Makes select chart elements draggable with the mouse.
|
||||
- <a href="https://github.com/y-takey/chartjs-plugin-stacked100" target="_blank">chartjs-plugin-stacked100</a> - Draws 100% stacked bar chart.
|
||||
- <a href="https://github.com/chartjs/chartjs-plugin-zoom" target="_blank">chartjs-plugin-zoom</a> - 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
|
||||
- <a href="https://github.com/apertureless/vue-chartjs/" target="_blank">vue-chartjs</a>
|
||||
|
||||
### Java
|
||||
- <a href="https://github.com/mdewilde/chart/" target="_blank">Chart.java</a>
|
||||
|
||||
41
gulpfile.js
41
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
|
||||
|
||||
50
package.json
50
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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0, ilen=datasets.length; i<ilen; ++i) {
|
||||
for (i = 0, ilen = datasets.length; i < ilen; ++i) {
|
||||
meta = chart.getDatasetMeta(i).$filler;
|
||||
if (meta) {
|
||||
dataset = datasets[i];
|
||||
|
||||
@@ -35,11 +35,11 @@
|
||||
};
|
||||
|
||||
function generateData(config) {
|
||||
return utils.numbers(utils.merge(inputs, config || {}));
|
||||
return utils.numbers(Chart.helpers.merge(inputs, config || {}));
|
||||
}
|
||||
|
||||
function generateLabels(config) {
|
||||
return utils.months(utils.merge({
|
||||
return utils.months(Chart.helpers.merge({
|
||||
count: inputs.count,
|
||||
section: 3
|
||||
}, config || {}));
|
||||
@@ -85,7 +85,7 @@
|
||||
fill: boundary
|
||||
}]
|
||||
},
|
||||
options: utils.merge(options, {
|
||||
options: Chart.helpers.merge(options, {
|
||||
title: {
|
||||
text: 'fill: ' + boundary,
|
||||
display: true
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<script src="samples.js"></script>
|
||||
<script src="utils.js"></script>
|
||||
<title>Chart.js samples</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -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: [{
|
||||
|
||||
104
samples/scales/time/financial.html
Normal file
104
samples/scales/time/financial.html
Normal file
@@ -0,0 +1,104 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
|
||||
<script src="../../../dist/Chart.js"></script>
|
||||
<script src="../../utils.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:1000px">
|
||||
<canvas id="chart1"></canvas>
|
||||
<div>
|
||||
<br>
|
||||
<br>
|
||||
Chart Type:
|
||||
<select id="type">
|
||||
<option value="line">Line</option>
|
||||
<option value="bar">Bar</option>
|
||||
</select>
|
||||
<button id="update">update</button>
|
||||
<script>
|
||||
function randomNumber(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function randomBar(date, lastClose) {
|
||||
var open = randomNumber(lastClose * .95, lastClose * 1.05);
|
||||
var close = randomNumber(open * .95, open * 1.05);
|
||||
var high = randomNumber(Math.max(open, close), Math.max(open, close) * 1.1);
|
||||
var low = randomNumber(Math.min(open, close) * .9, Math.min(open, close));
|
||||
return {
|
||||
t: date.valueOf(),
|
||||
y: close
|
||||
};
|
||||
}
|
||||
|
||||
var dateFormat = 'MMMM DD YYYY';
|
||||
var date = moment('April 01 2017', dateFormat);
|
||||
var data = [randomBar(date, 30)];
|
||||
var labels = [date];
|
||||
while (data.length < 60) {
|
||||
date = date.clone().add(1, 'd');
|
||||
if (date.isoWeekday() <= 5) {
|
||||
data.push(randomBar(date, data[data.length - 1].y));
|
||||
labels.push(date);
|
||||
}
|
||||
}
|
||||
|
||||
var ctx = document.getElementById("chart1").getContext("2d");
|
||||
ctx.canvas.width = 1000;
|
||||
ctx.canvas.height = 300;
|
||||
var cfg = {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: "CHRT - Chart.js Corporation",
|
||||
data: data,
|
||||
type: 'line',
|
||||
pointRadius: 0,
|
||||
fill: false,
|
||||
lineTension: 0,
|
||||
borderWidth: 2
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
distribution: 'series',
|
||||
ticks: {
|
||||
source: 'labels'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Closing price ($)'
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
var chart = new Chart(ctx, cfg);
|
||||
|
||||
document.getElementById('update').addEventListener('click', function() {
|
||||
var type = document.getElementById('type').value;
|
||||
chart.config.data.datasets[0].type = type;
|
||||
chart.update();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</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() {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<button id="removeData">Remove Data</button>
|
||||
<script>
|
||||
var timeFormat = 'MM/DD/YYYY HH:mm';
|
||||
|
||||
|
||||
function newDate(days) {
|
||||
return moment().add(days, 'd').toDate();
|
||||
}
|
||||
@@ -46,12 +46,12 @@
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [ // Date Objects
|
||||
newDate(0),
|
||||
newDate(1),
|
||||
newDate(2),
|
||||
newDate(3),
|
||||
newDate(4),
|
||||
newDate(5),
|
||||
newDate(0),
|
||||
newDate(1),
|
||||
newDate(2),
|
||||
newDate(3),
|
||||
newDate(4),
|
||||
newDate(5),
|
||||
newDate(6)
|
||||
],
|
||||
datasets: [{
|
||||
@@ -60,12 +60,12 @@
|
||||
borderColor: window.chartColors.red,
|
||||
fill: false,
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor()
|
||||
],
|
||||
}, {
|
||||
@@ -74,12 +74,12 @@
|
||||
borderColor: window.chartColors.blue,
|
||||
fill: false,
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor()
|
||||
],
|
||||
}, {
|
||||
|
||||
129
samples/scriptable/bubble.html
Normal file
129
samples/scriptable/bubble.html
Normal file
@@ -0,0 +1,129 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Scriptable > Bubble | Chart.js sample</title>
|
||||
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||
<script src="../../dist/Chart.js"></script>
|
||||
<script src="../utils.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class="wrapper"><canvas id="chart-0"></canvas></div>
|
||||
<div class="toolbar">
|
||||
<button onclick="randomize(this)">Randomize</button>
|
||||
<button onclick="addDataset(this)">Add Dataset</button>
|
||||
<button onclick="removeDataset(this)">Remove Dataset</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var DATA_COUNT = 16;
|
||||
var MIN_XY = -150;
|
||||
var MAX_XY = 100;
|
||||
|
||||
var presets = window.chartColors;
|
||||
var utils = Samples.utils;
|
||||
|
||||
utils.srand(110);
|
||||
|
||||
function colorize(opaque, context) {
|
||||
var value = context.dataset.data[context.dataIndex];
|
||||
var x = value.x / 100;
|
||||
var y = value.y / 100;
|
||||
var r = x < 0 && y < 0 ? 250 : x < 0 ? 150 : y < 0 ? 50 : 0;
|
||||
var g = x < 0 && y < 0 ? 0 : x < 0 ? 50 : y < 0 ? 150 : 250;
|
||||
var b = x < 0 && y < 0 ? 0 : x > 0 && y > 0 ? 250 : 150;
|
||||
var a = opaque ? 1 : 0.5 * value.v / 1000;
|
||||
|
||||
return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
|
||||
}
|
||||
|
||||
function generateData() {
|
||||
var data = [];
|
||||
var i;
|
||||
|
||||
for (i = 0; i < DATA_COUNT; ++i) {
|
||||
data.push({
|
||||
x: utils.rand(MIN_XY, MAX_XY),
|
||||
y: utils.rand(MIN_XY, MAX_XY),
|
||||
v: utils.rand(0, 1000)
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
var data = {
|
||||
datasets: [{
|
||||
data: generateData()
|
||||
}, {
|
||||
data: generateData()
|
||||
}]
|
||||
};
|
||||
|
||||
var options = {
|
||||
aspectRatio: 1,
|
||||
legend: false,
|
||||
tooltips: false,
|
||||
|
||||
elements: {
|
||||
point: {
|
||||
backgroundColor: colorize.bind(null, false),
|
||||
|
||||
borderColor: colorize.bind(null, true),
|
||||
|
||||
borderWidth: function(context) {
|
||||
return Math.min(Math.max(1, context.datasetIndex + 1), 8);
|
||||
},
|
||||
|
||||
hoverBackgroundColor: 'transparent',
|
||||
|
||||
hoverBorderColor: function(context) {
|
||||
return utils.color(context.datasetIndex);
|
||||
},
|
||||
|
||||
hoverBorderWidth: function(context) {
|
||||
var value = context.dataset.data[context.dataIndex];
|
||||
return Math.round(8 * value.v / 1000);
|
||||
},
|
||||
|
||||
radius: function(context) {
|
||||
var value = context.dataset.data[context.dataIndex];
|
||||
var size = context.chart.width;
|
||||
var base = Math.abs(value.v) / 1000;
|
||||
return (size / 24) * base;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var chart = new Chart('chart-0', {
|
||||
type: 'bubble',
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
|
||||
function randomize() {
|
||||
chart.data.datasets.forEach(function(dataset) {
|
||||
dataset.data = generateData()
|
||||
});
|
||||
chart.update();
|
||||
}
|
||||
|
||||
function addDataset() {
|
||||
chart.data.datasets.push({
|
||||
data: generateData()
|
||||
});
|
||||
chart.update();
|
||||
}
|
||||
|
||||
function removeDataset() {
|
||||
chart.data.datasets.shift();
|
||||
chart.update();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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<count; ++i) {
|
||||
for (i = 0; i < count; ++i) {
|
||||
value = (from[i] || 0) + this.rand(min, max);
|
||||
if (this.rand() <= continuity) {
|
||||
data.push(Math.round(dfactor * value) / dfactor);
|
||||
@@ -76,14 +84,14 @@ window.randomScalingFactor = function() {
|
||||
var min = cfg.min || 0;
|
||||
var max = cfg.max || 100;
|
||||
var count = cfg.count || 8;
|
||||
var step = (max-min) / count;
|
||||
var step = (max - min) / count;
|
||||
var decimals = cfg.decimals || 8;
|
||||
var dfactor = Math.pow(10, decimals) || 0;
|
||||
var prefix = cfg.prefix || '';
|
||||
var values = [];
|
||||
var i;
|
||||
|
||||
for (i=min; i<max; i+=step) {
|
||||
for (i = min; i < max; i += step) {
|
||||
values.push(prefix + Math.round(dfactor * i) / dfactor);
|
||||
}
|
||||
|
||||
@@ -97,23 +105,43 @@ window.randomScalingFactor = function() {
|
||||
var values = [];
|
||||
var i, value;
|
||||
|
||||
for (i=0; i<count; ++i) {
|
||||
value = Months[Math.ceil(i)%12];
|
||||
for (i = 0; i < count; ++i) {
|
||||
value = Months[Math.ceil(i) % 12];
|
||||
values.push(value.substring(0, section));
|
||||
}
|
||||
|
||||
return values;
|
||||
},
|
||||
|
||||
transparentize: function(color, opacity) {
|
||||
var alpha = opacity === undefined? 0.5 : 1 - opacity;
|
||||
return Chart.helpers.color(color).alpha(alpha).rgbString();
|
||||
color: function(index) {
|
||||
return COLORS[index % COLORS.length];
|
||||
},
|
||||
|
||||
merge: Chart.helpers.configMerge
|
||||
transparentize: function(color, opacity) {
|
||||
var alpha = opacity === undefined ? 0.5 : 1 - opacity;
|
||||
return Color(color).alpha(alpha).rgbString();
|
||||
}
|
||||
};
|
||||
|
||||
// DEPRECATED
|
||||
window.randomScalingFactor = function() {
|
||||
return Math.round(Samples.utils.rand(-100, 100));
|
||||
};
|
||||
|
||||
// INITIALIZATION
|
||||
|
||||
Samples.utils.srand(Date.now());
|
||||
|
||||
}(this));
|
||||
// Google Analytics
|
||||
/* eslint-disable */
|
||||
if (document.location.hostname.match(/^(www\.)?chartjs\.org$/)) {
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-28909194-3', 'auto');
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
}(this));
|
||||
|
||||
46
src/chart.js
46
src/chart.js
@@ -1,29 +1,29 @@
|
||||
/**
|
||||
* @namespace Chart
|
||||
*/
|
||||
var Chart = require('./core/core.js')();
|
||||
var Chart = require('./core/core')();
|
||||
|
||||
Chart.helpers = require('./helpers/index');
|
||||
|
||||
// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
|
||||
require('./core/core.helpers')(Chart);
|
||||
require('./platforms/platform.js')(Chart);
|
||||
require('./core/core.canvasHelpers')(Chart);
|
||||
require('./core/core.element')(Chart);
|
||||
require('./core/core.plugin.js')(Chart);
|
||||
|
||||
Chart.defaults = require('./core/core.defaults');
|
||||
Chart.Element = require('./core/core.element');
|
||||
Chart.elements = require('./elements/index');
|
||||
Chart.Interaction = require('./core/core.interaction');
|
||||
Chart.platform = require('./platforms/platform');
|
||||
|
||||
require('./core/core.plugin')(Chart);
|
||||
require('./core/core.animation')(Chart);
|
||||
require('./core/core.controller')(Chart);
|
||||
require('./core/core.datasetController')(Chart);
|
||||
require('./core/core.layoutService')(Chart);
|
||||
require('./core/core.scaleService')(Chart);
|
||||
require('./core/core.ticks.js')(Chart);
|
||||
require('./core/core.scale')(Chart);
|
||||
require('./core/core.interaction')(Chart);
|
||||
require('./core/core.tooltip')(Chart);
|
||||
|
||||
require('./elements/element.arc')(Chart);
|
||||
require('./elements/element.line')(Chart);
|
||||
require('./elements/element.point')(Chart);
|
||||
require('./elements/element.rectangle')(Chart);
|
||||
|
||||
require('./scales/scale.linearbase.js')(Chart);
|
||||
require('./scales/scale.linearbase')(Chart);
|
||||
require('./scales/scale.category')(Chart);
|
||||
require('./scales/scale.linear')(Chart);
|
||||
require('./scales/scale.logarithmic')(Chart);
|
||||
@@ -38,6 +38,7 @@ require('./controllers/controller.doughnut')(Chart);
|
||||
require('./controllers/controller.line')(Chart);
|
||||
require('./controllers/controller.polarArea')(Chart);
|
||||
require('./controllers/controller.radar')(Chart);
|
||||
require('./controllers/controller.scatter')(Chart);
|
||||
|
||||
require('./charts/Chart.Bar')(Chart);
|
||||
require('./charts/Chart.Bubble')(Chart);
|
||||
@@ -51,14 +52,27 @@ require('./charts/Chart.Scatter')(Chart);
|
||||
var plugins = [];
|
||||
|
||||
plugins.push(
|
||||
require('./plugins/plugin.filler.js')(Chart),
|
||||
require('./plugins/plugin.legend.js')(Chart),
|
||||
require('./plugins/plugin.title.js')(Chart)
|
||||
require('./plugins/plugin.filler')(Chart),
|
||||
require('./plugins/plugin.legend')(Chart),
|
||||
require('./plugins/plugin.title')(Chart)
|
||||
);
|
||||
|
||||
Chart.plugins.register(plugins);
|
||||
|
||||
Chart.platform.initialize();
|
||||
|
||||
module.exports = Chart;
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Chart = Chart;
|
||||
}
|
||||
|
||||
// DEPRECATIONS
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers.canvas instead.
|
||||
* @namespace Chart.canvasHelpers
|
||||
* @deprecated since version 2.6.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
Chart.canvasHelpers = Chart.helpers.canvas;
|
||||
|
||||
@@ -1,47 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var defaultConfig = {
|
||||
hover: {
|
||||
mode: 'single'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'linear', // scatter should not use a category axis
|
||||
position: 'bottom',
|
||||
id: 'x-axis-1' // need an ID so datasets can reference the scale
|
||||
}],
|
||||
yAxes: [{
|
||||
type: 'linear',
|
||||
position: 'left',
|
||||
id: 'y-axis-1'
|
||||
}]
|
||||
},
|
||||
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function() {
|
||||
// Title doesn't make sense for scatter since we format the data as a point
|
||||
return '';
|
||||
},
|
||||
label: function(tooltipItem) {
|
||||
return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Register the default config for this type
|
||||
Chart.defaults.scatter = defaultConfig;
|
||||
|
||||
// Scatter charts use line controllers
|
||||
Chart.controllers.scatter = Chart.controllers.line;
|
||||
|
||||
Chart.Scatter = function(context, config) {
|
||||
config.type = 'scatter';
|
||||
return new Chart(context, config);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -1,36 +1,105 @@
|
||||
'use strict';
|
||||
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
defaults._set('bar', {
|
||||
hover: {
|
||||
mode: 'label'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'category',
|
||||
|
||||
// Specific to Bar Controller
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9,
|
||||
|
||||
// offset settings
|
||||
offset: true,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
offsetGridLines: true
|
||||
}
|
||||
}],
|
||||
|
||||
yAxes: [{
|
||||
type: 'linear'
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
defaults._set('horizontalBar', {
|
||||
hover: {
|
||||
mode: 'index',
|
||||
axis: 'y'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'linear',
|
||||
position: 'bottom'
|
||||
}],
|
||||
|
||||
yAxes: [{
|
||||
position: 'left',
|
||||
type: 'category',
|
||||
|
||||
// Specific to Horizontal Bar Controller
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9,
|
||||
|
||||
// offset settings
|
||||
offset: true,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
offsetGridLines: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
|
||||
elements: {
|
||||
rectangle: {
|
||||
borderSkipped: 'left'
|
||||
}
|
||||
},
|
||||
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function(item, data) {
|
||||
// Pick first xLabel for now
|
||||
var title = '';
|
||||
|
||||
if (item.length > 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<ilen; ++i) {
|
||||
d = dataset.data[i];
|
||||
if (d !== null && d !== undefined && !isNaN(d)) {
|
||||
elements[i].draw();
|
||||
for (; i < ilen; ++i) {
|
||||
if (!isNaN(scale.getRightValue(dataset.data[i]))) {
|
||||
rects[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,9 +385,9 @@ module.exports = function(Chart) {
|
||||
var custom = rectangle.custom || {};
|
||||
var model = rectangle._model;
|
||||
|
||||
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
|
||||
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(rectangle) {
|
||||
@@ -303,68 +397,12 @@ module.exports = function(Chart) {
|
||||
var model = rectangle._model;
|
||||
var rectangleElementOptions = this.chart.options.elements.rectangle;
|
||||
|
||||
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
|
||||
model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
|
||||
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
|
||||
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
|
||||
model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
|
||||
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// including horizontalBar in the bar file, instead of a file of its own
|
||||
// it extends bar (like pie extends doughnut)
|
||||
Chart.defaults.horizontalBar = {
|
||||
hover: {
|
||||
mode: 'label'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'linear',
|
||||
position: 'bottom'
|
||||
}],
|
||||
yAxes: [{
|
||||
position: 'left',
|
||||
type: 'category',
|
||||
|
||||
// Specific to Horizontal Bar Controller
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
offsetGridLines: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
elements: {
|
||||
rectangle: {
|
||||
borderSkipped: 'left'
|
||||
}
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function(tooltipItems, data) {
|
||||
// Pick first xLabel for now
|
||||
var title = '';
|
||||
|
||||
if (tooltipItems.length > 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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('<ul class="' + chart.id + '-legend">');
|
||||
|
||||
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('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
|
||||
if (labels[i]) {
|
||||
text.push(labels[i]);
|
||||
}
|
||||
text.push('</li>');
|
||||
}
|
||||
}
|
||||
|
||||
text.push('</ul>');
|
||||
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('<ul class="' + chart.id + '-legend">');
|
||||
|
||||
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('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
|
||||
if (labels[i]) {
|
||||
text.push(labels[i]);
|
||||
}
|
||||
text.push('</li>');
|
||||
}
|
||||
}
|
||||
|
||||
text.push('</ul>');
|
||||
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;
|
||||
|
||||
@@ -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<ilen; ++i) {
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
me.updateElement(points[i], i, reset);
|
||||
}
|
||||
|
||||
@@ -93,7 +95,7 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
for (i=0, ilen=points.length; i<ilen; ++i) {
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
points[i].pivot();
|
||||
}
|
||||
},
|
||||
@@ -106,7 +108,7 @@ module.exports = function(Chart) {
|
||||
if (custom.backgroundColor) {
|
||||
backgroundColor = custom.backgroundColor;
|
||||
} else if (dataset.pointBackgroundColor) {
|
||||
backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
|
||||
backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
|
||||
} else if (dataset.backgroundColor) {
|
||||
backgroundColor = dataset.backgroundColor;
|
||||
}
|
||||
@@ -122,7 +124,7 @@ module.exports = function(Chart) {
|
||||
if (custom.borderColor) {
|
||||
borderColor = custom.borderColor;
|
||||
} else if (dataset.pointBorderColor) {
|
||||
borderColor = helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
|
||||
borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
|
||||
} else if (dataset.borderColor) {
|
||||
borderColor = dataset.borderColor;
|
||||
}
|
||||
@@ -137,8 +139,8 @@ module.exports = function(Chart) {
|
||||
|
||||
if (!isNaN(custom.borderWidth)) {
|
||||
borderWidth = custom.borderWidth;
|
||||
} else if (!isNaN(dataset.pointBorderWidth)) {
|
||||
borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
|
||||
} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
|
||||
borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
|
||||
} else if (!isNaN(dataset.borderWidth)) {
|
||||
borderWidth = dataset.borderWidth;
|
||||
}
|
||||
@@ -157,8 +159,6 @@ module.exports = function(Chart) {
|
||||
var xScale = me.getScaleForId(meta.xAxisID);
|
||||
var pointOptions = me.chart.options.elements.point;
|
||||
var x, y;
|
||||
var labels = me.chart.data.labels || [];
|
||||
var includeOffset = (labels.length === 1 || dataset.data.length === 1) || me.chart.isCombo;
|
||||
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
|
||||
@@ -168,7 +168,7 @@ module.exports = function(Chart) {
|
||||
dataset.pointHitRadius = dataset.hitRadius;
|
||||
}
|
||||
|
||||
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, includeOffset);
|
||||
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
|
||||
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
|
||||
|
||||
// Utility
|
||||
@@ -183,15 +183,15 @@ module.exports = function(Chart) {
|
||||
y: y,
|
||||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||||
// Appearance
|
||||
radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
|
||||
pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
|
||||
radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
|
||||
pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
|
||||
backgroundColor: me.getPointBackgroundColor(point, index),
|
||||
borderColor: me.getPointBorderColor(point, index),
|
||||
borderWidth: me.getPointBorderWidth(point, index),
|
||||
tension: meta.dataset._model ? meta.dataset._model.tension : 0,
|
||||
steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
|
||||
// Tooltip
|
||||
hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
|
||||
hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
|
||||
};
|
||||
},
|
||||
|
||||
@@ -285,16 +285,16 @@ module.exports = function(Chart) {
|
||||
var ilen = points.length;
|
||||
var i = 0;
|
||||
|
||||
Chart.canvasHelpers.clipArea(chart.ctx, area);
|
||||
helpers.canvas.clipArea(chart.ctx, area);
|
||||
|
||||
if (lineEnabled(me.getDataset(), chart.options)) {
|
||||
meta.dataset.draw();
|
||||
}
|
||||
|
||||
Chart.canvasHelpers.unclipArea(chart.ctx);
|
||||
helpers.canvas.unclipArea(chart.ctx);
|
||||
|
||||
// Draw the points
|
||||
for (; i<ilen; ++i) {
|
||||
for (; i < ilen; ++i) {
|
||||
points[i].draw(area);
|
||||
}
|
||||
},
|
||||
@@ -306,10 +306,10 @@ module.exports = function(Chart) {
|
||||
var custom = point.custom || {};
|
||||
var model = point._model;
|
||||
|
||||
model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
|
||||
model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(point) {
|
||||
@@ -324,7 +324,7 @@ module.exports = function(Chart) {
|
||||
dataset.pointRadius = dataset.radius;
|
||||
}
|
||||
|
||||
model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
|
||||
model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
|
||||
model.backgroundColor = me.getPointBackgroundColor(point, index);
|
||||
model.borderColor = me.getPointBorderColor(point, index);
|
||||
model.borderWidth = me.getPointBorderWidth(point, index);
|
||||
|
||||
@@ -1,118 +1,118 @@
|
||||
'use strict';
|
||||
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
defaults._set('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,
|
||||
legendCallback: function(chart) {
|
||||
var text = [];
|
||||
text.push('<ul class="' + chart.id + '-legend">');
|
||||
|
||||
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('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
|
||||
if (labels[i]) {
|
||||
text.push(labels[i]);
|
||||
}
|
||||
text.push('</li>');
|
||||
}
|
||||
}
|
||||
|
||||
text.push('</ul>');
|
||||
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('<ul class="' + chart.id + '-legend">');
|
||||
|
||||
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('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
|
||||
if (labels[i]) {
|
||||
text.push(labels[i]);
|
||||
}
|
||||
text.push('</li>');
|
||||
}
|
||||
}
|
||||
|
||||
text.push('</ul>');
|
||||
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])
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
42
src/controllers/controller.scatter.js
Normal file
42
src/controllers/controller.scatter.js
Normal file
@@ -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;
|
||||
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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<ilen; ++i) {
|
||||
for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
|
||||
if (me.isDatasetVisible(i)) {
|
||||
me.getDatasetMeta(i).controller.transition(easingValue);
|
||||
}
|
||||
@@ -543,7 +563,7 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
// Draw datasets reversed to support proper line stacking
|
||||
for (var i=(me.data.datasets || []).length - 1; 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<ilen; ++i) {
|
||||
for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
|
||||
if (this.isDatasetVisible(i)) {
|
||||
count++;
|
||||
}
|
||||
@@ -640,32 +660,42 @@ module.exports = function(Chart) {
|
||||
|
||||
// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
|
||||
// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
|
||||
return typeof meta.hidden === 'boolean'? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
|
||||
return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
|
||||
},
|
||||
|
||||
generateLegend: function() {
|
||||
return this.options.legendCallback(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroyDatasetMeta: function(datasetIndex) {
|
||||
var id = this.id;
|
||||
var dataset = this.data.datasets[datasetIndex];
|
||||
var meta = dataset._meta && dataset._meta[id];
|
||||
|
||||
if (meta) {
|
||||
meta.controller.destroy();
|
||||
delete dataset._meta[id];
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
var me = this;
|
||||
var canvas = me.canvas;
|
||||
var meta, i, ilen;
|
||||
var i, ilen;
|
||||
|
||||
me.stop();
|
||||
|
||||
// dataset controllers need to cleanup associated data
|
||||
for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
|
||||
meta = me.getDatasetMeta(i);
|
||||
if (meta.controller) {
|
||||
meta.controller.destroy();
|
||||
meta.controller = null;
|
||||
}
|
||||
me.destroyDatasetMeta(i);
|
||||
}
|
||||
|
||||
if (canvas) {
|
||||
me.unbindEvents();
|
||||
helpers.clear(me);
|
||||
helpers.canvas.clear(me);
|
||||
platform.releaseContext(me.ctx);
|
||||
me.canvas = null;
|
||||
me.ctx = null;
|
||||
@@ -684,11 +714,10 @@ module.exports = function(Chart) {
|
||||
var me = this;
|
||||
me.tooltip = new Chart.Tooltip({
|
||||
_chart: me,
|
||||
_chartInstance: me, // deprecated, backward compatibility
|
||||
_chartInstance: me, // deprecated, backward compatibility
|
||||
_data: me.data,
|
||||
_options: me.options.tooltips
|
||||
}, me);
|
||||
me.tooltip.initialize();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -706,9 +735,7 @@ module.exports = function(Chart) {
|
||||
listeners[type] = listener;
|
||||
});
|
||||
|
||||
// Responsiveness is currently based on the use of an iframe, however this method causes
|
||||
// performance issues and could be troublesome when used with ad blockers. So make sure
|
||||
// that the user is still able to create a chart without iframe when responsive is false.
|
||||
// Elements used to detect size change should not be injected for non responsive charts.
|
||||
// See https://github.com/chartjs/Chart.js/issues/2210
|
||||
if (me.options.responsive) {
|
||||
listener = function() {
|
||||
@@ -737,10 +764,10 @@ module.exports = function(Chart) {
|
||||
},
|
||||
|
||||
updateHoverStyle: function(elements, mode, enabled) {
|
||||
var method = enabled? 'setHoverStyle' : 'removeHoverStyle';
|
||||
var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
|
||||
var element, i, ilen;
|
||||
|
||||
for (i=0, ilen=elements.length; i<ilen; ++i) {
|
||||
for (i = 0, ilen = elements.length; i < ilen; ++i) {
|
||||
element = elements[i];
|
||||
if (element) {
|
||||
this.getDatasetMeta(element._datasetIndex).controller[method](element);
|
||||
@@ -771,7 +798,7 @@ module.exports = function(Chart) {
|
||||
var bufferedRequest = me._bufferedRequest;
|
||||
if (bufferedRequest) {
|
||||
// If we have an update that was triggered, we need to do a normal render
|
||||
me.render(bufferedRequest.duration, bufferedRequest.lazy);
|
||||
me.render(bufferedRequest);
|
||||
} else if (changed && !me.animating) {
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
me.stop();
|
||||
@@ -808,11 +835,9 @@ module.exports = function(Chart) {
|
||||
me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
|
||||
}
|
||||
|
||||
// On Hover hook
|
||||
if (hoverOptions.onHover) {
|
||||
// Need to call with native event here to not break backwards compatibility
|
||||
hoverOptions.onHover.call(me, e.native, me.active);
|
||||
}
|
||||
// Invoke onHover hook
|
||||
// Need to call with native event here to not break backwards compatibility
|
||||
helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
|
||||
|
||||
if (e.type === 'mouseup' || e.type === 'click') {
|
||||
if (options.onClick) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
|
||||
|
||||
@@ -170,7 +170,7 @@ module.exports = function(Chart) {
|
||||
var metaData = meta.data;
|
||||
var i, ilen;
|
||||
|
||||
for (i=0, ilen=data.length; i<ilen; ++i) {
|
||||
for (i = 0, ilen = data.length; i < ilen; ++i) {
|
||||
metaData[i] = metaData[i] || me.createMetaData(i);
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ module.exports = function(Chart) {
|
||||
var ilen = elements.length;
|
||||
var i = 0;
|
||||
|
||||
for (; i<ilen; ++i) {
|
||||
for (; i < ilen; ++i) {
|
||||
elements[i].transition(easingValue);
|
||||
}
|
||||
|
||||
@@ -233,17 +233,17 @@ module.exports = function(Chart) {
|
||||
meta.dataset.draw();
|
||||
}
|
||||
|
||||
for (; i<ilen; ++i) {
|
||||
for (; i < ilen; ++i) {
|
||||
elements[i].draw();
|
||||
}
|
||||
},
|
||||
|
||||
removeHoverStyle: function(element, elementOpts) {
|
||||
var dataset = this.chart.data.datasets[element._datasetIndex],
|
||||
index = element._index,
|
||||
custom = element.custom || {},
|
||||
valueOrDefault = helpers.getValueAtIndexOrDefault,
|
||||
model = element._model;
|
||||
var dataset = this.chart.data.datasets[element._datasetIndex];
|
||||
var index = element._index;
|
||||
var custom = element.custom || {};
|
||||
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
||||
var model = element._model;
|
||||
|
||||
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
|
||||
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
|
||||
@@ -251,12 +251,12 @@ module.exports = function(Chart) {
|
||||
},
|
||||
|
||||
setHoverStyle: function(element) {
|
||||
var dataset = this.chart.data.datasets[element._datasetIndex],
|
||||
index = element._index,
|
||||
custom = element.custom || {},
|
||||
valueOrDefault = helpers.getValueAtIndexOrDefault,
|
||||
getHoverColor = helpers.getHoverColor,
|
||||
model = element._model;
|
||||
var dataset = this.chart.data.datasets[element._datasetIndex];
|
||||
var index = element._index;
|
||||
var custom = element.custom || {};
|
||||
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
||||
var getHoverColor = helpers.getHoverColor;
|
||||
var model = element._model;
|
||||
|
||||
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
|
||||
@@ -284,7 +284,7 @@ module.exports = function(Chart) {
|
||||
* @private
|
||||
*/
|
||||
insertElements: function(start, count) {
|
||||
for (var i=0; i<count; ++i) {
|
||||
for (var i = 0; i < count; ++i) {
|
||||
this.addElementAndReset(start + i);
|
||||
}
|
||||
},
|
||||
@@ -293,7 +293,7 @@ module.exports = function(Chart) {
|
||||
* @private
|
||||
*/
|
||||
onDataPush: function() {
|
||||
this.insertElements(this.getDataset().data.length-1, arguments.length);
|
||||
this.insertElements(this.getDataset().data.length - 1, arguments.length);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
12
src/core/core.defaults.js
Normal file
12
src/core/core.defaults.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_set: function(scope, values) {
|
||||
return helpers.merge(this[scope] || (this[scope] = {}), values);
|
||||
}
|
||||
};
|
||||
@@ -1,119 +1,115 @@
|
||||
'use strict';
|
||||
|
||||
var color = require('chartjs-color');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
module.exports = function(Chart) {
|
||||
function interpolate(start, view, model, ease) {
|
||||
var keys = Object.keys(model);
|
||||
var i, ilen, key, actual, origin, target, type, c0, c1;
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
for (i = 0, ilen = keys.length; i < ilen; ++i) {
|
||||
key = keys[i];
|
||||
|
||||
function interpolate(start, view, model, ease) {
|
||||
var keys = Object.keys(model);
|
||||
var i, ilen, key, actual, origin, target, type, c0, c1;
|
||||
|
||||
for (i=0, ilen=keys.length; i<ilen; ++i) {
|
||||
key = keys[i];
|
||||
|
||||
target = model[key];
|
||||
|
||||
// if a value is added to the model after pivot() has been called, the view
|
||||
// doesn't contain it, so let's initialize the view to the target value.
|
||||
if (!view.hasOwnProperty(key)) {
|
||||
view[key] = target;
|
||||
}
|
||||
|
||||
actual = view[key];
|
||||
|
||||
if (actual === target || key[0] === '_') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!start.hasOwnProperty(key)) {
|
||||
start[key] = actual;
|
||||
}
|
||||
|
||||
origin = start[key];
|
||||
|
||||
type = typeof(target);
|
||||
|
||||
if (type === typeof(origin)) {
|
||||
if (type === 'string') {
|
||||
c0 = color(origin);
|
||||
if (c0.valid) {
|
||||
c1 = color(target);
|
||||
if (c1.valid) {
|
||||
view[key] = c1.mix(c0, ease).rgbString();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (type === 'number' && isFinite(origin) && isFinite(target)) {
|
||||
view[key] = origin + (target - origin) * ease;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
target = model[key];
|
||||
|
||||
// if a value is added to the model after pivot() has been called, the view
|
||||
// doesn't contain it, so let's initialize the view to the target value.
|
||||
if (!view.hasOwnProperty(key)) {
|
||||
view[key] = target;
|
||||
}
|
||||
}
|
||||
|
||||
Chart.elements = {};
|
||||
actual = view[key];
|
||||
|
||||
Chart.Element = function(configuration) {
|
||||
helpers.extend(this, configuration);
|
||||
this.initialize.apply(this, arguments);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.Element.prototype, {
|
||||
|
||||
initialize: function() {
|
||||
this.hidden = false;
|
||||
},
|
||||
|
||||
pivot: function() {
|
||||
var me = this;
|
||||
if (!me._view) {
|
||||
me._view = helpers.clone(me._model);
|
||||
}
|
||||
me._start = {};
|
||||
return me;
|
||||
},
|
||||
|
||||
transition: function(ease) {
|
||||
var me = this;
|
||||
var model = me._model;
|
||||
var start = me._start;
|
||||
var view = me._view;
|
||||
|
||||
// No animation -> No Transition
|
||||
if (!model || ease === 1) {
|
||||
me._view = model;
|
||||
me._start = null;
|
||||
return me;
|
||||
}
|
||||
|
||||
if (!view) {
|
||||
view = me._view = {};
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
start = me._start = {};
|
||||
}
|
||||
|
||||
interpolate(start, view, model, ease);
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
tooltipPosition: function() {
|
||||
return {
|
||||
x: this._model.x,
|
||||
y: this._model.y
|
||||
};
|
||||
},
|
||||
|
||||
hasValue: function() {
|
||||
return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
|
||||
if (actual === target || key[0] === '_') {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
Chart.Element.extend = helpers.inherits;
|
||||
if (!start.hasOwnProperty(key)) {
|
||||
start[key] = actual;
|
||||
}
|
||||
|
||||
origin = start[key];
|
||||
|
||||
type = typeof target;
|
||||
|
||||
if (type === typeof origin) {
|
||||
if (type === 'string') {
|
||||
c0 = color(origin);
|
||||
if (c0.valid) {
|
||||
c1 = color(target);
|
||||
if (c1.valid) {
|
||||
view[key] = c1.mix(c0, ease).rgbString();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (type === 'number' && isFinite(origin) && isFinite(target)) {
|
||||
view[key] = origin + (target - origin) * ease;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
view[key] = target;
|
||||
}
|
||||
}
|
||||
|
||||
var Element = function(configuration) {
|
||||
helpers.extend(this, configuration);
|
||||
this.initialize.apply(this, arguments);
|
||||
};
|
||||
|
||||
helpers.extend(Element.prototype, {
|
||||
|
||||
initialize: function() {
|
||||
this.hidden = false;
|
||||
},
|
||||
|
||||
pivot: function() {
|
||||
var me = this;
|
||||
if (!me._view) {
|
||||
me._view = helpers.clone(me._model);
|
||||
}
|
||||
me._start = {};
|
||||
return me;
|
||||
},
|
||||
|
||||
transition: function(ease) {
|
||||
var me = this;
|
||||
var model = me._model;
|
||||
var start = me._start;
|
||||
var view = me._view;
|
||||
|
||||
// No animation -> No Transition
|
||||
if (!model || ease === 1) {
|
||||
me._view = model;
|
||||
me._start = null;
|
||||
return me;
|
||||
}
|
||||
|
||||
if (!view) {
|
||||
view = me._view = {};
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
start = me._start = {};
|
||||
}
|
||||
|
||||
interpolate(start, view, model, ease);
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
tooltipPosition: function() {
|
||||
return {
|
||||
x: this._model.x,
|
||||
y: this._model.y
|
||||
};
|
||||
},
|
||||
|
||||
hasValue: function() {
|
||||
return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
|
||||
}
|
||||
});
|
||||
|
||||
Element.extend = helpers.inherits;
|
||||
|
||||
module.exports = Element;
|
||||
|
||||
@@ -3,47 +3,13 @@
|
||||
'use strict';
|
||||
|
||||
var color = require('chartjs-color');
|
||||
var defaults = require('./core.defaults');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
module.exports = function(Chart) {
|
||||
// Global Chart helpers object for utility methods and classes
|
||||
var helpers = Chart.helpers = {};
|
||||
|
||||
// -- Basic js utility methods
|
||||
helpers.each = function(loopable, callback, self, reverse) {
|
||||
// Check to see if null or undefined firstly.
|
||||
var i, len;
|
||||
if (helpers.isArray(loopable)) {
|
||||
len = loopable.length;
|
||||
if (reverse) {
|
||||
for (i = len - 1; i >= 0; i--) {
|
||||
callback.call(self, loopable[i], i);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < len; i++) {
|
||||
callback.call(self, loopable[i], i);
|
||||
}
|
||||
}
|
||||
} else if (typeof loopable === 'object') {
|
||||
var keys = Object.keys(loopable);
|
||||
len = keys.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
callback.call(self, loopable[keys[i]], keys[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
helpers.clone = function(obj) {
|
||||
var objClone = {};
|
||||
helpers.each(obj, function(value, key) {
|
||||
if (helpers.isArray(value)) {
|
||||
objClone[key] = value.slice(0);
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
objClone[key] = helpers.clone(value);
|
||||
} else {
|
||||
objClone[key] = value;
|
||||
}
|
||||
});
|
||||
return objClone;
|
||||
};
|
||||
|
||||
helpers.extend = function(base) {
|
||||
var setFn = function(value, key) {
|
||||
base[key] = value;
|
||||
@@ -53,102 +19,61 @@ module.exports = function(Chart) {
|
||||
}
|
||||
return base;
|
||||
};
|
||||
// Need a special merge function to chart configs since they are now grouped
|
||||
helpers.configMerge = function(_base) {
|
||||
var base = helpers.clone(_base);
|
||||
helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
|
||||
helpers.each(extension, function(value, key) {
|
||||
var baseHasProperty = base.hasOwnProperty(key);
|
||||
var baseVal = baseHasProperty ? base[key] : {};
|
||||
|
||||
helpers.configMerge = function(/* objects ... */) {
|
||||
return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
|
||||
merger: function(key, target, source, options) {
|
||||
var tval = target[key] || {};
|
||||
var sval = source[key];
|
||||
|
||||
if (key === 'scales') {
|
||||
// Scale config merging is complex. Add our own function here for that
|
||||
base[key] = helpers.scaleMerge(baseVal, value);
|
||||
// scale config merging is complex. Add our own function here for that
|
||||
target[key] = helpers.scaleMerge(tval, sval);
|
||||
} else if (key === 'scale') {
|
||||
// Used in polar area & radar charts since there is only one scale
|
||||
base[key] = helpers.configMerge(baseVal, Chart.scaleService.getScaleDefaults(value.type), value);
|
||||
} else if (baseHasProperty
|
||||
&& typeof baseVal === 'object'
|
||||
&& !helpers.isArray(baseVal)
|
||||
&& baseVal !== null
|
||||
&& typeof value === 'object'
|
||||
&& !helpers.isArray(value)) {
|
||||
// If we are overwriting an object with an object, do a merge of the properties.
|
||||
base[key] = helpers.configMerge(baseVal, value);
|
||||
// used in polar area & radar charts since there is only one scale
|
||||
target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]);
|
||||
} else {
|
||||
// can just overwrite the value in this case
|
||||
base[key] = value;
|
||||
helpers._merger(key, target, source, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return base;
|
||||
};
|
||||
helpers.scaleMerge = function(_base, extension) {
|
||||
var base = helpers.clone(_base);
|
||||
|
||||
helpers.each(extension, function(value, key) {
|
||||
if (key === 'xAxes' || key === 'yAxes') {
|
||||
// These properties are arrays of items
|
||||
if (base.hasOwnProperty(key)) {
|
||||
helpers.each(value, function(valueObj, index) {
|
||||
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
|
||||
var axisDefaults = Chart.scaleService.getScaleDefaults(axisType);
|
||||
if (index >= base[key].length || !base[key][index].type) {
|
||||
base[key].push(helpers.configMerge(axisDefaults, valueObj));
|
||||
} else if (valueObj.type && valueObj.type !== base[key][index].type) {
|
||||
// Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults
|
||||
base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj);
|
||||
} else {
|
||||
// Type is the same
|
||||
base[key][index] = helpers.configMerge(base[key][index], valueObj);
|
||||
helpers.scaleMerge = function(/* objects ... */) {
|
||||
return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
|
||||
merger: function(key, target, source, options) {
|
||||
if (key === 'xAxes' || key === 'yAxes') {
|
||||
var slen = source[key].length;
|
||||
var i, type, scale;
|
||||
|
||||
if (!target[key]) {
|
||||
target[key] = [];
|
||||
}
|
||||
|
||||
for (i = 0; i < slen; ++i) {
|
||||
scale = source[key][i];
|
||||
type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear');
|
||||
|
||||
if (i >= target[key].length) {
|
||||
target[key].push({});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
base[key] = [];
|
||||
helpers.each(value, function(valueObj) {
|
||||
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
|
||||
base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj));
|
||||
});
|
||||
}
|
||||
} else if (base.hasOwnProperty(key) && typeof base[key] === 'object' && base[key] !== null && typeof value === 'object') {
|
||||
// If we are overwriting an object with an object, do a merge of the properties.
|
||||
base[key] = helpers.configMerge(base[key], value);
|
||||
|
||||
} else {
|
||||
// can just overwrite the value in this case
|
||||
base[key] = value;
|
||||
if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
|
||||
// new/untyped scale or type changed: let's apply the new defaults
|
||||
// then merge source scale to correctly overwrite the defaults.
|
||||
helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]);
|
||||
} else {
|
||||
// scales type are the same
|
||||
helpers.merge(target[key][i], scale);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpers._merger(key, target, source, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return base;
|
||||
};
|
||||
helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
|
||||
if (value === undefined || value === null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (helpers.isArray(value)) {
|
||||
return index < value.length ? value[index] : defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
helpers.getValueOrDefault = function(value, defaultValue) {
|
||||
return value === undefined ? defaultValue : value;
|
||||
};
|
||||
helpers.indexOf = Array.prototype.indexOf?
|
||||
function(array, item) {
|
||||
return array.indexOf(item);
|
||||
}:
|
||||
function(array, item) {
|
||||
for (var i = 0, ilen = array.length; i < ilen; ++i) {
|
||||
if (array[i] === item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
helpers.where = function(collection, filterCallback) {
|
||||
if (helpers.isArray(collection) && Array.prototype.filter) {
|
||||
return collection.filter(filterCallback);
|
||||
@@ -163,12 +88,12 @@ module.exports = function(Chart) {
|
||||
|
||||
return filtered;
|
||||
};
|
||||
helpers.findIndex = Array.prototype.findIndex?
|
||||
helpers.findIndex = Array.prototype.findIndex ?
|
||||
function(array, callback, scope) {
|
||||
return array.findIndex(callback, scope);
|
||||
} :
|
||||
function(array, callback, scope) {
|
||||
scope = scope === undefined? array : scope;
|
||||
scope = scope === undefined ? array : scope;
|
||||
for (var i = 0, ilen = array.length; i < ilen; ++i) {
|
||||
if (callback.call(scope, array[i], i, array)) {
|
||||
return i;
|
||||
@@ -178,7 +103,7 @@ module.exports = function(Chart) {
|
||||
};
|
||||
helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
|
||||
// Default to start of the array
|
||||
if (startIndex === undefined || startIndex === null) {
|
||||
if (helpers.isNullOrUndef(startIndex)) {
|
||||
startIndex = -1;
|
||||
}
|
||||
for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
|
||||
@@ -190,7 +115,7 @@ module.exports = function(Chart) {
|
||||
};
|
||||
helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
|
||||
// Default to end of the array
|
||||
if (startIndex === undefined || startIndex === null) {
|
||||
if (helpers.isNullOrUndef(startIndex)) {
|
||||
startIndex = arrayToSearch.length;
|
||||
}
|
||||
for (var i = startIndex - 1; i >= 0; i--) {
|
||||
@@ -223,13 +148,6 @@ module.exports = function(Chart) {
|
||||
|
||||
return ChartElement;
|
||||
};
|
||||
helpers.noop = function() {};
|
||||
helpers.uid = (function() {
|
||||
var id = 0;
|
||||
return function() {
|
||||
return id++;
|
||||
};
|
||||
}());
|
||||
// -- Math methods
|
||||
helpers.isNumber = function(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
@@ -257,7 +175,7 @@ module.exports = function(Chart) {
|
||||
return min;
|
||||
}, Number.POSITIVE_INFINITY);
|
||||
};
|
||||
helpers.sign = Math.sign?
|
||||
helpers.sign = Math.sign ?
|
||||
function(x) {
|
||||
return Math.sign(x);
|
||||
} :
|
||||
@@ -268,7 +186,7 @@ module.exports = function(Chart) {
|
||||
}
|
||||
return x > 0 ? 1 : -1;
|
||||
};
|
||||
helpers.log10 = Math.log10?
|
||||
helpers.log10 = Math.log10 ?
|
||||
function(x) {
|
||||
return Math.log10(x);
|
||||
} :
|
||||
@@ -283,9 +201,9 @@ module.exports = function(Chart) {
|
||||
};
|
||||
// Gets the angle from vertical upright to the point about a centre.
|
||||
helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
|
||||
var distanceFromXCenter = anglePoint.x - centrePoint.x,
|
||||
distanceFromYCenter = anglePoint.y - centrePoint.y,
|
||||
radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
|
||||
var distanceFromXCenter = anglePoint.x - centrePoint.x;
|
||||
var distanceFromYCenter = anglePoint.y - centrePoint.y;
|
||||
var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
|
||||
|
||||
var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
|
||||
|
||||
@@ -310,9 +228,9 @@ module.exports = function(Chart) {
|
||||
|
||||
// This function must also respect "skipped" points
|
||||
|
||||
var previous = firstPoint.skip ? middlePoint : firstPoint,
|
||||
current = middlePoint,
|
||||
next = afterPoint.skip ? middlePoint : afterPoint;
|
||||
var previous = firstPoint.skip ? middlePoint : firstPoint;
|
||||
var current = middlePoint;
|
||||
var next = afterPoint.skip ? middlePoint : afterPoint;
|
||||
|
||||
var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
|
||||
var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
|
||||
@@ -470,203 +388,6 @@ module.exports = function(Chart) {
|
||||
|
||||
return niceFraction * Math.pow(10, exponent);
|
||||
};
|
||||
// Easing functions adapted from Robert Penner's easing equations
|
||||
// http://www.robertpenner.com/easing/
|
||||
var easingEffects = helpers.easingEffects = {
|
||||
linear: function(t) {
|
||||
return t;
|
||||
},
|
||||
easeInQuad: function(t) {
|
||||
return t * t;
|
||||
},
|
||||
easeOutQuad: function(t) {
|
||||
return -1 * t * (t - 2);
|
||||
},
|
||||
easeInOutQuad: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t;
|
||||
}
|
||||
return -1 / 2 * ((--t) * (t - 2) - 1);
|
||||
},
|
||||
easeInCubic: function(t) {
|
||||
return t * t * t;
|
||||
},
|
||||
easeOutCubic: function(t) {
|
||||
return 1 * ((t = t / 1 - 1) * t * t + 1);
|
||||
},
|
||||
easeInOutCubic: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t;
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * t + 2);
|
||||
},
|
||||
easeInQuart: function(t) {
|
||||
return t * t * t * t;
|
||||
},
|
||||
easeOutQuart: function(t) {
|
||||
return -1 * ((t = t / 1 - 1) * t * t * t - 1);
|
||||
},
|
||||
easeInOutQuart: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t * t;
|
||||
}
|
||||
return -1 / 2 * ((t -= 2) * t * t * t - 2);
|
||||
},
|
||||
easeInQuint: function(t) {
|
||||
return 1 * (t /= 1) * t * t * t * t;
|
||||
},
|
||||
easeOutQuint: function(t) {
|
||||
return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
|
||||
},
|
||||
easeInOutQuint: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t * t * t;
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
|
||||
},
|
||||
easeInSine: function(t) {
|
||||
return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
|
||||
},
|
||||
easeOutSine: function(t) {
|
||||
return 1 * Math.sin(t / 1 * (Math.PI / 2));
|
||||
},
|
||||
easeInOutSine: function(t) {
|
||||
return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
|
||||
},
|
||||
easeInExpo: function(t) {
|
||||
return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
|
||||
},
|
||||
easeOutExpo: function(t) {
|
||||
return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
|
||||
},
|
||||
easeInOutExpo: function(t) {
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * Math.pow(2, 10 * (t - 1));
|
||||
}
|
||||
return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
|
||||
},
|
||||
easeInCirc: function(t) {
|
||||
if (t >= 1) {
|
||||
return t;
|
||||
}
|
||||
return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
|
||||
},
|
||||
easeOutCirc: function(t) {
|
||||
return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
|
||||
},
|
||||
easeInOutCirc: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
|
||||
}
|
||||
return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
|
||||
},
|
||||
easeInElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1) === 1) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * 0.3;
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
|
||||
},
|
||||
easeOutElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1) === 1) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * 0.3;
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
|
||||
},
|
||||
easeInOutElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1 / 2) === 2) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * (0.3 * 1.5);
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
if (t < 1) {
|
||||
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
|
||||
}
|
||||
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
|
||||
},
|
||||
easeInBack: function(t) {
|
||||
var s = 1.70158;
|
||||
return 1 * (t /= 1) * t * ((s + 1) * t - s);
|
||||
},
|
||||
easeOutBack: function(t) {
|
||||
var s = 1.70158;
|
||||
return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
|
||||
},
|
||||
easeInOutBack: function(t) {
|
||||
var s = 1.70158;
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
|
||||
},
|
||||
easeInBounce: function(t) {
|
||||
return 1 - easingEffects.easeOutBounce(1 - t);
|
||||
},
|
||||
easeOutBounce: function(t) {
|
||||
if ((t /= 1) < (1 / 2.75)) {
|
||||
return 1 * (7.5625 * t * t);
|
||||
} else if (t < (2 / 2.75)) {
|
||||
return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
|
||||
} else if (t < (2.5 / 2.75)) {
|
||||
return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
|
||||
}
|
||||
return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
|
||||
},
|
||||
easeInOutBounce: function(t) {
|
||||
if (t < 1 / 2) {
|
||||
return easingEffects.easeInBounce(t * 2) * 0.5;
|
||||
}
|
||||
return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
|
||||
}
|
||||
};
|
||||
// Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
|
||||
helpers.requestAnimFrame = (function() {
|
||||
if (typeof window === 'undefined') {
|
||||
@@ -686,9 +407,9 @@ module.exports = function(Chart) {
|
||||
// -- DOM methods
|
||||
helpers.getRelativePosition = function(evt, chart) {
|
||||
var mouseX, mouseY;
|
||||
var e = evt.originalEvent || evt,
|
||||
canvas = evt.currentTarget || evt.srcElement,
|
||||
boundingRect = canvas.getBoundingClientRect();
|
||||
var e = evt.originalEvent || evt;
|
||||
var canvas = evt.currentTarget || evt.srcElement;
|
||||
var boundingRect = canvas.getBoundingClientRect();
|
||||
|
||||
var touches = e.touches;
|
||||
if (touches && touches.length > 0) {
|
||||
@@ -721,29 +442,11 @@ module.exports = function(Chart) {
|
||||
};
|
||||
|
||||
};
|
||||
helpers.addEvent = function(node, eventType, method) {
|
||||
if (node.addEventListener) {
|
||||
node.addEventListener(eventType, method);
|
||||
} else if (node.attachEvent) {
|
||||
node.attachEvent('on' + eventType, method);
|
||||
} else {
|
||||
node['on' + eventType] = method;
|
||||
}
|
||||
};
|
||||
helpers.removeEvent = function(node, eventType, handler) {
|
||||
if (node.removeEventListener) {
|
||||
node.removeEventListener(eventType, handler, false);
|
||||
} else if (node.detachEvent) {
|
||||
node.detachEvent('on' + eventType, handler);
|
||||
} else {
|
||||
node['on' + eventType] = helpers.noop;
|
||||
}
|
||||
};
|
||||
|
||||
// Private helper function to convert max-width/max-height values that may be percentages into a number
|
||||
function parseMaxStyle(styleValue, node, parentProperty) {
|
||||
var valueInPixels;
|
||||
if (typeof(styleValue) === 'string') {
|
||||
if (typeof styleValue === 'string') {
|
||||
valueInPixels = parseInt(styleValue, 10);
|
||||
|
||||
if (styleValue.indexOf('%') !== -1) {
|
||||
@@ -781,8 +484,8 @@ module.exports = function(Chart) {
|
||||
|
||||
if (hasCNode || hasCContainer) {
|
||||
return Math.min(
|
||||
hasCNode? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
|
||||
hasCContainer? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
|
||||
hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
|
||||
hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
|
||||
}
|
||||
|
||||
return 'none';
|
||||
@@ -797,27 +500,35 @@ module.exports = function(Chart) {
|
||||
};
|
||||
helpers.getMaximumWidth = function(domNode) {
|
||||
var container = domNode.parentNode;
|
||||
if (!container) {
|
||||
return domNode.clientWidth;
|
||||
}
|
||||
|
||||
var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10);
|
||||
var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10);
|
||||
var w = container.clientWidth - paddingLeft - paddingRight;
|
||||
var cw = helpers.getConstraintWidth(domNode);
|
||||
return isNaN(cw)? w : Math.min(w, cw);
|
||||
return isNaN(cw) ? w : Math.min(w, cw);
|
||||
};
|
||||
helpers.getMaximumHeight = function(domNode) {
|
||||
var container = domNode.parentNode;
|
||||
if (!container) {
|
||||
return domNode.clientHeight;
|
||||
}
|
||||
|
||||
var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10);
|
||||
var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10);
|
||||
var h = container.clientHeight - paddingTop - paddingBottom;
|
||||
var ch = helpers.getConstraintHeight(domNode);
|
||||
return isNaN(ch)? h : Math.min(h, ch);
|
||||
return isNaN(ch) ? h : Math.min(h, ch);
|
||||
};
|
||||
helpers.getStyle = function(el, property) {
|
||||
return el.currentStyle ?
|
||||
el.currentStyle[property] :
|
||||
document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
|
||||
};
|
||||
helpers.retinaScale = function(chart) {
|
||||
var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1;
|
||||
helpers.retinaScale = function(chart, forceRatio) {
|
||||
var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1;
|
||||
if (pixelRatio === 1) {
|
||||
return;
|
||||
}
|
||||
@@ -837,9 +548,6 @@ module.exports = function(Chart) {
|
||||
canvas.style.width = width + 'px';
|
||||
};
|
||||
// -- Canvas methods
|
||||
helpers.clear = function(chart) {
|
||||
chart.ctx.clearRect(0, 0, chart.width, chart.height);
|
||||
};
|
||||
helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
|
||||
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
|
||||
};
|
||||
@@ -903,21 +611,8 @@ module.exports = function(Chart) {
|
||||
});
|
||||
return numberOfLines;
|
||||
};
|
||||
helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + radius, y);
|
||||
ctx.lineTo(x + width - radius, y);
|
||||
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
||||
ctx.lineTo(x + width, y + height - radius);
|
||||
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
||||
ctx.lineTo(x + radius, y + height);
|
||||
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||||
ctx.lineTo(x, y + radius);
|
||||
ctx.quadraticCurveTo(x, y, x + radius, y);
|
||||
ctx.closePath();
|
||||
};
|
||||
|
||||
helpers.color = !color?
|
||||
helpers.color = !color ?
|
||||
function(value) {
|
||||
console.error('Color.js not found!');
|
||||
return value;
|
||||
@@ -925,60 +620,16 @@ module.exports = function(Chart) {
|
||||
function(value) {
|
||||
/* global CanvasGradient */
|
||||
if (value instanceof CanvasGradient) {
|
||||
value = Chart.defaults.global.defaultColor;
|
||||
value = defaults.global.defaultColor;
|
||||
}
|
||||
|
||||
return color(value);
|
||||
};
|
||||
|
||||
helpers.isArray = Array.isArray?
|
||||
function(obj) {
|
||||
return Array.isArray(obj);
|
||||
} :
|
||||
function(obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||
};
|
||||
// ! @see http://stackoverflow.com/a/14853974
|
||||
helpers.arrayEquals = function(a0, a1) {
|
||||
var i, ilen, v0, v1;
|
||||
|
||||
if (!a0 || !a1 || a0.length !== a1.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0, ilen=a0.length; i < ilen; ++i) {
|
||||
v0 = a0[i];
|
||||
v1 = a1[i];
|
||||
|
||||
if (v0 instanceof Array && v1 instanceof Array) {
|
||||
if (!helpers.arrayEquals(v0, v1)) {
|
||||
return false;
|
||||
}
|
||||
} else if (v0 !== v1) {
|
||||
// NOTE: two different object instances will never be equal: {x:20} != {x:20}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
helpers.callback = function(fn, args, thisArg) {
|
||||
if (fn && typeof fn.call === 'function') {
|
||||
fn.apply(thisArg, args);
|
||||
}
|
||||
};
|
||||
helpers.getHoverColor = function(colorValue) {
|
||||
/* global CanvasPattern */
|
||||
return (colorValue instanceof CanvasPattern) ?
|
||||
colorValue :
|
||||
helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers#callback instead.
|
||||
* @function Chart.helpers#callCallback
|
||||
* @deprecated since version 2.6.0
|
||||
* @todo remove at version 3
|
||||
*/
|
||||
helpers.callCallback = helpers.callback;
|
||||
};
|
||||
|
||||
@@ -1,316 +1,330 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var helpers = Chart.helpers;
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
/**
|
||||
* Helper function to get relative position for an event
|
||||
* @param {Event|IEvent} event - The event to get the position for
|
||||
* @param {Chart} chart - The chart
|
||||
* @returns {Point} the event position
|
||||
*/
|
||||
function getRelativePosition(e, chart) {
|
||||
if (e.native) {
|
||||
return {
|
||||
x: e.x,
|
||||
y: e.y
|
||||
};
|
||||
}
|
||||
|
||||
return helpers.getRelativePosition(e, chart);
|
||||
/**
|
||||
* Helper function to get relative position for an event
|
||||
* @param {Event|IEvent} event - The event to get the position for
|
||||
* @param {Chart} chart - The chart
|
||||
* @returns {Point} the event position
|
||||
*/
|
||||
function getRelativePosition(e, chart) {
|
||||
if (e.native) {
|
||||
return {
|
||||
x: e.x,
|
||||
y: e.y
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to traverse all of the visible elements in the chart
|
||||
* @param chart {chart} the chart
|
||||
* @param handler {Function} the callback to execute for each visible item
|
||||
*/
|
||||
function parseVisibleItems(chart, handler) {
|
||||
var datasets = chart.data.datasets;
|
||||
var meta, i, j, ilen, jlen;
|
||||
return helpers.getRelativePosition(e, chart);
|
||||
}
|
||||
|
||||
for (i = 0, ilen = datasets.length; i < ilen; ++i) {
|
||||
if (!chart.isDatasetVisible(i)) {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Helper function to traverse all of the visible elements in the chart
|
||||
* @param chart {chart} the chart
|
||||
* @param handler {Function} the callback to execute for each visible item
|
||||
*/
|
||||
function parseVisibleItems(chart, handler) {
|
||||
var datasets = chart.data.datasets;
|
||||
var meta, i, j, ilen, jlen;
|
||||
|
||||
meta = chart.getDatasetMeta(i);
|
||||
for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
|
||||
var element = meta.data[j];
|
||||
if (!element._view.skip) {
|
||||
handler(element);
|
||||
}
|
||||
for (i = 0, ilen = datasets.length; i < ilen; ++i) {
|
||||
if (!chart.isDatasetVisible(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
meta = chart.getDatasetMeta(i);
|
||||
for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
|
||||
var element = meta.data[j];
|
||||
if (!element._view.skip) {
|
||||
handler(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the items that intersect the event position
|
||||
* @param items {ChartElement[]} elements to filter
|
||||
* @param position {Point} the point to be nearest to
|
||||
* @return {ChartElement[]} the nearest items
|
||||
*/
|
||||
function getIntersectItems(chart, position) {
|
||||
var elements = [];
|
||||
/**
|
||||
* Helper function to get the items that intersect the event position
|
||||
* @param items {ChartElement[]} elements to filter
|
||||
* @param position {Point} the point to be nearest to
|
||||
* @return {ChartElement[]} the nearest items
|
||||
*/
|
||||
function getIntersectItems(chart, position) {
|
||||
var elements = [];
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
elements.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the items nearest to the event position considering all visible items in teh chart
|
||||
* @param chart {Chart} the chart to look at elements from
|
||||
* @param position {Point} the point to be nearest to
|
||||
* @param intersect {Boolean} if true, only consider items that intersect the position
|
||||
* @param distanceMetric {Function} function to provide the distance between points
|
||||
* @return {ChartElement[]} the nearest items
|
||||
*/
|
||||
function getNearestItems(chart, position, intersect, distanceMetric) {
|
||||
var minDistance = Number.POSITIVE_INFINITY;
|
||||
var nearestItems = [];
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (intersect && !element.inRange(position.x, position.y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var center = element.getCenterPoint();
|
||||
var distance = distanceMetric(position, center);
|
||||
|
||||
if (distance < minDistance) {
|
||||
nearestItems = [element];
|
||||
minDistance = distance;
|
||||
} else if (distance === minDistance) {
|
||||
// Can have multiple items at the same distance in which case we sort by size
|
||||
nearestItems.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
return nearestItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a distance metric function for two points based on the
|
||||
* axis mode setting
|
||||
* @param {String} axis the axis mode. x|y|xy
|
||||
*/
|
||||
function getDistanceMetricForAxis(axis) {
|
||||
var useX = axis.indexOf('x') !== -1;
|
||||
var useY = axis.indexOf('y') !== -1;
|
||||
|
||||
return function(pt1, pt2) {
|
||||
var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
|
||||
var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
|
||||
return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
|
||||
};
|
||||
}
|
||||
|
||||
function indexMode(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
// Default axis for index mode is 'x' to match old behaviour
|
||||
options.axis = options.axis || 'x';
|
||||
var distanceMetric = getDistanceMetricForAxis(options.axis);
|
||||
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
|
||||
var elements = [];
|
||||
|
||||
if (!items.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
chart.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
if (chart.isDatasetVisible(datasetIndex)) {
|
||||
var meta = chart.getDatasetMeta(datasetIndex);
|
||||
var element = meta.data[items[0]._index];
|
||||
|
||||
// don't count items that are skipped (null data)
|
||||
if (element && !element._view.skip) {
|
||||
elements.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the items nearest to the event position considering all visible items in teh chart
|
||||
* @param chart {Chart} the chart to look at elements from
|
||||
* @param position {Point} the point to be nearest to
|
||||
* @param intersect {Boolean} if true, only consider items that intersect the position
|
||||
* @param distanceMetric {Function} Optional function to provide the distance between
|
||||
* @return {ChartElement[]} the nearest items
|
||||
*/
|
||||
function getNearestItems(chart, position, intersect, distanceMetric) {
|
||||
var minDistance = Number.POSITIVE_INFINITY;
|
||||
var nearestItems = [];
|
||||
|
||||
if (!distanceMetric) {
|
||||
distanceMetric = helpers.distanceBetweenPoints;
|
||||
}
|
||||
});
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (intersect && !element.inRange(position.x, position.y)) {
|
||||
return;
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
var center = element.getCenterPoint();
|
||||
var distance = distanceMetric(position, center);
|
||||
/**
|
||||
* @interface IInteractionOptions
|
||||
*/
|
||||
/**
|
||||
* If true, only consider items that intersect the point
|
||||
* @name IInterfaceOptions#boolean
|
||||
* @type Boolean
|
||||
*/
|
||||
|
||||
if (distance < minDistance) {
|
||||
nearestItems = [element];
|
||||
minDistance = distance;
|
||||
} else if (distance === minDistance) {
|
||||
// Can have multiple items at the same distance in which case we sort by size
|
||||
nearestItems.push(element);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Contains interaction related functions
|
||||
* @namespace Chart.Interaction
|
||||
*/
|
||||
module.exports = {
|
||||
// Helper function for different modes
|
||||
modes: {
|
||||
single: function(chart, e) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var elements = [];
|
||||
|
||||
return nearestItems;
|
||||
}
|
||||
|
||||
function indexMode(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var distanceMetric = function(pt1, pt2) {
|
||||
return Math.abs(pt1.x - pt2.x);
|
||||
};
|
||||
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
|
||||
var elements = [];
|
||||
|
||||
if (!items.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
chart.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
if (chart.isDatasetVisible(datasetIndex)) {
|
||||
var meta = chart.getDatasetMeta(datasetIndex),
|
||||
element = meta.data[items[0]._index];
|
||||
|
||||
// don't count items that are skipped (null data)
|
||||
if (element && !element._view.skip) {
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
elements.push(element);
|
||||
return elements;
|
||||
}
|
||||
});
|
||||
|
||||
return elements.slice(0, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* @function Chart.Interaction.modes.label
|
||||
* @deprecated since version 2.4.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
label: indexMode,
|
||||
|
||||
/**
|
||||
* Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
|
||||
* If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
|
||||
* @function Chart.Interaction.modes.index
|
||||
* @since v2.4.0
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use during interaction
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
index: indexMode,
|
||||
|
||||
/**
|
||||
* Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
|
||||
* If the options.intersect is false, we find the nearest item and return the items in that dataset
|
||||
* @function Chart.Interaction.modes.dataset
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use during interaction
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
dataset: function(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
options.axis = options.axis || 'xy';
|
||||
var distanceMetric = getDistanceMetricForAxis(options.axis);
|
||||
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
|
||||
|
||||
if (items.length > 0) {
|
||||
items = chart.getDatasetMeta(items[0]._datasetIndex).data;
|
||||
}
|
||||
});
|
||||
|
||||
return elements;
|
||||
}
|
||||
return items;
|
||||
},
|
||||
|
||||
/**
|
||||
* @interface IInteractionOptions
|
||||
*/
|
||||
/**
|
||||
* If true, only consider items that intersect the point
|
||||
* @name IInterfaceOptions#boolean
|
||||
* @type Boolean
|
||||
*/
|
||||
/**
|
||||
* @function Chart.Interaction.modes.x-axis
|
||||
* @deprecated since version 2.4.0. Use index mode and intersect == true
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
'x-axis': function(chart, e) {
|
||||
return indexMode(chart, e, {intersect: true});
|
||||
},
|
||||
|
||||
/**
|
||||
* Contains interaction related functions
|
||||
* @namespace Chart.Interaction
|
||||
*/
|
||||
Chart.Interaction = {
|
||||
// Helper function for different modes
|
||||
modes: {
|
||||
single: function(chart, e) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var elements = [];
|
||||
/**
|
||||
* Point mode returns all elements that hit test based on the event position
|
||||
* of the event
|
||||
* @function Chart.Interaction.modes.intersect
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
point: function(chart, e) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
return getIntersectItems(chart, position);
|
||||
},
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
elements.push(element);
|
||||
return elements;
|
||||
/**
|
||||
* nearest mode returns the element closest to the point
|
||||
* @function Chart.Interaction.modes.intersect
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
nearest: function(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
options.axis = options.axis || 'xy';
|
||||
var distanceMetric = getDistanceMetricForAxis(options.axis);
|
||||
var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric);
|
||||
|
||||
// We have multiple items at the same distance from the event. Now sort by smallest
|
||||
if (nearestItems.length > 1) {
|
||||
nearestItems.sort(function(a, b) {
|
||||
var sizeA = a.getArea();
|
||||
var sizeB = b.getArea();
|
||||
var ret = sizeA - sizeB;
|
||||
|
||||
if (ret === 0) {
|
||||
// if equal sort by dataset index
|
||||
ret = a._datasetIndex - b._datasetIndex;
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
return elements.slice(0, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* @function Chart.Interaction.modes.label
|
||||
* @deprecated since version 2.4.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
label: indexMode,
|
||||
|
||||
/**
|
||||
* Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
|
||||
* If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
|
||||
* @function Chart.Interaction.modes.index
|
||||
* @since v2.4.0
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use during interaction
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
index: indexMode,
|
||||
|
||||
/**
|
||||
* Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
|
||||
* If the options.intersect is false, we find the nearest item and return the items in that dataset
|
||||
* @function Chart.Interaction.modes.dataset
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use during interaction
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
dataset: function(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false);
|
||||
|
||||
if (items.length > 0) {
|
||||
items = chart.getDatasetMeta(items[0]._datasetIndex).data;
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
/**
|
||||
* @function Chart.Interaction.modes.x-axis
|
||||
* @deprecated since version 2.4.0. Use index mode and intersect == true
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
'x-axis': function(chart, e) {
|
||||
return indexMode(chart, e, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Point mode returns all elements that hit test based on the event position
|
||||
* of the event
|
||||
* @function Chart.Interaction.modes.intersect
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
point: function(chart, e) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
return getIntersectItems(chart, position);
|
||||
},
|
||||
|
||||
/**
|
||||
* nearest mode returns the element closest to the point
|
||||
* @function Chart.Interaction.modes.intersect
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
nearest: function(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var nearestItems = getNearestItems(chart, position, options.intersect);
|
||||
|
||||
// We have multiple items at the same distance from the event. Now sort by smallest
|
||||
if (nearestItems.length > 1) {
|
||||
nearestItems.sort(function(a, b) {
|
||||
var sizeA = a.getArea();
|
||||
var sizeB = b.getArea();
|
||||
var ret = sizeA - sizeB;
|
||||
|
||||
if (ret === 0) {
|
||||
// if equal sort by dataset index
|
||||
ret = a._datasetIndex - b._datasetIndex;
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
// Return only 1 item
|
||||
return nearestItems.slice(0, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* x mode returns the elements that hit-test at the current x coordinate
|
||||
* @function Chart.Interaction.modes.x
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
x: function(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var items = [];
|
||||
var intersectsItem = false;
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (element.inXRange(position.x)) {
|
||||
items.push(element);
|
||||
}
|
||||
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
intersectsItem = true;
|
||||
}
|
||||
});
|
||||
|
||||
// If we want to trigger on an intersect and we don't have any items
|
||||
// that intersect the position, return nothing
|
||||
if (options.intersect && !intersectsItem) {
|
||||
items = [];
|
||||
}
|
||||
return items;
|
||||
},
|
||||
|
||||
/**
|
||||
* y mode returns the elements that hit-test at the current y coordinate
|
||||
* @function Chart.Interaction.modes.y
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
y: function(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var items = [];
|
||||
var intersectsItem = false;
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (element.inYRange(position.y)) {
|
||||
items.push(element);
|
||||
}
|
||||
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
intersectsItem = true;
|
||||
}
|
||||
});
|
||||
|
||||
// If we want to trigger on an intersect and we don't have any items
|
||||
// that intersect the position, return nothing
|
||||
if (options.intersect && !intersectsItem) {
|
||||
items = [];
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
// Return only 1 item
|
||||
return nearestItems.slice(0, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* x mode returns the elements that hit-test at the current x coordinate
|
||||
* @function Chart.Interaction.modes.x
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
x: function(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var items = [];
|
||||
var intersectsItem = false;
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (element.inXRange(position.x)) {
|
||||
items.push(element);
|
||||
}
|
||||
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
intersectsItem = true;
|
||||
}
|
||||
});
|
||||
|
||||
// If we want to trigger on an intersect and we don't have any items
|
||||
// that intersect the position, return nothing
|
||||
if (options.intersect && !intersectsItem) {
|
||||
items = [];
|
||||
}
|
||||
return items;
|
||||
},
|
||||
|
||||
/**
|
||||
* y mode returns the elements that hit-test at the current y coordinate
|
||||
* @function Chart.Interaction.modes.y
|
||||
* @param chart {chart} the chart we are returning items from
|
||||
* @param e {Event} the event we are find things at
|
||||
* @param options {IInteractionOptions} options to use
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
y: function(chart, e, options) {
|
||||
var position = getRelativePosition(e, chart);
|
||||
var items = [];
|
||||
var intersectsItem = false;
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
if (element.inYRange(position.y)) {
|
||||
items.push(element);
|
||||
}
|
||||
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
intersectsItem = true;
|
||||
}
|
||||
});
|
||||
|
||||
// If we want to trigger on an intersect and we don't have any items
|
||||
// that intersect the position, return nothing
|
||||
if (options.intersect && !intersectsItem) {
|
||||
items = [];
|
||||
}
|
||||
return items;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
var defaults = require('./core.defaults');
|
||||
|
||||
defaults._set('global', {
|
||||
responsive: true,
|
||||
responsiveAnimationDuration: 0,
|
||||
maintainAspectRatio: true,
|
||||
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
|
||||
hover: {
|
||||
onHover: null,
|
||||
mode: 'nearest',
|
||||
intersect: true,
|
||||
animationDuration: 400
|
||||
},
|
||||
onClick: null,
|
||||
defaultColor: 'rgba(0,0,0,0.1)',
|
||||
defaultFontColor: '#666',
|
||||
defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
defaultFontSize: 12,
|
||||
defaultFontStyle: 'normal',
|
||||
showLines: true,
|
||||
|
||||
// Element defaults defined in element extensions
|
||||
elements: {},
|
||||
|
||||
// Layout options such as padding
|
||||
layout: {
|
||||
padding: {
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function() {
|
||||
|
||||
// Occupy the global variable of Chart, and create a simple base class
|
||||
@@ -8,48 +43,6 @@ module.exports = function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
// Globally expose the defaults to allow for user updating/changing
|
||||
Chart.defaults = {
|
||||
global: {
|
||||
responsive: true,
|
||||
responsiveAnimationDuration: 0,
|
||||
maintainAspectRatio: true,
|
||||
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
|
||||
hover: {
|
||||
onHover: null,
|
||||
mode: 'nearest',
|
||||
intersect: true,
|
||||
animationDuration: 400
|
||||
},
|
||||
onClick: null,
|
||||
defaultColor: 'rgba(0,0,0,0.1)',
|
||||
defaultFontColor: '#666',
|
||||
defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
defaultFontSize: 12,
|
||||
defaultFontStyle: 'normal',
|
||||
showLines: true,
|
||||
|
||||
// Element defaults defined in element extensions
|
||||
elements: {},
|
||||
|
||||
// Legend callback string
|
||||
legendCallback: function(chart) {
|
||||
var text = [];
|
||||
text.push('<ul class="' + chart.id + '-legend">');
|
||||
for (var i = 0; i < chart.data.datasets.length; i++) {
|
||||
text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
|
||||
if (chart.data.datasets[i].label) {
|
||||
text.push(chart.data.datasets[i].label);
|
||||
}
|
||||
text.push('</li>');
|
||||
}
|
||||
text.push('</ul>');
|
||||
|
||||
return text.join('');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Chart.Chart = Chart;
|
||||
|
||||
return Chart;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
module.exports = function(Chart) {
|
||||
|
||||
function filterByPosition(array, position) {
|
||||
return helpers.where(array, function(v) {
|
||||
@@ -75,7 +75,7 @@ module.exports = function(Chart) {
|
||||
* @param {Object} layoutItem - the item to remove from the layout
|
||||
*/
|
||||
removeBox: function(chart, layoutItem) {
|
||||
var index = chart.boxes? chart.boxes.indexOf(layoutItem) : -1;
|
||||
var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
|
||||
if (index !== -1) {
|
||||
chart.boxes.splice(index, 1);
|
||||
}
|
||||
@@ -93,7 +93,7 @@ module.exports = function(Chart) {
|
||||
var i = 0;
|
||||
var prop;
|
||||
|
||||
for (; i<ilen; ++i) {
|
||||
for (; i < ilen; ++i) {
|
||||
prop = props[i];
|
||||
if (options.hasOwnProperty(prop)) {
|
||||
item[prop] = options[prop];
|
||||
@@ -113,26 +113,12 @@ module.exports = function(Chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
var layoutOptions = chart.options.layout;
|
||||
var padding = layoutOptions ? layoutOptions.padding : null;
|
||||
|
||||
var leftPadding = 0;
|
||||
var rightPadding = 0;
|
||||
var topPadding = 0;
|
||||
var bottomPadding = 0;
|
||||
|
||||
if (!isNaN(padding)) {
|
||||
// options.layout.padding is a number. assign to all
|
||||
leftPadding = padding;
|
||||
rightPadding = padding;
|
||||
topPadding = padding;
|
||||
bottomPadding = padding;
|
||||
} else {
|
||||
leftPadding = padding.left || 0;
|
||||
rightPadding = padding.right || 0;
|
||||
topPadding = padding.top || 0;
|
||||
bottomPadding = padding.bottom || 0;
|
||||
}
|
||||
var layoutOptions = chart.options.layout || {};
|
||||
var padding = helpers.options.toPadding(layoutOptions.padding);
|
||||
var leftPadding = padding.left;
|
||||
var rightPadding = padding.right;
|
||||
var topPadding = padding.top;
|
||||
var bottomPadding = padding.bottom;
|
||||
|
||||
var leftBoxes = filterByPosition(chart.boxes, 'left');
|
||||
var rightBoxes = filterByPosition(chart.boxes, 'right');
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
var defaults = require('./core.defaults');
|
||||
var Element = require('./core.element');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
defaults._set('global', {
|
||||
plugins: {}
|
||||
});
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.plugins = {};
|
||||
|
||||
/**
|
||||
* The plugin service singleton
|
||||
* @namespace Chart.plugins
|
||||
@@ -98,7 +102,7 @@ module.exports = function(Chart) {
|
||||
var ilen = descriptors.length;
|
||||
var i, descriptor, plugin, params, method;
|
||||
|
||||
for (i=0; i<ilen; ++i) {
|
||||
for (i = 0; i < ilen; ++i) {
|
||||
descriptor = descriptors[i];
|
||||
plugin = descriptor.plugin;
|
||||
method = plugin[hook];
|
||||
@@ -128,7 +132,6 @@ module.exports = function(Chart) {
|
||||
var plugins = [];
|
||||
var descriptors = [];
|
||||
var config = (chart && chart.config) || {};
|
||||
var defaults = Chart.defaults.global.plugins;
|
||||
var options = (config.options && config.options.plugins) || {};
|
||||
|
||||
this._plugins.concat(config.plugins || []).forEach(function(plugin) {
|
||||
@@ -144,7 +147,7 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
if (opts === true) {
|
||||
opts = helpers.clone(defaults[id]);
|
||||
opts = helpers.clone(defaults.global.plugins[id]);
|
||||
}
|
||||
|
||||
plugins.push(plugin);
|
||||
@@ -215,8 +218,8 @@ module.exports = function(Chart) {
|
||||
* returns `false`, the datasets update is cancelled until another `update` is triggered.
|
||||
* @param {Chart} chart - The chart instance.
|
||||
* @param {Object} args - The call arguments.
|
||||
* @param {Object} args.index - The dataset index.
|
||||
* @param {Number} args.meta - The dataset metadata.
|
||||
* @param {Number} args.index - The dataset index.
|
||||
* @param {Object} args.meta - The dataset metadata.
|
||||
* @param {Object} options - The plugin options.
|
||||
* @returns {Boolean} `false` to cancel the chart datasets drawing.
|
||||
*/
|
||||
@@ -226,8 +229,8 @@ module.exports = function(Chart) {
|
||||
* that this hook will not be called if the datasets update has been previously cancelled.
|
||||
* @param {Chart} chart - The chart instance.
|
||||
* @param {Object} args - The call arguments.
|
||||
* @param {Object} args.index - The dataset index.
|
||||
* @param {Number} args.meta - The dataset metadata.
|
||||
* @param {Number} args.index - The dataset index.
|
||||
* @param {Object} args.meta - The dataset metadata.
|
||||
* @param {Object} options - The plugin options.
|
||||
*/
|
||||
/**
|
||||
@@ -302,8 +305,8 @@ module.exports = function(Chart) {
|
||||
* is cancelled until another `render` is triggered.
|
||||
* @param {Chart} chart - The chart instance.
|
||||
* @param {Object} args - The call arguments.
|
||||
* @param {Object} args.index - The dataset index.
|
||||
* @param {Number} args.meta - The dataset metadata.
|
||||
* @param {Number} args.index - The dataset index.
|
||||
* @param {Object} args.meta - The dataset metadata.
|
||||
* @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
|
||||
* @param {Object} options - The plugin options.
|
||||
* @returns {Boolean} `false` to cancel the chart datasets drawing.
|
||||
@@ -315,8 +318,8 @@ module.exports = function(Chart) {
|
||||
* if the datasets drawing has been previously cancelled.
|
||||
* @param {Chart} chart - The chart instance.
|
||||
* @param {Object} args - The call arguments.
|
||||
* @param {Object} args.index - The dataset index.
|
||||
* @param {Number} args.meta - The dataset metadata.
|
||||
* @param {Number} args.index - The dataset index.
|
||||
* @param {Object} args.meta - The dataset metadata.
|
||||
* @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
|
||||
* @param {Object} options - The plugin options.
|
||||
*/
|
||||
@@ -367,5 +370,5 @@ module.exports = function(Chart) {
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
Chart.PluginBase = Chart.Element.extend({});
|
||||
Chart.PluginBase = Element.extend({});
|
||||
};
|
||||
|
||||
@@ -1,56 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var defaults = require('./core.defaults');
|
||||
var Element = require('./core.element');
|
||||
var helpers = require('../helpers/index');
|
||||
var Ticks = require('./core.ticks');
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
defaults._set('scale', {
|
||||
display: true,
|
||||
position: 'left',
|
||||
offset: false,
|
||||
|
||||
Chart.defaults.scale = {
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
display: true,
|
||||
position: 'left',
|
||||
color: 'rgba(0, 0, 0, 0.1)',
|
||||
lineWidth: 1,
|
||||
drawBorder: true,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
tickMarkLength: 10,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: 'rgba(0,0,0,0.25)',
|
||||
zeroLineBorderDash: [],
|
||||
zeroLineBorderDashOffset: 0.0,
|
||||
offsetGridLines: false,
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0
|
||||
},
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
display: true,
|
||||
color: 'rgba(0, 0, 0, 0.1)',
|
||||
lineWidth: 1,
|
||||
drawBorder: true,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
tickMarkLength: 10,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: 'rgba(0,0,0,0.25)',
|
||||
zeroLineBorderDash: [],
|
||||
zeroLineBorderDashOffset: 0.0,
|
||||
offsetGridLines: false,
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0
|
||||
},
|
||||
// scale label
|
||||
scaleLabel: {
|
||||
// display property
|
||||
display: false,
|
||||
|
||||
// scale label
|
||||
scaleLabel: {
|
||||
// actual label
|
||||
labelString: '',
|
||||
// actual label
|
||||
labelString: '',
|
||||
|
||||
// display property
|
||||
display: false
|
||||
},
|
||||
// line height
|
||||
lineHeight: 1.2,
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
minRotation: 0,
|
||||
maxRotation: 50,
|
||||
mirror: false,
|
||||
padding: 0,
|
||||
reverse: false,
|
||||
display: true,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 0,
|
||||
labelOffset: 0,
|
||||
// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
|
||||
callback: Chart.Ticks.formatters.values
|
||||
// top/bottom padding
|
||||
padding: {
|
||||
top: 4,
|
||||
bottom: 4
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
minRotation: 0,
|
||||
maxRotation: 50,
|
||||
mirror: false,
|
||||
padding: 0,
|
||||
reverse: false,
|
||||
display: true,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 0,
|
||||
labelOffset: 0,
|
||||
// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
|
||||
callback: Ticks.formatters.values,
|
||||
minor: {},
|
||||
major: {}
|
||||
}
|
||||
});
|
||||
|
||||
function labelsFromTicks(ticks) {
|
||||
var labels = [];
|
||||
var i, ilen;
|
||||
|
||||
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
|
||||
labels.push(ticks[i].label);
|
||||
}
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
function getLineValue(scale, index, offsetGridLines) {
|
||||
var lineValue = scale.getPixelForTick(index);
|
||||
|
||||
if (offsetGridLines) {
|
||||
if (index === 0) {
|
||||
lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
|
||||
} else {
|
||||
lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;
|
||||
}
|
||||
}
|
||||
return lineValue;
|
||||
}
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
function computeTextSize(context, tick, font) {
|
||||
return helpers.isArray(tick) ?
|
||||
@@ -59,11 +98,11 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
function parseFontOptions(options) {
|
||||
var getValueOrDefault = helpers.getValueOrDefault;
|
||||
var globalDefaults = Chart.defaults.global;
|
||||
var size = getValueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
|
||||
var style = getValueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
|
||||
var family = getValueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var globalDefaults = defaults.global;
|
||||
var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
|
||||
var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
|
||||
var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);
|
||||
|
||||
return {
|
||||
size: size,
|
||||
@@ -73,7 +112,13 @@ module.exports = function(Chart) {
|
||||
};
|
||||
}
|
||||
|
||||
Chart.Scale = Chart.Element.extend({
|
||||
function parseLineHeight(options) {
|
||||
return helpers.options.toLineHeight(
|
||||
helpers.valueOrDefault(options.lineHeight, 1.2),
|
||||
helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));
|
||||
}
|
||||
|
||||
Chart.Scale = Element.extend({
|
||||
/**
|
||||
* Get the padding needed for the scale
|
||||
* @method getPadding
|
||||
@@ -90,15 +135,47 @@ module.exports = function(Chart) {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the scale tick objects ({label, major})
|
||||
* @since 2.7
|
||||
*/
|
||||
getTicks: function() {
|
||||
return this._ticks;
|
||||
},
|
||||
|
||||
// These methods are ordered by lifecyle. Utilities then follow.
|
||||
// Any function defined here is inherited by all scale types.
|
||||
// Any function can be extended by the scale type
|
||||
|
||||
mergeTicksOptions: function() {
|
||||
var ticks = this.options.ticks;
|
||||
if (ticks.minor === false) {
|
||||
ticks.minor = {
|
||||
display: false
|
||||
};
|
||||
}
|
||||
if (ticks.major === false) {
|
||||
ticks.major = {
|
||||
display: false
|
||||
};
|
||||
}
|
||||
for (var key in ticks) {
|
||||
if (key !== 'major' && key !== 'minor') {
|
||||
if (typeof ticks.minor[key] === 'undefined') {
|
||||
ticks.minor[key] = ticks[key];
|
||||
}
|
||||
if (typeof ticks.major[key] === 'undefined') {
|
||||
ticks.major[key] = ticks[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeUpdate: function() {
|
||||
helpers.callback(this.options.beforeUpdate, [this]);
|
||||
},
|
||||
update: function(maxWidth, maxHeight, margins) {
|
||||
var me = this;
|
||||
var i, ilen, labels, label, ticks, tick;
|
||||
|
||||
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
|
||||
me.beforeUpdate();
|
||||
@@ -124,15 +201,50 @@ module.exports = function(Chart) {
|
||||
me.determineDataLimits();
|
||||
me.afterDataLimits();
|
||||
|
||||
// Ticks
|
||||
// Ticks - `this.ticks` is now DEPRECATED!
|
||||
// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
|
||||
// and must not be accessed directly from outside this class. `this.ticks` being
|
||||
// around for long time and not marked as private, we can't change its structure
|
||||
// without unexpected breaking changes. If you need to access the scale ticks,
|
||||
// use scale.getTicks() instead.
|
||||
|
||||
me.beforeBuildTicks();
|
||||
me.buildTicks();
|
||||
|
||||
// New implementations should return an array of objects but for BACKWARD COMPAT,
|
||||
// we still support no return (`this.ticks` internally set by calling this method).
|
||||
ticks = me.buildTicks() || [];
|
||||
|
||||
me.afterBuildTicks();
|
||||
|
||||
me.beforeTickToLabelConversion();
|
||||
me.convertTicksToLabels();
|
||||
|
||||
// New implementations should return the formatted tick labels but for BACKWARD
|
||||
// COMPAT, we still support no return (`this.ticks` internally changed by calling
|
||||
// this method and supposed to contain only string values).
|
||||
labels = me.convertTicksToLabels(ticks) || me.ticks;
|
||||
|
||||
me.afterTickToLabelConversion();
|
||||
|
||||
me.ticks = labels; // BACKWARD COMPATIBILITY
|
||||
|
||||
// IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!
|
||||
|
||||
// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
|
||||
for (i = 0, ilen = labels.length; i < ilen; ++i) {
|
||||
label = labels[i];
|
||||
tick = ticks[i];
|
||||
if (!tick) {
|
||||
ticks.push(tick = {
|
||||
label: label,
|
||||
major: false
|
||||
});
|
||||
} else {
|
||||
tick.label = label;
|
||||
}
|
||||
}
|
||||
|
||||
me._ticks = ticks;
|
||||
|
||||
// Tick Rotation
|
||||
me.beforeCalculateTickRotation();
|
||||
me.calculateTickRotation();
|
||||
@@ -207,7 +319,7 @@ module.exports = function(Chart) {
|
||||
var me = this;
|
||||
// Convert ticks to strings
|
||||
var tickOpts = me.options.ticks;
|
||||
me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback);
|
||||
me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
|
||||
},
|
||||
afterTickToLabelConversion: function() {
|
||||
helpers.callback(this.options.afterTickToLabelConversion, [this]);
|
||||
@@ -222,6 +334,7 @@ module.exports = function(Chart) {
|
||||
var me = this;
|
||||
var context = me.ctx;
|
||||
var tickOpts = me.options.ticks;
|
||||
var labels = labelsFromTicks(me._ticks);
|
||||
|
||||
// Get the width of each grid by calculating the difference
|
||||
// between x offsets between 0 and 1.
|
||||
@@ -230,11 +343,10 @@ module.exports = function(Chart) {
|
||||
|
||||
var labelRotation = tickOpts.minRotation || 0;
|
||||
|
||||
if (me.options.display && me.isHorizontal()) {
|
||||
var originalLabelWidth = helpers.longestText(context, tickFont.font, me.ticks, me.longestTextCache);
|
||||
if (labels.length && me.options.display && me.isHorizontal()) {
|
||||
var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache);
|
||||
var labelWidth = originalLabelWidth;
|
||||
var cosRotation;
|
||||
var sinRotation;
|
||||
var cosRotation, sinRotation;
|
||||
|
||||
// Allow 3 pixels x2 padding either side for label readability
|
||||
var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
|
||||
@@ -275,6 +387,8 @@ module.exports = function(Chart) {
|
||||
height: 0
|
||||
};
|
||||
|
||||
var labels = labelsFromTicks(me._ticks);
|
||||
|
||||
var opts = me.options;
|
||||
var tickOpts = opts.ticks;
|
||||
var scaleLabelOpts = opts.scaleLabel;
|
||||
@@ -283,7 +397,6 @@ module.exports = function(Chart) {
|
||||
var isHorizontal = me.isHorizontal();
|
||||
|
||||
var tickFont = parseFontOptions(tickOpts);
|
||||
var scaleLabelFontSize = parseFontOptions(scaleLabelOpts).size * 1.5;
|
||||
var tickMarkLength = opts.gridLines.tickMarkLength;
|
||||
|
||||
// Width
|
||||
@@ -303,18 +416,23 @@ module.exports = function(Chart) {
|
||||
|
||||
// Are we showing a title for the scale?
|
||||
if (scaleLabelOpts.display && display) {
|
||||
var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);
|
||||
var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding);
|
||||
var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height;
|
||||
|
||||
if (isHorizontal) {
|
||||
minSize.height += scaleLabelFontSize;
|
||||
minSize.height += deltaHeight;
|
||||
} else {
|
||||
minSize.width += scaleLabelFontSize;
|
||||
minSize.width += deltaHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't bother fitting the ticks if we are not showing them
|
||||
if (tickOpts.display && display) {
|
||||
var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, me.ticks, me.longestTextCache);
|
||||
var tallestLabelHeightInLines = helpers.numberOfLabelLines(me.ticks);
|
||||
var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache);
|
||||
var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels);
|
||||
var lineSpace = tickFont.size * 0.5;
|
||||
var tickPadding = me.options.ticks.padding;
|
||||
|
||||
if (isHorizontal) {
|
||||
// A horizontal axis is more constrained by the height.
|
||||
@@ -327,36 +445,37 @@ module.exports = function(Chart) {
|
||||
// TODO - improve this calculation
|
||||
var labelHeight = (sinRotation * largestTextWidth)
|
||||
+ (tickFont.size * tallestLabelHeightInLines)
|
||||
+ (lineSpace * tallestLabelHeightInLines);
|
||||
+ (lineSpace * (tallestLabelHeightInLines - 1))
|
||||
+ lineSpace; // padding
|
||||
|
||||
minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);
|
||||
|
||||
minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight);
|
||||
me.ctx.font = tickFont.font;
|
||||
var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font);
|
||||
var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font);
|
||||
|
||||
var firstTick = me.ticks[0];
|
||||
var firstLabelWidth = computeTextSize(me.ctx, firstTick, tickFont.font);
|
||||
|
||||
var lastTick = me.ticks[me.ticks.length - 1];
|
||||
var lastLabelWidth = computeTextSize(me.ctx, lastTick, tickFont.font);
|
||||
|
||||
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated
|
||||
// by the font height
|
||||
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
|
||||
// which means that the right padding is dominated by the font height
|
||||
if (me.labelRotation !== 0) {
|
||||
me.paddingLeft = opts.position === 'bottom'? (cosRotation * firstLabelWidth) + 3: (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
|
||||
me.paddingRight = opts.position === 'bottom'? (cosRotation * lineSpace) + 3: (cosRotation * lastLabelWidth) + 3;
|
||||
me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
|
||||
me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3;
|
||||
} else {
|
||||
me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
|
||||
me.paddingRight = lastLabelWidth / 2 + 3;
|
||||
}
|
||||
} else {
|
||||
// A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first
|
||||
// Account for padding
|
||||
|
||||
// A vertical axis is more constrained by the width. Labels are the
|
||||
// dominant factor here, so get that length first and account for padding
|
||||
if (tickOpts.mirror) {
|
||||
largestTextWidth = 0;
|
||||
} else {
|
||||
largestTextWidth += me.options.ticks.padding;
|
||||
// use lineSpace for consistency with horizontal axis
|
||||
// tickPadding is not implemented for horizontal
|
||||
largestTextWidth += tickPadding + lineSpace;
|
||||
}
|
||||
|
||||
minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);
|
||||
|
||||
me.paddingTop = tickFont.size / 2;
|
||||
me.paddingBottom = tickFont.size / 2;
|
||||
}
|
||||
@@ -397,19 +516,22 @@ module.exports = function(Chart) {
|
||||
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
|
||||
getRightValue: function(rawValue) {
|
||||
// Null and undefined values first
|
||||
if (rawValue === null || typeof(rawValue) === 'undefined') {
|
||||
if (helpers.isNullOrUndef(rawValue)) {
|
||||
return NaN;
|
||||
}
|
||||
// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
|
||||
if (typeof(rawValue) === 'number' && !isFinite(rawValue)) {
|
||||
if (typeof rawValue === 'number' && !isFinite(rawValue)) {
|
||||
return NaN;
|
||||
}
|
||||
// If it is in fact an object, dive in one more level
|
||||
if (typeof(rawValue) === 'object') {
|
||||
if ((rawValue instanceof Date) || (rawValue.isValid)) {
|
||||
return rawValue;
|
||||
if (rawValue) {
|
||||
if (this.isHorizontal()) {
|
||||
if (rawValue.x !== undefined) {
|
||||
return this.getRightValue(rawValue.x);
|
||||
}
|
||||
} else if (rawValue.y !== undefined) {
|
||||
return this.getRightValue(rawValue.y);
|
||||
}
|
||||
return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
|
||||
}
|
||||
|
||||
// Value is good, return it
|
||||
@@ -427,14 +549,15 @@ module.exports = function(Chart) {
|
||||
getValueForPixel: helpers.noop,
|
||||
|
||||
// Used for tick location, should
|
||||
getPixelForTick: function(index, includeOffset) {
|
||||
getPixelForTick: function(index) {
|
||||
var me = this;
|
||||
var offset = me.options.offset;
|
||||
if (me.isHorizontal()) {
|
||||
var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
|
||||
var tickWidth = innerWidth / Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
|
||||
var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
|
||||
var pixel = (tickWidth * index) + me.paddingLeft;
|
||||
|
||||
if (includeOffset) {
|
||||
if (offset) {
|
||||
pixel += tickWidth / 2;
|
||||
}
|
||||
|
||||
@@ -443,11 +566,11 @@ module.exports = function(Chart) {
|
||||
return finalVal;
|
||||
}
|
||||
var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
|
||||
return me.top + (index * (innerHeight / (me.ticks.length - 1)));
|
||||
return me.top + (index * (innerHeight / (me._ticks.length - 1)));
|
||||
},
|
||||
|
||||
// Utility for getting the pixel location of a percentage of scale
|
||||
getPixelForDecimal: function(decimal /* , includeOffset*/) {
|
||||
getPixelForDecimal: function(decimal) {
|
||||
var me = this;
|
||||
if (me.isHorizontal()) {
|
||||
var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
|
||||
@@ -469,12 +592,62 @@ module.exports = function(Chart) {
|
||||
var min = me.min;
|
||||
var max = me.max;
|
||||
|
||||
return me.beginAtZero ? 0:
|
||||
min < 0 && max < 0? max :
|
||||
min > 0 && max > 0? min :
|
||||
return me.beginAtZero ? 0 :
|
||||
min < 0 && max < 0 ? max :
|
||||
min > 0 && max > 0 ? min :
|
||||
0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a subset of ticks to be plotted to avoid overlapping labels.
|
||||
* @private
|
||||
*/
|
||||
_autoSkip: function(ticks) {
|
||||
var skipRatio;
|
||||
var me = this;
|
||||
var isHorizontal = me.isHorizontal();
|
||||
var optionTicks = me.options.ticks.minor;
|
||||
var tickCount = ticks.length;
|
||||
var labelRotationRadians = helpers.toRadians(me.labelRotation);
|
||||
var cosRotation = Math.cos(labelRotationRadians);
|
||||
var longestRotatedLabel = me.longestLabelWidth * cosRotation;
|
||||
var result = [];
|
||||
var i, tick, shouldSkip;
|
||||
|
||||
// figure out the maximum number of gridlines to show
|
||||
var maxTicks;
|
||||
if (optionTicks.maxTicksLimit) {
|
||||
maxTicks = optionTicks.maxTicksLimit;
|
||||
}
|
||||
|
||||
if (isHorizontal) {
|
||||
skipRatio = false;
|
||||
|
||||
if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) {
|
||||
skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight)));
|
||||
}
|
||||
|
||||
// if they defined a max number of optionTicks,
|
||||
// increase skipRatio until that number is met
|
||||
if (maxTicks && tickCount > maxTicks) {
|
||||
skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tickCount; i++) {
|
||||
tick = ticks[i];
|
||||
|
||||
// Since we always show the last tick,we need may need to hide the last shown one before
|
||||
shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount);
|
||||
if (shouldSkip && i !== tickCount - 1 || helpers.isNullOrUndef(tick.label)) {
|
||||
// leave tick in place but make sure it's not displayed (#4635)
|
||||
delete tick.label;
|
||||
}
|
||||
result.push(tick);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
// Actually draw the scale on the canvas
|
||||
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
|
||||
draw: function(chartArea) {
|
||||
@@ -485,117 +658,84 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
var context = me.ctx;
|
||||
var globalDefaults = Chart.defaults.global;
|
||||
var optionTicks = options.ticks;
|
||||
var globalDefaults = defaults.global;
|
||||
var optionTicks = options.ticks.minor;
|
||||
var optionMajorTicks = options.ticks.major || optionTicks;
|
||||
var gridLines = options.gridLines;
|
||||
var scaleLabel = options.scaleLabel;
|
||||
|
||||
var isRotated = me.labelRotation !== 0;
|
||||
var skipRatio;
|
||||
var useAutoskipper = optionTicks.autoSkip;
|
||||
var isHorizontal = me.isHorizontal();
|
||||
|
||||
// figure out the maximum number of gridlines to show
|
||||
var maxTicks;
|
||||
if (optionTicks.maxTicksLimit) {
|
||||
maxTicks = optionTicks.maxTicksLimit;
|
||||
}
|
||||
|
||||
var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
|
||||
var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
|
||||
var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
|
||||
var tickFont = parseFontOptions(optionTicks);
|
||||
var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor);
|
||||
var majorTickFont = parseFontOptions(optionMajorTicks);
|
||||
|
||||
var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;
|
||||
|
||||
var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
|
||||
var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
|
||||
var scaleLabelFont = parseFontOptions(scaleLabel);
|
||||
|
||||
var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
|
||||
var labelRotationRadians = helpers.toRadians(me.labelRotation);
|
||||
var cosRotation = Math.cos(labelRotationRadians);
|
||||
var longestRotatedLabel = me.longestLabelWidth * cosRotation;
|
||||
|
||||
// Make sure we draw text in the correct color and font
|
||||
context.fillStyle = tickFontColor;
|
||||
|
||||
var itemsToDraw = [];
|
||||
|
||||
if (isHorizontal) {
|
||||
skipRatio = false;
|
||||
|
||||
if ((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length > (me.width - (me.paddingLeft + me.paddingRight))) {
|
||||
skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length) / (me.width - (me.paddingLeft + me.paddingRight)));
|
||||
}
|
||||
|
||||
// if they defined a max number of optionTicks,
|
||||
// increase skipRatio until that number is met
|
||||
if (maxTicks && me.ticks.length > maxTicks) {
|
||||
while (!skipRatio || me.ticks.length / (skipRatio || 1) > maxTicks) {
|
||||
if (!skipRatio) {
|
||||
skipRatio = 1;
|
||||
}
|
||||
skipRatio += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!useAutoskipper) {
|
||||
skipRatio = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var xTickStart = options.position === 'right' ? me.left : me.right - tl;
|
||||
var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
|
||||
var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl;
|
||||
var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom;
|
||||
|
||||
helpers.each(me.ticks, function(label, index) {
|
||||
// If the callback returned a null or undefined value, do not draw this line
|
||||
if (label === undefined || label === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isLastTick = me.ticks.length === index + 1;
|
||||
|
||||
// Since we always show the last tick,we need may need to hide the last shown one before
|
||||
var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length);
|
||||
if (shouldSkip && !isLastTick || (label === undefined || label === null)) {
|
||||
helpers.each(ticks, function(tick, index) {
|
||||
// autoskipper skipped this tick (#4635)
|
||||
if (tick.label === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var label = tick.label;
|
||||
var lineWidth, lineColor, borderDash, borderDashOffset;
|
||||
if (index === (typeof me.zeroLineIndex !== 'undefined' ? me.zeroLineIndex : 0)) {
|
||||
if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {
|
||||
// Draw the first index specially
|
||||
lineWidth = gridLines.zeroLineWidth;
|
||||
lineColor = gridLines.zeroLineColor;
|
||||
borderDash = gridLines.zeroLineBorderDash;
|
||||
borderDashOffset = gridLines.zeroLineBorderDashOffset;
|
||||
} else {
|
||||
lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, index);
|
||||
lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, index);
|
||||
borderDash = helpers.getValueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
|
||||
borderDashOffset = helpers.getValueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
|
||||
lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index);
|
||||
lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index);
|
||||
borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
|
||||
borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
|
||||
}
|
||||
|
||||
// Common properties
|
||||
var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
|
||||
var textAlign = 'middle';
|
||||
var textBaseline = 'middle';
|
||||
var tickPadding = optionTicks.padding;
|
||||
|
||||
if (isHorizontal) {
|
||||
var labelYOffset = tl + tickPadding;
|
||||
|
||||
if (options.position === 'bottom') {
|
||||
// bottom
|
||||
textBaseline = !isRotated? 'top':'middle';
|
||||
textAlign = !isRotated? 'center': 'right';
|
||||
labelY = me.top + tl;
|
||||
textBaseline = !isRotated ? 'top' : 'middle';
|
||||
textAlign = !isRotated ? 'center' : 'right';
|
||||
labelY = me.top + labelYOffset;
|
||||
} else {
|
||||
// top
|
||||
textBaseline = !isRotated? 'bottom':'middle';
|
||||
textAlign = !isRotated? 'center': 'left';
|
||||
labelY = me.bottom - tl;
|
||||
textBaseline = !isRotated ? 'bottom' : 'middle';
|
||||
textAlign = !isRotated ? 'center' : 'left';
|
||||
labelY = me.bottom - labelYOffset;
|
||||
}
|
||||
|
||||
var xLineValue = me.getPixelForTick(index) + helpers.aliasPixel(lineWidth); // xvalues for grid lines
|
||||
labelX = me.getPixelForTick(index, gridLines.offsetGridLines) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
|
||||
var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
|
||||
if (xLineValue < me.left) {
|
||||
lineColor = 'rgba(0,0,0,0)';
|
||||
}
|
||||
xLineValue += helpers.aliasPixel(lineWidth);
|
||||
|
||||
labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
|
||||
|
||||
tx1 = tx2 = x1 = x2 = xLineValue;
|
||||
ty1 = yTickStart;
|
||||
@@ -604,7 +744,6 @@ module.exports = function(Chart) {
|
||||
y2 = chartArea.bottom;
|
||||
} else {
|
||||
var isLeft = options.position === 'left';
|
||||
var tickPadding = optionTicks.padding;
|
||||
var labelXOffset;
|
||||
|
||||
if (optionTicks.mirror) {
|
||||
@@ -617,9 +756,13 @@ module.exports = function(Chart) {
|
||||
|
||||
labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;
|
||||
|
||||
var yLineValue = me.getPixelForTick(index); // xvalues for grid lines
|
||||
var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
|
||||
if (yLineValue < me.top) {
|
||||
lineColor = 'rgba(0,0,0,0)';
|
||||
}
|
||||
yLineValue += helpers.aliasPixel(lineWidth);
|
||||
labelY = me.getPixelForTick(index, gridLines.offsetGridLines);
|
||||
|
||||
labelY = me.getPixelForTick(index) + optionTicks.labelOffset;
|
||||
|
||||
tx1 = xTickStart;
|
||||
tx2 = xTickEnd;
|
||||
@@ -645,6 +788,7 @@ module.exports = function(Chart) {
|
||||
glBorderDashOffset: borderDashOffset,
|
||||
rotation: -1 * labelRotationRadians,
|
||||
label: label,
|
||||
major: tick.major,
|
||||
textBaseline: textBaseline,
|
||||
textAlign: textAlign
|
||||
});
|
||||
@@ -678,10 +822,12 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
if (optionTicks.display) {
|
||||
// Make sure we draw text in the correct color and font
|
||||
context.save();
|
||||
context.translate(itemToDraw.labelX, itemToDraw.labelY);
|
||||
context.rotate(itemToDraw.rotation);
|
||||
context.font = tickFont.font;
|
||||
context.font = itemToDraw.major ? majorTickFont.font : tickFont.font;
|
||||
context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;
|
||||
context.textBaseline = itemToDraw.textBaseline;
|
||||
context.textAlign = itemToDraw.textAlign;
|
||||
|
||||
@@ -705,13 +851,18 @@ module.exports = function(Chart) {
|
||||
var scaleLabelX;
|
||||
var scaleLabelY;
|
||||
var rotation = 0;
|
||||
var halfLineHeight = parseLineHeight(scaleLabel) / 2;
|
||||
|
||||
if (isHorizontal) {
|
||||
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
|
||||
scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFont.size / 2) : me.top + (scaleLabelFont.size / 2);
|
||||
scaleLabelY = options.position === 'bottom'
|
||||
? me.bottom - halfLineHeight - scaleLabelPadding.bottom
|
||||
: me.top + halfLineHeight + scaleLabelPadding.top;
|
||||
} else {
|
||||
var isLeft = options.position === 'left';
|
||||
scaleLabelX = isLeft ? me.left + (scaleLabelFont.size / 2) : me.right - (scaleLabelFont.size / 2);
|
||||
scaleLabelX = isLeft
|
||||
? me.left + halfLineHeight + scaleLabelPadding.top
|
||||
: me.right - halfLineHeight - scaleLabelPadding.top;
|
||||
scaleLabelY = me.top + ((me.bottom - me.top) / 2);
|
||||
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
|
||||
}
|
||||
@@ -729,12 +880,12 @@ module.exports = function(Chart) {
|
||||
|
||||
if (gridLines.drawBorder) {
|
||||
// Draw the line at the edge of the axis
|
||||
context.lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, 0);
|
||||
context.strokeStyle = helpers.getValueAtIndexOrDefault(gridLines.color, 0);
|
||||
var x1 = me.left,
|
||||
x2 = me.right,
|
||||
y1 = me.top,
|
||||
y2 = me.bottom;
|
||||
context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0);
|
||||
context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0);
|
||||
var x1 = me.left;
|
||||
var x2 = me.right;
|
||||
var y1 = me.top;
|
||||
var y2 = me.bottom;
|
||||
|
||||
var aliasPixel = helpers.aliasPixel(context.lineWidth);
|
||||
if (isHorizontal) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var defaults = require('./core.defaults');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
module.exports = function(Chart) {
|
||||
|
||||
Chart.scaleService = {
|
||||
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
|
||||
@@ -13,21 +14,21 @@ module.exports = function(Chart) {
|
||||
|
||||
// Scale config defaults
|
||||
defaults: {},
|
||||
registerScaleType: function(type, scaleConstructor, defaults) {
|
||||
registerScaleType: function(type, scaleConstructor, scaleDefaults) {
|
||||
this.constructors[type] = scaleConstructor;
|
||||
this.defaults[type] = helpers.clone(defaults);
|
||||
this.defaults[type] = helpers.clone(scaleDefaults);
|
||||
},
|
||||
getScaleConstructor: function(type) {
|
||||
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
|
||||
},
|
||||
getScaleDefaults: function(type) {
|
||||
// Return the scale defaults merged with the global settings so that we always use the latest ones
|
||||
return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {};
|
||||
return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {};
|
||||
},
|
||||
updateScaleDefaults: function(type, additions) {
|
||||
var defaults = this.defaults;
|
||||
if (defaults.hasOwnProperty(type)) {
|
||||
defaults[type] = helpers.extend(defaults[type], additions);
|
||||
var me = this;
|
||||
if (me.defaults.hasOwnProperty(type)) {
|
||||
me.defaults[type] = helpers.extend(me.defaults[type], additions);
|
||||
}
|
||||
},
|
||||
addScalesToLayout: function(chart) {
|
||||
|
||||
@@ -1,208 +1,204 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
/**
|
||||
* Namespace to hold static tick generation functions
|
||||
* @namespace Chart.Ticks
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* Namespace to hold static tick generation functions
|
||||
* @namespace Chart.Ticks
|
||||
* Namespace to hold generators for different types of ticks
|
||||
* @namespace Chart.Ticks.generators
|
||||
*/
|
||||
Chart.Ticks = {
|
||||
generators: {
|
||||
/**
|
||||
* Namespace to hold generators for different types of ticks
|
||||
* @namespace Chart.Ticks.generators
|
||||
* Interface for the options provided to the numeric tick generator
|
||||
* @interface INumericTickGenerationOptions
|
||||
*/
|
||||
/**
|
||||
* The maximum number of ticks to display
|
||||
* @name INumericTickGenerationOptions#maxTicks
|
||||
* @type Number
|
||||
*/
|
||||
/**
|
||||
* The distance between each tick.
|
||||
* @name INumericTickGenerationOptions#stepSize
|
||||
* @type Number
|
||||
* @optional
|
||||
*/
|
||||
/**
|
||||
* Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
|
||||
* @name INumericTickGenerationOptions#min
|
||||
* @type Number
|
||||
* @optional
|
||||
*/
|
||||
/**
|
||||
* The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
|
||||
* @name INumericTickGenerationOptions#max
|
||||
* @type Number
|
||||
* @optional
|
||||
*/
|
||||
generators: {
|
||||
/**
|
||||
* Interface for the options provided to the numeric tick generator
|
||||
* @interface INumericTickGenerationOptions
|
||||
*/
|
||||
/**
|
||||
* The maximum number of ticks to display
|
||||
* @name INumericTickGenerationOptions#maxTicks
|
||||
* @type Number
|
||||
*/
|
||||
/**
|
||||
* The distance between each tick.
|
||||
* @name INumericTickGenerationOptions#stepSize
|
||||
* @type Number
|
||||
* @optional
|
||||
*/
|
||||
/**
|
||||
* Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
|
||||
* @name INumericTickGenerationOptions#min
|
||||
* @type Number
|
||||
* @optional
|
||||
*/
|
||||
/**
|
||||
* The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
|
||||
* @name INumericTickGenerationOptions#max
|
||||
* @type Number
|
||||
* @optional
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generate a set of linear ticks
|
||||
* @method Chart.Ticks.generators.linear
|
||||
* @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
|
||||
* @param dataRange {IRange} the range of the data
|
||||
* @returns {Array<Number>} array of tick values
|
||||
*/
|
||||
linear: function(generationOptions, dataRange) {
|
||||
var ticks = [];
|
||||
// To get a "nice" value for the tick spacing, we will use the appropriately named
|
||||
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
|
||||
// for details.
|
||||
/**
|
||||
* Generate a set of linear ticks
|
||||
* @method Chart.Ticks.generators.linear
|
||||
* @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
|
||||
* @param dataRange {IRange} the range of the data
|
||||
* @returns {Array<Number>} array of tick values
|
||||
*/
|
||||
linear: function(generationOptions, dataRange) {
|
||||
var ticks = [];
|
||||
// To get a "nice" value for the tick spacing, we will use the appropriately named
|
||||
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
|
||||
// for details.
|
||||
|
||||
var spacing;
|
||||
if (generationOptions.stepSize && generationOptions.stepSize > 0) {
|
||||
spacing = generationOptions.stepSize;
|
||||
} else {
|
||||
var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
|
||||
spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
|
||||
}
|
||||
var niceMin = Math.floor(dataRange.min / spacing) * spacing;
|
||||
var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
|
||||
|
||||
// If min, max and stepSize is set and they make an evenly spaced scale use it.
|
||||
if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
|
||||
// If very close to our whole number, use it.
|
||||
if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
|
||||
niceMin = generationOptions.min;
|
||||
niceMax = generationOptions.max;
|
||||
}
|
||||
}
|
||||
|
||||
var numSpaces = (niceMax - niceMin) / spacing;
|
||||
// If very close to our rounded value, use it.
|
||||
if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
|
||||
numSpaces = Math.round(numSpaces);
|
||||
} else {
|
||||
numSpaces = Math.ceil(numSpaces);
|
||||
}
|
||||
|
||||
// Put the values into the ticks array
|
||||
ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
|
||||
for (var j = 1; j < numSpaces; ++j) {
|
||||
ticks.push(niceMin + (j * spacing));
|
||||
}
|
||||
ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
|
||||
|
||||
return ticks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a set of logarithmic ticks
|
||||
* @method Chart.Ticks.generators.logarithmic
|
||||
* @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
|
||||
* @param dataRange {IRange} the range of the data
|
||||
* @returns {Array<Number>} array of tick values
|
||||
*/
|
||||
logarithmic: function(generationOptions, dataRange) {
|
||||
var ticks = [];
|
||||
var getValueOrDefault = helpers.getValueOrDefault;
|
||||
|
||||
// Figure out what the max number of ticks we can support it is based on the size of
|
||||
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
|
||||
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
|
||||
// the graph
|
||||
var tickVal = getValueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
|
||||
|
||||
var endExp = Math.floor(helpers.log10(dataRange.max));
|
||||
var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
|
||||
var exp;
|
||||
var significand;
|
||||
|
||||
if (tickVal === 0) {
|
||||
exp = Math.floor(helpers.log10(dataRange.minNotZero));
|
||||
significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
|
||||
|
||||
ticks.push(tickVal);
|
||||
tickVal = significand * Math.pow(10, exp);
|
||||
} else {
|
||||
exp = Math.floor(helpers.log10(tickVal));
|
||||
significand = Math.floor(tickVal / Math.pow(10, exp));
|
||||
}
|
||||
|
||||
do {
|
||||
ticks.push(tickVal);
|
||||
|
||||
++significand;
|
||||
if (significand === 10) {
|
||||
significand = 1;
|
||||
++exp;
|
||||
}
|
||||
|
||||
tickVal = significand * Math.pow(10, exp);
|
||||
} while (exp < endExp || (exp === endExp && significand < endSignificand));
|
||||
|
||||
var lastTick = getValueOrDefault(generationOptions.max, tickVal);
|
||||
ticks.push(lastTick);
|
||||
|
||||
return ticks;
|
||||
var spacing;
|
||||
if (generationOptions.stepSize && generationOptions.stepSize > 0) {
|
||||
spacing = generationOptions.stepSize;
|
||||
} else {
|
||||
var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
|
||||
spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
|
||||
}
|
||||
var niceMin = Math.floor(dataRange.min / spacing) * spacing;
|
||||
var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
|
||||
|
||||
// If min, max and stepSize is set and they make an evenly spaced scale use it.
|
||||
if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
|
||||
// If very close to our whole number, use it.
|
||||
if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
|
||||
niceMin = generationOptions.min;
|
||||
niceMax = generationOptions.max;
|
||||
}
|
||||
}
|
||||
|
||||
var numSpaces = (niceMax - niceMin) / spacing;
|
||||
// If very close to our rounded value, use it.
|
||||
if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
|
||||
numSpaces = Math.round(numSpaces);
|
||||
} else {
|
||||
numSpaces = Math.ceil(numSpaces);
|
||||
}
|
||||
|
||||
// Put the values into the ticks array
|
||||
ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
|
||||
for (var j = 1; j < numSpaces; ++j) {
|
||||
ticks.push(niceMin + (j * spacing));
|
||||
}
|
||||
ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
|
||||
|
||||
return ticks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Namespace to hold formatters for different types of ticks
|
||||
* @namespace Chart.Ticks.formatters
|
||||
* Generate a set of logarithmic ticks
|
||||
* @method Chart.Ticks.generators.logarithmic
|
||||
* @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
|
||||
* @param dataRange {IRange} the range of the data
|
||||
* @returns {Array<Number>} array of tick values
|
||||
*/
|
||||
formatters: {
|
||||
/**
|
||||
* Formatter for value labels
|
||||
* @method Chart.Ticks.formatters.values
|
||||
* @param value the value to display
|
||||
* @return {String|Array} the label to display
|
||||
*/
|
||||
values: function(value) {
|
||||
return helpers.isArray(value) ? value : '' + value;
|
||||
},
|
||||
logarithmic: function(generationOptions, dataRange) {
|
||||
var ticks = [];
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
|
||||
/**
|
||||
* Formatter for linear numeric ticks
|
||||
* @method Chart.Ticks.formatters.linear
|
||||
* @param tickValue {Number} the value to be formatted
|
||||
* @param index {Number} the position of the tickValue parameter in the ticks array
|
||||
* @param ticks {Array<Number>} the list of ticks being converted
|
||||
* @return {String} string representation of the tickValue parameter
|
||||
*/
|
||||
linear: function(tickValue, index, ticks) {
|
||||
// If we have lots of ticks, don't use the ones
|
||||
var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
|
||||
// Figure out what the max number of ticks we can support it is based on the size of
|
||||
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
|
||||
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
|
||||
// the graph
|
||||
var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
|
||||
|
||||
// If we have a number like 2.5 as the delta, figure out how many decimal places we need
|
||||
if (Math.abs(delta) > 1) {
|
||||
if (tickValue !== Math.floor(tickValue)) {
|
||||
// not an integer
|
||||
delta = tickValue - Math.floor(tickValue);
|
||||
}
|
||||
}
|
||||
var endExp = Math.floor(helpers.log10(dataRange.max));
|
||||
var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
|
||||
var exp, significand;
|
||||
|
||||
var logDelta = helpers.log10(Math.abs(delta));
|
||||
var tickString = '';
|
||||
if (tickVal === 0) {
|
||||
exp = Math.floor(helpers.log10(dataRange.minNotZero));
|
||||
significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
|
||||
|
||||
if (tickValue !== 0) {
|
||||
var numDecimal = -1 * Math.floor(logDelta);
|
||||
numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
|
||||
tickString = tickValue.toFixed(numDecimal);
|
||||
} else {
|
||||
tickString = '0'; // never show decimal places for 0
|
||||
}
|
||||
|
||||
return tickString;
|
||||
},
|
||||
|
||||
logarithmic: function(tickValue, index, ticks) {
|
||||
var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
|
||||
|
||||
if (tickValue === 0) {
|
||||
return '0';
|
||||
} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
|
||||
return tickValue.toExponential();
|
||||
}
|
||||
return '';
|
||||
ticks.push(tickVal);
|
||||
tickVal = significand * Math.pow(10, exp);
|
||||
} else {
|
||||
exp = Math.floor(helpers.log10(tickVal));
|
||||
significand = Math.floor(tickVal / Math.pow(10, exp));
|
||||
}
|
||||
|
||||
do {
|
||||
ticks.push(tickVal);
|
||||
|
||||
++significand;
|
||||
if (significand === 10) {
|
||||
significand = 1;
|
||||
++exp;
|
||||
}
|
||||
|
||||
tickVal = significand * Math.pow(10, exp);
|
||||
} while (exp < endExp || (exp === endExp && significand < endSignificand));
|
||||
|
||||
var lastTick = valueOrDefault(generationOptions.max, tickVal);
|
||||
ticks.push(lastTick);
|
||||
|
||||
return ticks;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Namespace to hold formatters for different types of ticks
|
||||
* @namespace Chart.Ticks.formatters
|
||||
*/
|
||||
formatters: {
|
||||
/**
|
||||
* Formatter for value labels
|
||||
* @method Chart.Ticks.formatters.values
|
||||
* @param value the value to display
|
||||
* @return {String|Array} the label to display
|
||||
*/
|
||||
values: function(value) {
|
||||
return helpers.isArray(value) ? value : '' + value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Formatter for linear numeric ticks
|
||||
* @method Chart.Ticks.formatters.linear
|
||||
* @param tickValue {Number} the value to be formatted
|
||||
* @param index {Number} the position of the tickValue parameter in the ticks array
|
||||
* @param ticks {Array<Number>} the list of ticks being converted
|
||||
* @return {String} string representation of the tickValue parameter
|
||||
*/
|
||||
linear: function(tickValue, index, ticks) {
|
||||
// If we have lots of ticks, don't use the ones
|
||||
var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
|
||||
|
||||
// If we have a number like 2.5 as the delta, figure out how many decimal places we need
|
||||
if (Math.abs(delta) > 1) {
|
||||
if (tickValue !== Math.floor(tickValue)) {
|
||||
// not an integer
|
||||
delta = tickValue - Math.floor(tickValue);
|
||||
}
|
||||
}
|
||||
|
||||
var logDelta = helpers.log10(Math.abs(delta));
|
||||
var tickString = '';
|
||||
|
||||
if (tickValue !== 0) {
|
||||
var numDecimal = -1 * Math.floor(logDelta);
|
||||
numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
|
||||
tickString = tickValue.toFixed(numDecimal);
|
||||
} else {
|
||||
tickString = '0'; // never show decimal places for 0
|
||||
}
|
||||
|
||||
return tickString;
|
||||
},
|
||||
|
||||
logarithmic: function(tickValue, index, ticks) {
|
||||
var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
|
||||
|
||||
if (tickValue === 0) {
|
||||
return '0';
|
||||
} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
|
||||
return tickValue.toExponential();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
'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;
|
||||
|
||||
/**
|
||||
* Helper method to merge the opacity into a color
|
||||
*/
|
||||
function mergeOpacity(colorString, opacity) {
|
||||
var color = helpers.color(colorString);
|
||||
return color.alpha(opacity * color.alpha()).rgbaString();
|
||||
}
|
||||
|
||||
Chart.defaults.global.tooltips = {
|
||||
defaults._set('global', {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
custom: null,
|
||||
mode: 'nearest',
|
||||
@@ -87,6 +80,9 @@ module.exports = function(Chart) {
|
||||
backgroundColor: view.backgroundColor
|
||||
};
|
||||
},
|
||||
labelTextColor: function() {
|
||||
return this._options.bodyFontColor;
|
||||
},
|
||||
afterLabel: helpers.noop,
|
||||
|
||||
// Args are: (tooltipItems, data)
|
||||
@@ -97,7 +93,18 @@ module.exports = function(Chart) {
|
||||
footer: helpers.noop,
|
||||
afterFooter: helpers.noop
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
/**
|
||||
* Helper method to merge the opacity into a color
|
||||
*/
|
||||
function mergeOpacity(colorString, opacity) {
|
||||
var color = helpers.color(colorString);
|
||||
return color.alpha(opacity * color.alpha()).rgbaString();
|
||||
}
|
||||
|
||||
// Helper to push or concat based on if the 2nd parameter is an array or not
|
||||
function pushOrConcat(base, toPush) {
|
||||
@@ -119,8 +126,8 @@ module.exports = function(Chart) {
|
||||
function createTooltipItem(element) {
|
||||
var xScale = element._xScale;
|
||||
var yScale = element._yScale || element._scale; // handle radar || polarArea charts
|
||||
var index = element._index,
|
||||
datasetIndex = element._datasetIndex;
|
||||
var index = element._index;
|
||||
var datasetIndex = element._datasetIndex;
|
||||
|
||||
return {
|
||||
xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
|
||||
@@ -137,8 +144,8 @@ module.exports = function(Chart) {
|
||||
* @param tooltipOpts {Object} the tooltip options
|
||||
*/
|
||||
function getBaseModel(tooltipOpts) {
|
||||
var globalDefaults = Chart.defaults.global;
|
||||
var getValueOrDefault = helpers.getValueOrDefault;
|
||||
var globalDefaults = defaults.global;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
|
||||
return {
|
||||
// Positioning
|
||||
@@ -149,26 +156,26 @@ module.exports = function(Chart) {
|
||||
|
||||
// Body
|
||||
bodyFontColor: tooltipOpts.bodyFontColor,
|
||||
_bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
|
||||
_bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
|
||||
_bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
|
||||
_bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
|
||||
_bodyAlign: tooltipOpts.bodyAlign,
|
||||
bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
|
||||
bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
|
||||
bodySpacing: tooltipOpts.bodySpacing,
|
||||
|
||||
// Title
|
||||
titleFontColor: tooltipOpts.titleFontColor,
|
||||
_titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
|
||||
_titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
|
||||
titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
|
||||
_titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
|
||||
_titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
|
||||
titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
|
||||
_titleAlign: tooltipOpts.titleAlign,
|
||||
titleSpacing: tooltipOpts.titleSpacing,
|
||||
titleMarginBottom: tooltipOpts.titleMarginBottom,
|
||||
|
||||
// Footer
|
||||
footerFontColor: tooltipOpts.footerFontColor,
|
||||
_footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
|
||||
_footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
|
||||
footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
|
||||
_footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
|
||||
_footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
|
||||
footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
|
||||
_footerAlign: tooltipOpts.footerAlign,
|
||||
footerSpacing: tooltipOpts.footerSpacing,
|
||||
footerMarginTop: tooltipOpts.footerMarginTop,
|
||||
@@ -203,9 +210,9 @@ module.exports = function(Chart) {
|
||||
|
||||
var titleLineCount = model.title.length;
|
||||
var footerLineCount = model.footer.length;
|
||||
var titleFontSize = model.titleFontSize,
|
||||
bodyFontSize = model.bodyFontSize,
|
||||
footerFontSize = model.footerFontSize;
|
||||
var titleFontSize = model.titleFontSize;
|
||||
var bodyFontSize = model.bodyFontSize;
|
||||
var footerFontSize = model.footerFontSize;
|
||||
|
||||
height += titleLineCount * titleFontSize; // Title Lines
|
||||
height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
|
||||
@@ -334,13 +341,13 @@ module.exports = function(Chart) {
|
||||
var x = vm.x;
|
||||
var y = vm.y;
|
||||
|
||||
var caretSize = vm.caretSize,
|
||||
caretPadding = vm.caretPadding,
|
||||
cornerRadius = vm.cornerRadius,
|
||||
xAlign = alignment.xAlign,
|
||||
yAlign = alignment.yAlign,
|
||||
paddingAndSize = caretSize + caretPadding,
|
||||
radiusAndPadding = cornerRadius + caretPadding;
|
||||
var caretSize = vm.caretSize;
|
||||
var caretPadding = vm.caretPadding;
|
||||
var cornerRadius = vm.cornerRadius;
|
||||
var xAlign = alignment.xAlign;
|
||||
var yAlign = alignment.yAlign;
|
||||
var paddingAndSize = caretSize + caretPadding;
|
||||
var radiusAndPadding = cornerRadius + caretPadding;
|
||||
|
||||
if (xAlign === 'right') {
|
||||
x -= size.width;
|
||||
@@ -374,7 +381,7 @@ module.exports = function(Chart) {
|
||||
};
|
||||
}
|
||||
|
||||
Chart.Tooltip = Chart.Element.extend({
|
||||
Chart.Tooltip = Element.extend({
|
||||
initialize: function() {
|
||||
this._model = getBaseModel(this._options);
|
||||
},
|
||||
@@ -386,9 +393,9 @@ module.exports = function(Chart) {
|
||||
var opts = me._options;
|
||||
var callbacks = opts.callbacks;
|
||||
|
||||
var beforeTitle = callbacks.beforeTitle.apply(me, arguments),
|
||||
title = callbacks.title.apply(me, arguments),
|
||||
afterTitle = callbacks.afterTitle.apply(me, arguments);
|
||||
var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
|
||||
var title = callbacks.title.apply(me, arguments);
|
||||
var afterTitle = callbacks.afterTitle.apply(me, arguments);
|
||||
|
||||
var lines = [];
|
||||
lines = pushOrConcat(lines, beforeTitle);
|
||||
@@ -487,6 +494,7 @@ module.exports = function(Chart) {
|
||||
model.opacity = 1;
|
||||
|
||||
var labelColors = [];
|
||||
var labelTextColors = [];
|
||||
tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition);
|
||||
|
||||
var tooltipItems = [];
|
||||
@@ -511,8 +519,10 @@ module.exports = function(Chart) {
|
||||
// Determine colors for boxes
|
||||
helpers.each(tooltipItems, function(tooltipItem) {
|
||||
labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
|
||||
labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
|
||||
});
|
||||
|
||||
|
||||
// Build the Text Lines
|
||||
model.title = me.getTitle(tooltipItems, data);
|
||||
model.beforeBody = me.getBeforeBody(tooltipItems, data);
|
||||
@@ -525,6 +535,7 @@ module.exports = function(Chart) {
|
||||
model.y = Math.round(tooltipPosition.y);
|
||||
model.caretPadding = opts.caretPadding;
|
||||
model.labelColors = labelColors;
|
||||
model.labelTextColors = labelTextColors;
|
||||
|
||||
// data points
|
||||
model.dataPoints = tooltipItems;
|
||||
@@ -567,16 +578,15 @@ module.exports = function(Chart) {
|
||||
ctx.lineTo(caretPosition.x3, caretPosition.y3);
|
||||
},
|
||||
getCaretPosition: function(tooltipPoint, size, vm) {
|
||||
var x1, x2, x3;
|
||||
var y1, y2, y3;
|
||||
var x1, x2, x3, y1, y2, y3;
|
||||
var caretSize = vm.caretSize;
|
||||
var cornerRadius = vm.cornerRadius;
|
||||
var xAlign = vm.xAlign,
|
||||
yAlign = vm.yAlign;
|
||||
var ptX = tooltipPoint.x,
|
||||
ptY = tooltipPoint.y;
|
||||
var width = size.width,
|
||||
height = size.height;
|
||||
var xAlign = vm.xAlign;
|
||||
var yAlign = vm.yAlign;
|
||||
var ptX = tooltipPoint.x;
|
||||
var ptY = tooltipPoint.y;
|
||||
var width = size.width;
|
||||
var height = size.height;
|
||||
|
||||
if (yAlign === 'center') {
|
||||
y2 = ptY + (height / 2);
|
||||
@@ -633,8 +643,8 @@ module.exports = function(Chart) {
|
||||
ctx.textAlign = vm._titleAlign;
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
var titleFontSize = vm.titleFontSize,
|
||||
titleSpacing = vm.titleSpacing;
|
||||
var titleFontSize = vm.titleFontSize;
|
||||
var titleSpacing = vm.titleSpacing;
|
||||
|
||||
ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
|
||||
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
|
||||
@@ -657,9 +667,6 @@ module.exports = function(Chart) {
|
||||
|
||||
ctx.textAlign = vm._bodyAlign;
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
var textColor = mergeOpacity(vm.bodyFontColor, opacity);
|
||||
ctx.fillStyle = textColor;
|
||||
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
|
||||
|
||||
// Before Body
|
||||
@@ -687,13 +694,14 @@ module.exports = function(Chart) {
|
||||
ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
|
||||
|
||||
// Border
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
|
||||
ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
|
||||
|
||||
// Inner square
|
||||
ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
|
||||
ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
|
||||
|
||||
var textColor = mergeOpacity(vm.labelTextColors[i], opacity);
|
||||
ctx.fillStyle = textColor;
|
||||
}
|
||||
|
||||
@@ -906,10 +914,9 @@ module.exports = function(Chart) {
|
||||
nearest: function(elements, eventPosition) {
|
||||
var x = eventPosition.x;
|
||||
var y = eventPosition.y;
|
||||
|
||||
var nearestElement;
|
||||
var minDistance = Number.POSITIVE_INFINITY;
|
||||
var i, len;
|
||||
var i, len, nearestElement;
|
||||
|
||||
for (i = 0, len = elements.length; i < len; ++i) {
|
||||
var el = elements[i];
|
||||
if (el && el.hasValue()) {
|
||||
|
||||
@@ -1,104 +1,107 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var defaults = require('../core/core.defaults');
|
||||
var Element = require('../core/core.element');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var helpers = Chart.helpers,
|
||||
globalOpts = Chart.defaults.global;
|
||||
|
||||
globalOpts.elements.arc = {
|
||||
backgroundColor: globalOpts.defaultColor,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
};
|
||||
|
||||
Chart.elements.Arc = Chart.Element.extend({
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
|
||||
}
|
||||
return false;
|
||||
},
|
||||
inRange: function(chartX, chartY) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
|
||||
x: chartX,
|
||||
y: chartY
|
||||
}),
|
||||
angle = pointRelativePosition.angle,
|
||||
distance = pointRelativePosition.distance;
|
||||
|
||||
// Sanitise angle range
|
||||
var startAngle = vm.startAngle;
|
||||
var endAngle = vm.endAngle;
|
||||
while (endAngle < startAngle) {
|
||||
endAngle += 2.0 * Math.PI;
|
||||
}
|
||||
while (angle > endAngle) {
|
||||
angle -= 2.0 * Math.PI;
|
||||
}
|
||||
while (angle < startAngle) {
|
||||
angle += 2.0 * Math.PI;
|
||||
}
|
||||
|
||||
// Check if within the range of the open/close angle
|
||||
var betweenAngles = (angle >= startAngle && angle <= endAngle),
|
||||
withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
|
||||
|
||||
return (betweenAngles && withinRadius);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getCenterPoint: function() {
|
||||
var vm = this._view;
|
||||
var halfAngle = (vm.startAngle + vm.endAngle) / 2;
|
||||
var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
|
||||
return {
|
||||
x: vm.x + Math.cos(halfAngle) * halfRadius,
|
||||
y: vm.y + Math.sin(halfAngle) * halfRadius
|
||||
};
|
||||
},
|
||||
getArea: function() {
|
||||
var vm = this._view;
|
||||
return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
|
||||
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
|
||||
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
|
||||
return {
|
||||
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
|
||||
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
|
||||
};
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx,
|
||||
vm = this._view,
|
||||
sA = vm.startAngle,
|
||||
eA = vm.endAngle;
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
|
||||
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = vm.borderWidth;
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor;
|
||||
|
||||
ctx.fill();
|
||||
ctx.lineJoin = 'bevel';
|
||||
|
||||
if (vm.borderWidth) {
|
||||
ctx.stroke();
|
||||
}
|
||||
defaults._set('global', {
|
||||
elements: {
|
||||
arc: {
|
||||
backgroundColor: defaults.global.defaultColor,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Element.extend({
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
inRange: function(chartX, chartY) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY});
|
||||
var angle = pointRelativePosition.angle;
|
||||
var distance = pointRelativePosition.distance;
|
||||
|
||||
// Sanitise angle range
|
||||
var startAngle = vm.startAngle;
|
||||
var endAngle = vm.endAngle;
|
||||
while (endAngle < startAngle) {
|
||||
endAngle += 2.0 * Math.PI;
|
||||
}
|
||||
while (angle > endAngle) {
|
||||
angle -= 2.0 * Math.PI;
|
||||
}
|
||||
while (angle < startAngle) {
|
||||
angle += 2.0 * Math.PI;
|
||||
}
|
||||
|
||||
// Check if within the range of the open/close angle
|
||||
var betweenAngles = (angle >= startAngle && angle <= endAngle);
|
||||
var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
|
||||
|
||||
return (betweenAngles && withinRadius);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getCenterPoint: function() {
|
||||
var vm = this._view;
|
||||
var halfAngle = (vm.startAngle + vm.endAngle) / 2;
|
||||
var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
|
||||
return {
|
||||
x: vm.x + Math.cos(halfAngle) * halfRadius,
|
||||
y: vm.y + Math.sin(halfAngle) * halfRadius
|
||||
};
|
||||
},
|
||||
|
||||
getArea: function() {
|
||||
var vm = this._view;
|
||||
return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
|
||||
},
|
||||
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
|
||||
var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
|
||||
|
||||
return {
|
||||
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
|
||||
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
|
||||
};
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
var sA = vm.startAngle;
|
||||
var eA = vm.endAngle;
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
|
||||
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = vm.borderWidth;
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor;
|
||||
|
||||
ctx.fill();
|
||||
ctx.lineJoin = 'bevel';
|
||||
|
||||
if (vm.borderWidth) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,87 +1,91 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var defaults = require('../core/core.defaults');
|
||||
var Element = require('../core/core.element');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
var globalDefaults = Chart.defaults.global;
|
||||
var globalDefaults = defaults.global;
|
||||
|
||||
Chart.defaults.global.elements.line = {
|
||||
tension: 0.4,
|
||||
backgroundColor: globalDefaults.defaultColor,
|
||||
borderWidth: 3,
|
||||
borderColor: globalDefaults.defaultColor,
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
capBezierPoints: true,
|
||||
fill: true, // do we fill in the area between the line and its base axis
|
||||
};
|
||||
defaults._set('global', {
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0.4,
|
||||
backgroundColor: globalDefaults.defaultColor,
|
||||
borderWidth: 3,
|
||||
borderColor: globalDefaults.defaultColor,
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
capBezierPoints: true,
|
||||
fill: true, // do we fill in the area between the line and its base axis
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Chart.elements.Line = Chart.Element.extend({
|
||||
draw: function() {
|
||||
var me = this;
|
||||
var vm = me._view;
|
||||
var ctx = me._chart.ctx;
|
||||
var spanGaps = vm.spanGaps;
|
||||
var points = me._children.slice(); // clone array
|
||||
var globalOptionLineElements = globalDefaults.elements.line;
|
||||
var lastDrawnIndex = -1;
|
||||
var index, current, previous, currentVM;
|
||||
module.exports = Element.extend({
|
||||
draw: function() {
|
||||
var me = this;
|
||||
var vm = me._view;
|
||||
var ctx = me._chart.ctx;
|
||||
var spanGaps = vm.spanGaps;
|
||||
var points = me._children.slice(); // clone array
|
||||
var globalOptionLineElements = globalDefaults.elements.line;
|
||||
var lastDrawnIndex = -1;
|
||||
var index, current, previous, currentVM;
|
||||
|
||||
// If we are looping, adding the first point again
|
||||
if (me._loop && points.length) {
|
||||
points.push(points[0]);
|
||||
}
|
||||
// If we are looping, adding the first point again
|
||||
if (me._loop && points.length) {
|
||||
points.push(points[0]);
|
||||
}
|
||||
|
||||
ctx.save();
|
||||
ctx.save();
|
||||
|
||||
// Stroke Line Options
|
||||
ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
|
||||
// Stroke Line Options
|
||||
ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
|
||||
|
||||
// IE 9 and 10 do not support line dash
|
||||
if (ctx.setLineDash) {
|
||||
ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
|
||||
}
|
||||
// IE 9 and 10 do not support line dash
|
||||
if (ctx.setLineDash) {
|
||||
ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
|
||||
}
|
||||
|
||||
ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
|
||||
ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
|
||||
ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
|
||||
ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
|
||||
ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
|
||||
ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
|
||||
ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
|
||||
ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
|
||||
|
||||
// Stroke Line
|
||||
ctx.beginPath();
|
||||
lastDrawnIndex = -1;
|
||||
// Stroke Line
|
||||
ctx.beginPath();
|
||||
lastDrawnIndex = -1;
|
||||
|
||||
for (index = 0; index < points.length; ++index) {
|
||||
current = points[index];
|
||||
previous = helpers.previousItem(points, index);
|
||||
currentVM = current._view;
|
||||
for (index = 0; index < points.length; ++index) {
|
||||
current = points[index];
|
||||
previous = helpers.previousItem(points, index);
|
||||
currentVM = current._view;
|
||||
|
||||
// First point moves to it's starting position no matter what
|
||||
if (index === 0) {
|
||||
if (!currentVM.skip) {
|
||||
// First point moves to it's starting position no matter what
|
||||
if (index === 0) {
|
||||
if (!currentVM.skip) {
|
||||
ctx.moveTo(currentVM.x, currentVM.y);
|
||||
lastDrawnIndex = index;
|
||||
}
|
||||
} else {
|
||||
previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
|
||||
|
||||
if (!currentVM.skip) {
|
||||
if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
|
||||
// There was a gap and this is the first point after the gap
|
||||
ctx.moveTo(currentVM.x, currentVM.y);
|
||||
lastDrawnIndex = index;
|
||||
}
|
||||
} else {
|
||||
previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
|
||||
|
||||
if (!currentVM.skip) {
|
||||
if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
|
||||
// There was a gap and this is the first point after the gap
|
||||
ctx.moveTo(currentVM.x, currentVM.y);
|
||||
} else {
|
||||
// Line to next point
|
||||
helpers.canvas.lineTo(ctx, previous._view, current._view);
|
||||
}
|
||||
lastDrawnIndex = index;
|
||||
} else {
|
||||
// Line to next point
|
||||
helpers.canvas.lineTo(ctx, previous._view, current._view);
|
||||
}
|
||||
lastDrawnIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,100 +1,106 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var defaults = require('../core/core.defaults');
|
||||
var Element = require('../core/core.element');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var helpers = Chart.helpers,
|
||||
globalOpts = Chart.defaults.global,
|
||||
defaultColor = globalOpts.defaultColor;
|
||||
var defaultColor = defaults.global.defaultColor;
|
||||
|
||||
globalOpts.elements.point = {
|
||||
radius: 3,
|
||||
pointStyle: 'circle',
|
||||
backgroundColor: defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: defaultColor,
|
||||
// Hover
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1
|
||||
};
|
||||
|
||||
function xRange(mouseX) {
|
||||
var vm = this._view;
|
||||
return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
|
||||
}
|
||||
|
||||
function yRange(mouseY) {
|
||||
var vm = this._view;
|
||||
return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
|
||||
}
|
||||
|
||||
Chart.elements.Point = Chart.Element.extend({
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._view;
|
||||
return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
|
||||
},
|
||||
|
||||
inLabelRange: xRange,
|
||||
inXRange: xRange,
|
||||
inYRange: yRange,
|
||||
|
||||
getCenterPoint: function() {
|
||||
var vm = this._view;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y
|
||||
};
|
||||
},
|
||||
getArea: function() {
|
||||
return Math.PI * Math.pow(this._view.radius, 2);
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y,
|
||||
padding: vm.radius + vm.borderWidth
|
||||
};
|
||||
},
|
||||
draw: function(chartArea) {
|
||||
var vm = this._view;
|
||||
var model = this._model;
|
||||
var ctx = this._chart.ctx;
|
||||
var pointStyle = vm.pointStyle;
|
||||
var radius = vm.radius;
|
||||
var x = vm.x;
|
||||
var y = vm.y;
|
||||
var color = Chart.helpers.color;
|
||||
var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
|
||||
var ratio = 0;
|
||||
|
||||
if (vm.skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.strokeStyle = vm.borderColor || defaultColor;
|
||||
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
|
||||
ctx.fillStyle = vm.backgroundColor || defaultColor;
|
||||
|
||||
// Cliping for Points.
|
||||
// going out from inner charArea?
|
||||
if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right*errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom*errMargin < model.y))) {
|
||||
// Point fade out
|
||||
if (model.x < chartArea.left) {
|
||||
ratio = (x - model.x) / (chartArea.left - model.x);
|
||||
} else if (chartArea.right*errMargin < model.x) {
|
||||
ratio = (model.x - x) / (model.x - chartArea.right);
|
||||
} else if (model.y < chartArea.top) {
|
||||
ratio = (y - model.y) / (chartArea.top - model.y);
|
||||
} else if (chartArea.bottom*errMargin < model.y) {
|
||||
ratio = (model.y - y) / (model.y - chartArea.bottom);
|
||||
}
|
||||
ratio = Math.round(ratio*100) / 100;
|
||||
ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString();
|
||||
ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
|
||||
}
|
||||
|
||||
Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y);
|
||||
defaults._set('global', {
|
||||
elements: {
|
||||
point: {
|
||||
radius: 3,
|
||||
pointStyle: 'circle',
|
||||
backgroundColor: defaultColor,
|
||||
borderColor: defaultColor,
|
||||
borderWidth: 1,
|
||||
// Hover
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
function xRange(mouseX) {
|
||||
var vm = this._view;
|
||||
return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
|
||||
}
|
||||
|
||||
function yRange(mouseY) {
|
||||
var vm = this._view;
|
||||
return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
|
||||
}
|
||||
|
||||
module.exports = Element.extend({
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._view;
|
||||
return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
|
||||
},
|
||||
|
||||
inLabelRange: xRange,
|
||||
inXRange: xRange,
|
||||
inYRange: yRange,
|
||||
|
||||
getCenterPoint: function() {
|
||||
var vm = this._view;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y
|
||||
};
|
||||
},
|
||||
|
||||
getArea: function() {
|
||||
return Math.PI * Math.pow(this._view.radius, 2);
|
||||
},
|
||||
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y,
|
||||
padding: vm.radius + vm.borderWidth
|
||||
};
|
||||
},
|
||||
|
||||
draw: function(chartArea) {
|
||||
var vm = this._view;
|
||||
var model = this._model;
|
||||
var ctx = this._chart.ctx;
|
||||
var pointStyle = vm.pointStyle;
|
||||
var radius = vm.radius;
|
||||
var x = vm.x;
|
||||
var y = vm.y;
|
||||
var color = helpers.color;
|
||||
var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
|
||||
var ratio = 0;
|
||||
|
||||
if (vm.skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.strokeStyle = vm.borderColor || defaultColor;
|
||||
ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);
|
||||
ctx.fillStyle = vm.backgroundColor || defaultColor;
|
||||
|
||||
// Cliping for Points.
|
||||
// going out from inner charArea?
|
||||
if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) {
|
||||
// Point fade out
|
||||
if (model.x < chartArea.left) {
|
||||
ratio = (x - model.x) / (chartArea.left - model.x);
|
||||
} else if (chartArea.right * errMargin < model.x) {
|
||||
ratio = (model.x - x) / (model.x - chartArea.right);
|
||||
} else if (model.y < chartArea.top) {
|
||||
ratio = (y - model.y) / (chartArea.top - model.y);
|
||||
} else if (chartArea.bottom * errMargin < model.y) {
|
||||
ratio = (model.y - y) / (model.y - chartArea.bottom);
|
||||
}
|
||||
ratio = Math.round(ratio * 100) / 100;
|
||||
ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString();
|
||||
ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
|
||||
}
|
||||
|
||||
helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,208 +1,217 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var defaults = require('../core/core.defaults');
|
||||
var Element = require('../core/core.element');
|
||||
|
||||
var globalOpts = Chart.defaults.global;
|
||||
defaults._set('global', {
|
||||
elements: {
|
||||
rectangle: {
|
||||
backgroundColor: defaults.global.defaultColor,
|
||||
borderColor: defaults.global.defaultColor,
|
||||
borderSkipped: 'bottom',
|
||||
borderWidth: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
globalOpts.elements.rectangle = {
|
||||
backgroundColor: globalOpts.defaultColor,
|
||||
borderWidth: 0,
|
||||
borderColor: globalOpts.defaultColor,
|
||||
borderSkipped: 'bottom'
|
||||
};
|
||||
function isVertical(bar) {
|
||||
return bar._view.width !== undefined;
|
||||
}
|
||||
|
||||
function isVertical(bar) {
|
||||
return bar._view.width !== undefined;
|
||||
/**
|
||||
* Helper function to get the bounds of the bar regardless of the orientation
|
||||
* @param bar {Chart.Element.Rectangle} the bar
|
||||
* @return {Bounds} bounds of the bar
|
||||
* @private
|
||||
*/
|
||||
function getBarBounds(bar) {
|
||||
var vm = bar._view;
|
||||
var x1, x2, y1, y2;
|
||||
|
||||
if (isVertical(bar)) {
|
||||
// vertical
|
||||
var halfWidth = vm.width / 2;
|
||||
x1 = vm.x - halfWidth;
|
||||
x2 = vm.x + halfWidth;
|
||||
y1 = Math.min(vm.y, vm.base);
|
||||
y2 = Math.max(vm.y, vm.base);
|
||||
} else {
|
||||
// horizontal bar
|
||||
var halfHeight = vm.height / 2;
|
||||
x1 = Math.min(vm.x, vm.base);
|
||||
x2 = Math.max(vm.x, vm.base);
|
||||
y1 = vm.y - halfHeight;
|
||||
y2 = vm.y + halfHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the bounds of the bar regardless of the orientation
|
||||
* @private
|
||||
* @param bar {Chart.Element.Rectangle} the bar
|
||||
* @return {Bounds} bounds of the bar
|
||||
*/
|
||||
function getBarBounds(bar) {
|
||||
var vm = bar._view;
|
||||
var x1, x2, y1, y2;
|
||||
return {
|
||||
left: x1,
|
||||
top: y1,
|
||||
right: x2,
|
||||
bottom: y2
|
||||
};
|
||||
}
|
||||
|
||||
if (isVertical(bar)) {
|
||||
// vertical
|
||||
var halfWidth = vm.width / 2;
|
||||
x1 = vm.x - halfWidth;
|
||||
x2 = vm.x + halfWidth;
|
||||
y1 = Math.min(vm.y, vm.base);
|
||||
y2 = Math.max(vm.y, vm.base);
|
||||
module.exports = Element.extend({
|
||||
draw: function() {
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
var left, right, top, bottom, signX, signY, borderSkipped;
|
||||
var borderWidth = vm.borderWidth;
|
||||
|
||||
if (!vm.horizontal) {
|
||||
// bar
|
||||
left = vm.x - vm.width / 2;
|
||||
right = vm.x + vm.width / 2;
|
||||
top = vm.y;
|
||||
bottom = vm.base;
|
||||
signX = 1;
|
||||
signY = bottom > top ? 1 : -1;
|
||||
borderSkipped = vm.borderSkipped || 'bottom';
|
||||
} else {
|
||||
// horizontal bar
|
||||
var halfHeight = vm.height / 2;
|
||||
x1 = Math.min(vm.x, vm.base);
|
||||
x2 = Math.max(vm.x, vm.base);
|
||||
y1 = vm.y - halfHeight;
|
||||
y2 = vm.y + halfHeight;
|
||||
left = vm.base;
|
||||
right = vm.x;
|
||||
top = vm.y - vm.height / 2;
|
||||
bottom = vm.y + vm.height / 2;
|
||||
signX = right > left ? 1 : -1;
|
||||
signY = 1;
|
||||
borderSkipped = vm.borderSkipped || 'left';
|
||||
}
|
||||
|
||||
// Canvas doesn't allow us to stroke inside the width so we can
|
||||
// adjust the sizes to fit if we're setting a stroke on the line
|
||||
if (borderWidth) {
|
||||
// borderWidth shold be less than bar width and bar height.
|
||||
var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
|
||||
borderWidth = borderWidth > barSize ? barSize : borderWidth;
|
||||
var halfStroke = borderWidth / 2;
|
||||
// Adjust borderWidth when bar top position is near vm.base(zero).
|
||||
var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
|
||||
var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
|
||||
var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
|
||||
var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
|
||||
// not become a vertical line?
|
||||
if (borderLeft !== borderRight) {
|
||||
top = borderTop;
|
||||
bottom = borderBottom;
|
||||
}
|
||||
// not become a horizontal line?
|
||||
if (borderTop !== borderBottom) {
|
||||
left = borderLeft;
|
||||
right = borderRight;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = vm.backgroundColor;
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = borderWidth;
|
||||
|
||||
// Corner points, from bottom-left to bottom-right clockwise
|
||||
// | 1 2 |
|
||||
// | 0 3 |
|
||||
var corners = [
|
||||
[left, bottom],
|
||||
[left, top],
|
||||
[right, top],
|
||||
[right, bottom]
|
||||
];
|
||||
|
||||
// Find first (starting) corner with fallback to 'bottom'
|
||||
var borders = ['bottom', 'left', 'top', 'right'];
|
||||
var startCorner = borders.indexOf(borderSkipped, 0);
|
||||
if (startCorner === -1) {
|
||||
startCorner = 0;
|
||||
}
|
||||
|
||||
function cornerAt(index) {
|
||||
return corners[(startCorner + index) % 4];
|
||||
}
|
||||
|
||||
// Draw rectangle from 'startCorner'
|
||||
var corner = cornerAt(0);
|
||||
ctx.moveTo(corner[0], corner[1]);
|
||||
|
||||
for (var i = 1; i < 4; i++) {
|
||||
corner = cornerAt(i);
|
||||
ctx.lineTo(corner[0], corner[1]);
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
if (borderWidth) {
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
|
||||
height: function() {
|
||||
var vm = this._view;
|
||||
return vm.base - vm.y;
|
||||
},
|
||||
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var inRange = false;
|
||||
|
||||
if (this._view) {
|
||||
var bounds = getBarBounds(this);
|
||||
inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||
}
|
||||
|
||||
return inRange;
|
||||
},
|
||||
|
||||
inLabelRange: function(mouseX, mouseY) {
|
||||
var me = this;
|
||||
if (!me._view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var inRange = false;
|
||||
var bounds = getBarBounds(me);
|
||||
|
||||
if (isVertical(me)) {
|
||||
inRange = mouseX >= bounds.left && mouseX <= bounds.right;
|
||||
} else {
|
||||
inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||
}
|
||||
|
||||
return inRange;
|
||||
},
|
||||
|
||||
inXRange: function(mouseX) {
|
||||
var bounds = getBarBounds(this);
|
||||
return mouseX >= bounds.left && mouseX <= bounds.right;
|
||||
},
|
||||
|
||||
inYRange: function(mouseY) {
|
||||
var bounds = getBarBounds(this);
|
||||
return mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||
},
|
||||
|
||||
getCenterPoint: function() {
|
||||
var vm = this._view;
|
||||
var x, y;
|
||||
if (isVertical(this)) {
|
||||
x = vm.x;
|
||||
y = (vm.y + vm.base) / 2;
|
||||
} else {
|
||||
x = (vm.x + vm.base) / 2;
|
||||
y = vm.y;
|
||||
}
|
||||
|
||||
return {x: x, y: y};
|
||||
},
|
||||
|
||||
getArea: function() {
|
||||
var vm = this._view;
|
||||
return vm.width * Math.abs(vm.y - vm.base);
|
||||
},
|
||||
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
return {
|
||||
left: x1,
|
||||
top: y1,
|
||||
right: x2,
|
||||
bottom: y2
|
||||
x: vm.x,
|
||||
y: vm.y
|
||||
};
|
||||
}
|
||||
|
||||
Chart.elements.Rectangle = Chart.Element.extend({
|
||||
draw: function() {
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
var left, right, top, bottom, signX, signY, borderSkipped;
|
||||
var borderWidth = vm.borderWidth;
|
||||
|
||||
if (!vm.horizontal) {
|
||||
// bar
|
||||
left = vm.x - vm.width / 2;
|
||||
right = vm.x + vm.width / 2;
|
||||
top = vm.y;
|
||||
bottom = vm.base;
|
||||
signX = 1;
|
||||
signY = bottom > top? 1: -1;
|
||||
borderSkipped = vm.borderSkipped || 'bottom';
|
||||
} else {
|
||||
// horizontal bar
|
||||
left = vm.base;
|
||||
right = vm.x;
|
||||
top = vm.y - vm.height / 2;
|
||||
bottom = vm.y + vm.height / 2;
|
||||
signX = right > left? 1: -1;
|
||||
signY = 1;
|
||||
borderSkipped = vm.borderSkipped || 'left';
|
||||
}
|
||||
|
||||
// Canvas doesn't allow us to stroke inside the width so we can
|
||||
// adjust the sizes to fit if we're setting a stroke on the line
|
||||
if (borderWidth) {
|
||||
// borderWidth shold be less than bar width and bar height.
|
||||
var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
|
||||
borderWidth = borderWidth > barSize? barSize: borderWidth;
|
||||
var halfStroke = borderWidth / 2;
|
||||
// Adjust borderWidth when bar top position is near vm.base(zero).
|
||||
var borderLeft = left + (borderSkipped !== 'left'? halfStroke * signX: 0);
|
||||
var borderRight = right + (borderSkipped !== 'right'? -halfStroke * signX: 0);
|
||||
var borderTop = top + (borderSkipped !== 'top'? halfStroke * signY: 0);
|
||||
var borderBottom = bottom + (borderSkipped !== 'bottom'? -halfStroke * signY: 0);
|
||||
// not become a vertical line?
|
||||
if (borderLeft !== borderRight) {
|
||||
top = borderTop;
|
||||
bottom = borderBottom;
|
||||
}
|
||||
// not become a horizontal line?
|
||||
if (borderTop !== borderBottom) {
|
||||
left = borderLeft;
|
||||
right = borderRight;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = vm.backgroundColor;
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = borderWidth;
|
||||
|
||||
// Corner points, from bottom-left to bottom-right clockwise
|
||||
// | 1 2 |
|
||||
// | 0 3 |
|
||||
var corners = [
|
||||
[left, bottom],
|
||||
[left, top],
|
||||
[right, top],
|
||||
[right, bottom]
|
||||
];
|
||||
|
||||
// Find first (starting) corner with fallback to 'bottom'
|
||||
var borders = ['bottom', 'left', 'top', 'right'];
|
||||
var startCorner = borders.indexOf(borderSkipped, 0);
|
||||
if (startCorner === -1) {
|
||||
startCorner = 0;
|
||||
}
|
||||
|
||||
function cornerAt(index) {
|
||||
return corners[(startCorner + index) % 4];
|
||||
}
|
||||
|
||||
// Draw rectangle from 'startCorner'
|
||||
var corner = cornerAt(0);
|
||||
ctx.moveTo(corner[0], corner[1]);
|
||||
|
||||
for (var i = 1; i < 4; i++) {
|
||||
corner = cornerAt(i);
|
||||
ctx.lineTo(corner[0], corner[1]);
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
if (borderWidth) {
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
height: function() {
|
||||
var vm = this._view;
|
||||
return vm.base - vm.y;
|
||||
},
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var inRange = false;
|
||||
|
||||
if (this._view) {
|
||||
var bounds = getBarBounds(this);
|
||||
inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||
}
|
||||
|
||||
return inRange;
|
||||
},
|
||||
inLabelRange: function(mouseX, mouseY) {
|
||||
var me = this;
|
||||
if (!me._view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var inRange = false;
|
||||
var bounds = getBarBounds(me);
|
||||
|
||||
if (isVertical(me)) {
|
||||
inRange = mouseX >= bounds.left && mouseX <= bounds.right;
|
||||
} else {
|
||||
inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||
}
|
||||
|
||||
return inRange;
|
||||
},
|
||||
inXRange: function(mouseX) {
|
||||
var bounds = getBarBounds(this);
|
||||
return mouseX >= bounds.left && mouseX <= bounds.right;
|
||||
},
|
||||
inYRange: function(mouseY) {
|
||||
var bounds = getBarBounds(this);
|
||||
return mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||
},
|
||||
getCenterPoint: function() {
|
||||
var vm = this._view;
|
||||
var x, y;
|
||||
if (isVertical(this)) {
|
||||
x = vm.x;
|
||||
y = (vm.y + vm.base) / 2;
|
||||
} else {
|
||||
x = (vm.x + vm.base) / 2;
|
||||
y = vm.y;
|
||||
}
|
||||
|
||||
return {x: x, y: y};
|
||||
},
|
||||
getArea: function() {
|
||||
var vm = this._view;
|
||||
return vm.width * Math.abs(vm.y - vm.base);
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
7
src/elements/index.js
Normal file
7
src/elements/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {};
|
||||
module.exports.Arc = require('./element.arc');
|
||||
module.exports.Line = require('./element.line');
|
||||
module.exports.Point = require('./element.point');
|
||||
module.exports.Rectangle = require('./element.rectangle');
|
||||
214
src/helpers/helpers.canvas.js
Normal file
214
src/helpers/helpers.canvas.js
Normal file
@@ -0,0 +1,214 @@
|
||||
'use strict';
|
||||
|
||||
var helpers = require('./helpers.core');
|
||||
|
||||
/**
|
||||
* @namespace Chart.helpers.canvas
|
||||
*/
|
||||
var exports = module.exports = {
|
||||
/**
|
||||
* Clears the entire canvas associated to the given `chart`.
|
||||
* @param {Chart} chart - The chart for which to clear the canvas.
|
||||
*/
|
||||
clear: function(chart) {
|
||||
chart.ctx.clearRect(0, 0, chart.width, chart.height);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a "path" for a rectangle with rounded corners at position (x, y) with a
|
||||
* given size (width, height) and the same `radius` for all corners.
|
||||
* @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
|
||||
* @param {Number} x - The x axis of the coordinate for the rectangle starting point.
|
||||
* @param {Number} y - The y axis of the coordinate for the rectangle starting point.
|
||||
* @param {Number} width - The rectangle's width.
|
||||
* @param {Number} height - The rectangle's height.
|
||||
* @param {Number} radius - The rounded amount (in pixels) for the four corners.
|
||||
* @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
|
||||
*/
|
||||
roundedRect: function(ctx, x, y, width, height, radius) {
|
||||
if (radius) {
|
||||
var rx = Math.min(radius, width / 2);
|
||||
var ry = Math.min(radius, height / 2);
|
||||
|
||||
ctx.moveTo(x + rx, y);
|
||||
ctx.lineTo(x + width - rx, y);
|
||||
ctx.quadraticCurveTo(x + width, y, x + width, y + ry);
|
||||
ctx.lineTo(x + width, y + height - ry);
|
||||
ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);
|
||||
ctx.lineTo(x + rx, y + height);
|
||||
ctx.quadraticCurveTo(x, y + height, x, y + height - ry);
|
||||
ctx.lineTo(x, y + ry);
|
||||
ctx.quadraticCurveTo(x, y, x + rx, y);
|
||||
} else {
|
||||
ctx.rect(x, y, width, height);
|
||||
}
|
||||
},
|
||||
|
||||
drawPoint: function(ctx, style, radius, x, y) {
|
||||
var type, edgeLength, xOffset, yOffset, height, size;
|
||||
|
||||
if (typeof style === 'object') {
|
||||
type = style.toString();
|
||||
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
|
||||
ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNaN(radius) || radius <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (style) {
|
||||
// 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;
|
||||
ctx.beginPath();
|
||||
this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);
|
||||
ctx.closePath();
|
||||
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();
|
||||
},
|
||||
|
||||
clipArea: function(ctx, area) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
|
||||
ctx.clip();
|
||||
},
|
||||
|
||||
unclipArea: function(ctx) {
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
lineTo: function(ctx, previous, target, flip) {
|
||||
if (target.steppedLine) {
|
||||
if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
// DEPRECATIONS
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
|
||||
* @namespace Chart.helpers.clear
|
||||
* @deprecated since version 2.7.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.clear = exports.clear;
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
|
||||
* @namespace Chart.helpers.drawRoundedRectangle
|
||||
* @deprecated since version 2.7.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.drawRoundedRectangle = function(ctx) {
|
||||
ctx.beginPath();
|
||||
exports.roundedRect.apply(exports, arguments);
|
||||
ctx.closePath();
|
||||
};
|
||||
297
src/helpers/helpers.core.js
Normal file
297
src/helpers/helpers.core.js
Normal file
@@ -0,0 +1,297 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @namespace Chart.helpers
|
||||
*/
|
||||
var helpers = {
|
||||
/**
|
||||
* An empty function that can be used, for example, for optional callback.
|
||||
*/
|
||||
noop: function() {},
|
||||
|
||||
/**
|
||||
* Returns a unique id, sequentially generated from a global variable.
|
||||
* @returns {Number}
|
||||
* @function
|
||||
*/
|
||||
uid: (function() {
|
||||
var id = 0;
|
||||
return function() {
|
||||
return id++;
|
||||
};
|
||||
}()),
|
||||
|
||||
/**
|
||||
* Returns true if `value` is neither null nor undefined, else returns false.
|
||||
* @param {*} value - The value to test.
|
||||
* @returns {Boolean}
|
||||
* @since 2.7.0
|
||||
*/
|
||||
isNullOrUndef: function(value) {
|
||||
return value === null || typeof value === 'undefined';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if `value` is an array, else returns false.
|
||||
* @param {*} value - The value to test.
|
||||
* @returns {Boolean}
|
||||
* @function
|
||||
*/
|
||||
isArray: Array.isArray ? Array.isArray : function(value) {
|
||||
return Object.prototype.toString.call(value) === '[object Array]';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if `value` is an object (excluding null), else returns false.
|
||||
* @param {*} value - The value to test.
|
||||
* @returns {Boolean}
|
||||
* @since 2.7.0
|
||||
*/
|
||||
isObject: function(value) {
|
||||
return value !== null && Object.prototype.toString.call(value) === '[object Object]';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns `value` if defined, else returns `defaultValue`.
|
||||
* @param {*} value - The value to return if defined.
|
||||
* @param {*} defaultValue - The value to return if `value` is undefined.
|
||||
* @returns {*}
|
||||
*/
|
||||
valueOrDefault: function(value, defaultValue) {
|
||||
return typeof value === 'undefined' ? defaultValue : value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns value at the given `index` in array if defined, else returns `defaultValue`.
|
||||
* @param {Array} value - The array to lookup for value at `index`.
|
||||
* @param {Number} index - The index in `value` to lookup for value.
|
||||
* @param {*} defaultValue - The value to return if `value[index]` is undefined.
|
||||
* @returns {*}
|
||||
*/
|
||||
valueAtIndexOrDefault: function(value, index, defaultValue) {
|
||||
return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
|
||||
* value returned by `fn`. If `fn` is not a function, this method returns undefined.
|
||||
* @param {Function} fn - The function to call.
|
||||
* @param {Array|undefined|null} args - The arguments with which `fn` should be called.
|
||||
* @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
|
||||
* @returns {*}
|
||||
*/
|
||||
callback: function(fn, args, thisArg) {
|
||||
if (fn && typeof fn.call === 'function') {
|
||||
return fn.apply(thisArg, args);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Note(SB) for performance sake, this method should only be used when loopable type
|
||||
* is unknown or in none intensive code (not called often and small loopable). Else
|
||||
* it's preferable to use a regular for() loop and save extra function calls.
|
||||
* @param {Object|Array} loopable - The object or array to be iterated.
|
||||
* @param {Function} fn - The function to call for each item.
|
||||
* @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
|
||||
* @param {Boolean} [reverse] - If true, iterates backward on the loopable.
|
||||
*/
|
||||
each: function(loopable, fn, thisArg, reverse) {
|
||||
var i, len, keys;
|
||||
if (helpers.isArray(loopable)) {
|
||||
len = loopable.length;
|
||||
if (reverse) {
|
||||
for (i = len - 1; i >= 0; i--) {
|
||||
fn.call(thisArg, loopable[i], i);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < len; i++) {
|
||||
fn.call(thisArg, loopable[i], i);
|
||||
}
|
||||
}
|
||||
} else if (helpers.isObject(loopable)) {
|
||||
keys = Object.keys(loopable);
|
||||
len = keys.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
fn.call(thisArg, loopable[keys[i]], keys[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
|
||||
* @see http://stackoverflow.com/a/14853974
|
||||
* @param {Array} a0 - The array to compare
|
||||
* @param {Array} a1 - The array to compare
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
arrayEquals: function(a0, a1) {
|
||||
var i, ilen, v0, v1;
|
||||
|
||||
if (!a0 || !a1 || a0.length !== a1.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0, ilen = a0.length; i < ilen; ++i) {
|
||||
v0 = a0[i];
|
||||
v1 = a1[i];
|
||||
|
||||
if (v0 instanceof Array && v1 instanceof Array) {
|
||||
if (!helpers.arrayEquals(v0, v1)) {
|
||||
return false;
|
||||
}
|
||||
} else if (v0 !== v1) {
|
||||
// NOTE: two different object instances will never be equal: {x:20} != {x:20}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a deep copy of `source` without keeping references on objects and arrays.
|
||||
* @param {*} source - The value to clone.
|
||||
* @returns {*}
|
||||
*/
|
||||
clone: function(source) {
|
||||
if (helpers.isArray(source)) {
|
||||
return source.map(helpers.clone);
|
||||
}
|
||||
|
||||
if (helpers.isObject(source)) {
|
||||
var target = {};
|
||||
var keys = Object.keys(source);
|
||||
var klen = keys.length;
|
||||
var k = 0;
|
||||
|
||||
for (; k < klen; ++k) {
|
||||
target[keys[k]] = helpers.clone(source[keys[k]]);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
return source;
|
||||
},
|
||||
|
||||
/**
|
||||
* The default merger when Chart.helpers.merge is called without merger option.
|
||||
* Note(SB): this method is also used by configMerge and scaleMerge as fallback.
|
||||
* @private
|
||||
*/
|
||||
_merger: function(key, target, source, options) {
|
||||
var tval = target[key];
|
||||
var sval = source[key];
|
||||
|
||||
if (helpers.isObject(tval) && helpers.isObject(sval)) {
|
||||
helpers.merge(tval, sval, options);
|
||||
} else {
|
||||
target[key] = helpers.clone(sval);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges source[key] in target[key] only if target[key] is undefined.
|
||||
* @private
|
||||
*/
|
||||
_mergerIf: function(key, target, source) {
|
||||
var tval = target[key];
|
||||
var sval = source[key];
|
||||
|
||||
if (helpers.isObject(tval) && helpers.isObject(sval)) {
|
||||
helpers.mergeIf(tval, sval);
|
||||
} else if (!target.hasOwnProperty(key)) {
|
||||
target[key] = helpers.clone(sval);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively deep copies `source` properties into `target` with the given `options`.
|
||||
* IMPORTANT: `target` is not cloned and will be updated with `source` properties.
|
||||
* @param {Object} target - The target object in which all sources are merged into.
|
||||
* @param {Object|Array(Object)} source - Object(s) to merge into `target`.
|
||||
* @param {Object} [options] - Merging options:
|
||||
* @param {Function} [options.merger] - The merge method (key, target, source, options)
|
||||
* @returns {Object} The `target` object.
|
||||
*/
|
||||
merge: function(target, source, options) {
|
||||
var sources = helpers.isArray(source) ? source : [source];
|
||||
var ilen = sources.length;
|
||||
var merge, i, keys, klen, k;
|
||||
|
||||
if (!helpers.isObject(target)) {
|
||||
return target;
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
merge = options.merger || helpers._merger;
|
||||
|
||||
for (i = 0; i < ilen; ++i) {
|
||||
source = sources[i];
|
||||
if (!helpers.isObject(source)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
keys = Object.keys(source);
|
||||
for (k = 0, klen = keys.length; k < klen; ++k) {
|
||||
merge(keys[k], target, source, options);
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively deep copies `source` properties into `target` *only* if not defined in target.
|
||||
* IMPORTANT: `target` is not cloned and will be updated with `source` properties.
|
||||
* @param {Object} target - The target object in which all sources are merged into.
|
||||
* @param {Object|Array(Object)} source - Object(s) to merge into `target`.
|
||||
* @returns {Object} The `target` object.
|
||||
*/
|
||||
mergeIf: function(target, source) {
|
||||
return helpers.merge(target, source, {merger: helpers._mergerIf});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = helpers;
|
||||
|
||||
// DEPRECATIONS
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers.callback instead.
|
||||
* @function Chart.helpers.callCallback
|
||||
* @deprecated since version 2.6.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.callCallback = helpers.callback;
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Array.prototype.indexOf instead.
|
||||
* Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
|
||||
* @function Chart.helpers.indexOf
|
||||
* @deprecated since version 2.7.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.indexOf = function(array, item, fromIndex) {
|
||||
return Array.prototype.indexOf.call(array, item, fromIndex);
|
||||
};
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
|
||||
* @function Chart.helpers.getValueOrDefault
|
||||
* @deprecated since version 2.7.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.getValueOrDefault = helpers.valueOrDefault;
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
|
||||
* @function Chart.helpers.getValueAtIndexOrDefault
|
||||
* @deprecated since version 2.7.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
|
||||
250
src/helpers/helpers.easing.js
Normal file
250
src/helpers/helpers.easing.js
Normal file
@@ -0,0 +1,250 @@
|
||||
'use strict';
|
||||
|
||||
var helpers = require('./helpers.core');
|
||||
|
||||
/**
|
||||
* Easing functions adapted from Robert Penner's easing equations.
|
||||
* @namespace Chart.helpers.easingEffects
|
||||
* @see http://www.robertpenner.com/easing/
|
||||
*/
|
||||
var effects = {
|
||||
linear: function(t) {
|
||||
return t;
|
||||
},
|
||||
|
||||
easeInQuad: function(t) {
|
||||
return t * t;
|
||||
},
|
||||
|
||||
easeOutQuad: function(t) {
|
||||
return -t * (t - 2);
|
||||
},
|
||||
|
||||
easeInOutQuad: function(t) {
|
||||
if ((t /= 0.5) < 1) {
|
||||
return 0.5 * t * t;
|
||||
}
|
||||
return -0.5 * ((--t) * (t - 2) - 1);
|
||||
},
|
||||
|
||||
easeInCubic: function(t) {
|
||||
return t * t * t;
|
||||
},
|
||||
|
||||
easeOutCubic: function(t) {
|
||||
return (t = t - 1) * t * t + 1;
|
||||
},
|
||||
|
||||
easeInOutCubic: function(t) {
|
||||
if ((t /= 0.5) < 1) {
|
||||
return 0.5 * t * t * t;
|
||||
}
|
||||
return 0.5 * ((t -= 2) * t * t + 2);
|
||||
},
|
||||
|
||||
easeInQuart: function(t) {
|
||||
return t * t * t * t;
|
||||
},
|
||||
|
||||
easeOutQuart: function(t) {
|
||||
return -((t = t - 1) * t * t * t - 1);
|
||||
},
|
||||
|
||||
easeInOutQuart: function(t) {
|
||||
if ((t /= 0.5) < 1) {
|
||||
return 0.5 * t * t * t * t;
|
||||
}
|
||||
return -0.5 * ((t -= 2) * t * t * t - 2);
|
||||
},
|
||||
|
||||
easeInQuint: function(t) {
|
||||
return t * t * t * t * t;
|
||||
},
|
||||
|
||||
easeOutQuint: function(t) {
|
||||
return (t = t - 1) * t * t * t * t + 1;
|
||||
},
|
||||
|
||||
easeInOutQuint: function(t) {
|
||||
if ((t /= 0.5) < 1) {
|
||||
return 0.5 * t * t * t * t * t;
|
||||
}
|
||||
return 0.5 * ((t -= 2) * t * t * t * t + 2);
|
||||
},
|
||||
|
||||
easeInSine: function(t) {
|
||||
return -Math.cos(t * (Math.PI / 2)) + 1;
|
||||
},
|
||||
|
||||
easeOutSine: function(t) {
|
||||
return Math.sin(t * (Math.PI / 2));
|
||||
},
|
||||
|
||||
easeInOutSine: function(t) {
|
||||
return -0.5 * (Math.cos(Math.PI * t) - 1);
|
||||
},
|
||||
|
||||
easeInExpo: function(t) {
|
||||
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
|
||||
},
|
||||
|
||||
easeOutExpo: function(t) {
|
||||
return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
|
||||
},
|
||||
|
||||
easeInOutExpo: function(t) {
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
if ((t /= 0.5) < 1) {
|
||||
return 0.5 * Math.pow(2, 10 * (t - 1));
|
||||
}
|
||||
return 0.5 * (-Math.pow(2, -10 * --t) + 2);
|
||||
},
|
||||
|
||||
easeInCirc: function(t) {
|
||||
if (t >= 1) {
|
||||
return t;
|
||||
}
|
||||
return -(Math.sqrt(1 - t * t) - 1);
|
||||
},
|
||||
|
||||
easeOutCirc: function(t) {
|
||||
return Math.sqrt(1 - (t = t - 1) * t);
|
||||
},
|
||||
|
||||
easeInOutCirc: function(t) {
|
||||
if ((t /= 0.5) < 1) {
|
||||
return -0.5 * (Math.sqrt(1 - t * t) - 1);
|
||||
}
|
||||
return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
|
||||
},
|
||||
|
||||
easeInElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 0.3;
|
||||
}
|
||||
if (a < 1) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
|
||||
},
|
||||
|
||||
easeOutElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 0.3;
|
||||
}
|
||||
if (a < 1) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
|
||||
},
|
||||
|
||||
easeInOutElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 0.5) === 2) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 0.45;
|
||||
}
|
||||
if (a < 1) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
if (t < 1) {
|
||||
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
|
||||
}
|
||||
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
|
||||
},
|
||||
easeInBack: function(t) {
|
||||
var s = 1.70158;
|
||||
return t * t * ((s + 1) * t - s);
|
||||
},
|
||||
|
||||
easeOutBack: function(t) {
|
||||
var s = 1.70158;
|
||||
return (t = t - 1) * t * ((s + 1) * t + s) + 1;
|
||||
},
|
||||
|
||||
easeInOutBack: function(t) {
|
||||
var s = 1.70158;
|
||||
if ((t /= 0.5) < 1) {
|
||||
return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
|
||||
}
|
||||
return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
|
||||
},
|
||||
|
||||
easeInBounce: function(t) {
|
||||
return 1 - effects.easeOutBounce(1 - t);
|
||||
},
|
||||
|
||||
easeOutBounce: function(t) {
|
||||
if (t < (1 / 2.75)) {
|
||||
return 7.5625 * t * t;
|
||||
}
|
||||
if (t < (2 / 2.75)) {
|
||||
return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
|
||||
}
|
||||
if (t < (2.5 / 2.75)) {
|
||||
return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
|
||||
}
|
||||
return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
|
||||
},
|
||||
|
||||
easeInOutBounce: function(t) {
|
||||
if (t < 0.5) {
|
||||
return effects.easeInBounce(t * 2) * 0.5;
|
||||
}
|
||||
return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
effects: effects
|
||||
};
|
||||
|
||||
// DEPRECATIONS
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers.easing.effects instead.
|
||||
* @function Chart.helpers.easingEffects
|
||||
* @deprecated since version 2.7.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.easingEffects = effects;
|
||||
96
src/helpers/helpers.options.js
Normal file
96
src/helpers/helpers.options.js
Normal file
@@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
var helpers = require('./helpers.core');
|
||||
|
||||
/**
|
||||
* @alias Chart.helpers.options
|
||||
* @namespace
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* Converts the given line height `value` in pixels for a specific font `size`.
|
||||
* @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
|
||||
* @param {Number} size - The font size (in pixels) used to resolve relative `value`.
|
||||
* @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
|
||||
* @since 2.7.0
|
||||
*/
|
||||
toLineHeight: function(value, size) {
|
||||
var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
|
||||
if (!matches || matches[1] === 'normal') {
|
||||
return size * 1.2;
|
||||
}
|
||||
|
||||
value = +matches[2];
|
||||
|
||||
switch (matches[3]) {
|
||||
case 'px':
|
||||
return value;
|
||||
case '%':
|
||||
value /= 100;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return size * value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the given value into a padding object with pre-computed width/height.
|
||||
* @param {Number|Object} value - If a number, set the value to all TRBL component,
|
||||
* else, if and object, use defined properties and sets undefined ones to 0.
|
||||
* @returns {Object} The padding values (top, right, bottom, left, width, height)
|
||||
* @since 2.7.0
|
||||
*/
|
||||
toPadding: function(value) {
|
||||
var t, r, b, l;
|
||||
|
||||
if (helpers.isObject(value)) {
|
||||
t = +value.top || 0;
|
||||
r = +value.right || 0;
|
||||
b = +value.bottom || 0;
|
||||
l = +value.left || 0;
|
||||
} else {
|
||||
t = r = b = l = +value || 0;
|
||||
}
|
||||
|
||||
return {
|
||||
top: t,
|
||||
right: r,
|
||||
bottom: b,
|
||||
left: l,
|
||||
height: t + b,
|
||||
width: l + r
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Evaluates the given `inputs` sequentially and returns the first defined value.
|
||||
* @param {Array[]} inputs - An array of values, falling back to the last value.
|
||||
* @param {Object} [context] - If defined and the current value is a function, the value
|
||||
* is called with `context` as first argument and the result becomes the new input.
|
||||
* @param {Number} [index] - If defined and the current value is an array, the value
|
||||
* at `index` become the new input.
|
||||
* @since 2.7.0
|
||||
*/
|
||||
resolve: function(inputs, context, index) {
|
||||
var i, ilen, value;
|
||||
|
||||
for (i = 0, ilen = inputs.length; i < ilen; ++i) {
|
||||
value = inputs[i];
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (context !== undefined && typeof value === 'function') {
|
||||
value = value(context);
|
||||
}
|
||||
if (index !== undefined && helpers.isArray(value)) {
|
||||
value = value[index];
|
||||
}
|
||||
if (value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
6
src/helpers/index.js
Normal file
6
src/helpers/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./helpers.core');
|
||||
module.exports.easing = require('./helpers.easing');
|
||||
module.exports.canvas = require('./helpers.canvas');
|
||||
module.exports.options = require('./helpers.options');
|
||||
15
src/platforms/platform.basic.js
Normal file
15
src/platforms/platform.basic.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Platform fallback implementation (minimal).
|
||||
* @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
acquireContext: function(item) {
|
||||
if (item && item.canvas) {
|
||||
// Support for any object associated to a canvas (including a context2d)
|
||||
item = item.canvas;
|
||||
}
|
||||
|
||||
return item && item.getContext('2d') || null;
|
||||
}
|
||||
};
|
||||
@@ -1,283 +1,450 @@
|
||||
/**
|
||||
* Chart.Platform implementation for targeting a web browser
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Chart.Platform implementation for targeting a web browser
|
||||
module.exports = function(Chart) {
|
||||
var helpers = Chart.helpers;
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
// DOM event types -> Chart.js event types.
|
||||
// Note: only events with different types are mapped.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Events
|
||||
var eventTypeMap = {
|
||||
// Touch events
|
||||
touchstart: 'mousedown',
|
||||
touchmove: 'mousemove',
|
||||
touchend: 'mouseup',
|
||||
var EXPANDO_KEY = '$chartjs';
|
||||
var CSS_PREFIX = 'chartjs-';
|
||||
var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
|
||||
var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
|
||||
var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];
|
||||
|
||||
// Pointer events
|
||||
pointerenter: 'mouseenter',
|
||||
pointerdown: 'mousedown',
|
||||
pointermove: 'mousemove',
|
||||
pointerup: 'mouseup',
|
||||
pointerleave: 'mouseout',
|
||||
pointerout: 'mouseout'
|
||||
/**
|
||||
* DOM event types -> Chart.js event types.
|
||||
* Note: only events with different types are mapped.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/Events
|
||||
*/
|
||||
var EVENT_TYPES = {
|
||||
touchstart: 'mousedown',
|
||||
touchmove: 'mousemove',
|
||||
touchend: 'mouseup',
|
||||
pointerenter: 'mouseenter',
|
||||
pointerdown: 'mousedown',
|
||||
pointermove: 'mousemove',
|
||||
pointerup: 'mouseup',
|
||||
pointerleave: 'mouseout',
|
||||
pointerout: 'mouseout'
|
||||
};
|
||||
|
||||
/**
|
||||
* The "used" size is the final value of a dimension property after all calculations have
|
||||
* been performed. This method uses the computed style of `element` but returns undefined
|
||||
* if the computed style is not expressed in pixels. That can happen in some cases where
|
||||
* `element` has a size relative to its parent and this last one is not yet displayed,
|
||||
* for example because of `display: none` on a parent node.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||||
* @returns {Number} Size in pixels or undefined if unknown.
|
||||
*/
|
||||
function readUsedSize(element, property) {
|
||||
var value = helpers.getStyle(element, property);
|
||||
var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
|
||||
return matches ? Number(matches[1]) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the canvas style and render size without modifying the canvas display size,
|
||||
* since responsiveness is handled by the controller.resize() method. The config is used
|
||||
* to determine the aspect ratio to apply in case no explicit height has been specified.
|
||||
*/
|
||||
function initCanvas(canvas, config) {
|
||||
var style = canvas.style;
|
||||
|
||||
// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
|
||||
// returns null or '' if no explicit value has been set to the canvas attribute.
|
||||
var renderHeight = canvas.getAttribute('height');
|
||||
var renderWidth = canvas.getAttribute('width');
|
||||
|
||||
// Chart.js modifies some canvas values that we want to restore on destroy
|
||||
canvas[EXPANDO_KEY] = {
|
||||
initial: {
|
||||
height: renderHeight,
|
||||
width: renderWidth,
|
||||
style: {
|
||||
display: style.display,
|
||||
height: style.height,
|
||||
width: style.width
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The "used" size is the final value of a dimension property after all calculations have
|
||||
* been performed. This method uses the computed style of `element` but returns undefined
|
||||
* if the computed style is not expressed in pixels. That can happen in some cases where
|
||||
* `element` has a size relative to its parent and this last one is not yet displayed,
|
||||
* for example because of `display: none` on a parent node.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||||
* @returns {Number} Size in pixels or undefined if unknown.
|
||||
*/
|
||||
function readUsedSize(element, property) {
|
||||
var value = helpers.getStyle(element, property);
|
||||
var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
|
||||
return matches? Number(matches[1]) : undefined;
|
||||
// Force canvas to display as block to avoid extra space caused by inline
|
||||
// elements, which would interfere with the responsive resize process.
|
||||
// https://github.com/chartjs/Chart.js/issues/2538
|
||||
style.display = style.display || 'block';
|
||||
|
||||
if (renderWidth === null || renderWidth === '') {
|
||||
var displayWidth = readUsedSize(canvas, 'width');
|
||||
if (displayWidth !== undefined) {
|
||||
canvas.width = displayWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the canvas style and render size without modifying the canvas display size,
|
||||
* since responsiveness is handled by the controller.resize() method. The config is used
|
||||
* to determine the aspect ratio to apply in case no explicit height has been specified.
|
||||
*/
|
||||
function initCanvas(canvas, config) {
|
||||
var style = canvas.style;
|
||||
|
||||
// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
|
||||
// returns null or '' if no explicit value has been set to the canvas attribute.
|
||||
var renderHeight = canvas.getAttribute('height');
|
||||
var renderWidth = canvas.getAttribute('width');
|
||||
|
||||
// Chart.js modifies some canvas values that we want to restore on destroy
|
||||
canvas._chartjs = {
|
||||
initial: {
|
||||
height: renderHeight,
|
||||
width: renderWidth,
|
||||
style: {
|
||||
display: style.display,
|
||||
height: style.height,
|
||||
width: style.width
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Force canvas to display as block to avoid extra space caused by inline
|
||||
// elements, which would interfere with the responsive resize process.
|
||||
// https://github.com/chartjs/Chart.js/issues/2538
|
||||
style.display = style.display || 'block';
|
||||
|
||||
if (renderWidth === null || renderWidth === '') {
|
||||
var displayWidth = readUsedSize(canvas, 'width');
|
||||
if (renderHeight === null || renderHeight === '') {
|
||||
if (canvas.style.height === '') {
|
||||
// If no explicit render height and style height, let's apply the aspect ratio,
|
||||
// which one can be specified by the user but also by charts as default option
|
||||
// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
|
||||
canvas.height = canvas.width / (config.options.aspectRatio || 2);
|
||||
} else {
|
||||
var displayHeight = readUsedSize(canvas, 'height');
|
||||
if (displayWidth !== undefined) {
|
||||
canvas.width = displayWidth;
|
||||
canvas.height = displayHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (renderHeight === null || renderHeight === '') {
|
||||
if (canvas.style.height === '') {
|
||||
// If no explicit render height and style height, let's apply the aspect ratio,
|
||||
// which one can be specified by the user but also by charts as default option
|
||||
// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
|
||||
canvas.height = canvas.width / (config.options.aspectRatio || 2);
|
||||
} else {
|
||||
var displayHeight = readUsedSize(canvas, 'height');
|
||||
if (displayWidth !== undefined) {
|
||||
canvas.height = displayHeight;
|
||||
}
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects support for options object argument in addEventListener.
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
|
||||
* @private
|
||||
*/
|
||||
var supportsEventListenerOptions = (function() {
|
||||
var supports = false;
|
||||
try {
|
||||
var options = Object.defineProperty({}, 'passive', {
|
||||
get: function() {
|
||||
supports = true;
|
||||
}
|
||||
});
|
||||
window.addEventListener('e', null, options);
|
||||
} catch (e) {
|
||||
// continue regardless of error
|
||||
}
|
||||
return supports;
|
||||
}());
|
||||
|
||||
// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
|
||||
// https://github.com/chartjs/Chart.js/issues/4287
|
||||
var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
|
||||
|
||||
function addEventListener(node, type, listener) {
|
||||
node.addEventListener(type, listener, eventListenerOptions);
|
||||
}
|
||||
|
||||
function removeEventListener(node, type, listener) {
|
||||
node.removeEventListener(type, listener, eventListenerOptions);
|
||||
}
|
||||
|
||||
function createEvent(type, chart, x, y, nativeEvent) {
|
||||
return {
|
||||
type: type,
|
||||
chart: chart,
|
||||
native: nativeEvent || null,
|
||||
x: x !== undefined ? x : null,
|
||||
y: y !== undefined ? y : null,
|
||||
};
|
||||
}
|
||||
|
||||
function fromNativeEvent(event, chart) {
|
||||
var type = EVENT_TYPES[event.type] || event.type;
|
||||
var pos = helpers.getRelativePosition(event, chart);
|
||||
return createEvent(type, chart, pos.x, pos.y, event);
|
||||
}
|
||||
|
||||
function throttled(fn, thisArg) {
|
||||
var ticking = false;
|
||||
var args = [];
|
||||
|
||||
return function() {
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
thisArg = thisArg || this;
|
||||
|
||||
if (!ticking) {
|
||||
ticking = true;
|
||||
helpers.requestAnimFrame.call(window, function() {
|
||||
ticking = false;
|
||||
fn.apply(thisArg, args);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
// Implementation based on https://github.com/marcj/css-element-queries
|
||||
function createResizer(handler) {
|
||||
var resizer = document.createElement('div');
|
||||
var cls = CSS_PREFIX + 'size-monitor';
|
||||
var maxSize = 1000000;
|
||||
var style =
|
||||
'position:absolute;' +
|
||||
'left:0;' +
|
||||
'top:0;' +
|
||||
'right:0;' +
|
||||
'bottom:0;' +
|
||||
'overflow:hidden;' +
|
||||
'pointer-events:none;' +
|
||||
'visibility:hidden;' +
|
||||
'z-index:-1;';
|
||||
|
||||
function createEvent(type, chart, x, y, nativeEvent) {
|
||||
return {
|
||||
type: type,
|
||||
chart: chart,
|
||||
native: nativeEvent || null,
|
||||
x: x !== undefined? x : null,
|
||||
y: y !== undefined? y : null,
|
||||
};
|
||||
}
|
||||
resizer.style.cssText = style;
|
||||
resizer.className = cls;
|
||||
resizer.innerHTML =
|
||||
'<div class="' + cls + '-expand" style="' + style + '">' +
|
||||
'<div style="' +
|
||||
'position:absolute;' +
|
||||
'width:' + maxSize + 'px;' +
|
||||
'height:' + maxSize + 'px;' +
|
||||
'left:0;' +
|
||||
'top:0">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="' + cls + '-shrink" style="' + style + '">' +
|
||||
'<div style="' +
|
||||
'position:absolute;' +
|
||||
'width:200%;' +
|
||||
'height:200%;' +
|
||||
'left:0; ' +
|
||||
'top:0">' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
function fromNativeEvent(event, chart) {
|
||||
var type = eventTypeMap[event.type] || event.type;
|
||||
var pos = helpers.getRelativePosition(event, chart);
|
||||
return createEvent(type, chart, pos.x, pos.y, event);
|
||||
}
|
||||
var expand = resizer.childNodes[0];
|
||||
var shrink = resizer.childNodes[1];
|
||||
|
||||
function createResizer(handler) {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.className = 'chartjs-hidden-iframe';
|
||||
iframe.style.cssText =
|
||||
'display:block;'+
|
||||
'overflow:hidden;'+
|
||||
'border:0;'+
|
||||
'margin:0;'+
|
||||
'top:0;'+
|
||||
'left:0;'+
|
||||
'bottom:0;'+
|
||||
'right:0;'+
|
||||
'height:100%;'+
|
||||
'width:100%;'+
|
||||
'position:absolute;'+
|
||||
'pointer-events:none;'+
|
||||
'z-index:-1;';
|
||||
resizer._reset = function() {
|
||||
expand.scrollLeft = maxSize;
|
||||
expand.scrollTop = maxSize;
|
||||
shrink.scrollLeft = maxSize;
|
||||
shrink.scrollTop = maxSize;
|
||||
};
|
||||
var onScroll = function() {
|
||||
resizer._reset();
|
||||
handler();
|
||||
};
|
||||
|
||||
// Prevent the iframe to gain focus on tab.
|
||||
// https://github.com/chartjs/Chart.js/issues/3090
|
||||
iframe.tabIndex = -1;
|
||||
addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
|
||||
addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));
|
||||
|
||||
// If the iframe is re-attached to the DOM, the resize listener is removed because the
|
||||
// content is reloaded, so make sure to install the handler after the iframe is loaded.
|
||||
// https://github.com/chartjs/Chart.js/issues/3521
|
||||
helpers.addEvent(iframe, 'load', function() {
|
||||
helpers.addEvent(iframe.contentWindow || iframe, 'resize', handler);
|
||||
return resizer;
|
||||
}
|
||||
|
||||
// The iframe size might have changed while loading, which can also
|
||||
// happen if the size has been changed while detached from the DOM.
|
||||
// https://davidwalsh.name/detect-node-insertion
|
||||
function watchForRender(node, handler) {
|
||||
var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
|
||||
var proxy = expando.renderProxy = function(e) {
|
||||
if (e.animationName === CSS_RENDER_ANIMATION) {
|
||||
handler();
|
||||
}
|
||||
};
|
||||
|
||||
helpers.each(ANIMATION_START_EVENTS, function(type) {
|
||||
addEventListener(node, type, proxy);
|
||||
});
|
||||
|
||||
node.classList.add(CSS_RENDER_MONITOR);
|
||||
}
|
||||
|
||||
function unwatchForRender(node) {
|
||||
var expando = node[EXPANDO_KEY] || {};
|
||||
var proxy = expando.renderProxy;
|
||||
|
||||
if (proxy) {
|
||||
helpers.each(ANIMATION_START_EVENTS, function(type) {
|
||||
removeEventListener(node, type, proxy);
|
||||
});
|
||||
|
||||
return iframe;
|
||||
delete expando.renderProxy;
|
||||
}
|
||||
|
||||
function addResizeListener(node, listener, chart) {
|
||||
var stub = node._chartjs = {
|
||||
ticking: false
|
||||
};
|
||||
node.classList.remove(CSS_RENDER_MONITOR);
|
||||
}
|
||||
|
||||
// Throttle the callback notification until the next animation frame.
|
||||
var notify = function() {
|
||||
if (!stub.ticking) {
|
||||
stub.ticking = true;
|
||||
helpers.requestAnimFrame.call(window, function() {
|
||||
if (stub.resizer) {
|
||||
stub.ticking = false;
|
||||
return listener(createEvent('resize', chart));
|
||||
}
|
||||
});
|
||||
function addResizeListener(node, listener, chart) {
|
||||
var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
|
||||
|
||||
// Let's keep track of this added resizer and thus avoid DOM query when removing it.
|
||||
var resizer = expando.resizer = createResizer(throttled(function() {
|
||||
if (expando.resizer) {
|
||||
return listener(createEvent('resize', chart));
|
||||
}
|
||||
}));
|
||||
|
||||
// The resizer needs to be attached to the node parent, so we first need to be
|
||||
// sure that `node` is attached to the DOM before injecting the resizer element.
|
||||
watchForRender(node, function() {
|
||||
if (expando.resizer) {
|
||||
var container = node.parentNode;
|
||||
if (container && container !== resizer.parentNode) {
|
||||
container.insertBefore(resizer, container.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
// Let's keep track of this added iframe and thus avoid DOM query when removing it.
|
||||
stub.resizer = createResizer(notify);
|
||||
// The container size might have changed, let's reset the resizer state.
|
||||
resizer._reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
node.insertBefore(stub.resizer, node.firstChild);
|
||||
function removeResizeListener(node) {
|
||||
var expando = node[EXPANDO_KEY] || {};
|
||||
var resizer = expando.resizer;
|
||||
|
||||
delete expando.resizer;
|
||||
unwatchForRender(node);
|
||||
|
||||
if (resizer && resizer.parentNode) {
|
||||
resizer.parentNode.removeChild(resizer);
|
||||
}
|
||||
}
|
||||
|
||||
function injectCSS(platform, css) {
|
||||
// http://stackoverflow.com/q/3922139
|
||||
var style = platform._style || document.createElement('style');
|
||||
if (!platform._style) {
|
||||
platform._style = style;
|
||||
css = '/* Chart.js */\n' + css;
|
||||
style.setAttribute('type', 'text/css');
|
||||
document.getElementsByTagName('head')[0].appendChild(style);
|
||||
}
|
||||
|
||||
function removeResizeListener(node) {
|
||||
if (!node || !node._chartjs) {
|
||||
style.appendChild(document.createTextNode(css));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* This property holds whether this platform is enabled for the current environment.
|
||||
* Currently used by platform.js to select the proper implementation.
|
||||
* @private
|
||||
*/
|
||||
_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
|
||||
|
||||
initialize: function() {
|
||||
var keyframes = 'from{opacity:0.99}to{opacity:1}';
|
||||
|
||||
injectCSS(this,
|
||||
// DOM rendering detection
|
||||
// https://davidwalsh.name/detect-node-insertion
|
||||
'@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
|
||||
'@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
|
||||
'.' + CSS_RENDER_MONITOR + '{' +
|
||||
'-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
|
||||
'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
|
||||
'}'
|
||||
);
|
||||
},
|
||||
|
||||
acquireContext: function(item, config) {
|
||||
if (typeof item === 'string') {
|
||||
item = document.getElementById(item);
|
||||
} else if (item.length) {
|
||||
// Support for array based queries (such as jQuery)
|
||||
item = item[0];
|
||||
}
|
||||
|
||||
if (item && item.canvas) {
|
||||
// Support for any object associated to a canvas (including a context2d)
|
||||
item = item.canvas;
|
||||
}
|
||||
|
||||
// To prevent canvas fingerprinting, some add-ons undefine the getContext
|
||||
// method, for example: https://github.com/kkapsner/CanvasBlocker
|
||||
// https://github.com/chartjs/Chart.js/issues/2807
|
||||
var context = item && item.getContext && item.getContext('2d');
|
||||
|
||||
// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
|
||||
// inside an iframe or when running in a protected environment. We could guess the
|
||||
// types from their toString() value but let's keep things flexible and assume it's
|
||||
// a sufficient condition if the item has a context2D which has item as `canvas`.
|
||||
// https://github.com/chartjs/Chart.js/issues/3887
|
||||
// https://github.com/chartjs/Chart.js/issues/4102
|
||||
// https://github.com/chartjs/Chart.js/issues/4152
|
||||
if (context && context.canvas === item) {
|
||||
initCanvas(item, config);
|
||||
return context;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
releaseContext: function(context) {
|
||||
var canvas = context.canvas;
|
||||
if (!canvas[EXPANDO_KEY]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var resizer = node._chartjs.resizer;
|
||||
if (resizer) {
|
||||
resizer.parentNode.removeChild(resizer);
|
||||
node._chartjs.resizer = null;
|
||||
var initial = canvas[EXPANDO_KEY].initial;
|
||||
['height', 'width'].forEach(function(prop) {
|
||||
var value = initial[prop];
|
||||
if (helpers.isNullOrUndef(value)) {
|
||||
canvas.removeAttribute(prop);
|
||||
} else {
|
||||
canvas.setAttribute(prop, value);
|
||||
}
|
||||
});
|
||||
|
||||
helpers.each(initial.style || {}, function(value, key) {
|
||||
canvas.style[key] = value;
|
||||
});
|
||||
|
||||
// The canvas render size might have been changed (and thus the state stack discarded),
|
||||
// we can't use save() and restore() to restore the initial state. So make sure that at
|
||||
// least the canvas context is reset to the default state by setting the canvas width.
|
||||
// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
|
||||
canvas.width = canvas.width;
|
||||
|
||||
delete canvas[EXPANDO_KEY];
|
||||
},
|
||||
|
||||
addEventListener: function(chart, type, listener) {
|
||||
var canvas = chart.canvas;
|
||||
if (type === 'resize') {
|
||||
// Note: the resize event is not supported on all browsers.
|
||||
addResizeListener(canvas, listener, chart);
|
||||
return;
|
||||
}
|
||||
|
||||
delete node._chartjs;
|
||||
var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
|
||||
var proxies = expando.proxies || (expando.proxies = {});
|
||||
var proxy = proxies[chart.id + '_' + type] = function(event) {
|
||||
listener(fromNativeEvent(event, chart));
|
||||
};
|
||||
|
||||
addEventListener(canvas, type, proxy);
|
||||
},
|
||||
|
||||
removeEventListener: function(chart, type, listener) {
|
||||
var canvas = chart.canvas;
|
||||
if (type === 'resize') {
|
||||
// Note: the resize event is not supported on all browsers.
|
||||
removeResizeListener(canvas, listener);
|
||||
return;
|
||||
}
|
||||
|
||||
var expando = listener[EXPANDO_KEY] || {};
|
||||
var proxies = expando.proxies || {};
|
||||
var proxy = proxies[chart.id + '_' + type];
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeEventListener(canvas, type, proxy);
|
||||
}
|
||||
|
||||
return {
|
||||
acquireContext: function(item, config) {
|
||||
if (typeof item === 'string') {
|
||||
item = document.getElementById(item);
|
||||
} else if (item.length) {
|
||||
// Support for array based queries (such as jQuery)
|
||||
item = item[0];
|
||||
}
|
||||
|
||||
if (item && item.canvas) {
|
||||
// Support for any object associated to a canvas (including a context2d)
|
||||
item = item.canvas;
|
||||
}
|
||||
|
||||
// To prevent canvas fingerprinting, some add-ons undefine the getContext
|
||||
// method, for example: https://github.com/kkapsner/CanvasBlocker
|
||||
// https://github.com/chartjs/Chart.js/issues/2807
|
||||
var context = item && item.getContext && item.getContext('2d');
|
||||
|
||||
// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
|
||||
// inside an iframe or when running in a protected environment. We could guess the
|
||||
// types from their toString() value but let's keep things flexible and assume it's
|
||||
// a sufficient condition if the item has a context2D which has item as `canvas`.
|
||||
// https://github.com/chartjs/Chart.js/issues/3887
|
||||
// https://github.com/chartjs/Chart.js/issues/4102
|
||||
// https://github.com/chartjs/Chart.js/issues/4152
|
||||
if (context && context.canvas === item) {
|
||||
initCanvas(item, config);
|
||||
return context;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
releaseContext: function(context) {
|
||||
var canvas = context.canvas;
|
||||
if (!canvas._chartjs) {
|
||||
return;
|
||||
}
|
||||
|
||||
var initial = canvas._chartjs.initial;
|
||||
['height', 'width'].forEach(function(prop) {
|
||||
var value = initial[prop];
|
||||
if (value === undefined || value === null) {
|
||||
canvas.removeAttribute(prop);
|
||||
} else {
|
||||
canvas.setAttribute(prop, value);
|
||||
}
|
||||
});
|
||||
|
||||
helpers.each(initial.style || {}, function(value, key) {
|
||||
canvas.style[key] = value;
|
||||
});
|
||||
|
||||
// The canvas render size might have been changed (and thus the state stack discarded),
|
||||
// we can't use save() and restore() to restore the initial state. So make sure that at
|
||||
// least the canvas context is reset to the default state by setting the canvas width.
|
||||
// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
|
||||
canvas.width = canvas.width;
|
||||
|
||||
delete canvas._chartjs;
|
||||
},
|
||||
|
||||
addEventListener: function(chart, type, listener) {
|
||||
var canvas = chart.canvas;
|
||||
if (type === 'resize') {
|
||||
// Note: the resize event is not supported on all browsers.
|
||||
addResizeListener(canvas.parentNode, listener, chart);
|
||||
return;
|
||||
}
|
||||
|
||||
var stub = listener._chartjs || (listener._chartjs = {});
|
||||
var proxies = stub.proxies || (stub.proxies = {});
|
||||
var proxy = proxies[chart.id + '_' + type] = function(event) {
|
||||
listener(fromNativeEvent(event, chart));
|
||||
};
|
||||
|
||||
helpers.addEvent(canvas, type, proxy);
|
||||
},
|
||||
|
||||
removeEventListener: function(chart, type, listener) {
|
||||
var canvas = chart.canvas;
|
||||
if (type === 'resize') {
|
||||
// Note: the resize event is not supported on all browsers.
|
||||
removeResizeListener(canvas.parentNode, listener);
|
||||
return;
|
||||
}
|
||||
|
||||
var stub = listener._chartjs || {};
|
||||
var proxies = stub.proxies || {};
|
||||
var proxy = proxies[chart.id + '_' + type];
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
helpers.removeEvent(canvas, type, proxy);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// DEPRECATIONS
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use EventTarget.addEventListener instead.
|
||||
* EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
||||
* @function Chart.helpers.addEvent
|
||||
* @deprecated since version 2.7.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.addEvent = addEventListener;
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use EventTarget.removeEventListener instead.
|
||||
* EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
|
||||
* @function Chart.helpers.removeEvent
|
||||
* @deprecated since version 2.7.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.removeEvent = removeEventListener;
|
||||
|
||||
@@ -1,69 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
// By default, select the browser (DOM) platform.
|
||||
var helpers = require('../helpers/index');
|
||||
var basic = require('./platform.basic');
|
||||
var dom = require('./platform.dom');
|
||||
|
||||
// @TODO Make possible to select another platform at build time.
|
||||
var implementation = require('./platform.dom.js');
|
||||
var implementation = dom._enabled ? dom : basic;
|
||||
|
||||
module.exports = function(Chart) {
|
||||
/**
|
||||
* @namespace Chart.platform
|
||||
* @see https://chartjs.gitbooks.io/proposals/content/Platform.html
|
||||
* @since 2.4.0
|
||||
*/
|
||||
module.exports = helpers.extend({
|
||||
/**
|
||||
* @namespace Chart.platform
|
||||
* @see https://chartjs.gitbooks.io/proposals/content/Platform.html
|
||||
* @since 2.4.0
|
||||
* @since 2.7.0
|
||||
*/
|
||||
Chart.platform = {
|
||||
/**
|
||||
* Called at chart construction time, returns a context2d instance implementing
|
||||
* the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
|
||||
* @param {*} item - The native item from which to acquire context (platform specific)
|
||||
* @param {Object} options - The chart options
|
||||
* @returns {CanvasRenderingContext2D} context2d instance
|
||||
*/
|
||||
acquireContext: function() {},
|
||||
|
||||
/**
|
||||
* Called at chart destruction time, releases any resources associated to the context
|
||||
* previously returned by the acquireContext() method.
|
||||
* @param {CanvasRenderingContext2D} context - The context2d instance
|
||||
* @returns {Boolean} true if the method succeeded, else false
|
||||
*/
|
||||
releaseContext: function() {},
|
||||
|
||||
/**
|
||||
* Registers the specified listener on the given chart.
|
||||
* @param {Chart} chart - Chart from which to listen for event
|
||||
* @param {String} type - The ({@link IEvent}) type to listen for
|
||||
* @param {Function} listener - Receives a notification (an object that implements
|
||||
* the {@link IEvent} interface) when an event of the specified type occurs.
|
||||
*/
|
||||
addEventListener: function() {},
|
||||
|
||||
/**
|
||||
* Removes the specified listener previously registered with addEventListener.
|
||||
* @param {Chart} chart -Chart from which to remove the listener
|
||||
* @param {String} type - The ({@link IEvent}) type to remove
|
||||
* @param {Function} listener - The listener function to remove from the event target.
|
||||
*/
|
||||
removeEventListener: function() {}
|
||||
};
|
||||
initialize: function() {},
|
||||
|
||||
/**
|
||||
* @interface IPlatform
|
||||
* Allows abstracting platform dependencies away from the chart
|
||||
* @borrows Chart.platform.acquireContext as acquireContext
|
||||
* @borrows Chart.platform.releaseContext as releaseContext
|
||||
* @borrows Chart.platform.addEventListener as addEventListener
|
||||
* @borrows Chart.platform.removeEventListener as removeEventListener
|
||||
* Called at chart construction time, returns a context2d instance implementing
|
||||
* the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
|
||||
* @param {*} item - The native item from which to acquire context (platform specific)
|
||||
* @param {Object} options - The chart options
|
||||
* @returns {CanvasRenderingContext2D} context2d instance
|
||||
*/
|
||||
acquireContext: function() {},
|
||||
|
||||
/**
|
||||
* @interface IEvent
|
||||
* @prop {String} type - The event type name, possible values are:
|
||||
* 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
|
||||
* 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
|
||||
* @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
|
||||
* @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
|
||||
* @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
|
||||
* Called at chart destruction time, releases any resources associated to the context
|
||||
* previously returned by the acquireContext() method.
|
||||
* @param {CanvasRenderingContext2D} context - The context2d instance
|
||||
* @returns {Boolean} true if the method succeeded, else false
|
||||
*/
|
||||
releaseContext: function() {},
|
||||
|
||||
Chart.helpers.extend(Chart.platform, implementation(Chart));
|
||||
};
|
||||
/**
|
||||
* Registers the specified listener on the given chart.
|
||||
* @param {Chart} chart - Chart from which to listen for event
|
||||
* @param {String} type - The ({@link IEvent}) type to listen for
|
||||
* @param {Function} listener - Receives a notification (an object that implements
|
||||
* the {@link IEvent} interface) when an event of the specified type occurs.
|
||||
*/
|
||||
addEventListener: function() {},
|
||||
|
||||
/**
|
||||
* Removes the specified listener previously registered with addEventListener.
|
||||
* @param {Chart} chart -Chart from which to remove the listener
|
||||
* @param {String} type - The ({@link IEvent}) type to remove
|
||||
* @param {Function} listener - The listener function to remove from the event target.
|
||||
*/
|
||||
removeEventListener: function() {}
|
||||
|
||||
}, implementation);
|
||||
|
||||
/**
|
||||
* @interface IPlatform
|
||||
* Allows abstracting platform dependencies away from the chart
|
||||
* @borrows Chart.platform.acquireContext as acquireContext
|
||||
* @borrows Chart.platform.releaseContext as releaseContext
|
||||
* @borrows Chart.platform.addEventListener as addEventListener
|
||||
* @borrows Chart.platform.removeEventListener as removeEventListener
|
||||
*/
|
||||
|
||||
/**
|
||||
* @interface IEvent
|
||||
* @prop {String} type - The event type name, possible values are:
|
||||
* 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
|
||||
* 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
|
||||
* @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
|
||||
* @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
|
||||
* @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
|
||||
*/
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
/**
|
||||
* Plugin based on discussion from the following Chart.js issues:
|
||||
* @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
|
||||
* @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
/**
|
||||
* Plugin based on discussion from the following Chart.js issues:
|
||||
* @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
|
||||
* @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
|
||||
*/
|
||||
Chart.defaults.global.plugins.filler = {
|
||||
propagate: true
|
||||
};
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
defaults._set('global', {
|
||||
plugins: {
|
||||
filler: {
|
||||
propagate: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function() {
|
||||
|
||||
var defaults = Chart.defaults;
|
||||
var helpers = Chart.helpers;
|
||||
var mappers = {
|
||||
dataset: function(source) {
|
||||
var index = source.fill;
|
||||
@@ -19,21 +27,22 @@ module.exports = function(Chart) {
|
||||
var meta = chart.getDatasetMeta(index);
|
||||
var visible = meta && chart.isDatasetVisible(index);
|
||||
var points = (visible && meta.dataset._children) || [];
|
||||
var length = points.length || 0;
|
||||
|
||||
return !points.length? null : function(point, i) {
|
||||
return points[i]._view || null;
|
||||
return !length ? null : function(point, i) {
|
||||
return (i < length && points[i]._view) || null;
|
||||
};
|
||||
},
|
||||
|
||||
boundary: function(source) {
|
||||
var boundary = source.boundary;
|
||||
var x = boundary? boundary.x : null;
|
||||
var y = boundary? boundary.y : null;
|
||||
var x = boundary ? boundary.x : null;
|
||||
var y = boundary ? boundary.y : null;
|
||||
|
||||
return function(point) {
|
||||
return {
|
||||
x: x === null? point.x : x,
|
||||
y: y === null? point.y : y,
|
||||
x: x === null ? point.x : x,
|
||||
y: y === null ? point.y : y,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -105,9 +114,9 @@ module.exports = function(Chart) {
|
||||
// controllers might still use it (e.g. the Smith chart).
|
||||
|
||||
if (fill === 'start') {
|
||||
target = model.scaleBottom === undefined? scale.bottom : model.scaleBottom;
|
||||
target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
|
||||
} else if (fill === 'end') {
|
||||
target = model.scaleTop === undefined? scale.top : model.scaleTop;
|
||||
target = model.scaleTop === undefined ? scale.top : model.scaleTop;
|
||||
} else if (model.scaleZero !== undefined) {
|
||||
target = model.scaleZero;
|
||||
} else if (scale.getBasePosition) {
|
||||
@@ -124,8 +133,8 @@ module.exports = function(Chart) {
|
||||
if (typeof target === 'number' && isFinite(target)) {
|
||||
horizontal = scale.isHorizontal();
|
||||
return {
|
||||
x: horizontal? target : null,
|
||||
y: horizontal? null : target
|
||||
x: horizontal ? target : null,
|
||||
y: horizontal ? null : target
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -192,16 +201,16 @@ module.exports = function(Chart) {
|
||||
|
||||
// building first area curve (normal)
|
||||
ctx.moveTo(curve0[0].x, curve0[0].y);
|
||||
for (i=1; i<len0; ++i) {
|
||||
helpers.canvas.lineTo(ctx, curve0[i-1], curve0[i]);
|
||||
for (i = 1; i < len0; ++i) {
|
||||
helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
|
||||
}
|
||||
|
||||
// joining the two area curves
|
||||
ctx.lineTo(curve1[len1-1].x, curve1[len1-1].y);
|
||||
ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
|
||||
|
||||
// building opposite area curve (reverse)
|
||||
for (i=len1-1; i>0; --i) {
|
||||
helpers.canvas.lineTo(ctx, curve1[i], curve1[i-1], true);
|
||||
for (i = len1 - 1; i > 0; --i) {
|
||||
helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +226,7 @@ module.exports = function(Chart) {
|
||||
ctx.beginPath();
|
||||
|
||||
for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
|
||||
index = i%count;
|
||||
index = i % count;
|
||||
p0 = points[index]._view;
|
||||
p1 = mapper(p0, index, view);
|
||||
d0 = isDrawable(p0);
|
||||
@@ -264,7 +273,7 @@ module.exports = function(Chart) {
|
||||
el = meta.dataset;
|
||||
source = null;
|
||||
|
||||
if (el && el._model && el instanceof Chart.elements.Line) {
|
||||
if (el && el._model && el instanceof elements.Line) {
|
||||
source = {
|
||||
visible: chart.isDatasetVisible(i),
|
||||
fill: decodeFill(el, i, count),
|
||||
@@ -277,7 +286,7 @@ module.exports = function(Chart) {
|
||||
sources.push(source);
|
||||
}
|
||||
|
||||
for (i=0; i<count; ++i) {
|
||||
for (i = 0; i < count; ++i) {
|
||||
source = sources[i];
|
||||
if (!source) {
|
||||
continue;
|
||||
@@ -295,6 +304,7 @@ module.exports = function(Chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ctx = chart.ctx;
|
||||
var el = meta.el;
|
||||
var view = el._view;
|
||||
var points = el._children || [];
|
||||
@@ -302,7 +312,9 @@ module.exports = function(Chart) {
|
||||
var color = view.backgroundColor || defaults.global.defaultColor;
|
||||
|
||||
if (mapper && color && points.length) {
|
||||
doFill(chart.ctx, points, mapper, view, color, el._loop);
|
||||
helpers.canvas.clipArea(ctx, chart.chartArea);
|
||||
doFill(ctx, points, mapper, view, color, el._loop);
|
||||
helpers.canvas.unclipArea(ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var defaults = require('../core/core.defaults');
|
||||
var Element = require('../core/core.element');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
var layout = Chart.layoutService;
|
||||
var noop = helpers.noop;
|
||||
|
||||
Chart.defaults.global.legend = {
|
||||
defaults._set('global', {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top',
|
||||
fullWidth: true,
|
||||
@@ -20,7 +19,7 @@ module.exports = function(Chart) {
|
||||
var meta = ci.getDatasetMeta(index);
|
||||
|
||||
// See controller.isDatasetVisible comment
|
||||
meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
|
||||
meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
|
||||
|
||||
// We hid a dataset ... rerender the chart
|
||||
ci.update();
|
||||
@@ -63,7 +62,27 @@ module.exports = function(Chart) {
|
||||
}, this) : [];
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
legendCallback: function(chart) {
|
||||
var text = [];
|
||||
text.push('<ul class="' + chart.id + '-legend">');
|
||||
for (var i = 0; i < chart.data.datasets.length; i++) {
|
||||
text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
|
||||
if (chart.data.datasets[i].label) {
|
||||
text.push(chart.data.datasets[i].label);
|
||||
}
|
||||
text.push('</li>');
|
||||
}
|
||||
text.push('</ul>');
|
||||
return text.join('');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var layout = Chart.layoutService;
|
||||
var noop = helpers.noop;
|
||||
|
||||
/**
|
||||
* Helper function to get the box width based on the usePointStyle option
|
||||
@@ -77,7 +96,7 @@ module.exports = function(Chart) {
|
||||
labelOpts.boxWidth;
|
||||
}
|
||||
|
||||
Chart.Legend = Chart.Element.extend({
|
||||
Chart.Legend = Element.extend({
|
||||
|
||||
initialize: function(config) {
|
||||
helpers.extend(this, config);
|
||||
@@ -163,8 +182,8 @@ module.exports = function(Chart) {
|
||||
beforeBuildLabels: noop,
|
||||
buildLabels: function() {
|
||||
var me = this;
|
||||
var labelOpts = me.options.labels;
|
||||
var legendItems = labelOpts.generateLabels.call(me, me.chart);
|
||||
var labelOpts = me.options.labels || {};
|
||||
var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];
|
||||
|
||||
if (labelOpts.filter) {
|
||||
legendItems = legendItems.filter(function(item) {
|
||||
@@ -191,12 +210,12 @@ module.exports = function(Chart) {
|
||||
|
||||
var ctx = me.ctx;
|
||||
|
||||
var globalDefault = Chart.defaults.global,
|
||||
itemOrDefault = helpers.getValueOrDefault,
|
||||
fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
|
||||
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
|
||||
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
|
||||
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
var globalDefault = defaults.global;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
|
||||
var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
|
||||
var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
|
||||
var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
|
||||
// Reset hit boxes
|
||||
var hitboxes = me.legendHitBoxes = [];
|
||||
@@ -303,31 +322,31 @@ module.exports = function(Chart) {
|
||||
var me = this;
|
||||
var opts = me.options;
|
||||
var labelOpts = opts.labels;
|
||||
var globalDefault = Chart.defaults.global,
|
||||
lineDefault = globalDefault.elements.line,
|
||||
legendWidth = me.width,
|
||||
lineWidths = me.lineWidths;
|
||||
var globalDefault = defaults.global;
|
||||
var lineDefault = globalDefault.elements.line;
|
||||
var legendWidth = me.width;
|
||||
var lineWidths = me.lineWidths;
|
||||
|
||||
if (opts.display) {
|
||||
var ctx = me.ctx,
|
||||
cursor,
|
||||
itemOrDefault = helpers.getValueOrDefault,
|
||||
fontColor = itemOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor),
|
||||
fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
|
||||
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
|
||||
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
|
||||
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
var ctx = me.ctx;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
|
||||
var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
|
||||
var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
|
||||
var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
|
||||
var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
var cursor;
|
||||
|
||||
// Canvas setup
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.lineWidth = 0.5;
|
||||
ctx.strokeStyle = fontColor; // for strikethrough effect
|
||||
ctx.fillStyle = fontColor; // render in correct colour
|
||||
ctx.font = labelFont;
|
||||
|
||||
var boxWidth = getBoxWidth(labelOpts, fontSize),
|
||||
hitboxes = me.legendHitBoxes;
|
||||
var boxWidth = getBoxWidth(labelOpts, fontSize);
|
||||
var hitboxes = me.legendHitBoxes;
|
||||
|
||||
// current position
|
||||
var drawLegendBox = function(x, y, legendItem) {
|
||||
@@ -338,17 +357,17 @@ module.exports = function(Chart) {
|
||||
// Set the ctx for the box
|
||||
ctx.save();
|
||||
|
||||
ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
|
||||
ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
|
||||
ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
|
||||
ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
|
||||
ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
|
||||
ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
|
||||
var isLineWidthZero = (itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
|
||||
ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
|
||||
ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
|
||||
ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
|
||||
ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
|
||||
ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
|
||||
ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
|
||||
var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
|
||||
|
||||
if (ctx.setLineDash) {
|
||||
// IE 9 and 10 do not support line dash
|
||||
ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash));
|
||||
ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
|
||||
}
|
||||
|
||||
if (opts.labels && opts.labels.usePointStyle) {
|
||||
@@ -360,7 +379,7 @@ module.exports = function(Chart) {
|
||||
var centerY = y + offSet;
|
||||
|
||||
// Draw pointStyle as legend symbol
|
||||
Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
|
||||
helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
|
||||
} else {
|
||||
// Draw box as legend symbol
|
||||
if (!isLineWidthZero) {
|
||||
@@ -372,14 +391,18 @@ module.exports = function(Chart) {
|
||||
ctx.restore();
|
||||
};
|
||||
var fillText = function(x, y, legendItem, textWidth) {
|
||||
ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y);
|
||||
var halfFontSize = fontSize / 2;
|
||||
var xLeft = boxWidth + halfFontSize + x;
|
||||
var yMiddle = y + halfFontSize;
|
||||
|
||||
ctx.fillText(legendItem.text, xLeft, yMiddle);
|
||||
|
||||
if (legendItem.hidden) {
|
||||
// Strikethrough the text if hidden
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 2;
|
||||
ctx.moveTo(boxWidth + (fontSize / 2) + x, y + (fontSize / 2));
|
||||
ctx.lineTo(boxWidth + (fontSize / 2) + x + textWidth, y + (fontSize / 2));
|
||||
ctx.moveTo(xLeft, yMiddle);
|
||||
ctx.lineTo(xLeft + textWidth, yMiddle);
|
||||
ctx.stroke();
|
||||
}
|
||||
};
|
||||
@@ -402,10 +425,10 @@ module.exports = function(Chart) {
|
||||
|
||||
var itemHeight = fontSize + labelOpts.padding;
|
||||
helpers.each(me.legendItems, function(legendItem, i) {
|
||||
var textWidth = ctx.measureText(legendItem.text).width,
|
||||
width = boxWidth + (fontSize / 2) + textWidth,
|
||||
x = cursor.x,
|
||||
y = cursor.y;
|
||||
var textWidth = ctx.measureText(legendItem.text).width;
|
||||
var width = boxWidth + (fontSize / 2) + textWidth;
|
||||
var x = cursor.x;
|
||||
var y = cursor.y;
|
||||
|
||||
if (isHorizontal) {
|
||||
if (x + width >= legendWidth) {
|
||||
@@ -462,8 +485,8 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
// Chart event already has relative position in it
|
||||
var x = e.x,
|
||||
y = e.y;
|
||||
var x = e.x;
|
||||
var y = e.y;
|
||||
|
||||
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
|
||||
// See if we are touching one of the dataset boxes
|
||||
@@ -520,7 +543,7 @@ module.exports = function(Chart) {
|
||||
var legend = chart.legend;
|
||||
|
||||
if (legendOpts) {
|
||||
legendOpts = helpers.configMerge(Chart.defaults.global.legend, legendOpts);
|
||||
helpers.mergeIf(legendOpts, defaults.global.legend);
|
||||
|
||||
if (legend) {
|
||||
layout.configure(chart, legend, legendOpts);
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
var defaults = require('../core/core.defaults');
|
||||
var Element = require('../core/core.element');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
defaults._set('global', {
|
||||
title: {
|
||||
display: false,
|
||||
fontStyle: 'bold',
|
||||
fullWidth: true,
|
||||
lineHeight: 1.2,
|
||||
padding: 10,
|
||||
position: 'top',
|
||||
text: '',
|
||||
weight: 2000 // by default greater than legend (1000) to be above
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
var layout = Chart.layoutService;
|
||||
var noop = helpers.noop;
|
||||
|
||||
Chart.defaults.global.title = {
|
||||
display: false,
|
||||
position: 'top',
|
||||
fullWidth: true,
|
||||
weight: 2000, // by default greater than legend (1000) to be above
|
||||
fontStyle: 'bold',
|
||||
padding: 10,
|
||||
|
||||
// actual title
|
||||
text: ''
|
||||
};
|
||||
|
||||
Chart.Title = Chart.Element.extend({
|
||||
Chart.Title = Element.extend({
|
||||
initialize: function(config) {
|
||||
var me = this;
|
||||
helpers.extend(me, config);
|
||||
@@ -105,19 +109,21 @@ module.exports = function(Chart) {
|
||||
|
||||
beforeFit: noop,
|
||||
fit: function() {
|
||||
var me = this,
|
||||
valueOrDefault = helpers.getValueOrDefault,
|
||||
opts = me.options,
|
||||
globalDefaults = Chart.defaults.global,
|
||||
display = opts.display,
|
||||
fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize),
|
||||
minSize = me.minSize;
|
||||
var me = this;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var opts = me.options;
|
||||
var display = opts.display;
|
||||
var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
|
||||
var minSize = me.minSize;
|
||||
var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
|
||||
var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
|
||||
var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;
|
||||
|
||||
if (me.isHorizontal()) {
|
||||
minSize.width = me.maxWidth; // fill all the width
|
||||
minSize.height = display ? fontSize + (opts.padding * 2) : 0;
|
||||
minSize.height = textSize;
|
||||
} else {
|
||||
minSize.width = display ? fontSize + (opts.padding * 2) : 0;
|
||||
minSize.width = textSize;
|
||||
minSize.height = me.maxHeight; // fill all the height
|
||||
}
|
||||
|
||||
@@ -135,25 +141,25 @@ module.exports = function(Chart) {
|
||||
|
||||
// Actually draw the title block on the canvas
|
||||
draw: function() {
|
||||
var me = this,
|
||||
ctx = me.ctx,
|
||||
valueOrDefault = helpers.getValueOrDefault,
|
||||
opts = me.options,
|
||||
globalDefaults = Chart.defaults.global;
|
||||
var me = this;
|
||||
var ctx = me.ctx;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var opts = me.options;
|
||||
var globalDefaults = defaults.global;
|
||||
|
||||
if (opts.display) {
|
||||
var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize),
|
||||
fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle),
|
||||
fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily),
|
||||
titleFont = helpers.fontString(fontSize, fontStyle, fontFamily),
|
||||
rotation = 0,
|
||||
titleX,
|
||||
titleY,
|
||||
top = me.top,
|
||||
left = me.left,
|
||||
bottom = me.bottom,
|
||||
right = me.right,
|
||||
maxWidth;
|
||||
var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
|
||||
var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
|
||||
var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
|
||||
var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
|
||||
var offset = lineHeight / 2 + opts.padding;
|
||||
var rotation = 0;
|
||||
var top = me.top;
|
||||
var left = me.left;
|
||||
var bottom = me.bottom;
|
||||
var right = me.right;
|
||||
var maxWidth, titleX, titleY;
|
||||
|
||||
ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
|
||||
ctx.font = titleFont;
|
||||
@@ -161,10 +167,10 @@ module.exports = function(Chart) {
|
||||
// Horizontal
|
||||
if (me.isHorizontal()) {
|
||||
titleX = left + ((right - left) / 2); // midpoint of the width
|
||||
titleY = top + ((bottom - top) / 2); // midpoint of the height
|
||||
titleY = top + offset;
|
||||
maxWidth = right - left;
|
||||
} else {
|
||||
titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2);
|
||||
titleX = opts.position === 'left' ? left + offset : right - offset;
|
||||
titleY = top + ((bottom - top) / 2);
|
||||
maxWidth = bottom - top;
|
||||
rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
|
||||
@@ -175,7 +181,18 @@ module.exports = function(Chart) {
|
||||
ctx.rotate(rotation);
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(opts.text, 0, 0, maxWidth);
|
||||
|
||||
var text = opts.text;
|
||||
if (helpers.isArray(text)) {
|
||||
var y = 0;
|
||||
for (var i = 0; i < text.length; ++i) {
|
||||
ctx.fillText(text[i], 0, y, maxWidth);
|
||||
y += lineHeight;
|
||||
}
|
||||
} else {
|
||||
ctx.fillText(text, 0, 0, maxWidth);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
@@ -209,7 +226,7 @@ module.exports = function(Chart) {
|
||||
var titleBlock = chart.titleBlock;
|
||||
|
||||
if (titleOpts) {
|
||||
titleOpts = helpers.configMerge(Chart.defaults.global.title, titleOpts);
|
||||
helpers.mergeIf(titleOpts, defaults.global.title);
|
||||
|
||||
if (titleBlock) {
|
||||
layout.configure(chart, titleBlock, titleOpts);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
// Default config for a category scale
|
||||
var defaultConfig = {
|
||||
position: 'bottom'
|
||||
@@ -16,7 +15,7 @@ module.exports = function(Chart) {
|
||||
*/
|
||||
getLabels: function() {
|
||||
var data = this.chart.data;
|
||||
return (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
|
||||
return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
|
||||
},
|
||||
|
||||
determineDataLimits: function() {
|
||||
@@ -28,13 +27,13 @@ module.exports = function(Chart) {
|
||||
|
||||
if (me.options.ticks.min !== undefined) {
|
||||
// user specified min value
|
||||
findIndex = helpers.indexOf(labels, me.options.ticks.min);
|
||||
findIndex = labels.indexOf(me.options.ticks.min);
|
||||
me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
|
||||
}
|
||||
|
||||
if (me.options.ticks.max !== undefined) {
|
||||
// user specified max value
|
||||
findIndex = helpers.indexOf(labels, me.options.ticks.max);
|
||||
findIndex = labels.indexOf(me.options.ticks.max);
|
||||
me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
|
||||
}
|
||||
|
||||
@@ -61,10 +60,11 @@ module.exports = function(Chart) {
|
||||
},
|
||||
|
||||
// Used to get data value locations. Value can either be an index or a numerical value
|
||||
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
|
||||
getPixelForValue: function(value, index) {
|
||||
var me = this;
|
||||
var offset = me.options.offset;
|
||||
// 1 is added because we need the length but we have the indexes
|
||||
var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
|
||||
var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);
|
||||
|
||||
// If value is a data object, then index is the index in the data array,
|
||||
// not the index of the scale. We need to change that.
|
||||
@@ -83,7 +83,7 @@ module.exports = function(Chart) {
|
||||
var valueWidth = me.width / offsetAmt;
|
||||
var widthOffset = (valueWidth * (index - me.minIndex));
|
||||
|
||||
if (me.options.gridLines.offsetGridLines && includeOffset || me.maxIndex === me.minIndex && includeOffset) {
|
||||
if (offset) {
|
||||
widthOffset += (valueWidth / 2);
|
||||
}
|
||||
|
||||
@@ -92,25 +92,26 @@ module.exports = function(Chart) {
|
||||
var valueHeight = me.height / offsetAmt;
|
||||
var heightOffset = (valueHeight * (index - me.minIndex));
|
||||
|
||||
if (me.options.gridLines.offsetGridLines && includeOffset) {
|
||||
if (offset) {
|
||||
heightOffset += (valueHeight / 2);
|
||||
}
|
||||
|
||||
return me.top + Math.round(heightOffset);
|
||||
},
|
||||
getPixelForTick: function(index, includeOffset) {
|
||||
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
|
||||
getPixelForTick: function(index) {
|
||||
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
|
||||
},
|
||||
getValueForPixel: function(pixel) {
|
||||
var me = this;
|
||||
var offset = me.options.offset;
|
||||
var value;
|
||||
var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
|
||||
var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
|
||||
var horz = me.isHorizontal();
|
||||
var valueDimension = (horz ? me.width : me.height) / offsetAmt;
|
||||
|
||||
pixel -= horz ? me.left : me.top;
|
||||
|
||||
if (me.options.gridLines.offsetGridLines) {
|
||||
if (offset) {
|
||||
pixel -= (valueDimension / 2);
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ module.exports = function(Chart) {
|
||||
value = Math.round(pixel / valueDimension);
|
||||
}
|
||||
|
||||
return value;
|
||||
return value + me.minIndex;
|
||||
},
|
||||
getBasePixel: function() {
|
||||
return this.bottom;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var defaults = require('../core/core.defaults');
|
||||
var helpers = require('../helpers/index');
|
||||
var Ticks = require('../core/core.ticks');
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var defaultConfig = {
|
||||
position: 'left',
|
||||
ticks: {
|
||||
callback: Chart.Ticks.formatters.linear
|
||||
callback: Ticks.formatters.linear
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,8 +126,8 @@ module.exports = function(Chart) {
|
||||
});
|
||||
}
|
||||
|
||||
me.min = isFinite(me.min) ? me.min : DEFAULT_MIN;
|
||||
me.max = isFinite(me.max) ? me.max : DEFAULT_MAX;
|
||||
me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
|
||||
me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
|
||||
|
||||
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
|
||||
this.handleTickRangeOptions();
|
||||
@@ -139,7 +141,7 @@ module.exports = function(Chart) {
|
||||
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
|
||||
} else {
|
||||
// The factor of 2 used to scale the font size has been experimentally determined.
|
||||
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize);
|
||||
var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
|
||||
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
var helpers = require('../helpers/index');
|
||||
var Ticks = require('../core/core.ticks');
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var helpers = Chart.helpers,
|
||||
noop = helpers.noop;
|
||||
var noop = helpers.noop;
|
||||
|
||||
Chart.LinearScaleBase = Chart.Scale.extend({
|
||||
getRightValue: function(value) {
|
||||
if (typeof value === 'string') {
|
||||
return +value;
|
||||
}
|
||||
return Chart.Scale.prototype.getRightValue.call(this, value);
|
||||
},
|
||||
|
||||
handleTickRangeOptions: function() {
|
||||
var me = this;
|
||||
var opts = me.options;
|
||||
@@ -27,6 +36,9 @@ module.exports = function(Chart) {
|
||||
}
|
||||
}
|
||||
|
||||
var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
|
||||
var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
|
||||
|
||||
if (tickOpts.min !== undefined) {
|
||||
me.min = tickOpts.min;
|
||||
} else if (tickOpts.suggestedMin !== undefined) {
|
||||
@@ -47,6 +59,20 @@ module.exports = function(Chart) {
|
||||
}
|
||||
}
|
||||
|
||||
if (setMin !== setMax) {
|
||||
// We set the min or the max but not both.
|
||||
// So ensure that our range is good
|
||||
// Inverted or 0 length range can happen when
|
||||
// ticks.min is set, and no datasets are visible
|
||||
if (me.min >= me.max) {
|
||||
if (setMin) {
|
||||
me.max = me.min + 1;
|
||||
} else {
|
||||
me.min = me.max - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (me.min === me.max) {
|
||||
me.max++;
|
||||
|
||||
@@ -74,9 +100,9 @@ module.exports = function(Chart) {
|
||||
maxTicks: maxTicks,
|
||||
min: tickOpts.min,
|
||||
max: tickOpts.max,
|
||||
stepSize: helpers.getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
|
||||
stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
|
||||
};
|
||||
var ticks = me.ticks = Chart.Ticks.generators.linear(numericGeneratorOptions, me);
|
||||
var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me);
|
||||
|
||||
me.handleDirectionalChanges();
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var helpers = require('../helpers/index');
|
||||
var Ticks = require('../core/core.ticks');
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var defaultConfig = {
|
||||
position: 'left',
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
callback: Chart.Ticks.formatters.logarithmic
|
||||
callback: Ticks.formatters.logarithmic
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,7 +22,7 @@ module.exports = function(Chart) {
|
||||
var chart = me.chart;
|
||||
var data = chart.data;
|
||||
var datasets = data.datasets;
|
||||
var getValueOrDefault = helpers.getValueOrDefault;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var isHorizontal = me.isHorizontal();
|
||||
function IDMatches(meta) {
|
||||
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
|
||||
@@ -120,8 +121,8 @@ module.exports = function(Chart) {
|
||||
});
|
||||
}
|
||||
|
||||
me.min = getValueOrDefault(tickOpts.min, me.min);
|
||||
me.max = getValueOrDefault(tickOpts.max, me.max);
|
||||
me.min = valueOrDefault(tickOpts.min, me.min);
|
||||
me.max = valueOrDefault(tickOpts.max, me.max);
|
||||
|
||||
if (me.min === me.max) {
|
||||
if (me.min !== 0 && me.min !== null) {
|
||||
@@ -142,7 +143,7 @@ module.exports = function(Chart) {
|
||||
min: tickOpts.min,
|
||||
max: tickOpts.max
|
||||
};
|
||||
var ticks = me.ticks = Chart.Ticks.generators.logarithmic(generationOptions, me);
|
||||
var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me);
|
||||
|
||||
if (!me.isHorizontal()) {
|
||||
// We are in a vertical orientation. The top value is the highest. So reverse the array
|
||||
@@ -178,14 +179,11 @@ module.exports = function(Chart) {
|
||||
},
|
||||
getPixelForValue: function(value) {
|
||||
var me = this;
|
||||
var innerDimension;
|
||||
var pixel;
|
||||
|
||||
var start = me.start;
|
||||
var newVal = +me.getRightValue(value);
|
||||
var range;
|
||||
var opts = me.options;
|
||||
var tickOpts = opts.ticks;
|
||||
var innerDimension, pixel, range;
|
||||
|
||||
if (me.isHorizontal()) {
|
||||
range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
|
||||
@@ -205,7 +203,7 @@ module.exports = function(Chart) {
|
||||
} else if (newVal === me.minNotZero) {
|
||||
pixel = me.bottom - innerDimension * 0.02;
|
||||
} else {
|
||||
pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
|
||||
pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
|
||||
}
|
||||
} else if (me.end === 0 && tickOpts.reverse) {
|
||||
range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
|
||||
@@ -214,7 +212,7 @@ module.exports = function(Chart) {
|
||||
} else if (newVal === me.minNotZero) {
|
||||
pixel = me.top + innerDimension * 0.02;
|
||||
} else {
|
||||
pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
|
||||
pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
|
||||
}
|
||||
} else if (newVal === 0) {
|
||||
pixel = tickOpts.reverse ? me.top : me.bottom;
|
||||
@@ -234,7 +232,7 @@ module.exports = function(Chart) {
|
||||
if (me.isHorizontal()) {
|
||||
innerDimension = me.width;
|
||||
value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension);
|
||||
} else { // todo: if start === 0
|
||||
} else { // todo: if start === 0
|
||||
innerDimension = me.height;
|
||||
value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var defaults = require('../core/core.defaults');
|
||||
var helpers = require('../helpers/index');
|
||||
var Ticks = require('../core/core.ticks');
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
var globalDefaults = Chart.defaults.global;
|
||||
var globalDefaults = defaults.global;
|
||||
|
||||
var defaultConfig = {
|
||||
display: true,
|
||||
@@ -36,7 +39,7 @@ module.exports = function(Chart) {
|
||||
// Number - The backdrop padding to the side of the label in pixels
|
||||
backdropPaddingX: 2,
|
||||
|
||||
callback: Chart.Ticks.formatters.linear
|
||||
callback: Ticks.formatters.linear
|
||||
},
|
||||
|
||||
pointLabels: {
|
||||
@@ -60,9 +63,9 @@ module.exports = function(Chart) {
|
||||
|
||||
function getPointLabelFontOptions(scale) {
|
||||
var pointLabelOptions = scale.options.pointLabels;
|
||||
var fontSize = helpers.getValueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
|
||||
var fontStyle = helpers.getValueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
|
||||
var fontFamily = helpers.getValueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
|
||||
var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
|
||||
var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
|
||||
var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
|
||||
var font = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
|
||||
return {
|
||||
@@ -150,9 +153,7 @@ module.exports = function(Chart) {
|
||||
b: 0
|
||||
};
|
||||
var furthestAngles = {};
|
||||
var i;
|
||||
var textSize;
|
||||
var pointPosition;
|
||||
var i, textSize, pointPosition;
|
||||
|
||||
scale.ctx.font = plFont.font;
|
||||
scale._pointLabelSizes = [];
|
||||
@@ -219,7 +220,7 @@ module.exports = function(Chart) {
|
||||
|
||||
for (var i = 0; i < text.length; ++i) {
|
||||
ctx.fillText(text[i], position.x, y);
|
||||
y+= spacing;
|
||||
y += spacing;
|
||||
}
|
||||
} else {
|
||||
ctx.fillText(text, position.x, position.y);
|
||||
@@ -236,7 +237,7 @@ module.exports = function(Chart) {
|
||||
|
||||
function drawPointLabels(scale) {
|
||||
var ctx = scale.ctx;
|
||||
var getValueOrDefault = helpers.getValueOrDefault;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var opts = scale.options;
|
||||
var angleLineOpts = opts.angleLines;
|
||||
var pointLabelOpts = opts.pointLabels;
|
||||
@@ -244,7 +245,7 @@ module.exports = function(Chart) {
|
||||
ctx.lineWidth = angleLineOpts.lineWidth;
|
||||
ctx.strokeStyle = angleLineOpts.color;
|
||||
|
||||
var outerDistance = scale.getDistanceFromCenterForValue(opts.reverse ? scale.min : scale.max);
|
||||
var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
|
||||
|
||||
// Point Label Font
|
||||
var plFont = getPointLabelFontOptions(scale);
|
||||
@@ -266,7 +267,7 @@ module.exports = function(Chart) {
|
||||
var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);
|
||||
|
||||
// Keep this in loop since we may support array properties here
|
||||
var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
|
||||
var pointLabelFontColor = valueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
|
||||
ctx.font = plFont.font;
|
||||
ctx.fillStyle = pointLabelFontColor;
|
||||
|
||||
@@ -281,8 +282,8 @@ module.exports = function(Chart) {
|
||||
|
||||
function drawRadiusLine(scale, gridLineOpts, radius, index) {
|
||||
var ctx = scale.ctx;
|
||||
ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1);
|
||||
ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
|
||||
ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
|
||||
ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
|
||||
|
||||
if (scale.options.gridLines.circular) {
|
||||
// Draw circular arcs between the points
|
||||
@@ -328,7 +329,7 @@ module.exports = function(Chart) {
|
||||
me.yCenter = Math.round(me.height / 2);
|
||||
|
||||
var minSize = helpers.min([me.height, me.width]);
|
||||
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
|
||||
var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
|
||||
me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
|
||||
},
|
||||
determineDataLimits: function() {
|
||||
@@ -361,11 +362,12 @@ module.exports = function(Chart) {
|
||||
},
|
||||
getTickLimit: function() {
|
||||
var tickOpts = this.options.ticks;
|
||||
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
|
||||
var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
|
||||
return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
|
||||
},
|
||||
convertTicksToLabels: function() {
|
||||
var me = this;
|
||||
|
||||
Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
|
||||
|
||||
// Point labels
|
||||
@@ -404,10 +406,10 @@ module.exports = function(Chart) {
|
||||
},
|
||||
setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
|
||||
var me = this;
|
||||
var maxRight = me.width - rightMovement - me.drawingArea,
|
||||
maxLeft = leftMovement + me.drawingArea,
|
||||
maxTop = topMovement + me.drawingArea,
|
||||
maxBottom = me.height - bottomMovement - me.drawingArea;
|
||||
var maxRight = me.width - rightMovement - me.drawingArea;
|
||||
var maxLeft = leftMovement + me.drawingArea;
|
||||
var maxTop = topMovement + me.drawingArea;
|
||||
var maxBottom = me.height - bottomMovement - me.drawingArea;
|
||||
|
||||
me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
|
||||
me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
|
||||
@@ -433,7 +435,7 @@ module.exports = function(Chart) {
|
||||
|
||||
// Take into account half font size + the yPadding of the top value
|
||||
var scalingFactor = me.drawingArea / (me.max - me.min);
|
||||
if (me.options.reverse) {
|
||||
if (me.options.ticks.reverse) {
|
||||
return (me.max - value) * scalingFactor;
|
||||
}
|
||||
return (value - me.min) * scalingFactor;
|
||||
@@ -456,9 +458,9 @@ module.exports = function(Chart) {
|
||||
var max = me.max;
|
||||
|
||||
return me.getPointPositionForValue(0,
|
||||
me.beginAtZero? 0:
|
||||
min < 0 && max < 0? max :
|
||||
min > 0 && max > 0? min :
|
||||
me.beginAtZero ? 0 :
|
||||
min < 0 && max < 0 ? max :
|
||||
min > 0 && max > 0 ? min :
|
||||
0);
|
||||
},
|
||||
|
||||
@@ -467,22 +469,22 @@ module.exports = function(Chart) {
|
||||
var opts = me.options;
|
||||
var gridLineOpts = opts.gridLines;
|
||||
var tickOpts = opts.ticks;
|
||||
var getValueOrDefault = helpers.getValueOrDefault;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
|
||||
if (opts.display) {
|
||||
var ctx = me.ctx;
|
||||
var startAngle = this.getIndexAngle(0);
|
||||
|
||||
// Tick Font
|
||||
var tickFontSize = getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
|
||||
var tickFontStyle = getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
|
||||
var tickFontFamily = getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
|
||||
var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
|
||||
var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
|
||||
var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
|
||||
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
|
||||
|
||||
helpers.each(me.ticks, function(label, index) {
|
||||
// Don't draw a centre value (if it is minimum)
|
||||
if (index > 0 || opts.reverse) {
|
||||
if (index > 0 || tickOpts.reverse) {
|
||||
var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
|
||||
var yHeight = me.yCenter - yCenterOffset;
|
||||
|
||||
// Draw circular lines around the scale
|
||||
if (gridLineOpts.display && index !== 0) {
|
||||
@@ -490,15 +492,19 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
if (tickOpts.display) {
|
||||
var tickFontColor = getValueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
|
||||
var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
|
||||
ctx.font = tickLabelFont;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(me.xCenter, me.yCenter);
|
||||
ctx.rotate(startAngle);
|
||||
|
||||
if (tickOpts.showLabelBackdrop) {
|
||||
var labelWidth = ctx.measureText(label).width;
|
||||
ctx.fillStyle = tickOpts.backdropColor;
|
||||
ctx.fillRect(
|
||||
me.xCenter - labelWidth / 2 - tickOpts.backdropPaddingX,
|
||||
yHeight - tickFontSize / 2 - tickOpts.backdropPaddingY,
|
||||
-labelWidth / 2 - tickOpts.backdropPaddingX,
|
||||
-yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY,
|
||||
labelWidth + tickOpts.backdropPaddingX * 2,
|
||||
tickFontSize + tickOpts.backdropPaddingY * 2
|
||||
);
|
||||
@@ -507,7 +513,8 @@ module.exports = function(Chart) {
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillStyle = tickFontColor;
|
||||
ctx.fillText(label, me.xCenter, yHeight);
|
||||
ctx.fillText(label, 0, -yCenterOffset);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
13
test/.eslintrc
Normal file
13
test/.eslintrc
Normal file
@@ -0,0 +1,13 @@
|
||||
env:
|
||||
jasmine: true
|
||||
|
||||
globals:
|
||||
acquireChart: true
|
||||
Chart: true
|
||||
moment: true
|
||||
waitForResize: true
|
||||
|
||||
# http://eslint.org/docs/rules/
|
||||
rules:
|
||||
# Best Practices
|
||||
complexity: 0
|
||||
41
test/fixtures/core.scale/label-offset-vertical-axes.json
vendored
Normal file
41
test/fixtures/core.scale/label-offset-vertical-axes.json
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "horizontalBar",
|
||||
"data": {
|
||||
"labels": ["\u25C0", "\u25A0", "\u25C6", "\u25CF"],
|
||||
"datasets": [{
|
||||
"data": [12, 19, 3, 5]
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"ticks": {
|
||||
"display": false
|
||||
},
|
||||
"gridLines":{
|
||||
"display": false,
|
||||
"drawBorder": false
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"ticks": {
|
||||
"labelOffset": 25
|
||||
},
|
||||
"gridLines":{
|
||||
"display": false,
|
||||
"drawBorder": false
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/core.scale/label-offset-vertical-axes.png
vendored
Normal file
BIN
test/fixtures/core.scale/label-offset-vertical-axes.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
@@ -24,14 +24,10 @@
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"ticks": {
|
||||
"display": false
|
||||
}
|
||||
"display": false
|
||||
}],
|
||||
"yAxes": [{
|
||||
"ticks": {
|
||||
"display": false
|
||||
}
|
||||
"display": false
|
||||
}]
|
||||
},
|
||||
"elements": {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -24,14 +24,10 @@
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"ticks": {
|
||||
"display": false
|
||||
}
|
||||
"display": false
|
||||
}],
|
||||
"yAxes": [{
|
||||
"ticks": {
|
||||
"display": false
|
||||
}
|
||||
"display": false
|
||||
}]
|
||||
},
|
||||
"elements": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user