Browse Source

Merge pull request #1 from masayuki0812/master

update repo to latest changes
pull/840/head
Yuval Bar-On 10 years ago
parent
commit
f97a0693f0
  1. 4
      .gitignore
  2. 12
      Gruntfile.coffee
  3. 10
      README.md
  4. 10
      c3.css
  5. 1100
      c3.js
  6. 1
      c3.min.css
  7. 9
      c3.min.js
  8. 2
      component.json
  9. 6
      htdocs/data/c3_test.tsv
  10. 3
      htdocs/index.html
  11. 45
      htdocs/samples/chart_gauge.html
  12. 13
      htdocs/samples/data_url.html
  13. 49
      htdocs/samples/regions_timeseries.html
  14. 10
      htdocs/samples/zoom.html
  15. 6
      package.json
  16. 162
      spec/api.data-spec.js
  17. 126
      spec/api.zoom-spec.js
  18. 78
      spec/arc-spec.js
  19. 612
      spec/axis-spec.js
  20. 42
      spec/c3-helper.js
  21. 19
      spec/c3-spec.js
  22. 60
      spec/class-spec.js
  23. 59
      spec/core-spec.js
  24. 150
      spec/data-spec.js
  25. 96
      spec/domain-spec.js
  26. 83
      spec/grid-spec.js
  27. 140
      spec/interaction-spec.js
  28. 89
      spec/legend-spec.js
  29. 100
      spec/shape.bar-spec.js
  30. 60
      spec/shape.line-spec.js
  31. 108
      spec/type-spec.js
  32. 1
      src/api.chart.js
  33. 37
      src/api.data.js
  34. 74
      src/api.focus.js
  35. 11
      src/api.load.js
  36. 4
      src/api.region.js
  37. 24
      src/api.show.js
  38. 11
      src/api.zoom.js
  39. 119
      src/arc.js
  40. 71
      src/axis.js
  41. 114
      src/c3.axis.js
  42. 24
      src/class.js
  43. 17
      src/clip.js
  44. 17
      src/config.js
  45. 91
      src/core.js
  46. 14
      src/data.convert.js
  47. 69
      src/data.js
  48. 31
      src/domain.js
  49. 15
      src/grid.js
  50. 78
      src/interaction.js
  51. 63
      src/legend.js
  52. 10
      src/scale.js
  53. 21
      src/shape.bar.js
  54. 17
      src/shape.js
  55. 46
      src/shape.line.js
  56. 28
      src/size.js
  57. 4
      src/subchart.js
  58. 17
      src/text.js
  59. 17
      src/tooltip.js
  60. 18
      src/type.js
  61. 37
      src/zoom.js

4
.gitignore vendored

@ -2,4 +2,6 @@
node_modules
bower_components
d3.js
d3.min.js
d3.min.js
components
build

12
Gruntfile.coffee

@ -83,11 +83,19 @@ module.exports = (grunt) ->
c3:
src: 'c3.js'
options:
specs: 'spec/*.js'
specs: 'spec/*-spec.js'
helpers: 'spec/*-helper.js'
styles: 'c3.css'
vendor: 'http://d3js.org/d3.v3.min.js'
uglify:
c3:
files:
'c3.min.js': 'c3.js'
grunt.registerTask 'default', ['concat', 'jshint', 'jasmine', 'uglify']
cssmin:
c3:
src: 'c3.css'
dest: 'c3.min.css'
grunt.registerTask 'default', ['concat', 'jshint', 'jasmine', 'cssmin', 'uglify']

10
README.md

@ -1,26 +1,26 @@
c3 [![Build Status](https://travis-ci.org/masayuki0812/c3.png?branch=master)](https://travis-ci.org/masayuki0812/c3)
c3 [![Build Status](https://travis-ci.org/masayuki0812/c3.svg?branch=master)](https://travis-ci.org/masayuki0812/c3)
==
c3 is a D3-based reusable chart library that enables deeper integration of charts into web applications.
More information is here: [http://c3js.org](http://c3js.org/)
Follow the link for more information: [http://c3js.org](http://c3js.org/)
## Tutorial and Examples
+ [Getting Started](http://c3js.org/gettingstarted.html)
+ [Examples](http://c3js.org/examples.html)
Another samples are included in this repository:
Additional samples can be found in this repository:
+ [https://github.com/masayuki0812/c3/tree/master/htdocs/samples](https://github.com/masayuki0812/c3/tree/master/htdocs/samples)
And you can run these samples as:
You can run these samples as:
```
$ cd c3/htdocs
$ python -m SimpleHTTPServer 8080
```
## Forum
Now you can ask anything in this forum:
Discuss anything in this forum:
+ [https://groups.google.com/forum/#!forum/c3js](https://groups.google.com/forum/#!forum/c3js)
## Playground

10
c3.css

@ -104,9 +104,16 @@
/*-- Focus --*/
.c3-target.c3-focused {
opacity: 1;
}
.c3-target.c3-focused path.c3-line, .c3-target.c3-focused path.c3-step {
stroke-width: 2px;
}
.c3-target.c3-defocused {
opacity: 0.3 !important;
}
/*-- Region --*/
@ -141,6 +148,9 @@
/*-- Tooltip --*/
.c3-tooltip-container {
z-index: 10;
}
.c3-tooltip {
border-collapse:collapse;
border-spacing:0;

1100
c3.js

File diff suppressed because it is too large Load Diff

1
c3.min.css vendored

@ -0,0 +1 @@
.c3 svg{font:10px sans-serif}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:gray;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-chart-arcs-title{font-size:1.3em}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #CCC}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#FFF}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000;font-size:28px}

9
c3.min.js vendored

File diff suppressed because one or more lines are too long

2
component.json

@ -5,7 +5,7 @@
"version": "0.3.0",
"keywords": [],
"dependencies": {
"mbostock/d3": "*"
"mbostock/d3": "v3.4.4"
},
"development": {},
"license": "MIT",

6
htdocs/data/c3_test.tsv

@ -0,0 +1,6 @@
data1 data2 data3
520 380 100
540 350 110
570 400 150
550 370 200
580 420 180
1 data1 data2 data3
2 520 380 100
3 540 350 110
4 570 400 150
5 550 370 200
6 580 420 180

3
htdocs/index.html

@ -273,6 +273,9 @@
<a href="./samples/regions.html">
Show regions
</a>
<a href="./samples/regions_timeseries.html">
Show regions with timeseries
</a>
</div>
<div class="col-md-4">
<h3>Legend</h3>

45
htdocs/samples/chart_gauge.html

@ -4,6 +4,9 @@
</head>
<body>
<div id="chart"></div>
<div id="chart1"></div>
<div id="chart2"></div>
<div id="chart3"></div>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="/js/c3.js"></script>
@ -40,6 +43,48 @@
}
});
var chart1 = c3.generate({
bindto: '#chart1',
data: {
columns: [
['data', 75.0]
],
type: 'gauge',
},
gauge: {
min: 50,
max: 100
}
});
var chart2 = c3.generate({
bindto: '#chart2',
data: {
columns: [
['data', 0.0]
],
type: 'gauge',
},
gauge: {
min: -100,
max: 100
}
});
var chart3 = c3.generate({
bindto: '#chart3',
data: {
columns: [
['data', -75.0]
],
type: 'gauge',
},
gauge: {
min: -100,
max: -50
}
});
var cycleDemo = function () {
setTimeout(function () {

13
htdocs/samples/data_url.html

@ -21,6 +21,13 @@
});
}, 1000);
setTimeout(function () {
chart.load({
url: '/data/c3_test.tsv',
mimeType: 'tsv'
});
}, 2000);
setTimeout(function () {
chart = c3.generate({
data: {
@ -28,14 +35,14 @@
mimeType: 'json'
}
});
}, 2000);
}, 3000);
setTimeout(function () {
chart.load({
url: '/data/c3_test_2.json',
mimeType: 'json'
});
}, 3000);
}, 4000);
setTimeout(function () {
chart.load({
@ -45,7 +52,7 @@
value: ['data1', 'data2']
}
});
}, 4000);
}, 5000);
</script>
</body>

49
htdocs/samples/regions_timeseries.html

@ -0,0 +1,49 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="/css/c3.css">
</head>
<body>
<div id="chart"></div>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="/js/c3.js"></script>
<script>
var chart = c3.generate({
data: {
x: 'date',
columns: [
['date', '2014-01-01', '2014-01-10', '2014-01-20', '2014-01-30', '2014-02-01'],
['sample', 30, 200, 100, 400, 150, 250]
]
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y%m%d %H:%M:%S'
}
},
},
regions: [
{start: '2014-01-05', end: '2014-01-10'},
// {start: new Date('2014-01-10'), end: new Date('2014-01-15')},
{start: 1390608000000, end: 1391040000000}
]
});
setTimeout(function () {
chart.load({
columns: [
['date', +new Date('2014-01-01'), +new Date('2014-01-10'), +new Date('2014-03-01')],
['sample', 100, 200, 300]
]
});
chart.regions([
{start: +new Date('2014-01-10'), end: +new Date('2014-01-15')}
]);
}, 1000);
</script>
</body>
</html>

10
htdocs/samples/zoom.html

@ -23,7 +23,15 @@
default: [30, 60]
}
},
zoom: { enabled: true },
zoom: {
enabled: true,
onzoomstart: function (event) {
console.log("onzoomstart", event);
},
onzoomend: function (domain) {
console.log("onzoomend", domain);
},
},
subchart: { show: true }
});

6
package.json

@ -19,10 +19,14 @@
"license": "MIT",
"gitHead": "84e03109d9a590f9c8ef687c03d751f666080c6f",
"readmeFilename": "README.md",
"dependencies": {
"d3": "~3.4.4"
},
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-concat": "~0.5.0",
"grunt-contrib-jasmine": "~0.5.2",
"grunt-contrib-cssmin": "^0.10.0",
"grunt-contrib-jasmine": "~0.8.0",
"grunt-contrib-jshint": "~0.7.1",
"grunt-contrib-uglify": "~0.4.0",
"grunt-contrib-watch": "^0.6.1",

162
spec/api.data-spec.js

@ -0,0 +1,162 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 api data', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 5000, 2000, 1000, 4000, 1500, 2500]
],
names: {
data1: 'Data Name 1',
data2: 'Data Name 2'
},
colors: {
data1: '#FF0000',
data2: '#00FF00'
},
axes: {
data1: 'y',
data2: 'y2'
}
},
axis: {
y2: {
show: true
}
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
window.initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter')
.style('position', 'absolute')
.style('right', 0);
window.setTimeout(function () {
done();
}, 10);
});
describe('data()', function () {
it('should return all of data if no argument given', function () {
var results = chart.data(),
expected = ['data1', 'data2'];
results.forEach(function (result, i) {
expect(result.id).toBe(expected[i]);
});
});
it('should return specifid data if string argument given', function () {
var results = chart.data('data1');
expect(results.length).toBe(1);
expect(results[0].id).toBe('data1');
});
it('should return specifid data if array argument given', function () {
var results = chart.data(['data1', 'data2']);
expect(results.length).toBe(2);
expect(results[0].id).toBe('data1');
expect(results[1].id).toBe('data2');
});
});
describe('data.shown()', function () {
it('should return only shown targets', function () {
var results;
chart.hide('data1');
results = chart.data.shown();
expect(results.length).toBe(1);
expect(results[0].id).toBe('data2');
});
});
describe('data.names()', function () {
it('should return data.names specified as argument', function () {
var results = chart.data.names();
expect(results.data1).toBe('Data Name 1');
expect(results.data2).toBe('Data Name 2');
});
it('should return data.names specified as api', function () {
var results = chart.data.names({
data1: 'New Data Name 1',
data2: 'New Data Name 2'
});
expect(results.data1).toBe('New Data Name 1');
expect(results.data2).toBe('New Data Name 2');
});
it('should set data.names specified as api', function () {
expect(d3.select('.c3-legend-item-data1 text').text()).toBe("New Data Name 1");
expect(d3.select('.c3-legend-item-data2 text').text()).toBe("New Data Name 2");
});
});
describe('data.colors()', function () {
it('should return data.colors specified as argument', function () {
var results = chart.data.colors();
expect(results.data1).toBe('#FF0000');
expect(results.data2).toBe('#00FF00');
});
it('should return data.colors specified as api', function () {
var results = chart.data.colors({
data1: '#00FF00',
data2: '#FF0000'
});
expect(results.data1).toBe('#00FF00');
expect(results.data2).toBe('#FF0000');
});
it('should set data.colors specified as api', function () {
expect(d3.select('.c3-line-data1').style('stroke')).toBe("#00ff00");
expect(d3.select('.c3-line-data2').style('stroke')).toBe("#ff0000");
expect(d3.select('.c3-legend-item-data1 .c3-legend-item-tile').style('fill')).toBe("#00ff00");
expect(d3.select('.c3-legend-item-data2 .c3-legend-item-tile').style('fill')).toBe("#ff0000");
});
});
describe('data.axes()', function () {
it('should return data.axes specified as argument', function () {
var results = chart.data.axes();
expect(results.data1).toBe('y');
expect(results.data2).toBe('y2');
expect(d3.select('.c3-axis-y g.tick text').text()).toBe('0');
expect(d3.select('.c3-axis-y2 g.tick text').text()).toBe('1000');
});
it('should return data.axes specified as api', function () {
var results = chart.data.axes({
data1: 'y2',
data2: 'y'
});
expect(results.data1).toBe('y2');
expect(results.data2).toBe('y');
expect(d3.select('.c3-axis-y g.tick text').text()).toBe('1000');
expect(d3.select('.c3-axis-y2 g.tick text').text()).toBe('0');
});
});
});

126
spec/api.zoom-spec.js

@ -0,0 +1,126 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 api zoom', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
]
},
zoom: {
enabled: true
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
window.initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
// chart.internal.d3.select('.jasmine_html-reporter').style('display', 'none');
window.setTimeout(function () {
done();
}, 10);
});
describe('zoom', function () {
it('should be zoomed properly', function () {
var target = [3, 5], domain;
chart.zoom(target);
domain = chart.internal.x.domain();
expect(domain[0]).toBe(target[0]);
expect(domain[1]).toBe(target[1]);
});
it('should be zoomed properly again', function () {
var target = [1, 4], domain;
chart.zoom(target);
domain = chart.internal.x.domain();
expect(domain[0]).toBe(target[0]);
expect(domain[1]).toBe(target[1]);
});
it('should load timeseries data', function () {
args = {
data: {
x: 'date',
columns: [
['date', '2014-01-01', '2014-01-02', '2014-08-01', '2014-10-19'],
['data1', 30, 200, 100, 400]
]
},
axis: {
x: {
type: 'timeseries'
}
},
zoom: {
enabled: true
}
};
expect(true).toBeTruthy();
});
it('should be zoomed properly', function () {
var target = [new Date(2014, 7, 1), new Date(2014, 8, 1)], domain;
chart.zoom(target);
domain = chart.internal.x.domain();
expect(+domain[0]).toBe(+target[0]);
expect(+domain[1]).toBe(+target[1]);
});
it('should be zoomed properly', function () {
var target = ['2014-08-01', '2014-09-01'], domain;
chart.zoom(target);
domain = chart.internal.x.domain();
expect(+domain[0]).toBe(+chart.internal.parseDate(target[0]));
expect(+domain[1]).toBe(+chart.internal.parseDate(target[1]));
});
});
describe('unzoom', function () {
it('should load indexed data', function () {
args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250]
]
},
zoom: {
enabled: true
}
};
expect(true).toBeTruthy();
});
it('should be unzoomed properly', function () {
var target = [1, 4], orginal = chart.internal.x.domain(), domain;
chart.zoom(target);
domain = chart.internal.x.domain();
expect(domain[0]).toBe(target[0]);
expect(domain[1]).toBe(target[1]);
chart.unzoom();
domain = chart.internal.x.domain();
expect(domain[0]).toBe(orginal[0]);
expect(domain[1]).toBe(orginal[1]);
});
});
});

78
spec/arc-spec.js

@ -0,0 +1,78 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart axis', function () {
'use strict';
var chart, d3, args;
beforeEach(function (done) {
if (typeof chart === 'undefined') {
window.initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter')
.style('position', 'absolute')
.style('right', 0);
window.setTimeout(function () {
done();
}, 50);
});
describe('show pie chart', function () {
args = {
data: {
columns: [
['data1', 30],
['data2', 150],
['data3', 120]
],
type: 'pie'
}
};
it('should have correct classes', function () {
var chartArc = d3.select('.c3-chart-arcs'),
arcs = {
data1: chartArc.select('.c3-chart-arc.c3-target.c3-target-data1')
.select('g.c3-shapes.c3-shapes-data1.c3-arcs.c3-arcs-data1')
.select('path.c3-shape.c3-shape.c3-arc.c3-arc-data1'),
data2: chartArc.select('.c3-chart-arc.c3-target.c3-target-data2')
.select('g.c3-shapes.c3-shapes-data2.c3-arcs.c3-arcs-data2')
.select('path.c3-shape.c3-shape.c3-arc.c3-arc-data2'),
data3: chartArc.select('.c3-chart-arc.c3-target.c3-target-data3')
.select('g.c3-shapes.c3-shapes-data3.c3-arcs.c3-arcs-data3')
.select('path.c3-shape.c3-shape.c3-arc.c3-arc-data3')
};
expect(arcs.data1.size()).toBe(1);
expect(arcs.data2.size()).toBe(1);
expect(arcs.data3.size()).toBe(1);
});
it('should have correct d', function () {
expect(d3.select('.c3-arc-data1').attr('d')).toMatch(/M-124\..+,-171\..+A211\..+,211\..+ 0 0,1 -3\..+,-211\..+L0,0Z/);
expect(d3.select('.c3-arc-data2').attr('d')).toMatch(/M1\..+,-211\..+A211\..+,211\..+ 0 0,1 1\..+,211\..+L0,0Z/);
expect(d3.select('.c3-arc-data3').attr('d')).toMatch(/M1\..+,211\..+A211\..+,211\..+ 0 0,1 -124\..+,-171\..+L0,0Z/);
});
it('should set args with data id that can be converted to a color', function () {
args.data.columns = [
['black', 30],
['data2', 150],
['data3', 120]
];
expect(true).toBeTruthy();
});
it('should have correct d even if data id can be converted to a color', function () {
expect(d3.select('.c3-arc-black').attr('d')).toMatch(/M-124\..+,-171\..+A211\..+,211\..+ 0 0,1 -3\..+,-211\..+L0,0Z/);
});
});
});

612
spec/axis-spec.js

@ -0,0 +1,612 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart axis', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
]
},
axis: {
y: {
tick: {
values: null,
count: undefined
}
},
y2: {
tick: {
values: null,
count: undefined
}
}
}
};
beforeEach(function (done) {
chart = window.initChart(chart, args, done);
d3 = chart.internal.d3;
});
describe('axis.y.tick.count', function () {
var i = 1;
beforeEach(function () {
args.axis.y.tick.count = i++;
chart = window.c3.generate(args);
});
it('should have only 1 tick on y axis', function () {
var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size();
expect(ticksSize).toBe(1);
});
it('should have 2 ticks on y axis', function () {
var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size();
expect(ticksSize).toBe(2);
});
it('should have 3 ticks on y axis', function () {
var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size();
expect(ticksSize).toBe(3);
});
});
describe('axis.y.tick.values', function () {
var values = [100, 500];
beforeEach(function () {
args.axis.y.tick.values = values;
chart = window.c3.generate(args);
});
it('should have only 2 tick on y axis', function () {
var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size();
expect(ticksSize).toBe(2);
});
it('should have specified tick texts', function () {
d3.select('.c3-axis-y').selectAll('g.tick').each(function (d, i) {
var text = d3.select(this).select('text').text();
expect(+text).toBe(values[i]);
});
});
});
describe('axis.x.tick.width', function () {
describe('indexed x axis and y/y2 axis', function () {
describe('not rotated', function () {
it('should update args successfully', function () {
args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
],
axes: {
data2: 'y2'
}
},
axis: {
y2: {
show: true
}
}
};
expect(true).toBeTruthy();
});
it('should construct indexed x axis properly', function () {
var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'),
expectedX = '0',
expectedDy = '.71em';
expect(ticks.size()).toBe(6);
ticks.each(function (d, i) {
var tspans = d3.select(this).selectAll('tspan');
expect(tspans.size()).toBe(1);
tspans.each(function () {
var tspan = d3.select(this);
expect(tspan.text()).toBe(i + '');
expect(tspan.attr('x')).toBe(expectedX);
expect(tspan.attr('dy')).toBe(expectedDy);
});
});
});
it('should set axis.x.tick.format', function () {
args.axis.x = {
tick: {
format: function () {
return 'very long tick text on x axis';
}
}
};
expect(true).toBeTruthy();
});
it('should split x axis tick text to multiple lines', function () {
var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'),
expectedTexts = ['very long tick text on x', 'axis'],
expectedX = '0';
expect(ticks.size()).toBe(6);
ticks.each(function () {
var tspans = d3.select(this).selectAll('tspan');
expect(tspans.size()).toBe(2);
tspans.each(function (d, i) {
var tspan = d3.select(this);
expect(tspan.text()).toBe(expectedTexts[i]);
expect(tspan.attr('x')).toBe(expectedX);
if (i === 0) {
expect(tspan.attr('dy')).toBe('.71em');
} else {
expect(tspan.attr('dy')).toBeGreaterThan(8);
}
});
});
});
it('should construct y axis properly', function () {
var ticks = chart.internal.main.select('.c3-axis-y').selectAll('g.tick'),
expectedX = '-9',
expectedDy = '3';
expect(ticks.size()).toBe(9);
ticks.each(function (d) {
var tspans = d3.select(this).selectAll('tspan');
expect(tspans.size()).toBe(1);
tspans.each(function () {
var tspan = d3.select(this);
expect(tspan.text()).toBe(d + '');
expect(tspan.attr('x')).toBe(expectedX);
expect(tspan.attr('dy')).toBe(expectedDy);
});
});
});
it('should construct y2 axis properly', function () {
var ticks = chart.internal.main.select('.c3-axis-y2').selectAll('g.tick'),
expectedX = '9',
expectedDy = '3';
expect(ticks.size()).toBe(9);
ticks.each(function (d) {
var tspans = d3.select(this).selectAll('tspan');
expect(tspans.size()).toBe(1);
tspans.each(function () {
var tspan = d3.select(this);
expect(tspan.text()).toBe(d + '');
expect(tspan.attr('x')).toBe(expectedX);
expect(tspan.attr('dy')).toBe(expectedDy);
});
});
});
it('should set big values in y', function () {
args.data.columns = [
['data1', 3000000000000000, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
];
expect(true).toBeTruthy();
});
it('should not split y axis tick text to multiple lines', function () {
var ticks = chart.internal.main.select('.c3-axis-y2').selectAll('g.tick');
ticks.each(function () {
var tspans = d3.select(this).selectAll('tspan');
expect(tspans.size()).toBe(1);
});
});
});
describe('rotated', function () {
it('should update args to rotate axis', function () {
args.axis.rotated = true;
expect(true).toBeTruthy();
});
it('should split x axis tick text to multiple lines', function () {
var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'),
expectedTexts = ['very long tick text on', 'x axis'],
expectedX = '-9';
expect(ticks.size()).toBe(6);
ticks.each(function () {
var tspans = d3.select(this).selectAll('tspan');
expect(tspans.size()).toBe(2);
tspans.each(function (d, i) {
var tspan = d3.select(this);
expect(tspan.text()).toBe(expectedTexts[i]);
expect(tspan.attr('x')).toBe(expectedX);
if (i === 0) {
expect(tspan.attr('dy')).toBeLessThan(0);
} else {
expect(tspan.attr('dy')).toBeGreaterThan(9);
}
});
});
});
it('should not split y axis tick text to multiple lines', function () {
var ticks = chart.internal.main.select('.c3-axis-y').selectAll('g.tick'),
expectedTexts = [
'0',
'500000000000000',
'1000000000000000',
'1500000000000000',
'2000000000000000',
'2500000000000000',
'3000000000000000'
],
expectedX = '0',
expectedDy = '.71em';
expect(ticks.size()).toBe(7);
ticks.each(function (d, i) {
var tspans = d3.select(this).selectAll('tspan');
expect(tspans.size()).toBe(1);
tspans.each(function () {
var tspan = d3.select(this);
expect(tspan.text()).toBe(expectedTexts[i]);
expect(tspan.attr('x')).toBe(expectedX);
expect(tspan.attr('dy')).toBe(expectedDy);
});
});
});
});
});
describe('category axis', function () {
describe('not rotated', function () {
it('should update args successfully', function () {
args = {
data: {
x: 'x',
columns: [
['x', 'this is a very long tick text on category axis', 'cat1', 'cat2', 'cat3', 'cat4', 'cat5'],
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
},
axis: {
x: {
type: 'category'
}
}
};
expect(true).toBeTruthy();
});
it('should locate ticks properly', function () {
var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick');
ticks.each(function (d, i) {
var tspans = d3.select(this).selectAll('tspan'),
expectedX = '0',
expectedDy = '.40em';
if (i > 0) { // i === 0 should be checked in next test
expect(tspans.size()).toBe(1);
tspans.each(function () {
var tspan = d3.select(this);
expect(tspan.attr('x')).toBe(expectedX);
expect(tspan.attr('dy')).toBe(expectedDy);
});
}
});
});
it('should split tick text properly', function () {
var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'),
tspans = tick.selectAll('tspan'),
expectedTickTexts = [
'this is a very',
'long tick text on',
'category axis'
],
expectedX = '0';
expect(tspans.size()).toBe(3);
tspans.each(function (d, i) {
var tspan = d3.select(this);
expect(tspan.text()).toBe(expectedTickTexts[i]);
expect(tspan.attr('x')).toBe(expectedX);
// unable to define pricise number because it differs depends on environment..
if (i === 0) {
expect(tspan.attr('dy')).toBe('.40em');
} else {
expect(tspan.attr('dy')).toBeGreaterThan(8);
}
});
});
});
describe('rotated', function () {
it('should update args to rotate axis', function () {
args.axis.rotated = true;
expect(true).toBeTruthy();
});
it('should locate ticks on rotated axis properly', function () {
var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick');
ticks.each(function (d, i) {
var tspans = d3.select(this).selectAll('tspan'),
expectedX = '-9',
expectedDy = '2';
if (i > 0) { // i === 0 should be checked in next test
expect(tspans.size()).toBe(1);
tspans.each(function () {
var tspan = d3.select(this);
expect(tspan.attr('x')).toBe(expectedX);
expect(tspan.attr('dy')).toBe(expectedDy);
});
}
});
});
it('should split tick text on rotated axis properly', function () {
var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'),
tspans = tick.selectAll('tspan'),
expectedTickTexts = [
'this is a very long',
'tick text on',
'category axis'
],
expectedX = '-9';
expect(tspans.size()).toBe(3);
tspans.each(function (d, i) {
var tspan = d3.select(this);
expect(tspan.text()).toBe(expectedTickTexts[i]);
expect(tspan.attr('x')).toBe(expectedX);
// unable to define pricise number because it differs depends on environment..
if (i === 0) {
expect(tspan.attr('dy')).toBeLessThan(0);
} else {
expect(tspan.attr('dy')).toBeGreaterThan(8);
}
});
});
});
describe('option used', function () {
describe('as null', function () {
it('should update args not to split ticks', function () {
args.axis.x.tick = {
multiline: false
};
expect(true).toBeTruthy();
});
it('should split x tick', function () {
var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'),
tspans = tick.selectAll('tspan');
expect(tspans.size()).toBe(1);
});
});
describe('as value', function () {
it('should update args not to split ticks', function () {
args.axis.x.tick = {
width: 150
};
expect(true).toBeTruthy();
});
it('should split x tick to 2 lines properly', function () {
var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'),
tspans = tick.selectAll('tspan'),
expectedTickTexts = [
'this is a very long tick text',
'on category axis'
],
expectedX = '-9';
expect(tspans.size()).toBe(2);
tspans.each(function (d, i) {
var tspan = d3.select(this);
expect(tspan.text()).toBe(expectedTickTexts[i]);
expect(tspan.attr('x')).toBe(expectedX);
// unable to define pricise number because it differs depends on environment..
if (i === 0) {
expect(tspan.attr('dy')).toBeLessThan(0);
} else {
expect(tspan.attr('dy')).toBeGreaterThan(8);
}
});
});
});
});
});
describe('with axis.x.tick.format', function () {
it('should update args to use axis.x.tick.format', function () {
args.axis.x.tick.format = function () {
return ['this is a very long tick text', 'on category axis'];
};
expect(true).toBeTruthy();
});
it('should have multiline tick text', function () {
var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'),
tspans = tick.selectAll('tspan'),
expectedTickTexts = ['this is a very long tick text', 'on category axis'];
expect(tspans.size()).toBe(2);
tspans.each(function (d, i) {
var tspan = d3.select(this);
expect(tspan.text()).toBe(expectedTickTexts[i]);
});
});
});
});
describe('axis.x.tick.rotate', function () {
describe('not rotated', function () {
it('should update args successfully', function () {
args = {
data: {
x: 'x',
columns: [
['x', 'category 1', 'category 2', 'category 3', 'category 4', 'category 5', 'category 6'],
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
},
axis: {
x: {
type: 'category',
tick: {
rotate: 60
}
}
}
};
expect(true).toBeTruthy();
});
it('should rotate tick texts', function () {
chart.internal.main.selectAll('.c3-axis-x g.tick').each(function () {
var tick = d3.select(this),
text = tick.select('text'),
tspan = text.select('tspan');
expect(text.attr('transform')).toBe('rotate(60)');
expect(text.attr('y')).toBe('1.5');
expect(tspan.attr('dx')).toBe('6.928203230275509');
});
});
it('should have automatically calculated x axis height', function () {
var box = chart.internal.main.select('.c3-axis-x').node().getBoundingClientRect();
expect(box.height).toBeGreaterThan(50);
});
});
});
describe('axis.x.tick.fit', function () {
describe('axis.x.tick.fit = true', function () {
it('should set args for indexed data', function () {
args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
]
}
};
expect(true).toBeTruthy();
});
it('should show fitted ticks on indexed data', function () {
var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick');
expect(ticks.size()).toBe(6);
});
it('should set args for x-based data', function () {
args = {
data: {
x: 'x',
columns: [
['x', 10, 20, 100, 110, 200, 1000],
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
]
}
};
expect(true).toBeTruthy();
});
it('should show fitted ticks on indexed data', function () {
var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick');
expect(ticks.size()).toBe(6);
});
it('should show fitted ticks after hide and show', function () {
chart.hide();
chart.show();
var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick');
expect(ticks.size()).toBe(6);
});
});
describe('axis.x.tick.fit = false', function () {
it('should set args for indexed data', function () {
args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
]
},
axis: {
x: {
tick: {
fit: false
}
}
}
};
expect(true).toBeTruthy();
});
it('should show fitted ticks on indexed data', function () {
var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick');
expect(ticks.size()).toBe(11);
});
it('should set args for x-based data', function () {
args.data = {
x: 'x',
columns: [
['x', 10, 20, 100, 110, 200, 1000],
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
]
};
expect(true).toBeTruthy();
});
it('should show fitted ticks on indexed data', function () {
var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick');
expect(ticks.size()).toBe(10);
});
it('should show fitted ticks after hide and show', function () {
chart.hide();
chart.show();
var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick');
expect(ticks.size()).toBe(10);
});
});
});
});

42
spec/c3-helper.js

@ -0,0 +1,42 @@
function initDom() {
'use strict';
var div = document.createElement('div');
div.id = 'chart';
div.style.width = '640px';
div.style.height = '480px';
document.body.appendChild(div);
document.body.style.margin = '0px';
}
typeof initDom !== 'undefined';
function setEvent(chart, x, y) {
'use strict';
var paddingLeft = chart.internal.main.node().transform.baseVal.getItem(0).matrix.e,
evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window,
0, 0, 0, x + paddingLeft, y + 5,
false, false, false, false, 0, null);
chart.internal.d3.event = evt;
}
typeof setEvent !== 'undefined';
function initChart(chart, args, done) {
'use strict';
if (typeof chart === 'undefined') {
window.initDom();
}
chart = window.c3.generate(args);
chart.internal.d3.select('.jasmine_html-reporter')
.style('position', 'absolute')
.style('right', 0);
window.setTimeout(function () {
done();
}, 10);
return chart;
}
typeof initChart !== 'undefined';

19
spec/c3-spec.js

@ -1,20 +1,15 @@
var describe = window.describe;
var expect = window.expect;
var it = window.it;
var c3 = window.c3;
var describe = window.describe,
expect = window.expect,
it = window.it;
describe('c3', function () {
'use strict';
it('exists', function () {
var c3 = window.c3;
expect(c3).not.toBe(null);
it('exists', function () {
expect(c3).not.toBeNull();
expect(typeof c3).toBe('object');
});
// ...write other tests here
});

60
spec/class-spec.js

@ -0,0 +1,60 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart class', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2 prefix', 50, 20, 10, 40, 15, 25],
['data3 мужчины', 150, 120, 110, 140, 115, 125]
]
}
};
beforeEach(function (done) {
chart = window.initChart(chart, args, done);
d3 = chart.internal.d3;
});
describe('internal.getTargetSelectorSuffix', function () {
it('should not replace any characters', function () {
var input = 'data1',
expected = '-' + input,
suffix = chart.internal.getTargetSelectorSuffix(input);
expect(suffix).toBe(expected);
});
it('should replace space to "-"', function () {
var input = 'data1 suffix',
expected = '-data1-suffix',
suffix = chart.internal.getTargetSelectorSuffix(input);
expect(suffix).toBe(expected);
});
it('should replace space to "-" with multibyte characters', function () {
var input = 'data1 suffix 日本語',
expected = '-data1-suffix-日本語',
suffix = chart.internal.getTargetSelectorSuffix(input);
expect(suffix).toBe(expected);
});
});
describe('multibyte characters on chart', function () {
it('should replace space to "-" with multibyte characters', function () {
var selector = '.c3-target-data3-мужчины';
expect(chart.internal.main.select(selector).size()).toBe(1);
});
});
});

59
spec/core-spec.js

@ -0,0 +1,59 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
]
}
};
beforeEach(function (done) {
chart = window.initChart(chart, args, done);
d3 = chart.internal.d3;
});
describe('init', function () {
it('should be created', function () {
var svg = d3.select('#chart svg');
expect(svg).not.toBeNull();
});
it('should set 3rd party property to Function', function () {
Function.prototype.$extIsFunction = true;
expect(true).toBeTruthy();
});
it('should be created even if 3rd party property has been set', function () {
var svg = d3.select('#chart svg');
expect(svg).not.toBeNull();
});
});
describe('size', function () {
it('should have same width', function () {
var svg = d3.select('#chart svg');
expect(+svg.attr('width')).toBe(640);
});
it('should have same height', function () {
var svg = d3.select('#chart svg');
expect(+svg.attr('height')).toBe(480);
});
});
});

150
spec/data-spec.js

@ -0,0 +1,150 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart data', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
],
order: function () {
return 0;
}
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
window.initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter').style('display', 'none');
window.setTimeout(function () {
done();
}, 10);
});
describe('function in data.order', function () {
it('should return false in isOrderAsc and isOrderDesc functions', function () {
expect(chart.internal.isOrderAsc() || chart.internal.isOrderDesc()).toBe(false);
});
});
describe('data.xs', function () {
describe('normal x', function () {
it('should have correct number of xs for each', function () {
expect(Object.keys(chart.internal.data.xs).length).toBe(3);
expect(chart.internal.data.xs.data1.length).toBe(6);
expect(chart.internal.data.xs.data2.length).toBe(6);
expect(chart.internal.data.xs.data3.length).toBe(6);
});
it('should have integer index as x', function () {
for (var i = 0; i < chart.internal.data.xs.data3.length; i++) {
expect(chart.internal.data.xs.data1[i]).toBe(i);
expect(chart.internal.data.xs.data2[i]).toBe(i);
expect(chart.internal.data.xs.data3[i]).toBe(i);
}
});
});
describe('timeseries x', function () {
it('should load timeseries data successfully', function () {
args = {
data: {
x : 'date',
columns: [
['date', '2013-01-01', '2013-01-02', '2013-01-03'],
['data1', 30, 200, 100],
['data2', 130, 300, 200]
]
},
axis : {
x : {
type : 'timeseries'
}
}
};
expect(true).toBeTruthy();
});
it('should have correct number of xs', function () {
expect(Object.keys(chart.internal.data.xs).length).toBe(2);
expect(chart.internal.data.xs.data1.length).toBe(3);
expect(chart.internal.data.xs.data2.length).toBe(3);
});
it('should have Date object as x', function () {
var xs = chart.internal.data.xs;
expect(+xs.data1[0]).toBe(+new Date(2013, 0, 1, 0, 0, 0));
expect(+xs.data1[1]).toBe(+new Date(2013, 0, 2, 0, 0, 0));
expect(+xs.data1[2]).toBe(+new Date(2013, 0, 3, 0, 0, 0));
expect(+xs.data2[0]).toBe(+new Date(2013, 0, 1, 0, 0, 0));
expect(+xs.data2[1]).toBe(+new Date(2013, 0, 2, 0, 0, 0));
expect(+xs.data2[2]).toBe(+new Date(2013, 0, 3, 0, 0, 0));
});
});
describe('milliseconds timeseries x', function () {
it('should load timeseries data successfully', function () {
args = {
data: {
x : 'date',
xFormat: '%Y-%m-%d %H:%M:%S.%L',
columns: [
['date', "2014-05-20 17:25:00.123", "2014-05-20 17:30:00.345"],
['data1', 30, 200],
['data2', 130, 300]
]
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d %H:%M:%S.%L',
multiline: false
}
}
}
};
expect(true).toBeTruthy();
});
it('should have correct number of xs', function () {
expect(Object.keys(chart.internal.data.xs).length).toBe(2);
expect(chart.internal.data.xs.data1.length).toBe(2);
expect(chart.internal.data.xs.data2.length).toBe(2);
});
it('should have Date object as x', function () {
var xs = chart.internal.data.xs;
expect(+xs.data1[0]).toBe(+new Date(2014, 4, 20, 17, 25, 0, 123));
expect(+xs.data1[1]).toBe(+new Date(2014, 4, 20, 17, 30, 0, 345));
expect(+xs.data2[0]).toBe(+new Date(2014, 4, 20, 17, 25, 0, 123));
expect(+xs.data2[1]).toBe(+new Date(2014, 4, 20, 17, 30, 0, 345));
});
it('should have milliseconds tick format', function () {
var expected = ["2014-05-20 17:25:00.123", "2014-05-20 17:30:00.345"];
chart.internal.main.selectAll('.c3-axis-x g.tick text').each(function (d, i) {
expect(d3.select(this).text()).toBe(expected[i]);
});
});
});
});
});

96
spec/domain-spec.js

@ -0,0 +1,96 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
var initDom = window.initDom;
describe('c3 chart axis', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
},
axis: {
y: {},
y2: {}
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter').style('display', 'none');
window.setTimeout(function () {
done();
}, 10);
});
describe('axis.y.min', function () {
it('should change axis.y.min to -100', function () {
args.axis.y.min = -100;
expect(true).toBeTruthy();
});
it('should be set properly when smaller than max of data', function () {
var domain = chart.internal.y.domain();
expect(domain[0]).toBe(-150);
expect(domain[1]).toBe(450);
});
it('should change axis.y.min to 500', function () {
args.axis.y.min = 500;
expect(true).toBeTruthy();
});
it('should be set properly when bigger than max of data', function () {
var domain = chart.internal.y.domain();
expect(domain[0]).toBe(499);
expect(domain[1]).toBe(511);
});
it('should change axis.y.min to undefined', function () {
args.axis.y.min = undefined;
expect(true).toBeTruthy();
});
});
describe('axis.y.max', function () {
it('should change axis.y.max to 1000', function () {
args.axis.y.max = 1000;
expect(true).toBeTruthy();
});
it('should be set properly when bigger than min of data', function () {
var domain = chart.internal.y.domain();
expect(domain[0]).toBe(-89);
expect(domain[1]).toBe(1099);
});
it('should change axis.y.max to 0', function () {
args.axis.y.max = 0;
expect(true).toBeTruthy();
});
it('should be set properly when smaller than min of data', function () {
var domain = chart.internal.y.domain();
expect(domain[0]).toBe(-11);
expect(domain[1]).toBe(1);
});
});
});

83
spec/grid-spec.js

@ -0,0 +1,83 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart grid', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250]
]
},
axis: {
y: {
tick: {
}
}
},
grid: {
y: {
show: false
}
}
};
beforeEach(function (done) {
chart = window.initChart(chart, args, done);
d3 = chart.internal.d3;
});
describe('y grid', function () {
it('should not show y grids', function () {
expect(chart.internal.main.select('.c3-ygrids').size()).toBe(0);
});
it('should update args to show y grids', function () {
args.grid.y.show = true;
expect(true).toBeTruthy();
});
it('should show y grids', function () {
var ygrids = chart.internal.main.select('.c3-ygrids');
expect(ygrids.size()).toBe(1);
expect(ygrids.selectAll('.c3-ygrid').size()).toBe(9);
});
it('should update args to show only 3 y grids', function () {
args.grid.y.ticks = 3;
expect(true).toBeTruthy();
});
it('should show only 3 y grids', function () {
var ygrids = chart.internal.main.select('.c3-ygrids');
expect(ygrids.size()).toBe(1);
expect(ygrids.selectAll('.c3-ygrid').size()).toBe(3);
});
it('should update args to show y grids depending on y axis ticks', function () {
args.axis.y.tick.count = 5;
expect(true).toBeTruthy();
});
it('should show grids depending on y axis ticks', function () {
var ygrids = chart.internal.main.select('.c3-ygrids'),
expectedYs = [];
ygrids.selectAll('.c3-ygrid').each(function (d, i) {
expectedYs[i] = +d3.select(this).attr('y1');
});
expect(ygrids.size()).toBe(1);
expect(ygrids.selectAll('.c3-ygrid').size()).toBe(5);
chart.internal.main.select('.c3-axis-y').selectAll('.tick').each(function (d, i) {
var t = d3.transform(d3.select(this).attr('transform'));
expect(t.translate[1]).toBe(expectedYs[i]);
});
});
});
});

140
spec/interaction-spec.js

@ -0,0 +1,140 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart interaction', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
]
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
window.initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter').style('display', 'none');
window.setTimeout(function () {
done();
}, 10);
});
describe('generate event rects', function () {
describe('custom x', function () {
it('should generate bar chart', function () {
args = {
data: {
x: 'x',
columns: [
['x', 0, 1000, 3000, 10000],
['data', 10, 10, 10, 10]
],
type: 'bar'
}
};
expect(true).toBeTruthy();
});
it('should have 4 event rects properly', function () {
var lefts = [77.5, 137, 203.5, 402.5],
widths = [59.5, 66.5, 199, 191.5];
d3.selectAll('.c3-event-rect').each(function (d, i) {
var box = d3.select(this).node().getBoundingClientRect();
expect(box.left).toBe(lefts[i]);
expect(box.width).toBe(widths[i]);
});
});
it('should generate bar chart with only one data', function () {
args = {
data: {
x: 'x',
columns: [
['x', 0],
['data', 10]
],
type: 'bar'
}
};
expect(true).toBeTruthy();
});
it('should have 1 event rects properly', function () {
var eventRects = d3.selectAll('.c3-event-rect');
expect(eventRects.size()).toBe(1);
eventRects.each(function () {
var box = d3.select(this).node().getBoundingClientRect();
expect(box.left).toBe(40.5);
expect(box.width).toBe(590);
});
});
});
describe('timeseries', function () {
it('should generate line chart with timeseries', function () {
args = {
data: {
x: 'x',
columns: [
['x', '20140101', '20140201', '20140210', '20140301'],
['data', 10, 10, 10, 10]
]
}
};
expect(true).toBeTruthy();
});
it('should have 4 event rects properly', function () {
var lefts = [43.5, 191, 349, 494],
widths = [147.5, 158, 145, 134];
d3.selectAll('.c3-event-rect').each(function (d, i) {
var box = d3.select(this).node().getBoundingClientRect();
expect(box.left).toBe(lefts[i]);
expect(box.width).toBe(widths[i]);
});
});
it('should generate line chart with only 1 data timeseries', function () {
args = {
data: {
x: 'x',
columns: [
['x', '20140101'],
['data', 10]
]
}
};
expect(true).toBeTruthy();
});
it('should have 1 event rects properly', function () {
var eventRects = d3.selectAll('.c3-event-rect');
expect(eventRects.size()).toBe(1);
eventRects.each(function () {
var box = d3.select(this).node().getBoundingClientRect();
expect(box.left).toBe(40.5);
expect(box.width).toBe(590);
});
});
});
});
});

89
spec/legend-spec.js

@ -0,0 +1,89 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart legend', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
window.initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter')
.style('position', 'absolute')
.style('right', 0);
window.setTimeout(function () {
done();
}, 10);
});
describe('legend position', function () {
it('should be located on the center of chart', function () {
var box = chart.internal.legend.node().getBoundingClientRect();
expect(box.left + box.right).toBe(640);
});
});
describe('legend as inset', function () {
it('should change the legend to "inset" successfully', function () {
args.legend = {
position: 'inset',
inset: {
step: null
}
};
expect(true).toBeTruthy();
});
it('should be positioned properly', function () {
var box = d3.select('.c3-legend-background').node().getBoundingClientRect();
expect(box.top).toBe(5.5);
expect(box.left).toBe(60.5);
});
it('should have automatically calculated height', function () {
var box = d3.select('.c3-legend-background').node().getBoundingClientRect();
expect(box.height).toBe(48);
});
it('should change the legend step to 1 successfully', function () {
args.legend.inset.step = 1;
expect(true).toBeTruthy();
});
it('should have automatically calculated height', function () {
var box = d3.select('.c3-legend-background').node().getBoundingClientRect();
expect(box.height).toBe(28);
});
it('should change the legend step to 2 successfully', function () {
args.legend.inset.step = 2;
expect(true).toBeTruthy();
});
it('should have automatically calculated height', function () {
var box = d3.select('.c3-legend-background').node().getBoundingClientRect();
expect(box.height).toBe(48);
});
});
});

100
spec/shape.bar-spec.js

@ -0,0 +1,100 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
var initDom = window.initDom,
setEvent = window.setEvent;
describe('c3 chart shape bar', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, -150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', -150, 120, 110, 140, 115, 125]
],
type: 'bar'
},
axis: {
rotated: false
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter').style('display', 'none');
window.setTimeout(function () {
done();
}, 10);
});
describe('internal.isWithinBar', function () {
describe('with normal axis', function () {
it('should not be within bar', function () {
var bar = d3.select('.c3-target-data1 .c3-bar-0').node();
setEvent(chart, 0, 0);
expect(chart.internal.isWithinBar(bar)).toBeFalsy();
});
it('should be within bar', function () {
var bar = d3.select('.c3-target-data1 .c3-bar-0').node();
setEvent(chart, 31, 280);
expect(chart.internal.isWithinBar(bar)).toBeTruthy();
});
it('should not be within bar of negative value', function () {
var bar = d3.select('.c3-target-data3 .c3-bar-0').node();
setEvent(chart, 68, 280);
expect(chart.internal.isWithinBar(bar)).toBeFalsy();
});
it('should be within bar of negative value', function () {
var bar = d3.select('.c3-target-data3 .c3-bar-0').node();
setEvent(chart, 68, 350);
expect(chart.internal.isWithinBar(bar)).toBeTruthy();
});
});
describe('with rotated axis', function () {
it('should change the chart as axis rotated', function () {
args.axis.rotated = true;
expect(true).toBeTruthy();
});
it('should not be within bar', function () {
var bar = d3.select('.c3-target-data1 .c3-bar-0').node();
setEvent(chart, 0, 0);
expect(chart.internal.isWithinBar(bar)).toBeFalsy();
});
it('should be within bar', function () {
var bar = d3.select('.c3-target-data1 .c3-bar-0').node();
setEvent(chart, 190, 20);
expect(chart.internal.isWithinBar(bar)).toBeTruthy();
});
it('should be within bar of negative value', function () {
var bar = d3.select('.c3-target-data3 .c3-bar-0').node();
setEvent(chart, 68, 50);
expect(chart.internal.isWithinBar(bar)).toBeTruthy();
});
});
});
});

60
spec/shape.line-spec.js

@ -0,0 +1,60 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
var initDom = window.initDom;
describe('c3 chart shape line', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, -150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', -150, 120, 110, 140, 115, 125]
],
type: 'line'
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter').style('display', 'none');
window.setTimeout(function () {
done();
}, 10);
});
describe('shape-rendering for line chart', function () {
it("should not have shape-rendering when it's line chart", function () {
d3.selectAll('.c3-line').each(function () {
var style = d3.select(this).style('shape-rendering');
expect(style).toBe('auto');
});
});
it('should chnage to step chart', function () {
args.data.type = 'step';
expect(true).toBeTruthy();
});
it("should have shape-rendering = crispedges when it's step chart", function () {
d3.selectAll('.c3-line').each(function () {
var style = d3.select(this).style('shape-rendering');
expect(style).toBe('crispedges');
});
});
});
});

108
spec/type-spec.js

@ -0,0 +1,108 @@
var describe = window.describe,
expect = window.expect,
it = window.it,
beforeEach = window.beforeEach;
describe('c3 chart types', function () {
'use strict';
var chart, d3;
var args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25],
['data3', 150, 120, 110, 140, 115, 125]
],
type: 'pie'
}
};
beforeEach(function (done) {
if (typeof chart === 'undefined') {
window.initDom();
}
chart = window.c3.generate(args);
d3 = chart.internal.d3;
chart.internal.d3.select('.jasmine_html-reporter').style('display', 'none');
window.setTimeout(function () {
done();
}, 10);
});
describe('internal.hasArcType', function () {
it('should return true', function () {
expect(chart.internal.hasArcType()).toBeTruthy();
});
it('should change chart type to "bar" successfully', function () {
args.data.type = 'bar';
expect(true).toBeTruthy();
});
it('should return false', function () {
expect(chart.internal.hasArcType()).toBeFalsy();
});
});
describe('internal.hasType', function () {
it('should change chart type to "pie" successfully', function () {
args.data.type = 'pie';
expect(true).toBeTruthy();
});
it('should return true for "pie" type', function () {
expect(chart.internal.hasType('pie')).toBeTruthy();
});
it('should return false for "line" type', function () {
expect(chart.internal.hasType('line')).toBeFalsy();
});
it('should return false for "bar" type', function () {
expect(chart.internal.hasType('bar')).toBeFalsy();
});
it('should unload successfully', function () {
chart.unload([]);
expect(true).toBeTruthy();
});
it('should return true for "pie" type even if no data', function () {
expect(chart.internal.hasType('pie')).toBeTruthy();
});
it('should return false for "line" type even if no data', function () {
expect(chart.internal.hasType('line')).toBeFalsy();
});
it('should return false for "bar" type even if no data', function () {
expect(chart.internal.hasType('bar')).toBeFalsy();
});
it('should change chart type to "bar" successfully', function () {
args.data.type = 'bar';
expect(true).toBeTruthy();
});
it('should return false for "pie" type even if no data', function () {
expect(chart.internal.hasType('pie')).toBeFalsy();
});
it('should return false for "line" type even if no data', function () {
expect(chart.internal.hasType('line')).toBeFalsy();
});
it('should return true for "bar" type even if no data', function () {
expect(chart.internal.hasType('bar')).toBeTruthy();
});
});
});

1
src/api.chart.js

@ -15,5 +15,6 @@ c3_chart_fn.destroy = function () {
$$.data.targets = undefined;
$$.data.xs = {};
$$.selectChart.classed('c3', false).html("");
window.clearInterval($$.intervalForObserveInserted);
window.onresize = null;
};

37
src/api.data.js

@ -1,27 +1,22 @@
c3_chart_fn.data = function () {};
c3_chart_fn.data.get = function (targetId) {
var target = this.data.getAsTarget(targetId);
return isDefined(target) ? target.values.map(function (d) { return d.value; }) : undefined;
c3_chart_fn.data = function (targetIds) {
var targets = this.internal.data.targets;
return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) {
return [].concat(targetIds).indexOf(t.id) >= 0;
});
};
c3_chart_fn.data.shown = function (targetId) {
return this.internal.filterTargetsToShow(this.data(targetId));
};
c3_chart_fn.data.getAsTarget = function (targetId) {
var targets = this.data.targets.filter(function (t) { return t.id === targetId; });
return targets.length > 0 ? targets[0] : undefined;
c3_chart_fn.data.values = function (targetId) {
var target = this.data(targetId);
return target ? target.values.map(function (d) { return d.value; }) : null;
};
c3_chart_fn.data.names = function (names) {
var $$ = this.internal, config = $$.config;
if (!arguments.length) { return config.data_names; }
Object.keys(names).forEach(function (id) {
config.data_names[id] = names[id];
});
$$.redraw({withLegend: true});
return config.data_names;
return this.internal.updateDataAttributes('names', names);
};
c3_chart_fn.data.colors = function (colors) {
var $$ = this.internal, config = $$.config;
if (!arguments.length) { return config.data_colors; }
Object.keys(colors).forEach(function (id) {
config.data_colors[id] = colors[id];
});
$$.redraw({withLegend: true});
return config.data_colors;
return this.internal.updateDataAttributes('colors', colors);
};
c3_chart_fn.data.axes = function (axes) {
return this.internal.updateDataAttributes('axes', axes);
};

74
src/api.focus.js

@ -1,50 +1,54 @@
c3_chart_fn.focus = function (targetId) {
var $$ = this.internal,
candidates = $$.svg.selectAll($$.selectorTarget(targetId)),
candidatesForNoneArc = candidates.filter($$.isNoneArc.bind($$)),
candidatesForArc = candidates.filter($$.isArc.bind($$));
function focus(targets) {
$$.filterTargetsToShow(targets).transition().duration(100).style('opacity', 1);
}
c3_chart_fn.focus = function (targetIds) {
var $$ = this.internal, candidates;
targetIds = $$.mapToTargetIds(targetIds);
candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))),
this.revert();
this.defocus();
focus(candidatesForNoneArc.classed(CLASS.focused, true));
focus(candidatesForArc);
candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false);
if ($$.hasArcType()) {
$$.expandArc(targetId, true);
$$.expandArc(targetIds);
}
$$.toggleFocusLegend(targetId, true);
$$.toggleFocusLegend(targetIds, true);
$$.focusedTargetIds = targetIds;
$$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) {
return targetIds.indexOf(id) < 0;
});
};
c3_chart_fn.defocus = function (targetId) {
var $$ = this.internal,
candidates = $$.svg.selectAll($$.selectorTarget(targetId)),
candidatesForNoneArc = candidates.filter($$.isNoneArc.bind($$)),
candidatesForArc = candidates.filter($$.isArc.bind($$));
function defocus(targets) {
$$.filterTargetsToShow(targets).transition().duration(100).style('opacity', 0.3);
}
c3_chart_fn.defocus = function (targetIds) {
var $$ = this.internal, candidates;
targetIds = $$.mapToTargetIds(targetIds);
candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))),
this.revert();
defocus(candidatesForNoneArc.classed(CLASS.focused, false));
defocus(candidatesForArc);
candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true);
if ($$.hasArcType()) {
$$.unexpandArc(targetId);
$$.unexpandArc(targetIds);
}
$$.toggleFocusLegend(targetId, false);
$$.toggleFocusLegend(targetIds, false);
$$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) {
return targetIds.indexOf(id) < 0;
});
$$.defocusedTargetIds = targetIds;
};
c3_chart_fn.revert = function (targetId) {
var $$ = this.internal,
candidates = $$.svg.selectAll($$.selectorTarget(targetId)),
candidatesForNoneArc = candidates.filter($$.isNoneArc.bind($$)),
candidatesForArc = candidates.filter($$.isArc.bind($$));
function revert(targets) {
$$.filterTargetsToShow(targets).transition().duration(100).style('opacity', 1);
}
revert(candidatesForNoneArc.classed(CLASS.focused, false));
revert(candidatesForArc);
c3_chart_fn.revert = function (targetIds) {
var $$ = this.internal, candidates;
targetIds = $$.mapToTargetIds(targetIds);
candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets
candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false);
if ($$.hasArcType()) {
$$.unexpandArc(targetId);
$$.unexpandArc(targetIds);
}
$$.revertLegend();
$$.focusedTargetIds = [];
$$.defocusedTargetIds = [];
};

11
src/api.load.js

@ -14,6 +14,12 @@ c3_chart_fn.load = function (args) {
if ('categories' in args && $$.isCategorized()) {
config.axis_x_categories = args.categories;
}
// update axes if exists
if ('axes' in args) {
Object.keys(args.axes).forEach(function (id) {
config.data_axes[id] = args.axes[id];
});
}
// use cache if exists
if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) {
$$.load($$.getCaches(args.cacheIds), args.done);
@ -33,6 +39,11 @@ c3_chart_fn.load = function (args) {
c3_chart_fn.unload = function (args) {
var $$ = this.internal;
args = args || {};
if (args instanceof Array) {
args = {ids: args};
} else if (typeof args === 'string') {
args = {ids: [args]};
}
$$.unload($$.mapToTargetIds(args.ids), function () {
$$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
if (args.done) { args.done(); }

4
src/api.region.js

@ -27,10 +27,10 @@ c3_chart_fn.regions.remove = function (options) {
config.regions = config.regions.filter(function (region) {
var found = false;
if (!region.class) {
if (!region['class']) {
return true;
}
region.class.split(' ').forEach(function (c) {
region['class'].split(' ').forEach(function (c) {
if (classes.indexOf(c) >= 0) { found = true; }
});
return !found;

24
src/api.show.js

@ -1,13 +1,17 @@
c3_chart_fn.show = function (targetIds, options) {
var $$ = this.internal;
var $$ = this.internal, targets;
targetIds = $$.mapToTargetIds(targetIds);
options = options || {};
$$.removeHiddenTargetIds(targetIds);
$$.svg.selectAll($$.selectorTargets(targetIds))
.transition()
.style('opacity', 1);
targets = $$.svg.selectAll($$.selectorTargets(targetIds));
targets.transition()
.style('opacity', 1, 'important')
.call($$.endall, function () {
targets.style('opacity', null).style('opacity', 1);
});
if (options.withLegend) {
$$.showLegend(targetIds);
@ -17,15 +21,19 @@ c3_chart_fn.show = function (targetIds, options) {
};
c3_chart_fn.hide = function (targetIds, options) {
var $$ = this.internal;
var $$ = this.internal, targets;
targetIds = $$.mapToTargetIds(targetIds);
options = options || {};
$$.addHiddenTargetIds(targetIds);
$$.svg.selectAll($$.selectorTargets(targetIds))
.transition()
.style('opacity', 0);
targets = $$.svg.selectAll($$.selectorTargets(targetIds));
targets.transition()
.style('opacity', 0, 'important')
.call($$.endall, function () {
targets.style('opacity', null).style('opacity', 0);
});
if (options.withLegend) {
$$.hideLegend(targetIds);

11
src/api.zoom.js

@ -1,4 +1,13 @@
c3_chart_fn.zoom = function () {
c3_chart_fn.zoom = function (domain) {
var $$ = this.internal;
if (domain) {
if ($$.isTimeSeries()) {
domain = domain.map(function (x) { return $$.parseDate(x); });
}
$$.brush.extent(domain);
$$.redraw({withUpdateXDomain: true});
}
return $$.brush.extent();
};
c3_chart_fn.zoom.enable = function (enabled) {
var $$ = this.internal;

119
src/arc.js

@ -3,7 +3,7 @@ c3_chart_internal_fn.initPie = function () {
$$.pie = d3.layout.pie().value(function (d) {
return d.values.reduce(function (a, b) { return a + b.value; }, 0);
});
if (!config.data_order || !config.pie_sort || !config.donut_sort) {
if (!config.data_order) {
$$.pie.sort(null);
}
};
@ -26,8 +26,9 @@ c3_chart_internal_fn.updateArc = function () {
c3_chart_internal_fn.updateAngle = function (d) {
var $$ = this, config = $$.config,
found = false, index = 0;
$$.pie($$.filterTargetsToShow($$.data.targets)).sort($$.descByStartAngle).forEach(function (t) {
found = false, index = 0,
gMin = config.gauge_min, gMax = config.gauge_max, gTic, gValue;
$$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) {
if (! found && t.data.id === d.data.id) {
found = true;
d = t;
@ -39,11 +40,10 @@ c3_chart_internal_fn.updateAngle = function (d) {
d.endAngle = d.startAngle;
}
if ($$.isGaugeType(d.data)) {
var gMin = config.gauge_min, gMax = config.gauge_max,
gF = Math.abs(gMin) + gMax,
aTic = (Math.PI) / gF;
d.startAngle = (-1 * (Math.PI / 2)) + (aTic * Math.abs(gMin));
d.endAngle = d.startAngle + (aTic * ((d.value > gMax) ? gMax : d.value));
gTic = (Math.PI) / (gMax - gMin);
gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : (gMax - gMin);
d.startAngle = -1 * (Math.PI / 2);
d.endAngle = d.startAngle + gTic * gValue;
}
return found ? d : null;
};
@ -108,23 +108,38 @@ c3_chart_internal_fn.convertToArcData = function (d) {
c3_chart_internal_fn.textForArcLabel = function (d) {
var $$ = this,
updated, value, ratio, format;
updated, value, ratio, id, format;
if (! $$.shouldShowArcLabel()) { return ""; }
updated = $$.updateAngle(d);
value = updated ? updated.value : null;
ratio = $$.getArcRatio(updated);
id = d.data.id;
if (! $$.hasType('gauge') && ! $$.meetsArcLabelThreshold(ratio)) { return ""; }
format = $$.getArcLabelFormat();
return format ? format(value, ratio) : $$.defaultArcValueFormat(value, ratio);
return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio);
};
c3_chart_internal_fn.expandArc = function (id, withoutFadeOut) {
var $$ = this,
target = $$.svg.selectAll('.' + CLASS.chartArc + $$.selectorTarget(id)),
noneTargets = $$.svg.selectAll('.' + CLASS.arc).filter(function (data) { return data.data.id !== id; });
c3_chart_internal_fn.expandArc = function (targetIds) {
var $$ = this, interval;
// MEMO: avoid to cancel transition
if ($$.transiting) {
interval = window.setInterval(function () {
if (!$$.transiting) {
window.clearInterval(interval);
if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) {
$$.expandArc(targetIds);
}
}
}, 10);
return;
}
if ($$.shouldExpand(id)) {
target.selectAll('path')
targetIds = $$.mapToTargetIds(targetIds);
$$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) {
if (! $$.shouldExpand(d.data.id)) { return; }
$$.d3.select(this).selectAll('path')
.transition().duration(50)
.attr("d", $$.svgArcExpanded)
.transition().duration(100)
@ -134,16 +149,17 @@ c3_chart_internal_fn.expandArc = function (id, withoutFadeOut) {
// callback here
}
});
}
if (!withoutFadeOut) {
noneTargets.style("opacity", 0.3);
}
});
};
c3_chart_internal_fn.unexpandArc = function (id) {
var $$ = this,
target = $$.svg.selectAll('.' + CLASS.chartArc + $$.selectorTarget(id));
target.selectAll('path.' + CLASS.arc)
c3_chart_internal_fn.unexpandArc = function (targetIds) {
var $$ = this;
if ($$.transiting) { return; }
targetIds = $$.mapToTargetIds(targetIds);
$$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).selectAll('path')
.transition().duration(50)
.attr("d", $$.svgArc);
$$.svg.selectAll('.' + CLASS.arc)
@ -188,18 +204,15 @@ c3_chart_internal_fn.getArcTitle = function () {
return $$.hasType('donut') ? $$.config.donut_title : "";
};
c3_chart_internal_fn.descByStartAngle = function (a, b) {
return a.startAngle - b.startAngle;
};
c3_chart_internal_fn.updateTargetsForArc = function (targets) {
var $$ = this, main = $$.main,
mainPieUpdate, mainPieEnter,
classChartArc = $$.classChartArc.bind($$),
classArcs = $$.classArcs.bind($$);
classArcs = $$.classArcs.bind($$),
classFocus = $$.classFocus.bind($$);
mainPieUpdate = main.select('.' + CLASS.chartArcs).selectAll('.' + CLASS.chartArc)
.data($$.pie(targets))
.attr("class", classChartArc);
.attr("class", function (d) { return classChartArc(d) + classFocus(d.data); });
mainPieEnter = mainPieUpdate.enter().append("g")
.attr("class", classChartArc);
mainPieEnter.append('g')
@ -249,6 +262,7 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf
arcData = $$.convertToArcData(updated);
// transitions
$$.expandArc(updated.data.id);
$$.api.focus(updated.data.id);
$$.toggleFocusLegend(updated.data.id, true);
$$.config.data_onmouseover(arcData, this);
})
@ -267,6 +281,7 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf
arcData = $$.convertToArcData(updated);
// transitions
$$.unexpandArc(updated.data.id);
$$.api.revert();
$$.revertLegend();
$$.hideTooltip();
$$.config.data_onmouseout(arcData, this);
@ -298,7 +313,11 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf
}
interpolate = d3.interpolate(this._current, updated);
this._current = interpolate(0);
return function (t) { return $$.getArc(interpolate(t), true); };
return function (t) {
var interpolated = interpolate(t);
interpolated.data = d.data; // data.id will be updated by interporator
return $$.getArc(interpolated, true);
};
})
.attr("transform", withTransform ? "scale(1)" : "")
.style("fill", function (d) {
@ -321,12 +340,8 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf
main.select('.' + CLASS.chartArcsTitle)
.style("opacity", $$.hasType('donut') || $$.hasType('gauge') ? 1 : 0);
};
c3_chart_internal_fn.initGauge = function () {
var $$ = this, config = $$.config, arcs = $$.arcs;
if ($$.hasType('gauge')) {
arcs.append('path')
.attr("class", CLASS.chartArcsBackground)
$$.arcs.select('.' + CLASS.chartArcsBackground)
.attr("d", function () {
var d = {
data: [{value: config.gauge_max}],
@ -335,26 +350,36 @@ c3_chart_internal_fn.initGauge = function () {
};
return $$.getArc(d, true, true);
});
arcs.append("text")
$$.arcs.select('.' + CLASS.chartArcsGaugeUnit)
.attr("dy", ".75em")
.attr("class", CLASS.chartArcsGaugeUnit)
.style("text-anchor", "middle")
.style("pointer-events", "none")
.text(config.gauge_label_show ? config.gauge_units : '');
arcs.append("text")
$$.arcs.select('.' + CLASS.chartArcsGaugeMin)
.attr("dx", -1 * ($$.innerRadius + (($$.radius - $$.innerRadius) / 2)) + "px")
.attr("dy", "1.2em")
.attr("class", CLASS.chartArcsGaugeMin)
.style("text-anchor", "middle")
.style("pointer-events", "none")
.text(config.gauge_label_show ? config.gauge_min : '');
arcs.append("text")
$$.arcs.select('.' + CLASS.chartArcsGaugeMax)
.attr("dx", $$.innerRadius + (($$.radius - $$.innerRadius) / 2) + "px")
.attr("dy", "1.2em")
.text(config.gauge_label_show ? config.gauge_max : '');
}
};
c3_chart_internal_fn.initGauge = function () {
var arcs = this.arcs;
if (this.hasType('gauge')) {
arcs.append('path')
.attr("class", CLASS.chartArcsBackground);
arcs.append("text")
.attr("class", CLASS.chartArcsGaugeUnit)
.style("text-anchor", "middle")
.style("pointer-events", "none");
arcs.append("text")
.attr("class", CLASS.chartArcsGaugeMin)
.style("text-anchor", "middle")
.style("pointer-events", "none");
arcs.append("text")
.attr("class", CLASS.chartArcsGaugeMax)
.style("text-anchor", "middle")
.style("pointer-events", "none")
.text(config.gauge_label_show ? config.gauge_max : '');
.style("pointer-events", "none");
}
};
c3_chart_internal_fn.getGaugeLabelHeight = function () {

71
src/axis.js

@ -32,7 +32,12 @@ c3_chart_internal_fn.initAxis = function () {
};
c3_chart_internal_fn.getXAxis = function (scale, orient, tickFormat, tickValues, withOuterTick) {
var $$ = this, config = $$.config,
axisParams = {isCategory: $$.isCategorized(), withOuterTick: withOuterTick},
axisParams = {
isCategory: $$.isCategorized(),
withOuterTick: withOuterTick,
tickMultiline: config.axis_x_tick_multiline,
tickWidth: config.axis_x_tick_width
},
axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient);
if ($$.isTimeSeries() && tickValues) {
@ -58,9 +63,9 @@ c3_chart_internal_fn.getXAxis = function (scale, orient, tickFormat, tickValues,
return axis;
};
c3_chart_internal_fn.getYAxis = function (scale, orient, tickFormat, ticks, withOuterTick) {
c3_chart_internal_fn.getYAxis = function (scale, orient, tickFormat, tickValues, withOuterTick) {
var axisParams = {withOuterTick: withOuterTick};
return c3_axis(this.d3, axisParams).scale(scale).orient(orient).tickFormat(tickFormat).ticks(ticks);
return c3_axis(this.d3, axisParams).scale(scale).orient(orient).tickFormat(tickFormat).tickValues(tickValues);
};
c3_chart_internal_fn.getAxisId = function (id) {
var config = this.config;
@ -80,6 +85,18 @@ c3_chart_internal_fn.getXAxisTickFormat = function () {
}
return isFunction(format) ? function (v) { return format.call($$, v); } : format;
};
c3_chart_internal_fn.getAxisTickValues = function (tickValues, axis) {
return tickValues ? tickValues : axis ? axis.tickValues() : undefined;
};
c3_chart_internal_fn.getXAxisTickValues = function () {
return this.getAxisTickValues(this.config.axis_x_tick_values, this.xAxis);
};
c3_chart_internal_fn.getYAxisTickValues = function () {
return this.getAxisTickValues(this.config.axis_y_tick_values, this.yAxis);
};
c3_chart_internal_fn.getY2AxisTickValues = function () {
return this.getAxisTickValues(this.config.axis_y2_tick_values, this.y2Axis);
};
c3_chart_internal_fn.getAxisLabelOptionByAxisId = function (axisId) {
var $$ = this, config = $$.config, option;
if (axisId === 'y') {
@ -226,7 +243,7 @@ c3_chart_internal_fn.textAnchorForY2AxisLabel = function () {
};
c3_chart_internal_fn.xForRotatedTickText = function (r) {
return 10 * Math.sin(Math.PI * (r / 180));
return 8 * Math.sin(Math.PI * (r / 180));
};
c3_chart_internal_fn.yForRotatedTickText = function (r) {
return 11.5 - 2.5 * (r / 15) * (r > 0 ? 1 : -1);
@ -236,8 +253,9 @@ c3_chart_internal_fn.rotateTickText = function (axis, transition, rotate) {
.style("text-anchor", rotate > 0 ? "start" : "end");
transition.selectAll('.tick text')
.attr("y", this.yForRotatedTickText(rotate))
.attr("x", this.xForRotatedTickText(rotate))
.attr("transform", "rotate(" + rotate + ")");
.attr("transform", "rotate(" + rotate + ")")
.selectAll('tspan')
.attr('dx', this.xForRotatedTickText(rotate));
};
c3_chart_internal_fn.getMaxTickWidth = function (id) {
@ -247,23 +265,23 @@ c3_chart_internal_fn.getMaxTickWidth = function (id) {
targetsToShow = $$.filterTargetsToShow($$.data.targets);
if (id === 'y') {
scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y'));
axis = $$.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, config.axis_y_ticks);
axis = $$.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.getYAxisTickValues());
} else if (id === 'y2') {
scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2'));
axis = $$.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, config.axis_y2_ticks);
axis = $$.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.getY2AxisTickValues());
} else {
scale = $$.x.copy().domain($$.getXDomain(targetsToShow));
axis = $$.getXAxis(scale, $$.xOrient, $$.getXAxisTickFormat(), config.axis_x_tick_values ? config.axis_x_tick_values : $$.xAxis.tickValues());
axis = $$.getXAxis(scale, $$.xOrient, $$.getXAxisTickFormat(), $$.getXAxisTickValues());
}
$$.d3.select('body').append("g").style('visibility', 'hidden').call(axis).each(function () {
$$.d3.select(this).selectAll('text').each(function () {
$$.d3.select(this).selectAll('text tspan').each(function () {
var box = this.getBoundingClientRect();
if (maxWidth < box.width) { maxWidth = box.width; }
if (box.left > 0 && maxWidth < box.width) { maxWidth = box.width; }
});
}).remove();
}
$$.currentMaxTickWidth = maxWidth <= 0 ? $$.currentMaxTickWidth : maxWidth;
return $$.currentMaxTickWidth;
$$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth;
return $$.currentMaxTickWidths[id];
};
c3_chart_internal_fn.updateAxisLabels = function (withTransition) {
@ -293,31 +311,30 @@ c3_chart_internal_fn.getAxisPadding = function (padding, key, defaultValue, all)
return isValue(padding[key]) ? padding[key] * ratio : defaultValue;
};
c3_chart_internal_fn.generateTickValues = function (xs, tickCount) {
var $$ = this;
var tickValues = xs, targetCount, start, end, count, interval, i, tickValue;
c3_chart_internal_fn.generateTickValues = function (values, tickCount, forTimeSeries) {
var tickValues = values, targetCount, start, end, count, interval, i, tickValue;
if (tickCount) {
targetCount = isFunction(tickCount) ? tickCount() : tickCount;
// compute ticks according to $$.config.axis_x_tick_count
// compute ticks according to tickCount
if (targetCount === 1) {
tickValues = [xs[0]];
tickValues = [values[0]];
} else if (targetCount === 2) {
tickValues = [xs[0], xs[xs.length - 1]];
tickValues = [values[0], values[values.length - 1]];
} else if (targetCount > 2) {
count = targetCount - 2;
start = xs[0];
end = xs[xs.length - 1];
start = values[0];
end = values[values.length - 1];
interval = (end - start) / (count + 1);
// re-construct uniqueXs
// re-construct unique values
tickValues = [start];
for (i = 0; i < count; i++) {
tickValue = +start + interval * (i + 1);
tickValues.push($$.isTimeSeries() ? new Date(tickValue) : tickValue);
tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue);
}
tickValues.push(end);
}
}
if (!$$.isTimeSeries()) { tickValues = tickValues.sort(function (a, b) { return a - b; }); }
if (!forTimeSeries) { tickValues = tickValues.sort(function (a, b) { return a - b; }); }
return tickValues;
};
c3_chart_internal_fn.generateAxisTransitions = function (duration) {
@ -330,7 +347,7 @@ c3_chart_internal_fn.generateAxisTransitions = function (duration) {
};
};
c3_chart_internal_fn.redrawAxis = function (transitions, isHidden) {
var $$ = this;
var $$ = this, config = $$.config;
$$.axes.x.style("opacity", isHidden ? 0 : 1);
$$.axes.y.style("opacity", isHidden ? 0 : 1);
$$.axes.y2.style("opacity", isHidden ? 0 : 1);
@ -339,4 +356,8 @@ c3_chart_internal_fn.redrawAxis = function (transitions, isHidden) {
transitions.axisY.call($$.yAxis);
transitions.axisY2.call($$.y2Axis);
transitions.axisSubX.call($$.subXAxis);
// rotate tick text if needed
if (!config.axis_rotated && config.axis_x_tick_rotate) {
$$.rotateTickText($$.axes.x, transitions.axisX, config.axis_x_tick_rotate);
}
};

114
src/c3.axis.js

@ -48,6 +48,23 @@ function c3_axis(d3, params) {
function textFormatted(v) {
return tickFormat ? tickFormat(v) : v;
}
function getSizeFor1Char(tick) {
var size = {
h: 11.5,
w: 5.5
};
tick.select('text').text(textFormatted).each(function (d) {
var box = this.getBoundingClientRect(),
text = textFormatted(d),
h = box.height,
w = text ? (box.width / text.length) : undefined;
if (h && w) {
size.h = h;
size.w = w;
}
}).text('');
return size;
}
function axis(g) {
g.each(function () {
var g = d3.select(this);
@ -59,7 +76,7 @@ function c3_axis(d3, params) {
// MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks.
tickExit = tick.exit().remove(),
tickUpdate = d3.transition(tick).style("opacity", 1),
tickTransform, tickX;
tickTransform, tickX, tickY;
var range = scale.rangeExtent ? scale.rangeExtent() : scaleExtent(scale.range()),
path = g.selectAll(".domain").data([ 0 ]),
@ -69,31 +86,95 @@ function c3_axis(d3, params) {
var lineEnter = tickEnter.select("line"),
lineUpdate = tickUpdate.select("line"),
text = tick.select("text").text(textFormatted),
textEnter = tickEnter.select("text"),
textUpdate = tickUpdate.select("text");
if (params.isCategory) {
tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2);
tickX = tickCentered ? 0 : tickOffset;
tickY = tickCentered ? tickOffset : 0;
} else {
tickOffset = tickX = 0;
}
var text, tspan, sizeFor1Char = getSizeFor1Char(tick), counts = [];
var tickLength = Math.max(innerTickSize, 0) + tickPadding,
isVertical = orient === 'left' || orient === 'right';
// this should be called only when category axis
function splitTickText(d, maxWidth) {
var tickText = textFormatted(d),
subtext, spaceIndex, textWidth, splitted = [];
if (Object.prototype.toString.call(tickText) === "[object Array]") {
return tickText;
}
if (!maxWidth || maxWidth <= 0) {
maxWidth = isVertical ? 95 : params.isCategory ? (tickOffset * 2 - 10) : 110;
}
function split(splitted, text) {
spaceIndex = undefined;
for (var i = 0; i < text.length; i++) {
if (text.charAt(i) === ' ') {
spaceIndex = i;
}
subtext = text.substr(0, i + 1);
textWidth = sizeFor1Char.w * subtext.length;
// if text width gets over tick width, split by space index or crrent index
if (maxWidth < textWidth) {
return split(
splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)),
text.slice(spaceIndex ? spaceIndex + 1 : i)
);
}
}
return splitted.concat(text);
}
return split(splitted, tickText + "");
}
function tspanDy(d, i) {
var dy = sizeFor1Char.h;
if (i === 0) {
if (orient === 'left' || orient === 'right') {
dy = -((counts[d.index] - 1) * (sizeFor1Char.h / 2) - (params.isCategory ? 2 : 3));
} else {
dy = params.isCategory ? ".40em" : ".71em";
}
}
return dy;
}
function tickSize(d) {
var tickPosition = scale(d) + tickOffset;
return range[0] < tickPosition && tickPosition < range[1] ? innerTickSize : 0;
}
text = tick.select("text");
tspan = text.selectAll('tspan')
.data(function (d, i) {
var splitted = params.tickMultiline ? splitTickText(d, params.tickWidth) : [].concat(textFormatted(d));
counts[i] = splitted.length;
return splitted.map(function (s) {
return { index: i, splitted: s };
});
})
.enter().append('tspan')
.text(function (d) { return d.splitted; });
switch (orient) {
case "bottom":
{
tickTransform = axisX;
lineEnter.attr("y2", innerTickSize);
textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding);
textEnter.attr("y", tickLength);
lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize);
textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding);
text.attr("dy", ".71em").style("text-anchor", "middle");
textUpdate.attr("x", 0).attr("y", tickLength);
text.style("text-anchor", "middle");
tspan.attr('x', 0).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
break;
}
@ -101,10 +182,11 @@ function c3_axis(d3, params) {
{
tickTransform = axisX;
lineEnter.attr("y2", -innerTickSize);
textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
textEnter.attr("y", -tickLength);
lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);
textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
text.attr("dy", "0em").style("text-anchor", "middle");
textUpdate.attr("x", 0).attr("y", -tickLength);
text.style("text-anchor", "middle");
tspan.attr('x', 0).attr("dy", "0em");
pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);
break;
}
@ -112,10 +194,11 @@ function c3_axis(d3, params) {
{
tickTransform = axisY;
lineEnter.attr("x2", -innerTickSize);
textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding));
lineUpdate.attr("x2", -innerTickSize).attr("y2", 0);
textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", tickOffset);
text.attr("dy", ".32em").style("text-anchor", "end");
textEnter.attr("x", -tickLength);
lineUpdate.attr("x2", -innerTickSize).attr("y1", tickY).attr("y2", tickY);
textUpdate.attr("x", -tickLength).attr("y", tickOffset);
text.style("text-anchor", "end");
tspan.attr('x', -tickLength).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);
break;
}
@ -123,10 +206,11 @@ function c3_axis(d3, params) {
{
tickTransform = axisY;
lineEnter.attr("x2", innerTickSize);
textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding);
textEnter.attr("x", tickLength);
lineUpdate.attr("x2", innerTickSize).attr("y2", 0);
textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0);
text.attr("dy", ".32em").style("text-anchor", "start");
textUpdate.attr("x", tickLength).attr("y", 0);
text.style("text-anchor", "start");
tspan.attr('x', tickLength).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);
break;
}

24
src/class.js

@ -23,8 +23,10 @@ var CLASS = c3_chart_internal_fn.CLASS = {
zoomRect: 'c3-zoom-rect',
brush: 'c3-brush',
focused: 'c3-focused',
defocused: 'c3-defocused',
region: 'c3-region',
regions: 'c3-regions',
tooltipContainer: 'c3-tooltip-container',
tooltip: 'c3-tooltip',
tooltipName: 'c3-tooltip-name',
shape: 'c3-shape',
@ -118,7 +120,7 @@ c3_chart_internal_fn.classAreas = function (d) {
return this.classShapes(d) + this.generateClass(CLASS.areas, d.id);
};
c3_chart_internal_fn.classRegion = function (d, i) {
return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d.class : '');
return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '');
};
c3_chart_internal_fn.classEvent = function (d) {
return this.generateClass(CLASS.eventRect, d.index);
@ -131,6 +133,15 @@ c3_chart_internal_fn.classTarget = function (id) {
}
return $$.generateClass(CLASS.target, id) + additionalClass;
};
c3_chart_internal_fn.classFocus = function (d) {
return this.classFocused(d) + this.classDefocused(d);
};
c3_chart_internal_fn.classFocused = function (d) {
return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : '');
};
c3_chart_internal_fn.classDefocused = function (d) {
return ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '');
};
c3_chart_internal_fn.classChartText = function (d) {
return CLASS.chartText + this.classTarget(d.id);
};
@ -144,14 +155,15 @@ c3_chart_internal_fn.classChartArc = function (d) {
return CLASS.chartArc + this.classTarget(d.data.id);
};
c3_chart_internal_fn.getTargetSelectorSuffix = function (targetId) {
return targetId || targetId === 0 ? '-' + (targetId.replace ? targetId.replace(/([^a-zA-Z0-9-_])/g, '-') : targetId) : '';
return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : '';
};
c3_chart_internal_fn.selectorTarget = function (id) {
return '.' + CLASS.target + this.getTargetSelectorSuffix(id);
c3_chart_internal_fn.selectorTarget = function (id, prefix) {
return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id);
};
c3_chart_internal_fn.selectorTargets = function (ids) {
c3_chart_internal_fn.selectorTargets = function (ids, prefix) {
var $$ = this;
return ids.length ? ids.map(function (id) { return $$.selectorTarget(id); }) : null;
ids = ids || [];
return ids.length ? ids.map(function (id) { return $$.selectorTarget(id, prefix); }) : null;
};
c3_chart_internal_fn.selectorLegend = function (id) {
return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id);

17
src/clip.js

@ -2,12 +2,16 @@ c3_chart_internal_fn.getClipPath = function (id) {
var isIE9 = window.navigator.appVersion.toLowerCase().indexOf("msie 9.") >= 0;
return "url(" + (isIE9 ? "" : document.URL.split('#')[0]) + "#" + id + ")";
};
c3_chart_internal_fn.appendClip = function (parent, id) {
return parent.append("clipPath").attr("id", id).append("rect");
};
c3_chart_internal_fn.getAxisClipX = function (forHorizontal) {
// axis line width + padding for left
return forHorizontal ? -(1 + 30) : -(this.margin.left - 1);
var left = Math.max(30, this.margin.left);
return forHorizontal ? -(1 + left) : -(left - 1);
};
c3_chart_internal_fn.getAxisClipY = function (forHorizontal) {
return forHorizontal ? -20 : -4;
return forHorizontal ? -10 : -4;
};
c3_chart_internal_fn.getXAxisClipX = function () {
var $$ = this;
@ -26,13 +30,14 @@ c3_chart_internal_fn.getYAxisClipY = function () {
return $$.getAxisClipY($$.config.axis_rotated);
};
c3_chart_internal_fn.getAxisClipWidth = function (forHorizontal) {
var $$ = this;
var $$ = this,
left = Math.max(30, $$.margin.left),
right = Math.max(30, $$.margin.right);
// width + axis line width + padding for left/right
return forHorizontal ? $$.width + 2 + 30 + 30 : $$.margin.left + 20;
return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20;
};
c3_chart_internal_fn.getAxisClipHeight = function (forHorizontal) {
var $$ = this, config = $$.config;
return forHorizontal ? (config.axis_x_height ? config.axis_x_height : 0) + 80 : $$.height + 8;
return (forHorizontal ? this.margin.bottom : this.height) + 8;
};
c3_chart_internal_fn.getXAxisClipWidth = function () {
var $$ = this;

17
src/config.js

@ -10,7 +10,10 @@ c3_chart_internal_fn.getDefaultConfig = function () {
zoom_enabled: false,
zoom_extent: undefined,
zoom_privileged: false,
zoom_rescale: false,
zoom_onzoom: function () {},
zoom_onzoomstart: function () {},
zoom_onzoomend: function () {},
interaction_enabled: true,
onmouseover: function () {},
onmouseout: function () {},
@ -87,8 +90,10 @@ c3_chart_internal_fn.getDefaultConfig = function () {
axis_x_tick_count: undefined,
axis_x_tick_fit: true,
axis_x_tick_values: null,
axis_x_tick_rotate: undefined,
axis_x_tick_rotate: 0,
axis_x_tick_outer: true,
axis_x_tick_multiline: true,
axis_x_tick_width: null,
axis_x_max: undefined,
axis_x_min: undefined,
axis_x_padding: {},
@ -102,8 +107,9 @@ c3_chart_internal_fn.getDefaultConfig = function () {
axis_y_label: {},
axis_y_tick_format: undefined,
axis_y_tick_outer: true,
axis_y_tick_values: null,
axis_y_tick_count: undefined,
axis_y_padding: {},
axis_y_ticks: 10,
axis_y_default: undefined,
axis_y2_show: false,
axis_y2_max: undefined,
@ -112,8 +118,9 @@ c3_chart_internal_fn.getDefaultConfig = function () {
axis_y2_label: {},
axis_y2_tick_format: undefined,
axis_y2_tick_outer: true,
axis_y2_tick_values: null,
axis_y2_tick_count: undefined,
axis_y2_padding: {},
axis_y2_ticks: 10,
axis_y2_default: undefined,
// grid
grid_x_show: false,
@ -132,7 +139,9 @@ c3_chart_internal_fn.getDefaultConfig = function () {
point_focus_expand_enabled: true,
point_focus_expand_r: undefined,
point_select_r: undefined,
// line
line_connectNull: false,
line_step_type: 'step',
// bar
bar_width: undefined,
bar_width_ratio: 0.6,
@ -144,7 +153,6 @@ c3_chart_internal_fn.getDefaultConfig = function () {
pie_label_show: true,
pie_label_format: undefined,
pie_label_threshold: 0.05,
pie_sort: true,
pie_expand: true,
// gauge
gauge_label_show: true,
@ -159,7 +167,6 @@ c3_chart_internal_fn.getDefaultConfig = function () {
donut_label_format: undefined,
donut_label_threshold: 0.05,
donut_width: undefined,
donut_sort: true,
donut_expand: true,
donut_title: "",
// region - region to change style

91
src/core.js

@ -9,12 +9,12 @@ function Chart(config) {
// bind "this" to nested API
(function bindThis(fn, target, argThis) {
for (var key in fn) {
Object.keys(fn).forEach(function (key) {
target[key] = fn[key].bind(argThis);
if (Object.keys(fn[key]).length > 0) {
bindThis(fn[key], target[key], argThis);
}
}
});
})(c3_chart_fn, this, this);
}
@ -71,9 +71,13 @@ c3_chart_internal_fn.initParams = function () {
$$.clipId = "c3-" + (+new Date()) + '-clip',
$$.clipIdForXAxis = $$.clipId + '-xaxis',
$$.clipIdForYAxis = $$.clipId + '-yaxis',
$$.clipIdForGrid = $$.clipId + '-grid',
$$.clipIdForSubchart = $$.clipId + '-subchart',
$$.clipPath = $$.getClipPath($$.clipId),
$$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis),
$$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis);
$$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid),
$$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart),
$$.dragStart = null;
$$.dragging = false;
@ -100,6 +104,8 @@ c3_chart_internal_fn.initParams = function () {
$$.hiddenTargetIds = [];
$$.hiddenLegendIds = [];
$$.focusedTargetIds = [];
$$.defocusedTargetIds = [];
$$.xOrient = config.axis_rotated ? "left" : "bottom";
$$.yOrient = config.axis_rotated ? "bottom" : "left";
@ -115,7 +121,11 @@ c3_chart_internal_fn.initParams = function () {
$$.legendItemHeight = 0;
$$.legendOpacityForHidden = 0.15;
$$.currentMaxTickWidth = 0;
$$.currentMaxTickWidths = {
x: 0,
y: 0,
y2: 0
};
$$.rotated_padding_left = 30;
$$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30;
@ -123,12 +133,14 @@ c3_chart_internal_fn.initParams = function () {
$$.withoutFadeIn = {};
$$.intervalForObserveInserted = undefined;
$$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js
};
c3_chart_internal_fn.initWithData = function (data) {
var $$ = this, d3 = $$.d3, config = $$.config;
var main, binding = true;
var defs, main, binding = true;
if ($$.initPie) { $$.initPie(); }
if ($$.initBrush) { $$.initBrush(); }
@ -188,10 +200,12 @@ c3_chart_internal_fn.initWithData = function (data) {
.on('mouseleave', function () { return config.onmouseout.call($$); });
// Define defs
$$.defs = $$.svg.append("defs");
$$.defs.append("clipPath").attr("id", $$.clipId).append("rect");
$$.defs.append("clipPath").attr("id", $$.clipIdForXAxis).append("rect");
$$.defs.append("clipPath").attr("id", $$.clipIdForYAxis).append("rect");
defs = $$.svg.append("defs");
$$.clipChart = $$.appendClip(defs, $$.clipId);
$$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis);
$$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis);
$$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid);
$$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart);
$$.updateSvgSize();
// Define regions
@ -408,12 +422,12 @@ c3_chart_internal_fn.updateTargets = function (targets) {
c3_chart_internal_fn.redraw = function (options, transitions) {
var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config;
var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType);
var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend;
var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect;
var hideAxis = $$.hasArcType();
var drawArea, drawBar, drawLine, xForText, yForText;
var duration, durationForExit, durationForAxis;
var waitForDraw, flow;
var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling;
var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom;
var xv = $$.xv.bind($$),
cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$),
cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$);
@ -427,6 +441,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", false);
withTrimXDomain = getOption(options, "withTrimXDomain", true);
withLegend = getOption(options, "withLegend", false);
withEventRect = getOption(options, "withEventRect", true);
withTransitionForExit = getOption(options, "withTransitionForExit", withTransition);
withTransitionForAxis = getOption(options, "withTransitionForAxis", withTransition);
@ -448,9 +463,12 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
if (targetsToShow.length) {
$$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain);
// update axis tick values according to options
if (!config.axis_x_tick_values && (config.axis_x_tick_fit || config.axis_x_tick_count)) {
tickValues = $$.generateTickValues($$.mapTargetsToUniqueXs(targetsToShow), config.axis_x_tick_count);
if (!config.axis_x_tick_values) {
if (config.axis_x_tick_fit || config.axis_x_tick_count) {
tickValues = $$.generateTickValues($$.mapTargetsToUniqueXs(targetsToShow), config.axis_x_tick_count, $$.isTimeSeries());
} else {
tickValues = undefined;
}
$$.xAxis.tickValues(tickValues);
$$.subXAxis.tickValues(tickValues);
}
@ -459,8 +477,19 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
$$.subXAxis.tickValues([]);
}
$$.y.domain($$.getYDomain(targetsToShow, 'y'));
$$.y2.domain($$.getYDomain(targetsToShow, 'y2'));
if (withY) {
xDomainForZoom = $$.x.orgDomain();
}
$$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom));
$$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom));
if (!config.axis_y_tick_values && config.axis_y_tick_count) {
$$.yAxis.tickValues($$.generateTickValues($$.y.domain(), config.axis_y_tick_count));
}
if (!config.axis_y2_tick_values && config.axis_y2_tick_count) {
$$.y2Axis.tickValues($$.generateTickValues($$.y2.domain(), config.axis_y2_tick_count));
}
// axes
$$.redrawAxis(transitions, hideAxis);
@ -488,11 +517,6 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
}
}
// rotate tick text if needed
if (!config.axis_rotated && config.axis_x_tick_rotate) {
$$.rotateTickText($$.axes.x, transitions.axisX, config.axis_x_tick_rotate);
}
// setup drawer - MEMO: these must be called after axis updated
drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined;
drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined;
@ -530,7 +554,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
// lines, areas and cricles
$$.redrawLine(durationForExit);
$$.redrawArea(durationForExit);
if (config.point_show) { $$.redrawCircle(); }
$$.redrawCircle();
// text
if ($$.hasDataLabel()) {
@ -552,8 +576,9 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
.remove();
// event rects will redrawn when flow called
if (config.interaction_enabled && !options.flow) {
if (config.interaction_enabled && !options.flow && withEventRect) {
$$.redrawEventRect();
if ($$.updateZoom) { $$.updateZoom(); }
}
// transition should be derived from one transition
@ -563,7 +588,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
$$.addTransitionForBar(transitions, drawBar);
$$.addTransitionForLine(transitions, drawLine);
$$.addTransitionForArea(transitions, drawArea);
if (config.point_show) { $$.addTransitionForCircle(transitions, cx, cy); }
$$.addTransitionForCircle(transitions, cx, cy);
$$.addTransitionForText(transitions, xForText, yForText, options.flow);
$$.addTransitionForRegion(transitions);
$$.addTransitionForGrid(transitions);
@ -595,8 +620,6 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
$$.mapToIds($$.data.targets).forEach(function (id) {
$$.withoutFadeIn[id] = true;
});
if ($$.updateZoom) { $$.updateZoom(); }
};
c3_chart_internal_fn.updateAndRedraw = function (options) {
@ -669,9 +692,11 @@ c3_chart_internal_fn.getTranslate = function (target) {
c3_chart_internal_fn.initialOpacity = function (d) {
return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0;
};
c3_chart_internal_fn.initialOpacityForCircle = function (d) {
return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0;
};
c3_chart_internal_fn.opacityForCircle = function (d) {
var $$ = this;
return isValue(d.value) ? $$.isScatterType(d) ? 0.5 : 1 : 0;
return isValue(d.value) && this.config.point_show ? (this.isScatterType(d) ? 0.5 : 1) : 0;
};
c3_chart_internal_fn.opacityForText = function () {
return this.hasDataLabel() ? 1 : 0;
@ -727,9 +752,10 @@ c3_chart_internal_fn.transformAll = function (withTransition, transitions) {
};
c3_chart_internal_fn.updateSvgSize = function () {
var $$ = this;
var $$ = this,
brush = $$.svg.select(".c3-brush .background");
$$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight);
$$.svg.select('#' + $$.clipId).select('rect')
$$.svg.selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]).select('rect')
.attr('width', $$.width)
.attr('height', $$.height);
$$.svg.select('#' + $$.clipIdForXAxis).select('rect')
@ -742,6 +768,9 @@ c3_chart_internal_fn.updateSvgSize = function () {
.attr('y', $$.getYAxisClipY.bind($$))
.attr('width', $$.getYAxisClipWidth.bind($$))
.attr('height', $$.getYAxisClipHeight.bind($$));
$$.svg.select('#' + $$.clipIdForSubchart).select('rect')
.attr('width', $$.width)
.attr('height', brush.size() ? brush.attr('height') : 0);
$$.svg.select('.' + CLASS.zoomRect)
.attr('width', $$.width)
.attr('height', $$.height);
@ -771,10 +800,10 @@ c3_chart_internal_fn.observeInserted = function (selection) {
if (mutation.type === 'childList' && mutation.previousSibling) {
observer.disconnect();
// need to wait for completion of load because size calculation requires the actual sizes determined after that completion
var interval = window.setInterval(function () {
$$.intervalForObserveInserted = window.setInterval(function () {
// parentNode will NOT be null when completed
if (selection.node().parentNode) {
window.clearInterval(interval);
window.clearInterval($$.intervalForObserveInserted);
$$.updateDimension();
$$.config.oninit.call($$);
$$.redraw({

14
src/data.convert.js

@ -4,24 +4,32 @@ c3_chart_internal_fn.convertUrlToData = function (url, mimeType, keys, done) {
var d;
if (type === 'json') {
d = $$.convertJsonToData(JSON.parse(data.response), keys);
} else if (type === 'tsv') {
d = $$.convertTsvToData(data.response);
} else {
d = $$.convertCsvToData(data.response);
}
done.call($$, d);
});
};
c3_chart_internal_fn.convertCsvToData = function (csv) {
var d3 = this.d3, rows = d3.csv.parseRows(csv), d;
c3_chart_internal_fn.convertXsvToData = function (xsv, parser) {
var rows = parser.parseRows(xsv), d;
if (rows.length === 1) {
d = [{}];
rows[0].forEach(function (id) {
d[0][id] = null;
});
} else {
d = d3.csv.parse(csv);
d = parser.parse(xsv);
}
return d;
};
c3_chart_internal_fn.convertCsvToData = function (csv) {
return this.convertXsvToData(csv, this.d3.csv);
};
c3_chart_internal_fn.convertTsvToData = function (tsv) {
return this.convertXsvToData(tsv, this.d3.tsv);
};
c3_chart_internal_fn.convertJsonToData = function (json, keys) {
var $$ = this,
new_rows = [], targetKeys, data;

69
src/data.js

@ -161,8 +161,8 @@ c3_chart_internal_fn.filterTargetsToShow = function (targets) {
};
c3_chart_internal_fn.mapTargetsToUniqueXs = function (targets) {
var $$ = this;
var xs = $$.d3.set($$.d3.merge(targets.map(function (t) { return t.values.map(function (v) { return v.x; }); }))).values();
return $$.isTimeSeries() ? xs.map(function (x) { return new Date(x); }) : xs.map(function (x) { return +x; });
var xs = $$.d3.set($$.d3.merge(targets.map(function (t) { return t.values.map(function (v) { return +v.x; }); }))).values();
return $$.isTimeSeries() ? xs.map(function (x) { return new Date(+x); }) : xs.map(function (x) { return +x; });
};
c3_chart_internal_fn.addHiddenTargetIds = function (targetIds) {
this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds);
@ -206,11 +206,11 @@ c3_chart_internal_fn.hasPositiveValueInTargets = function (targets) {
};
c3_chart_internal_fn.isOrderDesc = function () {
var config = this.config;
return config.data_order && config.data_order.toLowerCase() === 'desc';
return typeof(config.data_order) === 'string' && config.data_order.toLowerCase() === 'desc';
};
c3_chart_internal_fn.isOrderAsc = function () {
var config = this.config;
return config.data_order && config.data_order.toLowerCase() === 'asc';
return typeof(config.data_order) === 'string' && config.data_order.toLowerCase() === 'asc';
};
c3_chart_internal_fn.orderTargets = function (targets) {
var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc();
@ -232,6 +232,17 @@ c3_chart_internal_fn.filterByX = function (targets, x) {
c3_chart_internal_fn.filterRemoveNull = function (data) {
return data.filter(function (d) { return isValue(d.value); });
};
c3_chart_internal_fn.filterByXDomain = function (targets, xDomain) {
return targets.map(function (t) {
return {
id: t.id,
id_org: t.id_org,
values: t.values.filter(function (v) {
return xDomain[0] <= v.x && v.x <= xDomain[1];
})
};
});
};
c3_chart_internal_fn.hasDataLabel = function () {
var config = this.config;
if (typeof config.data_labels === 'boolean' && config.data_labels) {
@ -285,14 +296,25 @@ c3_chart_internal_fn.findClosestFromTargets = function (targets, pos) {
return $$.findClosest(candidates, pos);
};
c3_chart_internal_fn.findClosest = function (values, pos) {
var $$ = this, minDist, closest;
values.forEach(function (v) {
var $$ = this, minDist = 100, closest;
// find mouseovering bar
values.filter(function (v) { return v && $$.isBarType(v.id); }).forEach(function (v) {
var shape = $$.d3.select('.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index).node();
if ($$.isWithinBar(shape)) {
closest = v;
}
});
// find closest point from non-bar
values.filter(function (v) { return v && !$$.isBarType(v.id); }).forEach(function (v) {
var d = $$.dist(v, pos);
if (d < minDist || ! minDist) {
if (d < minDist) {
minDist = d;
closest = v;
}
});
return closest;
};
c3_chart_internal_fn.dist = function (data, pos) {
@ -302,3 +324,36 @@ c3_chart_internal_fn.dist = function (data, pos) {
yIndex = config.axis_rotated ? 0 : 1;
return Math.pow($$.x(data.x) - pos[xIndex], 2) + Math.pow(yScale(data.value) - pos[yIndex], 2);
};
c3_chart_internal_fn.convertValuesToStep = function (values) {
var converted = [].concat(values), i;
if (!this.isCategorized()) {
return values;
}
for (i = values.length + 1; 0 < i; i--) {
converted[i] = converted[i - 1];
}
converted[0] = {
x: converted[0].x - 1,
value: converted[0].value,
id: converted[0].id
};
converted[values.length + 1] = {
x: converted[values.length].x + 1,
value: converted[values.length].value,
id: converted[values.length].id
};
return converted;
};
c3_chart_internal_fn.updateDataAttributes = function (name, attrs) {
var $$ = this, config = $$.config, current = config['data_' + name];
if (typeof attrs === 'undefined') { return current; }
Object.keys(attrs).forEach(function (id) {
current[id] = attrs[id];
});
$$.redraw({withLegend: true});
return current;
};

31
src/domain.js

@ -60,9 +60,10 @@ c3_chart_internal_fn.getYDomainMax = function (targets) {
}
return $$.d3.max(Object.keys(ys).map(function (key) { return $$.d3.max(ys[key]); }));
};
c3_chart_internal_fn.getYDomain = function (targets, axisId) {
c3_chart_internal_fn.getYDomain = function (targets, axisId, xDomain) {
var $$ = this, config = $$.config,
yTargets = targets.filter(function (d) { return $$.getAxisId(d.id) === axisId; }),
targetsByAxisId = targets.filter(function (t) { return $$.getAxisId(t.id) === axisId; }),
yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId,
yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min,
yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max,
yDomainMin = isValue(yMin) ? yMin : $$.getYDomainMin(yTargets),
@ -73,6 +74,15 @@ c3_chart_internal_fn.getYDomain = function (targets, axisId) {
isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased),
showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated,
showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated;
if (yDomainMax < yDomainMin) {
if (isValue(yMin)) {
yDomainMax = yDomainMin + 10; // TODO: introduce axis.y.maxMin
} else {
yDomainMin = yDomainMax - 10; // TODO: introduce axis.y.minMax
}
}
if (yTargets.length === 0) { // use current domain if target of axisId is none
return axisId === 'y2' ? $$.y2.domain() : $$.y.domain();
}
@ -146,13 +156,13 @@ c3_chart_internal_fn.getXDomainMax = function (targets) {
($$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max) :
$$.d3.max(targets, function (t) { return $$.d3.max(t.values, function (v) { return v.x; }); });
};
c3_chart_internal_fn.getXDomainPadding = function (targets) {
c3_chart_internal_fn.getXDomainPadding = function (domain) {
var $$ = this, config = $$.config,
edgeX = this.getEdgeX(targets), diff = edgeX[1] - edgeX[0],
diff = domain[1] - domain[0],
maxDataCount, padding, paddingLeft, paddingRight;
if ($$.isCategorized()) {
padding = 0;
} else if ($$.hasType('bar', targets)) {
} else if ($$.hasType('bar')) {
maxDataCount = $$.getMaxDataCount();
padding = maxDataCount > 1 ? (diff / (maxDataCount - 1)) / 2 : 0.5;
} else {
@ -172,12 +182,17 @@ c3_chart_internal_fn.getXDomain = function (targets) {
var $$ = this,
xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)],
firstX = xDomain[0], lastX = xDomain[1],
padding = $$.getXDomainPadding(targets),
padding = $$.getXDomainPadding(xDomain),
min = 0, max = 0;
// show center of x domain if min and max are the same
if ((firstX - lastX) === 0 && !$$.isCategorized()) {
firstX = $$.isTimeSeries() ? new Date(firstX.getTime() * 0.5) : -0.5;
lastX = $$.isTimeSeries() ? new Date(lastX.getTime() * 1.5) : 0.5;
if ($$.isTimeSeries()) {
firstX = new Date(firstX.getTime() * 0.5);
lastX = new Date(lastX.getTime() * 1.5);
} else {
firstX = firstX === 0 ? 1 : (firstX * 0.5);
lastX = lastX === 0 ? -1 : (lastX * 1.5);
}
}
if (firstX || firstX === 0) {
min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left;

15
src/grid.js

@ -1,7 +1,7 @@
c3_chart_internal_fn.initGrid = function () {
var $$ = this, config = $$.config, d3 = $$.d3;
$$.grid = $$.main.append('g')
.attr("clip-path", $$.clipPath)
.attr("clip-path", $$.clipPathForGrid)
.attr('class', CLASS.grid);
if (config.grid_x_show) {
$$.grid.append("g").attr("class", CLASS.xgrids);
@ -21,7 +21,7 @@ c3_chart_internal_fn.initGrid = function () {
c3_chart_internal_fn.initGridLines = function () {
var $$ = this, d3 = $$.d3;
$$.gridLines = $$.main.append('g')
.attr("clip-path", $$.clipPath)
.attr("clip-path", $$.clipPathForGrid)
.attr('class', CLASS.grid + ' ' + CLASS.gridLines);
$$.gridLines.append('g').attr("class", CLASS.xgridLines);
$$.gridLines.append('g').attr('class', CLASS.ygridLines);
@ -55,9 +55,10 @@ c3_chart_internal_fn.updateXGrid = function (withoutUpdate) {
};
c3_chart_internal_fn.updateYGrid = function () {
var $$ = this, config = $$.config;
var $$ = this, config = $$.config,
gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks);
$$.ygrid = $$.main.select('.' + CLASS.ygrids).selectAll('.' + CLASS.ygrid)
.data($$.y.ticks(config.grid_y_ticks));
.data(gridValues);
$$.ygrid.enter().append('line')
.attr('class', CLASS.ygrid);
$$.ygrid.attr("x1", config.axis_rotated ? $$.y : 0)
@ -84,7 +85,7 @@ c3_chart_internal_fn.redrawGrid = function (duration, withY) {
.data(config.grid_x_lines);
// enter
xgridLine = $$.xgridLines.enter().append('g')
.attr("class", function (d) { return CLASS.xgridLine + (d.class ? ' ' + d.class : ''); });
.attr("class", function (d) { return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : ''); });
xgridLine.append('line')
.style("opacity", 0);
xgridLine.append('text')
@ -109,7 +110,7 @@ c3_chart_internal_fn.redrawGrid = function (duration, withY) {
.data(config.grid_y_lines);
// enter
ygridLine = $$.ygridLines.enter().append('g')
.attr("class", function (d) { return CLASS.ygridLine + (d.class ? ' ' + d.class : ''); });
.attr("class", function (d) { return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : ''); });
ygridLine.append('line')
.style("opacity", 0);
ygridLine.append('text')
@ -202,7 +203,7 @@ c3_chart_internal_fn.getGridFilterToRemove = function (params) {
return params ? function (line) {
var found = false;
[].concat(params).forEach(function (param) {
if ((('value' in param && line.value === params.value) || ('class' in param && line.class === params.class))) {
if ((('value' in param && line.value === params.value) || ('class' in param && line['class'] === params['class']))) {
found = true;
}
});

78
src/interaction.js

@ -60,27 +60,30 @@ c3_chart_internal_fn.updateEventRect = function (eventRectUpdate) {
else {
if (($$.isCustomX() || $$.isTimeSeries()) && !$$.isCategorized()) {
rectW = function (d) {
var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index), dx = $$.data.xs[d.id][d.index],
w = ($$.x(nextX ? nextX : dx) - $$.x(prevX ? prevX : dx)) / 2;
var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index);
// if there this is a single data point make the eventRect full width (or height)
if (prevX === null && nextX === null) {
return config.axis_rotated ? $$.height : $$.width;
}
else {
return w < 0 ? 0 : w;
}
if (prevX === null) { prevX = $$.x.domain()[0]; }
if (nextX === null) { nextX = $$.x.domain()[1]; }
return Math.max(0, ($$.x(nextX) - $$.x(prevX)) / 2);
};
rectX = function (d) {
var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index), dx = $$.data.xs[d.id][d.index];
var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index),
thisX = $$.data.xs[d.id][d.index];
// if there this is a single data point position the eventRect at 0
if (prevX === null && nextX === null) {
return 0;
}
else {
return ($$.x(dx) + $$.x(prevX ? prevX : dx)) / 2;
}
if (prevX === null) { prevX = $$.x.domain()[0]; }
return ($$.x(thisX) + $$.x(prevX)) / 2;
};
} else {
rectW = $$.getEventRectWidth();
@ -158,7 +161,7 @@ c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) {
if ($$.dragging || $$.flowing) { return; } // do nothing while dragging/flowing
if ($$.hasArcType()) { return; }
if ($$.isStepType(d) && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) {
if ($$.isStepType(d) && $$.config.line_step_type === 'step-after' && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) {
index -= 1;
}
@ -192,12 +195,7 @@ c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) {
}
})
.filter(function (d) {
if (this.nodeName === 'circle') {
return $$.isWithinCircle(this, $$.pointSelectR(d));
}
else if (this.nodeName === 'path') {
return $$.isWithinBar(this);
}
return $$.isWithinShape(this, d);
})
.each(function (d) {
if (config.data_selection_enabled && (config.data_selection_grouped || config.data_selection_isselectable(d))) {
@ -218,7 +216,7 @@ c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) {
$$.cancelClick = false;
return;
}
if ($$.isStepType(d) && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) {
if ($$.isStepType(d) && config.line_step_type === 'step-after' && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) {
index -= 1;
}
$$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) {
@ -233,12 +231,20 @@ c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) {
.on('drag', function () { $$.drag(d3.mouse(this)); })
.on('dragstart', function () { $$.dragstart(d3.mouse(this)); })
.on('dragend', function () { $$.dragend(); })
)
.on("dblclick.zoom", null);
);
};
c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter) {
var $$ = this, d3 = $$.d3, config = $$.config;
function mouseout() {
$$.svg.select('.' + CLASS.eventRect).style('cursor', null);
$$.hideXGridFocus();
$$.hideTooltip();
$$.unexpandCircles();
$$.unexpandBars();
}
eventRectEnter.append('rect')
.attr('x', 0)
.attr('y', 0)
@ -247,9 +253,7 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter)
.attr('class', CLASS.eventRect)
.on('mouseout', function () {
if ($$.hasArcType()) { return; }
$$.hideXGridFocus();
$$.hideTooltip();
$$.unexpandCircles();
mouseout();
})
.on('mousemove', function () {
var targetsToShow = $$.filterTargetsToShow($$.data.targets);
@ -261,9 +265,17 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter)
mouse = d3.mouse(this);
closest = $$.findClosestFromTargets(targetsToShow, mouse);
if (! closest) { return; }
if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id)) {
config.data_onmouseout.call($$, $$.mouseover);
$$.mouseover = undefined;
}
if (! closest) {
mouseout();
return;
}
if ($$.isScatterType(closest)) {
if ($$.isScatterType(closest) || !config.tooltip_grouped) {
sameXData = [closest];
} else {
sameXData = $$.filterByX(targetsToShow, closest.x);
@ -279,21 +291,18 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter)
if (config.point_focus_expand_enabled) {
$$.expandCircles(closest.index, closest.id, true);
}
$$.expandBars(closest.index, closest.id, true);
// Show xgrid focus line
$$.showXGridFocus(selectedData);
// Show cursor as pointer if point is close to mouse position
if ($$.dist(closest, mouse) < 100) {
if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < 100) {
$$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer');
if (!$$.mouseover) {
config.data_onmouseover.call($$, closest);
$$.mouseover = true;
$$.mouseover = closest;
}
} else if ($$.mouseover) {
$$.svg.select('.' + CLASS.eventRect).style('cursor', null);
config.data_onmouseout.call($$, closest);
$$.mouseover = false;
}
})
.on('click', function () {
@ -308,8 +317,8 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter)
if (! closest) { return; }
// select if selection enabled
if ($$.dist(closest, mouse) < 100 && $$.toggleShape) {
$$.main.select('.' + CLASS.circles + $$.getTargetSelectorSuffix(closest.id)).select('.' + CLASS.circle + '-' + closest.index).each(function () {
if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < 100) {
$$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id)).select('.' + CLASS.shape + '-' + closest.index).each(function () {
if (config.data_selection_grouped || $$.isWithinShape(this, closest)) {
$$.toggleShape(this, closest, closest.index);
$$.config.data_onclick.call($$.api, closest, this);
@ -322,8 +331,7 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter)
.on('drag', function () { $$.drag(d3.mouse(this)); })
.on('dragstart', function () { $$.dragstart(d3.mouse(this)); })
.on('dragend', function () { $$.dragend(); })
)
.on("dblclick.zoom", null);
);
};
c3_chart_internal_fn.dispatchEvent = function (type, index, mouse) {
var $$ = this,

63
src/legend.js

@ -40,12 +40,10 @@ c3_chart_internal_fn.getLegendWidth = function () {
return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0;
};
c3_chart_internal_fn.getLegendHeight = function () {
var $$ = this, config = $$.config, h = 0;
if (config.legend_show) {
var $$ = this, h = 0;
if ($$.config.legend_show) {
if ($$.isLegendRight) {
h = $$.currentHeight;
} else if ($$.isLegendInset) {
h = config.legend_inset_step ? Math.max(20, $$.legendItemHeight) * (config.legend_inset_step + 1) : $$.height;
} else {
h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1);
}
@ -60,17 +58,17 @@ c3_chart_internal_fn.opacityForUnfocusedLegend = function (legendItem) {
var $$ = this;
return legendItem.classed(CLASS.legendItemHidden) ? $$.legendOpacityForHidden : 0.3;
};
c3_chart_internal_fn.toggleFocusLegend = function (id, focus) {
c3_chart_internal_fn.toggleFocusLegend = function (targetIds, focus) {
var $$ = this;
targetIds = $$.mapToTargetIds(targetIds);
$$.legend.selectAll('.' + CLASS.legendItem)
.classed(CLASS.legendItemFocused, function (id) {
return targetIds.indexOf(id) >= 0 && focus;
})
.transition().duration(100)
.style('opacity', function (_id) {
var This = $$.d3.select(this);
if (id && _id !== id) {
return focus ? $$.opacityForUnfocusedLegend(This) : $$.opacityForLegend(This);
} else {
return focus ? $$.opacityForLegend(This) : $$.opacityForUnfocusedLegend(This);
}
.style('opacity', function (id) {
var opacity = targetIds.indexOf(id) >= 0 && focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend;
return opacity.call($$, $$.d3.select(this));
});
};
c3_chart_internal_fn.revertLegend = function () {
@ -109,7 +107,7 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0;
var withTransition, withTransitionForTransform;
var hasFocused = $$.legend.selectAll('.' + CLASS.legendItemFocused).size();
var texts, rects, tiles;
var texts, rects, tiles, background;
options = options || {};
withTransition = getOption(options, "withTransition", true);
@ -176,6 +174,11 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
}
}
if ($$.isLegendInset) {
step = config.legend_inset_step ? config.legend_inset_step : targetIds.length;
$$.updateLegendStep(step);
}
if ($$.isLegendRight) {
xForLegend = function (id) { return maxWidth * steps[id]; };
yForLegend = function (id) { return margins[steps[id]] + offsets[id]; };
@ -199,11 +202,21 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
.style('visibility', function (id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden'; })
.style('cursor', 'pointer')
.on('click', function (id) {
config.legend_item_onclick ? config.legend_item_onclick.call($$, id) : $$.api.toggle(id);
if (config.legend_item_onclick) {
config.legend_item_onclick.call($$, id);
} else {
if ($$.d3.event.altKey) {
$$.api.hide();
$$.api.show(id);
} else {
$$.api.toggle(id);
$$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert();
}
}
})
.on('mouseover', function (id) {
$$.d3.select(this).classed(CLASS.legendItemFocused, true);
if (!$$.transiting) {
if (!$$.transiting && $$.isTargetToShow(id)) {
$$.api.focus(id);
}
if (config.legend_item_onmouseover) {
@ -212,9 +225,7 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
})
.on('mouseout', function (id) {
$$.d3.select(this).classed(CLASS.legendItemFocused, false);
if (!$$.transiting) {
$$.api.revert();
}
$$.api.revert();
if (config.legend_item_onmouseout) {
config.legend_item_onmouseout.call($$, id);
}
@ -238,13 +249,13 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
.attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegend)
.attr('width', 10)
.attr('height', 10);
// Set background for inset legend
if ($$.isLegendInset && maxWidth !== 0) {
$$.legend.insert('g', '.' + CLASS.legendItem)
background = $$.legend.select('.' + CLASS.legendBackground + ' rect');
if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) {
background = $$.legend.insert('g', '.' + CLASS.legendItem)
.attr("class", CLASS.legendBackground)
.append('rect')
.attr('height', $$.getLegendHeight() - 10)
.attr('width', maxWidth * (step + 1) + 10);
.append('rect');
}
texts = $$.legend.selectAll('text')
@ -270,6 +281,12 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
.attr('x', xForLegend)
.attr('y', yForLegend);
if (background) {
(withTransition ? background.transition() : background)
.attr('height', $$.getLegendHeight() - 12)
.attr('width', maxWidth * (step + 1) + 10);
}
// toggle legend state
$$.legend.selectAll('.' + CLASS.legendItem)
.classed(CLASS.legendItemHidden, function (id) { return !$$.isTargetToShow(id); })

10
src/scale.js

@ -70,11 +70,15 @@ c3_chart_internal_fn.updateScales = function () {
$$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain());
// update axes
$$.xAxisTickFormat = $$.getXAxisTickFormat();
$$.xAxisTickValues = config.axis_x_tick_values ? config.axis_x_tick_values : (forInit ? undefined : $$.xAxis.tickValues());
$$.xAxisTickValues = $$.getXAxisTickValues();
$$.yAxisTickValues = $$.getYAxisTickValues();
$$.y2AxisTickValues = $$.getY2AxisTickValues();
$$.xAxis = $$.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
$$.subXAxis = $$.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
$$.yAxis = $$.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, config.axis_y_ticks, config.axis_y_tick_outer);
$$.y2Axis = $$.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, config.axis_y2_ticks, config.axis_y2_tick_outer);
$$.yAxis = $$.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, config.axis_y_tick_outer);
$$.y2Axis = $$.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, config.axis_y2_tick_outer);
// Set initialized scales to brush and zoom
if (!forInit) {
if ($$.brush) { $$.brush.scale($$.subX); }

21
src/shape.bar.js

@ -7,10 +7,11 @@ c3_chart_internal_fn.updateTargetsForBar = function (targets) {
var $$ = this, config = $$.config,
mainBarUpdate, mainBarEnter,
classChartBar = $$.classChartBar.bind($$),
classBars = $$.classBars.bind($$);
classBars = $$.classBars.bind($$),
classFocus = $$.classFocus.bind($$);
mainBarUpdate = $$.main.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar)
.data(targets)
.attr('class', classChartBar);
.attr('class', function (d) { return classChartBar(d) + classFocus(d); });
mainBarEnter = mainBarUpdate.enter().append('g')
.attr('class', classChartBar)
.style('opacity', 0)
@ -51,14 +52,14 @@ c3_chart_internal_fn.getBarW = function (axis, barTargetsNum) {
w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickOffset() * 2 * config.bar_width_ratio) / barTargetsNum : 0;
return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w;
};
c3_chart_internal_fn.getBars = function (i) {
c3_chart_internal_fn.getBars = function (i, id) {
var $$ = this;
return $$.main.selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : ''));
return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : ''));
};
c3_chart_internal_fn.expandBars = function (i, id, reset) {
var $$ = this;
if (reset) { $$.unexpandBars(); }
$$.getBars(i).classed(CLASS.EXPANDED, true);
$$.getBars(i, id).classed(CLASS.EXPANDED, true);
};
c3_chart_internal_fn.unexpandBars = function (i) {
var $$ = this;
@ -110,11 +111,11 @@ c3_chart_internal_fn.generateGetBarPoints = function (barIndices, isSub) {
];
};
};
c3_chart_internal_fn.isWithinBar = function (_this) {
var d3 = this.d3,
mouse = d3.mouse(_this), box = _this.getBoundingClientRect(),
seg0 = _this.pathSegList.getItem(0), seg1 = _this.pathSegList.getItem(1),
x = seg0.x, y = Math.min(seg0.y, seg1.y), w = box.width, h = box.height, offset = 2,
c3_chart_internal_fn.isWithinBar = function (that) {
var mouse = this.d3.mouse(that), box = that.getBoundingClientRect(),
seg0 = that.pathSegList.getItem(0), seg1 = that.pathSegList.getItem(1),
x = Math.min(seg0.x, seg1.x), y = Math.min(seg0.y, seg1.y),
w = box.width, h = box.height, offset = 2,
sx = x - offset, ex = x + w + offset, sy = y + h + offset, ey = y - offset;
return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
};

17
src/shape.js

@ -38,9 +38,12 @@ c3_chart_internal_fn.getShapeOffset = function (typeFilter, indices, isSub) {
var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id),
y0 = scale(0), offset = y0;
targets.forEach(function (t) {
var values = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values;
if (t.id === d.id || indices[t.id] !== indices[d.id]) { return; }
if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id) && t.values[i].value * d.value >= 0) {
offset += scale(t.values[i].value) - y0;
if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) {
if (values[i].value * d.value >= 0) {
offset += scale(values[i].value) - y0;
}
}
});
return offset;
@ -49,9 +52,11 @@ c3_chart_internal_fn.getShapeOffset = function (typeFilter, indices, isSub) {
c3_chart_internal_fn.isWithinShape = function (that, d) {
var $$ = this,
shape = $$.d3.select(that), isWithin;
if (that.nodeName === 'circle') {
// circle is hidden in step chart, so treat as within the click area
isWithin = $$.isStepType(d) ? true : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5);
if (!$$.isTargetToShow(d.id)) {
isWithin = false;
}
else if (that.nodeName === 'circle') {
isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5);
}
else if (that.nodeName === 'path') {
isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar(that) : true;
@ -62,5 +67,5 @@ c3_chart_internal_fn.isWithinShape = function (that, d) {
c3_chart_internal_fn.getInterpolate = function (d) {
var $$ = this;
return $$.isSplineType(d) ? "cardinal" : $$.isStepType(d) ? "step-after" : "linear";
return $$.isSplineType(d) ? "cardinal" : $$.isStepType(d) ? $$.config.line_step_type : "linear";
};

46
src/shape.line.js

@ -9,10 +9,11 @@ c3_chart_internal_fn.updateTargetsForLine = function (targets) {
classChartLine = $$.classChartLine.bind($$),
classLines = $$.classLines.bind($$),
classAreas = $$.classAreas.bind($$),
classCircles = $$.classCircles.bind($$);
classCircles = $$.classCircles.bind($$),
classFocus = $$.classFocus.bind($$);
mainLineUpdate = $$.main.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine)
.data(targets)
.attr('class', classChartLine);
.attr('class', function (d) { return classChartLine(d) + classFocus(d); });
mainLineEnter = mainLineUpdate.enter().append('g')
.attr('class', classChartLine)
.style('opacity', 0)
@ -47,6 +48,7 @@ c3_chart_internal_fn.redrawLine = function (durationForExit) {
.style("stroke", $$.color);
$$.mainLine
.style("opacity", $$.initialOpacity.bind($$))
.style('shape-rendering', function (d) { return $$.isStepType(d) ? 'crispEdges' : ''; })
.attr('transform', null);
$$.mainLine.exit().transition().duration(durationForExit)
.style('opacity', 0)
@ -72,18 +74,19 @@ c3_chart_internal_fn.generateDrawLine = function (lineIndices, isSub) {
line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue);
if (!config.line_connectNull) { line = line.defined(function (d) { return d.value != null; }); }
return function (d) {
var data = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
x = isSub ? $$.x : $$.subX, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path;
if ($$.isLineType(d)) {
if (config.data_regions[d.id]) {
path = $$.lineWithRegions(data, x, y, config.data_regions[d.id]);
path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]);
} else {
path = line.interpolate($$.getInterpolate(d))(data);
if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); }
path = line.interpolate($$.getInterpolate(d))(values);
}
} else {
if (data[0]) {
x0 = x(data[0].x);
y0 = y(data[0].value);
if (values[0]) {
x0 = x(values[0].x);
y0 = y(values[0].value);
}
path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
}
@ -235,13 +238,15 @@ c3_chart_internal_fn.generateDrawArea = function (areaIndices, isSub) {
}
return function (d) {
var data = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x0 = 0, y0 = 0, path;
var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
x0 = 0, y0 = 0, path;
if ($$.isAreaType(d)) {
path = area.interpolate($$.getInterpolate(d))(data);
if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); }
path = area.interpolate($$.getInterpolate(d))(values);
} else {
if (data[0]) {
x0 = $$.x(data[0].x);
y0 = $$.getYScale(d.id)(data[0].value);
if (values[0]) {
x0 = $$.x(values[0].x);
y0 = $$.getYScale(d.id)(values[0].value);
}
path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
}
@ -284,7 +289,7 @@ c3_chart_internal_fn.redrawCircle = function () {
.attr("r", $$.pointR.bind($$))
.style("fill", $$.color);
$$.mainCircle
.style("opacity", $$.initialOpacity.bind($$));
.style("opacity", $$.initialOpacityForCircle.bind($$));
$$.mainCircle.exit().remove();
};
c3_chart_internal_fn.addTransitionForCircle = function (transitions, cx, cy) {
@ -328,7 +333,7 @@ c3_chart_internal_fn.unexpandCircles = function (i) {
};
c3_chart_internal_fn.pointR = function (d) {
var $$ = this, config = $$.config;
return config.point_show && !$$.isStepType(d) ? (isFunction(config.point_r) ? config.point_r(d) : config.point_r) : 0;
return $$.isStepType(d) ? 0 : (isFunction(config.point_r) ? config.point_r(d) : config.point_r);
};
c3_chart_internal_fn.pointExpandedR = function (d) {
var $$ = this, config = $$.config;
@ -338,9 +343,12 @@ c3_chart_internal_fn.pointSelectR = function (d) {
var $$ = this, config = $$.config;
return config.point_select_r ? config.point_select_r : $$.pointR(d) * 4;
};
c3_chart_internal_fn.isWithinCircle = function (_this, _r) {
c3_chart_internal_fn.isWithinCircle = function (that, r) {
var d3 = this.d3,
mouse = d3.mouse(_this), d3_this = d3.select(_this),
cx = d3_this.attr("cx") * 1, cy = d3_this.attr("cy") * 1;
return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < _r;
mouse = d3.mouse(that), d3_this = d3.select(that),
cx = +d3_this.attr("cx"), cy = +d3_this.attr("cy");
return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r;
};
c3_chart_internal_fn.isWithinStep = function (that, y) {
return Math.abs(y - this.d3.mouse(that)[1]) < 30;
};

28
src/size.js

@ -15,14 +15,14 @@ c3_chart_internal_fn.getCurrentPaddingBottom = function () {
var config = this.config;
return isValue(config.padding_bottom) ? config.padding_bottom : 0;
};
c3_chart_internal_fn.getCurrentPaddingLeft = function () {
c3_chart_internal_fn.getCurrentPaddingLeft = function (withoutRecompute) {
var $$ = this, config = $$.config;
if (isValue(config.padding_left)) {
return config.padding_left;
} else if (config.axis_rotated) {
return !config.axis_x_show ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x')), 40);
return !config.axis_x_show ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40);
} else {
return !config.axis_y_show ? 1 : ceil10($$.getAxisWidthByAxisId('y'));
return !config.axis_y_show ? 1 : ceil10($$.getAxisWidthByAxisId('y', withoutRecompute));
}
};
c3_chart_internal_fn.getCurrentPaddingRight = function () {
@ -57,29 +57,37 @@ c3_chart_internal_fn.getParentHeight = function () {
};
c3_chart_internal_fn.getSvgLeft = function () {
c3_chart_internal_fn.getSvgLeft = function (withoutRecompute) {
var $$ = this, config = $$.config,
leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY,
leftAxis = $$.main.select('.' + leftAxisClass).node(),
svgRect = leftAxis ? leftAxis.getBoundingClientRect() : {right: 0},
chartRect = $$.selectChart.node().getBoundingClientRect(),
hasArc = $$.hasArcType(),
svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft());
svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute));
return svgLeft > 0 ? svgLeft : 0;
};
c3_chart_internal_fn.getAxisWidthByAxisId = function (id) {
c3_chart_internal_fn.getAxisWidthByAxisId = function (id, withoutRecompute) {
var $$ = this, position = $$.getAxisLabelPositionById(id);
return position.isInner ? 20 + $$.getMaxTickWidth(id) : 40 + $$.getMaxTickWidth(id);
if (withoutRecompute) {
var box = $$.d3.select('.c3-axis-y').node().getBoundingClientRect();
return Math.floor(box.left + box.width);
}
return $$.getMaxTickWidth(id) + (position.isInner ? 20 : 40);
};
c3_chart_internal_fn.getHorizontalAxisHeight = function (axisId) {
var $$ = this, config = $$.config;
if (axisId === 'x' && !config.axis_x_show) { return 0; }
var $$ = this, config = $$.config, h = 30;
if (axisId === 'x' && !config.axis_x_show) { return 8; }
if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height; }
if (axisId === 'y' && !config.axis_y_show) { return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1; }
if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top; }
return ($$.getAxisLabelPositionById(axisId).isInner ? 30 : 40) + (axisId === 'y2' ? -10 : 0);
// Calculate x axis height when tick rotated
if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) {
h = $$.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - config.axis_x_tick_rotate) / 180);
}
return h + ($$.getAxisLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0);
};
c3_chart_internal_fn.getEventRectWidth = function () {

4
src/subchart.js

@ -19,7 +19,7 @@ c3_chart_internal_fn.initSubchart = function () {
// Define g for chart area
context.append('g')
.attr("clip-path", $$.clipPath)
.attr("clip-path", $$.clipPathForSubchart)
.attr('class', CLASS.chart);
// Define g for bar chart area
@ -164,7 +164,7 @@ c3_chart_internal_fn.redrawForBrush = function () {
var $$ = this, x = $$.x;
$$.redraw({
withTransition: false,
withY: false,
withY: $$.config.zoom_rescale,
withSubchart: false,
withUpdateXDomain: true
});

17
src/text.js

@ -7,10 +7,11 @@ c3_chart_internal_fn.initText = function () {
c3_chart_internal_fn.updateTargetsForText = function (targets) {
var $$ = this, mainTextUpdate, mainTextEnter,
classChartText = $$.classChartText.bind($$),
classTexts = $$.classTexts.bind($$);
classTexts = $$.classTexts.bind($$),
classFocus = $$.classFocus.bind($$);
mainTextUpdate = $$.main.select('.' + CLASS.chartTexts).selectAll('.' + CLASS.chartText)
.data(targets)
.attr('class', classChartText);
.attr('class', function (d) { return classChartText(d) + classFocus(d); });
mainTextEnter = mainTextUpdate.enter().append('g')
.attr('class', classChartText)
.style('opacity', 0)
@ -31,7 +32,7 @@ c3_chart_internal_fn.redrawText = function (durationForExit) {
.style("fill", function (d) { return $$.color(d); })
.style("fill-opacity", 0);
$$.mainText
.text(function (d) { return $$.formatByAxisId($$.getAxisId(d.id))(d.value, d.id); });
.text(function (d, i, j) { return $$.formatByAxisId($$.getAxisId(d.id))(d.value, d.id, i, j); });
$$.mainText.exit()
.transition().duration(durationForExit)
.style('fill-opacity', 0)
@ -47,14 +48,16 @@ c3_chart_internal_fn.addTransitionForText = function (transitions, xForText, yFo
.style("fill-opacity", opacityForText));
};
c3_chart_internal_fn.getTextRect = function (text, cls) {
var rect;
this.svg.selectAll('.dummy')
var body = this.d3.select('body').classed('c3', true),
svg = body.append("svg").style('visibility', 'hidden'), rect;
svg.selectAll('.dummy')
.data([text])
.enter().append('text')
.classed(cls ? cls : "", true)
.text(text)
.each(function () { rect = this.getBoundingClientRect(); })
.remove();
.each(function () { rect = this.getBoundingClientRect(); });
svg.remove();
body.classed('c3', false);
return rect;
};
c3_chart_internal_fn.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) {

17
src/tooltip.js

@ -2,10 +2,10 @@ c3_chart_internal_fn.initTooltip = function () {
var $$ = this, config = $$.config, i;
$$.tooltip = $$.selectChart
.style("position", "relative")
.append("div")
.append("div")
.attr('class', CLASS.tooltipContainer)
.style("position", "absolute")
.style("pointer-events", "none")
.style("z-index", "10")
.style("display", "none");
// Show tooltip if needed
if (config.tooltip_init_show) {
@ -38,7 +38,7 @@ c3_chart_internal_fn.getTooltipContent = function (d, defaultTitleFormat, defaul
text = "<table class='" + CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
}
name = nameFormat(d[i].name);
name = nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index);
value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
@ -67,24 +67,23 @@ c3_chart_internal_fn.showTooltip = function (selectedData, mouse) {
tooltipLeft = ($$.width / 2) + mouse[0];
tooltipTop = ($$.height / 2) + mouse[1] + 20;
} else {
svgLeft = $$.getSvgLeft(true);
if (config.axis_rotated) {
svgLeft = $$.getSvgLeft();
tooltipLeft = svgLeft + mouse[0] + 100;
tooltipRight = tooltipLeft + tWidth;
chartRight = $$.getCurrentWidth() - $$.getCurrentPaddingRight();
chartRight = $$.currentWidth - $$.getCurrentPaddingRight();
tooltipTop = $$.x(dataToShow[0].x) + 20;
} else {
svgLeft = $$.getSvgLeft();
tooltipLeft = svgLeft + $$.getCurrentPaddingLeft() + $$.x(dataToShow[0].x) + 20;
tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20;
tooltipRight = tooltipLeft + tWidth;
chartRight = svgLeft + $$.getCurrentWidth() - $$.getCurrentPaddingRight();
chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();
tooltipTop = mouse[1] + 15;
}
if (tooltipRight > chartRight) {
tooltipLeft -= tooltipRight - chartRight;
}
if (tooltipTop + tHeight > $$.getCurrentHeight() && tooltipTop > tHeight + 30) {
if (tooltipTop + tHeight > $$.currentHeight && tooltipTop > tHeight + 30) {
tooltipTop -= tHeight + 30;
}
}

18
src/type.js

@ -10,11 +10,19 @@ c3_chart_internal_fn.setTargetType = function (targetIds, type) {
};
c3_chart_internal_fn.hasType = function (type, targets) {
var $$ = this, types = $$.config.data_types, has = false;
(targets || $$.data.targets).forEach(function (t) {
if ((types[t.id] && types[t.id].indexOf(type) >= 0) || (!(t.id in types) && type === 'line')) {
has = true;
}
});
targets = targets || $$.data.targets;
if (targets && targets.length) {
targets.forEach(function (target) {
var t = types[target.id];
if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) {
has = true;
}
});
} else {
Object.keys(types).forEach(function (id) {
if (types[id] === type) { has = true; }
});
}
return has;
};
c3_chart_internal_fn.hasArcType = function (targets) {

37
src/zoom.js

@ -1,18 +1,24 @@
c3_chart_internal_fn.initZoom = function () {
var $$ = this, d3 = $$.d3, config = $$.config,
prevZoomTranslate, wheeled = false;
var $$ = this, d3 = $$.d3, config = $$.config, startEvent;
$$.zoom = d3.behavior.zoom()
.on("zoomstart", function () {
startEvent = d3.event.sourceEvent;
$$.zoom.altDomain = d3.event.sourceEvent.altKey ? $$.x.orgDomain() : null;
config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent);
})
.on("zoom", function () {
// prevZoomTranslate is needed for the fix of unexpected zoom.translate after remaining zoom
if (prevZoomTranslate && wheeled) {
$$.zoom.translate(prevZoomTranslate);
}
$$.redrawForZoom.call($$);
prevZoomTranslate = $$.zoom.translate();
wheeled = d3.event.sourceEvent.type === 'wheel';
})
.on('zoomend', function () {
var event = d3.event.sourceEvent;
// if click, do nothing. otherwise, click interaction will be canceled.
if (event && startEvent.x === event.x && startEvent.y === event.y) {
return;
}
$$.redrawEventRect();
$$.updateZoom();
config.zoom_onzoomend.call($$.api, $$.x.orgDomain());
});
$$.zoom.scale = function (scale) {
return config.axis_rotated ? this.y(scale) : this.x(scale);
@ -30,11 +36,11 @@ c3_chart_internal_fn.initZoom = function () {
};
c3_chart_internal_fn.updateZoom = function () {
var $$ = this, z = $$.config.zoom_enabled ? $$.zoom : function () {};
$$.main.select('.' + CLASS.zoomRect).call(z);
$$.main.selectAll('.' + CLASS.eventRect).call(z);
$$.main.select('.' + CLASS.zoomRect).call(z).on("dblclick.zoom", null);
$$.main.selectAll('.' + CLASS.eventRect).call(z).on("dblclick.zoom", null);
};
c3_chart_internal_fn.redrawForZoom = function () {
var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x, orgXDomain = $$.orgXDomain;
var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x;
if (!config.zoom_enabled) {
return;
}
@ -46,13 +52,14 @@ c3_chart_internal_fn.redrawForZoom = function () {
zoom.scale(x).updateScaleExtent();
return;
}
if ($$.isCategorized() && x.orgDomain()[0] === orgXDomain[0]) {
x.domain([orgXDomain[0] - 1e-10, x.orgDomain()[1]]);
if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) {
x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]);
}
$$.redraw({
withTransition: false,
withY: false,
withSubchart: false
withY: config.zoom_rescale,
withSubchart: false,
withEventRect: false
});
if (d3.event.sourceEvent.type === 'mousemove') {
$$.cancelClick = true;

Loading…
Cancel
Save