Comparar commits
1 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 669499ef1e |
@@ -7,5 +7,3 @@ custom/*
|
||||
docs/index.md
|
||||
|
||||
bower_components/
|
||||
|
||||
coverage/*
|
||||
|
||||
externo
+6143
-5778
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+6
-31
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+172
-76
@@ -25,159 +25,255 @@ require(['path/to/Chartjs'], function(Chart){
|
||||
});
|
||||
```
|
||||
|
||||
You can also grab Chart.js using bower, npm, or CDN:
|
||||
You can also grab Chart.js using bower:
|
||||
|
||||
```bash
|
||||
bower install Chart.js --save
|
||||
```
|
||||
```bash
|
||||
npm install Chart.js --save
|
||||
```
|
||||
|
||||
Also, Chart.js is available from CDN:
|
||||
|
||||
https://cdnjs.com/libraries/chart.js
|
||||
|
||||
###Creating a chart
|
||||
|
||||
To create a chart, we need to instantiate the `Chart` class. To do this, we need to pass in the node, jQuery instance, or 2d context of the canvas of where we want to draw the chart. Here's an example.
|
||||
To create a chart, we need to instantiate the `Chart` class. To do this, we need to pass in the 2d context of where we want to draw the chart. Here's an example.
|
||||
|
||||
```html
|
||||
<canvas id="myChart" width="400" height="400"></canvas>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Any of the following formats may be used
|
||||
var ctx = document.getElementById("myChart");
|
||||
// Get the context of the canvas element we want to select
|
||||
var ctx = document.getElementById("myChart").getContext("2d");
|
||||
var ctx = $("myChart");
|
||||
var myNewChart = new Chart(ctx).PolarArea(data);
|
||||
```
|
||||
|
||||
Once you have the element or context, you're ready to instantiate a pre-defined chart-type or create your own!
|
||||
We can also get the context of our canvas with jQuery. To do this, we need to get the DOM node out of the jQuery collection, and call the `getContext("2d")` method on that.
|
||||
|
||||
The following example instantiates a the pre-defined Polar Area chart type with a config object of data and options.
|
||||
```javascript
|
||||
var myNewChart = Chart.PolarArea(ctx, {
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
// Get context with jQuery - using jQuery's .get() method.
|
||||
var ctx = $("#myChart").get(0).getContext("2d");
|
||||
// This will get the first returned node in the jQuery collection.
|
||||
var myNewChart = new Chart(ctx);
|
||||
```
|
||||
|
||||
To create a scatter chart, which is a special configuration of a line chart, we use the following.
|
||||
After we've instantiated the Chart class on the canvas we want to draw on, Chart.js will handle the scaling for retina displays.
|
||||
|
||||
With the Chart class set up, we can go on to create one of the charts Chart.js has available. In the example below, we would be drawing a Polar area chart.
|
||||
|
||||
```javascript
|
||||
var myScatterChart = Chart.Scatter(ctx, {
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
new Chart(ctx).PolarArea(data, options);
|
||||
```
|
||||
|
||||
Alternatively, we can use the more advanced API to create simple or advanced chart types. In the example below, we are creating a line chart.
|
||||
```javascript
|
||||
var myChart = new Chart(ctx, {
|
||||
type: 'line', // built in types are 'line', 'bar', 'radar', 'polarArea', 'doughnut', 'scatter'
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
```
|
||||
We call a method of the name of the chart we want to create. We pass in the data for that chart type, and the options for that chart as parameters. Chart.js will merge the global defaults with chart type specific defaults, then merge any options passed in as a second argument after data.
|
||||
|
||||
###Global chart configuration
|
||||
|
||||
This concept was introduced in Chart.js 1.0 to keep configuration DRY, and allow for changing options globally across chart types, avoiding the need to specify options for each instance, or the default for a particular chart type.
|
||||
|
||||
Chart.js merges configurations and options in a few places with the global defaults using chart type defaults and scales defaults. This way you can be as specific as you want in your individual chart configs, or change the defaults for Chart.js as a whole.
|
||||
|
||||
```javascript
|
||||
Chart.defaults.global = {
|
||||
responsive: true,
|
||||
responsiveAnimationDuration: 0,
|
||||
maintainAspectRatio: true,
|
||||
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
|
||||
hover: {
|
||||
onHover: null,
|
||||
mode: 'single',
|
||||
animationDuration: 400,
|
||||
},
|
||||
onClick: null,
|
||||
defaultColor: 'rgba(0,0,0,0.1)',
|
||||
|
||||
// Element defaults defined in element extensions
|
||||
elements: {},
|
||||
|
||||
// Legend template string
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i = 0; i < data.datasets.length; i++){%><li><span style=\"background-color:<%=data.datasets[i].backgroundColor%>\"><%if(data.datasets[i].label){%><%=data.datasets[i].label%><%}%></span></li><%}%></ul>",
|
||||
|
||||
// Animation settings
|
||||
animation: {
|
||||
// Length that animation should take in ms assuming 60fps.
|
||||
// Set to 0 to disable animation
|
||||
duration: 1000,
|
||||
|
||||
// Easing type. Possible values are:
|
||||
// [easeInOutQuart, linear, easeOutBounce, easeInBack, easeInOutQuad,
|
||||
// easeOutQuart, easeOutQuad, easeInOutBounce, easeOutSine, easeInOutCubic,
|
||||
// easeInExpo, easeInOutBack, easeInCirc, easeInOutElastic, easeOutBack,
|
||||
// easeInQuad, easeInOutExpo, easeInQuart, easeOutQuint, easeInOutCirc,
|
||||
// easeInSine, easeOutExpo, easeOutCirc, easeOutCubic, easeInQuint,
|
||||
// easeInElastic, easeInOutSine, easeInOutQuint, easeInBounce,
|
||||
// easeOutElastic, easeInCubic]
|
||||
easing: "easeOutQuart",
|
||||
|
||||
// Function - function to call each time an animation step occurs
|
||||
onProgress: function() {},
|
||||
|
||||
// Function - function to call when animations finish
|
||||
onComplete: function() {},
|
||||
},
|
||||
|
||||
tooltips:{
|
||||
// Boolean - if true, resize the charts when the page resizes
|
||||
responsive: false,
|
||||
|
||||
// Boolean - if true, try to maintain the screen aspect ratio
|
||||
maintainAspectRatio: true,
|
||||
|
||||
// Array - events to bind tooltips to
|
||||
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
|
||||
|
||||
// Hover settings
|
||||
hover: {
|
||||
// Function - called when the user hovers over the items.
|
||||
// Parameters: array of active elements
|
||||
onHover: null,
|
||||
|
||||
// String - hover mode. Options are 'single', 'label', and 'dataset'
|
||||
// 'single' gets the nearest element
|
||||
// 'label' gets all of the elements at the given dataset index (do not use for scatter charts)
|
||||
// 'dataset' gets all the elements in the given dataset
|
||||
mode: 'single',
|
||||
|
||||
// Number - duration (in ms) of the tooltip animation. 0 to disable
|
||||
animationDuration: 400,
|
||||
},
|
||||
|
||||
// Function - click handler to bind to chart area
|
||||
onClick: null,
|
||||
|
||||
// Tooltip configuration
|
||||
tooltips: {
|
||||
// Boolean - if true show tooltips
|
||||
enabled: true,
|
||||
|
||||
// Function - custom tooltip function to use
|
||||
custom: null,
|
||||
|
||||
// String - color of tooltip background
|
||||
backgroundColor: "rgba(0,0,0,0.8)",
|
||||
|
||||
// String - fonts to use
|
||||
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
|
||||
// Number - font size
|
||||
fontSize: 10,
|
||||
|
||||
// String - font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - font color
|
||||
fontColor: "#fff",
|
||||
|
||||
// String - title fonts
|
||||
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
|
||||
// Number - title font size
|
||||
titleFontSize: 12,
|
||||
|
||||
// String - title font style
|
||||
titleFontStyle: "bold",
|
||||
|
||||
// String - title font color
|
||||
titleFontColor: "#fff",
|
||||
|
||||
// Number -
|
||||
yPadding: 6,
|
||||
|
||||
// Number -
|
||||
xPadding: 6,
|
||||
|
||||
// Number -
|
||||
caretSize: 8,
|
||||
|
||||
// Number - radius of rounded corners
|
||||
cornerRadius: 6,
|
||||
|
||||
// Number -
|
||||
xOffset: 10,
|
||||
|
||||
// String - template string to use for tooltips in single mode
|
||||
template: [
|
||||
'<% if(label){ %>',
|
||||
'<%=label %>: ',
|
||||
'<%=label %>:',
|
||||
'<% } %>',
|
||||
'<%=value %>',
|
||||
].join(''),
|
||||
|
||||
// String - template string to use for tooltips in label mode
|
||||
multiTemplate: [
|
||||
'<%if (datasetLabel){ %>',
|
||||
'<%=datasetLabel %>: ',
|
||||
'<%=datasetLabel %>:',
|
||||
'<% } %>',
|
||||
'<%=value %>'
|
||||
].join(''),
|
||||
|
||||
// String -
|
||||
multiKeyBackground: '#fff',
|
||||
},
|
||||
|
||||
// String - default grey color. 'rgba(0,0,0,0.1)'
|
||||
defaultColor: defaultColor,
|
||||
|
||||
// Element defaults
|
||||
elements: {
|
||||
arc: {
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderColor: "#fff",
|
||||
borderWidth: 2
|
||||
},
|
||||
// Default settings for all line elements
|
||||
line: {
|
||||
// Number - Bezier curve tension. Set to 0 for no bezier curves
|
||||
tension: 0.4,
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
|
||||
// String - the fill color
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - width of the line
|
||||
borderWidth: 3,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
fill: true, // do we fill in the area between the line and its base axis
|
||||
|
||||
// String = color of the line
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Boolean - if true fill in the area between the line and the x axis with the background color
|
||||
fill: true,
|
||||
|
||||
// Boolean -
|
||||
skipNull: true,
|
||||
|
||||
// Boolean -
|
||||
drawNull: false,
|
||||
},
|
||||
// Settings for all point elements
|
||||
point: {
|
||||
// Number - radius of point circle
|
||||
radius: 3,
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
// Hover
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1,
|
||||
},
|
||||
rectangle: {
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 0,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
}
|
||||
|
||||
// String - fill color of point
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - width of stroke of point circle
|
||||
borderWidth: 1,
|
||||
|
||||
// String - stroke color for point
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Number - extra radius added to radius for hit detection
|
||||
hitRadius: 6,
|
||||
|
||||
// Number - radius of point circle when hovered
|
||||
hoverRadius: 4,
|
||||
|
||||
// Number - radius of circle stroke when hovered
|
||||
hoverBorderWidth: 2,
|
||||
},
|
||||
// Settings for all bar elements
|
||||
bar: {
|
||||
// String - fill color of bar
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// Number - width of stroke of line surrounding bar fill
|
||||
borderWidth: 0,
|
||||
|
||||
// String - Border color
|
||||
borderColor: defaultColor,
|
||||
|
||||
// Number -
|
||||
valueSpacing: 5,
|
||||
|
||||
// Number -
|
||||
datasetSpacing: 1,
|
||||
},
|
||||
// Settings for all slice elements
|
||||
slice: {
|
||||
// String - fill color
|
||||
backgroundColor: defaultColor,
|
||||
|
||||
// String - border color
|
||||
borderColor: "#fff",
|
||||
|
||||
// Number - border stroke width
|
||||
borderWidth: 2,
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -188,4 +284,4 @@ If for example, you wanted all charts created to be responsive, and resize when
|
||||
Chart.defaults.global.responsive = true;
|
||||
```
|
||||
|
||||
Now, every time we create a chart, `options.responsive` will be `true`.
|
||||
Now, every time we create a chart, `options.responsive` will be `true`.
|
||||
|
||||
@@ -13,16 +13,7 @@ Often, it is used to show trend data, and the comparison of two data sets.
|
||||
|
||||
###Example usage
|
||||
```javascript
|
||||
var myLineChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
```
|
||||
|
||||
Alternatively a line chart can be created using syntax similar to the v1.0 syntax
|
||||
```javascript
|
||||
var myLineChart = Chart.Line(ctx, {
|
||||
var myLineChart = new Chart(ctx).Line({
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
@@ -47,18 +38,6 @@ var data = {
|
||||
// String or array - Line color
|
||||
borderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// String - cap style of the line. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
|
||||
borderCapStyle: 'butt',
|
||||
|
||||
// Array - Length and spacing of dashes. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
|
||||
borderDash: [],
|
||||
|
||||
// Number - Offset for line dashes. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
|
||||
borderDashOffset: 0.0,
|
||||
|
||||
// String - line join style. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
|
||||
borderJoinStyle: 'miter',
|
||||
|
||||
// String or array - Point stroke color
|
||||
pointBorderColor: "rgba(220,220,220,1)",
|
||||
|
||||
@@ -78,7 +57,7 @@ var data = {
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
|
||||
// Number or array - border width of point when hovered
|
||||
pointHoverBorderWidth: 2,
|
||||
pointBorderWidth: 2,
|
||||
|
||||
// The actual data
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
@@ -97,7 +76,7 @@ var data = {
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: "rgba(220,220,220,1)",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointHoverBorderWidth: 2,
|
||||
pointBorderWidth: 2,
|
||||
data: [28, 48, 40, 19, 86, 27, 90]
|
||||
}
|
||||
]
|
||||
@@ -124,22 +103,138 @@ These are the customisation options specific to Line charts. These options are m
|
||||
},
|
||||
|
||||
scales: {
|
||||
// Defines all of the x axes used in the chart. See the [scale documentation](#getting-started-scales) for details on the available options
|
||||
// The line chart officially supports only 1 x-axis but uses an array to keep the API consistent. Use a scatter chart if you need multiple x axes.
|
||||
xAxes: [{
|
||||
// String - type of scale. Built in types are 'category' and 'linear'
|
||||
type: 'category',
|
||||
// String - type of axis to use. Should not be changed from 'dataset'. To use a 'linear' axis on the x, use the scatter chart type
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
// String - position of the scale. possible options are "top" and "bottom" for dataset scales
|
||||
position: "bottom",
|
||||
|
||||
// String - id of the axis so that data can bind to it
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
// Number - width of the grid line for the first index (index 0)
|
||||
zeroLineWidth: 1,
|
||||
|
||||
// String - color of the grid line for the first index
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
|
||||
// Boolean - if true, offset labels from grid lines
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
|
||||
// Defines all of the y axes used in the chart.
|
||||
// By default, the line chart uses a linear scale along the y axis
|
||||
yAxes: [{
|
||||
type: 'linear',
|
||||
// String - type of axis. 'linear' is the default but extensions may provide other types such as logarithmic
|
||||
scaleType: "linear",
|
||||
|
||||
// String - ID of the axis for data binding
|
||||
id: "y-axis-1",
|
||||
// Boolean - if true, show the scale
|
||||
display: true,
|
||||
|
||||
// String - position of axis. Vertical axes can have either "left" or "right"
|
||||
position: "left",
|
||||
|
||||
// ID of the axis for data binding
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
// Boolean - if true, show the grid lines
|
||||
show: true,
|
||||
|
||||
// String - color of the grid lines
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
|
||||
// Number - width of the grid lines
|
||||
lineWidth: 1,
|
||||
|
||||
// Boolean - if true draw lines on the chart area
|
||||
drawOnChartArea: true,
|
||||
|
||||
// Boolean - if true draw ticks in the axis area
|
||||
drawTicks: true,
|
||||
|
||||
// Number - width of the grid line representing a numerical value of 0
|
||||
zeroLineWidth: 1,
|
||||
|
||||
// String - color of the grid line representing a numerical value of 0
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// Boolean - if true ensures that the scale always has a 0 point
|
||||
beginAtZero: false,
|
||||
|
||||
// Object - if specified, allows the user to override the step generation algorithm.
|
||||
// Contains the following values
|
||||
// start: // number to start at
|
||||
// stepWidth: // size of step
|
||||
// steps: // number of steps
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
// Boolean - if true show labels
|
||||
show: true,
|
||||
|
||||
// String - template string for labels
|
||||
template: "<%=value%>",
|
||||
|
||||
// Function - if specified this is passed the tick value, index, and the array of all tick values. Returns a string that is used as the label for that value
|
||||
userCallback: null,
|
||||
|
||||
// Number - label font size
|
||||
fontSize: 12,
|
||||
|
||||
// String - label font style
|
||||
fontStyle: "normal",
|
||||
|
||||
// String - label font color
|
||||
fontColor: "#666",
|
||||
|
||||
// String - label font family
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
}
|
||||
};
|
||||
@@ -150,8 +245,7 @@ You can override these for your `Chart` instance by passing a member `options` i
|
||||
For example, we could have a line chart display without an x axis by doing the following. The config merge is smart enough to handle arrays so that you do not need to specify all axis settings to change one thing.
|
||||
|
||||
```javascript
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
new Chart(ctx).Line({
|
||||
data: data,
|
||||
options: {
|
||||
xAxes: [{
|
||||
@@ -168,13 +262,13 @@ We can also change these defaults values for each Line type that is created, thi
|
||||
|
||||
### Prototype methods
|
||||
|
||||
#### .getElementsAtEvent( event )
|
||||
#### .getPointsAtEvent( event )
|
||||
|
||||
Calling `getElementsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event.
|
||||
Calling `getPointsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event.
|
||||
|
||||
```javascript
|
||||
canvas.onclick = function(evt){
|
||||
var activePoints = myLineChart.getElementsAtEvent(evt);
|
||||
var activePoints = myLineChart.getPointsAtEvent(evt);
|
||||
// => activePoints is an array of points on the canvas that are at the same position as the click event.
|
||||
};
|
||||
```
|
||||
@@ -1,227 +0,0 @@
|
||||
---
|
||||
title: Getting started
|
||||
anchor: getting-started
|
||||
---
|
||||
|
||||
###Scales
|
||||
|
||||
Scales in v2.0 of Chart.js are significantly more powerful, but also different than those of v1.0.
|
||||
- Multiple x & y axes are now supported.
|
||||
- A built-in label auto-skip feature now detects would-be overlapping ticks and labels and removes every nth label to keep things displaying normally.
|
||||
- Scale labels
|
||||
|
||||
Every scale extends a core scale class with the following options:
|
||||
|
||||
```javascript
|
||||
Chart.defaults.scale = {
|
||||
display: true,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// scale label
|
||||
scaleLabel: {
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'normal',
|
||||
|
||||
// actual label
|
||||
labelString: '',
|
||||
|
||||
// display property
|
||||
show: false,
|
||||
},
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
maxRotation: 90,
|
||||
minRotation: 20,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
reverse: false,
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
userCallback: false,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The `userCallback` method may be used for advanced tick customization. The following callback would display every label in scientific notation
|
||||
```javascript
|
||||
{
|
||||
scales: {
|
||||
xAxes: [{
|
||||
ticks: {
|
||||
// Return an empty string to draw the tick line but hide the tick label
|
||||
// Return `null` or `undefined` to hide the tick line entirely
|
||||
userCallback: function(value, index, values) {
|
||||
return value.toExponential();
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Category Scale
|
||||
The category scale will be familiar to those who have used v1.0. Labels are drawn in from the labels array included in the chart data.
|
||||
|
||||
The category scale extends the core scale class with the following tick template:
|
||||
|
||||
```javascript
|
||||
{
|
||||
position: "bottom",
|
||||
}
|
||||
```
|
||||
|
||||
#### Linear Scale
|
||||
The linear scale can be used to display numerical data. It can be placed on either the x or y axis. The scatter chart type automatically configures a line chart to use one of these scales for the x axis.
|
||||
|
||||
The linear scale extends the core scale class with the following tick template:
|
||||
|
||||
```javascript
|
||||
{
|
||||
position: "left",
|
||||
}
|
||||
```
|
||||
|
||||
#### Logarithmic Scale
|
||||
The logarithmic scale is used to display logarithmic data of course. It can be placed on either the x or y axis.
|
||||
|
||||
The log scale extends the core scale class with the following tick template:
|
||||
|
||||
```javascript
|
||||
{
|
||||
position: "left",
|
||||
ticks: {
|
||||
template: "<%var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));if (remain === 1 || remain === 2 || remain === 5) {%><%=value.toExponential()%><%} else {%><%= null %><%}%>",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Time Scale
|
||||
The time scale is used to display times and dates. It can be placed on the x axis. When building its ticks, it will automatically calculate the most comfortable unit base on the size of the scale.
|
||||
|
||||
The time scale extends the core scale class with the following tick template:
|
||||
|
||||
```javascript
|
||||
{
|
||||
position: "bottom",
|
||||
time: {
|
||||
// string/callback - By default, date objects are expected. You may use a pattern string from http://momentjs.com/docs/#/parsing/string-format/ to parse a time string format, or use a callback function that is passed the label, and must return a moment() instance.
|
||||
format: false,
|
||||
// string - By default, unit will automatically be detected. Override with 'week', 'month', 'year', etc. (see supported time measurements)
|
||||
unit: false,
|
||||
// string - By default, no rounding is applied. To round, set to a supported time unit eg. 'week', 'month', 'year', etc.
|
||||
round: false,
|
||||
// string - By default, is set to the detected (or manually overridden) time unit's `display` property (see supported time measurements). To override, use a pattern string from http://momentjs.com/docs/#/displaying/format/
|
||||
displayFormat: false
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The following time measurements are supported:
|
||||
|
||||
```javascript
|
||||
{
|
||||
'millisecond': {
|
||||
display: 'SSS [ms]', // 002 ms
|
||||
maxStep: 1000,
|
||||
},
|
||||
'second': {
|
||||
display: 'h:mm:ss a', // 11:20:01 AM
|
||||
maxStep: 60,
|
||||
},
|
||||
'minute': {
|
||||
display: 'h:mm:ss a', // 11:20:01 AM
|
||||
maxStep: 60,
|
||||
},
|
||||
'hour': {
|
||||
display: 'MMM D, hA', // Sept 4, 5PM
|
||||
maxStep: 24,
|
||||
},
|
||||
'day': {
|
||||
display: 'll', // Sep 4 2015
|
||||
maxStep: 7,
|
||||
},
|
||||
'week': {
|
||||
display: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
|
||||
maxStep: 4.3333,
|
||||
},
|
||||
'month': {
|
||||
display: 'MMM YYYY', // Sept 2015
|
||||
maxStep: 12,
|
||||
},
|
||||
'quarter': {
|
||||
display: '[Q]Q - YYYY', // Q3
|
||||
maxStep: 4,
|
||||
},
|
||||
'year': {
|
||||
display: 'YYYY', // 2015
|
||||
maxStep: false,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Radial Linear Scale
|
||||
The radial linear scale is used specifically for the radar chart type.
|
||||
|
||||
The radial linear scale extends the core scale class with the following tick template:
|
||||
|
||||
```javascript
|
||||
{
|
||||
animate: true,
|
||||
lineArc: false,
|
||||
position: "chartArea",
|
||||
|
||||
angleLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1
|
||||
},
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
showLabelBackdrop: true,
|
||||
|
||||
//String - The colour of the label backdrop
|
||||
backdropColor: "rgba(255,255,255,0.75)",
|
||||
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
backdropPaddingY: 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
backdropPaddingX: 2,
|
||||
},
|
||||
|
||||
pointLabels: {
|
||||
//String - Point label font declaration
|
||||
fontFamily: "'Arial'",
|
||||
|
||||
//String - Point label font weight
|
||||
fontStyle: "normal",
|
||||
|
||||
//Number - Point label font size in pixels
|
||||
fontSize: 10,
|
||||
|
||||
//String - Point label font colour
|
||||
fontColor: "#666",
|
||||
},
|
||||
}
|
||||
```
|
||||
+61
-124
@@ -12,81 +12,53 @@ var gulp = require('gulp'),
|
||||
exec = require('child_process').exec,
|
||||
fs = require('fs'),
|
||||
package = require('./package.json'),
|
||||
bower = require('./bower.json'),
|
||||
karma = require('gulp-karma');
|
||||
bower = require('./bower.json');
|
||||
|
||||
var srcDir = './src/';
|
||||
var testDir = './test/';
|
||||
/*
|
||||
* Usage : gulp build --types=Bar,Line,Doughnut
|
||||
* Output: - A built Chart.js file with Core and types Bar, Line and Doughnut concatenated together
|
||||
* - A minified version of this code, in Chart.min.js
|
||||
* Usage : gulp build --types=Bar,Line,Doughnut
|
||||
* Output: - A built Chart.js file with Core and types Bar, Line and Doughnut concatenated together
|
||||
* - A minified version of this code, in Chart.min.js
|
||||
*/
|
||||
|
||||
var srcFiles = [
|
||||
'./node_modules/color/dist/color.min.js',
|
||||
'./src/core/core.js',
|
||||
'./src/core/core.helpers.js',
|
||||
'./src/core/core.chart.js',
|
||||
'./src/core/core.element.js',
|
||||
'./src/core/**',
|
||||
'./src/controllers/**',
|
||||
'./src/scales/**',
|
||||
'./src/elements/**',
|
||||
'./src/charts/**',
|
||||
];
|
||||
gulp.task('build', function(){
|
||||
|
||||
var testFiles = [
|
||||
'./node_modules/moment/min/moment.min.js',
|
||||
'./test/mockContext.js',
|
||||
'./test/*.js'
|
||||
];
|
||||
|
||||
gulp.task('build', buildTask);
|
||||
gulp.task('coverage', coverageTask);
|
||||
gulp.task('watch', watchTask);
|
||||
gulp.task('bump', bumpTask);
|
||||
gulp.task('release', ['build'], releaseTask);
|
||||
gulp.task('jshint', jshintTask);
|
||||
gulp.task('test', ['jshint', 'validHTML', 'unittest']);
|
||||
gulp.task('size', ['library-size', 'module-sizes']);
|
||||
gulp.task('server', serverTask);
|
||||
gulp.task('validHTML', validHTMLTask);
|
||||
gulp.task('unittest', unittestTask);
|
||||
gulp.task('unittestWatch', unittestWatchTask);
|
||||
gulp.task('library-size', librarySizeTask);
|
||||
gulp.task('module-sizes', moduleSizesTask);
|
||||
gulp.task('_open', _openTask);
|
||||
gulp.task('dev', ['server', 'default']);
|
||||
|
||||
gulp.task('default', ['build', 'watch']);
|
||||
|
||||
|
||||
function buildTask() {
|
||||
|
||||
var isCustom = !!(util.env.types),
|
||||
// Default to all of the chart types, with Chart.Core first
|
||||
var srcFiles = [FileName('Core')],
|
||||
isCustom = !!(util.env.types),
|
||||
outputDir = (isCustom) ? 'custom' : '.';
|
||||
if (isCustom){
|
||||
util.env.types.split(',').forEach(function(type){ return srcFiles.push(FileName(type));});
|
||||
}
|
||||
else{
|
||||
// Seems gulp-concat remove duplicates - nice!
|
||||
// So we can use this to sort out dependency order - aka include Core first!
|
||||
srcFiles.push(srcDir+'*');
|
||||
}
|
||||
srcFiles.push('./node_modules/color/dist/color.min.js');
|
||||
|
||||
return gulp.src(srcFiles)
|
||||
.pipe(concat('Chart.js'))
|
||||
.pipe(replace('{{ version }}', package.version))
|
||||
.pipe(gulp.dest(outputDir))
|
||||
.pipe(uglify({
|
||||
preserveComments: 'some'
|
||||
}))
|
||||
.pipe(uglify({preserveComments:'some'}))
|
||||
.pipe(concat('Chart.min.js'))
|
||||
.pipe(gulp.dest(outputDir));
|
||||
|
||||
}
|
||||
function FileName(moduleName){
|
||||
return srcDir+'Chart.'+moduleName+'.js';
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Usage : gulp bump
|
||||
* Prompts: Version increment to bump
|
||||
* Output: - New version number written into package.json & bower.json
|
||||
* Usage : gulp bump
|
||||
* Prompts: Version increment to bump
|
||||
* Output: - New version number written into package.json & bower.json
|
||||
*/
|
||||
function bumpTask(complete) {
|
||||
|
||||
gulp.task('bump', function(complete){
|
||||
util.log('Current version:', util.colors.cyan(package.version));
|
||||
var choices = ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease'].map(function(versionType) {
|
||||
var choices = ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease'].map(function(versionType){
|
||||
return versionType + ' (v' + semver.inc(package.version, versionType) + ')';
|
||||
});
|
||||
inquirer.prompt({
|
||||
@@ -94,7 +66,7 @@ function bumpTask(complete) {
|
||||
name: 'version',
|
||||
message: 'What version update would you like?',
|
||||
choices: choices
|
||||
}, function(res) {
|
||||
}, function(res){
|
||||
var increment = res.version.split(' ')[0],
|
||||
newVersion = semver.inc(package.version, increment);
|
||||
|
||||
@@ -108,94 +80,59 @@ function bumpTask(complete) {
|
||||
|
||||
complete();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function releaseTask() {
|
||||
gulp.task('release', ['build'], function(){
|
||||
exec('git tag -a v' + package.version);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function jshintTask() {
|
||||
gulp.task('jshint', function(){
|
||||
return gulp.src(srcDir + '*.js')
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter('default'));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function validHTMLTask() {
|
||||
gulp.task('valid', function(){
|
||||
return gulp.src('samples/*.html')
|
||||
.pipe(htmlv());
|
||||
}
|
||||
.pipe(htmlv());
|
||||
});
|
||||
|
||||
|
||||
function unittestTask() {
|
||||
var files = srcFiles.slice();
|
||||
Array.prototype.push.apply(files, testFiles);
|
||||
|
||||
return gulp.src(files)
|
||||
.pipe(karma({
|
||||
configFile: 'karma.conf.js',
|
||||
action: 'run'
|
||||
}));
|
||||
}
|
||||
|
||||
function unittestWatchTask() {
|
||||
var files = srcFiles.slice();
|
||||
Array.prototype.push.apply(files, testFiles);
|
||||
|
||||
return gulp.src(files)
|
||||
.pipe(karma({
|
||||
configFile: 'karma.conf.js',
|
||||
action: 'watch'
|
||||
}));
|
||||
}
|
||||
|
||||
function coverageTask() {
|
||||
var files = srcFiles.slice();
|
||||
Array.prototype.push.apply(files, testFiles);
|
||||
|
||||
return gulp.src(files)
|
||||
.pipe(karma({
|
||||
configFile: 'karma.coverage.conf.js',
|
||||
action: 'run'
|
||||
}));
|
||||
}
|
||||
|
||||
function librarySizeTask() {
|
||||
gulp.task('library-size', function(){
|
||||
return gulp.src('Chart.min.js')
|
||||
.pipe(size({
|
||||
gzip: true
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
function moduleSizesTask() {
|
||||
gulp.task('module-sizes', function(){
|
||||
return gulp.src(srcDir + '*.js')
|
||||
.pipe(uglify({
|
||||
preserveComments: 'some'
|
||||
}))
|
||||
.pipe(size({
|
||||
showFiles: true,
|
||||
gzip: true
|
||||
}));
|
||||
}
|
||||
.pipe(uglify({preserveComments:'some'}))
|
||||
.pipe(size({
|
||||
showFiles: true,
|
||||
gzip: true
|
||||
}));
|
||||
});
|
||||
|
||||
function watchTask() {
|
||||
if (util.env.test) {
|
||||
return gulp.watch('./src/**', ['build', 'unittest', 'unittestWatch']);
|
||||
}
|
||||
return gulp.watch('./src/**', ['build']);
|
||||
}
|
||||
gulp.task('watch', function(){
|
||||
gulp.watch('./src/*', ['build']);
|
||||
});
|
||||
|
||||
function serverTask() {
|
||||
gulp.task('test', ['jshint', 'valid']);
|
||||
|
||||
gulp.task('size', ['library-size', 'module-sizes']);
|
||||
|
||||
gulp.task('default', ['build', 'watch']);
|
||||
|
||||
gulp.task('server', function(){
|
||||
connect.server({
|
||||
port: 8000
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Convenience task for opening the project straight from the command line
|
||||
|
||||
function _openTask() {
|
||||
gulp.task('_open', function(){
|
||||
exec('open http://localhost:8000');
|
||||
exec('subl .');
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('dev', ['server', 'default']);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
frameworks: ['jasmine'],
|
||||
reporters: ['progress', 'html'],
|
||||
});
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
|
||||
coverageReporter: {
|
||||
type: 'html',
|
||||
dir: 'coverage/'
|
||||
},
|
||||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
preprocessors: {
|
||||
'src/**/*.js': ['coverage']
|
||||
},
|
||||
|
||||
reporters: ['progress', 'coverage'],
|
||||
});
|
||||
};
|
||||
+29
-40
@@ -1,42 +1,31 @@
|
||||
{
|
||||
"name": "chart.js",
|
||||
"homepage": "http://www.chartjs.org",
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"version": "2.0.0-alpha",
|
||||
"main": "Chart.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nnnick/Chart.js.git"
|
||||
},
|
||||
"dependences": {},
|
||||
"devDependencies": {
|
||||
"color": "git://github.com/chartjs/color",
|
||||
"gulp": "3.5.x",
|
||||
"gulp-concat": "~2.1.x",
|
||||
"gulp-connect": "~2.0.5",
|
||||
"gulp-html-validator": "^0.0.2",
|
||||
"gulp-jshint": "~1.5.1",
|
||||
"gulp-karma": "0.0.4",
|
||||
"gulp-replace": "^0.4.0",
|
||||
"gulp-size": "~0.4.0",
|
||||
"gulp-uglify": "~0.2.x",
|
||||
"gulp-util": "~2.2.x",
|
||||
"inquirer": "^0.5.1",
|
||||
"jasmine": "^2.3.2",
|
||||
"jasmine-core": "^2.3.4",
|
||||
"jquery": "^2.1.4",
|
||||
"karma": "^0.12.37",
|
||||
"karma-chrome-launcher": "^0.2.0",
|
||||
"karma-coverage": "^0.5.1",
|
||||
"karma-firefox-launcher": "^0.1.6",
|
||||
"karma-jasmine": "^0.3.6",
|
||||
"karma-jasmine-html-reporter": "^0.1.8",
|
||||
"semver": "^3.0.1"
|
||||
},
|
||||
"spm": {
|
||||
"main": "Chart.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"moment": "^2.10.6"
|
||||
}
|
||||
"name": "chart.js",
|
||||
"homepage": "http://www.chartjs.org",
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"version": "2.0.0-alpha",
|
||||
"main": "Chart.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nnnick/Chart.js.git"
|
||||
},
|
||||
"dependences": {},
|
||||
"devDependencies": {
|
||||
"color": "git://github.com/chartjs/color",
|
||||
"gulp": "3.5.x",
|
||||
"gulp-concat": "~2.1.x",
|
||||
"gulp-connect": "~2.0.5",
|
||||
"gulp-html-validator": "^0.0.2",
|
||||
"gulp-jshint": "~1.5.1",
|
||||
"gulp-replace": "^0.4.0",
|
||||
"gulp-size": "~0.4.0",
|
||||
"gulp-uglify": "~0.2.x",
|
||||
"gulp-util": "~2.2.x",
|
||||
"inquirer": "^0.5.1",
|
||||
"jquery": "^2.1.4",
|
||||
"onecolor": "^2.5.0",
|
||||
"semver": "^3.0.1"
|
||||
},
|
||||
"spm": {
|
||||
"main": "Chart.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = Chart.Bar(ctx, {
|
||||
window.myBar = new Chart(ctx).Bar({
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
@@ -54,17 +54,66 @@
|
||||
stacked: false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}, {
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
id: "y-axis-2",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
drawOnChartArea: false
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||
drawTicks: false,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Stacked Bar Chart</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
stacked: true,
|
||||
}],
|
||||
yAxes: [{
|
||||
stacked: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
});
|
||||
window.myBar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+37
-120
@@ -5,139 +5,56 @@
|
||||
<title>Bar Chart</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
<style type="text/css">
|
||||
canvas {
|
||||
border: 1px solid red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container" style="width: 50%; height: 25%;">
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<button id="show">Show</button>
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function() {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
};
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myBar.generateLegend());
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
}
|
||||
});
|
||||
|
||||
updateLegend();
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
var zero = Math.random() < 0.2 ? true : false;
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor = randomColor();
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return zero ? 0.0 : randomScalingFactor();
|
||||
});
|
||||
|
||||
});
|
||||
window.myBar.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + barChartData.datasets.length,
|
||||
backgroundColor: randomColor(),
|
||||
data: []
|
||||
};
|
||||
|
||||
for (var index = 0; index < barChartData.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
barChartData.datasets.push(newDataset);
|
||||
window.myBar.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (barChartData.datasets.length > 0) {
|
||||
barChartData.labels.push('data #' + barChartData.labels.length);
|
||||
|
||||
for (var index = 0; index < barChartData.datasets.length; ++index) {
|
||||
//window.myBar.addData(randomScalingFactor(), index);
|
||||
barChartData.datasets[index].data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
window.myBar.update();
|
||||
updateLegend();
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx).Bar({
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
barChartData.datasets.splice(0, 1);
|
||||
window.myBar.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
barChartData.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
barChartData.datasets.forEach(function(dataset, datasetIndex) {
|
||||
dataset.data.pop();
|
||||
});
|
||||
|
||||
window.myBar.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#show').click(function() {
|
||||
document.getElementById('container').style.display = '';
|
||||
});
|
||||
window.myBar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Bar Chart</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
type: 'bar',
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
borderColor: 'white',
|
||||
borderWidth: 2
|
||||
}, {
|
||||
type: 'bar',
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
borderColor: 'white',
|
||||
borderWidth: 2
|
||||
}, {
|
||||
type: 'line',
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, ]
|
||||
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(barChartData.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
});
|
||||
window.myBar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,191 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<script src="../node_modules/moment/min/moment.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:100%;">
|
||||
<canvas id="canvas" style="width:100%;height:100%"></canvas>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
var newDate = function(days) {
|
||||
var date = new Date();
|
||||
return date.setDate(date.getDate() + days);
|
||||
};
|
||||
var newTimestamp = function(days) {
|
||||
return Date.now() - days * 100000;
|
||||
};
|
||||
|
||||
var config = {
|
||||
type: 'bar',
|
||||
data: {
|
||||
//labels: [newTimestamp(0), newTimestamp(1), newTimestamp(2), newTimestamp(3), newTimestamp(4), newTimestamp(5), newTimestamp(6)], // unix timestamps
|
||||
// labels: [newDate(0), newDate(1), newDate(2), newDate(3), newDate(4), newDate(5), newDate(6)], // Date Objects
|
||||
labels: ["01/01/2015 20:00", "01/02/2015 20:00", "01/03/2015 22:00", "01/05/2015 23:00", "01/07/2015 03:00", "01/08/2015 10:00", "01/10/2015"], // Hours
|
||||
// labels: ["01/01/2015", "01/02/2015", "01/03/2015", "01/06/2015", "01/15/2015", "01/17/2015", "01/30/2015"], // Days
|
||||
// labels: ["12/25/2014", "01/08/2015", "01/15/2015", "01/22/2015", "01/29/2015", "02/05/2015", "02/12/2015"], // Weeks
|
||||
datasets: [{
|
||||
type: 'bar',
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
borderColor: 'white',
|
||||
borderWidth: 2
|
||||
}, {
|
||||
type: 'bar',
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
borderColor: 'white',
|
||||
borderWidth: 2
|
||||
}, {
|
||||
type: 'line',
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, ]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
display: true,
|
||||
time: {
|
||||
format: 'MM/DD/YYYY HH:mm',
|
||||
// round: 'day'
|
||||
}
|
||||
}, ],
|
||||
yAxes: [{
|
||||
display: true
|
||||
}]
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0.3
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx, config);
|
||||
|
||||
updateLegend();
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myLine.generateLegend());
|
||||
}
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + config.data.datasets.length,
|
||||
borderColor: randomColor(0.4),
|
||||
backgroundColor: randomColor(0.5),
|
||||
pointBorderColor: randomColor(0.7),
|
||||
pointBackgroundColor: randomColor(0.5),
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
};
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
config.data.datasets.push(newDataset);
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push(
|
||||
myLine.scales['x-axis-0'].labelMoments[myLine.scales['x-axis-0'].labelMoments.length - 1].add(1, 'day')
|
||||
.format('MM/DD/YYYY')
|
||||
);
|
||||
|
||||
for (var index = 0; index < config.data.datasets.length; ++index) {
|
||||
config.data.datasets[index].data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
config.data.datasets.splice(0, 1);
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
config.data.datasets[datasetIndex].data.pop();
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,63 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Doughnut Chart</title>
|
||||
<script src="../src/Chart.Core.js"></script>
|
||||
<script src="../src/Chart.Doughnut.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#canvas-holder{
|
||||
width:30%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area" width="500" height="500"/>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
var doughnutData = [
|
||||
{
|
||||
value: 1,
|
||||
label: "One"
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: "Two"
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: "Three"
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: "Four"
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: "Five"
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myDoughnut = new Chart(ctx).Doughnut({
|
||||
data: doughnutData,
|
||||
options: {
|
||||
responsive : true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+5
-81
@@ -18,20 +18,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="canvas-holder" style="width:50%">
|
||||
<div id="canvas-holder" style="width:100%">
|
||||
<canvas id="chart-area" width="500" height="500" />
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
@@ -39,9 +29,6 @@
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var config = {
|
||||
data: {
|
||||
@@ -104,81 +91,18 @@
|
||||
}
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myDoughnut.generateLegend());
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myDoughnut = Chart.Doughnut(ctx, config);
|
||||
console.log(window.myDoughnut);
|
||||
|
||||
updateLegend();
|
||||
window.myDoughnut = new Chart(ctx).Doughnut(config);
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
|
||||
dataset.backgroundColor = dataset.backgroundColor.map(function() {
|
||||
return randomColor(0.7);
|
||||
$.each(config.data.datasets, function(i, piece) {
|
||||
$.each(piece.data, function(j, value) {
|
||||
config.data.datasets[i].data[j] = randomScalingFactor();
|
||||
});
|
||||
});
|
||||
|
||||
window.myDoughnut.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
backgroundColor: [],
|
||||
data: [],
|
||||
};
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
newDataset.backgroundColor.push(randomColor(0.7));
|
||||
}
|
||||
|
||||
config.data.datasets.push(newDataset);
|
||||
window.myDoughnut.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push('data #' + config.data.labels.length);
|
||||
|
||||
$.each(config.data.datasets, function(index, dataset) {
|
||||
dataset.data.push(randomScalingFactor());
|
||||
dataset.backgroundColor.push(randomColor(0.7));
|
||||
});
|
||||
|
||||
window.myDoughnut.update();
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
config.data.datasets.splice(0, 1);
|
||||
window.myDoughnut.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
dataset.data.pop();
|
||||
dataset.backgroundColor.pop();
|
||||
});
|
||||
|
||||
window.myDoughnut.update();
|
||||
updateLegend();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -5,22 +5,22 @@
|
||||
<title>Line Chart with Custom Tooltips</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
|
||||
<style>
|
||||
#canvas-holder1 {
|
||||
width: 300px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#canvas-holder2 {
|
||||
width: 50%;
|
||||
margin: 20px 25%;
|
||||
}
|
||||
|
||||
#chartjs-tooltip {
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
background: rgba(0, 0, 0, .7);
|
||||
color: white;
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
-webkit-transition: all .1s ease;
|
||||
transition: all .1s ease;
|
||||
@@ -28,12 +28,11 @@
|
||||
-webkit-transform: translate(-50%, 0);
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
.chartjs-tooltip-key {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
.chartjs-tooltip-key{
|
||||
display:inline-block;
|
||||
width:10px;
|
||||
height:10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -41,80 +40,48 @@
|
||||
<div id="canvas-holder1">
|
||||
<canvas id="chart1" width="300" height="30" />
|
||||
</div>
|
||||
<div id="canvas-holder2">
|
||||
<canvas id="chart2" width="450" height="600" />
|
||||
</div>
|
||||
|
||||
<div id="chartjs-tooltip"></div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
Chart.defaults.global.pointHitDetectionRadius = 1;
|
||||
var customTooltips = function(tooltip) {
|
||||
Chart.defaults.global.customTooltips = function(tooltip) {
|
||||
|
||||
console.log(tooltip._view);
|
||||
|
||||
// Tooltip Element
|
||||
var tooltipEl = $('#chartjs-tooltip');
|
||||
|
||||
if (!tooltipEl[0]) {
|
||||
$('body').append('<div id="chartjs-tooltip"></div>');
|
||||
tooltipEl = $('#chartjs-tooltip');
|
||||
}
|
||||
|
||||
// Hide if no tooltip
|
||||
if (!tooltip._view.opacity) {
|
||||
if (!tooltip) {
|
||||
tooltipEl.css({
|
||||
opacity: 0
|
||||
});
|
||||
$('.chartjs-wrap canvas')
|
||||
.each(function(index, el) {
|
||||
$(el).css('cursor', 'default');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$(tooltip._chart.canvas).css('cursor', 'pointer');
|
||||
tooltipEl.removeClass('above below');
|
||||
tooltipEl.addClass(tooltip.yAlign);
|
||||
|
||||
// Set caret Position
|
||||
tooltipEl.removeClass('above below no-transform');
|
||||
if (tooltip._view.yAlign) {
|
||||
tooltipEl.addClass(tooltip._view.yAlign);
|
||||
} else {
|
||||
tooltipEl.addClass('no-transform');
|
||||
var innerHtml = '';
|
||||
for (var i = tooltip.labels.length - 1; i >= 0; i--) {
|
||||
innerHtml += [
|
||||
'<div class="chartjs-tooltip-section">',
|
||||
' <span class="chartjs-tooltip-key" style="background-color:' + tooltip.legendColors[i].fill + '"></span>',
|
||||
' <span class="chartjs-tooltip-value">' + tooltip.labels[i] + '</span>',
|
||||
'</div>'
|
||||
].join('');
|
||||
}
|
||||
tooltipEl.html(innerHtml);
|
||||
|
||||
// Set Text
|
||||
if (tooltip._view.text) {
|
||||
tooltipEl.html(tooltip._view.text);
|
||||
} else if (tooltip._view.labels) {
|
||||
var innerHtml = '<div class="title">' + tooltip._view.title + '</div>';
|
||||
for (var i = 0; i < tooltip._view.labels.length; i++) {
|
||||
innerHtml += [
|
||||
'<div class="section">',
|
||||
' <span class="key" style="background-color:' + tooltip._view.legendColors[i].fill + '"></span>',
|
||||
' <span class="value">' + tooltip._view.labels[i] + '</span>',
|
||||
'</div>'
|
||||
].join('');
|
||||
}
|
||||
tooltipEl.html(innerHtml);
|
||||
}
|
||||
|
||||
// Find Y Location on page
|
||||
var top = 0;
|
||||
if (tooltip._view.yAlign) {
|
||||
if (tooltip._view.yAlign == 'above') {
|
||||
top = tooltip._view.y - tooltip._view.caretHeight - tooltip._view.caretPadding;
|
||||
} else {
|
||||
top = tooltip._view.y + tooltip._view.caretHeight + tooltip._view.caretPadding;
|
||||
}
|
||||
}
|
||||
|
||||
var offset = $(tooltip._chart.canvas).offset();
|
||||
|
||||
// Display, position, and set styles for font
|
||||
tooltipEl.css({
|
||||
opacity: 1,
|
||||
width: tooltip._view.width ? (tooltip._view.width + 'px') : 'auto',
|
||||
left: offset.left + tooltip._view.x + 'px',
|
||||
top: offset.top + top + 'px',
|
||||
fontFamily: tooltip._view._fontFamily,
|
||||
fontSize: tooltip._view.fontSize,
|
||||
fontStyle: tooltip._view._fontStyle,
|
||||
padding: tooltip._view.yPadding + 'px ' + tooltip._view.xPadding + 'px',
|
||||
left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
|
||||
top: tooltip.chart.canvas.offsetTop + tooltip.y + 'px',
|
||||
fontFamily: tooltip.fontFamily,
|
||||
fontSize: tooltip.fontSize,
|
||||
fontStyle: tooltip.fontStyle,
|
||||
});
|
||||
};
|
||||
var randomScalingFactor = function() {
|
||||
@@ -124,25 +91,40 @@
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
fillColor: "rgba(220,220,220,0.2)",
|
||||
strokeColor: "rgba(220,220,220,1)",
|
||||
pointColor: "rgba(220,220,220,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(220,220,220,1)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
fillColor: "rgba(151,187,205,0.2)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
var chartEl = document.getElementById("chart1");
|
||||
window.myLine = new Chart(chartEl, {
|
||||
type: 'line',
|
||||
data: lineChartData,
|
||||
var ctx1 = document.getElementById("chart1").getContext("2d");
|
||||
window.myLine = new Chart(ctx1).Line({
|
||||
data: lineChartData,
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: false,
|
||||
custom: customTooltips
|
||||
}
|
||||
showScale: false,
|
||||
pointDot : true,
|
||||
responsive: true
|
||||
}
|
||||
});
|
||||
|
||||
var ctx2 = document.getElementById("chart2").getContext("2d");
|
||||
window.myLine = new Chart(ctx2).Line(lineChartData, {
|
||||
responsive: true
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%;">
|
||||
<canvas id="canvas" style="width:100%;height:100%"></canvas>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.ceil(Math.random() * 10.0) * Math.pow(10, Math.ceil(Math.random() * 5));
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
borderDash: [5, 5],
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'x axis'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
type: 'logarithmic',
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'y axis'
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx, config);
|
||||
|
||||
updateLegend();
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myLine.generateLegend());
|
||||
}
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + config.data.datasets.length,
|
||||
borderColor: randomColor(0.4),
|
||||
backgroundColor: randomColor(0.5),
|
||||
pointBorderColor: randomColor(0.7),
|
||||
pointBackgroundColor: randomColor(0.5),
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
};
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
config.data.datasets.push(newDataset);
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push('dataset #' + config.data.labels.length);
|
||||
|
||||
for (var index = 0; index < config.data.datasets.length; ++index) {
|
||||
config.data.datasets[index].data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
config.data.datasets.splice(0, 1);
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
dataset.data.pop();
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -8,8 +8,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%;">
|
||||
<canvas id="canvas" style="width:100%;height:100%"></canvas>
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
@@ -45,34 +47,80 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = Chart.Line(ctx, {
|
||||
data: lineChartData,
|
||||
window.myLine = new Chart(ctx).Line({
|
||||
data: lineChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
stacked: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
gridLines: {
|
||||
offsetGridLines: false
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}, {
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
id: "y-axis-2",
|
||||
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||
drawTicks: false,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<script src="../node_modules/moment/min/moment.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:100%;">
|
||||
<canvas id="canvas" style="width:100%;height:100%"></canvas>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
var newDate = function(days) {
|
||||
var date = new Date();
|
||||
return date.setDate(date.getDate() + days);
|
||||
};
|
||||
var newTimestamp = function(days) {
|
||||
return Date.now() - days * 100000;
|
||||
};
|
||||
|
||||
var config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
//labels: [newTimestamp(0), newTimestamp(1), newTimestamp(2), newTimestamp(3), newTimestamp(4), newTimestamp(5), newTimestamp(6)], // unix timestamps
|
||||
// labels: [newDate(0), newDate(1), newDate(2), newDate(3), newDate(4), newDate(5), newDate(6)], // Date Objects
|
||||
labels: ["01/01/2015 20:00", "01/02/2015 21:00", "01/03/2015 22:00", "01/05/2015 23:00", "01/07/2015 03:00", "01/08/2015 10:00", "01/10/2015"], // Hours
|
||||
// labels: ["01/01/2015", "01/02/2015", "01/03/2015", "01/06/2015", "01/15/2015", "01/17/2015", "01/30/2015"], // Days
|
||||
// labels: ["12/25/2014", "01/08/2015", "01/15/2015", "01/22/2015", "01/29/2015", "02/05/2015", "02/12/2015"], // Weeks
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
borderDash: [5, 5],
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
display: true,
|
||||
time: {
|
||||
format: 'MM/DD/YYYY HH:mm',
|
||||
// round: 'day'
|
||||
},
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'Date'
|
||||
}
|
||||
}, ],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'value'
|
||||
}
|
||||
}]
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0.3
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx, config);
|
||||
|
||||
updateLegend();
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myLine.generateLegend());
|
||||
}
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + config.data.datasets.length,
|
||||
borderColor: randomColor(0.4),
|
||||
backgroundColor: randomColor(0.5),
|
||||
pointBorderColor: randomColor(0.7),
|
||||
pointBackgroundColor: randomColor(0.5),
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
};
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
config.data.datasets.push(newDataset);
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push(
|
||||
myLine.scales['x-axis-0'].labelMoments[myLine.scales['x-axis-0'].labelMoments.length - 1]
|
||||
.clone()
|
||||
.add(1, 'day')
|
||||
.format('MM/DD/YYYY HH:mm')
|
||||
);
|
||||
|
||||
for (var index = 0; index < config.data.datasets.length; ++index) {
|
||||
config.data.datasets[index].data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
config.data.datasets.splice(0, 1);
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
dataset.data.pop();
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Chart with xAxis Filtering</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%;">
|
||||
<canvas id="canvas" style="width:100%;height:100%"></canvas>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 50 * (Math.random() > 0.5 ? 1 : 1)) + 50;
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ["January is a long month", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
borderDash: [5, 5],
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
ticks: {
|
||||
callback: function(dataLabel, index) {
|
||||
return dataLabel;
|
||||
}
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
beginAtZero: false
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx, config);
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + config.data.datasets.length,
|
||||
borderColor: randomColor(0.4),
|
||||
backgroundColor: randomColor(0.5),
|
||||
pointBorderColor: randomColor(0.7),
|
||||
pointBackgroundColor: randomColor(0.5),
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
};
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
config.data.datasets.push(newDataset);
|
||||
window.myLine.update();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push('dataset #' + config.data.labels.length);
|
||||
|
||||
for (var index = 0; index < config.data.datasets.length; ++index) {
|
||||
config.data.datasets[index].data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
window.myLine.update();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
config.data.datasets.splice(0, 1);
|
||||
window.myLine.update();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
dataset.data.pop();
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+42
-139
@@ -5,159 +5,62 @@
|
||||
<title>Line Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%;">
|
||||
<canvas id="canvas" style="width:100%;height:100%"></canvas>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
borderDash: [5, 5],
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'Month'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'Value'
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx, config);
|
||||
|
||||
updateLegend();
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myLine.generateLegend());
|
||||
var config = {
|
||||
data: {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
});
|
||||
console.log(config.data);
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx).Line(config);
|
||||
};
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + config.data.datasets.length,
|
||||
borderColor: randomColor(0.4),
|
||||
backgroundColor: randomColor(0.5),
|
||||
pointBorderColor: randomColor(0.7),
|
||||
pointBackgroundColor: randomColor(0.5),
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
};
|
||||
$('#randomizeData').click(function() {
|
||||
config.data.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
config.data.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
config.data.datasets.push(newDataset);
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push('dataset #' + config.data.labels.length);
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data.push(randomScalingFactor());
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
config.data.datasets.splice(0, 1);
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
dataset.data.pop();
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
window.myLine.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Pie Chart with Custom Tooltips</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
|
||||
<style>
|
||||
#canvas-holder {
|
||||
width: 100%;
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
#chartjs-tooltip {
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
background: rgba(0, 0, 0, .7);
|
||||
color: white;
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
-webkit-transition: all .1s ease;
|
||||
transition: all .1s ease;
|
||||
pointer-events: none;
|
||||
-webkit-transform: translate(-50%, 0);
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
#chartjs-tooltip.below {
|
||||
-webkit-transform: translate(-50%, 0);
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
#chartjs-tooltip.below:before {
|
||||
border: solid;
|
||||
border-color: #111 transparent;
|
||||
border-color: rgba(0, 0, 0, .8) transparent;
|
||||
border-width: 0 8px 8px 8px;
|
||||
bottom: 1em;
|
||||
content: "";
|
||||
display: block;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
-webkit-transform: translate(-50%, -100%);
|
||||
transform: translate(-50%, -100%);
|
||||
}
|
||||
#chartjs-tooltip.above {
|
||||
-webkit-transform: translate(-50%, -100%);
|
||||
transform: translate(-50%, -100%);
|
||||
}
|
||||
#chartjs-tooltip.above:before {
|
||||
border: solid;
|
||||
border-color: #111 transparent;
|
||||
border-color: rgba(0, 0, 0, .8) transparent;
|
||||
border-width: 8px 8px 0 8px;
|
||||
bottom: 1em;
|
||||
content: "";
|
||||
display: block;
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
-webkit-transform: translate(-50%, 0);
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area1" width="50" height="50" />
|
||||
</div>
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area2" width="300" height="300" />
|
||||
</div>
|
||||
|
||||
<div id="chartjs-tooltip"></div>
|
||||
|
||||
|
||||
<script>
|
||||
Chart.defaults.global.customTooltips = function(tooltip) {
|
||||
|
||||
// Tooltip Element
|
||||
var tooltipEl = $('#chartjs-tooltip');
|
||||
|
||||
// Hide if no tooltip
|
||||
if (!tooltip) {
|
||||
tooltipEl.css({
|
||||
opacity: 0
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Set caret Position
|
||||
tooltipEl.removeClass('above below');
|
||||
tooltipEl.addClass(tooltip.yAlign);
|
||||
|
||||
// Set Text
|
||||
tooltipEl.html(tooltip.text);
|
||||
|
||||
// Find Y Location on page
|
||||
var top;
|
||||
if (tooltip.yAlign == 'above') {
|
||||
top = tooltip.y - tooltip.caretHeight - tooltip.caretPadding;
|
||||
} else {
|
||||
top = tooltip.y + tooltip.caretHeight + tooltip.caretPadding;
|
||||
}
|
||||
|
||||
// Display, position, and set styles for font
|
||||
tooltipEl.css({
|
||||
opacity: 1,
|
||||
left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
|
||||
top: tooltip.chart.canvas.offsetTop + top + 'px',
|
||||
fontFamily: tooltip.fontFamily,
|
||||
fontSize: tooltip.fontSize,
|
||||
fontStyle: tooltip.fontStyle,
|
||||
});
|
||||
};
|
||||
|
||||
var pieData = [{
|
||||
value: 300,
|
||||
color: "#F7464A",
|
||||
highlight: "#FF5A5E",
|
||||
label: "Red"
|
||||
}, {
|
||||
value: 50,
|
||||
color: "#46BFBD",
|
||||
highlight: "#5AD3D1",
|
||||
label: "Green"
|
||||
}, {
|
||||
value: 100,
|
||||
color: "#FDB45C",
|
||||
highlight: "#FFC870",
|
||||
label: "Yellow"
|
||||
}, {
|
||||
value: 40,
|
||||
color: "#949FB1",
|
||||
highlight: "#A8B3C5",
|
||||
label: "Grey"
|
||||
}, {
|
||||
value: 120,
|
||||
color: "#4D5360",
|
||||
highlight: "#616774",
|
||||
label: "Dark Grey"
|
||||
}];
|
||||
|
||||
window.onload = function() {
|
||||
var ctx1 = document.getElementById("chart-area1").getContext("2d");
|
||||
window.myPie = new Chart(ctx1).Pie({
|
||||
data: pieData
|
||||
});
|
||||
|
||||
var ctx2 = document.getElementById("chart-area2").getContext("2d");
|
||||
window.myPie = new Chart(ctx2).Pie({
|
||||
data: pieData
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+2
-23
@@ -8,12 +8,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="canvas-holder" style="width:50%">
|
||||
<div id="canvas-holder">
|
||||
<canvas id="chart-area" width="300" height="300" />
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
@@ -21,12 +19,8 @@
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var config = {
|
||||
type: 'pie',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [
|
||||
@@ -89,7 +83,7 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPie = new Chart(ctx, config);
|
||||
window.myPie = new Chart(ctx).Pie(config);
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
@@ -101,21 +95,6 @@
|
||||
});
|
||||
window.myPie.update();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
backgroundColor: [randomColor(0.7), randomColor(0.7), randomColor(0.7), randomColor(0.7), randomColor(0.7)],
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
};
|
||||
|
||||
config.data.datasets.push(newDataset);
|
||||
window.myPie.update();
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
config.data.datasets.splice(0, 1);
|
||||
window.myPie.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
+13
-62
@@ -8,18 +8,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="canvas-holder" style="width:50%" width="50%">
|
||||
<canvas id="chart-area"></canvas>
|
||||
<div id="canvas-holder" style="width:100%">
|
||||
<canvas id="chart-area" width="300" height="300" />
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
@@ -27,9 +19,6 @@
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var config = {
|
||||
data: {
|
||||
@@ -48,71 +37,33 @@
|
||||
"#949FB1",
|
||||
"#4D5360",
|
||||
],
|
||||
labels: [
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Grey",
|
||||
"Dark Grey"
|
||||
]
|
||||
}],
|
||||
labels: [
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Grey",
|
||||
"Dark Grey"
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scale: {
|
||||
beginAtZero: true,
|
||||
reverse: false
|
||||
}
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area");
|
||||
window.myPolarArea = Chart.PolarArea(ctx, config);
|
||||
updateLegend();
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPolarArea = new Chart(ctx).PolarArea(config);
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myPolarArea.generateLegend());
|
||||
}
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, piece) {
|
||||
$.each(piece.data, function(j, value) {
|
||||
config.data.datasets[i].data[j] = randomScalingFactor();
|
||||
config.data.datasets[i].backgroundColor[j] = randomColor();
|
||||
//config.data.datasets.backgroundColor[i] = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
});
|
||||
});
|
||||
window.myPolarArea.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push('dataset #' + config.data.labels.length);
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor.push(randomColor());
|
||||
dataset.data.push(randomScalingFactor());
|
||||
});
|
||||
|
||||
window.myPolarArea.update();
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.pop(); // remove the label first
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.backgroundColor.pop();
|
||||
dataset.data.pop();
|
||||
});
|
||||
|
||||
window.myPolarArea.update();
|
||||
updateLegend();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
+7
-83
@@ -8,20 +8,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%">
|
||||
<div style="width:100%">
|
||||
<canvas id="canvas" height="450" width="450"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<button id="addDataset">Add Dataset</button>
|
||||
<button id="removeDataset">Remove Dataset</button>
|
||||
<button id="addData">Add Data</button>
|
||||
<button id="removeData">Remove Data</button>
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
@@ -29,12 +19,8 @@
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var config = {
|
||||
type: 'radar',
|
||||
data: {
|
||||
labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"],
|
||||
datasets: [{
|
||||
@@ -52,84 +38,22 @@
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scale: {
|
||||
beginAtZero: true,
|
||||
reverse: false
|
||||
}
|
||||
responsive: true
|
||||
}
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myRadar.generateLegend());
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
window.myRadar = new Chart(document.getElementById("canvas"), config);
|
||||
updateLegend();
|
||||
window.myRadar = new Chart(document.getElementById("canvas").getContext("2d")).Radar(config);
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
config.data.datasets[0].backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.3)';
|
||||
config.data.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
});
|
||||
config.data.datasets[1].backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.3)';
|
||||
config.data.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
window.myRadar.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + config.data.datasets.length,
|
||||
borderColor: randomColor(0.4),
|
||||
backgroundColor: randomColor(0.5),
|
||||
pointBorderColor: randomColor(0.7),
|
||||
pointBackgroundColor: randomColor(0.5),
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
};
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
config.data.datasets.push(newDataset);
|
||||
window.myRadar.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push('dataset #' + config.data.labels.length);
|
||||
|
||||
$.each(config.data.datasets, function (i, dataset) {
|
||||
dataset.data.push(randomScalingFactor());
|
||||
});
|
||||
|
||||
window.myRadar.update();
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
config.data.datasets.splice(0, 1);
|
||||
window.myRadar.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.pop(); // remove the label first
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data.pop();
|
||||
});
|
||||
|
||||
window.myRadar.update();
|
||||
updateLegend();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Scatter Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600" style="border: 1px solid red;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 10.0) * Math.pow(10, Math.ceil(Math.random() * 5));
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var scatterChartData = {
|
||||
datasets: [{
|
||||
label: "V(node2)",
|
||||
data: [{
|
||||
x: 1,
|
||||
y: -1.711e-2,
|
||||
}, {
|
||||
x: 1.26,
|
||||
y: -2.708e-2,
|
||||
}, {
|
||||
x: 1.58,
|
||||
y: -4.285e-2,
|
||||
}, {
|
||||
x: 2.0,
|
||||
y: -6.772e-2,
|
||||
}, {
|
||||
x: 2.51,
|
||||
y: -1.068e-1,
|
||||
}, {
|
||||
x: 3.16,
|
||||
y: -1.681e-1,
|
||||
}, {
|
||||
x: 3.98,
|
||||
y: -2.635e-1,
|
||||
}, {
|
||||
x: 5.01,
|
||||
y: -4.106e-1,
|
||||
}, {
|
||||
x: 6.31,
|
||||
y: -6.339e-1,
|
||||
}, {
|
||||
x: 7.94,
|
||||
y: -9.659e-1,
|
||||
}, {
|
||||
x: 10.00,
|
||||
y: -1.445,
|
||||
}, {
|
||||
x: 12.6,
|
||||
y: -2.110,
|
||||
}, {
|
||||
x: 15.8,
|
||||
y: -2.992,
|
||||
}, {
|
||||
x: 20.0,
|
||||
y: -4.102,
|
||||
}, {
|
||||
x: 25.1,
|
||||
y: -5.429,
|
||||
}, {
|
||||
x: 31.6,
|
||||
y: -6.944,
|
||||
}, {
|
||||
x: 39.8,
|
||||
y: -8.607,
|
||||
}, {
|
||||
x: 50.1,
|
||||
y: -1.038e1,
|
||||
}, {
|
||||
x: 63.1,
|
||||
y: -1.223e1,
|
||||
}, {
|
||||
x: 79.4,
|
||||
y: -1.413e1,
|
||||
}, {
|
||||
x: 100.00,
|
||||
y: -1.607e1,
|
||||
}, {
|
||||
x: 126,
|
||||
y: -1.803e1,
|
||||
}, {
|
||||
x: 158,
|
||||
y: -2e1,
|
||||
}, {
|
||||
x: 200,
|
||||
y: -2.199e1,
|
||||
}, {
|
||||
x: 251,
|
||||
y: -2.398e1,
|
||||
}, {
|
||||
x: 316,
|
||||
y: -2.597e1,
|
||||
}, {
|
||||
x: 398,
|
||||
y: -2.797e1,
|
||||
}, {
|
||||
x: 501,
|
||||
y: -2.996e1,
|
||||
}, {
|
||||
x: 631,
|
||||
y: -3.196e1,
|
||||
}, {
|
||||
x: 794,
|
||||
y: -3.396e1,
|
||||
}, {
|
||||
x: 1000,
|
||||
y: -3.596e1,
|
||||
},]
|
||||
}]
|
||||
};
|
||||
|
||||
$.each(scatterChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myScatter = Chart.Scatter(ctx, {
|
||||
data: scatterChartData,
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'logarithmic',
|
||||
position: 'bottom',
|
||||
ticks: {
|
||||
userCallback: function(tick) {
|
||||
var remain = tick / (Math.pow(10, Math.floor(Chart.helpers.log10(tick))));
|
||||
if (remain === 1 || remain === 2 || remain === 5) {
|
||||
return tick.toString() + "Hz";
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
scaleLabel: {
|
||||
labelString: 'Frequency',
|
||||
show: true,
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
type: 'linear',
|
||||
ticks: {
|
||||
userCallback: function(tick) {
|
||||
return tick.toString() + "dB";
|
||||
}
|
||||
},
|
||||
scaleLabel: {
|
||||
labelString: 'Voltage',
|
||||
show: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myScatter = Chart.Scatter(ctx, {
|
||||
window.myScatter = new Chart(ctx).Scatter({
|
||||
data: scatterChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
@@ -103,21 +103,67 @@
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}, {
|
||||
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
reverse: true,
|
||||
id: "y-axis-2",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||
drawTicks: false,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
+130
-143
@@ -15,153 +15,140 @@
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var scatterChartData = {
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}]
|
||||
};
|
||||
var scatterChartData = {
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
$.each(scatterChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
$.each(scatterChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(scatterChartData);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myScatter = new Chart(ctx).Scatter({
|
||||
data: scatterChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'single', // should always use single for a scatter chart
|
||||
scales: {
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
zeroLineColor: "rgba(0,0,0,1)"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
console.log(scatterChartData);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myScatter = Chart.Scatter(ctx, {
|
||||
data: scatterChartData,
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
position: 'top',
|
||||
gridLines: {
|
||||
zeroLineColor: "rgba(0,255,0,1)"
|
||||
},
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'x axis'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
position: 'right',
|
||||
gridLines: {
|
||||
zeroLineColor: "rgba(0,255,0,1)"
|
||||
},
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'y axis'
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
scatterChartData.datasets[0].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}];
|
||||
scatterChartData.datasets[1].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
window.myScatter.update();
|
||||
});
|
||||
$('#randomizeData').click(function() {
|
||||
scatterChartData.datasets[0].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}];
|
||||
scatterChartData.datasets[1].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
window.myScatter.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -0,0 +1,561 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
stacked: false,
|
||||
|
||||
hover: {
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
display: true,
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
offsetGridLines: true,
|
||||
},
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
name: "Bar",
|
||||
defaults: defaultConfig,
|
||||
initialize: function() {
|
||||
|
||||
var _this = this;
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
//Create a new bar for each piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Rectangle({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
}));
|
||||
}, this);
|
||||
|
||||
// The bar chart only supports a single x axis because the x axis is always a dataset axis
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
|
||||
if (!dataset.yAxisID) {
|
||||
dataset.yAxisID = this.options.scales.yAxes[0].id;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Need to fit scales before we reset elements.
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// So that we animate from the baseline
|
||||
this.resetElements();
|
||||
|
||||
// Update the chart with the latest data.
|
||||
this.update();
|
||||
},
|
||||
resetElements: function() {
|
||||
// Update the points
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
var yScalePoint;
|
||||
|
||||
if (yScale.min < 0 && yScale.max <0) {
|
||||
// all less than 0. use the top
|
||||
yScalePoint = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
yScalePoint = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
yScalePoint = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
helpers.extend(bar, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: yScalePoint,
|
||||
|
||||
// Appearance
|
||||
base: yScale.calculateBarBase(datasetIndex, index),
|
||||
width: xScale.calculateBarWidth(this.data.datasets.length),
|
||||
backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.bar.backgroundColor),
|
||||
borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.bar.borderColor),
|
||||
borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.bar.borderWidth),
|
||||
|
||||
// Tooltip
|
||||
label: this.data.labels[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
},
|
||||
});
|
||||
bar.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
// Update the scale sizes
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(bar, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: yScale.calculateBarY(datasetIndex, index),
|
||||
|
||||
// Appearance
|
||||
base: yScale.calculateBarBase(datasetIndex, index),
|
||||
width: xScale.calculateBarWidth(this.data.datasets.length),
|
||||
backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.bar.backgroundColor),
|
||||
borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.bar.borderColor),
|
||||
borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.bar.borderWidth),
|
||||
|
||||
// Tooltip
|
||||
label: this.data.labels[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
},
|
||||
});
|
||||
bar.pivot();
|
||||
}, this);
|
||||
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function(labels) {
|
||||
var self = this;
|
||||
|
||||
// Function to determine the range of all the
|
||||
var calculateYRange = function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
var positiveValues = [];
|
||||
var negativeValues = [];
|
||||
|
||||
if (self.options.stacked) {
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
positiveValues[index] = positiveValues[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
|
||||
if (self.options.relativePoints) {
|
||||
positiveValues[index] = 100;
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negativeValues[index] += value;
|
||||
} else {
|
||||
positiveValues[index] += value;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
|
||||
var values = positiveValues.concat(negativeValues);
|
||||
this.min = helpers.min(values);
|
||||
this.max = helpers.max(values);
|
||||
|
||||
} else {
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Map of scale ID to scale object so we can lookup later
|
||||
this.scales = {};
|
||||
|
||||
// Build the x axis. The line chart only supports a single x axis
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(this.options.scales.xAxes[0].scaleType);
|
||||
var xScale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: this.options.scales.xAxes[0],
|
||||
id: this.options.scales.xAxes[0].id,
|
||||
calculateRange: function() {
|
||||
this.labels = self.data.labels;
|
||||
this.min = 0;
|
||||
this.max = this.labels.length;
|
||||
},
|
||||
calculateBaseWidth: function() {
|
||||
return (this.getPixelForValue(null, 1, true) - this.getPixelForValue(null, 0, true)) - (2 * self.options.elements.bar.valueSpacing);
|
||||
},
|
||||
calculateBarWidth: function(datasetCount) {
|
||||
//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
|
||||
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * self.options.elements.bar.datasetSpacing);
|
||||
|
||||
if (self.options.stacked) {
|
||||
return baseWidth;
|
||||
}
|
||||
return (baseWidth / datasetCount);
|
||||
},
|
||||
calculateBarX: function(datasetCount, datasetIndex, elementIndex) {
|
||||
var xWidth = this.calculateBaseWidth(),
|
||||
xAbsolute = this.getPixelForValue(null, elementIndex, true) - (xWidth / 2),
|
||||
barWidth = this.calculateBarWidth(datasetCount);
|
||||
|
||||
if (self.options.stacked) {
|
||||
return xAbsolute + barWidth / 2;
|
||||
}
|
||||
|
||||
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * self.options.elements.bar.datasetSpacing) + barWidth / 2;
|
||||
},
|
||||
});
|
||||
this.scales[xScale.id] = xScale;
|
||||
|
||||
// Build up all the y scales
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(yAxisOptions.scaleType);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: yAxisOptions,
|
||||
calculateRange: calculateYRange,
|
||||
calculateBarBase: function(datasetIndex, index) {
|
||||
var base = 0;
|
||||
|
||||
if (self.options.stacked) {
|
||||
|
||||
var value = self.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (value < 0) {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (self.data.datasets[i].yAxisID === this.id) {
|
||||
base += self.data.datasets[i].data[index] < 0 ? self.data.datasets[i].data[index] : 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < datasetIndex; j++) {
|
||||
if (self.data.datasets[j].yAxisID === this.id) {
|
||||
base += self.data.datasets[j].data[index] > 0 ? self.data.datasets[j].data[index] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.getPixelForValue(base);
|
||||
}
|
||||
|
||||
base = this.getPixelForValue(this.min);
|
||||
|
||||
if (this.beginAtZero || ((this.min <= 0 && this.max >= 0) || (this.min >= 0 && this.max <= 0))) {
|
||||
base = this.getPixelForValue(0);
|
||||
base += this.options.gridLines.lineWidth;
|
||||
} else if (this.min < 0 && this.max < 0) {
|
||||
// All values are negative. Use the top as the base
|
||||
base = this.getPixelForValue(this.max);
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
},
|
||||
calculateBarY: function(datasetIndex, index) {
|
||||
|
||||
var value = self.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (self.options.stacked) {
|
||||
|
||||
var sumPos = 0,
|
||||
sumNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (self.data.datasets[i].data[index] < 0) {
|
||||
sumNeg += self.data.datasets[i].data[index] || 0;
|
||||
} else {
|
||||
sumPos += self.data.datasets[i].data[index] || 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return this.getPixelForValue(sumNeg + value);
|
||||
} else {
|
||||
return this.getPixelForValue(sumPos + value);
|
||||
}
|
||||
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
|
||||
var offset = 0;
|
||||
|
||||
for (var j = datasetIndex; j < self.data.datasets.length; j++) {
|
||||
if (j === datasetIndex && value) {
|
||||
offset += value;
|
||||
} else {
|
||||
offset = offset + value;
|
||||
}
|
||||
}
|
||||
|
||||
return this.getPixelForValue(value);
|
||||
},
|
||||
id: yAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
|
||||
//Draw all the bars for each dataset
|
||||
this.eachElement(function(bar, index, datasetIndex) {
|
||||
bar.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.bar.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.bar.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.bar.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.bar.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.bar.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.bar.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[0]._model.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[i]._model.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.tooltip.pivot();
|
||||
|
||||
// Hover animations
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
Arquivo executável
+1985
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,367 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
//Cache a local reference to Chart.helpers
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
animation: {
|
||||
//Boolean - Whether we animate the rotation of the Doughnut
|
||||
animateRotate: true,
|
||||
|
||||
//Boolean - Whether we animate scaling the Doughnut from the centre
|
||||
animateScale: false,
|
||||
},
|
||||
|
||||
hover: {
|
||||
mode: 'single'
|
||||
},
|
||||
|
||||
//The percentage of the chart that we cut out of the middle.
|
||||
|
||||
cutoutPercentage: 50,
|
||||
|
||||
};
|
||||
|
||||
Chart.Type.extend({
|
||||
//Passing in a name registers this chart in the Chart namespace
|
||||
name: "Doughnut",
|
||||
//Providing a defaults will also register the deafults in the chart namespace
|
||||
defaults: defaultConfig,
|
||||
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
|
||||
//Config is automatically merged by the core of Chart.js, and is available at this.options
|
||||
initialize: function() {
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
//Create a new bar for each piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Arc({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_model: {}
|
||||
}));
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
this.resetElements();
|
||||
|
||||
// Update the chart with the latest data.
|
||||
this.update();
|
||||
|
||||
},
|
||||
|
||||
calculateCircumference: function(dataset, value) {
|
||||
if (dataset.total > 0) {
|
||||
return (Math.PI * 2) * (value / dataset.total);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
resetElements: function() {
|
||||
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.slice.borderWidth / 2) / 2;
|
||||
this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
|
||||
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
|
||||
|
||||
// Update the points
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
// So that calculateCircumference works
|
||||
dataset.total = 0;
|
||||
helpers.each(dataset.data, function(value) {
|
||||
dataset.total += Math.abs(value);
|
||||
}, this);
|
||||
|
||||
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
|
||||
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
|
||||
|
||||
helpers.each(dataset.metaData, function(slice, index) {
|
||||
helpers.extend(slice, {
|
||||
_model: {
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
|
||||
circumference: (this.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value),
|
||||
outerRadius: (this.options.animation.animateScale) ? 0 : dataset.outerRadius,
|
||||
innerRadius: (this.options.animation.animateScale) ? 0 : dataset.innerRadius,
|
||||
|
||||
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor),
|
||||
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.slice.hoverBackgroundColor),
|
||||
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth),
|
||||
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
slice.pivot();
|
||||
}, this);
|
||||
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
|
||||
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.slice.borderWidth / 2) / 2;
|
||||
this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
|
||||
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
|
||||
|
||||
|
||||
// Update the points
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
|
||||
dataset.total = 0;
|
||||
helpers.each(dataset.data, function(value) {
|
||||
dataset.total += Math.abs(value);
|
||||
}, this);
|
||||
|
||||
|
||||
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
|
||||
|
||||
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
|
||||
|
||||
helpers.each(dataset.metaData, function(slice, index) {
|
||||
|
||||
helpers.extend(slice, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
circumference: this.calculateCircumference(dataset, dataset.data[index]),
|
||||
outerRadius: dataset.outerRadius,
|
||||
innerRadius: dataset.innerRadius,
|
||||
|
||||
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor),
|
||||
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.slice.hoverBackgroundColor),
|
||||
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth),
|
||||
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
if (index === 0) {
|
||||
slice._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
|
||||
} else {
|
||||
slice._model.startAngle = dataset.metaData[index - 1]._model.endAngle;
|
||||
}
|
||||
|
||||
slice._model.endAngle = slice._model.startAngle + slice._model.circumference;
|
||||
|
||||
|
||||
//Check to see if it's the last slice, if not get the next and update its start angle
|
||||
if (index < dataset.data.length - 1) {
|
||||
dataset.metaData[index + 1]._model.startAngle = slice._model.endAngle;
|
||||
}
|
||||
|
||||
slice.pivot();
|
||||
}, this);
|
||||
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
draw: function(easeDecimal) {
|
||||
easeDecimal = easeDecimal || 1;
|
||||
this.clear();
|
||||
|
||||
this.eachElement(function(slice) {
|
||||
slice.transition(easeDecimal).draw();
|
||||
}, this);
|
||||
|
||||
this.tooltip.transition(easeDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getSliceAtEvent(e);
|
||||
case 'label':
|
||||
return this.getSlicesAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, this.active[0]._model.borderColor);
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, this.active[0]._model.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, this.active[0]._model.borderColor);
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, this.active[i]._model.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hover.animationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
getSliceAtEvent: function(e) {
|
||||
var elements = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
this.eachElement(function(slice, index) {
|
||||
if (slice.inRange(location.x, location.y)) {
|
||||
elements.push(slice);
|
||||
}
|
||||
}, this);
|
||||
return elements;
|
||||
},
|
||||
/*getSlicesAtEvent: function(e) {
|
||||
var elements = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
this.eachElement(function(slice, index) {
|
||||
if (slice.inGroupRange(location.x, location.y)) {
|
||||
elements.push(slice);
|
||||
}
|
||||
}, this);
|
||||
return elements;
|
||||
},*/
|
||||
});
|
||||
|
||||
Chart.types.Doughnut.extend({
|
||||
name: "Pie",
|
||||
defaults: helpers.merge(defaultConfig, {
|
||||
cutoutPercentage: 0
|
||||
})
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,620 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
stacked: false,
|
||||
|
||||
hover: {
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
display: true,
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
name: "Line",
|
||||
defaults: defaultConfig,
|
||||
initialize: function() {
|
||||
|
||||
var _this = this;
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
// Create a new line and its points for each dataset and piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
|
||||
dataset.metaDataset = new Chart.Line({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_points: dataset.metaData,
|
||||
});
|
||||
|
||||
dataset.metaData = [];
|
||||
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Point({
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart,
|
||||
_model: {
|
||||
x: 0, //xScale.getPixelForValue(null, index, true),
|
||||
y: 0, //this.chartArea.bottom,
|
||||
},
|
||||
}));
|
||||
|
||||
}, this);
|
||||
|
||||
// The line chart onlty supports a single x axis because the x axis is always a dataset axis
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
|
||||
if (!dataset.yAxisID) {
|
||||
dataset.yAxisID = this.options.scales.yAxes[0].id;
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Need to fit scales before we reset elements.
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Reset so that we animation from the baseline
|
||||
this.resetElements();
|
||||
|
||||
// Update that shiz
|
||||
this.update();
|
||||
},
|
||||
nextPoint: function(collection, index) {
|
||||
return collection[index + 1] || collection[index];
|
||||
},
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[index];
|
||||
},
|
||||
resetElements: function() {
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
var yScalePoint;
|
||||
|
||||
if (yScale.min < 0 && yScale.max <0) {
|
||||
// all less than 0. use the top
|
||||
yScalePoint = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
yScalePoint = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
yScalePoint = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(null, index, true), // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: yScalePoint,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: typeof this.data.datasets[datasetIndex].data[index] != 'number',
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
var yScale = this.scales[dataset.yAxisID];
|
||||
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
// Utility
|
||||
_scale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
// Data
|
||||
_children: dataset.metaData,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.elements.line.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.elements.line.borderColor,
|
||||
fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
|
||||
skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
|
||||
drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
|
||||
// Scale
|
||||
scaleTop: yScale.top,
|
||||
scaleBottom: yScale.bottom,
|
||||
scaleZero: yScale.getPixelForValue(0),
|
||||
},
|
||||
});
|
||||
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(null, index, true), // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: yScale.getPointPixelForValue(this.data.datasets[datasetIndex].data[index], index, datasetIndex),
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: typeof this.data.datasets[datasetIndex].data[index] != 'number',
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function() {
|
||||
var self = this;
|
||||
|
||||
// Function to determine the range of all the
|
||||
var calculateYRange = function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
var positiveValues = [];
|
||||
var negativeValues = [];
|
||||
|
||||
if (self.options.stacked) {
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
positiveValues[index] = positiveValues[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
|
||||
if (self.options.relativePoints) {
|
||||
positiveValues[index] = 100;
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negativeValues[index] += value;
|
||||
} else {
|
||||
positiveValues[index] += value;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
|
||||
var values = positiveValues.concat(negativeValues);
|
||||
this.min = helpers.min(values);
|
||||
this.max = helpers.max(values);
|
||||
} else {
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Map of scale ID to scale object so we can lookup later
|
||||
this.scales = {};
|
||||
|
||||
// Build the x axis. The line chart only supports a single x axis
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(this.options.scales.xAxes[0].scaleType);
|
||||
var xScale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: this.options.scales.xAxes[0],
|
||||
calculateRange: function() {
|
||||
this.labels = self.data.labels;
|
||||
this.min = 0;
|
||||
this.max = this.labels.length;
|
||||
},
|
||||
id: this.options.scales.xAxes[0].id,
|
||||
});
|
||||
this.scales[xScale.id] = xScale;
|
||||
|
||||
// Build up all the y scales
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(yAxisOptions.scaleType);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: yAxisOptions,
|
||||
calculateRange: calculateYRange,
|
||||
getPointPixelForValue: function(value, index, datasetIndex) {
|
||||
if (self.options.stacked) {
|
||||
var offsetPos = 0;
|
||||
var offsetNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; ++i) {
|
||||
if (self.data.datasets[i].data[index] < 0) {
|
||||
offsetNeg += self.data.datasets[i].data[index];
|
||||
} else {
|
||||
offsetPos += self.data.datasets[i].data[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return this.getPixelForValue(offsetNeg + value);
|
||||
} else {
|
||||
return this.getPixelForValue(offsetPos + value);
|
||||
}
|
||||
} else {
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
},
|
||||
id: yAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
|
||||
// reverse for-loop for proper stacking
|
||||
for (var i = this.data.datasets.length - 1; i >= 0; i--) {
|
||||
|
||||
var dataset = this.data.datasets[i];
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(dataset.metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
}, this);
|
||||
|
||||
// Transition and Draw the line
|
||||
dataset.metaDataset.transition(easingDecimal).draw();
|
||||
|
||||
// Draw the points
|
||||
helpers.each(dataset.metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
}
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hover.animationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,403 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
//Cache a local reference to Chart.helpers
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
scale: {
|
||||
scaleType: "radialLinear",
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to animate scaling the chart from the centre
|
||||
animate: false,
|
||||
|
||||
lineArc: true,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: true,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
showLabelBackdrop: true,
|
||||
|
||||
//String - The colour of the label backdrop
|
||||
backdropColor: "rgba(255,255,255,0.75)",
|
||||
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
backdropPaddingY: 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
backdropPaddingX: 2,
|
||||
}
|
||||
},
|
||||
|
||||
//Boolean - Whether to animate the rotation of the chart
|
||||
animateRotate: true,
|
||||
};
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
//Passing in a name registers this chart in the Chart namespace
|
||||
name: "PolarArea",
|
||||
//Providing a defaults will also register the deafults in the chart namespace
|
||||
defaults: defaultConfig,
|
||||
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
|
||||
//Config is automatically merged by the core of Chart.js, and is available at this.options
|
||||
initialize: function() {
|
||||
|
||||
// Scale setup
|
||||
var self = this;
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(this.options.scale.scaleType);
|
||||
this.scale = new ScaleClass({
|
||||
options: this.options.scale,
|
||||
lineArc: true,
|
||||
width: this.chart.width,
|
||||
height: this.chart.height,
|
||||
xCenter: this.chart.width / 2,
|
||||
yCenter: this.chart.height / 2,
|
||||
ctx: this.chart.ctx,
|
||||
valuesCount: this.data.length,
|
||||
calculateRange: function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data.datasets[0].data, function(value) {
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
//Create a new bar for each piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Arc({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_model: {}
|
||||
}));
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Fit the scale before we animate
|
||||
this.updateScaleRange();
|
||||
this.scale.calculateRange();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// so that we animate nicely
|
||||
this.resetElements();
|
||||
|
||||
// Update the chart with the latest data.
|
||||
this.update();
|
||||
|
||||
},
|
||||
updateScaleRange: function() {
|
||||
helpers.extend(this.scale, {
|
||||
size: helpers.min([this.chart.width, this.chart.height]),
|
||||
xCenter: this.chart.width / 2,
|
||||
yCenter: this.chart.height / 2
|
||||
});
|
||||
},
|
||||
resetElements: function() {
|
||||
var circumference = 1 / this.data.datasets[0].data.length * 2;
|
||||
|
||||
// Map new data to data points
|
||||
helpers.each(this.data.datasets[0].metaData, function(slice, index) {
|
||||
|
||||
var value = this.data.datasets[0].data[index];
|
||||
|
||||
var startAngle = Math.PI * 1.5 + (Math.PI * circumference) * index;
|
||||
var endAngle = startAngle + (circumference * Math.PI);
|
||||
|
||||
helpers.extend(slice, {
|
||||
_index: index,
|
||||
_model: {
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: 0,
|
||||
startAngle: Math.PI * 1.5,
|
||||
endAngle: Math.PI * 1.5,
|
||||
|
||||
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].backgroundColor, index, this.options.elements.slice.backgroundColor),
|
||||
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].hoverBackgroundColor, index, this.options.elements.slice.hoverBackgroundColor),
|
||||
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderWidth, index, this.options.elements.slice.borderWidth),
|
||||
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderColor, index, this.options.elements.slice.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.data.datasets[0].labels, index, this.data.datasets[0].labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
slice.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
|
||||
this.updateScaleRange();
|
||||
this.scale.calculateRange();
|
||||
this.scale.generateTicks();
|
||||
this.scale.buildYLabels();
|
||||
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
var circumference = 1 / this.data.datasets[0].data.length * 2;
|
||||
|
||||
// Map new data to data points
|
||||
helpers.each(this.data.datasets[0].metaData, function(slice, index) {
|
||||
|
||||
var value = this.data.datasets[0].data[index];
|
||||
|
||||
var startAngle = Math.PI * 1.5 + (Math.PI * circumference) * index;
|
||||
var endAngle = startAngle + (circumference * Math.PI);
|
||||
|
||||
helpers.extend(slice, {
|
||||
_index: index,
|
||||
_model: {
|
||||
x: this.chart.width / 2,
|
||||
y: this.chart.height / 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: this.scale.calculateCenterOffset(value),
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle,
|
||||
|
||||
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].backgroundColor, index, this.options.elements.slice.backgroundColor),
|
||||
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].hoverBackgroundColor, index, this.options.elements.slice.hoverBackgroundColor),
|
||||
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderWidth, index, this.options.elements.slice.borderWidth),
|
||||
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderColor, index, this.options.elements.slice.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.data.datasets[0].labels, index, this.data.datasets[0].labels[index])
|
||||
},
|
||||
});
|
||||
slice.pivot();
|
||||
|
||||
console.log(slice);
|
||||
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
|
||||
this.clear();
|
||||
|
||||
helpers.each(this.data.datasets[0].metaData, function(slice, index) {
|
||||
slice.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
|
||||
this.scale.draw();
|
||||
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getSliceAtEvent(e);
|
||||
case 'label':
|
||||
return this.getSlicesAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.slice.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.slice.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.slice.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hover.animationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
getSliceAtEvent: function(e) {
|
||||
var elements = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
this.eachElement(function(slice, index) {
|
||||
if (slice.inRange(location.x, location.y)) {
|
||||
elements.push(slice);
|
||||
}
|
||||
}, this);
|
||||
return elements;
|
||||
},
|
||||
/*getSlicesAtEvent: function(e) {
|
||||
var elements = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
this.eachElement(function(slice, index) {
|
||||
if (slice.inGroupRange(location.x, location.y)) {
|
||||
elements.push(slice);
|
||||
}
|
||||
}, this);
|
||||
return elements;
|
||||
},*/
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,506 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
name: "Radar",
|
||||
defaults: {
|
||||
|
||||
scale: {
|
||||
scaleType: "radialLinear",
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to animate scaling the chart from the centre
|
||||
animate: false,
|
||||
|
||||
lineArc: false,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
},
|
||||
|
||||
angleLines: {
|
||||
show: true,
|
||||
color: "rgba(0,0,0,.1)",
|
||||
lineWidth: 1
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: true,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
showLabelBackdrop: true,
|
||||
|
||||
//String - The colour of the label backdrop
|
||||
backdropColor: "rgba(255,255,255,0.75)",
|
||||
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
backdropPaddingY: 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
backdropPaddingX: 2,
|
||||
},
|
||||
|
||||
pointLabels: {
|
||||
//String - Point label font declaration
|
||||
fontFamily: "'Arial'",
|
||||
|
||||
//String - Point label font weight
|
||||
fontStyle: "normal",
|
||||
|
||||
//Number - Point label font size in pixels
|
||||
fontSize: 10,
|
||||
|
||||
//String - Point label font colour
|
||||
fontColor: "#666",
|
||||
},
|
||||
},
|
||||
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0, // no bezier in radar
|
||||
}
|
||||
},
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
|
||||
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
// Create a new line and its points for each dataset and piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
|
||||
dataset.metaDataset = new Chart.Line({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_points: dataset.metaData,
|
||||
_loop: true
|
||||
});
|
||||
|
||||
dataset.metaData = [];
|
||||
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Point({
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart,
|
||||
_model: {
|
||||
x: 0, //xScale.getPixelForValue(null, index, true),
|
||||
y: 0, //this.chartArea.bottom,
|
||||
},
|
||||
}));
|
||||
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
// Build the scale.
|
||||
this.buildScale();
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Need to fit scales before we reset elements.
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Reset so that we animation from the baseline
|
||||
this.resetElements();
|
||||
|
||||
// Update that shiz
|
||||
this.update();
|
||||
},
|
||||
nextPoint: function(collection, index) {
|
||||
return collection[index + 1] || collection[0];
|
||||
},
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[collection.length - 1];
|
||||
},
|
||||
resetElements: function() {
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_scale: this.scale,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: this.scale.xCenter,
|
||||
y: this.scale.yCenter,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: typeof this.data.datasets[datasetIndex].data[index] != 'number',
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = this.scale.xCenter;
|
||||
point._model.controlPointPreviousY = this.scale.yCenter;
|
||||
point._model.controlPointNextX = this.scale.xCenter;
|
||||
point._model.controlPointNextY = this.scale.yCenter;
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
// Utility
|
||||
_datasetIndex: datasetIndex,
|
||||
|
||||
// Data
|
||||
_children: dataset.metaData,
|
||||
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.elements.line.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.elements.line.borderColor,
|
||||
fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
|
||||
skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
|
||||
drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
|
||||
|
||||
// Scale
|
||||
scaleTop: this.scale.top,
|
||||
scaleBottom: this.scale.bottom,
|
||||
scaleZero: this.scale.getPointPosition(0),
|
||||
},
|
||||
});
|
||||
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(this.data.datasets[datasetIndex].data[index]));
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: pointPosition.y,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: typeof this.data.datasets[datasetIndex].data[index] != 'number',
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function() {
|
||||
var self = this;
|
||||
|
||||
var ScaleConstructor = Chart.scales.getScaleConstructor(this.options.scale.scaleType);
|
||||
this.scale = new ScaleConstructor({
|
||||
options: this.options.scale,
|
||||
height: this.chart.height,
|
||||
width: this.chart.width,
|
||||
xCenter: this.chart.width / 2,
|
||||
yCenter: this.chart.height / 2,
|
||||
ctx: this.chart.ctx,
|
||||
labels: this.data.labels,
|
||||
valuesCount: this.data.datasets[0].data.length,
|
||||
calculateRange: function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
|
||||
this.scale.setScaleSize();
|
||||
this.scale.calculateRange();
|
||||
this.scale.generateTicks();
|
||||
this.scale.buildYLabels();
|
||||
},
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
this.scale.draw(this.chartArea);
|
||||
|
||||
// reverse for-loop for proper stacking
|
||||
for (var i = this.data.datasets.length - 1; i >= 0; i--) {
|
||||
|
||||
var dataset = this.data.datasets[i];
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(dataset.metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
}, this);
|
||||
|
||||
// Transition and Draw the line
|
||||
dataset.metaDataset.transition(easingDecimal).draw();
|
||||
|
||||
// Draw the points
|
||||
helpers.each(dataset.metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
}
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hover.animationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
});
|
||||
}).call(this);
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,593 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
hover: {
|
||||
mode: 'single',
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleType: "linear", // scatter should not use a dataset axis
|
||||
display: true,
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
},
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].borderColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>",
|
||||
|
||||
tooltips: {
|
||||
template: "(<%= value.x %>, <%= value.y %>)",
|
||||
multiTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%>(<%= value.x %>, <%= value.y %>)",
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
name: "Scatter",
|
||||
defaults: defaultConfig,
|
||||
initialize: function() {
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
//Custom Point Defaults
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaDataset = new Chart.Line({
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_points: dataset.metaData,
|
||||
});
|
||||
|
||||
dataset.metaData = [];
|
||||
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Point({
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart,
|
||||
_model: {
|
||||
x: 0, //xScale.getPixelForValue(null, index, true),
|
||||
y: 0, //this.chartArea.bottom,
|
||||
},
|
||||
}));
|
||||
|
||||
}, this);
|
||||
|
||||
// The line chart onlty supports a single x axis because the x axis is always a dataset axis
|
||||
if (!dataset.xAxisID) {
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
}
|
||||
|
||||
if (!dataset.yAxisID) {
|
||||
dataset.yAxisID = this.options.scales.yAxes[0].id;
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
// Need to fit scales before we reset elements.
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Reset so that we animation from the baseline
|
||||
this.resetElements();
|
||||
|
||||
// Update that shiz
|
||||
this.update();
|
||||
},
|
||||
nextPoint: function(collection, index) {
|
||||
return collection[index + 1] || collection[index];
|
||||
},
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[index];
|
||||
},
|
||||
resetElements: function() {
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
var yScalePoint;
|
||||
|
||||
if (yScale.min < 0 && yScale.max < 0) {
|
||||
// all less than 0. use the top
|
||||
yScalePoint = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
yScalePoint = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
yScalePoint = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x), // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: yScalePoint,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: (typeof this.data.datasets[datasetIndex].data[index].x != 'number') || (typeof this.data.datasets[datasetIndex].data[index].y != 'number'),
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
},
|
||||
update: function() {
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
var yScale = this.scales[dataset.yAxisID];
|
||||
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
// Utility
|
||||
_scale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
// Data
|
||||
_children: dataset.metaData,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.elements.line.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.elements.line.borderColor,
|
||||
fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
|
||||
skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
|
||||
drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
|
||||
// Scale
|
||||
scaleTop: yScale.top,
|
||||
scaleBottom: yScale.bottom,
|
||||
scaleZero: yScale.getPixelForValue(0),
|
||||
},
|
||||
});
|
||||
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x),
|
||||
y: yScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].y),
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
|
||||
skip: (typeof this.data.datasets[datasetIndex].data[index].x != 'number') || (typeof this.data.datasets[datasetIndex].data[index].y != 'number'),
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index)._model,
|
||||
point._model,
|
||||
this.nextPoint(dataset, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function() {
|
||||
var self = this;
|
||||
|
||||
var calculateXRange = function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
// Only set the scale range for datasets that actually use this axis
|
||||
if (dataset.xAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value) {
|
||||
if (this.min === null) {
|
||||
this.min = value.x;
|
||||
} else if (value.x < this.min) {
|
||||
this.min = value.x;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value.x;
|
||||
} else if (value.x > this.max) {
|
||||
this.max = value.x;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
var calculateYRange = function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value) {
|
||||
if (this.min === null) {
|
||||
this.min = value.y;
|
||||
} else if (value.y < this.min) {
|
||||
this.min = value.y;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value.y;
|
||||
} else if (value.y > this.max) {
|
||||
this.max = value.y;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Map of scale ID to scale object so we can lookup later
|
||||
this.scales = {};
|
||||
|
||||
helpers.each(this.options.scales.xAxes, function(xAxisOptions) {
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(xAxisOptions.scaleType);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: xAxisOptions,
|
||||
calculateRange: calculateXRange,
|
||||
id: xAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(yAxisOptions.scaleType);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: yAxisOptions,
|
||||
calculateRange: calculateYRange,
|
||||
id: yAxisOptions.id,
|
||||
getPointPixelForValue: function(value, index, datasetIndex) {
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
|
||||
// reverse for-loop for proper stacking
|
||||
for (var i = this.data.datasets.length - 1; i >= 0; i--) {
|
||||
|
||||
var dataset = this.data.datasets[i];
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(dataset.metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
}, this);
|
||||
|
||||
// Transition and Draw the line
|
||||
dataset.metaDataset.transition(easingDecimal).draw();
|
||||
|
||||
// Draw the points
|
||||
helpers.each(dataset.metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
}
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
index = this.lastActive[0]._index;
|
||||
|
||||
this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
index = this.lastActive[i]._index;
|
||||
|
||||
this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
|
||||
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
|
||||
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
|
||||
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
index = this.active[0]._index;
|
||||
|
||||
this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
|
||||
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
index = this.active[i]._index;
|
||||
|
||||
this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
|
||||
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
|
||||
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,14 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this;
|
||||
var Chart = root.Chart;
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
Chart.Bar = function(context, config) {
|
||||
config.type = 'bar';
|
||||
|
||||
return new Chart(context, config);
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
@@ -1,20 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this;
|
||||
var Chart = root.Chart;
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
aspectRatio: 1,
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i = 0; i < data.datasets[0].data.length; i++){%><li><span style=\"background-color:<%=data.datasets[0].backgroundColor[i]%>\"><%if(data.labels && i < data.labels.length){%><%=data.labels[i]%><%}%></span></li><%}%></ul>",
|
||||
};
|
||||
|
||||
Chart.Doughnut = function(context, config) {
|
||||
config.options = helpers.configMerge(defaultConfig, config.options);
|
||||
config.type = 'doughnut';
|
||||
|
||||
return new Chart(context, config);
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
@@ -1,14 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this;
|
||||
var Chart = root.Chart;
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
Chart.Line = function(context, config) {
|
||||
config.type = 'line';
|
||||
|
||||
return new Chart(context, config);
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
@@ -1,20 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this;
|
||||
var Chart = root.Chart;
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
aspectRatio: 1,
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i = 0; i < data.datasets[0].data.length; i++){%><li><span style=\"background-color:<%=data.datasets[0].backgroundColor[i]%>\"><%if(data.labels && i < data.labels.length){%><%=data.labels[i]%><%}%></span></li><%}%></ul>",
|
||||
};
|
||||
|
||||
Chart.PolarArea = function(context, config) {
|
||||
config.options = helpers.configMerge(defaultConfig, config.options);
|
||||
config.type = 'polarArea';
|
||||
|
||||
return new Chart(context, config);
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
@@ -1,19 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this;
|
||||
var Chart = root.Chart;
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
aspectRatio: 1,
|
||||
};
|
||||
|
||||
Chart.Radar = function(context, config) {
|
||||
config.options = helpers.configMerge(defaultConfig, config.options);
|
||||
config.type = 'radar';
|
||||
|
||||
return new Chart(context, config);
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
@@ -1,39 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this;
|
||||
var Chart = root.Chart;
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
hover: {
|
||||
mode: 'single',
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "linear", // scatter should not use a category axis
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear",
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
}],
|
||||
},
|
||||
|
||||
tooltips: {
|
||||
template: "(<%= value.x %>, <%= value.y %>)",
|
||||
multiTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%>(<%= value.x %>, <%= value.y %>)",
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
Chart.Scatter = function(context, config) {
|
||||
config.options = helpers.configMerge(defaultConfig, config.options);
|
||||
config.type = 'line';
|
||||
return new Chart(context, config);
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
@@ -1,352 +0,0 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
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 = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.controllers.bar.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
updateIndex: function(datasetIndex) {
|
||||
this.index = datasetIndex;
|
||||
},
|
||||
linkScales: function() {
|
||||
if (!this.getDataset().xAxisID) {
|
||||
this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
|
||||
}
|
||||
|
||||
if (!this.getDataset().yAxisID) {
|
||||
this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
|
||||
}
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForID: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
// Get the number of datasets that display bars. We use this to correctly calculate the bar width
|
||||
getBarCount: function getBarCount() {
|
||||
var barCount = 0;
|
||||
|
||||
helpers.each(this.chart.data.datasets, function(dataset) {
|
||||
if (dataset.type === 'bar') {
|
||||
++barCount;
|
||||
} else if (dataset.type === undefined && this.chart.config.type === 'bar') {
|
||||
++barCount;
|
||||
}
|
||||
}, this);
|
||||
|
||||
return barCount;
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Rectangle({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
addElementAndReset: function(index) {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
|
||||
var numBars = this.getBarCount();
|
||||
|
||||
this.updateElement(rectangle, index, true, numBars);
|
||||
this.getDataset().metaData.splice(index, 0, rectangle);
|
||||
},
|
||||
|
||||
removeElement: function(index) {
|
||||
this.getDataset().metaData.splice(index, 1);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
buildOrUpdateElements: function buildOrUpdateElements() {
|
||||
var numData = this.getDataset().data.length;
|
||||
var numRectangles = this.getDataset().metaData.length;
|
||||
|
||||
// Make sure that we handle number of datapoints changing
|
||||
if (numData < numRectangles) {
|
||||
// Remove excess bars for data points that have been removed
|
||||
this.getDataset().metaData.splice(numData, numRectangles - numData);
|
||||
} else if (numData > numRectangles) {
|
||||
// Add new elements
|
||||
for (var index = numRectangles; index < numData; ++index) {
|
||||
this.addElementAndReset(index);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update: function update(reset) {
|
||||
var numBars = this.getBarCount();
|
||||
|
||||
helpers.each(this.getDataset().metaData, function(rectangle, index) {
|
||||
this.updateElement(rectangle, index, reset, numBars);
|
||||
}, this);
|
||||
},
|
||||
|
||||
updateElement: function updateElement(rectangle, index, reset, numBars) {
|
||||
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
var yScale = this.getScaleForID(this.getDataset().yAxisID);
|
||||
|
||||
var yScalePoint;
|
||||
|
||||
if (yScale.min < 0 && yScale.max < 0) {
|
||||
// all less than 0. use the top
|
||||
yScalePoint = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
yScalePoint = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
yScalePoint = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
helpers.extend(rectangle, {
|
||||
// Utility
|
||||
_chart: this.chart.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: this.calculateBarX(index, this.index),
|
||||
y: reset ? yScalePoint : this.calculateBarY(index, this.index),
|
||||
|
||||
// Tooltip
|
||||
label: this.chart.data.labels[index],
|
||||
datasetLabel: this.getDataset().label,
|
||||
|
||||
// Appearance
|
||||
base: this.calculateBarBase(this.index, index),
|
||||
width: this.calculateBarWidth(numBars),
|
||||
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
|
||||
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
|
||||
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth),
|
||||
},
|
||||
});
|
||||
rectangle.pivot();
|
||||
},
|
||||
|
||||
calculateBarBase: function(datasetIndex, index) {
|
||||
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
var yScale = this.getScaleForID(this.getDataset().yAxisID);
|
||||
|
||||
var base = 0;
|
||||
|
||||
if (yScale.options.stacked) {
|
||||
|
||||
var value = this.chart.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (value < 0) {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (this.chart.data.datasets[i].yAxisID === yScale.id) {
|
||||
base += this.chart.data.datasets[i].data[index] < 0 ? this.chart.data.datasets[i].data[index] : 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < datasetIndex; j++) {
|
||||
if (this.chart.data.datasets[j].yAxisID === yScale.id) {
|
||||
base += this.chart.data.datasets[j].data[index] > 0 ? this.chart.data.datasets[j].data[index] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return yScale.getPixelForValue(base);
|
||||
}
|
||||
|
||||
base = yScale.getPixelForValue(yScale.min);
|
||||
|
||||
if (yScale.beginAtZero || ((yScale.min <= 0 && yScale.max >= 0) || (yScale.min >= 0 && yScale.max <= 0))) {
|
||||
base = yScale.getPixelForValue(0, 0);
|
||||
//base += yScale.options.gridLines.lineWidth;
|
||||
} else if (yScale.min < 0 && yScale.max < 0) {
|
||||
// All values are negative. Use the top as the base
|
||||
base = yScale.getPixelForValue(yScale.max);
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
},
|
||||
|
||||
getRuler: function() {
|
||||
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
var yScale = this.getScaleForID(this.getDataset().yAxisID);
|
||||
|
||||
var datasetCount = !this.chart.isCombo ? this.chart.data.datasets.length : helpers.where(this.chart.data.datasets, function(ds) {
|
||||
return ds.type == 'bar';
|
||||
}).length;
|
||||
var tickWidth = (function() {
|
||||
var min = xScale.getPixelForValue(null, 1) - xScale.getPixelForValue(null, 0);
|
||||
for (var i = 2; i < this.getDataset().data.length; i++) {
|
||||
min = Math.min(xScale.getPixelForValue(null, i) - xScale.getPixelForValue(null, i - 1), min);
|
||||
}
|
||||
return min;
|
||||
}).call(this);
|
||||
var categoryWidth = tickWidth * xScale.options.categoryPercentage;
|
||||
var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
|
||||
var fullBarWidth = categoryWidth / datasetCount;
|
||||
var barWidth = fullBarWidth * xScale.options.barPercentage;
|
||||
var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
|
||||
|
||||
return {
|
||||
datasetCount: datasetCount,
|
||||
tickWidth: tickWidth,
|
||||
categoryWidth: categoryWidth,
|
||||
categorySpacing: categorySpacing,
|
||||
fullBarWidth: fullBarWidth,
|
||||
barWidth: barWidth,
|
||||
barSpacing: barSpacing,
|
||||
};
|
||||
},
|
||||
|
||||
calculateBarWidth: function() {
|
||||
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
var ruler = this.getRuler();
|
||||
|
||||
if (xScale.options.stacked) {
|
||||
return ruler.categoryWidth;
|
||||
}
|
||||
|
||||
return ruler.barWidth;
|
||||
|
||||
},
|
||||
|
||||
|
||||
calculateBarX: function(index, datasetIndex) {
|
||||
|
||||
var yScale = this.getScaleForID(this.getDataset().yAxisID);
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
|
||||
var ruler = this.getRuler();
|
||||
var leftTick = xScale.getPixelForValue(null, index, datasetIndex, this.chart.isCombo);
|
||||
leftTick -= this.chart.isCombo ? (ruler.tickWidth / 2) : 0;
|
||||
|
||||
if (yScale.options.stacked) {
|
||||
return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing;
|
||||
}
|
||||
|
||||
return leftTick +
|
||||
(ruler.barWidth / 2) +
|
||||
ruler.categorySpacing +
|
||||
(ruler.barWidth * datasetIndex) +
|
||||
(ruler.barSpacing / 2) +
|
||||
(ruler.barSpacing * datasetIndex);
|
||||
},
|
||||
|
||||
calculateBarY: function(index, datasetIndex) {
|
||||
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
var yScale = this.getScaleForID(this.getDataset().yAxisID);
|
||||
|
||||
var value = this.getDataset().data[index];
|
||||
|
||||
if (yScale.options.stacked) {
|
||||
|
||||
var sumPos = 0,
|
||||
sumNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (this.chart.data.datasets[i].data[index] < 0) {
|
||||
sumNeg += this.chart.data.datasets[i].data[index] || 0;
|
||||
} else {
|
||||
sumPos += this.chart.data.datasets[i].data[index] || 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return yScale.getPixelForValue(sumNeg + value);
|
||||
} else {
|
||||
return yScale.getPixelForValue(sumPos + value);
|
||||
}
|
||||
|
||||
return yScale.getPixelForValue(value);
|
||||
}
|
||||
|
||||
return yScale.getPixelForValue(value);
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
helpers.each(this.getDataset().metaData, function(rectangle, index) {
|
||||
rectangle.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
},
|
||||
|
||||
setHoverStyle: function(rectangle) {
|
||||
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
|
||||
var index = rectangle._index;
|
||||
|
||||
rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.hoverBackgroundColor ? rectangle.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(rectangle._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
rectangle._model.borderColor = rectangle.custom && rectangle.custom.hoverBorderColor ? rectangle.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(rectangle._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
rectangle._model.borderWidth = rectangle.custom && rectangle.custom.hoverBorderWidth ? rectangle.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, rectangle._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(rectangle) {
|
||||
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
|
||||
var index = rectangle._index;
|
||||
|
||||
rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor);
|
||||
rectangle._model.borderColor = rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor);
|
||||
rectangle._model.borderWidth = rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,211 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
//Cache a local reference to Chart.helpers
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.doughnut = {
|
||||
animation: {
|
||||
//Boolean - Whether we animate the rotation of the Doughnut
|
||||
animateRotate: true,
|
||||
//Boolean - Whether we animate scaling the Doughnut from the centre
|
||||
animateScale: false,
|
||||
},
|
||||
hover: {
|
||||
mode: 'single'
|
||||
},
|
||||
//The percentage of the chart that we cut out of the middle.
|
||||
cutoutPercentage: 50,
|
||||
};
|
||||
|
||||
Chart.defaults.pie = helpers.clone(Chart.defaults.doughnut);
|
||||
helpers.extend(Chart.defaults.pie, {
|
||||
cutoutPercentage: 0
|
||||
});
|
||||
|
||||
|
||||
Chart.controllers.doughnut = Chart.controllers.pie = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.controllers.doughnut.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
updateIndex: function(datasetIndex) {
|
||||
this.index = datasetIndex;
|
||||
},
|
||||
linkScales: function() {
|
||||
// no scales for doughnut
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
addElementAndReset: function(index, colorForNewElement) {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
var arc = new Chart.elements.Arc({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
|
||||
if (colorForNewElement && helpers.isArray(this.getDataset().backgroundColor)) {
|
||||
this.getDataset().backgroundColor.splice(index, 0, colorForNewElement);
|
||||
}
|
||||
|
||||
// Reset the point
|
||||
this.updateElement(arc, index, true);
|
||||
|
||||
// Add to the points array
|
||||
this.getDataset().metaData.splice(index, 0, arc);
|
||||
},
|
||||
removeElement: function(index) {
|
||||
this.getDataset().metaData.splice(index, 1);
|
||||
},
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
buildOrUpdateElements: function buildOrUpdateElements() {
|
||||
// Make sure we have metaData for each data point
|
||||
var numData = this.getDataset().data.length;
|
||||
var numArcs = this.getDataset().metaData.length;
|
||||
|
||||
// Make sure that we handle number of datapoints changing
|
||||
if (numData < numArcs) {
|
||||
// Remove excess bars for data points that have been removed
|
||||
this.getDataset().metaData.splice(numData, numArcs - numData)
|
||||
} else if (numData > numArcs) {
|
||||
// Add new elements
|
||||
for (var index = numArcs; index < numData; ++index) {
|
||||
this.addElementAndReset(index);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update: function update(reset) {
|
||||
|
||||
this.chart.outerRadius = Math.max((helpers.min([this.chart.chart.width, this.chart.chart.height]) / 2) - this.chart.options.elements.arc.borderWidth / 2, 0);
|
||||
this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0);
|
||||
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.chart.data.datasets.length;
|
||||
|
||||
this.getDataset().total = 0;
|
||||
helpers.each(this.getDataset().data, function(value) {
|
||||
this.getDataset().total += Math.abs(value);
|
||||
}, this);
|
||||
|
||||
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index);
|
||||
this.innerRadius = this.outerRadius - this.chart.radiusLength;
|
||||
|
||||
helpers.each(this.getDataset().metaData, function(arc, index) {
|
||||
this.updateElement(arc, index, reset);
|
||||
}, this);
|
||||
},
|
||||
updateElement: function(arc, index, reset) {
|
||||
var resetModel = {
|
||||
x: this.chart.chart.width / 2,
|
||||
y: this.chart.chart.height / 2,
|
||||
startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
|
||||
circumference: (this.chart.options.animation.animateRotate) ? 0 : this.calculateCircumference(this.getDataset().data[index]),
|
||||
outerRadius: (this.chart.options.animation.animateScale) ? 0 : this.outerRadius,
|
||||
innerRadius: (this.chart.options.animation.animateScale) ? 0 : this.innerRadius,
|
||||
};
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: reset ? resetModel : {
|
||||
x: this.chart.chart.width / 2,
|
||||
y: this.chart.chart.height / 2,
|
||||
circumference: this.calculateCircumference(this.getDataset().data[index]),
|
||||
outerRadius: this.outerRadius,
|
||||
innerRadius: this.innerRadius,
|
||||
|
||||
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
|
||||
hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
|
||||
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
|
||||
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.getDataset().label, index, this.chart.data.labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
if (!reset) {
|
||||
|
||||
if (index === 0) {
|
||||
arc._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
|
||||
} else {
|
||||
arc._model.startAngle = this.getDataset().metaData[index - 1]._model.endAngle;
|
||||
}
|
||||
|
||||
arc._model.endAngle = arc._model.startAngle + arc._model.circumference;
|
||||
|
||||
|
||||
//Check to see if it's the last arc, if not get the next and update its start angle
|
||||
if (index < this.getDataset().data.length - 1) {
|
||||
this.getDataset().metaData[index + 1]._model.startAngle = arc._model.endAngle;
|
||||
}
|
||||
}
|
||||
|
||||
arc.pivot();
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
helpers.each(this.getDataset().metaData, function(arc, index) {
|
||||
arc.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
},
|
||||
|
||||
setHoverStyle: function(arc) {
|
||||
var dataset = this.chart.data.datasets[arc._datasetIndex];
|
||||
var index = arc._index;
|
||||
|
||||
arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, arc._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(arc) {
|
||||
var dataset = this.chart.data.datasets[arc._datasetIndex];
|
||||
var index = arc._index;
|
||||
|
||||
arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor);
|
||||
arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor);
|
||||
arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth);
|
||||
},
|
||||
|
||||
calculateCircumference: function(value) {
|
||||
if (this.getDataset().total > 0) {
|
||||
return (Math.PI * 1.999999) * (value / this.getDataset().total);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,289 +0,0 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.line = {
|
||||
hover: {
|
||||
mode: "label"
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "category",
|
||||
id: 'x-axis-0'
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear",
|
||||
id: 'y-axis-0'
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Chart.controllers.line = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.controllers.line.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
updateIndex: function(datasetIndex) {
|
||||
this.index = datasetIndex;
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
if (!this.getDataset().xAxisID) {
|
||||
this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
|
||||
}
|
||||
|
||||
if (!this.getDataset().yAxisID) {
|
||||
this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
|
||||
}
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
|
||||
this.getDataset().metaDataset = this.getDataset().metaDataset || new Chart.elements.Line({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_points: this.getDataset().metaData,
|
||||
});
|
||||
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
addElementAndReset: function(index) {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
var point = new Chart.elements.Point({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
|
||||
// Reset the point
|
||||
this.updateElement(point, index, true);
|
||||
|
||||
// Add to the points array
|
||||
this.getDataset().metaData.splice(index, 0, point);
|
||||
|
||||
// Make sure bezier control points are updated
|
||||
this.updateBezierControlPoints();
|
||||
},
|
||||
removeElement: function(index) {
|
||||
this.getDataset().metaData.splice(index, 1);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
buildOrUpdateElements: function buildOrUpdateElements() {
|
||||
// Handle the number of data points changing
|
||||
var numData = this.getDataset().data.length;
|
||||
var numPoints = this.getDataset().metaData.length;
|
||||
|
||||
// Make sure that we handle number of datapoints changing
|
||||
if (numData < numPoints) {
|
||||
// Remove excess bars for data points that have been removed
|
||||
this.getDataset().metaData.splice(numData, numPoints - numData);
|
||||
} else if (numData > numPoints) {
|
||||
// Add new elements
|
||||
for (var index = numPoints; index < numData; ++index) {
|
||||
this.addElementAndReset(index);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update: function update(reset) {
|
||||
var line = this.getDataset().metaDataset;
|
||||
var points = this.getDataset().metaData;
|
||||
|
||||
var yScale = this.getScaleForId(this.getDataset().yAxisID);
|
||||
var xScale = this.getScaleForId(this.getDataset().xAxisID);
|
||||
var scaleBase;
|
||||
|
||||
if (yScale.min < 0 && yScale.max < 0) {
|
||||
scaleBase = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
scaleBase = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
scaleBase = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
// Update Line
|
||||
helpers.extend(line, {
|
||||
// Utility
|
||||
_scale: yScale,
|
||||
_datasetIndex: this.index,
|
||||
// Data
|
||||
_children: points,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: line.custom && line.custom.tension ? line.custom.tension : (this.getDataset().tension || this.chart.options.elements.line.tension),
|
||||
backgroundColor: line.custom && line.custom.backgroundColor ? line.custom.backgroundColor : (this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor),
|
||||
borderWidth: line.custom && line.custom.borderWidth ? line.custom.borderWidth : (this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth),
|
||||
borderColor: line.custom && line.custom.borderColor ? line.custom.borderColor : (this.getDataset().borderColor || this.chart.options.elements.line.borderColor),
|
||||
borderCapStyle: line.custom && line.custom.borderCapStyle ? line.custom.borderCapStyle : (this.getDataset().borderCapStyle || this.chart.options.elements.line.borderCapStyle),
|
||||
borderDash: line.custom && line.custom.borderDash ? line.custom.borderDash : (this.getDataset().borderDash || this.chart.options.elements.line.borderDash),
|
||||
borderDashOffset: line.custom && line.custom.borderDashOffset ? line.custom.borderDashOffset : (this.getDataset().borderDashOffset || this.chart.options.elements.line.borderDashOffset),
|
||||
borderJoinStyle: line.custom && line.custom.borderJoinStyle ? line.custom.borderJoinStyle : (this.getDataset().borderJoinStyle || this.chart.options.elements.line.borderJoinStyle),
|
||||
fill: line.custom && line.custom.fill ? line.custom.fill : (this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill),
|
||||
skipNull: this.getDataset().skipNull !== undefined ? this.getDataset().skipNull : this.chart.options.elements.line.skipNull,
|
||||
drawNull: this.getDataset().drawNull !== undefined ? this.getDataset().drawNull : this.chart.options.elements.line.drawNull,
|
||||
// Scale
|
||||
scaleTop: yScale.top,
|
||||
scaleBottom: yScale.bottom,
|
||||
scaleZero: scaleBase,
|
||||
},
|
||||
});
|
||||
line.pivot();
|
||||
|
||||
// Update Points
|
||||
helpers.each(points, function(point, index) {
|
||||
this.updateElement(point, index, reset);
|
||||
}, this);
|
||||
|
||||
this.updateBezierControlPoints();
|
||||
},
|
||||
|
||||
updateElement: function(point, index, reset) {
|
||||
var yScale = this.getScaleForId(this.getDataset().yAxisID);
|
||||
var xScale = this.getScaleForId(this.getDataset().xAxisID);
|
||||
var scaleBase;
|
||||
|
||||
if (yScale.min < 0 && yScale.max < 0) {
|
||||
scaleBase = yScale.getPixelForValue(yScale.max);
|
||||
} else if (yScale.min > 0 && yScale.max > 0) {
|
||||
scaleBase = yScale.getPixelForValue(yScale.min);
|
||||
} else {
|
||||
scaleBase = yScale.getPixelForValue(0);
|
||||
}
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart.chart,
|
||||
_xScale: xScale,
|
||||
_yScale: yScale,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: xScale.getPixelForValue(this.getDataset().data[index], index, this.index, this.chart.isCombo),
|
||||
y: reset ? scaleBase : yScale.getPixelForValue(this.getDataset().data[index], index, this.index),
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : (this.getDataset().tension || this.chart.options.elements.line.tension),
|
||||
radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth),
|
||||
skip: point.custom && point.custom.skip ? point.custom.skip : this.getDataset().data[index] === null,
|
||||
|
||||
// Tooltip
|
||||
hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
updateBezierControlPoints: function() {
|
||||
// Update bezier control points
|
||||
helpers.each(this.getDataset().metaData, function(point, index) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(this.getDataset().metaData, index)._model,
|
||||
point._model,
|
||||
helpers.nextItem(this.getDataset().metaData, index)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap outer bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chart.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chart.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chart.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chart.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chart.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chart.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chart.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chart.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(this.getDataset().metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
}, this);
|
||||
|
||||
// Transition and Draw the line
|
||||
this.getDataset().metaDataset.transition(easingDecimal).draw();
|
||||
|
||||
// Draw the points
|
||||
helpers.each(this.getDataset().metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
},
|
||||
|
||||
setHoverStyle: function(point) {
|
||||
// Point
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var index = point._index;
|
||||
|
||||
point._model.radius = point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, point._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(point) {
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var index = point._index;
|
||||
|
||||
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius);
|
||||
point._model.backgroundColor = point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor);
|
||||
point._model.borderColor = point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor);
|
||||
point._model.borderWidth = point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth);
|
||||
}
|
||||
});
|
||||
}).call(this);
|
||||
@@ -1,213 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
//Cache a local reference to Chart.helpers
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
Chart.defaults.polarArea = {
|
||||
|
||||
scale: {
|
||||
type: "radialLinear",
|
||||
lineArc: true, // so that lines are circular
|
||||
},
|
||||
|
||||
//Boolean - Whether to animate the rotation of the chart
|
||||
animateRotate: true,
|
||||
animateScale: true,
|
||||
};
|
||||
|
||||
Chart.controllers.polarArea = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.controllers.polarArea.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
updateIndex: function(datasetIndex) {
|
||||
this.index = datasetIndex;
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
// no scales for doughnut
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
addElementAndReset: function(index) {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
var arc = new Chart.elements.Arc({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
|
||||
// Reset the point
|
||||
this.updateElement(arc, index, true);
|
||||
|
||||
// Add to the points array
|
||||
this.getDataset().metaData.splice(index, 0, arc);
|
||||
},
|
||||
removeElement: function(index) {
|
||||
this.getDataset().metaData.splice(index, 1);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
buildOrUpdateElements: function buildOrUpdateElements() {
|
||||
// Handle the number of data points changing
|
||||
var numData = this.getDataset().data.length;
|
||||
var numPoints = this.getDataset().metaData.length;
|
||||
|
||||
// Make sure that we handle number of datapoints changing
|
||||
if (numData < numPoints) {
|
||||
// Remove excess bars for data points that have been removed
|
||||
this.getDataset().metaData.splice(numData, numPoints - numData)
|
||||
} else if (numData > numPoints) {
|
||||
// Add new elements
|
||||
for (var index = numPoints; index < numData; ++index) {
|
||||
this.addElementAndReset(index);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update: function update(reset) {
|
||||
this.chart.outerRadius = Math.max((helpers.min([this.chart.chart.width, this.chart.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2, 0);
|
||||
this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0);
|
||||
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.chart.data.datasets.length;
|
||||
|
||||
this.getDataset().total = 0;
|
||||
helpers.each(this.getDataset().data, function(value) {
|
||||
this.getDataset().total += Math.abs(value);
|
||||
}, this);
|
||||
|
||||
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index);
|
||||
this.innerRadius = this.outerRadius - this.chart.radiusLength;
|
||||
|
||||
helpers.each(this.getDataset().metaData, function(arc, index) {
|
||||
this.updateElement(arc, index, reset);
|
||||
}, this);
|
||||
},
|
||||
updateElement: function(arc, index, reset) {
|
||||
var circumference = 1 / this.getDataset().data.length * 2;
|
||||
var startAngle = (-0.5 * Math.PI) + (Math.PI * circumference) * index;
|
||||
var endAngle = startAngle + (circumference * Math.PI);
|
||||
|
||||
var resetModel = {
|
||||
x: this.chart.chart.width / 2,
|
||||
y: this.chart.chart.height / 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: this.chart.options.animateScale ? 0 : this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]),
|
||||
startAngle: this.chart.options.animateRotate ? Math.PI * -0.5 : startAngle,
|
||||
endAngle: this.chart.options.animateRotate ? Math.PI * -0.5 : endAngle,
|
||||
|
||||
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
|
||||
hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
|
||||
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
|
||||
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.chart.data.labels, index, this.chart.data.labels[index])
|
||||
};
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: reset ? resetModel : {
|
||||
x: this.chart.chart.width / 2,
|
||||
y: this.chart.chart.height / 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]),
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle,
|
||||
|
||||
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
|
||||
hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
|
||||
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
|
||||
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
|
||||
|
||||
label: helpers.getValueAtIndexOrDefault(this.chart.data.labels, index, this.chart.data.labels[index])
|
||||
},
|
||||
});
|
||||
|
||||
arc.pivot();
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
helpers.each(this.getDataset().metaData, function(arc, index) {
|
||||
arc.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
},
|
||||
|
||||
setHoverStyle: function(arc) {
|
||||
var dataset = this.chart.data.datasets[arc._datasetIndex];
|
||||
var index = arc._index;
|
||||
|
||||
arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, arc._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(arc) {
|
||||
var dataset = this.chart.data.datasets[arc._datasetIndex];
|
||||
var index = arc._index;
|
||||
|
||||
arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor);
|
||||
arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor);
|
||||
arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth);
|
||||
},
|
||||
|
||||
calculateCircumference: function(value) {
|
||||
if (this.getDataset().total > 0) {
|
||||
return (Math.PI * 2) * (value / this.getDataset().total);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
updateScaleRange: function() {
|
||||
helpers.extend(this.chart.scale, {
|
||||
size: helpers.min([this.chart.width, this.chart.height]),
|
||||
xCenter: this.chart.width / 2,
|
||||
yCenter: this.chart.height / 2
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return;
|
||||
|
||||
|
||||
Chart.Type.extend({});
|
||||
|
||||
}).call(this);
|
||||
@@ -1,270 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
|
||||
Chart.defaults.radar = {
|
||||
scale: {
|
||||
type: "radialLinear",
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0, // no bezier in radar
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Chart.controllers.radar = function(chart, datasetIndex) {
|
||||
this.initialize.call(this, chart, datasetIndex);
|
||||
};
|
||||
|
||||
|
||||
helpers.extend(Chart.controllers.radar.prototype, {
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
this.chart = chart;
|
||||
this.index = datasetIndex;
|
||||
this.linkScales();
|
||||
this.addElements();
|
||||
},
|
||||
updateIndex: function(datasetIndex) {
|
||||
this.index = datasetIndex;
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
// No need. Single scale only
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
|
||||
this.getDataset().metaDataset = this.getDataset().metaDataset || new Chart.elements.Line({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_points: this.getDataset().metaData,
|
||||
_loop: true
|
||||
});
|
||||
|
||||
helpers.each(this.getDataset().data, function(value, index) {
|
||||
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
_model: {
|
||||
x: 0, //xScale.getPixelForValue(null, index, true),
|
||||
y: 0, //this.chartArea.bottom,
|
||||
},
|
||||
});
|
||||
}, this);
|
||||
},
|
||||
addElementAndReset: function(index) {
|
||||
this.getDataset().metaData = this.getDataset().metaData || [];
|
||||
var point = new Chart.elements.Point({
|
||||
_chart: this.chart.chart,
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
});
|
||||
|
||||
// Reset the point
|
||||
this.updateElement(point, index, true);
|
||||
|
||||
// Add to the points array
|
||||
this.getDataset().metaData.splice(index, 0, point);
|
||||
|
||||
// Make sure bezier control points are updated
|
||||
this.updateBezierControlPoints();
|
||||
},
|
||||
removeElement: function(index) {
|
||||
this.getDataset().metaData.splice(index, 1);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
buildOrUpdateElements: function buildOrUpdateElements() {
|
||||
// Handle the number of data points changing
|
||||
var numData = this.getDataset().data.length;
|
||||
var numPoints = this.getDataset().metaData.length;
|
||||
|
||||
// Make sure that we handle number of datapoints changing
|
||||
if (numData < numPoints) {
|
||||
// Remove excess bars for data points that have been removed
|
||||
this.getDataset().metaData.splice(numData, numPoints - numData)
|
||||
} else if (numData > numPoints) {
|
||||
// Add new elements
|
||||
for (var index = numPoints; index < numData; ++index) {
|
||||
this.addElementAndReset(index);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update: function update(reset) {
|
||||
|
||||
var line = this.getDataset().metaDataset;
|
||||
var points = this.getDataset().metaData;
|
||||
|
||||
var scale = this.chart.scale;
|
||||
var scaleBase;
|
||||
|
||||
if (scale.min < 0 && scale.max < 0) {
|
||||
scaleBase = scale.getPointPositionForValue(0, scale.max);
|
||||
} else if (scale.min > 0 && scale.max > 0) {
|
||||
scaleBase = scale.getPointPositionForValue(0, scale.min);
|
||||
} else {
|
||||
scaleBase = scale.getPointPositionForValue(0, 0);
|
||||
}
|
||||
|
||||
helpers.extend(this.getDataset().metaDataset, {
|
||||
// Utility
|
||||
_datasetIndex: this.index,
|
||||
// Data
|
||||
_children: this.getDataset().metaData,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: this.getDataset().tension || this.chart.options.elements.line.tension,
|
||||
backgroundColor: this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor,
|
||||
borderWidth: this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth,
|
||||
borderColor: this.getDataset().borderColor || this.chart.options.elements.line.borderColor,
|
||||
fill: this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill, // use the value from the this.getDataset() if it was provided. else fall back to the default
|
||||
skipNull: this.getDataset().skipNull !== undefined ? this.getDataset().skipNull : this.chart.options.elements.line.skipNull,
|
||||
drawNull: this.getDataset().drawNull !== undefined ? this.getDataset().drawNull : this.chart.options.elements.line.drawNull,
|
||||
|
||||
// Scale
|
||||
scaleTop: scale.top,
|
||||
scaleBottom: scale.bottom,
|
||||
scaleZero: scaleBase,
|
||||
},
|
||||
});
|
||||
|
||||
this.getDataset().metaDataset.pivot();
|
||||
|
||||
// Update Points
|
||||
helpers.each(points, function(point, index) {
|
||||
this.updateElement(point, index, reset);
|
||||
}, this);
|
||||
|
||||
|
||||
// Update bezier control points
|
||||
this.updateBezierControlPoints();
|
||||
},
|
||||
updateElement: function(point, index, reset) {
|
||||
var pointPosition = this.chart.scale.getPointPositionForValue(index, this.getDataset().data[index]);
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_datasetIndex: this.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: reset ? this.chart.scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: reset ? this.chart.scale.yCenter : pointPosition.y,
|
||||
|
||||
// Appearance
|
||||
tension: point.custom && point.custom.tension ? point.custom.tension : this.chart.options.elements.line.tension,
|
||||
radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.getDataset().pointRadius, index, this.chart.options.elements.point.radius),
|
||||
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor),
|
||||
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor),
|
||||
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth),
|
||||
skip: point.custom && point.custom.skip ? point.custom.skip : this.getDataset().data[index] === null,
|
||||
|
||||
// Tooltip
|
||||
hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius),
|
||||
},
|
||||
});
|
||||
},
|
||||
updateBezierControlPoints: function() {
|
||||
helpers.each(this.getDataset().metaData, function(point, index) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(this.getDataset().metaData, index, true)._model,
|
||||
point._model,
|
||||
helpers.nextItem(this.getDataset().metaData, index, true)._model,
|
||||
point._model.tension
|
||||
);
|
||||
|
||||
point._model.controlPointPreviousX = controlPoints.previous.x;
|
||||
point._model.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap outer bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chart.chartArea.bottom) {
|
||||
point._model.controlPointNextY = this.chart.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chart.chartArea.top) {
|
||||
point._model.controlPointNextY = this.chart.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chart.chartArea.bottom) {
|
||||
point._model.controlPointPreviousY = this.chart.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chart.chartArea.top) {
|
||||
point._model.controlPointPreviousY = this.chart.chartArea.top;
|
||||
} else {
|
||||
point._model.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
|
||||
// Transition Point Locations
|
||||
helpers.each(this.getDataset().metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
}, this);
|
||||
|
||||
// Transition and Draw the line
|
||||
this.getDataset().metaDataset.transition(easingDecimal).draw();
|
||||
|
||||
// Draw the points
|
||||
helpers.each(this.getDataset().metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
},
|
||||
|
||||
setHoverStyle: function(point) {
|
||||
// Point
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var index = point._index;
|
||||
|
||||
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
|
||||
point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
|
||||
point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, point._model.borderWidth);
|
||||
},
|
||||
|
||||
removeHoverStyle: function(point) {
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var index = point._index;
|
||||
|
||||
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius);
|
||||
point._model.backgroundColor = point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor);
|
||||
point._model.borderColor = point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor);
|
||||
point._model.borderWidth = point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,112 +0,0 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.animation = {
|
||||
duration: 1000,
|
||||
easing: "easeOutQuart",
|
||||
onProgress: function() {},
|
||||
onComplete: function() {},
|
||||
};
|
||||
|
||||
Chart.Animation = Chart.Element.extend({
|
||||
currentStep: null, // the current animation step
|
||||
numSteps: 60, // default number of steps
|
||||
easing: "", // the easing to use for this animation
|
||||
render: null, // render function used by the animation service
|
||||
|
||||
onAnimationProgress: null, // user specified callback to fire on each step of the animation
|
||||
onAnimationComplete: null, // user specified callback to fire when the animation finishes
|
||||
});
|
||||
|
||||
Chart.animationService = {
|
||||
frameDuration: 17,
|
||||
animations: [],
|
||||
dropFrames: 0,
|
||||
addAnimation: function(chartInstance, animationObject, duration, lazy) {
|
||||
|
||||
if (!lazy) {
|
||||
chartInstance.animating = true;
|
||||
}
|
||||
|
||||
for (var index = 0; index < this.animations.length; ++index) {
|
||||
if (this.animations[index].chartInstance === chartInstance) {
|
||||
// replacing an in progress animation
|
||||
this.animations[index].animationObject = animationObject;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.animations.push({
|
||||
chartInstance: chartInstance,
|
||||
animationObject: animationObject
|
||||
});
|
||||
|
||||
// If there are no animations queued, manually kickstart a digest, for lack of a better word
|
||||
if (this.animations.length == 1) {
|
||||
helpers.requestAnimFrame.call(window, this.digestWrapper);
|
||||
}
|
||||
},
|
||||
// Cancel the animation for a given chart instance
|
||||
cancelAnimation: function(chartInstance) {
|
||||
var index = helpers.findNextWhere(this.animations, function(animationWrapper) {
|
||||
return animationWrapper.chartInstance === chartInstance;
|
||||
});
|
||||
|
||||
if (index) {
|
||||
this.animations.splice(index, 1);
|
||||
chartInstance.animating = false;
|
||||
}
|
||||
},
|
||||
// calls startDigest with the proper context
|
||||
digestWrapper: function() {
|
||||
Chart.animationService.startDigest.call(Chart.animationService);
|
||||
},
|
||||
startDigest: function() {
|
||||
|
||||
var startTime = Date.now();
|
||||
var framesToDrop = 0;
|
||||
|
||||
if (this.dropFrames > 1) {
|
||||
framesToDrop = Math.floor(this.dropFrames);
|
||||
this.dropFrames = this.dropFrames % 1;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.animations.length; i++) {
|
||||
|
||||
if (this.animations[i].animationObject.currentStep === null) {
|
||||
this.animations[i].animationObject.currentStep = 0;
|
||||
}
|
||||
this.animations[i].animationObject.currentStep += 1 + framesToDrop;
|
||||
if (this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps) {
|
||||
this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
|
||||
}
|
||||
|
||||
this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
|
||||
|
||||
if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps) {
|
||||
// executed the last frame. Remove the animation.
|
||||
this.animations[i].chartInstance.animating = false;
|
||||
this.animations.splice(i, 1);
|
||||
// Keep the index in place to offset the splice
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
var endTime = Date.now();
|
||||
var dropFrames = (endTime - startTime) / this.frameDuration;
|
||||
|
||||
this.dropFrames += dropFrames;
|
||||
|
||||
// Do we have more stuff to animate?
|
||||
if (this.animations.length > 0) {
|
||||
helpers.requestAnimFrame.call(window, this.digestWrapper);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
@@ -1,485 +0,0 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
|
||||
//Create a dictionary of chart types, to allow for extension of existing types
|
||||
Chart.types = {};
|
||||
|
||||
//Store a reference to each instance - allowing us to globally resize chart instances on window resize.
|
||||
//Destroy method on the chart will remove the instance of the chart from this reference.
|
||||
Chart.instances = {};
|
||||
|
||||
// Controllers available for dataset visualization eg. bar, line, slice, etc.
|
||||
Chart.controllers = {};
|
||||
|
||||
// The main controller of a chart
|
||||
Chart.Controller = function(instance) {
|
||||
|
||||
this.chart = instance;
|
||||
this.config = instance.config;
|
||||
this.data = this.config.data;
|
||||
this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {});
|
||||
this.id = helpers.uid();
|
||||
|
||||
//Add the chart instance to the global namespace
|
||||
Chart.instances[this.id] = this;
|
||||
|
||||
if (this.options.responsive) {
|
||||
// Silent resize before chart draws
|
||||
this.resize(true);
|
||||
}
|
||||
|
||||
this.initialize.call(this);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
helpers.extend(Chart.Controller.prototype, {
|
||||
|
||||
initialize: function initialize() {
|
||||
|
||||
// TODO
|
||||
// If BeforeInit(this) doesn't return false, proceed
|
||||
|
||||
this.bindEvents();
|
||||
|
||||
// Make sure controllers are built first so that each dataset is bound to an axis before the scales
|
||||
// are built
|
||||
this.ensureScalesHaveIDs();
|
||||
this.buildOrUpdateControllers();
|
||||
this.buildScales();
|
||||
this.resetElements();
|
||||
this.initToolTip();
|
||||
this.update();
|
||||
|
||||
// TODO
|
||||
// If AfterInit(this) doesn't return false, proceed
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
clear: function clear() {
|
||||
helpers.clear(this.chart);
|
||||
return this;
|
||||
},
|
||||
|
||||
stop: function stop() {
|
||||
// Stops any current animation loop occuring
|
||||
Chart.animationService.cancelAnimation(this);
|
||||
return this;
|
||||
},
|
||||
|
||||
resize: function resize(silent) {
|
||||
this.stop();
|
||||
var canvas = this.chart.canvas;
|
||||
var newWidth = helpers.getMaximumWidth(this.chart.canvas);
|
||||
var newHeight = (this.options.maintainAspectRatio && isNaN(this.chart.aspectRatio) === false && isFinite(this.chart.aspectRatio) && this.chart.aspectRatio !== 0) ? newWidth / this.chart.aspectRatio : helpers.getMaximumHeight(this.chart.canvas);
|
||||
|
||||
canvas.width = this.chart.width = newWidth;
|
||||
canvas.height = this.chart.height = newHeight;
|
||||
|
||||
helpers.retinaScale(this.chart);
|
||||
|
||||
if (!silent) {
|
||||
this.update(this.options.responsiveAnimationDuration);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
ensureScalesHaveIDs: function ensureScalesHaveIDs() {
|
||||
var defaultXAxisID = 'x-axis-';
|
||||
var defaultYAxisID = 'y-axis-';
|
||||
|
||||
if (this.options.scales) {
|
||||
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
|
||||
helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) {
|
||||
xAxisOptions.id = xAxisOptions.id || (defaultXAxisID + index);
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
|
||||
// Build the y axes
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) {
|
||||
yAxisOptions.id = yAxisOptions.id || (defaultYAxisID + index);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
},
|
||||
buildScales: function buildScales() {
|
||||
// Map of scale ID to scale object so we can lookup later
|
||||
this.scales = {};
|
||||
|
||||
// Build the x axes
|
||||
if (this.options.scales) {
|
||||
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
|
||||
helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) {
|
||||
var ScaleClass = Chart.scaleService.getScaleConstructor(xAxisOptions.type);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: xAxisOptions,
|
||||
data: this.data,
|
||||
id: xAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
|
||||
// Build the y axes
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) {
|
||||
var ScaleClass = Chart.scaleService.getScaleConstructor(yAxisOptions.type);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: yAxisOptions,
|
||||
data: this.data,
|
||||
id: yAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
if (this.options.scale) {
|
||||
// Build radial axes
|
||||
var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scale.type);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: this.options.scale,
|
||||
data: this.data,
|
||||
chart: this.chart,
|
||||
});
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
this.scales['radialScale'] = scale;
|
||||
}
|
||||
|
||||
Chart.scaleService.update(this, this.chart.width, this.chart.height);
|
||||
},
|
||||
|
||||
buildOrUpdateControllers: function buildOrUpdateControllers(resetNewControllers) {
|
||||
var types = [];
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
if (!dataset.type) {
|
||||
dataset.type = this.config.type;
|
||||
}
|
||||
var type = dataset.type;
|
||||
types.push(type);
|
||||
if (dataset.controller) {
|
||||
dataset.controller.updateIndex(datasetIndex);
|
||||
return;
|
||||
}
|
||||
dataset.controller = new Chart.controllers[type](this, datasetIndex);
|
||||
|
||||
if (resetNewControllers) {
|
||||
dataset.controller.reset();
|
||||
}
|
||||
}, this);
|
||||
if (types.length > 1) {
|
||||
for (var i = 1; i < types.length; i++) {
|
||||
if (types[i] != types[i - 1]) {
|
||||
this.isCombo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
resetElements: function resetElements() {
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.controller.reset();
|
||||
}, this);
|
||||
},
|
||||
|
||||
update: function update(animationDuration, lazy) {
|
||||
Chart.scaleService.update(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Make sure dataset controllers are updated and new controllers are reset
|
||||
this.buildOrUpdateControllers(true);
|
||||
|
||||
// Make sure all dataset controllers have correct meta data counts
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.controller.buildOrUpdateElements();
|
||||
}, this);
|
||||
|
||||
// This will loop through any data and do the appropriate element update for the type
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.controller.update();
|
||||
}, this);
|
||||
this.render(animationDuration, lazy);
|
||||
},
|
||||
|
||||
render: function render(duration, lazy) {
|
||||
|
||||
if ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration == 'undefined' && this.options.animation.duration !== 0)) {
|
||||
var animation = new Chart.Animation();
|
||||
animation.numSteps = (duration || this.options.animation.duration) / 16.66; //60 fps
|
||||
animation.easing = this.options.animation.easing;
|
||||
|
||||
// render function
|
||||
animation.render = function(chartInstance, animationObject) {
|
||||
var easingFunction = helpers.easingEffects[animationObject.easing];
|
||||
var stepDecimal = animationObject.currentStep / animationObject.numSteps;
|
||||
var easeDecimal = easingFunction(stepDecimal);
|
||||
|
||||
chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
|
||||
};
|
||||
|
||||
// user events
|
||||
animation.onAnimationProgress = this.options.onAnimationProgress;
|
||||
animation.onAnimationComplete = this.options.onAnimationComplete;
|
||||
|
||||
Chart.animationService.addAnimation(this, animation, duration, lazy);
|
||||
} else {
|
||||
this.draw();
|
||||
if (this.options.onAnimationComplete && this.options.onAnimationComplete.call) {
|
||||
this.options.onAnimationComplete.call(this);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
draw: function(ease) {
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
if (this.scale) {
|
||||
this.scale.draw();
|
||||
}
|
||||
|
||||
// Draw each dataset via its respective controller (reversed to support proper line stacking)
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.controller.draw(ease);
|
||||
}, this);
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
|
||||
// Get the single element that was clicked on
|
||||
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
|
||||
getElementAtEvent: function(e) {
|
||||
|
||||
var eventPosition = helpers.getRelativePosition(e, this.chart);
|
||||
var elementsArray = [];
|
||||
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
helpers.each(dataset.metaData, function(element, index) {
|
||||
if (element.inRange(eventPosition.x, eventPosition.y)) {
|
||||
elementsArray.push(element);
|
||||
return elementsArray;
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
return elementsArray;
|
||||
},
|
||||
|
||||
getElementsAtEvent: function(e) {
|
||||
var eventPosition = helpers.getRelativePosition(e, this.chart);
|
||||
var elementsArray = [];
|
||||
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
helpers.each(dataset.metaData, function(element, index) {
|
||||
if (element.inLabelRange(eventPosition.x, eventPosition.y)) {
|
||||
elementsArray.push(element);
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
return elementsArray;
|
||||
},
|
||||
|
||||
getDatasetAtEvent: function(e) {
|
||||
var eventPosition = helpers.getRelativePosition(e, this.chart);
|
||||
var elementsArray = [];
|
||||
|
||||
for (var datasetIndex = 0; datasetIndex < this.data.datasets.length; datasetIndex++) {
|
||||
for (var elementIndex = 0; elementIndex < this.data.datasets[datasetIndex].metaData.length; elementIndex++) {
|
||||
if (this.data.datasets[datasetIndex].metaData[elementIndex].inLabelRange(eventPosition.x, eventPosition.y)) {
|
||||
helpers.each(this.data.datasets[datasetIndex].metaData, function(element, index) {
|
||||
elementsArray.push(element);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return elementsArray.length ? elementsArray : [];
|
||||
},
|
||||
|
||||
generateLegend: function generateLegend() {
|
||||
return helpers.template(this.options.legendTemplate, this);
|
||||
},
|
||||
|
||||
destroy: function destroy() {
|
||||
this.clear();
|
||||
helpers.unbindEvents(this, this.events);
|
||||
helpers.removeResizeListener(this.chart.canvas.parentNode);
|
||||
|
||||
// Reset canvas height/width attributes
|
||||
var canvas = this.chart.canvas;
|
||||
canvas.width = this.chart.width;
|
||||
canvas.height = this.chart.height;
|
||||
|
||||
// if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here
|
||||
if (this.chart.originalDevicePixelRatio !== undefined) {
|
||||
this.chart.ctx.scale(1 / this.chart.originalDevicePixelRatio, 1 / this.chart.originalDevicePixelRatio);
|
||||
}
|
||||
|
||||
// Reset to the old style since it may have been changed by the device pixel ratio changes
|
||||
canvas.style.width = this.chart.originalCanvasStyleWidth;
|
||||
canvas.style.height = this.chart.originalCanvasStyleHeight;
|
||||
|
||||
delete Chart.instances[this.id];
|
||||
},
|
||||
|
||||
toBase64Image: function toBase64Image() {
|
||||
return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
|
||||
},
|
||||
|
||||
initToolTip: function initToolTip() {
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
},
|
||||
|
||||
bindEvents: function bindEvents() {
|
||||
helpers.bindEvents(this, this.options.events, function(evt) {
|
||||
this.eventHandler(evt);
|
||||
});
|
||||
},
|
||||
eventHandler: function eventHandler(e) {
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
if (e.type == 'mouseout') {
|
||||
this.active = [];
|
||||
} else {
|
||||
this.active = function() {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
}
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.hover.onHover) {
|
||||
this.options.hover.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
var index;
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
this.data.datasets[this.lastActive[0]._datasetIndex].controller.removeHoverStyle(this.lastActive[0], this.lastActive[0]._datasetIndex, this.lastActive[0]._index);
|
||||
break;
|
||||
case 'label':
|
||||
case 'dataset':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
this.data.datasets[this.lastActive[i]._datasetIndex].controller.removeHoverStyle(this.lastActive[i], this.lastActive[i]._datasetIndex, this.lastActive[i]._index);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hover.mode) {
|
||||
switch (this.options.hover.mode) {
|
||||
case 'single':
|
||||
this.data.datasets[this.active[0]._datasetIndex].controller.setHoverStyle(this.active[0]);
|
||||
break;
|
||||
case 'label':
|
||||
case 'dataset':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
this.data.datasets[this.active[i]._datasetIndex].controller.setHoverStyle(this.active[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.tooltips.enabled || this.options.tooltips.custom) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
this.tooltip._model.opacity = 1;
|
||||
|
||||
helpers.extend(this.tooltip, {
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
this.tooltip._model.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
|
||||
// We only need to render at this point. Updating will cause scales to be recomputed generating flicker & using more
|
||||
// memory than necessary.
|
||||
this.render(this.options.hover.animationDuration, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -1,92 +0,0 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.elements = {};
|
||||
|
||||
Chart.Element = function(configuration) {
|
||||
helpers.extend(this, configuration);
|
||||
this.initialize.apply(this, arguments);
|
||||
};
|
||||
helpers.extend(Chart.Element.prototype, {
|
||||
initialize: function() {},
|
||||
pivot: function() {
|
||||
if (!this._view) {
|
||||
this._view = helpers.clone(this._model);
|
||||
}
|
||||
this._start = helpers.clone(this._view);
|
||||
return this;
|
||||
},
|
||||
transition: function(ease) {
|
||||
if (!this._view) {
|
||||
this._view = helpers.clone(this._model);
|
||||
}
|
||||
if (!this._start) {
|
||||
this.pivot();
|
||||
}
|
||||
|
||||
helpers.each(this._model, function(value, key) {
|
||||
|
||||
if (key[0] === '_' || !this._model.hasOwnProperty(key)) {
|
||||
// Only non-underscored properties
|
||||
}
|
||||
|
||||
// Init if doesn't exist
|
||||
else if (!this._view[key]) {
|
||||
if (typeof value === 'number') {
|
||||
this._view[key] = value * ease;
|
||||
} else {
|
||||
this._view[key] = value || null;
|
||||
}
|
||||
}
|
||||
|
||||
// No unnecessary computations
|
||||
else if (this._model[key] === this._view[key]) {
|
||||
// It's the same! Woohoo!
|
||||
}
|
||||
|
||||
// Color transitions if possible
|
||||
else if (typeof value === 'string') {
|
||||
try {
|
||||
var color = helpers.color(this._start[key]).mix(helpers.color(this._model[key]), ease);
|
||||
this._view[key] = color.rgbString();
|
||||
} catch (err) {
|
||||
this._view[key] = value;
|
||||
}
|
||||
}
|
||||
// Number transitions
|
||||
else if (typeof value === 'number') {
|
||||
var startVal = this._start[key] !== undefined ? this._start[key] : 0;
|
||||
this._view[key] = ((this._model[key] - startVal) * ease) + startVal;
|
||||
}
|
||||
// Everything else
|
||||
else {
|
||||
this._view[key] = value;
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
if (ease === 1) {
|
||||
delete this._start;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
return {
|
||||
x: this._model.x,
|
||||
y: this._model.y
|
||||
};
|
||||
},
|
||||
hasValue: function() {
|
||||
return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
|
||||
}
|
||||
});
|
||||
|
||||
Chart.Element.extend = helpers.inherits;
|
||||
|
||||
}).call(this);
|
||||
@@ -1,880 +0,0 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart;
|
||||
|
||||
//Global Chart helpers object for utility methods and classes
|
||||
var helpers = Chart.helpers = {};
|
||||
|
||||
//-- Basic js utility methods
|
||||
var each = helpers.each = function(loopable, callback, self, reverse) {
|
||||
var additionalArgs = Array.prototype.slice.call(arguments, 3);
|
||||
// Check to see if null or undefined firstly.
|
||||
if (loopable) {
|
||||
if (loopable.length === +loopable.length) {
|
||||
var i;
|
||||
if (reverse) {
|
||||
for (i = loopable.length - 1; i >= 0; i--) {
|
||||
callback.apply(self, [loopable[i], i].concat(additionalArgs));
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < loopable.length; i++) {
|
||||
callback.apply(self, [loopable[i], i].concat(additionalArgs));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var item in loopable) {
|
||||
callback.apply(self, [loopable[item], item].concat(additionalArgs));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
clone = helpers.clone = function(obj) {
|
||||
var objClone = {};
|
||||
each(obj, function(value, key) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (helpers.isArray(value)) {
|
||||
objClone[key] = value.slice(0);
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
objClone[key] = clone(value);
|
||||
} else {
|
||||
objClone[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
return objClone;
|
||||
},
|
||||
extend = helpers.extend = function(base) {
|
||||
each(Array.prototype.slice.call(arguments, 1), function(extensionObject) {
|
||||
each(extensionObject, function(value, key) {
|
||||
if (extensionObject.hasOwnProperty(key)) {
|
||||
base[key] = value;
|
||||
}
|
||||
});
|
||||
});
|
||||
return base;
|
||||
},
|
||||
// Need a special merge function to chart configs since they are now grouped
|
||||
configMerge = helpers.configMerge = function(_base) {
|
||||
var base = clone(_base);
|
||||
helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
|
||||
helpers.each(extension, function(value, key) {
|
||||
if (extension.hasOwnProperty(key)) {
|
||||
if (key === 'scales') {
|
||||
// Scale config merging is complex. Add out own function here for that
|
||||
base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value);
|
||||
|
||||
} else if (key === 'scale') {
|
||||
// Used in polar area & radar charts since there is only one scale
|
||||
base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value);
|
||||
} else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) {
|
||||
// In this case we have an array of objects replacing another array. Rather than doing a strict replace,
|
||||
// merge. This allows easy scale option merging
|
||||
var baseArray = base[key];
|
||||
|
||||
helpers.each(value, function(valueObj, index) {
|
||||
|
||||
if (index < baseArray.length) {
|
||||
if (typeof baseArray[index] == 'object' && baseArray[index] !== null && typeof valueObj == 'object' && valueObj !== null) {
|
||||
// Two objects are coming together. Do a merge of them.
|
||||
baseArray[index] = helpers.configMerge(baseArray[index], valueObj);
|
||||
} else {
|
||||
// Just overwrite in this case since there is nothing to merge
|
||||
baseArray[index] = valueObj;
|
||||
}
|
||||
} else {
|
||||
baseArray.push(valueObj); // nothing to merge
|
||||
}
|
||||
});
|
||||
|
||||
} else if (base.hasOwnProperty(key) && typeof base[key] == "object" && base[key] !== null && typeof value == "object") {
|
||||
// If we are overwriting an object with an object, do a merge of the properties.
|
||||
base[key] = helpers.configMerge(base[key], value);
|
||||
|
||||
} else {
|
||||
// can just overwrite the value in this case
|
||||
base[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return base;
|
||||
},
|
||||
extendDeep = helpers.extendDeep = function(_base) {
|
||||
return _extendDeep.apply(this, arguments);
|
||||
|
||||
function _extendDeep(dst) {
|
||||
helpers.each(arguments, function(obj) {
|
||||
if (obj !== dst) {
|
||||
helpers.each(obj, function(value, key) {
|
||||
if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
|
||||
_extendDeep(dst[key], value);
|
||||
} else {
|
||||
dst[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return dst;
|
||||
}
|
||||
},
|
||||
scaleMerge = helpers.scaleMerge = function(_base, extension) {
|
||||
var base = clone(_base);
|
||||
|
||||
helpers.each(extension, function(value, key) {
|
||||
if (extension.hasOwnProperty(key)) {
|
||||
if (key === 'xAxes' || key === 'yAxes') {
|
||||
// These properties are arrays of items
|
||||
if (base.hasOwnProperty(key)) {
|
||||
helpers.each(value, function(valueObj, index) {
|
||||
if (index >= base[key].length || !base[key][index].type) {
|
||||
base[key].push(helpers.configMerge(valueObj.type ? Chart.scaleService.getScaleDefaults(valueObj.type) : {}, valueObj));
|
||||
} else if (valueObj.type !== base[key][index].type) {
|
||||
// Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults
|
||||
base[key][index] = helpers.configMerge(base[key][index], valueObj.type ? Chart.scaleService.getScaleDefaults(valueObj.type) : {}, valueObj)
|
||||
} else {
|
||||
// Type is the same
|
||||
base[key][index] = helpers.configMerge(base[key][index], valueObj);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
base[key] = [];
|
||||
helpers.each(value, function(valueObj) {
|
||||
base[key].push(helpers.configMerge(valueObj.type ? Chart.scaleService.getScaleDefaults(valueObj.type) : {}, valueObj));
|
||||
});
|
||||
}
|
||||
} else if (base.hasOwnProperty(key) && typeof base[key] == "object" && base[key] !== null && typeof value == "object") {
|
||||
// If we are overwriting an object with an object, do a merge of the properties.
|
||||
base[key] = helpers.configMerge(base[key], value);
|
||||
|
||||
} else {
|
||||
// can just overwrite the value in this case
|
||||
base[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return base;
|
||||
},
|
||||
getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
|
||||
if (value === undefined || value === null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (helpers.isArray(value)) {
|
||||
return index < value.length ? value[index] : defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
indexOf = helpers.indexOf = function(arrayToSearch, item) {
|
||||
if (Array.prototype.indexOf) {
|
||||
return arrayToSearch.indexOf(item);
|
||||
} else {
|
||||
for (var i = 0; i < arrayToSearch.length; i++) {
|
||||
if (arrayToSearch[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
where = helpers.where = function(collection, filterCallback) {
|
||||
var filtered = [];
|
||||
|
||||
helpers.each(collection, function(item) {
|
||||
if (filterCallback(item)) {
|
||||
filtered.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return filtered;
|
||||
},
|
||||
findNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
|
||||
// Default to start of the array
|
||||
if (startIndex === undefined || startIndex === null) {
|
||||
startIndex = -1;
|
||||
}
|
||||
for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
|
||||
var currentItem = arrayToSearch[i];
|
||||
if (filterCallback(currentItem)) {
|
||||
return currentItem;
|
||||
}
|
||||
}
|
||||
},
|
||||
findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
|
||||
// Default to end of the array
|
||||
if (startIndex === undefined || startIndex === null) {
|
||||
startIndex = arrayToSearch.length;
|
||||
}
|
||||
for (var i = startIndex - 1; i >= 0; i--) {
|
||||
var currentItem = arrayToSearch[i];
|
||||
if (filterCallback(currentItem)) {
|
||||
return currentItem;
|
||||
}
|
||||
}
|
||||
},
|
||||
inherits = helpers.inherits = function(extensions) {
|
||||
//Basic javascript inheritance based on the model created in Backbone.js
|
||||
var parent = this;
|
||||
var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() {
|
||||
return parent.apply(this, arguments);
|
||||
};
|
||||
|
||||
var Surrogate = function() {
|
||||
this.constructor = ChartElement;
|
||||
};
|
||||
Surrogate.prototype = parent.prototype;
|
||||
ChartElement.prototype = new Surrogate();
|
||||
|
||||
ChartElement.extend = inherits;
|
||||
|
||||
if (extensions) extend(ChartElement.prototype, extensions);
|
||||
|
||||
ChartElement.__super__ = parent.prototype;
|
||||
|
||||
return ChartElement;
|
||||
},
|
||||
noop = helpers.noop = function() {},
|
||||
uid = helpers.uid = (function() {
|
||||
var id = 0;
|
||||
return function() {
|
||||
return "chart-" + id++;
|
||||
};
|
||||
})(),
|
||||
warn = helpers.warn = function(str) {
|
||||
//Method for warning of errors
|
||||
if (window.console && typeof window.console.warn === "function") console.warn(str);
|
||||
},
|
||||
amd = helpers.amd = (typeof define === 'function' && define.amd),
|
||||
//-- Math methods
|
||||
isNumber = helpers.isNumber = function(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
},
|
||||
max = helpers.max = function(array) {
|
||||
return Math.max.apply(Math, array);
|
||||
},
|
||||
min = helpers.min = function(array) {
|
||||
return Math.min.apply(Math, array);
|
||||
},
|
||||
sign = helpers.sign = function(x) {
|
||||
if (Math.sign) {
|
||||
return Math.sign(x);
|
||||
} else {
|
||||
x = +x; // convert to a number
|
||||
if (x === 0 || isNaN(x)) {
|
||||
return x;
|
||||
}
|
||||
return x > 0 ? 1 : -1;
|
||||
}
|
||||
},
|
||||
log10 = helpers.log10 = function(x) {
|
||||
if (Math.log10) {
|
||||
return Math.log10(x)
|
||||
} else {
|
||||
return Math.log(x) / Math.LN10;
|
||||
}
|
||||
},
|
||||
getDecimalPlaces = helpers.getDecimalPlaces = function(num) {
|
||||
if (num % 1 !== 0 && isNumber(num)) {
|
||||
var s = num.toString();
|
||||
if (s.indexOf("e-") < 0) {
|
||||
// no exponent, e.g. 0.01
|
||||
return s.split(".")[1].length;
|
||||
} else if (s.indexOf(".") < 0) {
|
||||
// no decimal point, e.g. 1e-9
|
||||
return parseInt(s.split("e-")[1]);
|
||||
} else {
|
||||
// exponent and decimal point, e.g. 1.23e-9
|
||||
var parts = s.split(".")[1].split("e-");
|
||||
return parts[0].length + parseInt(parts[1]);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
toRadians = helpers.toRadians = function(degrees) {
|
||||
return degrees * (Math.PI / 180);
|
||||
},
|
||||
toDegrees = helpers.toDegrees = function(radians) {
|
||||
return radians * (180 / Math.PI);
|
||||
},
|
||||
// Gets the angle from vertical upright to the point about a centre.
|
||||
getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
|
||||
var distanceFromXCenter = anglePoint.x - centrePoint.x,
|
||||
distanceFromYCenter = anglePoint.y - centrePoint.y,
|
||||
radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
|
||||
|
||||
var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
|
||||
|
||||
if (angle < (-0.5 * Math.PI)) {
|
||||
angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
|
||||
}
|
||||
|
||||
return {
|
||||
angle: angle,
|
||||
distance: radialDistanceFromCenter
|
||||
};
|
||||
},
|
||||
aliasPixel = helpers.aliasPixel = function(pixelWidth) {
|
||||
return (pixelWidth % 2 === 0) ? 0 : 0.5;
|
||||
},
|
||||
splineCurve = helpers.splineCurve = function(FirstPoint, MiddlePoint, AfterPoint, t) {
|
||||
//Props to Rob Spencer at scaled innovation for his post on splining between points
|
||||
//http://scaledinnovation.com/analytics/splines/aboutSplines.html
|
||||
var d01 = Math.sqrt(Math.pow(MiddlePoint.x - FirstPoint.x, 2) + Math.pow(MiddlePoint.y - FirstPoint.y, 2)),
|
||||
d12 = Math.sqrt(Math.pow(AfterPoint.x - MiddlePoint.x, 2) + Math.pow(AfterPoint.y - MiddlePoint.y, 2)),
|
||||
fa = t * d01 / (d01 + d12), // scaling factor for triangle Ta
|
||||
fb = t * d12 / (d01 + d12);
|
||||
return {
|
||||
previous: {
|
||||
x: MiddlePoint.x - fa * (AfterPoint.x - FirstPoint.x),
|
||||
y: MiddlePoint.y - fa * (AfterPoint.y - FirstPoint.y)
|
||||
},
|
||||
next: {
|
||||
x: MiddlePoint.x + fb * (AfterPoint.x - FirstPoint.x),
|
||||
y: MiddlePoint.y + fb * (AfterPoint.y - FirstPoint.y)
|
||||
}
|
||||
};
|
||||
},
|
||||
nextItem = helpers.nextItem = function(collection, index, loop) {
|
||||
if (loop) {
|
||||
return index >= collection.length - 1 ? collection[0] : collection[index + 1];
|
||||
}
|
||||
|
||||
return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
|
||||
},
|
||||
previousItem = helpers.previousItem = function(collection, index, loop) {
|
||||
if (loop) {
|
||||
return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
|
||||
}
|
||||
return index <= 0 ? collection[0] : collection[index - 1];
|
||||
},
|
||||
// Implementation of the nice number algorithm used in determining where axis labels will go
|
||||
niceNum = helpers.niceNum = function(range, round) {
|
||||
var exponent = Math.floor(helpers.log10(range));
|
||||
var fraction = range / Math.pow(10, exponent);
|
||||
var niceFraction;
|
||||
|
||||
if (round) {
|
||||
if (fraction < 1.5) {
|
||||
niceFraction = 1;
|
||||
} else if (fraction < 3) {
|
||||
niceFraction = 2;
|
||||
} else if (fraction < 7) {
|
||||
niceFraction = 5;
|
||||
} else {
|
||||
niceFraction = 10;
|
||||
}
|
||||
} else {
|
||||
if (fraction <= 1.0) {
|
||||
niceFraction = 1;
|
||||
} else if (fraction <= 2) {
|
||||
niceFraction = 2;
|
||||
} else if (fraction <= 5) {
|
||||
niceFraction = 5;
|
||||
} else {
|
||||
niceFraction = 10;
|
||||
}
|
||||
}
|
||||
|
||||
return niceFraction * Math.pow(10, exponent);
|
||||
},
|
||||
/* jshint ignore:start */
|
||||
// Blows up jshint errors based on the new Function constructor
|
||||
//Templating methods
|
||||
//Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
|
||||
templateStringCache = {},
|
||||
template = helpers.template = function(templateString, valuesObject) {
|
||||
|
||||
// If templateString is function rather than string-template - call the function for valuesObject
|
||||
|
||||
if (templateString instanceof Function) {
|
||||
return templateString(valuesObject);
|
||||
}
|
||||
|
||||
function tmpl(str, data) {
|
||||
// Figure out if we're getting a template, or if we need to
|
||||
// load the template - and be sure to cache the result.
|
||||
var fn;
|
||||
|
||||
if (templateStringCache.hasOwnProperty(str)) {
|
||||
fn = templateStringCache[str];
|
||||
} else {
|
||||
// Generate a reusable function that will serve as a template
|
||||
// generator (and which will be cached).
|
||||
var functionCode = "var p=[],print=function(){p.push.apply(p,arguments);};" +
|
||||
|
||||
// Introduce the data as local variables using with(){}
|
||||
"with(obj){p.push('" +
|
||||
|
||||
// Convert the template into pure JavaScript
|
||||
str
|
||||
.replace(/[\r\t\n]/g, " ")
|
||||
.split("<%").join("\t")
|
||||
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
|
||||
.replace(/\t=(.*?)%>/g, "',$1,'")
|
||||
.split("\t").join("');")
|
||||
.split("%>").join("p.push('")
|
||||
.split("\r").join("\\'") +
|
||||
"');}return p.join('');";
|
||||
fn = new Function("obj", functionCode);
|
||||
|
||||
// Cache the result
|
||||
templateStringCache[str] = fn;
|
||||
}
|
||||
|
||||
// Provide some basic currying to the user
|
||||
return data ? fn(data) : fn;
|
||||
}
|
||||
return tmpl(templateString, valuesObject);
|
||||
},
|
||||
/* jshint ignore:end */
|
||||
//--Animation methods
|
||||
//Easing functions adapted from Robert Penner's easing equations
|
||||
//http://www.robertpenner.com/easing/
|
||||
easingEffects = helpers.easingEffects = {
|
||||
linear: function(t) {
|
||||
return t;
|
||||
},
|
||||
easeInQuad: function(t) {
|
||||
return t * t;
|
||||
},
|
||||
easeOutQuad: function(t) {
|
||||
return -1 * t * (t - 2);
|
||||
},
|
||||
easeInOutQuad: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t;
|
||||
}
|
||||
return -1 / 2 * ((--t) * (t - 2) - 1);
|
||||
},
|
||||
easeInCubic: function(t) {
|
||||
return t * t * t;
|
||||
},
|
||||
easeOutCubic: function(t) {
|
||||
return 1 * ((t = t / 1 - 1) * t * t + 1);
|
||||
},
|
||||
easeInOutCubic: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t;
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * t + 2);
|
||||
},
|
||||
easeInQuart: function(t) {
|
||||
return t * t * t * t;
|
||||
},
|
||||
easeOutQuart: function(t) {
|
||||
return -1 * ((t = t / 1 - 1) * t * t * t - 1);
|
||||
},
|
||||
easeInOutQuart: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t * t;
|
||||
}
|
||||
return -1 / 2 * ((t -= 2) * t * t * t - 2);
|
||||
},
|
||||
easeInQuint: function(t) {
|
||||
return 1 * (t /= 1) * t * t * t * t;
|
||||
},
|
||||
easeOutQuint: function(t) {
|
||||
return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
|
||||
},
|
||||
easeInOutQuint: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * t * t * t * t * t;
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
|
||||
},
|
||||
easeInSine: function(t) {
|
||||
return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
|
||||
},
|
||||
easeOutSine: function(t) {
|
||||
return 1 * Math.sin(t / 1 * (Math.PI / 2));
|
||||
},
|
||||
easeInOutSine: function(t) {
|
||||
return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
|
||||
},
|
||||
easeInExpo: function(t) {
|
||||
return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
|
||||
},
|
||||
easeOutExpo: function(t) {
|
||||
return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
|
||||
},
|
||||
easeInOutExpo: function(t) {
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * Math.pow(2, 10 * (t - 1));
|
||||
}
|
||||
return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
|
||||
},
|
||||
easeInCirc: function(t) {
|
||||
if (t >= 1) {
|
||||
return t;
|
||||
}
|
||||
return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
|
||||
},
|
||||
easeOutCirc: function(t) {
|
||||
return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
|
||||
},
|
||||
easeInOutCirc: function(t) {
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
|
||||
}
|
||||
return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
|
||||
},
|
||||
easeInElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1) == 1) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * 0.3;
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
|
||||
},
|
||||
easeOutElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1) == 1) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * 0.3;
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
|
||||
},
|
||||
easeInOutElastic: function(t) {
|
||||
var s = 1.70158;
|
||||
var p = 0;
|
||||
var a = 1;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((t /= 1 / 2) == 2) {
|
||||
return 1;
|
||||
}
|
||||
if (!p) {
|
||||
p = 1 * (0.3 * 1.5);
|
||||
}
|
||||
if (a < Math.abs(1)) {
|
||||
a = 1;
|
||||
s = p / 4;
|
||||
} else {
|
||||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||||
}
|
||||
if (t < 1) {
|
||||
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
|
||||
}
|
||||
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
|
||||
},
|
||||
easeInBack: function(t) {
|
||||
var s = 1.70158;
|
||||
return 1 * (t /= 1) * t * ((s + 1) * t - s);
|
||||
},
|
||||
easeOutBack: function(t) {
|
||||
var s = 1.70158;
|
||||
return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
|
||||
},
|
||||
easeInOutBack: function(t) {
|
||||
var s = 1.70158;
|
||||
if ((t /= 1 / 2) < 1) {
|
||||
return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
|
||||
}
|
||||
return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
|
||||
},
|
||||
easeInBounce: function(t) {
|
||||
return 1 - easingEffects.easeOutBounce(1 - t);
|
||||
},
|
||||
easeOutBounce: function(t) {
|
||||
if ((t /= 1) < (1 / 2.75)) {
|
||||
return 1 * (7.5625 * t * t);
|
||||
} else if (t < (2 / 2.75)) {
|
||||
return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
|
||||
} else if (t < (2.5 / 2.75)) {
|
||||
return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
|
||||
} else {
|
||||
return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
|
||||
}
|
||||
},
|
||||
easeInOutBounce: function(t) {
|
||||
if (t < 1 / 2) {
|
||||
return easingEffects.easeInBounce(t * 2) * 0.5;
|
||||
}
|
||||
return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
|
||||
}
|
||||
},
|
||||
//Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
|
||||
requestAnimFrame = helpers.requestAnimFrame = (function() {
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function(callback) {
|
||||
return window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})(),
|
||||
cancelAnimFrame = helpers.cancelAnimFrame = (function() {
|
||||
return window.cancelAnimationFrame ||
|
||||
window.webkitCancelAnimationFrame ||
|
||||
window.mozCancelAnimationFrame ||
|
||||
window.oCancelAnimationFrame ||
|
||||
window.msCancelAnimationFrame ||
|
||||
function(callback) {
|
||||
return window.clearTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})(),
|
||||
//-- DOM methods
|
||||
getRelativePosition = helpers.getRelativePosition = function(evt, chart) {
|
||||
var mouseX, mouseY;
|
||||
var e = evt.originalEvent || evt,
|
||||
canvas = evt.currentTarget || evt.srcElement,
|
||||
boundingRect = canvas.getBoundingClientRect();
|
||||
|
||||
if (e.touches) {
|
||||
mouseX = e.touches[0].clientX;
|
||||
mouseY = e.touches[0].clientY;
|
||||
|
||||
} else {
|
||||
mouseX = e.clientX;
|
||||
mouseY = e.clientY;
|
||||
}
|
||||
|
||||
// Scale mouse coordinates into canvas coordinates
|
||||
// by following the pattern laid out by 'jerryj' in the comments of
|
||||
// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
|
||||
|
||||
// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
|
||||
// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
|
||||
mouseX = Math.round((mouseX - boundingRect.left) / (boundingRect.right - boundingRect.left) * canvas.width / chart.currentDevicePixelRatio);
|
||||
mouseY = Math.round((mouseY - boundingRect.top) / (boundingRect.bottom - boundingRect.top) * canvas.height / chart.currentDevicePixelRatio);
|
||||
|
||||
return {
|
||||
x: mouseX,
|
||||
y: mouseY
|
||||
};
|
||||
|
||||
},
|
||||
addEvent = helpers.addEvent = function(node, eventType, method) {
|
||||
if (node.addEventListener) {
|
||||
node.addEventListener(eventType, method);
|
||||
} else if (node.attachEvent) {
|
||||
node.attachEvent("on" + eventType, method);
|
||||
} else {
|
||||
node["on" + eventType] = method;
|
||||
}
|
||||
},
|
||||
removeEvent = helpers.removeEvent = function(node, eventType, handler) {
|
||||
if (node.removeEventListener) {
|
||||
node.removeEventListener(eventType, handler, false);
|
||||
} else if (node.detachEvent) {
|
||||
node.detachEvent("on" + eventType, handler);
|
||||
} else {
|
||||
node["on" + eventType] = noop;
|
||||
}
|
||||
},
|
||||
bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) {
|
||||
// Create the events object if it's not already present
|
||||
if (!chartInstance.events) chartInstance.events = {};
|
||||
|
||||
each(arrayOfEvents, function(eventName) {
|
||||
chartInstance.events[eventName] = function() {
|
||||
handler.apply(chartInstance, arguments);
|
||||
};
|
||||
addEvent(chartInstance.chart.canvas, eventName, chartInstance.events[eventName]);
|
||||
});
|
||||
},
|
||||
unbindEvents = helpers.unbindEvents = function(chartInstance, arrayOfEvents) {
|
||||
each(arrayOfEvents, function(handler, eventName) {
|
||||
removeEvent(chartInstance.chart.canvas, eventName, handler);
|
||||
});
|
||||
},
|
||||
getConstraintWidth = helpers.getConstraintWidth = function(domNode) { // returns Number or undefined if no constraint
|
||||
var constrainedWidth;
|
||||
var constrainedWNode = document.defaultView.getComputedStyle(domNode)['max-width'];
|
||||
var constrainedWContainer = document.defaultView.getComputedStyle(domNode.parentNode)['max-width'];
|
||||
var hasCWNode = constrainedWNode !== null && constrainedWNode !== "none";
|
||||
var hasCWContainer = constrainedWContainer !== null && constrainedWContainer !== "none";
|
||||
|
||||
if (hasCWNode || hasCWContainer) {
|
||||
constrainedWidth = Math.min((hasCWNode ? parseInt(constrainedWNode, 10) : Number.POSITIVE_INFINITY), (hasCWContainer ? parseInt(constrainedWContainer, 10) : Number.POSITIVE_INFINITY));
|
||||
}
|
||||
return constrainedWidth;
|
||||
},
|
||||
getConstraintHeight = helpers.getConstraintHeight = function(domNode) { // returns Number or undefined if no constraint
|
||||
|
||||
var constrainedHeight;
|
||||
var constrainedHNode = document.defaultView.getComputedStyle(domNode)['max-height'];
|
||||
var constrainedHContainer = document.defaultView.getComputedStyle(domNode.parentNode)['max-height'];
|
||||
var hasCHNode = constrainedHNode !== null && constrainedHNode !== "none";
|
||||
var hasCHContainer = constrainedHContainer !== null && constrainedHContainer !== "none";
|
||||
|
||||
if (constrainedHNode || constrainedHContainer) {
|
||||
constrainedHeight = Math.min((hasCHNode ? parseInt(constrainedHNode, 10) : Number.POSITIVE_INFINITY), (hasCHContainer ? parseInt(constrainedHContainer, 10) : Number.POSITIVE_INFINITY));
|
||||
}
|
||||
return constrainedHeight;
|
||||
},
|
||||
getMaximumWidth = helpers.getMaximumWidth = function(domNode) {
|
||||
var container = domNode.parentNode;
|
||||
var padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right'));
|
||||
|
||||
var w = container.clientWidth - padding;
|
||||
var cw = getConstraintWidth(domNode);
|
||||
if (cw !== undefined) {
|
||||
w = Math.min(w, cw);
|
||||
}
|
||||
|
||||
return w;
|
||||
},
|
||||
getMaximumHeight = helpers.getMaximumHeight = function(domNode) {
|
||||
var container = domNode.parentNode;
|
||||
var padding = parseInt(getStyle(container, 'padding-top')) + parseInt(getStyle(container, 'padding-bottom'));
|
||||
|
||||
var h = container.clientHeight - padding;
|
||||
var ch = getConstraintHeight(domNode);
|
||||
if (ch !== undefined) {
|
||||
h = Math.min(h, ch);
|
||||
}
|
||||
|
||||
return h;
|
||||
},
|
||||
getStyle = helpers.getStyle = function(el, property) {
|
||||
return el.currentStyle ?
|
||||
el.currentStyle[property] :
|
||||
document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
|
||||
},
|
||||
getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support
|
||||
retinaScale = helpers.retinaScale = function(chart) {
|
||||
var ctx = chart.ctx;
|
||||
var width = chart.canvas.width;
|
||||
var height = chart.canvas.height;
|
||||
chart.currentDevicePixelRatio = window.devicePixelRatio || 1;
|
||||
|
||||
if (window.devicePixelRatio !== 1) {
|
||||
ctx.canvas.height = height * window.devicePixelRatio;
|
||||
ctx.canvas.width = width * window.devicePixelRatio;
|
||||
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
|
||||
ctx.canvas.style.width = width + 'px';
|
||||
ctx.canvas.style.height = height + 'px';
|
||||
|
||||
// Store the device pixel ratio so that we can go backwards in `destroy`.
|
||||
// The devicePixelRatio changes with zoom, so there are no guarantees that it is the same
|
||||
// when destroy is called
|
||||
chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || window.devicePixelRatio;
|
||||
}
|
||||
},
|
||||
//-- Canvas methods
|
||||
clear = helpers.clear = function(chart) {
|
||||
chart.ctx.clearRect(0, 0, chart.width, chart.height);
|
||||
},
|
||||
fontString = helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
|
||||
return fontStyle + " " + pixelSize + "px " + fontFamily;
|
||||
},
|
||||
longestText = helpers.longestText = function(ctx, font, arrayOfStrings) {
|
||||
ctx.font = font;
|
||||
var longest = 0;
|
||||
each(arrayOfStrings, function(string) {
|
||||
var textWidth = ctx.measureText(string).width;
|
||||
longest = (textWidth > longest) ? textWidth : longest;
|
||||
});
|
||||
return longest;
|
||||
},
|
||||
drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + radius, y);
|
||||
ctx.lineTo(x + width - radius, y);
|
||||
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
||||
ctx.lineTo(x + width, y + height - radius);
|
||||
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
||||
ctx.lineTo(x + radius, y + height);
|
||||
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||||
ctx.lineTo(x, y + radius);
|
||||
ctx.quadraticCurveTo(x, y, x + radius, y);
|
||||
ctx.closePath();
|
||||
},
|
||||
color = helpers.color = function(color) {
|
||||
if (!window.Color) {
|
||||
console.log('Color.js not found!');
|
||||
return color;
|
||||
}
|
||||
return window.Color(color);
|
||||
},
|
||||
addResizeListener = helpers.addResizeListener = function(node, callback) {
|
||||
// Hide an iframe before the node
|
||||
var hiddenIframe = document.createElement('iframe');
|
||||
var hiddenIframeClass = 'chartjs-hidden-iframe';
|
||||
|
||||
if (hiddenIframe.classlist) {
|
||||
// can use classlist
|
||||
hiddenIframe.classlist.add(hiddenIframeClass);
|
||||
} else {
|
||||
hiddenIframe.setAttribute('class', hiddenIframeClass)
|
||||
}
|
||||
|
||||
// Set the style
|
||||
hiddenIframe.style.width = '100%';
|
||||
hiddenIframe.style.display = 'block';
|
||||
hiddenIframe.style.border = 0;
|
||||
hiddenIframe.style.height = 0;
|
||||
hiddenIframe.style.margin = 0;
|
||||
hiddenIframe.style.position = 'absolute';
|
||||
hiddenIframe.style.left = 0;
|
||||
hiddenIframe.style.right = 0;
|
||||
hiddenIframe.style.top = 0;
|
||||
hiddenIframe.style.bottom = 0;
|
||||
|
||||
// Insert the iframe so that contentWindow is available
|
||||
node.insertBefore(hiddenIframe, node.firstChild);
|
||||
|
||||
var timer = 0;
|
||||
(hiddenIframe.contentWindow || hiddenIframe).onresize = function() {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
},
|
||||
removeResizeListener = helpers.removeResizeListener = function(node) {
|
||||
var hiddenIframe = node.querySelector('.chartjs-hidden-iframe');
|
||||
|
||||
// Remove the resize detect iframe
|
||||
if (hiddenIframe) {
|
||||
hiddenIframe.remove();
|
||||
}
|
||||
},
|
||||
isArray = helpers.isArray = function(obj) {
|
||||
if (!Array.isArray) {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]';
|
||||
}
|
||||
return Array.isArray(obj);
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
@@ -1,115 +0,0 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: {{ version }}
|
||||
*
|
||||
* Copyright 2015 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
//Declare root variable - window in the browser, global on the server
|
||||
var root = this,
|
||||
previous = root.Chart;
|
||||
|
||||
//Occupy the global variable of Chart, and create a simple base class
|
||||
var Chart = function(context, config) {
|
||||
this.config = config;
|
||||
|
||||
// Support a jQuery'd canvas element
|
||||
if (context.length && context[0].getContext) {
|
||||
context = context[0];
|
||||
}
|
||||
|
||||
// Support a canvas domnode
|
||||
if (context.getContext) {
|
||||
context = context.getContext("2d");
|
||||
}
|
||||
|
||||
this.ctx = context;
|
||||
this.canvas = context.canvas;
|
||||
|
||||
// Figure out what the size of the chart will be.
|
||||
// If the canvas has a specified width and height, we use those else
|
||||
// we look to see if the canvas node has a CSS width and height.
|
||||
// If there is still no height, fill the parent container
|
||||
this.width = context.canvas.width || parseInt(Chart.helpers.getStyle(context.canvas, 'width')) || Chart.helpers.getMaximumWidth(context.canvas);
|
||||
this.height = context.canvas.height || parseInt(Chart.helpers.getStyle(context.canvas, 'height')) || Chart.helpers.getMaximumHeight(context.canvas);
|
||||
|
||||
this.aspectRatio = this.width / this.height;
|
||||
|
||||
if (isNaN(this.aspectRatio) || isFinite(this.aspectRatio) === false) {
|
||||
// If the canvas has no size, try and figure out what the aspect ratio will be.
|
||||
// Some charts prefer square canvases (pie, radar, etc). If that is specified, use that
|
||||
// else use the canvas default ratio of 2
|
||||
this.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2;
|
||||
}
|
||||
|
||||
// Store the original style of the element so we can set it back
|
||||
this.originalCanvasStyleWidth = context.canvas.style.width;
|
||||
this.originalCanvasStyleHeight = context.canvas.style.height;
|
||||
|
||||
// High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
|
||||
Chart.helpers.retinaScale(this);
|
||||
|
||||
// Always bind this so that if the responsive state changes we still work
|
||||
var _this = this;
|
||||
Chart.helpers.addResizeListener(context.canvas.parentNode, function() {
|
||||
if (config.options.responsive) {
|
||||
_this.controller.resize();
|
||||
}
|
||||
});
|
||||
|
||||
if (config) {
|
||||
this.controller = new Chart.Controller(this);
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
//Globally expose the defaults to allow for user updating/changing
|
||||
Chart.defaults = {
|
||||
global: {
|
||||
responsive: true,
|
||||
responsiveAnimationDuration: 0,
|
||||
maintainAspectRatio: true,
|
||||
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
|
||||
hover: {
|
||||
onHover: null,
|
||||
mode: 'single',
|
||||
animationDuration: 400,
|
||||
},
|
||||
onClick: null,
|
||||
defaultColor: 'rgba(0,0,0,0.1)',
|
||||
|
||||
// Element defaults defined in element extensions
|
||||
elements: {},
|
||||
|
||||
// Legend template string
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i = 0; i < data.datasets.length; i++){%><li><span style=\"background-color:<%=data.datasets[i].backgroundColor%>\"><%if(data.datasets[i].label){%><%=data.datasets[i].label%><%}%></span></li><%}%></ul>",
|
||||
},
|
||||
};
|
||||
|
||||
if (typeof amd !== 'undefined') {
|
||||
define(function() {
|
||||
return Chart;
|
||||
});
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
module.exports = Chart;
|
||||
}
|
||||
|
||||
root.Chart = Chart;
|
||||
|
||||
Chart.noConflict = function() {
|
||||
root.Chart = previous;
|
||||
return Chart;
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
@@ -1,527 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.scale = {
|
||||
display: true,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// scale label
|
||||
scaleLabel: {
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'normal',
|
||||
|
||||
// actual label
|
||||
labelString: '',
|
||||
|
||||
// display property
|
||||
show: false,
|
||||
},
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
maxRotation: 90,
|
||||
minRotation: 20,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
reverse: false,
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
},
|
||||
};
|
||||
|
||||
Chart.Scale = Chart.Element.extend({
|
||||
|
||||
// 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
|
||||
|
||||
beforeUpdate: helpers.noop,
|
||||
update: function(maxWidth, maxHeight, margins) {
|
||||
|
||||
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
|
||||
this.beforeUpdate();
|
||||
|
||||
// Absorb the master measurements
|
||||
this.maxWidth = maxWidth;
|
||||
this.maxHeight = maxHeight;
|
||||
this.margins = margins;
|
||||
|
||||
// Dimensions
|
||||
this.beforeSetDimensions();
|
||||
this.setDimensions();
|
||||
this.afterSetDimensions();
|
||||
// Ticks
|
||||
this.beforeBuildTicks();
|
||||
this.buildTicks();
|
||||
this.afterBuildTicks();
|
||||
|
||||
this.beforeTickToLabelConversion();
|
||||
this.convertTicksToLabels();
|
||||
this.afterTickToLabelConversion();
|
||||
|
||||
// Tick Rotation
|
||||
this.beforeCalculateTickRotation();
|
||||
this.calculateTickRotation();
|
||||
this.afterCalculateTickRotation();
|
||||
// Fit
|
||||
this.beforeFit();
|
||||
this.fit();
|
||||
this.afterFit();
|
||||
//
|
||||
this.afterUpdate();
|
||||
|
||||
return this.minSize;
|
||||
|
||||
},
|
||||
afterUpdate: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeSetDimensions: helpers.noop,
|
||||
setDimensions: function() {
|
||||
// Set the unconstrained dimension before label rotation
|
||||
if (this.isHorizontal()) {
|
||||
this.width = this.maxWidth;
|
||||
} else {
|
||||
this.height = this.maxHeight;
|
||||
}
|
||||
|
||||
// Reset padding
|
||||
this.paddingLeft = 0;
|
||||
this.paddingTop = 0;
|
||||
this.paddingRight = 0;
|
||||
this.paddingBottom = 0;
|
||||
},
|
||||
afterSetDimensions: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeBuildTicks: helpers.noop,
|
||||
buildTicks: helpers.noop,
|
||||
afterBuildTicks: helpers.noop,
|
||||
|
||||
beforeTickToLabelConversion: helpers.noop,
|
||||
convertTicksToLabels: function() {
|
||||
// Convert ticks to strings
|
||||
this.ticks = this.ticks.map(function(numericalTick, index, ticks) {
|
||||
if (this.options.ticks.userCallback) {
|
||||
return this.options.ticks.userCallback(numericalTick, index, ticks);
|
||||
} else {
|
||||
return helpers.template(this.options.ticks.template, {
|
||||
value: numericalTick
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
afterTickToLabelConversion: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeCalculateTickRotation: helpers.noop,
|
||||
calculateTickRotation: function() {
|
||||
//Get the width of each grid by calculating the difference
|
||||
//between x offsets between 0 and 1.
|
||||
var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
this.ctx.font = labelFont;
|
||||
|
||||
var firstWidth = this.ctx.measureText(this.ticks[0]).width;
|
||||
var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
|
||||
var firstRotated;
|
||||
var lastRotated;
|
||||
|
||||
this.paddingRight = lastWidth / 2 + 3;
|
||||
this.paddingLeft = firstWidth / 2 + 3;
|
||||
|
||||
this.labelRotation = 0;
|
||||
|
||||
if (this.options.display && this.isHorizontal()) {
|
||||
var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
|
||||
var cosRotation;
|
||||
var sinRotation;
|
||||
var firstRotatedWidth;
|
||||
|
||||
this.labelWidth = originalLabelWidth;
|
||||
|
||||
// Allow 3 pixels x2 padding either side for label readability
|
||||
// only the index matters for a dataset scale, but we want a consistent interface between scales
|
||||
|
||||
var tickWidth = this.getPixelForTick(1) - this.getPixelForTick(0) - 6;
|
||||
|
||||
//Max label rotation can be set or default to 90 - also act as a loop counter
|
||||
while (this.labelWidth > tickWidth && this.labelRotation <= this.options.ticks.maxRotation) {
|
||||
cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
|
||||
sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
|
||||
|
||||
firstRotated = cosRotation * firstWidth;
|
||||
lastRotated = cosRotation * lastWidth;
|
||||
|
||||
// We're right aligning the text now.
|
||||
if (firstRotated + this.options.ticks.fontSize / 2 > this.yLabelWidth) {
|
||||
this.paddingLeft = firstRotated + this.options.ticks.fontSize / 2;
|
||||
}
|
||||
|
||||
this.paddingRight = this.options.ticks.fontSize / 2;
|
||||
|
||||
if (sinRotation * originalLabelWidth > this.maxHeight) {
|
||||
// go back one step
|
||||
this.labelRotation--;
|
||||
break;
|
||||
}
|
||||
|
||||
this.labelRotation++;
|
||||
this.labelWidth = cosRotation * originalLabelWidth;
|
||||
|
||||
}
|
||||
} else {
|
||||
this.labelWidth = 0;
|
||||
this.paddingRight = 0;
|
||||
this.paddingLeft = 0;
|
||||
}
|
||||
|
||||
if (this.margins) {
|
||||
this.paddingLeft -= this.margins.left;
|
||||
this.paddingRight -= this.margins.right;
|
||||
|
||||
this.paddingLeft = Math.max(this.paddingLeft, 0);
|
||||
this.paddingRight = Math.max(this.paddingRight, 0);
|
||||
}
|
||||
},
|
||||
afterCalculateTickRotation: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeFit: helpers.noop,
|
||||
fit: function() {
|
||||
|
||||
this.minSize = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
|
||||
// Width
|
||||
if (this.isHorizontal()) {
|
||||
this.minSize.width = this.maxWidth; // fill all the width
|
||||
} else {
|
||||
this.minSize.width = this.options.gridLines.show && this.options.display ? 10 : 0;
|
||||
}
|
||||
|
||||
// height
|
||||
if (this.isHorizontal()) {
|
||||
this.minSize.height = this.options.gridLines.show && this.options.display ? 10 : 0;
|
||||
} else {
|
||||
this.minSize.height = this.maxHeight; // fill all the height
|
||||
}
|
||||
|
||||
// Are we showing a title for the scale?
|
||||
if (this.options.scaleLabel.show) {
|
||||
if (this.isHorizontal()) {
|
||||
this.minSize.height += (this.options.scaleLabel.fontSize * 1.5);
|
||||
} else {
|
||||
this.minSize.width += (this.options.scaleLabel.fontSize * 1.5);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.ticks.show && this.options.display) {
|
||||
// Don't bother fitting the ticks if we are not showing them
|
||||
var labelFont = helpers.fontString(this.options.ticks.fontSize,
|
||||
this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
// A horizontal axis is more constrained by the height.
|
||||
var maxLabelHeight = this.maxHeight - this.minSize.height;
|
||||
var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
|
||||
var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.ticks.fontSize;
|
||||
|
||||
this.minSize.height = Math.min(this.maxHeight, this.minSize.height + labelHeight);
|
||||
|
||||
labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
this.ctx.font = labelFont;
|
||||
|
||||
var firstLabelWidth = this.ctx.measureText(this.ticks[0]).width;
|
||||
var lastLabelWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
|
||||
|
||||
// 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
|
||||
var cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
|
||||
var sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
|
||||
this.paddingLeft = this.labelRotation !== 0 ? (cosRotation * firstLabelWidth) + 3 : firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
|
||||
this.paddingRight = this.labelRotation !== 0 ? (sinRotation * (this.options.ticks.fontSize / 2)) + 3 : lastLabelWidth / 2 + 3; // when rotated
|
||||
} else {
|
||||
// A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first
|
||||
var maxLabelWidth = this.maxWidth - this.minSize.width;
|
||||
var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
|
||||
|
||||
if (largestTextWidth < maxLabelWidth) {
|
||||
// We don't need all the room
|
||||
this.minSize.width += largestTextWidth;
|
||||
} else {
|
||||
// Expand to max size
|
||||
this.minSize.width = this.maxWidth;
|
||||
}
|
||||
|
||||
this.paddingTop = this.options.ticks.fontSize / 2;
|
||||
this.paddingBottom = this.options.ticks.fontSize / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.margins) {
|
||||
this.paddingLeft -= this.margins.left;
|
||||
this.paddingTop -= this.margins.top;
|
||||
this.paddingRight -= this.margins.right;
|
||||
this.paddingBottom -= this.margins.bottom;
|
||||
|
||||
this.paddingLeft = Math.max(this.paddingLeft, 0);
|
||||
this.paddingTop = Math.max(this.paddingTop, 0);
|
||||
this.paddingRight = Math.max(this.paddingRight, 0);
|
||||
this.paddingBottom = Math.max(this.paddingBottom, 0);
|
||||
}
|
||||
|
||||
this.width = this.minSize.width;
|
||||
this.height = this.minSize.height;
|
||||
|
||||
},
|
||||
afterFit: helpers.noop,
|
||||
|
||||
// Shared Methods
|
||||
isHorizontal: function() {
|
||||
return this.options.position == "top" || this.options.position == "bottom";
|
||||
},
|
||||
|
||||
// Used to get data value locations. Value can either be an index or a numerical value
|
||||
getPixelForValue: helpers.noop,
|
||||
|
||||
// Used for tick location, should
|
||||
getPixelForTick: function(index, includeOffset) {
|
||||
if (this.isHorizontal()) {
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var tickWidth = innerWidth / Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
|
||||
var pixel = (tickWidth * index) + this.paddingLeft;
|
||||
|
||||
if (includeOffset) {
|
||||
pixel += tickWidth / 2;
|
||||
}
|
||||
return this.left + Math.round(pixel);
|
||||
} else {
|
||||
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
|
||||
return this.top + (index * (innerHeight / (this.ticks.length - 1)));
|
||||
}
|
||||
},
|
||||
|
||||
// Utility for getting the pixel location of a percentage of scale
|
||||
getPixelForDecimal: function(decimal, includeOffset) {
|
||||
if (this.isHorizontal()) {
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var valueOffset = (innerWidth * decimal) + this.paddingLeft;
|
||||
|
||||
return this.left + Math.round(valueOffset);
|
||||
} else {
|
||||
return this.top + (decimal * (this.height / this.ticks.length));
|
||||
}
|
||||
},
|
||||
|
||||
// Actualy draw the scale on the canvas
|
||||
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
|
||||
draw: function(chartArea) {
|
||||
if (this.options.display) {
|
||||
|
||||
var setContextLineSettings;
|
||||
var isRotated = this.labelRotation !== 0;
|
||||
var skipRatio;
|
||||
var scaleLabelX;
|
||||
var scaleLabelY;
|
||||
|
||||
// Make sure we draw text in the correct color
|
||||
this.ctx.fillStyle = this.options.ticks.fontColor;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
setContextLineSettings = true;
|
||||
var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 10;
|
||||
var yTickEnd = this.options.position == "bottom" ? this.top + 10 : this.bottom;
|
||||
skipRatio = false;
|
||||
|
||||
if ((this.options.ticks.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
|
||||
skipRatio = 1 + Math.floor(((this.options.ticks.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
|
||||
}
|
||||
|
||||
helpers.each(this.ticks, function(label, index) {
|
||||
// Blank ticks
|
||||
if ((skipRatio > 1 && index % skipRatio > 0) || (label === undefined || label === null)) {
|
||||
return;
|
||||
}
|
||||
var xLineValue = this.getPixelForTick(index); // xvalues for grid lines
|
||||
var xLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option)
|
||||
|
||||
if (this.options.gridLines.show) {
|
||||
if (index === (typeof this.zeroLineIndex !== 'undefined' ? this.zeroLineIndex : 0)) {
|
||||
// Draw the first index specially
|
||||
this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
|
||||
setContextLineSettings = true; // reset next time
|
||||
} else if (setContextLineSettings) {
|
||||
this.ctx.lineWidth = this.options.gridLines.lineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.color;
|
||||
setContextLineSettings = false;
|
||||
}
|
||||
|
||||
xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
|
||||
// Draw the label area
|
||||
this.ctx.beginPath();
|
||||
|
||||
if (this.options.gridLines.drawTicks) {
|
||||
this.ctx.moveTo(xLineValue, yTickStart);
|
||||
this.ctx.lineTo(xLineValue, yTickEnd);
|
||||
}
|
||||
|
||||
// Draw the chart area
|
||||
if (this.options.gridLines.drawOnChartArea) {
|
||||
this.ctx.moveTo(xLineValue, chartArea.top);
|
||||
this.ctx.lineTo(xLineValue, chartArea.bottom);
|
||||
}
|
||||
|
||||
// Need to stroke in the loop because we are potentially changing line widths & colours
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
if (this.options.ticks.show) {
|
||||
this.ctx.save();
|
||||
this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.options.position === "top" ? this.bottom - 10 : this.top + 10);
|
||||
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
|
||||
this.ctx.font = this.font;
|
||||
this.ctx.textAlign = (isRotated) ? "right" : "center";
|
||||
this.ctx.textBaseline = (isRotated) ? "middle" : this.options.position === "top" ? "bottom" : "top";
|
||||
this.ctx.fillText(label, 0, 0);
|
||||
this.ctx.restore();
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (this.options.scaleLabel.show) {
|
||||
// Draw the scale label
|
||||
this.ctx.textAlign = "center";
|
||||
this.ctx.textBaseline = 'middle';
|
||||
this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
|
||||
|
||||
scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
|
||||
scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
|
||||
|
||||
this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
|
||||
}
|
||||
|
||||
} else {
|
||||
setContextLineSettings = true;
|
||||
var xTickStart = this.options.position == "right" ? this.left : this.right - 5;
|
||||
var xTickEnd = this.options.position == "right" ? this.left + 5 : this.right;
|
||||
|
||||
helpers.each(this.ticks, function(label, index) {
|
||||
var yLineValue = this.getPixelForTick(index); // xvalues for grid lines
|
||||
|
||||
if (this.options.gridLines.show) {
|
||||
if (index === (typeof this.zeroLineIndex !== 'undefined' ? this.zeroLineIndex : 0)) {
|
||||
// Draw the first index specially
|
||||
this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
|
||||
setContextLineSettings = true; // reset next time
|
||||
} else if (setContextLineSettings) {
|
||||
this.ctx.lineWidth = this.options.gridLines.lineWidth;
|
||||
this.ctx.strokeStyle = this.options.gridLines.color;
|
||||
setContextLineSettings = false;
|
||||
}
|
||||
|
||||
yLineValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
|
||||
// Draw the label area
|
||||
this.ctx.beginPath();
|
||||
|
||||
if (this.options.gridLines.drawTicks) {
|
||||
this.ctx.moveTo(xTickStart, yLineValue);
|
||||
this.ctx.lineTo(xTickEnd, yLineValue);
|
||||
}
|
||||
|
||||
// Draw the chart area
|
||||
if (this.options.gridLines.drawOnChartArea) {
|
||||
this.ctx.moveTo(chartArea.left, yLineValue);
|
||||
this.ctx.lineTo(chartArea.right, yLineValue);
|
||||
}
|
||||
|
||||
// Need to stroke in the loop because we are potentially changing line widths & colours
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
if (this.options.ticks.show) {
|
||||
var xLabelValue;
|
||||
var yLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option)
|
||||
|
||||
this.ctx.save();
|
||||
|
||||
if (this.options.position == "left") {
|
||||
if (this.options.ticks.mirror) {
|
||||
xLabelValue = this.right + this.options.ticks.padding;
|
||||
this.ctx.textAlign = "left";
|
||||
} else {
|
||||
xLabelValue = this.right - this.options.ticks.padding;
|
||||
this.ctx.textAlign = "right";
|
||||
}
|
||||
} else {
|
||||
// right side
|
||||
if (this.options.ticks.mirror) {
|
||||
xLabelValue = this.left - this.options.ticks.padding;
|
||||
this.ctx.textAlign = "right";
|
||||
} else {
|
||||
xLabelValue = this.left + this.options.ticks.padding;
|
||||
this.ctx.textAlign = "left";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.ctx.translate(xLabelValue, yLabelValue);
|
||||
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
|
||||
this.ctx.font = this.font;
|
||||
this.ctx.textBaseline = "middle";
|
||||
this.ctx.fillText(label, 0, 0);
|
||||
this.ctx.restore();
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (this.options.scaleLabel.show) {
|
||||
// Draw the scale label
|
||||
scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
|
||||
scaleLabelY = this.top + ((this.bottom - this.top) / 2);
|
||||
var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
|
||||
|
||||
this.ctx.save();
|
||||
this.ctx.translate(scaleLabelX, scaleLabelY);
|
||||
this.ctx.rotate(rotation);
|
||||
this.ctx.textAlign = "center";
|
||||
this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
|
||||
this.ctx.textBaseline = 'middle';
|
||||
this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0);
|
||||
this.ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -1,335 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
// The scale service is used to resize charts along with all of their axes. We make this as
|
||||
// a service where scales are registered with their respective charts so that changing the
|
||||
// scales does not require
|
||||
Chart.scaleService = {
|
||||
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
|
||||
// use the new chart options to grab the correct scale
|
||||
constructors: {},
|
||||
// Use a registration function so that we can move to an ES6 map when we no longer need to support
|
||||
// old browsers
|
||||
|
||||
// Scale config defaults
|
||||
defaults: {},
|
||||
registerScaleType: function(type, scaleConstructor, defaults) {
|
||||
this.constructors[type] = scaleConstructor;
|
||||
this.defaults[type] = helpers.scaleMerge(Chart.defaults.scale, defaults);
|
||||
},
|
||||
getScaleConstructor: function(type) {
|
||||
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
|
||||
},
|
||||
getScaleDefaults: function(type) {
|
||||
return this.defaults.hasOwnProperty(type) ? this.defaults[type] : {};
|
||||
},
|
||||
// The interesting function
|
||||
update: function(chartInstance, width, height) {
|
||||
var xPadding = width > 30 ? 5 : 2;
|
||||
var yPadding = height > 30 ? 5 : 2;
|
||||
|
||||
if (chartInstance) {
|
||||
var leftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "left";
|
||||
});
|
||||
var rightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "right";
|
||||
});
|
||||
var topScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "top";
|
||||
});
|
||||
var bottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "bottom";
|
||||
});
|
||||
|
||||
// Scales that overlay the chartarea such as the radialLinear scale
|
||||
var chartAreaScales = helpers.where(chartInstance.scales, function(scaleInstance) {
|
||||
return scaleInstance.options.position == "chartArea";
|
||||
});
|
||||
|
||||
// Essentially we now have any number of scales on each of the 4 sides.
|
||||
// Our canvas looks like the following.
|
||||
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
|
||||
// B1 is the bottom axis
|
||||
// |------------------------------------------------------|
|
||||
// | | T1 | |
|
||||
// |----|-----|-------------------------------------|-----|
|
||||
// | | | | |
|
||||
// | L1 | L2 | Chart area | R1 |
|
||||
// | | | | |
|
||||
// | | | | |
|
||||
// |----|-----|-------------------------------------|-----|
|
||||
// | | B1 | |
|
||||
// | | | |
|
||||
// |------------------------------------------------------|
|
||||
|
||||
// What we do to find the best sizing, we do the following
|
||||
// 1. Determine the minimum size of the chart area.
|
||||
// 2. Split the remaining width equally between each vertical axis
|
||||
// 3. Split the remaining height equally between each horizontal axis
|
||||
// 4. Give each scale the maximum size it can be. The scale will return it's minimum size
|
||||
// 5. Adjust the sizes of each axis based on it's minimum reported size.
|
||||
// 6. Refit each axis
|
||||
// 7. Position each axis in the final location
|
||||
// 8. Tell the chart the final location of the chart area
|
||||
// 9. Tell any axes that overlay the chart area the positions of the chart area
|
||||
|
||||
// Step 1
|
||||
var chartWidth = width / 2; // min 50%
|
||||
var chartHeight = height / 2; // min 50%
|
||||
|
||||
chartWidth -= (2 * xPadding);
|
||||
chartHeight -= (2 * yPadding);
|
||||
|
||||
// Step 2
|
||||
var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
|
||||
|
||||
// Step 3
|
||||
var horizontalScaleHeight = (height - chartHeight) / (topScales.length + bottomScales.length);
|
||||
|
||||
// Step 4;
|
||||
var minimumScaleSizes = [];
|
||||
|
||||
var verticalScaleMinSizeFunction = function(scaleInstance) {
|
||||
var minSize = scaleInstance.update(verticalScaleWidth, chartHeight);
|
||||
minimumScaleSizes.push({
|
||||
horizontal: false,
|
||||
minSize: minSize,
|
||||
scale: scaleInstance,
|
||||
});
|
||||
};
|
||||
|
||||
var horizontalScaleMinSizeFunction = function(scaleInstance) {
|
||||
var minSize = scaleInstance.update(chartWidth, horizontalScaleHeight);
|
||||
minimumScaleSizes.push({
|
||||
horizontal: true,
|
||||
minSize: minSize,
|
||||
scale: scaleInstance,
|
||||
});
|
||||
};
|
||||
|
||||
// vertical scales
|
||||
helpers.each(leftScales, verticalScaleMinSizeFunction);
|
||||
helpers.each(rightScales, verticalScaleMinSizeFunction);
|
||||
|
||||
// horizontal scales
|
||||
helpers.each(topScales, horizontalScaleMinSizeFunction);
|
||||
helpers.each(bottomScales, horizontalScaleMinSizeFunction);
|
||||
|
||||
// Step 5
|
||||
var maxChartHeight = height - (2 * yPadding);
|
||||
var maxChartWidth = width - (2 * xPadding);
|
||||
|
||||
helpers.each(minimumScaleSizes, function(wrapper) {
|
||||
if (wrapper.horizontal) {
|
||||
maxChartHeight -= wrapper.minSize.height;
|
||||
} else {
|
||||
maxChartWidth -= wrapper.minSize.width;
|
||||
}
|
||||
});
|
||||
|
||||
// At this point, maxChartHeight and maxChartWidth are the size the chart area could
|
||||
// be if the axes are drawn at their minimum sizes.
|
||||
|
||||
// Step 6
|
||||
var verticalScaleFitFunction = function(scaleInstance) {
|
||||
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
|
||||
return wrapper.scale === scaleInstance;
|
||||
});
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.update(wrapper.minSize.width, maxChartHeight);
|
||||
}
|
||||
};
|
||||
|
||||
var horizontalScaleFitFunction = function(scaleInstance) {
|
||||
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
|
||||
return wrapper.scale === scaleInstance;
|
||||
});
|
||||
|
||||
var scaleMargin = {
|
||||
left: totalLeftWidth,
|
||||
right: totalRightWidth,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.update(maxChartWidth, wrapper.minSize.height, scaleMargin);
|
||||
}
|
||||
};
|
||||
|
||||
var totalLeftWidth = xPadding;
|
||||
var totalRightWidth = xPadding;
|
||||
var totalTopHeight = yPadding;
|
||||
var totalBottomHeight = yPadding;
|
||||
|
||||
helpers.each(leftScales, verticalScaleFitFunction);
|
||||
helpers.each(rightScales, verticalScaleFitFunction);
|
||||
|
||||
// Figure out how much margin is on the left and right of the horizontal axes
|
||||
helpers.each(leftScales, function(scaleInstance) {
|
||||
totalLeftWidth += scaleInstance.width;
|
||||
});
|
||||
|
||||
helpers.each(rightScales, function(scaleInstance) {
|
||||
totalRightWidth += scaleInstance.width;
|
||||
});
|
||||
|
||||
helpers.each(topScales, horizontalScaleFitFunction);
|
||||
helpers.each(bottomScales, horizontalScaleFitFunction);
|
||||
|
||||
helpers.each(topScales, function(scaleInstance) {
|
||||
totalTopHeight += scaleInstance.height;
|
||||
});
|
||||
helpers.each(bottomScales, function(scaleInstance) {
|
||||
totalBottomHeight += scaleInstance.height;
|
||||
});
|
||||
|
||||
// Let the left scale know the final margin
|
||||
helpers.each(leftScales, function(scaleInstance) {
|
||||
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
|
||||
return wrapper.scale === scaleInstance;
|
||||
});
|
||||
|
||||
var scaleMargin = {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: totalTopHeight,
|
||||
bottom: totalBottomHeight
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.update(wrapper.minSize.width, maxChartHeight, scaleMargin);
|
||||
}
|
||||
});
|
||||
|
||||
helpers.each(rightScales, function(scaleInstance) {
|
||||
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
|
||||
return wrapper.scale === scaleInstance;
|
||||
});
|
||||
|
||||
var scaleMargin = {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: totalTopHeight,
|
||||
bottom: totalBottomHeight
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.update(wrapper.minSize.width, maxChartHeight, scaleMargin);
|
||||
}
|
||||
});
|
||||
|
||||
// Recalculate because the size of each scale might have changed slightly due to the margins (label rotation for instance)
|
||||
totalLeftWidth = xPadding;
|
||||
totalRightWidth = xPadding;
|
||||
totalTopHeight = yPadding;
|
||||
totalBottomHeight = yPadding;
|
||||
|
||||
helpers.each(leftScales, function(scaleInstance) {
|
||||
totalLeftWidth += scaleInstance.width;
|
||||
});
|
||||
|
||||
helpers.each(rightScales, function(scaleInstance) {
|
||||
totalRightWidth += scaleInstance.width;
|
||||
});
|
||||
|
||||
helpers.each(topScales, function(scaleInstance) {
|
||||
totalTopHeight += scaleInstance.height;
|
||||
});
|
||||
helpers.each(bottomScales, function(scaleInstance) {
|
||||
totalBottomHeight += scaleInstance.height;
|
||||
});
|
||||
|
||||
// Figure out if our chart area changed. This would occur if the dataset scale label rotation
|
||||
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
|
||||
// without calling `fit` again
|
||||
var newMaxChartHeight = height - totalTopHeight - totalBottomHeight;
|
||||
var newMaxChartWidth = width - totalLeftWidth - totalRightWidth;
|
||||
|
||||
if (newMaxChartWidth !== maxChartWidth || newMaxChartHeight !== maxChartHeight) {
|
||||
helpers.each(leftScales, function(scale) {
|
||||
scale.height = newMaxChartHeight;
|
||||
});
|
||||
|
||||
helpers.each(rightScales, function(scale) {
|
||||
scale.height = newMaxChartHeight;
|
||||
});
|
||||
|
||||
helpers.each(topScales, function(scale) {
|
||||
scale.width = newMaxChartWidth;
|
||||
});
|
||||
|
||||
helpers.each(bottomScales, function(scale) {
|
||||
scale.width = newMaxChartWidth;
|
||||
});
|
||||
|
||||
maxChartHeight = newMaxChartHeight;
|
||||
maxChartWidth = newMaxChartWidth;
|
||||
}
|
||||
|
||||
// Step 7
|
||||
// Position the scales
|
||||
var left = xPadding;
|
||||
var top = yPadding;
|
||||
var right = 0;
|
||||
var bottom = 0;
|
||||
|
||||
var verticalScalePlacer = function(scaleInstance) {
|
||||
scaleInstance.left = left;
|
||||
scaleInstance.right = left + scaleInstance.width;
|
||||
scaleInstance.top = totalTopHeight;
|
||||
scaleInstance.bottom = totalTopHeight + maxChartHeight;
|
||||
|
||||
// Move to next point
|
||||
left = scaleInstance.right;
|
||||
};
|
||||
|
||||
var horizontalScalePlacer = function(scaleInstance) {
|
||||
scaleInstance.left = totalLeftWidth;
|
||||
scaleInstance.right = totalLeftWidth + maxChartWidth;
|
||||
scaleInstance.top = top;
|
||||
scaleInstance.bottom = top + scaleInstance.height;
|
||||
|
||||
// Move to next point
|
||||
top = scaleInstance.bottom;
|
||||
};
|
||||
|
||||
helpers.each(leftScales, verticalScalePlacer);
|
||||
helpers.each(topScales, horizontalScalePlacer);
|
||||
|
||||
// Account for chart width and height
|
||||
left += maxChartWidth;
|
||||
top += maxChartHeight;
|
||||
|
||||
helpers.each(rightScales, verticalScalePlacer);
|
||||
helpers.each(bottomScales, horizontalScalePlacer);
|
||||
|
||||
// Step 8
|
||||
chartInstance.chartArea = {
|
||||
left: totalLeftWidth,
|
||||
top: totalTopHeight,
|
||||
right: totalLeftWidth + maxChartWidth,
|
||||
bottom: totalTopHeight + maxChartHeight,
|
||||
};
|
||||
|
||||
// Step 9
|
||||
helpers.each(chartAreaScales, function(scaleInstance) {
|
||||
scaleInstance.left = chartInstance.chartArea.left;
|
||||
scaleInstance.top = chartInstance.chartArea.top;
|
||||
scaleInstance.right = chartInstance.chartArea.right;
|
||||
scaleInstance.bottom = chartInstance.chartArea.bottom;
|
||||
|
||||
scaleInstance.update(maxChartWidth, maxChartHeight);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,347 +0,0 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.tooltips = {
|
||||
enabled: true,
|
||||
custom: null,
|
||||
backgroundColor: "rgba(0,0,0,0.8)",
|
||||
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
fontSize: 10,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#fff",
|
||||
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||||
titleFontSize: 12,
|
||||
titleFontStyle: "bold",
|
||||
titleFontColor: "#fff",
|
||||
yPadding: 6,
|
||||
xPadding: 6,
|
||||
caretSize: 8,
|
||||
cornerRadius: 6,
|
||||
xOffset: 10,
|
||||
template: [
|
||||
'<% if(label){ %>',
|
||||
'<%=label %>: ',
|
||||
'<% } %>',
|
||||
'<%=value %>',
|
||||
].join(''),
|
||||
multiTemplate: [
|
||||
'<%if (datasetLabel){ %>',
|
||||
'<%=datasetLabel %>: ',
|
||||
'<% } %>',
|
||||
'<%=value %>'
|
||||
].join(''),
|
||||
multiKeyBackground: '#fff',
|
||||
};
|
||||
|
||||
Chart.Tooltip = Chart.Element.extend({
|
||||
initialize: function() {
|
||||
var options = this._options;
|
||||
helpers.extend(this, {
|
||||
_model: {
|
||||
// Positioning
|
||||
xPadding: options.tooltips.xPadding,
|
||||
yPadding: options.tooltips.yPadding,
|
||||
xOffset: options.tooltips.xOffset,
|
||||
|
||||
// Labels
|
||||
textColor: options.tooltips.fontColor,
|
||||
_fontFamily: options.tooltips.fontFamily,
|
||||
_fontStyle: options.tooltips.fontStyle,
|
||||
fontSize: options.tooltips.fontSize,
|
||||
|
||||
// Title
|
||||
titleTextColor: options.tooltips.titleFontColor,
|
||||
_titleFontFamily: options.tooltips.titleFontFamily,
|
||||
_titleFontStyle: options.tooltips.titleFontStyle,
|
||||
titleFontSize: options.tooltips.titleFontSize,
|
||||
|
||||
// Appearance
|
||||
caretHeight: options.tooltips.caretSize,
|
||||
cornerRadius: options.tooltips.cornerRadius,
|
||||
backgroundColor: options.tooltips.backgroundColor,
|
||||
opacity: 0,
|
||||
legendColorBackground: options.tooltips.multiKeyBackground,
|
||||
},
|
||||
});
|
||||
},
|
||||
update: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
|
||||
switch (this._options.hover.mode) {
|
||||
case 'single':
|
||||
helpers.extend(this._model, {
|
||||
text: helpers.template(this._options.tooltips.template, {
|
||||
// These variables are available in the template function. Add others here
|
||||
element: this._active[0],
|
||||
value: this._data.datasets[this._active[0]._datasetIndex].data[this._active[0]._index],
|
||||
label: this._active[0]._model.label !== undefined ? this._active[0]._model.label : this._data.labels ? this._data.labels[this._active[0]._index] : '',
|
||||
}),
|
||||
});
|
||||
|
||||
var tooltipPosition = this._active[0].tooltipPosition();
|
||||
helpers.extend(this._model, {
|
||||
x: Math.round(tooltipPosition.x),
|
||||
y: Math.round(tooltipPosition.y),
|
||||
caretPadding: tooltipPosition.padding
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case 'label':
|
||||
|
||||
// Tooltip Content
|
||||
|
||||
var dataArray,
|
||||
dataIndex;
|
||||
|
||||
var labels = [],
|
||||
colors = [];
|
||||
|
||||
for (var i = this._data.datasets.length - 1; i >= 0; i--) {
|
||||
dataArray = this._data.datasets[i].metaData;
|
||||
dataIndex = helpers.indexOf(dataArray, this._active[0]);
|
||||
if (dataIndex !== -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var medianPosition = (function(index) {
|
||||
// Get all the points at that particular index
|
||||
var elements = [],
|
||||
dataCollection,
|
||||
xPositions = [],
|
||||
yPositions = [],
|
||||
xMax,
|
||||
yMax,
|
||||
xMin,
|
||||
yMin;
|
||||
helpers.each(this._data.datasets, function(dataset) {
|
||||
dataCollection = dataset.metaData;
|
||||
if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()) {
|
||||
elements.push(dataCollection[dataIndex]);
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Reverse labels if stacked
|
||||
helpers.each(this._options.stacked ? elements.reverse() : elements, function(element) {
|
||||
xPositions.push(element._view.x);
|
||||
yPositions.push(element._view.y);
|
||||
|
||||
//Include any colour information about the element
|
||||
labels.push(helpers.template(this._options.tooltips.multiTemplate, {
|
||||
// These variables are available in the template function. Add others here
|
||||
element: element,
|
||||
datasetLabel: this._data.datasets[element._datasetIndex].label,
|
||||
value: this._data.datasets[element._datasetIndex].data[element._index],
|
||||
}));
|
||||
colors.push({
|
||||
fill: element._view.backgroundColor,
|
||||
stroke: element._view.borderColor
|
||||
});
|
||||
|
||||
}, this);
|
||||
|
||||
yMin = helpers.min(yPositions);
|
||||
yMax = helpers.max(yPositions);
|
||||
|
||||
xMin = helpers.min(xPositions);
|
||||
xMax = helpers.max(xPositions);
|
||||
|
||||
return {
|
||||
x: (xMin > this._chart.width / 2) ? xMin : xMax,
|
||||
y: (yMin + yMax) / 2,
|
||||
};
|
||||
}).call(this, dataIndex);
|
||||
|
||||
// Apply for now
|
||||
helpers.extend(this._model, {
|
||||
x: medianPosition.x,
|
||||
y: medianPosition.y,
|
||||
labels: labels,
|
||||
title: (function() {
|
||||
return this._data.timeLabels ? this._data.timeLabels[this._active[0]._index] :
|
||||
(this._data.labels && this._data.labels.length) ? this._data.labels[this._active[0]._index] :
|
||||
'';
|
||||
}).call(this),
|
||||
legendColors: colors,
|
||||
legendBackgroundColor: this._options.tooltips.multiKeyBackground,
|
||||
});
|
||||
|
||||
|
||||
// Calculate Appearance Tweaks
|
||||
|
||||
this._model.height = (labels.length * this._model.fontSize) + ((labels.length - 1) * (this._model.fontSize / 2)) + (this._model.yPadding * 2) + this._model.titleFontSize * 1.5;
|
||||
|
||||
var titleWidth = ctx.measureText(this._model.title).width,
|
||||
//Label has a legend square as well so account for this.
|
||||
labelWidth = helpers.longestText(ctx, this.font, labels) + this._model.fontSize + 3,
|
||||
longestTextWidth = helpers.max([labelWidth, titleWidth]);
|
||||
|
||||
this._model.width = longestTextWidth + (this._model.xPadding * 2);
|
||||
|
||||
|
||||
var halfHeight = this._model.height / 2;
|
||||
|
||||
//Check to ensure the height will fit on the canvas
|
||||
if (this._model.y - halfHeight < 0) {
|
||||
this._model.y = halfHeight;
|
||||
} else if (this._model.y + halfHeight > this._chart.height) {
|
||||
this._model.y = this._chart.height - halfHeight;
|
||||
}
|
||||
|
||||
//Decide whether to align left or right based on position on canvas
|
||||
if (this._model.x > this._chart.width / 2) {
|
||||
this._model.x -= this._model.xOffset + this._model.width;
|
||||
} else {
|
||||
this._model.x += this._model.xOffset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
|
||||
switch (this._options.hover.mode) {
|
||||
case 'single':
|
||||
|
||||
ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
|
||||
|
||||
vm.xAlign = "center";
|
||||
vm.yAlign = "above";
|
||||
|
||||
//Distance between the actual element.y position and the start of the tooltip caret
|
||||
var caretPadding = vm.caretPadding || 2;
|
||||
|
||||
var tooltipWidth = ctx.measureText(vm.text).width + 2 * vm.xPadding,
|
||||
tooltipRectHeight = vm.fontSize + 2 * vm.yPadding,
|
||||
tooltipHeight = tooltipRectHeight + vm.caretHeight + caretPadding;
|
||||
|
||||
if (vm.x + tooltipWidth / 2 > this._chart.width) {
|
||||
vm.xAlign = "left";
|
||||
} else if (vm.x - tooltipWidth / 2 < 0) {
|
||||
vm.xAlign = "right";
|
||||
}
|
||||
|
||||
if (vm.y - tooltipHeight < 0) {
|
||||
vm.yAlign = "below";
|
||||
}
|
||||
|
||||
var tooltipX = vm.x - tooltipWidth / 2,
|
||||
tooltipY = vm.y - tooltipHeight;
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
|
||||
|
||||
// Custom Tooltips
|
||||
if (this._options.tooltips.custom) {
|
||||
this._options.tooltips.custom(this);
|
||||
}
|
||||
if (!this._options.tooltips.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (vm.yAlign) {
|
||||
case "above":
|
||||
//Draw a caret above the x/y
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vm.x, vm.y - caretPadding);
|
||||
ctx.lineTo(vm.x + vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
|
||||
ctx.lineTo(vm.x - vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
case "below":
|
||||
tooltipY = vm.y + caretPadding + vm.caretHeight;
|
||||
//Draw a caret below the x/y
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vm.x, vm.y + caretPadding);
|
||||
ctx.lineTo(vm.x + vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
|
||||
ctx.lineTo(vm.x - vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vm.xAlign) {
|
||||
case "left":
|
||||
tooltipX = vm.x - tooltipWidth + (vm.cornerRadius + vm.caretHeight);
|
||||
break;
|
||||
case "right":
|
||||
tooltipX = vm.x - (vm.cornerRadius + vm.caretHeight);
|
||||
break;
|
||||
}
|
||||
|
||||
helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipRectHeight, vm.cornerRadius);
|
||||
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillText(vm.text, tooltipX + tooltipWidth / 2, tooltipY + tooltipRectHeight / 2);
|
||||
break;
|
||||
case 'label':
|
||||
|
||||
// Custom Tooltips
|
||||
if (this._options.tooltips.custom) {
|
||||
this._options.tooltips.custom(this);
|
||||
}
|
||||
if (!this._options.tooltips.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
helpers.drawRoundedRectangle(ctx, vm.x, vm.y - vm.height / 2, vm.width, vm.height, vm.cornerRadius);
|
||||
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
|
||||
ctx.textAlign = "left";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = helpers.color(vm.titleTextColor).alpha(vm.opacity).rgbString();
|
||||
ctx.font = helpers.fontString(vm.fontSize, vm._titleFontStyle, vm._titleFontFamily);
|
||||
ctx.fillText(vm.title, vm.x + vm.xPadding, this.getLineHeight(0));
|
||||
|
||||
ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
|
||||
helpers.each(vm.labels, function(label, index) {
|
||||
ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
|
||||
ctx.fillText(label, vm.x + vm.xPadding + vm.fontSize + 3, this.getLineHeight(index + 1));
|
||||
|
||||
//A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
|
||||
//ctx.clearRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize/2, vm.fontSize, vm.fontSize);
|
||||
//Instead we'll make a white filled block to put the legendColour palette over.
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.legendColors[index].stroke).alpha(vm.opacity).rgbString();
|
||||
ctx.fillRect(vm.x + vm.xPadding - 1, this.getLineHeight(index + 1) - vm.fontSize / 2 - 1, vm.fontSize + 2, vm.fontSize + 2);
|
||||
|
||||
ctx.fillStyle = helpers.color(vm.legendColors[index].fill).alpha(vm.opacity).rgbString();
|
||||
ctx.fillRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize / 2, vm.fontSize, vm.fontSize);
|
||||
|
||||
|
||||
}, this);
|
||||
break;
|
||||
}
|
||||
},
|
||||
getLineHeight: function(index) {
|
||||
var baseLineHeight = this._view.y - (this._view.height / 2) + this._view.yPadding,
|
||||
afterTitleIndex = index - 1;
|
||||
|
||||
//If the index is zero, we're getting the title
|
||||
if (index === 0) {
|
||||
return baseLineHeight + this._view.titleFontSize / 2;
|
||||
} else {
|
||||
return baseLineHeight + ((this._view.fontSize * 1.5 * afterTitleIndex) + this._view.fontSize / 2) + this._view.titleFontSize * 1.5;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -1,96 +0,0 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: {{ version }}
|
||||
*
|
||||
* Copyright 2015 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.elements.arc = {
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderColor: "#fff",
|
||||
borderWidth: 2
|
||||
};
|
||||
|
||||
Chart.elements.Arc = Chart.Element.extend({
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
inRange: function(chartX, chartY) {
|
||||
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
|
||||
x: chartX,
|
||||
y: chartY
|
||||
});
|
||||
|
||||
// Put into the range of (-PI/2, 3PI/2]
|
||||
var startAngle = vm.startAngle < (-0.5 * Math.PI) ? vm.startAngle + (2.0 * Math.PI) : vm.startAngle > (1.5 * Math.PI) ? vm.startAngle - (2.0 * Math.PI) : vm.startAngle;
|
||||
var endAngle = vm.endAngle < (-0.5 * Math.PI) ? vm.endAngle + (2.0 * Math.PI) : vm.endAngle > (1.5 * Math.PI) ? vm.endAngle - (2.0 * Math.PI) : vm.endAngle
|
||||
|
||||
//Check if within the range of the open/close angle
|
||||
var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle),
|
||||
withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius);
|
||||
|
||||
return (betweenAngles && withinRadius);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
|
||||
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
|
||||
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
|
||||
return {
|
||||
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
|
||||
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
|
||||
};
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(vm.x, vm.y, vm.outerRadius, vm.startAngle, vm.endAngle);
|
||||
|
||||
ctx.arc(vm.x, vm.y, vm.innerRadius, vm.endAngle, vm.startAngle, true);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = vm.borderWidth;
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor;
|
||||
|
||||
ctx.fill();
|
||||
ctx.lineJoin = 'bevel';
|
||||
|
||||
if (vm.borderWidth) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,192 +0,0 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: {{ version }}
|
||||
*
|
||||
* Copyright 2015 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.elements.line = {
|
||||
tension: 0.4,
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 3,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
fill: true, // do we fill in the area between the line and its base axis
|
||||
skipNull: true,
|
||||
drawNull: false,
|
||||
};
|
||||
|
||||
|
||||
Chart.elements.Line = Chart.Element.extend({
|
||||
draw: function() {
|
||||
|
||||
var vm = this._view;
|
||||
var ctx = this._chart.ctx;
|
||||
var first = this._children[0];
|
||||
var last = this._children[this._children.length - 1];
|
||||
|
||||
ctx.save();
|
||||
|
||||
// Draw the background first (so the border is always on top)
|
||||
helpers.each(this._children, function(point, index) {
|
||||
var previous = helpers.previousItem(this._children, index);
|
||||
var next = helpers.nextItem(this._children, index);
|
||||
|
||||
// First point only
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Skip and drag along scale baseline
|
||||
if (point._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.lineTo(previous._view.x, point._view.y);
|
||||
ctx.moveTo(next._view.x, point._view.y);
|
||||
}
|
||||
// End Skip Stright line from the base line
|
||||
else if (previous._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(point._view.x, previous._view.y);
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
|
||||
if (previous._view.skip && vm.skipNull) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
}
|
||||
// Normal Bezier Curve
|
||||
else {
|
||||
if (vm.tension > 0) {
|
||||
ctx.bezierCurveTo(
|
||||
previous._view.controlPointNextX,
|
||||
previous._view.controlPointNextY,
|
||||
point._view.controlPointPreviousX,
|
||||
point._view.controlPointPreviousY,
|
||||
point._view.x,
|
||||
point._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
// For radial scales, loop back around to the first point
|
||||
if (this._loop) {
|
||||
if (vm.tension > 0 && !first._view.skip) {
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
last._view.controlPointNextX,
|
||||
last._view.controlPointNextY,
|
||||
first._view.controlPointPreviousX,
|
||||
first._view.controlPointPreviousY,
|
||||
first._view.x,
|
||||
first._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(first._view.x, first._view.y);
|
||||
}
|
||||
}
|
||||
|
||||
// If we had points and want to fill this line, do so.
|
||||
if (this._children.length > 0 && vm.fill) {
|
||||
//Round off the line by going to the base of the chart, back to the start, then fill.
|
||||
ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero);
|
||||
ctx.lineTo(this._children[0]._view.x, vm.scaleZero);
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
|
||||
// Now draw the line between all the points with any borders
|
||||
ctx.lineCap = vm.borderCapStyle || Chart.defaults.global.elements.line.borderCapStyle;
|
||||
|
||||
// IE 9 and 10 do not support line dash
|
||||
if (ctx.setLineDash) {
|
||||
ctx.setLineDash(vm.borderDash || Chart.defaults.global.elements.line.borderDash);
|
||||
}
|
||||
|
||||
ctx.lineDashOffset = vm.borderDashOffset || Chart.defaults.global.elements.line.borderDashOffset;
|
||||
ctx.lineJoin = vm.borderJoinStyle || Chart.defaults.global.elements.line.borderJoinStyle;
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.line.borderWidth;
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
|
||||
ctx.beginPath();
|
||||
|
||||
helpers.each(this._children, function(point, index) {
|
||||
var previous = helpers.previousItem(this._children, index);
|
||||
var next = helpers.nextItem(this._children, index);
|
||||
|
||||
// First point only
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Skip and drag along scale baseline
|
||||
if (point._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(previous._view.x, point._view.y);
|
||||
ctx.moveTo(next._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
// End Skip Stright line from the base line
|
||||
if (previous._view.skip && vm.skipNull && !this._loop) {
|
||||
ctx.moveTo(point._view.x, previous._view.y);
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (previous._view.skip && vm.skipNull) {
|
||||
ctx.moveTo(point._view.x, point._view.y);
|
||||
return;
|
||||
}
|
||||
// Normal Bezier Curve
|
||||
if (vm.tension > 0) {
|
||||
ctx.bezierCurveTo(
|
||||
previous._view.controlPointNextX,
|
||||
previous._view.controlPointNextY,
|
||||
point._view.controlPointPreviousX,
|
||||
point._view.controlPointPreviousY,
|
||||
point._view.x,
|
||||
point._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(point._view.x, point._view.y);
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (this._loop && !first._view.skip) {
|
||||
if (vm.tension > 0) {
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
last._view.controlPointNextX,
|
||||
last._view.controlPointNextY,
|
||||
first._view.controlPointPreviousX,
|
||||
first._view.controlPointPreviousY,
|
||||
first._view.x,
|
||||
first._view.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(first._view.x, first._view.y);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
},
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
@@ -1,89 +0,0 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: {{ version }}
|
||||
*
|
||||
* Copyright 2015 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.elements.point = {
|
||||
radius: 3,
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
// Hover
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1,
|
||||
};
|
||||
|
||||
|
||||
Chart.elements.Point = Chart.Element.extend({
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
var hoverRange = vm.hitRadius + vm.radius;
|
||||
return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y,
|
||||
padding: vm.radius + vm.borderWidth
|
||||
};
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var vm = this._view;
|
||||
var ctx = this._chart.ctx;
|
||||
|
||||
|
||||
if (vm.skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vm.radius > 0 || vm.borderWidth > 0) {
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(vm.x, vm.y, vm.radius || Chart.defaults.global.elements.point.radius, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.point.borderWidth;
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,97 +0,0 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
Chart.defaults.global.elements.rectangle = {
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 0,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
};
|
||||
|
||||
Chart.elements.Rectangle = Chart.Element.extend({
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var vm = this._view;
|
||||
|
||||
var halfWidth = vm.width / 2,
|
||||
leftX = vm.x - halfWidth,
|
||||
rightX = vm.x + halfWidth,
|
||||
top = vm.base - (vm.base - vm.y),
|
||||
halfStroke = vm.borderWidth / 2;
|
||||
|
||||
// Canvas doesn't allow us to stroke inside the width so we can
|
||||
// adjust the sizes to fit if we're setting a stroke on the line
|
||||
if (vm.borderWidth) {
|
||||
leftX += halfStroke;
|
||||
rightX -= halfStroke;
|
||||
top += halfStroke;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor;
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = vm.borderWidth;
|
||||
|
||||
// It'd be nice to keep this class totally generic to any rectangle
|
||||
// and simply specify which border to miss out.
|
||||
ctx.moveTo(leftX, vm.base);
|
||||
ctx.lineTo(leftX, top);
|
||||
ctx.lineTo(rightX, top);
|
||||
ctx.lineTo(rightX, vm.base);
|
||||
ctx.fill();
|
||||
if (vm.borderWidth) {
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
height: function() {
|
||||
var vm = this._view;
|
||||
return vm.base - vm.y;
|
||||
},
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._view;
|
||||
var inRange = false;
|
||||
|
||||
if (vm) {
|
||||
if (vm.y < vm.base) {
|
||||
inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
|
||||
} else {
|
||||
inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y);
|
||||
}
|
||||
}
|
||||
|
||||
return inRange;
|
||||
},
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
||||
if (vm) {
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
if (vm.y < vm.base) {
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.base
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,47 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
// Default config for a category scale
|
||||
var defaultConfig = {
|
||||
position: "bottom",
|
||||
};
|
||||
|
||||
var DatasetScale = Chart.Scale.extend({
|
||||
buildTicks: function(index) {
|
||||
this.ticks = this.data.labels;
|
||||
},
|
||||
|
||||
// Used to get data value locations. Value can either be an index or a numerical value
|
||||
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var valueWidth = innerWidth / Math.max((this.data.labels.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
|
||||
var valueOffset = (valueWidth * index) + this.paddingLeft;
|
||||
|
||||
if (this.options.gridLines.offsetGridLines && includeOffset) {
|
||||
valueOffset += (valueWidth / 2);
|
||||
}
|
||||
|
||||
return this.left + Math.round(valueOffset);
|
||||
} else {
|
||||
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
|
||||
var valueHeight = innerHeight / Math.max((this.data.labels.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
|
||||
var valueOffset = (valueHeight * index) + this.paddingTop;
|
||||
|
||||
if (this.options.gridLines.offsetGridLines && includeOffset) {
|
||||
valueOffset += (valueHeight / 2);
|
||||
}
|
||||
|
||||
return this.top + Math.round(valueOffset);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
|
||||
|
||||
}).call(this);
|
||||
@@ -1,177 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
position: "left",
|
||||
};
|
||||
|
||||
var LinearScale = Chart.Scale.extend({
|
||||
buildTicks: function() {
|
||||
|
||||
// First Calculate the range
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
var positiveValues = [];
|
||||
var negativeValues = [];
|
||||
|
||||
if (this.options.stacked) {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(rawValue, index) {
|
||||
|
||||
var value = this.getRightValue(rawValue);
|
||||
|
||||
positiveValues[index] = positiveValues[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
|
||||
if (this.options.relativePoints) {
|
||||
positiveValues[index] = 100;
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negativeValues[index] += value;
|
||||
} else {
|
||||
positiveValues[index] += value;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
|
||||
var values = positiveValues.concat(negativeValues);
|
||||
this.min = helpers.min(values);
|
||||
this.max = helpers.max(values);
|
||||
|
||||
} else {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(rawValue, index) {
|
||||
var value = this.getRightValue(rawValue);
|
||||
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.min === this.max) {
|
||||
this.min--;
|
||||
this.max++;
|
||||
}
|
||||
|
||||
|
||||
// Then calulate the ticks
|
||||
this.ticks = [];
|
||||
|
||||
// Figure out what the max number of ticks we can support it is based on the size of
|
||||
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
|
||||
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
|
||||
// the graph
|
||||
|
||||
var maxTicks;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
maxTicks = Math.min(11, Math.ceil(this.width / 50));
|
||||
} else {
|
||||
// The factor of 2 used to scale the font size has been experimentally determined.
|
||||
maxTicks = Math.min(11, Math.ceil(this.height / (2 * this.options.ticks.fontSize)));
|
||||
}
|
||||
|
||||
// Make sure we always have at least 2 ticks
|
||||
maxTicks = Math.max(2, maxTicks);
|
||||
|
||||
// To get a "nice" value for the tick spacing, we will use the appropriately named
|
||||
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
|
||||
// for details.
|
||||
|
||||
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
|
||||
// do nothing since that would make the chart weird. If the user really wants a weird chart
|
||||
// axis, they can manually override it
|
||||
if (this.options.ticks.beginAtZero) {
|
||||
var minSign = helpers.sign(this.min);
|
||||
var maxSign = helpers.sign(this.max);
|
||||
|
||||
if (minSign < 0 && maxSign < 0) {
|
||||
// move the top up to 0
|
||||
this.max = 0;
|
||||
} else if (minSign > 0 && maxSign > 0) {
|
||||
// move the botttom down to 0
|
||||
this.min = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var niceRange = helpers.niceNum(this.max - this.min, false);
|
||||
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
|
||||
var niceMin = Math.floor(this.min / spacing) * spacing;
|
||||
var niceMax = Math.ceil(this.max / spacing) * spacing;
|
||||
|
||||
// Put the values into the ticks array
|
||||
for (var j = niceMin; j <= niceMax; j += spacing) {
|
||||
this.ticks.push(j);
|
||||
}
|
||||
|
||||
if (this.options.position == "left" || this.options.position == "right") {
|
||||
// We are in a vertical orientation. The top value is the highest. So reverse the array
|
||||
this.ticks.reverse();
|
||||
}
|
||||
|
||||
// At this point, we need to update our max and min given the tick values since we have expanded the
|
||||
// range of the scale
|
||||
this.max = helpers.max(this.ticks);
|
||||
this.min = helpers.min(this.ticks);
|
||||
|
||||
if (this.options.ticks.reverse) {
|
||||
this.ticks.reverse();
|
||||
|
||||
this.start = this.max;
|
||||
this.end = this.min;
|
||||
} else {
|
||||
this.start = this.min;
|
||||
this.end = this.max;
|
||||
}
|
||||
|
||||
this.zeroLineIndex = this.ticks.indexOf(0);
|
||||
},
|
||||
|
||||
// Utils
|
||||
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
|
||||
// This must be called after fit has been run so that
|
||||
// this.left, this.top, this.right, and this.bottom have been defined
|
||||
var pixel;
|
||||
var range = this.end - this.start;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
pixel = this.left + (innerWidth / range * (this.getRightValue(value) - this.start));
|
||||
return Math.round(pixel + this.paddingLeft);
|
||||
} else {
|
||||
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
|
||||
pixel = (this.bottom - this.paddingBottom) - (innerHeight / range * (this.getRightValue(value) - this.start));
|
||||
return Math.round(pixel);
|
||||
}
|
||||
},
|
||||
|
||||
// Get the correct value. If the value type is object get the x or y based on whether we are horizontal or not
|
||||
getRightValue: function(rawValue) {
|
||||
return (typeof(rawValue) === "object" && rawValue !== null) ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
|
||||
},
|
||||
|
||||
});
|
||||
Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig);
|
||||
|
||||
}).call(this);
|
||||
@@ -1,161 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
position: "left",
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
template: "<%var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));if (remain === 1 || remain === 2 || remain === 5) {%><%=value.toExponential()%><%} else {%><%= null %><%}%>",
|
||||
}
|
||||
};
|
||||
|
||||
var LogarithmicScale = Chart.Scale.extend({
|
||||
buildTicks: function() {
|
||||
|
||||
// Calculate Range (we may break this out into it's own lifecycle function)
|
||||
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
var values = [];
|
||||
|
||||
if (this.options.stacked) {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(rawValue, index) {
|
||||
|
||||
var value = this.getRightValue(rawValue);
|
||||
|
||||
values[index] = values[index] || 0;
|
||||
|
||||
if (this.options.relativePoints) {
|
||||
values[index] = 100;
|
||||
} else {
|
||||
// Don't need to split positive and negative since the log scale can't handle a 0 crossing
|
||||
values[index] += value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.min = helpers.min(values);
|
||||
this.max = helpers.max(values);
|
||||
|
||||
} else {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(rawValue, index) {
|
||||
var value = this.getRightValue(rawValue);
|
||||
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.min === this.max) {
|
||||
if (this.min !== 0 && this.min !== null) {
|
||||
this.min = Math.pow(10, Math.floor(helpers.log10(this.min)) - 1);
|
||||
this.max = Math.pow(10, Math.floor(helpers.log10(this.max)) + 1);
|
||||
} else {
|
||||
this.min = 1;
|
||||
this.max = 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reset the ticks array. Later on, we will draw a grid line at these positions
|
||||
// The array simply contains the numerical value of the spots where ticks will be
|
||||
this.tickValues = [];
|
||||
|
||||
// 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 minExponent = Math.floor(helpers.log10(this.min));
|
||||
var maxExponent = Math.ceil(helpers.log10(this.max));
|
||||
|
||||
for (var exponent = minExponent; exponent < maxExponent; ++exponent) {
|
||||
for (var i = 1; i < 10; ++i) {
|
||||
this.tickValues.push(i * Math.pow(10, exponent));
|
||||
}
|
||||
}
|
||||
|
||||
this.tickValues.push(1.0 * Math.pow(10, maxExponent));
|
||||
|
||||
if (this.options.position == "left" || this.options.position == "right") {
|
||||
// We are in a vertical orientation. The top value is the highest. So reverse the array
|
||||
this.tickValues.reverse();
|
||||
}
|
||||
|
||||
// At this point, we need to update our max and min given the tick values since we have expanded the
|
||||
// range of the scale
|
||||
this.max = helpers.max(this.tickValues);
|
||||
this.min = helpers.min(this.tickValues);
|
||||
|
||||
if (this.options.ticks.reverse) {
|
||||
this.tickValues.reverse();
|
||||
|
||||
this.start = this.max;
|
||||
this.end = this.min;
|
||||
} else {
|
||||
this.start = this.min;
|
||||
this.end = this.max;
|
||||
}
|
||||
|
||||
this.ticks = this.tickValues.slice();
|
||||
},
|
||||
// Get the correct value. If the value type is object get the x or y based on whether we are horizontal or not
|
||||
getRightValue: function(rawValue) {
|
||||
return typeof rawValue === "object" ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
|
||||
},
|
||||
getPixelForTick: function(index, includeOffset) {
|
||||
return this.getPixelForValue(this.tickValues[index], null, null, includeOffset);
|
||||
},
|
||||
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
|
||||
var pixel;
|
||||
|
||||
var newVal = this.getRightValue(value);
|
||||
var range = helpers.log10(this.end) - helpers.log10(this.start);
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
|
||||
if (newVal === 0) {
|
||||
pixel = this.left + this.paddingLeft;
|
||||
} else {
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
pixel = this.left + (innerWidth / range * (helpers.log10(newVal) - helpers.log10(this.start)));
|
||||
return pixel + this.paddingLeft;
|
||||
}
|
||||
} else {
|
||||
// Bottom - top since pixels increase downard on a screen
|
||||
if (newVal === 0) {
|
||||
pixel = this.top + this.paddingTop;
|
||||
} else {
|
||||
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
|
||||
return (this.bottom - this.paddingBottom) - (innerHeight / range * (helpers.log10(newVal) - helpers.log10(this.start)));
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig);
|
||||
|
||||
}).call(this);
|
||||
@@ -1,396 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to animate scaling the chart from the centre
|
||||
animate: true,
|
||||
lineArc: false,
|
||||
position: "chartArea",
|
||||
|
||||
angleLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1
|
||||
},
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
showLabelBackdrop: true,
|
||||
|
||||
//String - The colour of the label backdrop
|
||||
backdropColor: "rgba(255,255,255,0.75)",
|
||||
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
backdropPaddingY: 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
backdropPaddingX: 2,
|
||||
},
|
||||
|
||||
pointLabels: {
|
||||
//String - Point label font declaration
|
||||
fontFamily: "'Arial'",
|
||||
|
||||
//String - Point label font weight
|
||||
fontStyle: "normal",
|
||||
|
||||
//Number - Point label font size in pixels
|
||||
fontSize: 10,
|
||||
|
||||
//String - Point label font colour
|
||||
fontColor: "#666",
|
||||
},
|
||||
};
|
||||
|
||||
var LinearRadialScale = Chart.Scale.extend({
|
||||
getValueCount: function() {
|
||||
return this.data.labels.length;
|
||||
},
|
||||
setDimensions: function() {
|
||||
// Set the unconstrained dimension before label rotation
|
||||
this.width = this.maxWidth;
|
||||
this.height = this.maxHeight;
|
||||
this.xCenter = Math.round(this.width / 2);
|
||||
this.yCenter = Math.round(this.height / 2);
|
||||
|
||||
var minSize = helpers.min([this.height, this.width]);
|
||||
this.drawingArea = (this.options.display) ? (minSize / 2) - (this.options.ticks.fontSize / 2 + this.options.ticks.backdropPaddingY) : (minSize / 2);
|
||||
},
|
||||
buildTicks: function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
if (value === null) return;
|
||||
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
if (this.min === this.max) {
|
||||
this.min--;
|
||||
this.max++;
|
||||
}
|
||||
|
||||
this.ticks = [];
|
||||
|
||||
// Figure out what the max number of ticks we can support it is based on the size of
|
||||
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
|
||||
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
|
||||
// the graph
|
||||
var maxTicks = Math.min(11, Math.ceil(this.drawingArea / (1.5 * this.options.ticks.fontSize)));
|
||||
maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks
|
||||
|
||||
// To get a "nice" value for the tick spacing, we will use the appropriately named
|
||||
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
|
||||
// for details.
|
||||
|
||||
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
|
||||
// do nothing since that would make the chart weird. If the user really wants a weird chart
|
||||
// axis, they can manually override it
|
||||
if (this.options.ticks.beginAtZero) {
|
||||
var minSign = helpers.sign(this.min);
|
||||
var maxSign = helpers.sign(this.max);
|
||||
|
||||
if (minSign < 0 && maxSign < 0) {
|
||||
// move the top up to 0
|
||||
this.max = 0;
|
||||
} else if (minSign > 0 && maxSign > 0) {
|
||||
// move the botttom down to 0
|
||||
this.min = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var niceRange = helpers.niceNum(this.max - this.min, false);
|
||||
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
|
||||
var niceMin = Math.floor(this.min / spacing) * spacing;
|
||||
var niceMax = Math.ceil(this.max / spacing) * spacing;
|
||||
|
||||
// Put the values into the ticks array
|
||||
for (var j = niceMin; j <= niceMax; j += spacing) {
|
||||
this.ticks.push(j);
|
||||
}
|
||||
|
||||
// At this point, we need to update our max and min given the tick values since we have expanded the
|
||||
// range of the scale
|
||||
this.max = helpers.max(this.ticks);
|
||||
this.min = helpers.min(this.ticks);
|
||||
|
||||
if (this.options.ticks.reverse) {
|
||||
this.ticks.reverse();
|
||||
|
||||
this.start = this.max;
|
||||
this.end = this.min;
|
||||
} else {
|
||||
this.start = this.min;
|
||||
this.end = this.max;
|
||||
}
|
||||
|
||||
this.zeroLineIndex = this.ticks.indexOf(0);
|
||||
},
|
||||
getCircumference: function() {
|
||||
return ((Math.PI * 2) / this.getValueCount());
|
||||
},
|
||||
fit: function() {
|
||||
/*
|
||||
* Right, this is really confusing and there is a lot of maths going on here
|
||||
* The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
|
||||
*
|
||||
* Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
|
||||
*
|
||||
* Solution:
|
||||
*
|
||||
* We assume the radius of the polygon is half the size of the canvas at first
|
||||
* at each index we check if the text overlaps.
|
||||
*
|
||||
* Where it does, we store that angle and that index.
|
||||
*
|
||||
* After finding the largest index and angle we calculate how much we need to remove
|
||||
* from the shape radius to move the point inwards by that x.
|
||||
*
|
||||
* We average the left and right distances to get the maximum shape radius that can fit in the box
|
||||
* along with labels.
|
||||
*
|
||||
* Once we have that, we can find the centre point for the chart, by taking the x text protrusion
|
||||
* on each side, removing that from the size, halving it and adding the left x protrusion width.
|
||||
*
|
||||
* This will mean we have a shape fitted to the canvas, as large as it can be with the labels
|
||||
* and position it in the most space efficient manner
|
||||
*
|
||||
* https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
|
||||
*/
|
||||
|
||||
|
||||
// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
|
||||
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
|
||||
var largestPossibleRadius = helpers.min([(this.height / 2 - this.options.pointLabels.fontSize - 5), this.width / 2]),
|
||||
pointPosition,
|
||||
i,
|
||||
textWidth,
|
||||
halfTextWidth,
|
||||
furthestRight = this.width,
|
||||
furthestRightIndex,
|
||||
furthestRightAngle,
|
||||
furthestLeft = 0,
|
||||
furthestLeftIndex,
|
||||
furthestLeftAngle,
|
||||
xProtrusionLeft,
|
||||
xProtrusionRight,
|
||||
radiusReductionRight,
|
||||
radiusReductionLeft,
|
||||
maxWidthRadius;
|
||||
this.ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily);
|
||||
for (i = 0; i < this.getValueCount(); i++) {
|
||||
// 5px to space the text slightly out - similar to what we do in the draw function.
|
||||
pointPosition = this.getPointPosition(i, largestPossibleRadius);
|
||||
textWidth = this.ctx.measureText(helpers.template(this.options.ticks.template, {
|
||||
value: this.data.labels[i]
|
||||
})).width + 5;
|
||||
if (i === 0 || i === this.getValueCount() / 2) {
|
||||
// If we're at index zero, or exactly the middle, we're at exactly the top/bottom
|
||||
// of the radar chart, so text will be aligned centrally, so we'll half it and compare
|
||||
// w/left and right text sizes
|
||||
halfTextWidth = textWidth / 2;
|
||||
if (pointPosition.x + halfTextWidth > furthestRight) {
|
||||
furthestRight = pointPosition.x + halfTextWidth;
|
||||
furthestRightIndex = i;
|
||||
}
|
||||
if (pointPosition.x - halfTextWidth < furthestLeft) {
|
||||
furthestLeft = pointPosition.x - halfTextWidth;
|
||||
furthestLeftIndex = i;
|
||||
}
|
||||
} else if (i < this.getValueCount() / 2) {
|
||||
// Less than half the values means we'll left align the text
|
||||
if (pointPosition.x + textWidth > furthestRight) {
|
||||
furthestRight = pointPosition.x + textWidth;
|
||||
furthestRightIndex = i;
|
||||
}
|
||||
} else if (i > this.getValueCount() / 2) {
|
||||
// More than half the values means we'll right align the text
|
||||
if (pointPosition.x - textWidth < furthestLeft) {
|
||||
furthestLeft = pointPosition.x - textWidth;
|
||||
furthestLeftIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xProtrusionLeft = furthestLeft;
|
||||
xProtrusionRight = Math.ceil(furthestRight - this.width);
|
||||
|
||||
furthestRightAngle = this.getIndexAngle(furthestRightIndex);
|
||||
furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
|
||||
|
||||
radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
|
||||
radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
|
||||
|
||||
// Ensure we actually need to reduce the size of the chart
|
||||
radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
|
||||
radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
|
||||
|
||||
this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2);
|
||||
this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
|
||||
},
|
||||
setCenterPoint: function(leftMovement, rightMovement) {
|
||||
|
||||
var maxRight = this.width - rightMovement - this.drawingArea,
|
||||
maxLeft = leftMovement + this.drawingArea;
|
||||
|
||||
this.xCenter = Math.round(((maxLeft + maxRight) / 2) + this.left);
|
||||
// Always vertically in the centre as the text height doesn't change
|
||||
this.yCenter = Math.round((this.height / 2) + this.top);
|
||||
},
|
||||
|
||||
getIndexAngle: function(index) {
|
||||
var angleMultiplier = (Math.PI * 2) / this.getValueCount();
|
||||
// Start from the top instead of right, so remove a quarter of the circle
|
||||
|
||||
return index * angleMultiplier - (Math.PI / 2);
|
||||
},
|
||||
getDistanceFromCenterForValue: function(value) {
|
||||
if (value === null) return 0; // null always in center
|
||||
// Take into account half font size + the yPadding of the top value
|
||||
var scalingFactor = this.drawingArea / (this.max - this.min);
|
||||
if (this.options.reverse) {
|
||||
return (this.max - value) * scalingFactor;
|
||||
} else {
|
||||
return (value - this.min) * scalingFactor;
|
||||
}
|
||||
},
|
||||
getPointPosition: function(index, distanceFromCenter) {
|
||||
var thisAngle = this.getIndexAngle(index);
|
||||
return {
|
||||
x: (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
|
||||
y: (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
|
||||
};
|
||||
},
|
||||
getPointPositionForValue: function(index, value) {
|
||||
return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
|
||||
},
|
||||
draw: function() {
|
||||
if (this.options.display) {
|
||||
var ctx = this.ctx;
|
||||
helpers.each(this.ticks, function(label, index) {
|
||||
// Don't draw a centre value (if it is minimum)
|
||||
if (index > 0 || this.options.reverse) {
|
||||
var yCenterOffset = this.getDistanceFromCenterForValue(this.ticks[index]);
|
||||
var yHeight = this.yCenter - yCenterOffset;
|
||||
|
||||
// Draw circular lines around the scale
|
||||
if (this.options.gridLines.show) {
|
||||
ctx.strokeStyle = this.options.gridLines.color;
|
||||
ctx.lineWidth = this.options.gridLines.lineWidth;
|
||||
|
||||
if (this.options.lineArc) {
|
||||
// Draw circular arcs between the points
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
} else {
|
||||
// Draw straight lines connecting each index
|
||||
ctx.beginPath();
|
||||
for (var i = 0; i < this.getValueCount(); i++) {
|
||||
var pointPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.ticks[index]));
|
||||
if (i === 0) {
|
||||
ctx.moveTo(pointPosition.x, pointPosition.y);
|
||||
} else {
|
||||
ctx.lineTo(pointPosition.x, pointPosition.y);
|
||||
}
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.ticks.show) {
|
||||
ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
|
||||
if (this.options.ticks.showLabelBackdrop) {
|
||||
var labelWidth = ctx.measureText(label).width;
|
||||
ctx.fillStyle = this.options.ticks.backdropColor;
|
||||
ctx.fillRect(
|
||||
this.xCenter - labelWidth / 2 - this.options.ticks.backdropPaddingX,
|
||||
yHeight - this.options.ticks.fontSize / 2 - this.options.ticks.backdropPaddingY,
|
||||
labelWidth + this.options.ticks.backdropPaddingX * 2,
|
||||
this.options.ticks.fontSize + this.options.ticks.backdropPaddingY * 2
|
||||
);
|
||||
}
|
||||
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = this.options.ticks.fontColor;
|
||||
ctx.fillText(label, this.xCenter, yHeight);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (!this.options.lineArc) {
|
||||
ctx.lineWidth = this.options.angleLines.lineWidth;
|
||||
ctx.strokeStyle = this.options.angleLines.color;
|
||||
|
||||
for (var i = this.getValueCount() - 1; i >= 0; i--) {
|
||||
if (this.options.angleLines.show) {
|
||||
var outerPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max));
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.xCenter, this.yCenter);
|
||||
ctx.lineTo(outerPosition.x, outerPosition.y);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
// Extra 3px out for some label spacing
|
||||
var pointLabelPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max) + 5);
|
||||
ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily);
|
||||
ctx.fillStyle = this.options.pointLabels.fontColor;
|
||||
|
||||
var labelsCount = this.data.labels.length,
|
||||
halfLabelsCount = this.data.labels.length / 2,
|
||||
quarterLabelsCount = halfLabelsCount / 2,
|
||||
upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
|
||||
exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
|
||||
if (i === 0) {
|
||||
ctx.textAlign = 'center';
|
||||
} else if (i === halfLabelsCount) {
|
||||
ctx.textAlign = 'center';
|
||||
} else if (i < halfLabelsCount) {
|
||||
ctx.textAlign = 'left';
|
||||
} else {
|
||||
ctx.textAlign = 'right';
|
||||
}
|
||||
|
||||
// Set the correct text baseline based on outer positioning
|
||||
if (exactQuarter) {
|
||||
ctx.textBaseline = 'middle';
|
||||
} else if (upperHalf) {
|
||||
ctx.textBaseline = 'bottom';
|
||||
} else {
|
||||
ctx.textBaseline = 'top';
|
||||
}
|
||||
|
||||
ctx.fillText(this.data.labels[i], pointLabelPosition.x, pointLabelPosition.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig);
|
||||
|
||||
|
||||
}).call(this);
|
||||
@@ -1,194 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
if (!window.moment) {
|
||||
console.warn('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at http://momentjs.com/');
|
||||
return;
|
||||
}
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var time = {
|
||||
units: [
|
||||
'millisecond',
|
||||
'second',
|
||||
'minute',
|
||||
'hour',
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'quarter',
|
||||
'year',
|
||||
],
|
||||
unit: {
|
||||
'millisecond': {
|
||||
display: 'SSS [ms]', // 002 ms
|
||||
maxStep: 1000,
|
||||
},
|
||||
'second': {
|
||||
display: 'h:mm:ss a', // 11:20:01 AM
|
||||
maxStep: 60,
|
||||
},
|
||||
'minute': {
|
||||
display: 'h:mm:ss a', // 11:20:01 AM
|
||||
maxStep: 60,
|
||||
},
|
||||
'hour': {
|
||||
display: 'MMM D, hA', // Sept 4, 5PM
|
||||
maxStep: 24,
|
||||
},
|
||||
'day': {
|
||||
display: 'll', // Sep 4 2015
|
||||
maxStep: 7,
|
||||
},
|
||||
'week': {
|
||||
display: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
|
||||
maxStep: 4.3333,
|
||||
},
|
||||
'month': {
|
||||
display: 'MMM YYYY', // Sept 2015
|
||||
maxStep: 12,
|
||||
},
|
||||
'quarter': {
|
||||
display: '[Q]Q - YYYY', // Q3
|
||||
maxStep: 4,
|
||||
},
|
||||
'year': {
|
||||
display: 'YYYY', // 2015
|
||||
maxStep: false,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var defaultConfig = {
|
||||
position: "bottom",
|
||||
|
||||
time: {
|
||||
format: false, // false == date objects or use pattern string from http://momentjs.com/docs/#/parsing/string-format/
|
||||
unit: false, // false == automatic or override with week, month, year, etc.
|
||||
round: false, // none, or override with week, month, year, etc.
|
||||
displayFormat: false, // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
|
||||
},
|
||||
};
|
||||
|
||||
var TimeScale = Chart.Scale.extend({
|
||||
buildTicks: function(index) {
|
||||
|
||||
this.ticks = [];
|
||||
this.labelMoments = [];
|
||||
|
||||
// Parse each label into a moment
|
||||
this.data.labels.forEach(function(label, index) {
|
||||
var labelMoment = this.parseTime(label);
|
||||
if (this.options.time.round) {
|
||||
labelMoment.startOf(this.options.time.round);
|
||||
}
|
||||
this.labelMoments.push(labelMoment);
|
||||
}, this);
|
||||
|
||||
// Find the first and last moments, and range
|
||||
this.firstTick = moment.min.call(this, this.labelMoments).clone();
|
||||
this.lastTick = moment.max.call(this, this.labelMoments).clone();
|
||||
|
||||
// Set unit override if applicable
|
||||
if (this.options.time.unit) {
|
||||
this.tickUnit = this.options.time.unit || 'day';
|
||||
this.displayFormat = time.unit[this.tickUnit].display;
|
||||
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit, true));
|
||||
} else {
|
||||
// Determine the smallest needed unit of the time
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var labelCapacity = innerWidth / this.options.ticks.fontSize + 4;
|
||||
var buffer = this.options.time.round ? 0 : 2;
|
||||
|
||||
// Start as small as possible
|
||||
this.tickUnit = 'millisecond';
|
||||
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit, true) + buffer);
|
||||
this.displayFormat = time.unit[this.tickUnit].display;
|
||||
|
||||
// Work our way up to comfort
|
||||
helpers.each(time.units, function(format) {
|
||||
if (this.tickRange <= labelCapacity) {
|
||||
return;
|
||||
}
|
||||
this.tickUnit = format;
|
||||
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit) + buffer);
|
||||
this.displayFormat = time.unit[format].display;
|
||||
|
||||
}, this);
|
||||
}
|
||||
|
||||
this.firstTick.startOf(this.tickUnit);
|
||||
this.lastTick.endOf(this.tickUnit);
|
||||
this.smallestLabelSeparation = this.width;
|
||||
|
||||
var i = 0;
|
||||
|
||||
for (i = 1; i < this.labelMoments.length; i++) {
|
||||
this.smallestLabelSeparation = Math.min(this.smallestLabelSeparation, this.labelMoments[i].diff(this.labelMoments[i - 1], this.tickUnit, true));
|
||||
}
|
||||
|
||||
// Tick displayFormat override
|
||||
if (this.options.time.displayFormat) {
|
||||
this.displayFormat = this.options.time.displayFormat;
|
||||
}
|
||||
|
||||
// For every unit in between the first and last moment, create a moment and add it to the ticks tick
|
||||
for (i = 0; i <= this.tickRange; ++i) {
|
||||
this.ticks.push(this.firstTick.clone().add(i, this.tickUnit));
|
||||
}
|
||||
},
|
||||
convertTicksToLabels: function() {
|
||||
this.ticks = this.ticks.map(function(tick, index, ticks) {
|
||||
var formattedTick = tick.format(this.options.time.displayFormat ? this.options.time.displayFormat : time.unit[this.tickUnit].display);
|
||||
|
||||
if (this.options.ticks.userCallback) {
|
||||
return this.options.ticks.userCallback(formattedTick, index, ticks);
|
||||
} else {
|
||||
return formattedTick;
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
|
||||
|
||||
var offset = this.labelMoments[index].diff(this.firstTick, this.tickUnit, true);
|
||||
|
||||
var decimal = offset / this.tickRange;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var valueWidth = innerWidth / Math.max(this.ticks.length - 1, 1);
|
||||
var valueOffset = (innerWidth * decimal) + this.paddingLeft;
|
||||
|
||||
return this.left + Math.round(valueOffset);
|
||||
} else {
|
||||
//return this.top + (decimal * (this.height / this.ticks.length));
|
||||
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
|
||||
var valueHeight = innerHeight / Math.max(this.ticks.length - 1, 1);
|
||||
var heightOffset = (innerHeight * decimal) + this.paddingTop;
|
||||
|
||||
return this.top + Math.round(heightOffset);
|
||||
}
|
||||
},
|
||||
parseTime: function(label) {
|
||||
// Date objects
|
||||
if (typeof label.getMonth === 'function' || typeof label == 'number') {
|
||||
return moment(label);
|
||||
}
|
||||
// Moment support
|
||||
if (label.isValid && label.isValid()) {
|
||||
return label;
|
||||
}
|
||||
// Custom parsing (return an instance of moment)
|
||||
if (typeof this.options.time.format !== 'string' && this.options.time.format.call) {
|
||||
return this.options.time.format(label);
|
||||
}
|
||||
// Moment format parsing
|
||||
return moment(label, this.options.time.format);
|
||||
},
|
||||
});
|
||||
Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig);
|
||||
|
||||
}).call(this);
|
||||
@@ -1,586 +0,0 @@
|
||||
// Test the bar controller
|
||||
describe('Bar controller tests', function() {
|
||||
it('Should be constructed', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
|
||||
}, {
|
||||
xAxisID: 'myXAxis',
|
||||
yAxisID: 'myYAxis',
|
||||
data: []
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
expect(controller).not.toBe(undefined);
|
||||
expect(controller.index).toBe(1);
|
||||
expect(chart.data.datasets[1].metaData).toEqual([]);
|
||||
|
||||
controller.updateIndex(0);
|
||||
expect(controller.index).toBe(0);
|
||||
});
|
||||
|
||||
it('Should use the first scale IDs if the dataset does not specify them', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
|
||||
}, {
|
||||
data: []
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
expect(chart.data.datasets[1].xAxisID).toBe('firstXScaleID');
|
||||
expect(chart.data.datasets[1].yAxisID).toBe('firstYScaleID');
|
||||
});
|
||||
|
||||
it('should correctly count the number of bar datasets', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
type: 'line'
|
||||
}, {
|
||||
type: 'bar'
|
||||
}, {
|
||||
// no type, defaults to bar
|
||||
}]
|
||||
},
|
||||
config: {
|
||||
type: 'bar'
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
expect(controller.getBarCount()).toBe(2);
|
||||
});
|
||||
|
||||
it('Should create rectangle elements for each data item during initialization', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{}, {
|
||||
data: [10, 15, 0, -4]
|
||||
}]
|
||||
},
|
||||
config: {
|
||||
type: 'bar'
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
|
||||
expect(chart.data.datasets[1].metaData.length).toBe(4); // 4 rectangles created
|
||||
expect(chart.data.datasets[1].metaData[0] instanceof Chart.elements.Rectangle).toBe(true);
|
||||
expect(chart.data.datasets[1].metaData[1] instanceof Chart.elements.Rectangle).toBe(true);
|
||||
expect(chart.data.datasets[1].metaData[2] instanceof Chart.elements.Rectangle).toBe(true);
|
||||
expect(chart.data.datasets[1].metaData[3] instanceof Chart.elements.Rectangle).toBe(true);
|
||||
});
|
||||
|
||||
it('should remove elements', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{}, {
|
||||
data: [10, 15, 0, -4]
|
||||
}]
|
||||
},
|
||||
config: {
|
||||
type: 'bar'
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
controller.removeElement(1);
|
||||
expect(chart.data.datasets[1].metaData.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should update elements', function() {
|
||||
var data = {
|
||||
datasets: [{
|
||||
data: [1, 2],
|
||||
label: 'dataset1',
|
||||
xAxisID: 'firstXScaleID',
|
||||
yAxisID: 'firstYScaleID'
|
||||
}, {
|
||||
data: [10, 15, 0, -4],
|
||||
label: 'dataset2'
|
||||
}],
|
||||
labels: ['label1', 'label2', 'label3', 'label4']
|
||||
};
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
var VerticalScaleConstructor = Chart.scaleService.getScaleConstructor('linear');
|
||||
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
|
||||
verticalScaleConfig = Chart.helpers.scaleMerge(verticalScaleConfig, Chart.defaults.bar.scales.yAxes[0]);
|
||||
var yScale = new VerticalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstYScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var verticalSize = yScale.update(50, 200);
|
||||
yScale.top = 0;
|
||||
yScale.left = 0;
|
||||
yScale.right = verticalSize.width;
|
||||
yScale.bottom = verticalSize.height;
|
||||
|
||||
var HorizontalScaleConstructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var horizontalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
horizontalScaleConfig = Chart.helpers.scaleMerge(horizontalScaleConfig, Chart.defaults.bar.scales.xAxes[0]);
|
||||
var xScale = new HorizontalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstXScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var horizontalSize = xScale.update(200, 50);
|
||||
xScale.left = yScale.right;
|
||||
xScale.top = yScale.bottom;
|
||||
xScale.right = horizontalSize.width + xScale.left;
|
||||
xScale.bottom = horizontalSize.height + xScale.top;
|
||||
|
||||
var chart = {
|
||||
data: data,
|
||||
config: {
|
||||
type: 'bar'
|
||||
},
|
||||
options: {
|
||||
elements: {
|
||||
rectangle: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
firstXScaleID: xScale,
|
||||
firstYScaleID: yScale,
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
|
||||
chart.data.datasets[1].data = [1, 2]; // remove 2 items
|
||||
controller.buildOrUpdateElements();
|
||||
controller.update();
|
||||
|
||||
expect(chart.data.datasets[1].metaData.length).toBe(2);
|
||||
|
||||
var bar1 = chart.data.datasets[1].metaData[0];
|
||||
var bar2 = chart.data.datasets[1].metaData[1];
|
||||
|
||||
expect(bar1._datasetIndex).toBe(1);
|
||||
expect(bar1._index).toBe(0);
|
||||
expect(bar1._xScale).toBe(chart.scales.firstXScaleID);
|
||||
expect(bar1._yScale).toBe(chart.scales.firstYScaleID);
|
||||
expect(bar1._model).toEqual({
|
||||
x: 106.80000000000003,
|
||||
y: 194,
|
||||
label: 'label1',
|
||||
datasetLabel: 'dataset2',
|
||||
|
||||
base: 194,
|
||||
width: 12.240000000000002,
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
});
|
||||
|
||||
expect(bar2._datasetIndex).toBe(1);
|
||||
expect(bar2._index).toBe(1);
|
||||
expect(bar2._xScale).toBe(chart.scales.firstXScaleID);
|
||||
expect(bar2._yScale).toBe(chart.scales.firstYScaleID);
|
||||
expect(bar2._model).toEqual({
|
||||
x: 140.8,
|
||||
y: -15,
|
||||
label: 'label2',
|
||||
datasetLabel: 'dataset2',
|
||||
|
||||
base: 194,
|
||||
width: 12.240000000000002,
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
});
|
||||
|
||||
chart.data.datasets[1].data = [1, 2, 3];
|
||||
controller.buildOrUpdateElements();
|
||||
controller.update();
|
||||
|
||||
expect(chart.data.datasets[1].metaData.length).toBe(3); // should add a new meta data item
|
||||
});
|
||||
|
||||
it ('should draw all bars', function() {
|
||||
var data = {
|
||||
datasets: [{}, {
|
||||
data: [10, 15, 0, -4],
|
||||
label: 'dataset2',
|
||||
xAxisID: 'firstXScaleID',
|
||||
yAxisID: 'firstYScaleID'
|
||||
}],
|
||||
labels: ['label1', 'label2', 'label3', 'label4']
|
||||
};
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
var VerticalScaleConstructor = Chart.scaleService.getScaleConstructor('linear');
|
||||
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
|
||||
verticalScaleConfig = Chart.helpers.scaleMerge(verticalScaleConfig, Chart.defaults.bar.scales.yAxes[0]);
|
||||
var yScale = new VerticalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstYScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var verticalSize = yScale.update(50, 200);
|
||||
yScale.top = 0;
|
||||
yScale.left = 0;
|
||||
yScale.right = verticalSize.width;
|
||||
yScale.bottom = verticalSize.height;
|
||||
|
||||
var HorizontalScaleConstructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var horizontalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
horizontalScaleConfig = Chart.helpers.scaleMerge(horizontalScaleConfig, Chart.defaults.bar.scales.xAxes[0]);
|
||||
var xScale = new HorizontalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstXScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var horizontalSize = xScale.update(200, 50);
|
||||
xScale.left = yScale.right;
|
||||
xScale.top = yScale.bottom;
|
||||
xScale.right = horizontalSize.width + xScale.left;
|
||||
xScale.bottom = horizontalSize.height + xScale.top;
|
||||
|
||||
|
||||
var chart = {
|
||||
data: data,
|
||||
config: {
|
||||
type: 'bar'
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
firstXScaleID: xScale,
|
||||
firstYScaleID: yScale,
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
|
||||
spyOn(chart.data.datasets[1].metaData[0], 'draw');
|
||||
spyOn(chart.data.datasets[1].metaData[1], 'draw');
|
||||
spyOn(chart.data.datasets[1].metaData[2], 'draw');
|
||||
spyOn(chart.data.datasets[1].metaData[3], 'draw');
|
||||
|
||||
controller.draw();
|
||||
|
||||
expect(chart.data.datasets[1].metaData[0].draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[1].metaData[1].draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[1].metaData[2].draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[1].metaData[3].draw.calls.count()).toBe(1);
|
||||
});
|
||||
|
||||
it ('should set hover styles on rectangles', function() {
|
||||
var data = {
|
||||
datasets: [{}, {
|
||||
data: [10, 15, 0, -4],
|
||||
label: 'dataset2',
|
||||
xAxisID: 'firstXScaleID',
|
||||
yAxisID: 'firstYScaleID'
|
||||
}],
|
||||
labels: ['label1', 'label2', 'label3', 'label4']
|
||||
};
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
var VerticalScaleConstructor = Chart.scaleService.getScaleConstructor('linear');
|
||||
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
|
||||
verticalScaleConfig = Chart.helpers.scaleMerge(verticalScaleConfig, Chart.defaults.bar.scales.yAxes[0]);
|
||||
var yScale = new VerticalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstYScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var verticalSize = yScale.update(50, 200);
|
||||
yScale.top = 0;
|
||||
yScale.left = 0;
|
||||
yScale.right = verticalSize.width;
|
||||
yScale.bottom = verticalSize.height;
|
||||
|
||||
var HorizontalScaleConstructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var horizontalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
horizontalScaleConfig = Chart.helpers.scaleMerge(horizontalScaleConfig, Chart.defaults.bar.scales.xAxes[0]);
|
||||
var xScale = new HorizontalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstXScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var horizontalSize = xScale.update(200, 50);
|
||||
xScale.left = yScale.right;
|
||||
xScale.top = yScale.bottom;
|
||||
xScale.right = horizontalSize.width + xScale.left;
|
||||
xScale.bottom = horizontalSize.height + xScale.top;
|
||||
|
||||
|
||||
var chart = {
|
||||
data: data,
|
||||
config: {
|
||||
type: 'bar'
|
||||
},
|
||||
options: {
|
||||
elements: {
|
||||
rectangle: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
firstXScaleID: xScale,
|
||||
firstYScaleID: yScale,
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
controller.update();
|
||||
var bar = chart.data.datasets[1].metaData[0];
|
||||
controller.setHoverStyle(bar);
|
||||
|
||||
expect(bar._model.backgroundColor).toBe('rgb(230, 0, 0)');
|
||||
expect(bar._model.borderColor).toBe('rgb(0, 0, 230)');
|
||||
expect(bar._model.borderWidth).toBe(2);
|
||||
|
||||
// Set a dataset style
|
||||
chart.data.datasets[1].hoverBackgroundColor = 'rgb(128, 128, 128)';
|
||||
chart.data.datasets[1].hoverBorderColor = 'rgb(0, 0, 0)';
|
||||
chart.data.datasets[1].hoverBorderWidth = 5;
|
||||
|
||||
controller.setHoverStyle(bar);
|
||||
|
||||
expect(bar._model.backgroundColor).toBe('rgb(128, 128, 128)');
|
||||
expect(bar._model.borderColor).toBe('rgb(0, 0, 0)');
|
||||
expect(bar._model.borderWidth).toBe(5);
|
||||
|
||||
// Should work with array styles so that we can set per bar
|
||||
chart.data.datasets[1].hoverBackgroundColor = ['rgb(255, 255, 255)', 'rgb(128, 128, 128)'];
|
||||
chart.data.datasets[1].hoverBorderColor = ['rgb(9, 9, 9)', 'rgb(0, 0, 0)'];
|
||||
chart.data.datasets[1].hoverBorderWidth = [2.5, 5];
|
||||
|
||||
controller.setHoverStyle(bar);
|
||||
|
||||
expect(bar._model.backgroundColor).toBe('rgb(255, 255, 255)');
|
||||
expect(bar._model.borderColor).toBe('rgb(9, 9, 9)');
|
||||
expect(bar._model.borderWidth).toBe(2.5);
|
||||
|
||||
// Should allow a custom style
|
||||
bar.custom = {
|
||||
hoverBackgroundColor: 'rgb(255, 0, 0)',
|
||||
hoverBorderColor: 'rgb(0, 255, 0)',
|
||||
hoverBorderWidth: 1.5
|
||||
};
|
||||
|
||||
controller.setHoverStyle(bar);
|
||||
|
||||
expect(bar._model.backgroundColor).toBe('rgb(255, 0, 0)');
|
||||
expect(bar._model.borderColor).toBe('rgb(0, 255, 0)');
|
||||
expect(bar._model.borderWidth).toBe(1.5);
|
||||
});
|
||||
|
||||
it ('should remove a hover style from a bar', function() {
|
||||
var data = {
|
||||
datasets: [{}, {
|
||||
data: [10, 15, 0, -4],
|
||||
label: 'dataset2',
|
||||
xAxisID: 'firstXScaleID',
|
||||
yAxisID: 'firstYScaleID'
|
||||
}],
|
||||
labels: ['label1', 'label2', 'label3', 'label4']
|
||||
};
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
var VerticalScaleConstructor = Chart.scaleService.getScaleConstructor('linear');
|
||||
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
|
||||
verticalScaleConfig = Chart.helpers.scaleMerge(verticalScaleConfig, Chart.defaults.bar.scales.yAxes[0]);
|
||||
var yScale = new VerticalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstYScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var verticalSize = yScale.update(50, 200);
|
||||
yScale.top = 0;
|
||||
yScale.left = 0;
|
||||
yScale.right = verticalSize.width;
|
||||
yScale.bottom = verticalSize.height;
|
||||
|
||||
var HorizontalScaleConstructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var horizontalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
horizontalScaleConfig = Chart.helpers.scaleMerge(horizontalScaleConfig, Chart.defaults.bar.scales.xAxes[0]);
|
||||
var xScale = new HorizontalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstXScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var horizontalSize = xScale.update(200, 50);
|
||||
xScale.left = yScale.right;
|
||||
xScale.top = yScale.bottom;
|
||||
xScale.right = horizontalSize.width + xScale.left;
|
||||
xScale.bottom = horizontalSize.height + xScale.top;
|
||||
|
||||
|
||||
var chart = {
|
||||
data: data,
|
||||
config: {
|
||||
type: 'bar'
|
||||
},
|
||||
options: {
|
||||
elements: {
|
||||
rectangle: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
firstXScaleID: xScale,
|
||||
firstYScaleID: yScale,
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.bar(chart, 1);
|
||||
controller.update();
|
||||
var bar = chart.data.datasets[1].metaData[0];
|
||||
|
||||
// Change default
|
||||
chart.options.elements.rectangle.backgroundColor = 'rgb(128, 128, 128)';
|
||||
chart.options.elements.rectangle.borderColor = 'rgb(15, 15, 15)';
|
||||
chart.options.elements.rectangle.borderWidth = 3.14;
|
||||
|
||||
// Remove to defaults
|
||||
controller.removeHoverStyle(bar);
|
||||
|
||||
expect(bar._model.backgroundColor).toBe('rgb(128, 128, 128)');
|
||||
expect(bar._model.borderColor).toBe('rgb(15, 15, 15)');
|
||||
expect(bar._model.borderWidth).toBe(3.14);
|
||||
|
||||
// Should work with array styles so that we can set per bar
|
||||
chart.data.datasets[1].backgroundColor = ['rgb(255, 255, 255)', 'rgb(128, 128, 128)'];
|
||||
chart.data.datasets[1].borderColor = ['rgb(9, 9, 9)', 'rgb(0, 0, 0)'];
|
||||
chart.data.datasets[1].borderWidth = [2.5, 5];
|
||||
|
||||
controller.removeHoverStyle(bar);
|
||||
|
||||
expect(bar._model.backgroundColor).toBe('rgb(255, 255, 255)');
|
||||
expect(bar._model.borderColor).toBe('rgb(9, 9, 9)');
|
||||
expect(bar._model.borderWidth).toBe(2.5);
|
||||
|
||||
// Should allow a custom style
|
||||
bar.custom = {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 255, 0)',
|
||||
borderWidth: 1.5
|
||||
};
|
||||
|
||||
controller.removeHoverStyle(bar);
|
||||
|
||||
expect(bar._model.backgroundColor).toBe('rgb(255, 0, 0)');
|
||||
expect(bar._model.borderColor).toBe('rgb(0, 255, 0)');
|
||||
expect(bar._model.borderWidth).toBe(1.5);
|
||||
});
|
||||
});
|
||||
@@ -1,431 +0,0 @@
|
||||
// Test the bar controller
|
||||
describe('Doughnut controller tests', function() {
|
||||
it('Should be constructed', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: []
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.doughnut(chart, 0);
|
||||
expect(controller).not.toBe(undefined);
|
||||
expect(controller.index).toBe(0);
|
||||
expect(chart.data.datasets[0].metaData).toEqual([]);
|
||||
|
||||
controller.updateIndex(1);
|
||||
expect(controller.index).toBe(1);
|
||||
});
|
||||
|
||||
it('Should create arc elements for each data item during initialization', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, 4]
|
||||
}]
|
||||
},
|
||||
config: {
|
||||
type: 'doughnut'
|
||||
},
|
||||
options: {
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.doughnut(chart, 0);
|
||||
|
||||
expect(chart.data.datasets[0].metaData.length).toBe(4); // 4 rectangles created
|
||||
expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Arc).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Arc).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[2] instanceof Chart.elements.Arc).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[3] instanceof Chart.elements.Arc).toBe(true);
|
||||
});
|
||||
|
||||
it ('Should remove elements', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, 4]
|
||||
}]
|
||||
},
|
||||
config: {
|
||||
type: 'doughnut'
|
||||
},
|
||||
options: {
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.doughnut(chart, 0);
|
||||
controller.removeElement(1);
|
||||
expect(chart.data.datasets[0].metaData.length).toBe(3);
|
||||
});
|
||||
|
||||
it ('Should reset and update elements', function() {
|
||||
var chart = {
|
||||
chart: {
|
||||
width: 100,
|
||||
height: 200,
|
||||
},
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, 4]
|
||||
}, {
|
||||
data: [1]
|
||||
}],
|
||||
labels: ['label0', 'label1', 'label2', 'label3']
|
||||
},
|
||||
config: {
|
||||
type: 'doughnut'
|
||||
},
|
||||
options: {
|
||||
animation: {
|
||||
animateRotate: false,
|
||||
animateScale: false
|
||||
},
|
||||
cutoutPercentage: 50,
|
||||
elements: {
|
||||
arc: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
hoverBackgroundColor: 'rgb(255, 255, 255)'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.doughnut(chart, 0);
|
||||
controller.reset(); // reset first
|
||||
|
||||
expect(chart.data.datasets[0].metaData[0]._model).toEqual({
|
||||
x: 50,
|
||||
y: 100,
|
||||
startAngle: Math.PI * -0.5,
|
||||
circumference: 2.166614539857563,
|
||||
outerRadius: 49,
|
||||
innerRadius: 36.75,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[1]._model).toEqual({
|
||||
x: 50,
|
||||
y: 100,
|
||||
startAngle: Math.PI * -0.5,
|
||||
circumference: 3.2499218097863447,
|
||||
outerRadius: 49,
|
||||
innerRadius: 36.75,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[2]._model).toEqual({
|
||||
x: 50,
|
||||
y: 100,
|
||||
startAngle: Math.PI * -0.5,
|
||||
circumference: 0,
|
||||
outerRadius: 49,
|
||||
innerRadius: 36.75,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[3]._model).toEqual({
|
||||
x: 50,
|
||||
y: 100,
|
||||
startAngle: Math.PI * -0.5,
|
||||
circumference: 0.8666458159430251,
|
||||
outerRadius: 49,
|
||||
innerRadius: 36.75,
|
||||
});
|
||||
|
||||
controller.update();
|
||||
|
||||
expect(chart.data.datasets[0].metaData[0]._model).toEqual({
|
||||
x: 50,
|
||||
y: 100,
|
||||
startAngle: Math.PI * -0.5,
|
||||
endAngle: 0.5958182130626666,
|
||||
circumference: 2.166614539857563,
|
||||
outerRadius: 49,
|
||||
innerRadius: 36.75,
|
||||
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
hoverBackgroundColor: 'rgb(255, 255, 255)',
|
||||
|
||||
label: 'label0',
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[1]._model).toEqual({
|
||||
x: 50,
|
||||
y: 100,
|
||||
startAngle: 0.5958182130626666,
|
||||
endAngle: 3.8457400228490113,
|
||||
circumference: 3.2499218097863447,
|
||||
outerRadius: 49,
|
||||
innerRadius: 36.75,
|
||||
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
hoverBackgroundColor: 'rgb(255, 255, 255)',
|
||||
|
||||
label: 'label1'
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[2]._model).toEqual({
|
||||
x: 50,
|
||||
y: 100,
|
||||
startAngle: 3.8457400228490113,
|
||||
endAngle: 3.8457400228490113,
|
||||
circumference: 0,
|
||||
outerRadius: 49,
|
||||
innerRadius: 36.75,
|
||||
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
hoverBackgroundColor: 'rgb(255, 255, 255)',
|
||||
|
||||
label: 'label2'
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[3]._model).toEqual({
|
||||
x: 50,
|
||||
y: 100,
|
||||
startAngle: 3.8457400228490113,
|
||||
endAngle: 4.712385838792036,
|
||||
circumference: 0.8666458159430251,
|
||||
outerRadius: 49,
|
||||
innerRadius: 36.75,
|
||||
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
hoverBackgroundColor: 'rgb(255, 255, 255)',
|
||||
|
||||
label: 'label3'
|
||||
});
|
||||
|
||||
// Change the amount of data and ensure that arcs are updated accordingly
|
||||
chart.data.datasets[0].data = [1, 2]; // remove 2 elements from dataset 0
|
||||
controller.buildOrUpdateElements();
|
||||
controller.update();
|
||||
|
||||
expect(chart.data.datasets[0].metaData.length).toBe(2);
|
||||
expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Arc).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Arc).toBe(true);
|
||||
|
||||
// Add data
|
||||
chart.data.datasets[0].data = [1, 2, 3, 4];
|
||||
controller.buildOrUpdateElements();
|
||||
controller.update();
|
||||
|
||||
expect(chart.data.datasets[0].metaData.length).toBe(4);
|
||||
expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Arc).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Arc).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[2] instanceof Chart.elements.Arc).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[3] instanceof Chart.elements.Arc).toBe(true);
|
||||
});
|
||||
|
||||
it ('should draw all arcs', function() {
|
||||
var chart = {
|
||||
chart: {
|
||||
width: 100,
|
||||
height: 200,
|
||||
},
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, 4]
|
||||
}],
|
||||
labels: ['label0', 'label1', 'label2', 'label3']
|
||||
},
|
||||
config: {
|
||||
type: 'doughnut'
|
||||
},
|
||||
options: {
|
||||
animation: {
|
||||
animateRotate: false,
|
||||
animateScale: false
|
||||
},
|
||||
cutoutPercentage: 50,
|
||||
elements: {
|
||||
arc: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
hoverBackgroundColor: 'rgb(255, 255, 255)'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.doughnut(chart, 0);
|
||||
|
||||
spyOn(chart.data.datasets[0].metaData[0], 'draw');
|
||||
spyOn(chart.data.datasets[0].metaData[1], 'draw');
|
||||
spyOn(chart.data.datasets[0].metaData[2], 'draw');
|
||||
spyOn(chart.data.datasets[0].metaData[3], 'draw');
|
||||
|
||||
controller.draw();
|
||||
|
||||
expect(chart.data.datasets[0].metaData[0].draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[0].metaData[1].draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[0].metaData[2].draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[0].metaData[3].draw.calls.count()).toBe(1);
|
||||
});
|
||||
|
||||
it ('should set the hover style of an arc', function() {
|
||||
var chart = {
|
||||
chart: {
|
||||
width: 100,
|
||||
height: 200,
|
||||
},
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, 4]
|
||||
}],
|
||||
labels: ['label0', 'label1', 'label2', 'label3']
|
||||
},
|
||||
config: {
|
||||
type: 'doughnut'
|
||||
},
|
||||
options: {
|
||||
animation: {
|
||||
animateRotate: false,
|
||||
animateScale: false
|
||||
},
|
||||
cutoutPercentage: 50,
|
||||
elements: {
|
||||
arc: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.doughnut(chart, 0);
|
||||
controller.reset(); // reset first
|
||||
controller.update();
|
||||
|
||||
var arc = chart.data.datasets[0].metaData[0];
|
||||
|
||||
controller.setHoverStyle(arc);
|
||||
|
||||
expect(arc._model.backgroundColor).toBe('rgb(230, 0, 0)');
|
||||
expect(arc._model.borderColor).toBe('rgb(0, 0, 230)');
|
||||
expect(arc._model.borderWidth).toBe(2);
|
||||
|
||||
// Set a dataset style to take precedence
|
||||
chart.data.datasets[0].hoverBackgroundColor = 'rgb(9, 9, 9)';
|
||||
chart.data.datasets[0].hoverBorderColor = 'rgb(18, 18, 18)';
|
||||
chart.data.datasets[0].hoverBorderWidth = 1.56;
|
||||
|
||||
controller.setHoverStyle(arc);
|
||||
|
||||
expect(arc._model.backgroundColor).toBe('rgb(9, 9, 9)');
|
||||
expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
|
||||
expect(arc._model.borderWidth).toBe(1.56);
|
||||
|
||||
// Dataset styles can be an array
|
||||
chart.data.datasets[0].hoverBackgroundColor = ['rgb(255, 255, 255)', 'rgb(9, 9, 9)'];
|
||||
chart.data.datasets[0].hoverBorderColor = ['rgb(18, 18, 18)'];
|
||||
chart.data.datasets[0].hoverBorderWidth = [0.1, 1.56];
|
||||
|
||||
controller.setHoverStyle(arc);
|
||||
|
||||
expect(arc._model.backgroundColor).toBe('rgb(255, 255, 255)');
|
||||
expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
|
||||
expect(arc._model.borderWidth).toBe(0.1);
|
||||
|
||||
// Element custom styles also work
|
||||
arc.custom = {
|
||||
hoverBackgroundColor: 'rgb(7, 7, 7)',
|
||||
hoverBorderColor: 'rgb(17, 17, 17)',
|
||||
hoverBorderWidth: 3.14159,
|
||||
};
|
||||
|
||||
controller.setHoverStyle(arc);
|
||||
|
||||
expect(arc._model.backgroundColor).toBe('rgb(7, 7, 7)');
|
||||
expect(arc._model.borderColor).toBe('rgb(17, 17, 17)');
|
||||
expect(arc._model.borderWidth).toBe(3.14159);
|
||||
});
|
||||
|
||||
it ('should unset the hover style of an arc', function() {
|
||||
var chart = {
|
||||
chart: {
|
||||
width: 100,
|
||||
height: 200,
|
||||
},
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, 4]
|
||||
}],
|
||||
labels: ['label0', 'label1', 'label2', 'label3']
|
||||
},
|
||||
config: {
|
||||
type: 'doughnut'
|
||||
},
|
||||
options: {
|
||||
animation: {
|
||||
animateRotate: false,
|
||||
animateScale: false
|
||||
},
|
||||
cutoutPercentage: 50,
|
||||
elements: {
|
||||
arc: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.doughnut(chart, 0);
|
||||
controller.reset(); // reset first
|
||||
controller.update();
|
||||
|
||||
var arc = chart.data.datasets[0].metaData[0];
|
||||
|
||||
controller.removeHoverStyle(arc);
|
||||
|
||||
expect(arc._model.backgroundColor).toBe('rgb(255, 0, 0)');
|
||||
expect(arc._model.borderColor).toBe('rgb(0, 0, 255)');
|
||||
expect(arc._model.borderWidth).toBe(2);
|
||||
|
||||
// Set a dataset style to take precedence
|
||||
chart.data.datasets[0].backgroundColor = 'rgb(9, 9, 9)';
|
||||
chart.data.datasets[0].borderColor = 'rgb(18, 18, 18)';
|
||||
chart.data.datasets[0].borderWidth = 1.56;
|
||||
|
||||
controller.removeHoverStyle(arc);
|
||||
|
||||
expect(arc._model.backgroundColor).toBe('rgb(9, 9, 9)');
|
||||
expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
|
||||
expect(arc._model.borderWidth).toBe(1.56);
|
||||
|
||||
// Dataset styles can be an array
|
||||
chart.data.datasets[0].backgroundColor = ['rgb(255, 255, 255)', 'rgb(9, 9, 9)'];
|
||||
chart.data.datasets[0].borderColor = ['rgb(18, 18, 18)'];
|
||||
chart.data.datasets[0].borderWidth = [0.1, 1.56];
|
||||
|
||||
controller.removeHoverStyle(arc);
|
||||
|
||||
expect(arc._model.backgroundColor).toBe('rgb(255, 255, 255)');
|
||||
expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
|
||||
expect(arc._model.borderWidth).toBe(0.1);
|
||||
|
||||
// Element custom styles also work
|
||||
arc.custom = {
|
||||
backgroundColor: 'rgb(7, 7, 7)',
|
||||
borderColor: 'rgb(17, 17, 17)',
|
||||
borderWidth: 3.14159,
|
||||
};
|
||||
|
||||
controller.removeHoverStyle(arc);
|
||||
|
||||
expect(arc._model.backgroundColor).toBe('rgb(7, 7, 7)');
|
||||
expect(arc._model.borderColor).toBe('rgb(17, 17, 17)');
|
||||
expect(arc._model.borderWidth).toBe(3.14159);
|
||||
});
|
||||
});
|
||||
@@ -1,918 +0,0 @@
|
||||
// Test the line controller
|
||||
describe('Line controller tests', function() {
|
||||
it('Should be constructed', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
xAxisID: 'myXAxis',
|
||||
yAxisID: 'myYAxis',
|
||||
data: []
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
expect(controller).not.toBe(undefined);
|
||||
expect(controller.index).toBe(0);
|
||||
expect(chart.data.datasets[0].metaData).toEqual([]);
|
||||
|
||||
controller.updateIndex(1);
|
||||
expect(controller.index).toBe(1);
|
||||
});
|
||||
|
||||
it('Should use the first scale IDs if the dataset does not specify them', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: []
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
expect(chart.data.datasets[0].xAxisID).toBe('firstXScaleID');
|
||||
expect(chart.data.datasets[0].yAxisID).toBe('firstYScaleID');
|
||||
});
|
||||
|
||||
it('Should create line elements and point elements for each data item during initialization', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, -4]
|
||||
}]
|
||||
},
|
||||
config: {
|
||||
type: 'line'
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
|
||||
expect(chart.data.datasets[0].metaData.length).toBe(4); // 4 points created
|
||||
expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[2] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[3] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaDataset instanceof Chart.elements.Line).toBe(true); // 1 line element
|
||||
});
|
||||
|
||||
it('should remove elements', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, -4]
|
||||
}]
|
||||
},
|
||||
config: {
|
||||
type: 'line'
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
controller.removeElement(0);
|
||||
expect(chart.data.datasets[0].metaData.length).toBe(3);
|
||||
});
|
||||
|
||||
it ('should draw all elements', function() {
|
||||
var chart = {
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, -4]
|
||||
}]
|
||||
},
|
||||
config: {
|
||||
type: 'line'
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
|
||||
spyOn(chart.data.datasets[0].metaDataset, 'draw');
|
||||
spyOn(chart.data.datasets[0].metaData[0], 'draw');
|
||||
spyOn(chart.data.datasets[0].metaData[1], 'draw');
|
||||
spyOn(chart.data.datasets[0].metaData[2], 'draw');
|
||||
spyOn(chart.data.datasets[0].metaData[3], 'draw');
|
||||
|
||||
controller.draw();
|
||||
|
||||
expect(chart.data.datasets[0].metaDataset.draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[0].metaData[0].draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[0].metaData[2].draw.calls.count()).toBe(1);
|
||||
expect(chart.data.datasets[0].metaData[3].draw.calls.count()).toBe(1);
|
||||
});
|
||||
|
||||
it ('should update elements', function() {
|
||||
var data = {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, -4],
|
||||
label: 'dataset2',
|
||||
xAxisID: 'firstXScaleID',
|
||||
yAxisID: 'firstYScaleID'
|
||||
}],
|
||||
labels: ['label1', 'label2', 'label3', 'label4']
|
||||
};
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
var VerticalScaleConstructor = Chart.scaleService.getScaleConstructor('linear');
|
||||
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
|
||||
verticalScaleConfig = Chart.helpers.scaleMerge(verticalScaleConfig, Chart.defaults.line.scales.yAxes[0]);
|
||||
var yScale = new VerticalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstYScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var verticalSize = yScale.update(50, 200);
|
||||
yScale.top = 0;
|
||||
yScale.left = 0;
|
||||
yScale.right = verticalSize.width;
|
||||
yScale.bottom = verticalSize.height;
|
||||
|
||||
var HorizontalScaleConstructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var horizontalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
horizontalScaleConfig = Chart.helpers.scaleMerge(horizontalScaleConfig, Chart.defaults.line.scales.xAxes[0]);
|
||||
var xScale = new HorizontalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstXScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var horizontalSize = xScale.update(200, 50);
|
||||
xScale.left = yScale.right;
|
||||
xScale.top = yScale.bottom;
|
||||
xScale.right = horizontalSize.width + xScale.left;
|
||||
xScale.bottom = horizontalSize.height + xScale.top;
|
||||
|
||||
|
||||
var chart = {
|
||||
chartArea: {
|
||||
bottom: 200,
|
||||
left: xScale.left,
|
||||
right: 200,
|
||||
top: 0
|
||||
},
|
||||
data: data,
|
||||
config: {
|
||||
type: 'line'
|
||||
},
|
||||
options: {
|
||||
elements: {
|
||||
line: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(0, 255, 0)',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.1,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 1.2,
|
||||
fill: true,
|
||||
skipNull: true,
|
||||
tension: 0.1,
|
||||
},
|
||||
point: {
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1,
|
||||
radius: 3,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
firstXScaleID: xScale,
|
||||
firstYScaleID: yScale,
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
controller.update();
|
||||
|
||||
// Line element
|
||||
expect(chart.data.datasets[0].metaDataset._model).toEqual({
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(0, 255, 0)',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.1,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 1.2,
|
||||
fill: true,
|
||||
drawNull: undefined,
|
||||
skipNull: true,
|
||||
tension: 0.1,
|
||||
|
||||
scaleTop: 0,
|
||||
scaleBottom: 200,
|
||||
scaleZero: 156,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[0]._model).toEqual({
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
hitRadius: 1,
|
||||
radius: 3,
|
||||
skip: false,
|
||||
tension: 0.1,
|
||||
|
||||
// Point
|
||||
x: 63,
|
||||
y: 62,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 63,
|
||||
controlPointPreviousY: 62,
|
||||
controlPointNextX: 67.5,
|
||||
controlPointNextY: 57.3,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[1]._model).toEqual({
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
hitRadius: 1,
|
||||
radius: 3,
|
||||
skip: false,
|
||||
tension: 0.1,
|
||||
|
||||
// Point
|
||||
x: 108,
|
||||
y: 15,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 105.27827106822767,
|
||||
controlPointPreviousY: 12.125364948465183,
|
||||
controlPointNextX: 114.17827106822767,
|
||||
controlPointNextY: 21.52536494846518,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[2]._model).toEqual({
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
hitRadius: 1,
|
||||
radius: 3,
|
||||
skip: false,
|
||||
tension: 0.1,
|
||||
|
||||
// Point
|
||||
x: 152,
|
||||
y: 156,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 145.63719249781943,
|
||||
controlPointPreviousY: 143.20289277651324,
|
||||
controlPointNextX: 154.53719249781943,
|
||||
controlPointNextY: 161.10289277651324,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[3]._model).toEqual({
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
hitRadius: 1,
|
||||
radius: 3,
|
||||
skip: false,
|
||||
tension: 0.1,
|
||||
|
||||
// Point
|
||||
x: 197,
|
||||
y: 194,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 192.5,
|
||||
controlPointPreviousY: 190.2,
|
||||
controlPointNextX: 197,
|
||||
controlPointNextY: 194,
|
||||
});
|
||||
|
||||
// Use dataset level styles for lines & points
|
||||
chart.data.datasets[0].tension = 0.2;
|
||||
chart.data.datasets[0].backgroundColor = 'rgb(98, 98, 98)';
|
||||
chart.data.datasets[0].borderColor = 'rgb(8, 8, 8)';
|
||||
chart.data.datasets[0].borderWidth = 0.55;
|
||||
chart.data.datasets[0].borderCapStyle = 'butt';
|
||||
chart.data.datasets[0].borderDash = [2, 3];
|
||||
chart.data.datasets[0].borderDashOffset = 7;
|
||||
chart.data.datasets[0].borderJoinStyle = 'miter';
|
||||
chart.data.datasets[0].fill = false;
|
||||
chart.data.datasets[0].skipNull = false;
|
||||
chart.data.datasets[0].drawNull = true;
|
||||
|
||||
// point styles
|
||||
chart.data.datasets[0].radius = 22;
|
||||
chart.data.datasets[0].hitRadius = 3.3;
|
||||
chart.data.datasets[0].pointBackgroundColor = 'rgb(128, 129, 130)';
|
||||
chart.data.datasets[0].pointBorderColor = 'rgb(56, 57, 58)';
|
||||
chart.data.datasets[0].pointBorderWidth = 1.123;
|
||||
|
||||
controller.update();
|
||||
|
||||
expect(chart.data.datasets[0].metaDataset._model).toEqual({
|
||||
backgroundColor: 'rgb(98, 98, 98)',
|
||||
borderCapStyle: 'butt',
|
||||
borderColor: 'rgb(8, 8, 8)',
|
||||
borderDash: [2, 3],
|
||||
borderDashOffset: 7,
|
||||
borderJoinStyle: 'miter',
|
||||
borderWidth: 0.55,
|
||||
fill: false,
|
||||
drawNull: true,
|
||||
skipNull: false,
|
||||
tension: 0.2,
|
||||
|
||||
scaleTop: 0,
|
||||
scaleBottom: 200,
|
||||
scaleZero: 156,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[0]._model).toEqual({
|
||||
backgroundColor: 'rgb(128, 129, 130)',
|
||||
borderWidth: 1.123,
|
||||
borderColor: 'rgb(56, 57, 58)',
|
||||
hitRadius: 3.3,
|
||||
radius: 22,
|
||||
skip: false,
|
||||
tension: 0.2,
|
||||
|
||||
// Point
|
||||
x: 63,
|
||||
y: 62,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 63,
|
||||
controlPointPreviousY: 62,
|
||||
controlPointNextX: 72,
|
||||
controlPointNextY: 52.6,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[1]._model).toEqual({
|
||||
backgroundColor: 'rgb(128, 129, 130)',
|
||||
borderWidth: 1.123,
|
||||
borderColor: 'rgb(56, 57, 58)',
|
||||
hitRadius: 3.3,
|
||||
radius: 22,
|
||||
skip: false,
|
||||
tension: 0.2,
|
||||
|
||||
// Point
|
||||
x: 108,
|
||||
y: 15,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 102.55654213645535,
|
||||
controlPointPreviousY: 9.250729896930364,
|
||||
controlPointNextX: 120.35654213645535,
|
||||
controlPointNextY: 28.050729896930367,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[2]._model).toEqual({
|
||||
backgroundColor: 'rgb(128, 129, 130)',
|
||||
borderWidth: 1.123,
|
||||
borderColor: 'rgb(56, 57, 58)',
|
||||
hitRadius: 3.3,
|
||||
radius: 22,
|
||||
skip: false,
|
||||
tension: 0.2,
|
||||
|
||||
// Point
|
||||
x: 152,
|
||||
y: 156,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 139.27438499563885,
|
||||
controlPointPreviousY: 130.40578555302648,
|
||||
controlPointNextX: 157.07438499563887,
|
||||
controlPointNextY: 166.20578555302646,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[3]._model).toEqual({
|
||||
backgroundColor: 'rgb(128, 129, 130)',
|
||||
borderWidth: 1.123,
|
||||
borderColor: 'rgb(56, 57, 58)',
|
||||
hitRadius: 3.3,
|
||||
radius: 22,
|
||||
skip: false,
|
||||
tension: 0.2,
|
||||
|
||||
// Point
|
||||
x: 197,
|
||||
y: 194,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 188,
|
||||
controlPointPreviousY: 186.4,
|
||||
controlPointNextX: 197,
|
||||
controlPointNextY: 194,
|
||||
});
|
||||
|
||||
// Use custom styles for lines & first point
|
||||
chart.data.datasets[0].metaDataset.custom = {
|
||||
tension: 0.25,
|
||||
backgroundColor: 'rgb(55, 55, 54)',
|
||||
borderColor: 'rgb(8, 7, 6)',
|
||||
borderWidth: 0.3,
|
||||
borderCapStyle: 'square',
|
||||
borderDash: [4, 3],
|
||||
borderDashOffset: 4.4,
|
||||
borderJoinStyle: 'round',
|
||||
fill: true,
|
||||
skipNull: true,
|
||||
drawNull: false,
|
||||
};
|
||||
|
||||
// point styles
|
||||
chart.data.datasets[0].metaData[0].custom = {
|
||||
radius: 2.2,
|
||||
backgroundColor: 'rgb(0, 1, 3)',
|
||||
borderColor: 'rgb(4, 6, 8)',
|
||||
borderWidth: 0.787,
|
||||
tension: 0.15,
|
||||
skip: true,
|
||||
hitRadius: 5,
|
||||
};
|
||||
|
||||
controller.update();
|
||||
|
||||
expect(chart.data.datasets[0].metaDataset._model).toEqual({
|
||||
backgroundColor: 'rgb(55, 55, 54)',
|
||||
borderCapStyle: 'square',
|
||||
borderColor: 'rgb(8, 7, 6)',
|
||||
borderDash: [4, 3],
|
||||
borderDashOffset: 4.4,
|
||||
borderJoinStyle: 'round',
|
||||
borderWidth: 0.3,
|
||||
fill: true,
|
||||
drawNull: true,
|
||||
skipNull: false,
|
||||
tension: 0.25,
|
||||
|
||||
scaleTop: 0,
|
||||
scaleBottom: 200,
|
||||
scaleZero: 156,
|
||||
});
|
||||
|
||||
expect(chart.data.datasets[0].metaData[0]._model).toEqual({
|
||||
backgroundColor: 'rgb(0, 1, 3)',
|
||||
borderWidth: 0.787,
|
||||
borderColor: 'rgb(4, 6, 8)',
|
||||
hitRadius: 5,
|
||||
radius: 2.2,
|
||||
skip: true,
|
||||
tension: 0.15,
|
||||
|
||||
// Point
|
||||
x: 63,
|
||||
y: 62,
|
||||
|
||||
// Control points
|
||||
controlPointPreviousX: 63,
|
||||
controlPointPreviousY: 62,
|
||||
controlPointNextX: 69.75,
|
||||
controlPointNextY: 54.95,
|
||||
});
|
||||
});
|
||||
|
||||
it ('should handle number of data point changes in update', function() {
|
||||
var data = {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, -4],
|
||||
label: 'dataset2',
|
||||
xAxisID: 'firstXScaleID',
|
||||
yAxisID: 'firstYScaleID'
|
||||
}],
|
||||
labels: ['label1', 'label2', 'label3', 'label4']
|
||||
};
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
var VerticalScaleConstructor = Chart.scaleService.getScaleConstructor('linear');
|
||||
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
|
||||
verticalScaleConfig = Chart.helpers.scaleMerge(verticalScaleConfig, Chart.defaults.line.scales.yAxes[0]);
|
||||
var yScale = new VerticalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstYScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var verticalSize = yScale.update(50, 200);
|
||||
yScale.top = 0;
|
||||
yScale.left = 0;
|
||||
yScale.right = verticalSize.width;
|
||||
yScale.bottom = verticalSize.height;
|
||||
|
||||
var HorizontalScaleConstructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var horizontalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
horizontalScaleConfig = Chart.helpers.scaleMerge(horizontalScaleConfig, Chart.defaults.line.scales.xAxes[0]);
|
||||
var xScale = new HorizontalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstXScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var horizontalSize = xScale.update(200, 50);
|
||||
xScale.left = yScale.right;
|
||||
xScale.top = yScale.bottom;
|
||||
xScale.right = horizontalSize.width + xScale.left;
|
||||
xScale.bottom = horizontalSize.height + xScale.top;
|
||||
|
||||
|
||||
var chart = {
|
||||
chartArea: {
|
||||
bottom: 200,
|
||||
left: xScale.left,
|
||||
right: 200,
|
||||
top: 0
|
||||
},
|
||||
data: data,
|
||||
config: {
|
||||
type: 'line'
|
||||
},
|
||||
options: {
|
||||
elements: {
|
||||
line: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(0, 255, 0)',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.1,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 1.2,
|
||||
fill: true,
|
||||
skipNull: true,
|
||||
tension: 0.1,
|
||||
},
|
||||
point: {
|
||||
backgroundColor: Chart.defaults.global.defaultColor,
|
||||
borderWidth: 1,
|
||||
borderColor: Chart.defaults.global.defaultColor,
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1,
|
||||
radius: 3,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
firstXScaleID: xScale,
|
||||
firstYScaleID: yScale,
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
chart.data.datasets[0].data = [1, 2]; // remove 2 items
|
||||
controller.buildOrUpdateElements();
|
||||
controller.update();
|
||||
expect(chart.data.datasets[0].metaData.length).toBe(2);
|
||||
expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Point).toBe(true);
|
||||
|
||||
chart.data.datasets[0].data = [1, 2, 3, 4, 5]; // add 3 items
|
||||
controller.buildOrUpdateElements();
|
||||
controller.update();
|
||||
expect(chart.data.datasets[0].metaData.length).toBe(5);
|
||||
expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[2] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[3] instanceof Chart.elements.Point).toBe(true);
|
||||
expect(chart.data.datasets[0].metaData[4] instanceof Chart.elements.Point).toBe(true);
|
||||
});
|
||||
|
||||
it ('should set point hover styles', function() {
|
||||
var data = {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, -4],
|
||||
label: 'dataset2',
|
||||
xAxisID: 'firstXScaleID',
|
||||
yAxisID: 'firstYScaleID'
|
||||
}],
|
||||
labels: ['label1', 'label2', 'label3', 'label4']
|
||||
};
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
var VerticalScaleConstructor = Chart.scaleService.getScaleConstructor('linear');
|
||||
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
|
||||
verticalScaleConfig = Chart.helpers.scaleMerge(verticalScaleConfig, Chart.defaults.line.scales.yAxes[0]);
|
||||
var yScale = new VerticalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstYScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var verticalSize = yScale.update(50, 200);
|
||||
yScale.top = 0;
|
||||
yScale.left = 0;
|
||||
yScale.right = verticalSize.width;
|
||||
yScale.bottom = verticalSize.height;
|
||||
|
||||
var HorizontalScaleConstructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var horizontalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
horizontalScaleConfig = Chart.helpers.scaleMerge(horizontalScaleConfig, Chart.defaults.line.scales.xAxes[0]);
|
||||
var xScale = new HorizontalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstXScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var horizontalSize = xScale.update(200, 50);
|
||||
xScale.left = yScale.right;
|
||||
xScale.top = yScale.bottom;
|
||||
xScale.right = horizontalSize.width + xScale.left;
|
||||
xScale.bottom = horizontalSize.height + xScale.top;
|
||||
|
||||
|
||||
var chart = {
|
||||
chartArea: {
|
||||
bottom: 200,
|
||||
left: xScale.left,
|
||||
right: 200,
|
||||
top: 0
|
||||
},
|
||||
data: data,
|
||||
config: {
|
||||
type: 'line'
|
||||
},
|
||||
options: {
|
||||
elements: {
|
||||
line: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(0, 255, 0)',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.1,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 1.2,
|
||||
fill: true,
|
||||
skipNull: true,
|
||||
tension: 0.1,
|
||||
},
|
||||
point: {
|
||||
backgroundColor: 'rgb(255, 255, 0)',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgb(255, 255, 255)',
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1,
|
||||
radius: 3,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
firstXScaleID: xScale,
|
||||
firstYScaleID: yScale,
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
controller.update();
|
||||
var point = chart.data.datasets[0].metaData[0];
|
||||
|
||||
controller.setHoverStyle(point);
|
||||
expect(point._model.backgroundColor).toBe('rgb(229, 230, 0)');
|
||||
expect(point._model.borderColor).toBe('rgb(230, 230, 230)');
|
||||
expect(point._model.borderWidth).toBe(1);
|
||||
expect(point._model.radius).toBe(4);
|
||||
|
||||
// Can set hover style per dataset
|
||||
chart.data.datasets[0].pointHoverRadius = 3.3;
|
||||
chart.data.datasets[0].pointHoverBackgroundColor = 'rgb(77, 79, 81)';
|
||||
chart.data.datasets[0].pointHoverBorderColor = 'rgb(123, 125, 127)';
|
||||
chart.data.datasets[0].pointHoverBorderWidth = 2.1;
|
||||
|
||||
controller.setHoverStyle(point);
|
||||
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
|
||||
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
|
||||
expect(point._model.borderWidth).toBe(2.1);
|
||||
expect(point._model.radius).toBe(3.3);
|
||||
|
||||
// Custom style
|
||||
point.custom = {
|
||||
hoverRadius: 4.4,
|
||||
hoverBorderWidth: 5.5,
|
||||
hoverBackgroundColor: 'rgb(0, 0, 0)',
|
||||
hoverBorderColor: 'rgb(10, 10, 10)'
|
||||
};
|
||||
|
||||
controller.setHoverStyle(point);
|
||||
expect(point._model.backgroundColor).toBe('rgb(0, 0, 0)');
|
||||
expect(point._model.borderColor).toBe('rgb(10, 10, 10)');
|
||||
expect(point._model.borderWidth).toBe(5.5);
|
||||
expect(point._model.radius).toBe(4.4);
|
||||
});
|
||||
|
||||
it ('should remove hover styles', function() {
|
||||
var data = {
|
||||
datasets: [{
|
||||
data: [10, 15, 0, -4],
|
||||
label: 'dataset2',
|
||||
xAxisID: 'firstXScaleID',
|
||||
yAxisID: 'firstYScaleID'
|
||||
}],
|
||||
labels: ['label1', 'label2', 'label3', 'label4']
|
||||
};
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
var VerticalScaleConstructor = Chart.scaleService.getScaleConstructor('linear');
|
||||
var verticalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
|
||||
verticalScaleConfig = Chart.helpers.scaleMerge(verticalScaleConfig, Chart.defaults.line.scales.yAxes[0]);
|
||||
var yScale = new VerticalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstYScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var verticalSize = yScale.update(50, 200);
|
||||
yScale.top = 0;
|
||||
yScale.left = 0;
|
||||
yScale.right = verticalSize.width;
|
||||
yScale.bottom = verticalSize.height;
|
||||
|
||||
var HorizontalScaleConstructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var horizontalScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
horizontalScaleConfig = Chart.helpers.scaleMerge(horizontalScaleConfig, Chart.defaults.line.scales.xAxes[0]);
|
||||
var xScale = new HorizontalScaleConstructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalScaleConfig,
|
||||
data: data,
|
||||
id: 'firstXScaleID'
|
||||
});
|
||||
|
||||
// Update ticks & set physical dimensions
|
||||
var horizontalSize = xScale.update(200, 50);
|
||||
xScale.left = yScale.right;
|
||||
xScale.top = yScale.bottom;
|
||||
xScale.right = horizontalSize.width + xScale.left;
|
||||
xScale.bottom = horizontalSize.height + xScale.top;
|
||||
|
||||
|
||||
var chart = {
|
||||
chartArea: {
|
||||
bottom: 200,
|
||||
left: xScale.left,
|
||||
right: 200,
|
||||
top: 0
|
||||
},
|
||||
data: data,
|
||||
config: {
|
||||
type: 'line'
|
||||
},
|
||||
options: {
|
||||
elements: {
|
||||
line: {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(0, 255, 0)',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.1,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 1.2,
|
||||
fill: true,
|
||||
skipNull: true,
|
||||
tension: 0.1,
|
||||
},
|
||||
point: {
|
||||
backgroundColor: 'rgb(255, 255, 0)',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgb(255, 255, 255)',
|
||||
hitRadius: 1,
|
||||
hoverRadius: 4,
|
||||
hoverBorderWidth: 1,
|
||||
radius: 3,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'firstXScaleID'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'firstYScaleID'
|
||||
}]
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
firstXScaleID: xScale,
|
||||
firstYScaleID: yScale,
|
||||
}
|
||||
};
|
||||
|
||||
var controller = new Chart.controllers.line(chart, 0);
|
||||
controller.update();
|
||||
var point = chart.data.datasets[0].metaData[0];
|
||||
|
||||
chart.options.elements.point.backgroundColor = 'rgb(45, 46, 47)';
|
||||
chart.options.elements.point.borderColor = 'rgb(50, 51, 52)';
|
||||
chart.options.elements.point.borderWidth = 10.1;
|
||||
chart.options.elements.point.radius = 1.01;
|
||||
|
||||
controller.removeHoverStyle(point);
|
||||
expect(point._model.backgroundColor).toBe('rgb(45, 46, 47)');
|
||||
expect(point._model.borderColor).toBe('rgb(50, 51, 52)');
|
||||
expect(point._model.borderWidth).toBe(10.1);
|
||||
expect(point._model.radius).toBe(1.01);
|
||||
|
||||
// Can set hover style per dataset
|
||||
chart.data.datasets[0].radius = 3.3;
|
||||
chart.data.datasets[0].pointBackgroundColor = 'rgb(77, 79, 81)';
|
||||
chart.data.datasets[0].pointBorderColor = 'rgb(123, 125, 127)';
|
||||
chart.data.datasets[0].pointBorderWidth = 2.1;
|
||||
|
||||
controller.removeHoverStyle(point);
|
||||
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
|
||||
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
|
||||
expect(point._model.borderWidth).toBe(2.1);
|
||||
expect(point._model.radius).toBe(3.3);
|
||||
|
||||
// Custom style
|
||||
point.custom = {
|
||||
radius: 4.4,
|
||||
borderWidth: 5.5,
|
||||
backgroundColor: 'rgb(0, 0, 0)',
|
||||
borderColor: 'rgb(10, 10, 10)'
|
||||
};
|
||||
|
||||
controller.removeHoverStyle(point);
|
||||
expect(point._model.backgroundColor).toBe('rgb(0, 0, 0)');
|
||||
expect(point._model.borderColor).toBe('rgb(10, 10, 10)');
|
||||
expect(point._model.borderWidth).toBe(5.5);
|
||||
expect(point._model.radius).toBe(4.4);
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
// Test the core element functionality
|
||||
describe('Core element tests', function() {
|
||||
it ('should transition model properties', function() {
|
||||
var element = new Chart.Element({
|
||||
_model: {
|
||||
numberProp: 0,
|
||||
numberProp2: 100,
|
||||
_underscoreProp: 0,
|
||||
stringProp: 'abc',
|
||||
objectProp: {
|
||||
myObject: true
|
||||
},
|
||||
colorProp: 'rgb(0, 0, 0)'
|
||||
}
|
||||
});
|
||||
|
||||
// First transition clones model into view
|
||||
element.transition(0.25);
|
||||
expect(element._view).toEqual(element._model);
|
||||
expect(element._start).toEqual(element._model); // also cloned
|
||||
|
||||
expect(element._view.objectProp).toBe(element._model.objectProp); // not cloned
|
||||
expect(element._start.objectProp).toEqual(element._model.objectProp); // not cloned
|
||||
|
||||
element._model.numberProp = 100;
|
||||
element._model.numberProp2 = 250;
|
||||
element._model._underscoreProp = 200;
|
||||
element._model.stringProp = 'def'
|
||||
element._model.newStringProp = 'newString';
|
||||
element._model.colorProp = 'rgb(255, 255, 0)'
|
||||
|
||||
element.transition(0.25);
|
||||
expect(element._view).toEqual({
|
||||
numberProp: 25,
|
||||
numberProp2: 137.5,
|
||||
_underscoreProp: 0, // underscore props are not transition to a new value
|
||||
stringProp: 'def',
|
||||
newStringProp: 'newString',
|
||||
objectProp: {
|
||||
myObject: true
|
||||
},
|
||||
colorProp: 'rgb(64, 64, 0)',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,460 +0,0 @@
|
||||
describe('Core helper tests', function() {
|
||||
|
||||
var helpers;
|
||||
|
||||
beforeAll(function() {
|
||||
helpers = window.Chart.helpers;
|
||||
});
|
||||
|
||||
it('Should iterate over an array and pass the extra data to that function', function() {
|
||||
var testData = [0, 9, "abc"];
|
||||
var scope = {}; // fake out the scope and ensure that 'this' is the correct thing
|
||||
|
||||
helpers.each(testData, function(item, index) {
|
||||
expect(item).not.toBe(undefined);
|
||||
expect(index).not.toBe(undefined);
|
||||
|
||||
expect(testData[index]).toBe(item);
|
||||
expect(this).toBe(scope);
|
||||
}, scope);
|
||||
|
||||
// Reverse iteration
|
||||
var iterated = [];
|
||||
helpers.each(testData, function(item, index) {
|
||||
expect(item).not.toBe(undefined);
|
||||
expect(index).not.toBe(undefined);
|
||||
|
||||
expect(testData[index]).toBe(item);
|
||||
expect(this).toBe(scope);
|
||||
|
||||
iterated.push(item);
|
||||
}, scope, true);
|
||||
|
||||
expect(iterated.slice().reverse()).toEqual(testData);
|
||||
});
|
||||
|
||||
it('Should iterate over properties in an object', function() {
|
||||
var testData = {
|
||||
myProp1: 'abc',
|
||||
myProp2: 276,
|
||||
myProp3: ['a', 'b']
|
||||
};
|
||||
|
||||
helpers.each(testData, function(value, key) {
|
||||
if (key === 'myProp1') {
|
||||
expect(value).toBe('abc');
|
||||
} else if (key === 'myProp2') {
|
||||
expect(value).toBe(276);
|
||||
} else if (key === 'myProp3') {
|
||||
expect(value).toEqual(['a', 'b']);
|
||||
} else {
|
||||
expect(false).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not error when iterating over a null object', function() {
|
||||
expect(function() {
|
||||
helpers.each(undefined);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('Should clone an object', function() {
|
||||
var testData = {
|
||||
myProp1: 'abc',
|
||||
myProp2: ['a', 'b'],
|
||||
myProp3: {
|
||||
myProp4: 5,
|
||||
myProp5: [1, 2]
|
||||
}
|
||||
};
|
||||
|
||||
var clone = helpers.clone(testData);
|
||||
expect(clone).toEqual(testData);
|
||||
expect(clone).not.toBe(testData);
|
||||
|
||||
expect(clone.myProp2).not.toBe(testData.myProp2);
|
||||
expect(clone.myProp3).not.toBe(testData.myProp3);
|
||||
expect(clone.myProp3.myProp5).not.toBe(testData.myProp3.myProp5);
|
||||
});
|
||||
|
||||
it('should extend an object', function() {
|
||||
var original = {
|
||||
myProp1: 'abc',
|
||||
myProp2: 56
|
||||
};
|
||||
|
||||
var extension = {
|
||||
myProp3: [2, 5, 6],
|
||||
myProp2: 0
|
||||
};
|
||||
|
||||
helpers.extend(original, extension);
|
||||
|
||||
expect(original).toEqual({
|
||||
myProp1: 'abc',
|
||||
myProp2: 0,
|
||||
myProp3: [2, 5, 6],
|
||||
});
|
||||
});
|
||||
|
||||
it('Should merge a normal config without scales', function() {
|
||||
var baseConfig = {
|
||||
valueProp: 5,
|
||||
arrayProp: [1, 2, 3, 4, 5, 6],
|
||||
objectProp: {
|
||||
prop1: 'abc',
|
||||
prop2: 56
|
||||
}
|
||||
};
|
||||
|
||||
var toMerge = {
|
||||
valueProp2: null,
|
||||
arrayProp: ['a', 'c'],
|
||||
objectProp: {
|
||||
prop1: 'c',
|
||||
prop3: 'prop3'
|
||||
}
|
||||
};
|
||||
|
||||
var merged = helpers.configMerge(baseConfig, toMerge);
|
||||
expect(merged).toEqual({
|
||||
valueProp: 5,
|
||||
valueProp2: null,
|
||||
arrayProp: ['a', 'c', 3, 4, 5, 6],
|
||||
objectProp: {
|
||||
prop1: 'c',
|
||||
prop2: 56,
|
||||
prop3: 'prop3'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should merge arrays containing objects', function() {
|
||||
var baseConfig = {
|
||||
arrayProp: [{
|
||||
prop1: 'abc',
|
||||
prop2: 56
|
||||
}],
|
||||
};
|
||||
|
||||
var toMerge = {
|
||||
arrayProp: [{
|
||||
prop1: 'myProp1',
|
||||
prop3: 'prop3'
|
||||
}, 2, {
|
||||
prop1: 'myProp1'
|
||||
}],
|
||||
};
|
||||
|
||||
var merged = helpers.configMerge(baseConfig, toMerge);
|
||||
expect(merged).toEqual({
|
||||
arrayProp: [{
|
||||
prop1: 'myProp1',
|
||||
prop2: 56,
|
||||
prop3: 'prop3'
|
||||
},
|
||||
2, {
|
||||
prop1: 'myProp1'
|
||||
}],
|
||||
});
|
||||
});
|
||||
|
||||
it ('Should merge scale configs', function() {
|
||||
var baseConfig = {
|
||||
scales: {
|
||||
prop1: {
|
||||
abc: 123,
|
||||
def: '456'
|
||||
},
|
||||
prop2: 777,
|
||||
yAxes: [{
|
||||
type: 'linear',
|
||||
}, {
|
||||
type: 'log'
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
var toMerge = {
|
||||
scales: {
|
||||
prop1: {
|
||||
def: 'bbb',
|
||||
ghi: 78
|
||||
},
|
||||
prop2: null,
|
||||
yAxes: [{
|
||||
type: 'linear',
|
||||
axisProp: 456
|
||||
}, {
|
||||
// pulls in linear default config since axis type changes
|
||||
type: 'linear',
|
||||
position: 'right'
|
||||
}, {
|
||||
// Pulls in linear default config since axis not in base
|
||||
type: 'linear'
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
var merged = helpers.configMerge(baseConfig, toMerge);
|
||||
expect(merged).toEqual({
|
||||
scales: {
|
||||
prop1: {
|
||||
abc: 123,
|
||||
def: 'bbb',
|
||||
ghi: 78
|
||||
},
|
||||
prop2: null,
|
||||
yAxes: [{
|
||||
type: 'linear',
|
||||
axisProp: 456
|
||||
}, {
|
||||
display: true,
|
||||
|
||||
gridLines: {
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
lineWidth: 1,
|
||||
offsetGridLines: false,
|
||||
show: true,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
zeroLineWidth: 1,
|
||||
},
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'normal',
|
||||
labelString: '',
|
||||
show: false,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
maxRotation: 90,
|
||||
minRotation: 20,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
reverse: false,
|
||||
show: true,
|
||||
template: "<%=value%>"
|
||||
},
|
||||
type: 'linear'
|
||||
}, {
|
||||
display: true,
|
||||
|
||||
gridLines: {
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
lineWidth: 1,
|
||||
offsetGridLines: false,
|
||||
show: true,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
zeroLineWidth: 1,
|
||||
},
|
||||
position: "left",
|
||||
scaleLabel: {
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'normal',
|
||||
labelString: '',
|
||||
show: false,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
maxRotation: 90,
|
||||
minRotation: 20,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
reverse: false,
|
||||
show: true,
|
||||
template: "<%=value%>"
|
||||
},
|
||||
type: 'linear'
|
||||
}]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('should get value or default', function() {
|
||||
expect(helpers.getValueAtIndexOrDefault(98, 0, 56)).toBe(98);
|
||||
expect(helpers.getValueAtIndexOrDefault(0, 0, 56)).toBe(0);
|
||||
expect(helpers.getValueAtIndexOrDefault(undefined, undefined, 56)).toBe(56);
|
||||
expect(helpers.getValueAtIndexOrDefault([1, 2, 3], 1, 100)).toBe(2);
|
||||
expect(helpers.getValueAtIndexOrDefault([1, 2, 3], 3, 100)).toBe(100);
|
||||
});
|
||||
|
||||
it ('should filter an array', function() {
|
||||
var data = [-10, 0, 6, 0, 7];
|
||||
var callback = function(item) { return item > 2};
|
||||
expect(helpers.where(data, callback)).toEqual([6, 7]);
|
||||
expect(helpers.findNextWhere(data, callback)).toEqual(6);
|
||||
expect(helpers.findNextWhere(data, callback, 2)).toBe(7);
|
||||
expect(helpers.findNextWhere(data, callback, 4)).toBe(undefined);
|
||||
expect(helpers.findPreviousWhere(data, callback)).toBe(7);
|
||||
expect(helpers.findPreviousWhere(data, callback, 3)).toBe(6);
|
||||
expect(helpers.findPreviousWhere(data, callback, 0)).toBe(undefined);
|
||||
});
|
||||
|
||||
it ('Should get the correct sign', function() {
|
||||
expect(helpers.sign(0)).toBe(0);
|
||||
expect(helpers.sign(10)).toBe(1);
|
||||
expect(helpers.sign(-5)).toBe(-1);
|
||||
});
|
||||
|
||||
it ('should do a log10 operation', function() {
|
||||
expect(helpers.log10(0)).toBe(-Infinity);
|
||||
expect(helpers.log10(1)).toBe(0);
|
||||
expect(helpers.log10(1000)).toBe(3);
|
||||
});
|
||||
|
||||
it ('Should generate ids', function() {
|
||||
expect(helpers.uid()).toBe('chart-0');
|
||||
expect(helpers.uid()).toBe('chart-1');
|
||||
expect(helpers.uid()).toBe('chart-2');
|
||||
expect(helpers.uid()).toBe('chart-3');
|
||||
});
|
||||
|
||||
it ('should detect a number', function() {
|
||||
expect(helpers.isNumber(123)).toBe(true);
|
||||
expect(helpers.isNumber('123')).toBe(true);
|
||||
expect(helpers.isNumber(null)).toBe(false);
|
||||
expect(helpers.isNumber(NaN)).toBe(false);
|
||||
expect(helpers.isNumber(undefined)).toBe(false);
|
||||
expect(helpers.isNumber('cbc')).toBe(false);
|
||||
});
|
||||
|
||||
it ('should convert between radians and degrees', function() {
|
||||
expect(helpers.toRadians(180)).toBe(Math.PI);
|
||||
expect(helpers.toRadians(90)).toBe(0.5 * Math.PI);
|
||||
expect(helpers.toDegrees(Math.PI)).toBe(180);
|
||||
expect(helpers.toDegrees(Math.PI * 3 /2)).toBe(270);
|
||||
});
|
||||
|
||||
it ('should get an angle from a point', function() {
|
||||
var center = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
expect(helpers.getAngleFromPoint(center, {x: 0, y: 10})).toEqual({
|
||||
angle: Math.PI / 2,
|
||||
distance: 10,
|
||||
});
|
||||
|
||||
expect(helpers.getAngleFromPoint(center, {x: Math.sqrt(2), y: Math.sqrt(2)})).toEqual({
|
||||
angle: Math.PI / 4,
|
||||
distance: 2
|
||||
});
|
||||
|
||||
expect(helpers.getAngleFromPoint(center, {x: -1.0 * Math.sqrt(2), y: -1.0 * Math.sqrt(2)})).toEqual({
|
||||
angle: Math.PI * 1.25,
|
||||
distance: 2
|
||||
});
|
||||
});
|
||||
|
||||
it ('should spline curves', function() {
|
||||
expect(helpers.splineCurve({x: 0, y: 0}, {x: 1, y: 1}, { x: 2, y: 0}, 0)).toEqual({
|
||||
previous: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
},
|
||||
next: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
}
|
||||
});
|
||||
|
||||
expect(helpers.splineCurve({x: 0, y: 0}, {x: 1, y: 1}, { x: 2, y: 0}, 1)).toEqual({
|
||||
previous: {
|
||||
x: 0,
|
||||
y: 1,
|
||||
},
|
||||
next: {
|
||||
x: 2,
|
||||
y: 1,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('should get the next or previous item in an array', function() {
|
||||
var testData = [0, 1, 2];
|
||||
|
||||
expect(helpers.nextItem(testData, 0, false)).toEqual(1);
|
||||
expect(helpers.nextItem(testData, 2, false)).toEqual(2);
|
||||
expect(helpers.nextItem(testData, 2, true)).toEqual(0);
|
||||
expect(helpers.nextItem(testData, 1, true)).toEqual(2);
|
||||
expect(helpers.nextItem(testData, -1, false)).toEqual(0);
|
||||
|
||||
expect(helpers.previousItem(testData, 0, false)).toEqual(0);
|
||||
expect(helpers.previousItem(testData, 0, true)).toEqual(2);
|
||||
expect(helpers.previousItem(testData, 2, false)).toEqual(1);
|
||||
expect(helpers.previousItem(testData, 1, true)).toEqual(0);
|
||||
});
|
||||
|
||||
it ('should clear a canvas', function() {
|
||||
var context = window.createMockContext();
|
||||
helpers.clear({
|
||||
width: 100,
|
||||
height: 150,
|
||||
ctx: context
|
||||
});
|
||||
|
||||
expect(context.getCalls()).toEqual([{
|
||||
name: 'clearRect',
|
||||
args: [0, 0, 100, 150]
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should draw a rounded rectangle', function() {
|
||||
var context = window.createMockContext();
|
||||
helpers.drawRoundedRectangle(context, 10, 20, 30, 40, 5);
|
||||
|
||||
expect(context.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [15, 20]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [35, 20]
|
||||
}, {
|
||||
name: 'quadraticCurveTo',
|
||||
args: [40, 20, 40, 25]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [40, 55]
|
||||
}, {
|
||||
name: 'quadraticCurveTo',
|
||||
args: [40, 60, 35, 60]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, 60]
|
||||
}, {
|
||||
name: 'quadraticCurveTo',
|
||||
args: [10, 60, 10, 55]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [10, 25]
|
||||
}, {
|
||||
name: 'quadraticCurveTo',
|
||||
args: [10, 20, 15, 20]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}])
|
||||
});
|
||||
});
|
||||
@@ -1,176 +0,0 @@
|
||||
// Test the rectangle element
|
||||
|
||||
describe('Arc element tests', function() {
|
||||
it ('Should be constructed', function() {
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
expect(arc).not.toBe(undefined);
|
||||
expect(arc._datasetIndex).toBe(2);
|
||||
expect(arc._index).toBe(1);
|
||||
});
|
||||
|
||||
it ('should determine if in range', function() {
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Make sure we can run these before the view is added
|
||||
expect(arc.inRange(2, 2)).toBe(false);
|
||||
expect(arc.inLabelRange(2)).toBe(false);
|
||||
|
||||
// Mock out the view as if the controller put it there
|
||||
arc._view = {
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI / 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
innerRadius: 5,
|
||||
outerRadius: 10,
|
||||
};
|
||||
|
||||
expect(arc.inRange(2, 2)).toBe(false);
|
||||
expect(arc.inRange(7, 0)).toBe(true);
|
||||
expect(arc.inRange(0, 11)).toBe(false);
|
||||
expect(arc.inRange(Math.sqrt(32), Math.sqrt(32))).toBe(true);
|
||||
expect(arc.inRange(-1.0 * Math.sqrt(7), Math.sqrt(7))).toBe(false);
|
||||
});
|
||||
|
||||
it ('should get the tooltip position', function() {
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Mock out the view as if the controller put it there
|
||||
arc._view = {
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI / 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
innerRadius: 0,
|
||||
outerRadius: Math.sqrt(2),
|
||||
};
|
||||
|
||||
var pos = arc.tooltipPosition();
|
||||
expect(pos.x).toBeCloseTo(0.5);
|
||||
expect(pos.y).toBeCloseTo(0.5);
|
||||
});
|
||||
|
||||
it ('should draw correctly with no border', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Mock out the view as if the controller put it there
|
||||
arc._view = {
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI / 2,
|
||||
x: 10,
|
||||
y: 5,
|
||||
innerRadius: 1,
|
||||
outerRadius: 3,
|
||||
|
||||
backgroundColor: 'rgb(0, 0, 255)',
|
||||
borderColor: 'rgb(255, 0, 0)',
|
||||
};
|
||||
|
||||
arc.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 5, 3, 0, Math.PI / 2]
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 5, 1, Math.PI / 2, 0, true]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(255, 0, 0)']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [undefined]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(0, 0, 255)']
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['bevel']
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should draw correctly with a border', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Mock out the view as if the controller put it there
|
||||
arc._view = {
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI / 2,
|
||||
x: 10,
|
||||
y: 5,
|
||||
innerRadius: 1,
|
||||
outerRadius: 3,
|
||||
|
||||
backgroundColor: 'rgb(0, 0, 255)',
|
||||
borderColor: 'rgb(255, 0, 0)',
|
||||
borderWidth: 5
|
||||
};
|
||||
|
||||
arc.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 5, 3, 0, Math.PI / 2]
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 5, 1, Math.PI / 2, 0, true]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(255, 0, 0)']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [5]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(0, 0, 255)']
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['bevel']
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: []
|
||||
}]);
|
||||
});
|
||||
});
|
||||
@@ -1,526 +0,0 @@
|
||||
// Tests for the line element
|
||||
describe('Line element tests', function() {
|
||||
it ('should be constructed', function() {
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_points: [1, 2, 3, 4]
|
||||
});
|
||||
|
||||
expect(line).not.toBe(undefined);
|
||||
expect(line._datasetindex).toBe(2);
|
||||
expect(line._points).toEqual([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it ('should draw with default settings', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
// Create our points
|
||||
var points = [];
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 0,
|
||||
_view: {
|
||||
x: 0,
|
||||
y: 10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 1,
|
||||
_view: {
|
||||
x: 5,
|
||||
y: 0
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 2,
|
||||
_view: {
|
||||
x: 15,
|
||||
y: -10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 3,
|
||||
_view: {
|
||||
x: 19,
|
||||
y: -5
|
||||
}
|
||||
}));
|
||||
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
},
|
||||
_children: points,
|
||||
// Need to provide some settings
|
||||
_view: {
|
||||
fill: false, // don't want to fill
|
||||
tension: 0.0, // no bezier curve for now
|
||||
}
|
||||
})
|
||||
|
||||
line.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'save',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'setLineCap',
|
||||
args: ['butt']
|
||||
}, {
|
||||
name: 'setLineDash',
|
||||
args: [[]]
|
||||
}, {
|
||||
name: 'setLineDashOffset',
|
||||
args: [0.0]
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['miter']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [3]
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgba(0,0,0,0.1)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'restore',
|
||||
args: []
|
||||
}])
|
||||
});
|
||||
|
||||
it ('should draw with custom settings', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
// Create our points
|
||||
var points = [];
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 0,
|
||||
_view: {
|
||||
x: 0,
|
||||
y: 10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 1,
|
||||
_view: {
|
||||
x: 5,
|
||||
y: 0
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 2,
|
||||
_view: {
|
||||
x: 15,
|
||||
y: -10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 3,
|
||||
_view: {
|
||||
x: 19,
|
||||
y: -5
|
||||
}
|
||||
}));
|
||||
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
},
|
||||
_children: points,
|
||||
// Need to provide some settings
|
||||
_view: {
|
||||
fill: true,
|
||||
scaleZero: 2, // for filling lines
|
||||
tension: 0.0, // no bezier curve for now
|
||||
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(255, 255, 0)',
|
||||
borderDash: [2, 2],
|
||||
borderDashOffset: 1.5,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 4,
|
||||
backgroundColor: 'rgb(0, 0, 0)'
|
||||
}
|
||||
})
|
||||
|
||||
line.draw();
|
||||
|
||||
var expected = [{
|
||||
name: 'save',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, 2]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [0, 2]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(0, 0, 0)']
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setLineCap',
|
||||
args: ['round']
|
||||
}, {
|
||||
name: 'setLineDash',
|
||||
args: [[2, 2]]
|
||||
}, {
|
||||
name: 'setLineDashOffset',
|
||||
args: [1.5]
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['bevel']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [4]
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(255, 255, 0)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'restore',
|
||||
args: []
|
||||
}];
|
||||
expect(mockContext.getCalls()).toEqual(expected)
|
||||
});
|
||||
|
||||
it ('should be able to draw with a loop back to the beginning point', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
// Create our points
|
||||
var points = [];
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 0,
|
||||
_view: {
|
||||
x: 0,
|
||||
y: 10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 1,
|
||||
_view: {
|
||||
x: 5,
|
||||
y: 0
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 2,
|
||||
_view: {
|
||||
x: 15,
|
||||
y: -10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 3,
|
||||
_view: {
|
||||
x: 19,
|
||||
y: -5
|
||||
}
|
||||
}));
|
||||
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
},
|
||||
_children: points,
|
||||
_loop: true, // want the line to loop back to the first point
|
||||
// Need to provide some settings
|
||||
_view: {
|
||||
fill: false, // don't want to fill
|
||||
tension: 0.0, // no bezier curve for now
|
||||
}
|
||||
})
|
||||
|
||||
line.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'save',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'setLineCap',
|
||||
args: ['butt']
|
||||
}, {
|
||||
name: 'setLineDash',
|
||||
args: [[]]
|
||||
}, {
|
||||
name: 'setLineDashOffset',
|
||||
args: [0.0]
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['miter']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [3]
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgba(0,0,0,0.1)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'restore',
|
||||
args: []
|
||||
}])
|
||||
});
|
||||
|
||||
it ('should draw with bezier curves if tension > 0', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
// Create our points
|
||||
var points = [];
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 0,
|
||||
_view: {
|
||||
x: 0,
|
||||
y: 10,
|
||||
controlPointNextX: 1,
|
||||
controlPointNextY: 1,
|
||||
controlPointPreviousX: 0,
|
||||
controlPointPreviousY: 0,
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 1,
|
||||
_view: {
|
||||
x: 5,
|
||||
y: 0,
|
||||
controlPointNextX: 6,
|
||||
controlPointNextY: 7,
|
||||
controlPointPreviousX: 4,
|
||||
controlPointPreviousY: 3,
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 2,
|
||||
_view: {
|
||||
x: 15,
|
||||
y: -10,
|
||||
controlPointNextX: 16,
|
||||
controlPointNextY: 17,
|
||||
controlPointPreviousX: 14,
|
||||
controlPointPreviousY: 13,
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 3,
|
||||
_view: {
|
||||
x: 19,
|
||||
y: -5,
|
||||
controlPointNextX: 20,
|
||||
controlPointNextY: 21,
|
||||
controlPointPreviousX: 18,
|
||||
controlPointPreviousY: 17,
|
||||
}
|
||||
}));
|
||||
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
},
|
||||
_children: points,
|
||||
// Need to provide some settings
|
||||
_view: {
|
||||
fill: true,
|
||||
scaleZero: 2, // for filling lines
|
||||
tension: 0.5, // have bezier curves
|
||||
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(255, 255, 0)',
|
||||
borderDash: [2, 2],
|
||||
borderDashOffset: 1.5,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 4,
|
||||
backgroundColor: 'rgb(0, 0, 0)'
|
||||
}
|
||||
})
|
||||
|
||||
line.draw();
|
||||
|
||||
var expected = [{
|
||||
name: 'save',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [1, 1, 4, 3, 5, 0]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [6, 7, 14, 13, 15, -10]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [16, 17, 18, 17, 19, -5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, 2]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [0, 2]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(0, 0, 0)']
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setLineCap',
|
||||
args: ['round']
|
||||
}, {
|
||||
name: 'setLineDash',
|
||||
args: [[2, 2]]
|
||||
}, {
|
||||
name: 'setLineDashOffset',
|
||||
args: [1.5]
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['bevel']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [4]
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(255, 255, 0)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [1, 1, 4, 3, 5, 0]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [6, 7, 14, 13, 15, -10]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [16, 17, 18, 17, 19, -5]
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'restore',
|
||||
args: []
|
||||
}];
|
||||
expect(mockContext.getCalls()).toEqual(expected)
|
||||
});
|
||||
});
|
||||
@@ -1,190 +0,0 @@
|
||||
// Test the point element
|
||||
|
||||
describe('Point element tests', function() {
|
||||
it ('Should be constructed', function() {
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
expect(point).not.toBe(undefined);
|
||||
expect(point._datasetIndex).toBe(2);
|
||||
expect(point._index).toBe(1);
|
||||
});
|
||||
|
||||
it ('Should correctly identify as in range', function() {
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Safely handles if these are called before the viewmodel is instantiated
|
||||
expect(point.inRange(5)).toBe(false);
|
||||
expect(point.inLabelRange(5)).toBe(false);
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
hitRadius: 3,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(point.inRange(10, 15)).toBe(true);
|
||||
expect(point.inRange(10, 10)).toBe(false);
|
||||
expect(point.inRange(10, 5)).toBe(false);
|
||||
expect(point.inRange(5, 5)).toBe(false);
|
||||
|
||||
expect(point.inLabelRange(5)).toBe(false);
|
||||
expect(point.inLabelRange(7)).toBe(true);
|
||||
expect(point.inLabelRange(10)).toBe(true);
|
||||
expect(point.inLabelRange(12)).toBe(true);
|
||||
expect(point.inLabelRange(15)).toBe(false);
|
||||
expect(point.inLabelRange(20)).toBe(false);
|
||||
});
|
||||
|
||||
it ('should get the correct tooltip position', function() {
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
borderWidth: 6,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(point.tooltipPosition()).toEqual({
|
||||
x: 10,
|
||||
y: 15,
|
||||
padding: 8
|
||||
});
|
||||
});
|
||||
|
||||
it ('should draw correctly', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
hitRadius: 3,
|
||||
borderColor: 'rgba(1, 2, 3, 1)',
|
||||
borderWidth: 6,
|
||||
backgroundColor: 'rgba(0, 255, 0)',
|
||||
x: 10,
|
||||
y: 15,
|
||||
ctx: mockContext
|
||||
};
|
||||
|
||||
point.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 15, 2, 0, 2 * Math.PI]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgba(1, 2, 3, 1)']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [6]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgba(0, 255, 0)']
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: []
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should draw correctly with default settings if necessary', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
hitRadius: 3,
|
||||
x: 10,
|
||||
y: 15,
|
||||
ctx: mockContext
|
||||
};
|
||||
|
||||
point.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 15, 2, 0, 2 * Math.PI]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgba(0,0,0,0.1)']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [1]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgba(0,0,0,0.1)']
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: []
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should not draw if skipped', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
hitRadius: 3,
|
||||
x: 10,
|
||||
y: 15,
|
||||
ctx: mockContext,
|
||||
skip: true
|
||||
};
|
||||
|
||||
point.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1,246 +0,0 @@
|
||||
// Test the rectangle element
|
||||
|
||||
describe('Rectangle element tests', function() {
|
||||
it ('Should be constructed', function() {
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
expect(rectangle).not.toBe(undefined);
|
||||
expect(rectangle._datasetIndex).toBe(2);
|
||||
expect(rectangle._index).toBe(1);
|
||||
});
|
||||
|
||||
it ('Should correctly identify as in range', function() {
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Safely handles if these are called before the viewmodel is instantiated
|
||||
expect(rectangle.inRange(5)).toBe(false);
|
||||
expect(rectangle.inLabelRange(5)).toBe(false);
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
base: 0,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(rectangle.inRange(10, 15)).toBe(true);
|
||||
expect(rectangle.inRange(10, 10)).toBe(true);
|
||||
expect(rectangle.inRange(10, 16)).toBe(false);
|
||||
expect(rectangle.inRange(5, 5)).toBe(false);
|
||||
|
||||
expect(rectangle.inLabelRange(5)).toBe(false);
|
||||
expect(rectangle.inLabelRange(7)).toBe(false);
|
||||
expect(rectangle.inLabelRange(10)).toBe(true);
|
||||
expect(rectangle.inLabelRange(12)).toBe(true);
|
||||
expect(rectangle.inLabelRange(15)).toBe(false);
|
||||
expect(rectangle.inLabelRange(20)).toBe(false);
|
||||
|
||||
// Test when the y is below the base (negative bar)
|
||||
var negativeRectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
negativeRectangle._view = {
|
||||
base: 0,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: -15
|
||||
};
|
||||
|
||||
expect(negativeRectangle.inRange(10, -16)).toBe(false);
|
||||
expect(negativeRectangle.inRange(10, 1)).toBe(false);
|
||||
expect(negativeRectangle.inRange(10, -5)).toBe(true);
|
||||
});
|
||||
|
||||
it ('should get the correct height', function() {
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
base: 0,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(rectangle.height()).toBe(-15);
|
||||
|
||||
// Test when the y is below the base (negative bar)
|
||||
var negativeRectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
negativeRectangle._view = {
|
||||
base: -10,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: -15
|
||||
};
|
||||
expect(negativeRectangle.height()).toBe(5);
|
||||
});
|
||||
|
||||
it ('should get the correct tooltip position', function() {
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
base: 0,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(rectangle.tooltipPosition()).toEqual({
|
||||
x: 10,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
// Test when the y is below the base (negative bar)
|
||||
var negativeRectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
negativeRectangle._view = {
|
||||
base: -10,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: -15
|
||||
};
|
||||
|
||||
expect(negativeRectangle.tooltipPosition()).toEqual({
|
||||
x: 10,
|
||||
y: -15,
|
||||
});
|
||||
});
|
||||
|
||||
it ('should draw correctly', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
base: 0,
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 1,
|
||||
ctx: mockContext,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15,
|
||||
};
|
||||
|
||||
rectangle.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(255, 0, 0)']
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(0, 0, 255)'],
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [1]
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [8.5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [8.5, 15.5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [11.5, 15.5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [11.5, 0]
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: []
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should draw correctly with no stroke', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
base: 0,
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
ctx: mockContext,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15,
|
||||
};
|
||||
|
||||
rectangle.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(255, 0, 0)']
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(0, 0, 255)'],
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [undefined]
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [8, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [8, 15]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [12, 15]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [12, 0]
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: [],
|
||||
}]);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -1,122 +0,0 @@
|
||||
(function() {
|
||||
// Code from http://stackoverflow.com/questions/4406864/html-canvas-unit-testing
|
||||
var Context = function() {
|
||||
this._calls = []; // names/args of recorded calls
|
||||
this._initMethods();
|
||||
|
||||
this._fillStyle = null;
|
||||
this._lineCap = null;
|
||||
this._lineDashOffset = null;
|
||||
this._lineJoin = null;
|
||||
this._lineWidth = null;
|
||||
this._strokeStyle = null;
|
||||
|
||||
// Define properties here so that we can record each time they are set
|
||||
Object.defineProperties(this, {
|
||||
"fillStyle": {
|
||||
'get': function() { return this._fillStyle; },
|
||||
'set': function(style) {
|
||||
this._fillStyle = style;
|
||||
this.record('setFillStyle', [style]);
|
||||
}
|
||||
},
|
||||
'lineCap': {
|
||||
'get': function() { return this._lineCap; },
|
||||
'set': function(cap) {
|
||||
this._lineCap = cap;
|
||||
this.record('setLineCap', [cap]);
|
||||
}
|
||||
},
|
||||
'lineDashOffset': {
|
||||
'get': function() { return this._lineDashOffset; },
|
||||
'set': function(offset) {
|
||||
this._lineDashOffset = offset;
|
||||
this.record('setLineDashOffset', [offset]);
|
||||
}
|
||||
},
|
||||
'lineJoin': {
|
||||
'get': function() { return this._lineJoin; },
|
||||
'set': function(join) {
|
||||
this._lineJoin = join;
|
||||
this.record('setLineJoin', [join]);
|
||||
}
|
||||
},
|
||||
'lineWidth': {
|
||||
'get': function() { return this._lineWidth; },
|
||||
'set': function (width) {
|
||||
this._lineWidth = width;
|
||||
this.record('setLineWidth', [width]);
|
||||
}
|
||||
},
|
||||
'strokeStyle': {
|
||||
'get': function() { return this._strokeStyle; },
|
||||
'set': function(style) {
|
||||
this._strokeStyle = style;
|
||||
this.record('setStrokeStyle', [style]);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Context.prototype._initMethods = function() {
|
||||
// define methods to test here
|
||||
// no way to introspect so we have to do some extra work :(
|
||||
var methods = {
|
||||
arc: function() {},
|
||||
beginPath: function() {},
|
||||
bezierCurveTo: function() {},
|
||||
clearRect: function() {},
|
||||
closePath: function() {},
|
||||
fill: function() {},
|
||||
fillRect: function() {},
|
||||
fillText: function() {},
|
||||
lineTo: function(x, y) {},
|
||||
measureText: function(text) {
|
||||
// return the number of characters * fixed size
|
||||
return text ? { width: text.length * 10 } : {width: 0};
|
||||
},
|
||||
moveTo: function(x, y) {},
|
||||
quadraticCurveTo: function() {},
|
||||
restore: function() {},
|
||||
rotate: function() {},
|
||||
save: function() {},
|
||||
setLineDash: function() {},
|
||||
stroke: function() {},
|
||||
translate: function() {},
|
||||
};
|
||||
|
||||
// attach methods to the class itself
|
||||
var scope = this;
|
||||
var addMethod = function(name, method) {
|
||||
scope[methodName] = function() {
|
||||
scope.record(name, arguments);
|
||||
return method.apply(scope, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
for (var methodName in methods) {
|
||||
var method = methods[methodName];
|
||||
|
||||
addMethod(methodName, method);
|
||||
}
|
||||
};
|
||||
|
||||
Context.prototype.record = function(methodName, args) {
|
||||
this._calls.push({
|
||||
name: methodName,
|
||||
args: Array.prototype.slice.call(args)
|
||||
});
|
||||
},
|
||||
|
||||
Context.prototype.getCalls = function() {
|
||||
return this._calls;
|
||||
}
|
||||
|
||||
Context.prototype.resetCalls = function() {
|
||||
this._calls = [];
|
||||
};
|
||||
|
||||
window.createMockContext = function() {
|
||||
return new Context();
|
||||
};
|
||||
})();
|
||||
@@ -1,189 +0,0 @@
|
||||
// Test the category scale
|
||||
|
||||
describe('Category scale tests', function() {
|
||||
it('Should register the constructor with the scale service', function() {
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('category');
|
||||
expect(Constructor).not.toBe(undefined);
|
||||
expect(typeof Constructor).toBe('function');
|
||||
});
|
||||
|
||||
it('Should have the correct default config', function() {
|
||||
var defaultConfig = Chart.scaleService.getScaleDefaults('category');
|
||||
expect(defaultConfig).toEqual({
|
||||
display: true,
|
||||
|
||||
gridLines: {
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
lineWidth: 1,
|
||||
offsetGridLines: false,
|
||||
show: true,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
zeroLineWidth: 1,
|
||||
},
|
||||
position: "bottom",
|
||||
scaleLabel: {
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'normal',
|
||||
labelString: '',
|
||||
show: false,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
maxRotation: 90,
|
||||
minRotation: 20,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
reverse: false,
|
||||
show: true,
|
||||
template: "<%=value%>"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should generate ticks from the data labales', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, 25, 78]
|
||||
}],
|
||||
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5']
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var scale = new Constructor({
|
||||
ctx: {},
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
scale.buildTicks();
|
||||
expect(scale.ticks).toEqual(mockData.labels);
|
||||
});
|
||||
|
||||
it ('Should get the correct pixel for a value when horizontal', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, 25, 78]
|
||||
}],
|
||||
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick_last']
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
config.gridLines.offsetGridLines = true;
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
var minSize = scale.update(200, 100);
|
||||
|
||||
expect(scale.width).toBe(200);
|
||||
expect(scale.height).toBe(28);
|
||||
expect(scale.paddingTop).toBe(0);
|
||||
expect(scale.paddingBottom).toBe(0);
|
||||
expect(scale.paddingLeft).toBe(28);
|
||||
expect(scale.paddingRight).toBe(48);
|
||||
expect(scale.labelRotation).toBe(0);
|
||||
|
||||
expect(minSize).toEqual({
|
||||
width: 200,
|
||||
height: 28,
|
||||
});
|
||||
|
||||
scale.left = 5;
|
||||
scale.top = 5;
|
||||
scale.right = 205;
|
||||
scale.bottom = 33;
|
||||
|
||||
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(33);
|
||||
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(45);
|
||||
|
||||
expect(scale.getPixelForValue(0, 4, 0, false)).toBe(132);
|
||||
expect(scale.getPixelForValue(0, 4, 0, true)).toBe(145);
|
||||
|
||||
config.gridLines.offsetGridLines = false;
|
||||
|
||||
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(33);
|
||||
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(33);
|
||||
|
||||
expect(scale.getPixelForValue(0, 4, 0, false)).toBe(157);
|
||||
expect(scale.getPixelForValue(0, 4, 0, true)).toBe(157);
|
||||
});
|
||||
|
||||
it ('should get the correct pixel for a value when vertical', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, 25, 78]
|
||||
}],
|
||||
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick_last']
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
|
||||
config.gridLines.offsetGridLines = true;
|
||||
config.position = "left";
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('category');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
var minSize = scale.update(100, 200);
|
||||
|
||||
expect(scale.width).toBe(100);
|
||||
expect(scale.height).toBe(200);
|
||||
expect(scale.paddingTop).toBe(6);
|
||||
expect(scale.paddingBottom).toBe(6);
|
||||
expect(scale.paddingLeft).toBe(0);
|
||||
expect(scale.paddingRight).toBe(0);
|
||||
expect(scale.labelRotation).toBe(0);
|
||||
|
||||
expect(minSize).toEqual({
|
||||
width: 100,
|
||||
height: 200,
|
||||
});
|
||||
|
||||
scale.left = 5;
|
||||
scale.top = 5;
|
||||
scale.right = 105;
|
||||
scale.bottom = 205;
|
||||
|
||||
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(11);
|
||||
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(30);
|
||||
|
||||
expect(scale.getPixelForValue(0, 4, 0, false)).toBe(161);
|
||||
expect(scale.getPixelForValue(0, 4, 0, true)).toBe(180);
|
||||
|
||||
config.gridLines.offsetGridLines = false;
|
||||
|
||||
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(11);
|
||||
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(11);
|
||||
|
||||
expect(scale.getPixelForValue(0, 4, 0, false)).toBe(199);
|
||||
expect(scale.getPixelForValue(0, 4, 0, true)).toBe(199);
|
||||
});
|
||||
});
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -1,379 +0,0 @@
|
||||
describe('Logarithmic Scale tests', function() {
|
||||
|
||||
it('Should register the constructor with the scale service', function() {
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
expect(Constructor).not.toBe(undefined);
|
||||
expect(typeof Constructor).toBe('function');
|
||||
});
|
||||
|
||||
it('Should have the correct default config', function() {
|
||||
var defaultConfig = Chart.scaleService.getScaleDefaults('logarithmic');
|
||||
expect(defaultConfig).toEqual({
|
||||
display: true,
|
||||
gridLines: {
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
lineWidth: 1,
|
||||
offsetGridLines: false,
|
||||
show: true,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
zeroLineWidth: 1,
|
||||
},
|
||||
position: "left",
|
||||
scaleLabel: {
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'normal',
|
||||
labelString: '',
|
||||
show: false,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
maxRotation: 90,
|
||||
minRotation: 20,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
reverse: false,
|
||||
show: true,
|
||||
template: "<%var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));if (remain === 1 || remain === 2 || remain === 5) {%><%=value.toExponential()%><%} else {%><%= null %><%}%>",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Should correctly determine the max & min data values', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 5000, 78, 450]
|
||||
}, {
|
||||
yAxisID: 'second scale',
|
||||
data: [1, 1000, 10, 100],
|
||||
}, {
|
||||
yAxisID: scaleID,
|
||||
data: [150]
|
||||
}]
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: Chart.scaleService.getScaleDefaults('logarithmic'), // use default config for scale
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
expect(scale).not.toEqual(undefined); // must construct
|
||||
expect(scale.min).toBe(undefined); // not yet set
|
||||
expect(scale.max).toBe(undefined);
|
||||
|
||||
scale.update(400, 400);
|
||||
expect(scale.min).toBe(1);
|
||||
expect(scale.max).toBe(10000);
|
||||
});
|
||||
|
||||
it('Should correctly determine the max & min for scatter data', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
xAxisID: scaleID, // for the horizontal scale
|
||||
yAxisID: scaleID,
|
||||
data: [{
|
||||
x: 10,
|
||||
y: 100
|
||||
}, {
|
||||
x: 2,
|
||||
y: 6
|
||||
}, {
|
||||
x: 65,
|
||||
y: 121
|
||||
}, {
|
||||
x: 99,
|
||||
y: 7
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var verticalScale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
verticalScale.update(400, 400);
|
||||
expect(verticalScale.min).toBe(1);
|
||||
expect(verticalScale.max).toBe(1000);
|
||||
|
||||
var horizontalConfig = Chart.helpers.clone(config);
|
||||
horizontalConfig.position = 'bottom';
|
||||
var horizontalScale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalConfig,
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
horizontalScale.update(400, 400);
|
||||
expect(horizontalScale.min).toBe(1);
|
||||
expect(horizontalScale.max).toBe(100);
|
||||
});
|
||||
|
||||
it('Should correctly determine the min and max data values when stacked mode is turned on', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 1, 5, 78, 100]
|
||||
}, {
|
||||
yAxisID: 'second scale',
|
||||
data: [-1000, 1000],
|
||||
}, {
|
||||
yAxisID: scaleID,
|
||||
data: [150, 10, 10, 100, 10, 9]
|
||||
}]
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
|
||||
config.stacked = true; // enable scale stacked mode
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
scale.update(400, 400);
|
||||
expect(scale.min).toBe(10);
|
||||
expect(scale.max).toBe(1000);
|
||||
});
|
||||
|
||||
it('Should ensure that the scale has a max and min that are not equal', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: []
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
scale.update(400, 00);
|
||||
expect(scale.min).toBe(1);
|
||||
expect(scale.max).toBe(10);
|
||||
|
||||
mockData.datasets = [{
|
||||
yAxisID: scaleID,
|
||||
data: [0.15, 0.15]
|
||||
}];
|
||||
|
||||
scale.update(400, 400);
|
||||
expect(scale.min).toBe(0.01);
|
||||
expect(scale.max).toBe(1);
|
||||
});
|
||||
|
||||
it('Should generate tick marks', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 1, 25, 78]
|
||||
}, ]
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var scale = new Constructor({
|
||||
ctx: {},
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
// Set arbitrary width and height for now
|
||||
scale.width = 50;
|
||||
scale.height = 400;
|
||||
|
||||
scale.buildTicks();
|
||||
|
||||
// Counts down because the lines are drawn top to bottom
|
||||
expect(scale.ticks).toEqual([100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
|
||||
expect(scale.start).toBe(1);
|
||||
expect(scale.end).toBe(100);
|
||||
});
|
||||
|
||||
it('Should generate tick marks in the correct order in reversed mode', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 1, 25, 78]
|
||||
}, ]
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
|
||||
config.ticks.reverse = true;
|
||||
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var scale = new Constructor({
|
||||
ctx: {},
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
// Set arbitrary width and height for now
|
||||
scale.width = 50;
|
||||
scale.height = 400;
|
||||
|
||||
scale.buildTicks();
|
||||
|
||||
// Counts down because the lines are drawn top to bottom
|
||||
expect(scale.ticks).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
|
||||
expect(scale.start).toBe(100);
|
||||
expect(scale.end).toBe(1);
|
||||
});
|
||||
|
||||
it('Should build labels using the default template', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 1, 25, 78]
|
||||
}, ]
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
scale.update(400, 400);
|
||||
|
||||
expect(scale.ticks).toEqual(['1e+2', '', '', '', '', '5e+1', '', '', '2e+1', '1e+1', '', '', '', '', '5e+0', '', '', '2e+0', '1e+0']);
|
||||
});
|
||||
|
||||
it('Should build labels using the user supplied callback', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 1, 25, 78]
|
||||
}, ]
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
|
||||
config.ticks.userCallback = function(value, index) {
|
||||
return index.toString();
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
scale.update(400, 400);
|
||||
|
||||
// Just the index
|
||||
expect(scale.ticks).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18']);
|
||||
});
|
||||
|
||||
it('Should get the correct pixel value for a point', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
xAxisID: scaleID, // for the horizontal scale
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 1, 25, 78]
|
||||
}]
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('logarithmic'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
|
||||
var verticalScale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
verticalScale.update(50, 100);
|
||||
|
||||
// Fake out positioning of the scale service
|
||||
verticalScale.left = 0;
|
||||
verticalScale.top = 0;
|
||||
verticalScale.right = 50;
|
||||
verticalScale.bottom = 110;
|
||||
verticalScale.paddingTop = 5;
|
||||
verticalScale.paddingBottom = 5;
|
||||
verticalScale.width = 50;
|
||||
verticalScale.height = 110;
|
||||
|
||||
expect(verticalScale.getPixelForValue(100, 0, 0)).toBe(5); // top + paddingTop
|
||||
expect(verticalScale.getPixelForValue(1, 0, 0)).toBe(105); // bottom - paddingBottom
|
||||
expect(verticalScale.getPixelForValue(10, 0, 0)).toBe(55); // halfway
|
||||
|
||||
var horizontalConfig = Chart.helpers.clone(config);
|
||||
horizontalConfig.position = 'bottom';
|
||||
var horizontalScale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: horizontalConfig,
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
horizontalScale.update(100, 50);
|
||||
|
||||
// Fake out positioning of the scale service
|
||||
horizontalScale.left = 0;
|
||||
horizontalScale.top = 0;
|
||||
horizontalScale.right = 110;
|
||||
horizontalScale.bottom = 50;
|
||||
horizontalScale.paddingLeft = 5;
|
||||
horizontalScale.paddingRight = 5;
|
||||
horizontalScale.width = 110;
|
||||
horizontalScale.height = 50;
|
||||
|
||||
expect(horizontalScale.getPixelForValue(100, 0, 0)).toBe(105); // right - paddingRight
|
||||
expect(horizontalScale.getPixelForValue(1, 0, 0)).toBe(5); // left + paddingLeft
|
||||
expect(horizontalScale.getPixelForValue(10, 0, 0)).toBe(55); // halfway
|
||||
});
|
||||
});
|
||||
@@ -1,773 +0,0 @@
|
||||
// Tests for the radial linear scale used by the polar area and radar charts
|
||||
describe('Test the radial linear scale', function() {
|
||||
it('Should register the constructor with the scale service', function() {
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
expect(Constructor).not.toBe(undefined);
|
||||
expect(typeof Constructor).toBe('function');
|
||||
});
|
||||
|
||||
it('Should have the correct default config', function() {
|
||||
var defaultConfig = Chart.scaleService.getScaleDefaults('radialLinear');
|
||||
expect(defaultConfig).toEqual({
|
||||
angleLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1
|
||||
},
|
||||
animate: true,
|
||||
display: true,
|
||||
gridLines: {
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
lineWidth: 1,
|
||||
offsetGridLines: false,
|
||||
show: true,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
zeroLineWidth: 1,
|
||||
},
|
||||
lineArc: false,
|
||||
pointLabels: {
|
||||
fontColor: "#666",
|
||||
fontFamily: "'Arial'",
|
||||
fontSize: 10,
|
||||
fontStyle: "normal",
|
||||
},
|
||||
position: "chartArea",
|
||||
scaleLabel: {
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'normal',
|
||||
labelString: '',
|
||||
show: false,
|
||||
},
|
||||
ticks: {
|
||||
backdropColor: "rgba(255,255,255,0.75)",
|
||||
backdropPaddingY: 2,
|
||||
backdropPaddingX: 2,
|
||||
beginAtZero: false,
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
maxRotation: 90,
|
||||
minRotation: 20,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
reverse: false,
|
||||
showLabelBackdrop: true,
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Should correctly determine the max & min data values', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, -5, 78, -100]
|
||||
}, {
|
||||
yAxisID: scaleID,
|
||||
data: [150]
|
||||
}],
|
||||
labels: ['lablel1', 'label2', 'label3', 'label4', 'label5', 'label6']
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: Chart.scaleService.getScaleDefaults('radialLinear'), // use default config for scale
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
scale.update(200, 300);
|
||||
expect(scale.min).toBe(-100);
|
||||
expect(scale.max).toBe(200);
|
||||
});
|
||||
|
||||
it('Should ensure that the scale has a max and min that are not equal', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [],
|
||||
labels: []
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: Chart.scaleService.getScaleDefaults('radialLinear'), // use default config for scale
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
scale.update(200, 300);
|
||||
expect(scale.min).toBe(-1);
|
||||
expect(scale.max).toBe(1);
|
||||
});
|
||||
|
||||
it('should forcibly include 0 in the range if the beginAtZero option is used', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [20, 30, 40, 50]
|
||||
}],
|
||||
labels: [],
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
config.ticks.beginAtZero = false;
|
||||
scale.update(400, 400);
|
||||
expect(scale.ticks).toEqual(['20', '25', '30', '35', '40', '45', '50']);
|
||||
|
||||
config.ticks.beginAtZero = true;
|
||||
scale.update(400, 400);
|
||||
expect(scale.ticks).toEqual(['0', '5', '10', '15', '20', '25', '30', '35', '40', '45', '50']);
|
||||
|
||||
mockData.datasets[0].data = [-20, -30, -40, -50];
|
||||
scale.update(400, 400);
|
||||
expect(scale.ticks).toEqual(['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
|
||||
|
||||
config.ticks.beginAtZero = false;
|
||||
scale.update(400, 400);
|
||||
expect(scale.ticks).toEqual(['-50', '-45', '-40', '-35', '-30', '-25', '-20']);
|
||||
});
|
||||
|
||||
it('Should generate tick marks in the correct order in reversed mode', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, 25, 78]
|
||||
}],
|
||||
labels: []
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear'));
|
||||
config.ticks.reverse = true;
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
scale.update(200, 300);
|
||||
|
||||
// Reverse mode makes this count up
|
||||
expect(scale.ticks).toEqual(['80', '60', '40', '20', '0']);
|
||||
expect(scale.start).toBe(80);
|
||||
expect(scale.end).toBe(0);
|
||||
});
|
||||
|
||||
it('Should build labels using the user supplied callback', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, 25, 78]
|
||||
}],
|
||||
labels: []
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear'));
|
||||
config.ticks.userCallback = function(value, index) {
|
||||
return index.toString();
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
scale.update(200, 300);
|
||||
|
||||
// Just the index
|
||||
expect(scale.ticks).toEqual(['0', '1', '2', '3', '4']);
|
||||
});
|
||||
|
||||
it('should correctly set the center point', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, 25, 78]
|
||||
}],
|
||||
labels: ['point1', 'point2', 'point3', 'point4', 'point5'] // used in radar charts which use the same scales
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
scale.left = 10;
|
||||
scale.right = 210;
|
||||
scale.top = 5;
|
||||
scale.bottom = 305;
|
||||
scale.update(200, 300);
|
||||
|
||||
expect(scale.drawingArea).toBe(36);
|
||||
expect(scale.xCenter).toBe(110);
|
||||
expect(scale.yCenter).toBe(155);
|
||||
});
|
||||
|
||||
it('should get the correct distance from the center point', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, 25, 78]
|
||||
}],
|
||||
labels: ['point1', 'point2', 'point3', 'point4', 'point5'] // used in radar charts which use the same scales
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear'));
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
scale.left = 0;
|
||||
scale.right = 200;
|
||||
scale.top = 0;
|
||||
scale.bottom = 300;
|
||||
scale.update(200, 300);
|
||||
|
||||
expect(scale.getDistanceFromCenterForValue(scale.min)).toBe(0);
|
||||
expect(scale.getDistanceFromCenterForValue(scale.max)).toBe(36);
|
||||
expect(scale.getPointPositionForValue(1, 5)).toEqual({
|
||||
x: 102.13987716166409,
|
||||
y: 149.30471176265638,
|
||||
});
|
||||
|
||||
config.reverse = true;
|
||||
|
||||
scale.update(200, 300);
|
||||
|
||||
expect(scale.getDistanceFromCenterForValue(scale.min)).toBe(36);
|
||||
expect(scale.getDistanceFromCenterForValue(scale.max)).toBe(0);
|
||||
});
|
||||
|
||||
it('should draw correctly when there are no point labels', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
datasets: [{
|
||||
yAxisID: scaleID,
|
||||
data: [10, 5, 0, 25, 78]
|
||||
}, ],
|
||||
labels: ['point1', 'point2', 'point3', 'point4', 'point5'] // used in radar charts which use the same scales
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear'));
|
||||
config.lineArc = true;
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config,
|
||||
data: mockData,
|
||||
id: scaleID,
|
||||
});
|
||||
|
||||
scale.left = 0;
|
||||
scale.right = 200;
|
||||
scale.top = 0;
|
||||
scale.bottom = 300;
|
||||
scale.update(200, 300);
|
||||
|
||||
scale.draw();
|
||||
|
||||
var expected = [{
|
||||
"name": "measureText",
|
||||
"args": ["0"]
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["80"]
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["point1"]
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["point2"]
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["point3"]
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["point4"]
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["point5"]
|
||||
}, {
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "arc",
|
||||
"args": [100, 150, 9, 0, 6.283185307179586]
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["20"]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["rgba(255,255,255,0.75)"]
|
||||
}, {
|
||||
"name": "fillRect",
|
||||
"args": [88, 133, 24, 16]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["20", 100, 141]
|
||||
}, {
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "arc",
|
||||
"args": [100, 150, 18, 0, 6.283185307179586]
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["40"]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["rgba(255,255,255,0.75)"]
|
||||
}, {
|
||||
"name": "fillRect",
|
||||
"args": [88, 124, 24, 16]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["40", 100, 132]
|
||||
}, {
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "arc",
|
||||
"args": [100, 150, 27, 0, 6.283185307179586]
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["60"]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["rgba(255,255,255,0.75)"]
|
||||
}, {
|
||||
"name": "fillRect",
|
||||
"args": [88, 115, 24, 16]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["60", 100, 123]
|
||||
}, {
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "arc",
|
||||
"args": [100, 150, 36, 0, 6.283185307179586]
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["80"]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["rgba(255,255,255,0.75)"]
|
||||
}, {
|
||||
"name": "fillRect",
|
||||
"args": [88, 106, 24, 16]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["80", 100, 114]
|
||||
}];
|
||||
expect(mockContext.getCalls()).toEqual(expected);
|
||||
|
||||
mockContext.resetCalls();
|
||||
config.lineArc = false;
|
||||
scale.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 141]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [108.55950864665638, 147.21884705062547]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [105.29006727063226, 157.28115294937453]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [94.70993272936774, 157.28115294937453]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [91.44049135334362, 147.21884705062547]
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["20"]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["rgba(255,255,255,0.75)"]
|
||||
}, {
|
||||
"name": "fillRect",
|
||||
"args": [88, 133, 24, 16]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["20", 100, 141]
|
||||
}, {
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 132]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [117.11901729331277, 144.43769410125094]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [110.58013454126451, 164.56230589874906]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [89.41986545873549, 164.56230589874906]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [82.88098270668723, 144.43769410125094]
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["40"]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["rgba(255,255,255,0.75)"]
|
||||
}, {
|
||||
"name": "fillRect",
|
||||
"args": [88, 124, 24, 16]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["40", 100, 132]
|
||||
}, {
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 123]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [125.67852593996915, 141.6565411518764]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [115.87020181189678, 171.8434588481236]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [84.12979818810322, 171.8434588481236]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [74.32147406003085, 141.6565411518764]
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["60"]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["rgba(255,255,255,0.75)"]
|
||||
}, {
|
||||
"name": "fillRect",
|
||||
"args": [88, 115, 24, 16]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["60", 100, 123]
|
||||
}, {
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 114]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [134.23803458662553, 138.87538820250188]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [121.16026908252903, 179.12461179749812]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [78.83973091747097, 179.12461179749812]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [65.76196541337447, 138.8753882025019]
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "measureText",
|
||||
"args": ["80"]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["rgba(255,255,255,0.75)"]
|
||||
}, {
|
||||
"name": "fillRect",
|
||||
"args": [88, 106, 24, 16]
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["80", 100, 114]
|
||||
}, {
|
||||
"name": "setLineWidth",
|
||||
"args": [1]
|
||||
}, {
|
||||
"name": "setStrokeStyle",
|
||||
"args": ["rgba(0, 0, 0, 0.1)"]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 150]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [65.76196541337447, 138.8753882025019]
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["point5", 61.0066828318987, 137.33030323062715]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 150]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [78.83973091747097, 179.12461179749812]
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["point4", 75.9008046560086, 183.16969676937285]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 150]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [121.16026908252903, 179.12461179749812]
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["point3", 124.0991953439914, 183.16969676937285]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 150]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [134.23803458662553, 138.87538820250188]
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["point2", 138.9933171681013, 137.33030323062715]
|
||||
}, {
|
||||
"name": "beginPath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "moveTo",
|
||||
"args": [100, 150]
|
||||
}, {
|
||||
"name": "lineTo",
|
||||
"args": [100, 114]
|
||||
}, {
|
||||
"name": "stroke",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "closePath",
|
||||
"args": []
|
||||
}, {
|
||||
"name": "setFillStyle",
|
||||
"args": ["#666"]
|
||||
}, {
|
||||
"name": "fillText",
|
||||
"args": ["point1", 100, 109]
|
||||
}]);
|
||||
});
|
||||
});
|
||||
@@ -1,174 +0,0 @@
|
||||
// Time scale tests
|
||||
describe('Time scale tests', function() {
|
||||
it('Should register the constructor with the scale service', function() {
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('time');
|
||||
expect(Constructor).not.toBe(undefined);
|
||||
expect(typeof Constructor).toBe('function');
|
||||
});
|
||||
|
||||
it('Should have the correct default config', function() {
|
||||
var defaultConfig = Chart.scaleService.getScaleDefaults('time');
|
||||
expect(defaultConfig).toEqual({
|
||||
display: true,
|
||||
gridLines: {
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
lineWidth: 1,
|
||||
offsetGridLines: false,
|
||||
show: true,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
zeroLineWidth: 1,
|
||||
},
|
||||
position: "bottom",
|
||||
scaleLabel: {
|
||||
fontColor: '#666',
|
||||
fontFamily: 'Helvetica Neue',
|
||||
fontSize: 12,
|
||||
fontStyle: 'normal',
|
||||
labelString: '',
|
||||
show: false,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
maxRotation: 90,
|
||||
minRotation: 20,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
reverse: false,
|
||||
show: true,
|
||||
template: "<%=value%>"
|
||||
},
|
||||
time: {
|
||||
format: false,
|
||||
unit: false,
|
||||
round: false,
|
||||
displayFormat: false,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('should build ticks using days', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
labels: ["2015-01-01T20:00:00", "2015-01-02T21:00:00", "2015-01-03T22:00:00", "2015-01-05T23:00:00", "2015-01-07T03:00", "2015-01-08T10:00", "2015-01-10T12:00"], // days
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('time');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: Chart.scaleService.getScaleDefaults('time'), // use default config for scale
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
//scale.buildTicks();
|
||||
scale.update(400, 50);
|
||||
|
||||
// Counts down because the lines are drawn top to bottom
|
||||
expect(scale.ticks).toEqual(['Jan 1, 2015', 'Jan 2, 2015', 'Jan 3, 2015', 'Jan 4, 2015', 'Jan 5, 2015', 'Jan 6, 2015', 'Jan 7, 2015', 'Jan 8, 2015', 'Jan 9, 2015', 'Jan 10, 2015', 'Jan 11, 2015']);
|
||||
});
|
||||
|
||||
it ('should build ticks using the config unit', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
labels: ["2015-01-01T20:00:00", "2015-01-02T21:00:00"], // days
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.scaleService.getScaleDefaults('time');
|
||||
config.time.unit = 'hour';
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('time');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config, // use default config for scale
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
//scale.buildTicks();
|
||||
scale.update(400, 50);
|
||||
expect(scale.ticks).toEqual(['Jan 1, 8PM', 'Jan 1, 9PM', 'Jan 1, 10PM', 'Jan 1, 11PM', 'Jan 2, 12AM', 'Jan 2, 1AM', 'Jan 2, 2AM', 'Jan 2, 3AM', 'Jan 2, 4AM', 'Jan 2, 5AM', 'Jan 2, 6AM', 'Jan 2, 7AM', 'Jan 2, 8AM', 'Jan 2, 9AM', 'Jan 2, 10AM', 'Jan 2, 11AM', 'Jan 2, 12PM', 'Jan 2, 1PM', 'Jan 2, 2PM', 'Jan 2, 3PM', 'Jan 2, 4PM', 'Jan 2, 5PM', 'Jan 2, 6PM', 'Jan 2, 7PM', 'Jan 2, 8PM', 'Jan 2, 9PM']);
|
||||
});
|
||||
|
||||
it ('should build ticks using the config diff', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
labels: ["2015-01-01T20:00:00", "2015-02-02T21:00:00", "2015-02-21T01:00:00"], // days
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var config = Chart.scaleService.getScaleDefaults('time');
|
||||
config.time.unit = 'week';
|
||||
config.time.round = 'week';
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('time');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: config, // use default config for scale
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
//scale.buildTicks();
|
||||
scale.update(400, 50);
|
||||
expect(scale.ticks).toEqual(['Dec 28, 2014', 'Jan 4, 2015', 'Jan 11, 2015', 'Jan 18, 2015', 'Jan 25, 2015', 'Feb 1, 2015', 'Feb 8, 2015', 'Feb 15, 2015']);
|
||||
});
|
||||
|
||||
it ('should get the correct pixel for a value', function() {
|
||||
var scaleID = 'myScale';
|
||||
|
||||
var mockData = {
|
||||
labels: ["2015-01-01T20:00:00", "2015-01-02T21:00:00", "2015-01-03T22:00:00", "2015-01-05T23:00:00", "2015-01-07T03:00", "2015-01-08T10:00", "2015-01-10T12:00"], // days
|
||||
};
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var Constructor = Chart.scaleService.getScaleConstructor('time');
|
||||
var scale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: Chart.scaleService.getScaleDefaults('time'), // use default config for scale
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
|
||||
//scale.buildTicks();
|
||||
scale.update(400, 50);
|
||||
|
||||
expect(scale.width).toBe(400);
|
||||
expect(scale.height).toBe(28);
|
||||
scale.left = 0;
|
||||
scale.right = 400;
|
||||
scale.top = 10;
|
||||
scale.bottom = 38;
|
||||
|
||||
expect(scale.getPixelForValue('', 0, 0)).toBe(63);
|
||||
expect(scale.getPixelForValue('', 6, 0)).toBe(342);
|
||||
|
||||
var verticalScaleConfig = Chart.scaleService.getScaleDefaults('time');
|
||||
verticalScaleConfig.position = "left";
|
||||
|
||||
var verticalScale = new Constructor({
|
||||
ctx: mockContext,
|
||||
options: verticalScaleConfig,
|
||||
data: mockData,
|
||||
id: scaleID
|
||||
});
|
||||
verticalScale.update(50, 400);
|
||||
expect(verticalScale.width).toBe(50);
|
||||
expect(verticalScale.height).toBe(400);
|
||||
verticalScale.top = 0;
|
||||
verticalScale.left = 0;
|
||||
verticalScale.right = 50;
|
||||
verticalScale.bottom = 400;
|
||||
|
||||
expect(verticalScale.getPixelForValue('', 0, 0)).toBe(6);
|
||||
expect(verticalScale.getPixelForValue('', 6, 0)).toBe(394);
|
||||
});
|
||||
});
|
||||
Referência em uma Nova Issue
Bloquear um usuário