Browse Source

added ability to set names in load function

pull/1047/head
Timopheym 9 years ago
parent
commit
ced44d2529
  1. 3
      .gitignore
  2. 7
      .travis.yml
  3. 40
      CONTRIBUTING.md
  4. 21
      Gruntfile.coffee
  5. 14
      README.md
  6. 3
      bower.json
  7. 17
      c3.css
  8. 1082
      c3.js
  9. 2
      c3.min.css
  10. 10
      c3.min.js
  11. 4
      component.json
  12. 116
      extensions/chart-bubble/bubble.js
  13. 1
      extensions/chart-bubble/c3.css
  14. 1
      extensions/chart-bubble/c3.min.js
  15. 38
      extensions/chart-bubble/index.html
  16. 4
      htdocs/samples/data_label_format.html
  17. 4
      htdocs/samples/timeseries_date.html
  18. 77
      karma.conf.js
  19. 22
      package.json
  20. 54
      spec/api.axis-spec.js
  21. 4
      spec/api.data-spec.js
  22. 32
      spec/axis-spec.js
  23. 8
      spec/core-spec.js
  24. 301
      spec/data-spec.js
  25. 23
      spec/domain-spec.js
  26. 12
      spec/grid-spec.js
  27. 97
      spec/legend-spec.js
  28. 93
      spec/shape.bar-spec.js
  29. 47
      spec/shape.line-spec.js
  30. 98
      spec/title-spec.js
  31. 21
      spec/tooltip-spec.js
  32. 4
      src/api.axis.js
  33. 17
      src/api.chart.js
  34. 2
      src/api.flow.js
  35. 10
      src/api.load.js
  36. 4
      src/api.tooltip.js
  37. 32
      src/api.zoom.js
  38. 82
      src/arc.js
  39. 298
      src/axis.js
  40. 44
      src/c3.axis.js
  41. 1
      src/class.js
  42. 33
      src/config.js
  43. 144
      src/core.js
  44. 14
      src/data.convert.js
  45. 11
      src/data.js
  46. 3
      src/data.load.js
  47. 35
      src/domain.js
  48. 2
      src/format.js
  49. 9
      src/interaction.js
  50. 71
      src/legend.js
  51. 32
      src/polyfill.js
  52. 20
      src/scale.js
  53. 1
      src/scss/chart.scss
  54. 4
      src/scss/main.scss
  55. 3
      src/scss/title.scss
  56. 2
      src/selection.js
  57. 2
      src/shape.bar.js
  58. 17
      src/shape.js
  59. 32
      src/shape.line.js
  60. 33
      src/size.js
  61. 14
      src/subchart.js
  62. 2
      src/tail.js
  63. 30
      src/text.js
  64. 31
      src/title.js
  65. 34
      src/tooltip.js
  66. 3
      src/type.js
  67. 8
      src/ua.js
  68. 4
      src/util.js
  69. 8
      src/zoom.js

3
.gitignore vendored

@ -6,3 +6,6 @@ d3.min.js
components components
build build
.sass-cache .sass-cache
# coverage report
/coverage

7
.travis.yml

@ -3,3 +3,10 @@ language: node_js
before_script: before_script:
- npm install -g grunt-cli - npm install -g grunt-cli
- gem install sass - gem install sass
script:
- npm run lint
- npm test
after_success:
- npm run codecov

40
CONTRIBUTING.md

@ -0,0 +1,40 @@
## Filing an issue
Before filing an issue, please [search the queue](https://github.com/masayuki0812/c3/issues) to make sure it hasn't already been reported.
If a bug, please include the following —
1. What version of C3?
1. What browsers have you confirmed it in?
1. Can you isolate the issue by providing a jsFiddle demonstrating it in a minimalist capacity?
Please *do not* ask for support using the issue queue. For support, please ask [on chat](https://gitter.im/masayuki0812/c3) or [the mailing list](groups.google.com/forum/#!forum/c3js).
##Building C3 from sources
1. **Clone the repo from GitHub**
git clone https://github.com/masayuki0812/c3.git
cd c3
2. **Acquire build dependencies.** Make sure you have [Node.js](http://nodejs.org/) installed on your workstation. This is only needed to _build_ C3 from sources. C3 itself has no dependency on Node.js once it is built. Now run:
npm install -g grunt-cli
npm install
The first `npm` command sets up the popular [Grunt](http://gruntjs.com/) build tool. You might need to run this command with `sudo` if you're on Linux or Mac OS X, or in an Administrator command prompt on Windows. The second `npm` command fetches the remaining build dependencies.
3. **Run the build tool**
grunt
Now you'll find the built files in `c3.js`, `c3.min.js`, `c3.css` & `c3.min.css`.
## Running the tests
The `grunt` script will automatically run the specification suite and report its results.
Or, if you want to run the specs in a browser (e.g., for debugging), simply open `spec/runner.html` in your browser.
## Contributing your changes
Add something about PRs here, indicate that PRs should not bump the version number & the build output files (`c3.js`, `c3.min.js`, `c3.css` & `c3.min.css`) should be excluded

21
Gruntfile.coffee

@ -1,5 +1,5 @@
module.exports = (grunt) -> module.exports = (grunt) ->
require('load-grunt-tasks') grunt, pattern: ['grunt-contrib-*', 'grunt-sass'] require('load-grunt-tasks') grunt, pattern: ['grunt-contrib-*', 'grunt-sass', 'grunt-karma']
grunt.initConfig grunt.initConfig
watch: watch:
@ -40,6 +40,7 @@ module.exports = (grunt) ->
'src/grid.js', 'src/grid.js',
'src/tooltip.js', 'src/tooltip.js',
'src/legend.js', 'src/legend.js',
'src/title.js',
'src/axis.js', 'src/axis.js',
'src/clip.js', 'src/clip.js',
'src/arc.js', 'src/arc.js',
@ -72,6 +73,7 @@ module.exports = (grunt) ->
'src/api.chart.js', 'src/api.chart.js',
'src/api.tooltip.js', 'src/api.tooltip.js',
'src/c3.axis.js', 'src/c3.axis.js',
'src/ua.js',
'src/polyfill.js', 'src/polyfill.js',
'src/tail.js' 'src/tail.js'
] ]
@ -83,14 +85,9 @@ module.exports = (grunt) ->
options: options:
jshintrc: '.jshintrc' jshintrc: '.jshintrc'
jasmine: karma:
c3: unit:
src: 'c3.js' configFile: 'karma.conf.js'
options:
specs: 'spec/*-spec.js'
helpers: 'spec/*-helper.js'
styles: 'c3.css'
vendor: 'https://raw.githubusercontent.com/mbostock/d3/v3.5.0/d3.min.js'
uglify: uglify:
c3: c3:
@ -109,4 +106,8 @@ module.exports = (grunt) ->
files: files:
'c3.css': 'src/scss/main.scss' 'c3.css': 'src/scss/main.scss'
grunt.registerTask 'default', ['concat', 'jshint', 'jasmine', 'sass', 'cssmin', 'uglify'] grunt.registerTask 'lint', ['jshint']
grunt.registerTask 'test', ['karma']
grunt.registerTask 'build', ['concat', 'sass']
grunt.registerTask 'minify', ['cssmin', 'uglify']
grunt.registerTask 'default', ['lint', 'build', 'test', 'minify']

14
README.md

@ -1,4 +1,4 @@
c3 [![Build Status](https://travis-ci.org/masayuki0812/c3.svg?branch=master)](https://travis-ci.org/masayuki0812/c3) [![Dependency Status](https://david-dm.org/masayuki0812/c3.svg)](https://david-dm.org/masayuki0812/c3) [![devDependency Status](https://david-dm.org/masayuki0812/c3/dev-status.svg)](https://david-dm.org/masayuki0812/c3#info=devDependencies) [![license](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/masayuki0812/c3/blob/master/LICENSE) c3 [![Build Status](https://travis-ci.org/masayuki0812/c3.svg?branch=master)](https://travis-ci.org/masayuki0812/c3) [![Dependency Status](https://david-dm.org/masayuki0812/c3.svg)](https://david-dm.org/masayuki0812/c3) [![devDependency Status](https://david-dm.org/masayuki0812/c3/dev-status.svg)](https://david-dm.org/masayuki0812/c3#info=devDependencies) [![license](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/masayuki0812/c3/blob/master/LICENSE) [![codecov.io](https://codecov.io/github/masayuki0812/c3/coverage.svg?branch=master)](https://codecov.io/github/masayuki0812/c3?branch=master)
== ==
c3 is a D3-based reusable chart library that enables deeper integration of charts into web applications. c3 is a D3-based reusable chart library that enables deeper integration of charts into web applications.
@ -22,10 +22,14 @@ $ python -m SimpleHTTPServer 8080
## Google Group ## Google Group
For general C3.js-related discussion, please visit our [Google Group at https://groups.google.com/forum/#!forum/c3js](https://groups.google.com/forum/#!forum/c3js). For general C3.js-related discussion, please visit our [Google Group at https://groups.google.com/forum/#!forum/c3js](https://groups.google.com/forum/#!forum/c3js).
## Gitter
[![Join the chat at https://gitter.im/masayuki0812/c3](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/masayuki0812/c3?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Using the issue queue ## Using the issue queue
The [issue queue](https://github.com/masayuki0812/c3/issues) is to be used for reporting defects and problems with C3.js, in addition to feature requests and ideas. It is **not** a catch-all support forum. **For general support enquiries, please use the [Google Group](https://groups.google.com/forum/#!forum/c3js) at https://groups.google.com/forum/#!forum/c3js.** All questions involving the interplay between C3.js and any other library (such as AngularJS) should be posted there first! The [issue queue](https://github.com/masayuki0812/c3/issues) is to be used for reporting defects and problems with C3.js, in addition to feature requests and ideas. It is **not** a catch-all support forum. **For general support enquiries, please use the [Google Group](https://groups.google.com/forum/#!forum/c3js) at https://groups.google.com/forum/#!forum/c3js.** All questions involving the interplay between C3.js and any other library (such as AngularJS) should be posted there first!
Before reporting an issue, please do the following: Before reporting an issue, please do the following:
1. [Search for existing issues](https://github.com/masayuki0812/c3/issues) to ensure you're not posting a duplicate. 1. [Search for existing issues](https://github.com/masayuki0812/c3/issues) to ensure you're not posting a duplicate.
1. [Search the Google Group](https://groups.google.com/forum/#!forum/c3js) to ensure it hasn't been addressed there already. 1. [Search the Google Group](https://groups.google.com/forum/#!forum/c3js) to ensure it hasn't been addressed there already.
@ -34,18 +38,16 @@ Before reporting an issue, please do the following:
1. When posting the issue, please use a descriptive title and include the version of C3 (or, if cloning from Git, the commit hash — C3 is under active development and the master branch contains the latest dev commits!), along with any platform/browser/OS information that may be relevant. 1. When posting the issue, please use a descriptive title and include the version of C3 (or, if cloning from Git, the commit hash — C3 is under active development and the master branch contains the latest dev commits!), along with any platform/browser/OS information that may be relevant.
## Pull requests ## Pull requests
Pull requests are welcome, though please post an issue first to see whether such a change is desirable. Pull requests are welcome, though please post an issue first to see whether such a change is desirable.
If you choose to submit a pull request, please do not bump the version number unless asked to, and please include test cases for any new features! If you choose to submit a pull request, please do not bump the version number unless asked to, and please include test cases for any new features. Squash all your commits as well, please.
## Playground ## Playground
Please fork this fiddle: Please fork this fiddle:
+ [http://jsfiddle.net/masayuki0812/7kYJu/](http://jsfiddle.net/masayuki0812/7kYJu/) + [http://jsfiddle.net/masayuki0812/7kYJu/](http://jsfiddle.net/masayuki0812/7kYJu/)
## Dependency ## Dependency
+ [D3.js](https://github.com/mbostock/d3) `<=3.5.0` + [D3.js](https://github.com/mbostock/d3) `~3.5.0`
## License ## License
MIT MIT
[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=masayuki0812&url=https://github.com/masayuki0812/c3&title=c3&language=javascript&tags=github&category=software)

3
bower.json

@ -4,7 +4,6 @@
"c3.css", "c3.css",
"c3.js" "c3.js"
], ],
"version": "0.4.10-rc1",
"homepage": "https://github.com/masayuki0812/c3", "homepage": "https://github.com/masayuki0812/c3",
"authors": [ "authors": [
"Masayuki Tanaka <masayuki0812@mac.com>" "Masayuki Tanaka <masayuki0812@mac.com>"
@ -27,6 +26,6 @@
"Gruntfile.*" "Gruntfile.*"
], ],
"dependencies": { "dependencies": {
"d3": "<=3.5.0" "d3": "~3.5.0"
} }
} }

17
c3.css

@ -1,6 +1,7 @@
/*-- Chart --*/ /*-- Chart --*/
.c3 svg { .c3 svg {
font: 10px sans-serif; } font: 10px sans-serif;
-webkit-tap-highlight-color: transparent; }
.c3 path, .c3 line { .c3 path, .c3 line {
fill: none; fill: none;
@ -11,7 +12,11 @@
-moz-user-select: none; -moz-user-select: none;
user-select: none; } user-select: none; }
.c3-legend-item-tile, .c3-xgrid-focus, .c3-ygrid, .c3-event-rect, .c3-bars path { .c3-legend-item-tile,
.c3-xgrid-focus,
.c3-ygrid,
.c3-event-rect,
.c3-bars path {
shape-rendering: crispEdges; } shape-rendering: crispEdges; }
.c3-chart-arc path { .c3-chart-arc path {
@ -70,11 +75,11 @@
/*-- Region --*/ /*-- Region --*/
.c3-region { .c3-region {
fill: steelblue; fill: steelblue;
fill-opacity: 0.1; } fill-opacity: .1; }
/*-- Brush --*/ /*-- Brush --*/
.c3-brush .extent { .c3-brush .extent {
fill-opacity: 0.1; } fill-opacity: .1; }
/*-- Select - Drag --*/ /*-- Select - Drag --*/
/*-- Legend --*/ /*-- Legend --*/
@ -90,6 +95,10 @@
stroke: lightgray; stroke: lightgray;
stroke-width: 1; } stroke-width: 1; }
/*-- Title --*/
.c3-title {
font: 14px sans-serif; }
/*-- Tooltip --*/ /*-- Tooltip --*/
.c3-tooltip-container { .c3-tooltip-container {
z-index: 10; } z-index: 10; }

1082
c3.js

File diff suppressed because it is too large Load Diff

2
c3.min.css vendored

@ -1 +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-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-item-hidden{opacity:.15}.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-title{dominant-baseline:middle;font-size:1.3em}.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} .c3 svg{font:10px sans-serif;-webkit-tap-highlight-color:transparent}.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-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-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-title{font:14px sans-serif}.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-title{dominant-baseline:middle;font-size:1.3em}.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}

10
c3.min.js vendored

File diff suppressed because one or more lines are too long

4
component.json

@ -2,10 +2,10 @@
"name": "c3", "name": "c3",
"repo": "masayuki0812/c3", "repo": "masayuki0812/c3",
"description": "A D3-based reusable chart library", "description": "A D3-based reusable chart library",
"version": "0.4.10-rc1", "version": "0.4.11-rc4",
"keywords": [], "keywords": [],
"dependencies": { "dependencies": {
"mbostock/d3": "v3.5.0" "mbostock/d3": "v3.5.6"
}, },
"development": {}, "development": {},
"license": "MIT", "license": "MIT",

116
extensions/chart-bubble/bubble.js

@ -0,0 +1,116 @@
(function() {
var extra = {};
c3.chart.internal.fn.additionalConfig = {
data_pairs: [],
};
c3.chart.internal.fn.beforeInit = function (config) {
var that = this;
// update internals only when chart type is "bubble"
if (config.data.type !== 'bubble') {
return;
}
// Set extra to ba able to be used in other part
this.extra = extra;
extra.getKey = function (x, y) {
return x + '::' + y;
};
this.config.data_type = 'scatter';
this.config.axis_x_padding = 0;
this.config.axis_y_padding = 0;
this.config.axis_x_tick_centered = true;
this.config.axis_x_tick_format = function (d) {
return extra.names[d];
};
this.config.axis_y_tick_format = function (d) {
return extra.names[d];
};
if (!config.color || !config.color.pattern) {
this.config.color_pattern = ['#1f77b4'];
}
this.config.point_r = function (d) {
var names = extra.names, values = extra.values, base_length = extra.base_length,
x = names[d.x], y = d.id,
key = extra.getKey(x, y), value = !values[key] ? 0 : values[key],
max, max_r, max_area, a, area, r;
if (!base_length) {
base_length = extra.base_length = d3.min([
that.svg.select('.c3-axis.c3-axis-y path').node().getTotalLength(),
that.svg.select('.c3-axis.c3-axis-x path').node().getTotalLength(),
]);
}
max = d3.max(Object.keys(values).map(function (key) { return values[key]; }));
max_r = (base_length / (names.length * 2));
max_area = max_r * max_r * Math.PI;
a = max_area / max;
area = value * a;
r = Math.sqrt(area / Math.PI);
return r;
};
this.config.point_sensitivity = 25;
this.config.point_focus_expand_enabled = false;
this.config.legend_show = false;
if (!config.tooltip || !config.tooltip.contents) {
this.config.tooltip_contents = function (d, defaultTitleFormat, defaultValueFormat, color) {
var x = extra.names[d[0].x], y = d[0].name, v = extra.values[extra.getKey(x, y)], text;
text = "<table class='" + this.CLASS.tooltip + "'>";
text += "<tr><th colspan='2'>" + x + "&nbsp;/&nbsp;" + y + "</th></tr>";
text += "<tr><td class='value'>" + (!v ? 0 : v) + "</td></tr>";
text += "</table>";
return text;
};
}
// construct bubble chart data and setup config based on the values
var xs = this.config.data_pairs.map(function (pair) { return pair.x; }),
ys = this.config.data_pairs.map(function (pair) { return pair.y; });
extra.names = d3.set(xs.concat(ys)).values().sort();
this.config.axis_y_tick_values = extra.names.map(function (name, i) { return i; });
var data_xs = {};
extra.names.forEach(function (name) {
data_xs[name] = name + '_x';
});
var data_columns_xs = Object.keys(data_xs).map(function (key) {
return [data_xs[key]].concat(extra.names.map(function (name, i) { return i; }));
});
var data_columns_values = extra.names.map(function (name, i) {
return [name].concat(extra.names.map(function (name) { return i; }));
});
this.config.data_xs = data_xs;
this.config.data_columns = data_columns_xs.concat(data_columns_values);
var values = {};
this.config.data_pairs.forEach(function (pair) {
if (!pair.x || !pair.y) {
throw "x and y are required in data.";
}
values[extra.getKey(pair.x, pair.y)] = pair.value;
});
extra.values = values;
this.config.axis_x_min = this.config.axis_y_min = -0.5;
this.config.axis_x_max = this.config.axis_y_max = extra.names.length - 0.5;
};
})(window);

1
extensions/chart-bubble/c3.css

@ -0,0 +1 @@
../../c3.css

1
extensions/chart-bubble/c3.min.js vendored

@ -0,0 +1 @@
../../c3.min.js

38
extensions/chart-bubble/index.html

@ -0,0 +1,38 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="c3.css">
</head>
<body>
<div id="chart"></div>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="c3.min.js"></script>
<script src="bubble.js"></script>
<script>
var chart = c3.generate({
data: {
type: 'bubble',
pairs: [
{ x: 'Name_0', y: 'Name_0', value: 10000 },
{ x: 'Name_0', y: 'Name_1', value: 20000 },
{ x: 'Name_0', y: 'Name_2', value: 39990 },
// { x: 'Name_1', y: 'Name_0', value: 5000 },
{ x: 'Name_1', y: 'Name_1', value: 6000 },
{ x: 'Name_1', y: 'Name_2', value: 50000 },
{ x: 'Name_2', y: 'Name_0', value: 1000 },
{ x: 'Name_2', y: 'Name_1', value: 2000 },
{ x: 'Name_2', y: 'Name_2', value: 3000 },
]
},
grid: {
x: {
show: true
},
y: {
show: true
}
},
});
</script>
</body>
</html>

4
htdocs/samples/data_label_format.html

@ -17,8 +17,8 @@
labels: { labels: {
// format: function (v, id) { return "Default Format on " + id; }, // format: function (v, id) { return "Default Format on " + id; },
format: { format: {
y: function (v, id) { return "Y Format on " + id; }, data1: function (v, id) { return "data1 Format"; },
y2: function (v, id) { return "Y2 Format on " + id; } data2: function (v, id) { return "data2 Format"; }
} }
}, },
axes: { axes: {

4
htdocs/samples/timeseries_date.html

@ -14,7 +14,7 @@
x : 'x', x : 'x',
xFormat : '%Y%m%d', xFormat : '%Y%m%d',
columns: [ columns: [
['x', new Date('2013-01-01 00:00:00'), new Date('2013-01-02 00:00:00'), new Date('2013-01-03 00:00:00'), new Date('2013-01-04 00:00:00'), new Date('2013-01-05 00:00:00'), new Date('2013-01-06 00:00:00')], ['x', new Date('2013-01-01T00:00:00Z'), new Date('2013-01-02T00:00:00Z'), new Date('2013-01-03T00:00:00Z'), new Date('2013-01-04T00:00:00Z'), new Date('2013-01-05T00:00:00Z'), new Date('2013-01-06T00:00:00Z')],
['sample', 30, 200, 100, 400, 150, 250], ['sample', 30, 200, 100, 400, 150, 250],
['sample2', 130, 300, 200, 450, 250, 350] ['sample2', 130, 300, 200, 450, 250, 350]
] ]
@ -52,7 +52,7 @@
setTimeout(function () { setTimeout(function () {
chart.load({ chart.load({
columns: [ columns: [
['x', new Date('2014-01-02 00:00:00'), new Date('2014-02-02 00:00:00'), new Date('2014-03-02 00:00:00'), new Date('2014-04-02 00:00:00'), new Date('2014-05-02 00:00:00'), new Date('2014-06-02 00:00:00')], ['x', new Date('2014-01-02T00:00:00Z'), new Date('2014-02-02T00:00:00Z'), new Date('2014-03-02T00:00:00Z'), new Date('2014-04-02T00:00:00Z'), new Date('2014-05-02T00:00:00Z'), new Date('2014-06-02T00:00:00Z')],
['sample', 500, 630, 690, 440, 630, 900], ['sample', 500, 630, 690, 440, 630, 900],
['sample2', 400, 600, 460, 200, 350, 450] ['sample2', 400, 600, 460, 200, 350, 450]
] ]

77
karma.conf.js

@ -0,0 +1,77 @@
// Karma configuration
// Generated on Wed Sep 30 2015 22:01:48 GMT+0900 (KST)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'node_modules/d3/d3.min.js',
'c3.js',
'c3.css',
'spec/*-helper.js',
'spec/*-spec.js'
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'c3.js': ['coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['spec', 'coverage'],
coverageReporter: {
reporters: [{type: 'lcov'}]
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true,
browserNoActivityTimeout: 60000,
})
}

22
package.json

@ -1,10 +1,12 @@
{ {
"name": "c3", "name": "c3",
"version": "0.4.10-rc1", "version": "0.4.11-rc4",
"description": "D3-based reusable chart library", "description": "D3-based reusable chart library",
"main": "c3.js", "main": "c3.js",
"scripts": { "scripts": {
"test": "grunt" "lint": "grunt lint",
"test": "karma start karma.conf.js",
"codecov": "cat coverage/*/lcov.info | codecov"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -20,17 +22,25 @@
"gitHead": "84e03109d9a590f9c8ef687c03d751f666080c6f", "gitHead": "84e03109d9a590f9c8ef687c03d751f666080c6f",
"readmeFilename": "README.md", "readmeFilename": "README.md",
"dependencies": { "dependencies": {
"d3": "<=3.5.0" "d3": "~3.5.0"
}, },
"devDependencies": { "devDependencies": {
"codecov.io": "^0.1.6",
"grunt": "^0.4.5", "grunt": "^0.4.5",
"grunt-contrib-concat": "~0.5.0", "grunt-contrib-concat": "~0.5.0",
"grunt-contrib-cssmin": "^0.10.0", "grunt-contrib-cssmin": "^0.10.0",
"grunt-contrib-jasmine": "~0.8.0",
"grunt-contrib-jshint": "~0.10.0", "grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-uglify": "~0.4.0", "grunt-contrib-uglify": "~0.4.0",
"grunt-contrib-watch": "^0.6.1", "grunt-contrib-watch": "^0.6.1",
"grunt-sass": "^0.17.0", "grunt-karma": "^0.12.1",
"load-grunt-tasks": "~0.2.0" "grunt-sass": "^1.0.0",
"jasmine-core": "^2.3.4",
"karma": "^0.13.10",
"karma-coverage": "^0.5.2",
"karma-jasmine": "^0.3.6",
"karma-phantomjs-launcher": "^0.2.1",
"karma-spec-reporter": "0.0.20",
"load-grunt-tasks": "~0.2.0",
"phantomjs": "^1.9.18"
} }
} }

54
spec/api.axis-spec.js

@ -0,0 +1,54 @@
describe('c3 api axis', function () {
'use strict';
var chart, args;
beforeEach(function (done) {
chart = window.initChart(chart, args, done);
});
describe('axis.labels', function () {
it('should update args', function () {
args = {
data: {
columns: [
['data1', 30, 200, 100],
['data2', 50, 20, 10]
],
axes: {
data1: 'y',
data2: 'y2'
}
},
axis: {
y: {
label: 'Y Axis Label'
},
y2: {
show: true,
label: 'Y2 Axis Label'
}
}
};
expect(true).toBeTruthy();
});
it('should update y axis label', function () {
chart.axis.labels({y: 'New Y Axis Label'});
var label = d3.select('.c3-axis-y-label');
expect(label.text()).toBe('New Y Axis Label');
expect(label.attr('dx')).toBe('-0.5em');
expect(label.attr('dy')).toBe('1.2em');
});
it('should update y axis label', function () {
chart.axis.labels({y2: 'New Y2 Axis Label'});
var label = d3.select('.c3-axis-y2-label');
expect(label.text()).toBe('New Y2 Axis Label');
expect(label.attr('dx')).toBe('-0.5em');
expect(label.attr('dy')).toBe('-0.5em');
});
});
});

4
spec/api.data-spec.js

@ -132,8 +132,8 @@ describe('c3 api data', function () {
it('should set data.colors specified as api', function () { it('should set data.colors specified as api', function () {
expect(d3.select('.c3-line-data1').style('stroke')).toBe("#00ff00"); expect(d3.select('.c3-line-data1').style('stroke')).toBe("#00ff00");
expect(d3.select('.c3-line-data2').style('stroke')).toBe("#ff0000"); 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-data1 .c3-legend-item-tile').style('stroke')).toBe("#00ff00");
expect(d3.select('.c3-legend-item-data2 .c3-legend-item-tile').style('fill')).toBe("#ff0000"); expect(d3.select('.c3-legend-item-data2 .c3-legend-item-tile').style('stroke')).toBe("#ff0000");
}); });
}); });

32
spec/axis-spec.js

@ -151,6 +151,34 @@ describe('c3 chart axis', function () {
}); });
}); });
describe('axis.x.tick.values', function () {
describe('function is provided', function () {
var tickGenerator = function (/*domain*/) {
var values = [];
for (var i = 0; i <= 300; i += 50) {
values.push(i);
}
return values;
};
beforeEach(function () {
args.axis.x = {
tick: {
values: tickGenerator
}
};
chart = window.c3.generate(args);
window.generatedTicks = tickGenerator();
});
it('should use function to generate ticks', function () {
d3.select('.c3-axis-x').selectAll('g.tick').each(function (d, i) {
var tick = d3.select(this).select('text').text();
expect(+tick).toBe(window.generatedTicks[i]);
});
});
});
});
describe('axis.x.tick.width', function () { describe('axis.x.tick.width', function () {
describe('indexed x axis and y/y2 axis', function () { describe('indexed x axis and y/y2 axis', function () {
@ -562,8 +590,10 @@ describe('c3 chart axis', function () {
}); });
it('should have automatically calculated x axis height', function () { it('should have automatically calculated x axis height', function () {
var box = chart.internal.main.select('.c3-axis-x').node().getBoundingClientRect(); var box = chart.internal.main.select('.c3-axis-x').node().getBoundingClientRect(),
height = chart.internal.getHorizontalAxisHeight('x');
expect(box.height).toBeGreaterThan(50); expect(box.height).toBeGreaterThan(50);
expect(height).toBeCloseTo(70, -1);
}); });
}); });

8
spec/core-spec.js

@ -4,6 +4,9 @@ describe('c3 chart', function () {
var chart; var chart;
var args = { var args = {
svg: {
classname: 'customclass'
},
data: { data: {
columns: [ columns: [
['data1', 30, 200, 100, 400, 150, 250], ['data1', 30, 200, 100, 400, 150, 250],
@ -34,6 +37,11 @@ describe('c3 chart', function () {
expect(svg).not.toBeNull(); expect(svg).not.toBeNull();
}); });
it('should be created with a custom class', function () {
var svg = d3.select('#chart svg');
expect(svg.attr('class')).not.toBeNull();
expect(svg.attr('class')).toBe('customclass');
});
}); });
describe('size', function () { describe('size', function () {

301
spec/data-spec.js

@ -7,6 +7,76 @@ describe('c3 chart data', function () {
chart = window.initChart(chart, args, done); chart = window.initChart(chart, args, done);
}); });
describe('load json', function () {
it('should update args', function () {
args = {
data: {
json: {
data1: [30, 20, 50],
data2: [200, 130, 90]
}
}
};
expect(true).toBeTruthy();
});
it('should draw correctly', function () {
var expectedCx = [6, 299, 593],
expectedCy = [370, 390, 331];
d3.selectAll('.c3-circles-data1 .c3-circle').each(function (d, i) {
var circle = d3.select(this);
expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], -2);
expect(+circle.attr('cy')).toBeCloseTo(expectedCy[i], -2);
});
});
it('should update args', function () {
args = {
data: {
json: [{
"date": "2014-06-03",
"443": "3000",
"995": "500"
}, {
"date": "2014-06-04",
"443": "1000"
}, {
"date": "2014-06-05",
"443": "5000",
"995": "1000"
}],
keys: {
x: 'date',
value: [ "443", "995" ]
}
},
axis: {
x: {
type: "category"
}
}
};
expect(true).toBeTruthy();
});
it('should draw correctly', function () {
var expectedCx = {443: [98, 294, 490], 995: [98, 294, 490]},
expectedCy = {443: [193, 351, 36], 995: [390, 429, 351]};
d3.selectAll('.c3-circles-443 .c3-circle').each(function (d, i) {
var circle = d3.select(this);
expect(+circle.attr('cx')).toBeCloseTo(expectedCx[443][i], -2);
expect(+circle.attr('cy')).toBeCloseTo(expectedCy[443][i], -2);
});
d3.selectAll('.c3-circles-995 .c3-circle').each(function (d, i) {
var circle = d3.select(this);
expect(+circle.attr('cx')).toBeCloseTo(expectedCx[995][i], -2);
expect(+circle.attr('cy')).toBeCloseTo(expectedCy[995][i], -2);
});
});
});
describe('function in data.order', function () { describe('function in data.order', function () {
it('should update args', function () { it('should update args', function () {
args = { args = {
@ -239,6 +309,213 @@ describe('c3 chart data', function () {
describe('data.label', function () { describe('data.label', function () {
describe('on line chart', function () {
it('should update args', function () {
args = {
data: {
columns: [
['data1', 1030, 2200, 2100],
['data2', 1150, 2010, 1200],
['data3', -1150, -2010, -1200],
['data4', -1030, -2200, -2100],
],
type: 'line',
labels: true,
}
};
expect(true).toBeTruthy();
});
it('should locate data labels in correct position', function () {
var expectedTextY = {
data1: [128, 38, 46],
data2: [119, 53, 115],
data3: [311, 377, 315],
data4: [302, 392, 384],
};
var expectedTextX = {
data1: [6, 294, 583],
data2: [6, 294, 583],
data3: [6, 294, 583],
data4: [6, 294, 583],
};
Object.keys(expectedTextY).forEach(function (key) {
d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) {
var text = d3.select(this);
expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2);
expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2);
});
});
});
it('should update args to be stacked', function () {
args.data.groups = [['data1', 'data2'], ['data3', 'data4']];
expect(true).toBeTruthy();
});
it('should locate data labels in correct position', function () {
var expectedTextY = {
data1: [120, 38, 75],
data2: [161, 127, 159],
data3: [269, 303, 271],
data4: [310, 392, 355],
};
var expectedTextX = {
data1: [6, 294, 583],
data2: [6, 294, 583],
data3: [6, 294, 583],
data4: [6, 294, 583],
};
Object.keys(expectedTextY).forEach(function (key) {
d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) {
var text = d3.select(this);
expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2);
expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2);
});
});
});
});
describe('on area chart', function () {
it('should update args', function () {
args = {
data: {
columns: [
['data1', 1030, 2200, 2100],
['data2', 1150, 2010, 1200],
['data3', -1150, -2010, -1200],
['data4', -1030, -2200, -2100],
],
type: 'area',
labels: true,
}
};
expect(true).toBeTruthy();
});
it('should locate data labels in correct position', function () {
var expectedTextY = {
data1: [128, 38, 46],
data2: [119, 53, 115],
data3: [311, 377, 315],
data4: [302, 392, 384],
};
var expectedTextX = {
data1: [6, 294, 583],
data2: [6, 294, 583],
data3: [6, 294, 583],
data4: [6, 294, 583],
};
Object.keys(expectedTextY).forEach(function (key) {
d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) {
var text = d3.select(this);
expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2);
expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2);
});
});
});
it('should update args to be stacked', function () {
args.data.groups = [['data1', 'data2'], ['data3', 'data4']];
expect(true).toBeTruthy();
});
it('should locate data labels in correct position', function () {
var expectedTextY = {
data1: [120, 38, 75],
data2: [161, 127, 159],
data3: [269, 303, 271],
data4: [310, 392, 355],
};
var expectedTextX = {
data1: [6, 294, 583],
data2: [6, 294, 583],
data3: [6, 294, 583],
data4: [6, 294, 583],
};
Object.keys(expectedTextY).forEach(function (key) {
d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) {
var text = d3.select(this);
expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2);
expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2);
});
});
});
});
describe('on bar chart', function () {
it('should update args', function () {
args = {
data: {
columns: [
['data1', 1030, 2200, 2100],
['data2', 1150, 2010, 1200],
['data3', -1150, -2010, -1200],
['data4', -1030, -2200, -2100],
],
type: 'bar',
labels: true,
}
};
expect(true).toBeTruthy();
});
it('should locate data labels in correct position', function () {
var expectedTextY = {
data1: [128, 38, 46],
data2: [119, 53, 115],
data3: [311, 377, 315],
data4: [302, 392, 384],
};
var expectedTextX = {
data1: [53, 249, 445],
data2: [83, 279, 475],
data3: [112, 308, 504],
data4: [142, 338, 534],
};
Object.keys(expectedTextY).forEach(function (key) {
d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) {
var text = d3.select(this);
expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2);
expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2);
});
});
});
it('should update args to be stacked', function () {
args.data.groups = [['data1', 'data2'], ['data3', 'data4']];
expect(true).toBeTruthy();
});
it('should locate data labels in correct position', function () {
var expectedTextY = {
data1: [120, 38, 75],
data2: [161, 127, 159],
data3: [269, 303, 271],
data4: [310, 392, 355],
};
var expectedTextX = {
data1: [68.6, 264, 460],
data2: [68.6, 264, 460],
data3: [127, 323, 519],
data4: [127, 323, 519],
};
Object.keys(expectedTextY).forEach(function (key) {
d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) {
var text = d3.select(this);
expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2);
expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2);
});
});
});
});
describe('for all targets', function () { describe('for all targets', function () {
it('should update args to show data label for all data', function () { it('should update args to show data label for all data', function () {
@ -478,7 +755,7 @@ describe('c3 chart data', function () {
}); });
describe('with positive values and null', function () { describe('with negative values and null', function () {
describe('on not rotated axis', function () { describe('on not rotated axis', function () {
@ -486,7 +763,7 @@ describe('c3 chart data', function () {
args = { args = {
data: { data: {
columns: [ columns: [
['data1', -190, -200, -190, null], ['data1', -190, 0, -190, null],
], ],
type: 'bar', type: 'bar',
labels: { labels: {
@ -504,13 +781,13 @@ describe('c3 chart data', function () {
it('should have y domain with proper padding', function () { it('should have y domain with proper padding', function () {
var domain = chart.internal.y.domain(); var domain = chart.internal.y.domain();
expect(domain[0]).toBeCloseTo(-227, -1); expect(domain[0]).toBeCloseTo(-215, -1);
expect(domain[1]).toBeCloseTo(0, -1); expect(domain[1]).toBeCloseTo(0, -1);
}); });
it('should locate labels above each data point', function () { it('should locate labels above each data point', function () {
var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), var texts = chart.internal.main.selectAll('.c3-texts-data1 text'),
expectedYs = [368, 387, 368, 12], expectedYs = [368, 12, 368, 12],
expectedXs = [74, 221, 368, 515]; expectedXs = [74, 221, 368, 515];
texts.each(function (d, i) { texts.each(function (d, i) {
var text = d3.select(this); var text = d3.select(this);
@ -526,13 +803,13 @@ describe('c3 chart data', function () {
it('should have y domain with proper padding', function () { it('should have y domain with proper padding', function () {
var domain = chart.internal.y.domain(); var domain = chart.internal.y.domain();
expect(domain[0]).toBeCloseTo(-201, -1); expect(domain[0]).toBeCloseTo(-215, -1);
expect(domain[1]).toBeCloseTo(-189, -1); expect(domain[1]).toBeCloseTo(25, -1);
}); });
it('should locate labels above each data point', function () { it('should locate labels above each data point', function () {
var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), var texts = chart.internal.main.selectAll('.c3-texts-data1 text'),
expectedYs = [58, 392, 58, 12], expectedYs = [395, 60, 395, 12],
expectedXs = [6, 198, 391, 583]; expectedXs = [6, 198, 391, 583];
texts.each(function (d, i) { texts.each(function (d, i) {
var text = d3.select(this); var text = d3.select(this);
@ -555,14 +832,14 @@ describe('c3 chart data', function () {
it('should have y domain with proper padding', function () { it('should have y domain with proper padding', function () {
var domain = chart.internal.y.domain(); var domain = chart.internal.y.domain();
expect(domain[0]).toBeCloseTo(-232, -1); expect(domain[0]).toBeCloseTo(-220, -1);
expect(domain[1]).toBeCloseTo(0, -1); expect(domain[1]).toBeCloseTo(0, -1);
}); });
it('should locate labels above each data point', function () { it('should locate labels above each data point', function () {
var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), var texts = chart.internal.main.selectAll('.c3-texts-data1 text'),
expectedYs = [57, 163, 269, 375], expectedYs = [57, 163, 269, 375],
expectedXs = [103, 78, 103, 526]; expectedXs = [103, 594, 103, 526];
texts.each(function (d, i) { texts.each(function (d, i) {
var text = d3.select(this); var text = d3.select(this);
expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2); expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2);
@ -577,14 +854,14 @@ describe('c3 chart data', function () {
it('should have y domain with proper padding', function () { it('should have y domain with proper padding', function () {
var domain = chart.internal.y.domain(); var domain = chart.internal.y.domain();
expect(domain[0]).toBeCloseTo(-202, -1); expect(domain[0]).toBeCloseTo(-220, -1);
expect(domain[1]).toBeCloseTo(-188, -1); expect(domain[1]).toBeCloseTo(24, -1);
}); });
it('should locate labels above each data point', function () { it('should locate labels above each data point', function () {
var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), var texts = chart.internal.main.selectAll('.c3-texts-data1 text'),
expectedYs = [9, 147, 286, 424], expectedYs = [9, 147, 286, 424],
expectedXs = [511, 67, 511, 526]; expectedXs = [67, 537, 67, 526];
texts.each(function (d, i) { texts.each(function (d, i) {
var text = d3.select(this); var text = d3.select(this);
expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2); expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2);

23
spec/domain-spec.js

@ -80,6 +80,29 @@ describe('c3 chart domain', function () {
describe('axis.y.padding', function () { describe('axis.y.padding', function () {
it('should change axis.y.max to 1000', function () { it('should change axis.y.max to 1000', function () {
args = {
data: {
columns: [
['data1', 10, 20, 10, 40, 15, 25],
['data2', 50, 40, 30, 45, 25, 45]
]
},
axis: {
y: {
padding: 200,
}
}
};
expect(true).toBeTruthy();
});
it('should be set properly when bigger than min of data', function () {
var domain = chart.internal.y.domain();
expect(domain[0]).toBeCloseTo(-9, -1);
expect(domain[1]).toBeCloseTo(69, -1);
});
it('should change axis.y.max to 1000 with top/bottom padding', function () {
args = { args = {
data: { data: {
columns: [ columns: [

12
spec/grid-spec.js

@ -91,9 +91,9 @@ describe('c3 chart grid', function () {
grid: { grid: {
y: { y: {
lines: [ lines: [
{value: 30, text: 'Lable 30', position: 'start'}, {value: 30, text: 'Label 30', position: 'start'},
{value: 145, text: 'Lable 145', position: 'middle'}, {value: 145, text: 'Label 145', position: 'middle'},
{value: 225, text: 'Lable 225'} {value: 225, text: 'Label 225'}
] ]
} }
} }
@ -140,9 +140,9 @@ describe('c3 chart grid', function () {
grid: { grid: {
y: { y: {
lines: [ lines: [
{value: 30, text: 'Lable 30', position: 'start'}, {value: 30, text: 'Label 30', position: 'start'},
{value: 145, text: 'Lable 145', position: 'middle'}, {value: 145, text: 'Label 145', position: 'middle'},
{value: 225, text: 'Lable 225'} {value: 225, text: 'Label 225'}
] ]
} }
} }

97
spec/legend-spec.js

@ -7,6 +7,45 @@ describe('c3 chart legend', function () {
chart = window.initChart(chart, args, done); chart = window.initChart(chart, args, done);
}); });
describe('legend when multiple charts rendered', function () {
it('should update args', function () {
args = {
data: {
columns: [
['data1', 30],
['data2', 50],
['data3', 100]
]
}
};
expect(true).toBeTruthy();
});
it('should update args with long data names', function () {
args = {
data: {
columns: [
['long data name 1', 30],
['long data name 2', 50],
['long data name 3', 50],
]
}
};
expect(true).toBeTruthy();
});
it('should have properly computed legend width', function () {
var expectedLeft = [148, 226, 384],
expectedWidth = [118, 118, 108];
d3.selectAll('.c3-legend-item').each(function (d, i) {
var rect = d3.select(this).node().getBoundingClientRect();
expect(rect.left).toBeCloseTo(expectedLeft[i], -2);
expect(rect.width).toBeCloseTo(expectedWidth[i], -2);
});
});
});
describe('legend position', function () { describe('legend position', function () {
it('should update args', function () { it('should update args', function () {
@ -178,4 +217,62 @@ describe('c3 chart legend', function () {
}); });
describe('custom legend size', function() {
it('should update args', function () {
args = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 130, 100, 200, 100, 250, 150]
]
},
legend: {
item: {
tile: {
width: 15,
height: 2
}
}
}
};
expect(true).toBeTruthy();
});
it('renders the legend item with the correct width and height', function () {
d3.selectAll('.c3-legend-item-tile').each(function () {
expect(d3.select(this).style('stroke-width')).toBe(args.legend.item.tile.height + 'px');
var tileWidth = d3.select(this).attr('x2') - d3.select(this).attr('x1');
expect(tileWidth).toBe(args.legend.item.tile.width);
});
});
});
describe('custom legend padding', function() {
it('should update args', function () {
args = {
data: {
columns: [
['padded1', 30, 200, 100, 400, 150, 250],
['padded2', 130, 100, 200, 100, 250, 150]
]
},
legend: {
padding: 10
}
};
expect(true).toBeTruthy();
});
it('renders the correct amount of padding on the legend element', function () {
d3.selectAll('.c3-legend-item-padded1 .c3-legend-item-tile, .c3-legend-item-padded2 .c3-legend-item-tile').each(function (el, index) {
var itemWidth = d3.select(this).node().parentNode.getBBox().width,
textBoxWidth = d3.select(d3.select(this).node().parentNode).select('text').node().getBBox().width,
tileWidth = 15, // default value is 10, plus 5 more for padding
expectedWidth = textBoxWidth + tileWidth + (index ? 0 : 10) + args.legend.padding;
expect(itemWidth).toBe(expectedWidth);
});
});
});
}); });

93
spec/shape.bar-spec.js

@ -9,6 +9,99 @@ describe('c3 chart shape bar', function () {
chart = window.initChart(chart, args, done); chart = window.initChart(chart, args, done);
}); });
describe('with groups', function () {
describe('with indexed data', function () {
it('should update args', function () {
args = {
data: {
columns: [
['data1', 30, 200, -100, 400, -150, 250],
['data2', 50, 20, 10, 40, 15, 25],
],
groups: [
['data1', 'data2'],
],
type: 'bar'
},
};
expect(true).toBeTruthy();
});
it('should be stacked', function () {
var expectedBottom = [275, 293, 365, 281, 395, 290];
chart.internal.main.selectAll('.c3-bars-data1 .c3-bar').each(function (d, i) {
var rect = d3.select(this).node().getBoundingClientRect();
expect(rect.bottom).toBeCloseTo(expectedBottom[i], -1);
});
});
});
describe('with timeseries data', function () {
it('should update args', function () {
args = {
data: {
x: 'date',
columns: [
['date', '2012-12-24', '2012-12-25', '2012-12-26', '2012-12-27', '2012-12-28', '2012-12-29'],
['data1', 30, 200, -100, 400, -150, 250],
['data2', 50, 20, 10, 40, 15, 25],
],
groups: [
['data1', 'data2'],
],
type: 'bar'
},
axis: {
x: {
type: 'timeseries',
}
}
};
expect(true).toBeTruthy();
});
it('should be stacked', function () {
var expectedBottom = [275, 293, 365, 281, 395, 290];
chart.internal.main.selectAll('.c3-bars-data1 .c3-bar').each(function (d, i) {
var rect = d3.select(this).node().getBoundingClientRect();
expect(rect.bottom).toBeCloseTo(expectedBottom[i], -1);
});
});
});
describe('with category data', function () {
it('should update args', function () {
args = {
data: {
x: 'date',
columns: [
['date', '2012-12-24', '2012-12-25', '2012-12-26', '2012-12-27', '2012-12-28', '2012-12-29'],
['data1', 30, 200, -100, 400, -150, 250],
['data2', 50, 20, 10, 40, 15, 25],
],
groups: [
['data1', 'data2'],
],
type: 'bar'
},
axis: {
x: {
type: 'category',
}
}
};
expect(true).toBeTruthy();
});
it('should be stacked', function () {
var expectedBottom = [275, 293, 365, 281, 395, 290];
chart.internal.main.selectAll('.c3-bars-data1 .c3-bar').each(function (d, i) {
var rect = d3.select(this).node().getBoundingClientRect();
expect(rect.bottom).toBeCloseTo(expectedBottom[i], -1);
});
});
});
});
describe('internal.isWithinBar', function () { describe('internal.isWithinBar', function () {
describe('with normal axis', function () { describe('with normal axis', function () {

47
spec/shape.line-spec.js

@ -30,7 +30,7 @@ describe('c3 chart shape line', function () {
}); });
}); });
it('should chnage to step chart', function () { it('should change to step chart', function () {
args.data.type = 'step'; args.data.type = 'step';
expect(true).toBeTruthy(); expect(true).toBeTruthy();
}); });
@ -42,6 +42,15 @@ describe('c3 chart shape line', function () {
}); });
}); });
it('should change to spline chart', function () {
args.data.type = 'spline';
expect(true).toBeTruthy();
});
it('should use cardinal interpolation by default', function () {
expect(chart.internal.config.spline_interpolation_type).toBe('cardinal');
});
}); });
describe('point.show option', function () { describe('point.show option', function () {
@ -96,4 +105,40 @@ describe('c3 chart shape line', function () {
}); });
describe('spline.interpolation option', function () {
it('should update args', 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]
],
type: 'spline'
},
spline: {
interpolation: {
type: 'monotone'
}
}
};
expect(true).toBeTruthy();
});
it('should update interpolation function', function() {
expect(chart.internal.getInterpolate(chart.data()[0])).toBe('monotone');
});
it('should not use a non-valid interpolation', function () {
args.spline.interpolation.type = 'foo';
expect(true).toBeTruthy();
});
it('should use cardinal interpolation when given option is not valid', function() {
expect(chart.internal.getInterpolate(chart.data()[0])).toBe('cardinal');
});
});
}); });

98
spec/title-spec.js

@ -0,0 +1,98 @@
describe('c3 chart title', function () {
'use strict';
var chart, config;
describe('when given a title config option', function () {
describe('with no padding and no position', function () {
beforeEach(function(done) {
config = {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250]
]
},
title: {
text: 'new title'
}
};
chart = window.initChart(chart, config, done);
});
it('renders the title at the default config position', function () {
var titleEl = d3.select(".c3-title");
expect(+titleEl.attr("x")).toBeCloseTo(294, -2);
expect(+titleEl.attr("y")).toEqual(titleEl.node().getBBox().height);
});
it('renders the title text', function () {
var titleEl = d3.select(".c3-title");
expect(titleEl.node().textContent).toEqual('new title');
});
});
describe('with padding', function () {
var config, getConfig = function (titlePosition) {
return {
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250]
]
},
title: {
text: 'positioned title',
padding: {
top: 20,
right: 30,
bottom: 40,
left: 50
},
position: titlePosition
}
};
};
describe('and position center', function () {
beforeEach(function(done) {
config = getConfig('top-center');
chart = window.initChart(chart, config, done);
});
it('renders the title at the default config position', function () {
var titleEl = d3.select(".c3-title");
expect(+titleEl.attr("x")).toBeCloseTo(275, -2);
expect(+titleEl.attr("y")).toBeCloseTo(34, -1);
});
it('adds the correct amount of padding to fit the title', function() {
expect(chart.internal.getCurrentPaddingTop()).toEqual(
config.title.padding.top + d3.select('.c3-title').node().getBBox().height + config.title.padding.bottom
);
});
});
describe('and position left', function () {
beforeEach(function(done) {
config = getConfig('top-left');
chart = window.initChart(chart, config, done);
});
it('renders the title at the default config position', function () {
var titleEl = d3.select(".c3-title");
expect(+titleEl.attr("x")).toBeCloseTo(50, -1);
expect(+titleEl.attr("y")).toBeCloseTo(34, -1);
});
});
describe('and position right', function () {
beforeEach(function(done) {
config = getConfig('top-right');
chart = window.initChart(chart, config, done);
});
it('renders the title at the default config position', function () {
var titleEl = d3.select(".c3-title");
expect(+titleEl.attr("x")).toBeCloseTo(520, -2);
expect(+titleEl.attr("y")).toBeCloseTo(34, -1);
});
});
});
});
});

21
spec/tooltip-spec.js

@ -98,4 +98,25 @@ describe('c3 chart tooltip', function () {
expect(left).toBe(leftExpected); expect(left).toBe(leftExpected);
}); });
}); });
describe('tooltip getTooltipContent', function () {
beforeAll(function () {
tooltipConfiguration = {
data_order: 'desc'
};
});
it('should sort values desc', function () {
var eventRect = d3.select('.c3-event-rect-2').node();
window.setMouseEvent(chart, 'mousemove', 100, 100, eventRect);
var tooltipTable = d3.select('.c3-tooltip')[0];
var expected = ["", "c3-tooltip-name--data3",
"c3-tooltip-name--data1", "c3-tooltip-name--data2"];
var i;
for (i = 0; i < tooltipTable[0].rows.length; i++) {
expect(tooltipTable[0].rows[i].className).toBe(expected[i]);
}
});
});
}); });

4
src/api.axis.js

@ -3,9 +3,9 @@ c3_chart_fn.axis.labels = function (labels) {
var $$ = this.internal; var $$ = this.internal;
if (arguments.length) { if (arguments.length) {
Object.keys(labels).forEach(function (axisId) { Object.keys(labels).forEach(function (axisId) {
$$.setAxisLabelText(axisId, labels[axisId]); $$.axis.setLabelText(axisId, labels[axisId]);
}); });
$$.updateAxisLabels(); $$.axis.updateLabels();
} }
// TODO: return some values? // TODO: return some values?
}; };

17
src/api.chart.js

@ -14,7 +14,22 @@ c3_chart_fn.destroy = function () {
var $$ = this.internal; var $$ = this.internal;
window.clearInterval($$.intervalForObserveInserted); window.clearInterval($$.intervalForObserveInserted);
window.onresize = null;
if ($$.resizeTimeout !== undefined) {
window.clearTimeout($$.resizeTimeout);
}
if (window.detachEvent) {
window.detachEvent('onresize', $$.resizeFunction);
} else if (window.removeEventListener) {
window.removeEventListener('resize', $$.resizeFunction);
} else {
var wrapper = window.onresize;
// check if no one else removed our wrapper and remove our resizeFunction from it
if (wrapper && wrapper.add && wrapper.remove) {
wrapper.remove($$.resizeFunction);
}
}
$$.selectChart.classed('c3', false).html(""); $$.selectChart.classed('c3', false).html("");

2
src/api.flow.js

@ -214,9 +214,7 @@ c3_chart_internal_fn.generateFlow = function (args) {
scaleX = (diffDomain(orgDomain) / diffDomain(domain)); scaleX = (diffDomain(orgDomain) / diffDomain(domain));
transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)'; transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)';
// hide tooltip
$$.hideXGridFocus(); $$.hideXGridFocus();
$$.hideTooltip();
d3.transition().ease('linear').duration(durationForFlow).each(function () { d3.transition().ease('linear').duration(durationForFlow).each(function () {
wait.add($$.axes.x.transition().call($$.xAxis)); wait.add($$.axes.x.transition().call($$.xAxis));

10
src/api.load.js

@ -4,6 +4,10 @@ c3_chart_fn.load = function (args) {
if (args.xs) { if (args.xs) {
$$.addXs(args.xs); $$.addXs(args.xs);
} }
// update names if exists
if ('names' in args) {
c3_chart_fn.data.names.bind(this)(args.names);
}
// update classes if exists // update classes if exists
if ('classes' in args) { if ('classes' in args) {
Object.keys(args.classes).forEach(function (id) { Object.keys(args.classes).forEach(function (id) {
@ -20,6 +24,12 @@ c3_chart_fn.load = function (args) {
config.data_axes[id] = args.axes[id]; config.data_axes[id] = args.axes[id];
}); });
} }
// update colors if exists
if ('colors' in args) {
Object.keys(args.colors).forEach(function (id) {
config.data_colors[id] = args.colors[id];
});
}
// use cache if exists // use cache if exists
if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) {
$$.load($$.getCaches(args.cacheIds), args.done); $$.load($$.getCaches(args.cacheIds), args.done);

4
src/api.tooltip.js

@ -28,8 +28,12 @@ c3_chart_fn.tooltip.show = function (args) {
// emulate mouse events to show // emulate mouse events to show
$$.dispatchEvent('mouseover', index, mouse); $$.dispatchEvent('mouseover', index, mouse);
$$.dispatchEvent('mousemove', index, mouse); $$.dispatchEvent('mousemove', index, mouse);
$$.config.tooltip_onshow.call($$, args.data);
}; };
c3_chart_fn.tooltip.hide = function () { c3_chart_fn.tooltip.hide = function () {
// TODO: get target data by checking the state of focus // TODO: get target data by checking the state of focus
this.internal.dispatchEvent('mouseout', 0); this.internal.dispatchEvent('mouseout', 0);
this.internal.config.tooltip_onhide.call(this);
}; };

32
src/api.zoom.js

@ -20,3 +20,35 @@ c3_chart_fn.unzoom = function () {
$$.brush.clear().update(); $$.brush.clear().update();
$$.redraw({withUpdateXDomain: true}); $$.redraw({withUpdateXDomain: true});
}; };
c3_chart_fn.zoom.max = function (max) {
var $$ = this.internal, config = $$.config, d3 = $$.d3;
if (max === 0 || max) {
config.zoom_x_max = d3.max([$$.orgXDomain[1], max]);
}
else {
return config.zoom_x_max;
}
};
c3_chart_fn.zoom.min = function (min) {
var $$ = this.internal, config = $$.config, d3 = $$.d3;
if (min === 0 || min) {
config.zoom_x_min = d3.min([$$.orgXDomain[0], min]);
}
else {
return config.zoom_x_min;
}
};
c3_chart_fn.zoom.range = function (range) {
if (arguments.length) {
if (isDefined(range.max)) { this.domain.max(range.max); }
if (isDefined(range.min)) { this.domain.min(range.min); }
} else {
return {
max: this.domain.max(),
min: this.domain.min()
};
}
};

82
src/arc.js

@ -27,7 +27,12 @@ c3_chart_internal_fn.updateArc = function () {
c3_chart_internal_fn.updateAngle = function (d) { c3_chart_internal_fn.updateAngle = function (d) {
var $$ = this, config = $$.config, var $$ = this, config = $$.config,
found = false, index = 0, found = false, index = 0,
gMin = config.gauge_min, gMax = config.gauge_max, gTic, gValue; gMin, gMax, gTic, gValue;
if (!config) {
return null;
}
$$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) { $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) {
if (! found && t.data.id === d.data.id) { if (! found && t.data.id === d.data.id) {
found = true; found = true;
@ -43,6 +48,8 @@ c3_chart_internal_fn.updateAngle = function (d) {
d.endAngle = d.startAngle; d.endAngle = d.startAngle;
} }
if ($$.isGaugeType(d.data)) { if ($$.isGaugeType(d.data)) {
gMin = config.gauge_min;
gMax = config.gauge_max;
gTic = (Math.PI) / (gMax - gMin); gTic = (Math.PI) / (gMax - gMin);
gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : (gMax - gMin); gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : (gMax - gMin);
d.startAngle = -1 * (Math.PI / 2); d.startAngle = -1 * (Math.PI / 2);
@ -143,9 +150,9 @@ c3_chart_internal_fn.expandArc = function (targetIds) {
$$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) { $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) {
if (! $$.shouldExpand(d.data.id)) { return; } if (! $$.shouldExpand(d.data.id)) { return; }
$$.d3.select(this).selectAll('path') $$.d3.select(this).selectAll('path')
.transition().duration(50) .transition().duration($$.expandDuration(d.data.id))
.attr("d", $$.svgArcExpanded) .attr("d", $$.svgArcExpanded)
.transition().duration(100) .transition().duration($$.expandDuration(d.data.id) * 2)
.attr("d", $$.svgArcExpandedSub) .attr("d", $$.svgArcExpandedSub)
.each(function (d) { .each(function (d) {
if ($$.isDonutType(d.data)) { if ($$.isDonutType(d.data)) {
@ -163,15 +170,34 @@ c3_chart_internal_fn.unexpandArc = function (targetIds) {
targetIds = $$.mapToTargetIds(targetIds); targetIds = $$.mapToTargetIds(targetIds);
$$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).selectAll('path') $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).selectAll('path')
.transition().duration(50) .transition().duration(function(d) {
return $$.expandDuration(d.data.id);
})
.attr("d", $$.svgArc); .attr("d", $$.svgArc);
$$.svg.selectAll('.' + CLASS.arc) $$.svg.selectAll('.' + CLASS.arc)
.style("opacity", 1); .style("opacity", 1);
}; };
c3_chart_internal_fn.expandDuration = function (id) {
var $$ = this, config = $$.config;
if ($$.isDonutType(id)) {
return config.donut_expand_duration;
} else if ($$.isGaugeType(id)) {
return config.gauge_expand_duration;
} else if ($$.isPieType(id)) {
return config.pie_expand_duration;
} else {
return 50;
}
};
c3_chart_internal_fn.shouldExpand = function (id) { c3_chart_internal_fn.shouldExpand = function (id) {
var $$ = this, config = $$.config; var $$ = this, config = $$.config;
return ($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand); return ($$.isDonutType(id) && config.donut_expand) ||
($$.isGaugeType(id) && config.gauge_expand) ||
($$.isPieType(id) && config.pie_expand);
}; };
c3_chart_internal_fn.shouldShowArcLabel = function () { c3_chart_internal_fn.shouldShowArcLabel = function () {
@ -265,18 +291,22 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf
return; return;
} }
updated = $$.updateAngle(d); updated = $$.updateAngle(d);
arcData = $$.convertToArcData(updated); if (updated) {
// transitions arcData = $$.convertToArcData(updated);
$$.expandArc(updated.data.id); // transitions
$$.api.focus(updated.data.id); $$.expandArc(updated.data.id);
$$.toggleFocusLegend(updated.data.id, true); $$.api.focus(updated.data.id);
$$.config.data_onmouseover(arcData, this); $$.toggleFocusLegend(updated.data.id, true);
$$.config.data_onmouseover(arcData, this);
}
} : null) } : null)
.on('mousemove', config.interaction_enabled ? function (d) { .on('mousemove', config.interaction_enabled ? function (d) {
var updated = $$.updateAngle(d), var updated = $$.updateAngle(d), arcData, selectedData;
if (updated) {
arcData = $$.convertToArcData(updated), arcData = $$.convertToArcData(updated),
selectedData = [arcData]; selectedData = [arcData];
$$.showTooltip(selectedData, this); $$.showTooltip(selectedData, this);
}
} : null) } : null)
.on('mouseout', config.interaction_enabled ? function (d) { .on('mouseout', config.interaction_enabled ? function (d) {
var updated, arcData; var updated, arcData;
@ -284,19 +314,25 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf
return; return;
} }
updated = $$.updateAngle(d); updated = $$.updateAngle(d);
arcData = $$.convertToArcData(updated); if (updated) {
// transitions arcData = $$.convertToArcData(updated);
$$.unexpandArc(updated.data.id); // transitions
$$.api.revert(); $$.unexpandArc(updated.data.id);
$$.revertLegend(); $$.api.revert();
$$.hideTooltip(); $$.revertLegend();
$$.config.data_onmouseout(arcData, this); $$.hideTooltip();
$$.config.data_onmouseout(arcData, this);
}
} : null) } : null)
.on('click', config.interaction_enabled ? function (d, i) { .on('click', config.interaction_enabled ? function (d, i) {
var updated = $$.updateAngle(d), var updated = $$.updateAngle(d), arcData;
if (updated) {
arcData = $$.convertToArcData(updated); arcData = $$.convertToArcData(updated);
if ($$.toggleShape) { $$.toggleShape(this, arcData, i); } if ($$.toggleShape) {
$$.config.data_onclick.call($$.api, arcData, this); $$.toggleShape(this, arcData, i);
}
$$.config.data_onclick.call($$.api, arcData, this);
}
} : null) } : null)
.each(function () { $$.transiting = true; }) .each(function () { $$.transiting = true; })
.transition().duration(duration) .transition().duration(duration)

298
src/axis.js

@ -1,5 +1,12 @@
c3_chart_internal_fn.initAxis = function () { function Axis(owner) {
var $$ = this, config = $$.config, main = $$.main; API.call(this, owner);
}
inherit(API, Axis);
Axis.prototype.init = function init() {
var $$ = this.owner, config = $$.config, main = $$.main;
$$.axes.x = main.append("g") $$.axes.x = main.append("g")
.attr("class", CLASS.axis + ' ' + CLASS.axisX) .attr("class", CLASS.axis + ' ' + CLASS.axisX)
.attr("clip-path", $$.clipPathForXAxis) .attr("clip-path", $$.clipPathForXAxis)
@ -8,8 +15,7 @@ c3_chart_internal_fn.initAxis = function () {
$$.axes.x.append("text") $$.axes.x.append("text")
.attr("class", CLASS.axisXLabel) .attr("class", CLASS.axisXLabel)
.attr("transform", config.axis_rotated ? "rotate(-90)" : "") .attr("transform", config.axis_rotated ? "rotate(-90)" : "")
.style("text-anchor", $$.textAnchorForXAxisLabel.bind($$)); .style("text-anchor", this.textAnchorForXAxisLabel.bind(this));
$$.axes.y = main.append("g") $$.axes.y = main.append("g")
.attr("class", CLASS.axis + ' ' + CLASS.axisY) .attr("class", CLASS.axis + ' ' + CLASS.axisY)
.attr("clip-path", config.axis_y_inner ? "" : $$.clipPathForYAxis) .attr("clip-path", config.axis_y_inner ? "" : $$.clipPathForYAxis)
@ -18,7 +24,7 @@ c3_chart_internal_fn.initAxis = function () {
$$.axes.y.append("text") $$.axes.y.append("text")
.attr("class", CLASS.axisYLabel) .attr("class", CLASS.axisYLabel)
.attr("transform", config.axis_rotated ? "" : "rotate(-90)") .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
.style("text-anchor", $$.textAnchorForYAxisLabel.bind($$)); .style("text-anchor", this.textAnchorForYAxisLabel.bind(this));
$$.axes.y2 = main.append("g") $$.axes.y2 = main.append("g")
.attr("class", CLASS.axis + ' ' + CLASS.axisY2) .attr("class", CLASS.axis + ' ' + CLASS.axisY2)
@ -28,20 +34,21 @@ c3_chart_internal_fn.initAxis = function () {
$$.axes.y2.append("text") $$.axes.y2.append("text")
.attr("class", CLASS.axisY2Label) .attr("class", CLASS.axisY2Label)
.attr("transform", config.axis_rotated ? "" : "rotate(-90)") .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
.style("text-anchor", $$.textAnchorForY2AxisLabel.bind($$)); .style("text-anchor", this.textAnchorForY2AxisLabel.bind(this));
}; };
c3_chart_internal_fn.getXAxis = function (scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition) { Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) {
var $$ = this, config = $$.config, var $$ = this.owner, config = $$.config,
axisParams = { axisParams = {
isCategory: $$.isCategorized(), isCategory: $$.isCategorized(),
withOuterTick: withOuterTick, withOuterTick: withOuterTick,
tickMultiline: config.axis_x_tick_multiline, tickMultiline: config.axis_x_tick_multiline,
tickWidth: config.axis_x_tick_width, tickWidth: config.axis_x_tick_width,
tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate,
withoutTransition: withoutTransition, withoutTransition: withoutTransition,
}, },
axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient); axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient);
if ($$.isTimeSeries() && tickValues) { if ($$.isTimeSeries() && tickValues && typeof tickValues !== "function") {
tickValues = tickValues.map(function (v) { return $$.parseDate(v); }); tickValues = tickValues.map(function (v) { return $$.parseDate(v); });
} }
@ -56,10 +63,10 @@ c3_chart_internal_fn.getXAxis = function (scale, orient, tickFormat, tickValues,
return axis; return axis;
}; };
c3_chart_internal_fn.updateXAxisTickValues = function (targets, axis) { Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) {
var $$ = this, config = $$.config, tickValues; var $$ = this.owner, config = $$.config, tickValues;
if (config.axis_x_tick_fit || config.axis_x_tick_count) { if (config.axis_x_tick_fit || config.axis_x_tick_count) {
tickValues = $$.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries()); tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries());
} }
if (axis) { if (axis) {
axis.tickValues(tickValues); axis.tickValues(tickValues);
@ -69,22 +76,28 @@ c3_chart_internal_fn.updateXAxisTickValues = function (targets, axis) {
} }
return tickValues; return tickValues;
}; };
c3_chart_internal_fn.getYAxis = function (scale, orient, tickFormat, tickValues, withOuterTick) { Axis.prototype.getYAxis = function getYAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition) {
var axisParams = {withOuterTick: withOuterTick}, var axisParams = {
axis = c3_axis(this.d3, axisParams).scale(scale).orient(orient).tickFormat(tickFormat); withOuterTick: withOuterTick,
if (this.isTimeSeriesY()) { withoutTransition: withoutTransition,
axis.ticks(this.d3.time[this.config.axis_y_tick_time_value], this.config.axis_y_tick_time_interval); },
$$ = this.owner,
d3 = $$.d3,
config = $$.config,
axis = c3_axis(d3, axisParams).scale(scale).orient(orient).tickFormat(tickFormat);
if ($$.isTimeSeriesY()) {
axis.ticks(d3.time[config.axis_y_tick_time_value], config.axis_y_tick_time_interval);
} else { } else {
axis.tickValues(tickValues); axis.tickValues(tickValues);
} }
return axis; return axis;
}; };
c3_chart_internal_fn.getAxisId = function (id) { Axis.prototype.getId = function getId(id) {
var config = this.config; var config = this.owner.config;
return id in config.data_axes ? config.data_axes[id] : 'y'; return id in config.data_axes ? config.data_axes[id] : 'y';
}; };
c3_chart_internal_fn.getXAxisTickFormat = function () { Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() {
var $$ = this, config = $$.config, var $$ = this.owner, config = $$.config,
format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { return v < 0 ? v.toFixed(0) : v; }; format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { return v < 0 ? v.toFixed(0) : v; };
if (config.axis_x_tick_format) { if (config.axis_x_tick_format) {
if (isFunction(config.axis_x_tick_format)) { if (isFunction(config.axis_x_tick_format)) {
@ -97,20 +110,20 @@ c3_chart_internal_fn.getXAxisTickFormat = function () {
} }
return isFunction(format) ? function (v) { return format.call($$, v); } : format; return isFunction(format) ? function (v) { return format.call($$, v); } : format;
}; };
c3_chart_internal_fn.getAxisTickValues = function (tickValues, axis) { Axis.prototype.getTickValues = function getTickValues(tickValues, axis) {
return tickValues ? tickValues : axis ? axis.tickValues() : undefined; return tickValues ? tickValues : axis ? axis.tickValues() : undefined;
}; };
c3_chart_internal_fn.getXAxisTickValues = function () { Axis.prototype.getXAxisTickValues = function getXAxisTickValues() {
return this.getAxisTickValues(this.config.axis_x_tick_values, this.xAxis); return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis);
}; };
c3_chart_internal_fn.getYAxisTickValues = function () { Axis.prototype.getYAxisTickValues = function getYAxisTickValues() {
return this.getAxisTickValues(this.config.axis_y_tick_values, this.yAxis); return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis);
}; };
c3_chart_internal_fn.getY2AxisTickValues = function () { Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() {
return this.getAxisTickValues(this.config.axis_y2_tick_values, this.y2Axis); return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis);
}; };
c3_chart_internal_fn.getAxisLabelOptionByAxisId = function (axisId) { Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) {
var $$ = this, config = $$.config, option; var $$ = this.owner, config = $$.config, option;
if (axisId === 'y') { if (axisId === 'y') {
option = config.axis_y_label; option = config.axis_y_label;
} else if (axisId === 'y2') { } else if (axisId === 'y2') {
@ -120,13 +133,13 @@ c3_chart_internal_fn.getAxisLabelOptionByAxisId = function (axisId) {
} }
return option; return option;
}; };
c3_chart_internal_fn.getAxisLabelText = function (axisId) { Axis.prototype.getLabelText = function getLabelText(axisId) {
var option = this.getAxisLabelOptionByAxisId(axisId); var option = this.getLabelOptionByAxisId(axisId);
return isString(option) ? option : option ? option.text : null; return isString(option) ? option : option ? option.text : null;
}; };
c3_chart_internal_fn.setAxisLabelText = function (axisId, text) { Axis.prototype.setLabelText = function setLabelText(axisId, text) {
var $$ = this, config = $$.config, var $$ = this.owner, config = $$.config,
option = $$.getAxisLabelOptionByAxisId(axisId); option = this.getLabelOptionByAxisId(axisId);
if (isString(option)) { if (isString(option)) {
if (axisId === 'y') { if (axisId === 'y') {
config.axis_y_label = text; config.axis_y_label = text;
@ -139,8 +152,8 @@ c3_chart_internal_fn.setAxisLabelText = function (axisId, text) {
option.text = text; option.text = text;
} }
}; };
c3_chart_internal_fn.getAxisLabelPosition = function (axisId, defaultPosition) { Axis.prototype.getLabelPosition = function getLabelPosition(axisId, defaultPosition) {
var option = this.getAxisLabelOptionByAxisId(axisId), var option = this.getLabelOptionByAxisId(axisId),
position = (option && typeof option === 'object' && option.position) ? option.position : defaultPosition; position = (option && typeof option === 'object' && option.position) ? option.position : defaultPosition;
return { return {
isInner: position.indexOf('inner') >= 0, isInner: position.indexOf('inner') >= 0,
@ -153,131 +166,109 @@ c3_chart_internal_fn.getAxisLabelPosition = function (axisId, defaultPosition) {
isBottom: position.indexOf('bottom') >= 0 isBottom: position.indexOf('bottom') >= 0
}; };
}; };
c3_chart_internal_fn.getXAxisLabelPosition = function () { Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() {
return this.getAxisLabelPosition('x', this.config.axis_rotated ? 'inner-top' : 'inner-right'); return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right');
}; };
c3_chart_internal_fn.getYAxisLabelPosition = function () { Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() {
return this.getAxisLabelPosition('y', this.config.axis_rotated ? 'inner-right' : 'inner-top'); return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
}; };
c3_chart_internal_fn.getY2AxisLabelPosition = function () { Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() {
return this.getAxisLabelPosition('y2', this.config.axis_rotated ? 'inner-right' : 'inner-top'); return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
}; };
c3_chart_internal_fn.getAxisLabelPositionById = function (id) { Axis.prototype.getLabelPositionById = function getLabelPositionById(id) {
return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition(); return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition();
}; };
c3_chart_internal_fn.textForXAxisLabel = function () { Axis.prototype.textForXAxisLabel = function textForXAxisLabel() {
return this.getAxisLabelText('x'); return this.getLabelText('x');
}; };
c3_chart_internal_fn.textForYAxisLabel = function () { Axis.prototype.textForYAxisLabel = function textForYAxisLabel() {
return this.getAxisLabelText('y'); return this.getLabelText('y');
}; };
c3_chart_internal_fn.textForY2AxisLabel = function () { Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() {
return this.getAxisLabelText('y2'); return this.getLabelText('y2');
}; };
c3_chart_internal_fn.xForAxisLabel = function (forHorizontal, position) { Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) {
var $$ = this; var $$ = this.owner;
if (forHorizontal) { if (forHorizontal) {
return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width; return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width;
} else { } else {
return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0; return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0;
} }
}; };
c3_chart_internal_fn.dxForAxisLabel = function (forHorizontal, position) { Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) {
if (forHorizontal) { if (forHorizontal) {
return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0"; return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0";
} else { } else {
return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0"; return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0";
} }
}; };
c3_chart_internal_fn.textAnchorForAxisLabel = function (forHorizontal, position) { Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) {
if (forHorizontal) { if (forHorizontal) {
return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end'; return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end';
} else { } else {
return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end'; return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end';
} }
}; };
c3_chart_internal_fn.xForXAxisLabel = function () { Axis.prototype.xForXAxisLabel = function xForXAxisLabel() {
return this.xForAxisLabel(!this.config.axis_rotated, this.getXAxisLabelPosition()); return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
}; };
c3_chart_internal_fn.xForYAxisLabel = function () { Axis.prototype.xForYAxisLabel = function xForYAxisLabel() {
return this.xForAxisLabel(this.config.axis_rotated, this.getYAxisLabelPosition()); return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
}; };
c3_chart_internal_fn.xForY2AxisLabel = function () { Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() {
return this.xForAxisLabel(this.config.axis_rotated, this.getY2AxisLabelPosition()); return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
}; };
c3_chart_internal_fn.dxForXAxisLabel = function () { Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() {
return this.dxForAxisLabel(!this.config.axis_rotated, this.getXAxisLabelPosition()); return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
}; };
c3_chart_internal_fn.dxForYAxisLabel = function () { Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() {
return this.dxForAxisLabel(this.config.axis_rotated, this.getYAxisLabelPosition()); return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
}; };
c3_chart_internal_fn.dxForY2AxisLabel = function () { Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() {
return this.dxForAxisLabel(this.config.axis_rotated, this.getY2AxisLabelPosition()); return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
}; };
c3_chart_internal_fn.dyForXAxisLabel = function () { Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() {
var $$ = this, config = $$.config, var $$ = this.owner, config = $$.config,
position = $$.getXAxisLabelPosition(); position = this.getXAxisLabelPosition();
if (config.axis_rotated) { if (config.axis_rotated) {
return position.isInner ? "1.2em" : -25 - $$.getMaxTickWidth('x'); return position.isInner ? "1.2em" : -25 - this.getMaxTickWidth('x');
} else { } else {
return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em"; return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em";
} }
}; };
c3_chart_internal_fn.dyForYAxisLabel = function () { Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() {
var $$ = this, var $$ = this.owner,
position = $$.getYAxisLabelPosition(); position = this.getYAxisLabelPosition();
if ($$.config.axis_rotated) { if ($$.config.axis_rotated) {
return position.isInner ? "-0.5em" : "3em"; return position.isInner ? "-0.5em" : "3em";
} else { } else {
return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : ($$.getMaxTickWidth('y') + 10)); return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : (this.getMaxTickWidth('y') + 10));
} }
}; };
c3_chart_internal_fn.dyForY2AxisLabel = function () { Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() {
var $$ = this, var $$ = this.owner,
position = $$.getY2AxisLabelPosition(); position = this.getY2AxisLabelPosition();
if ($$.config.axis_rotated) { if ($$.config.axis_rotated) {
return position.isInner ? "1.2em" : "-2.2em"; return position.isInner ? "1.2em" : "-2.2em";
} else { } else {
return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : (this.getMaxTickWidth('y2') + 15)); return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : (this.getMaxTickWidth('y2') + 15));
} }
}; };
c3_chart_internal_fn.textAnchorForXAxisLabel = function () { Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() {
var $$ = this; var $$ = this.owner;
return $$.textAnchorForAxisLabel(!$$.config.axis_rotated, $$.getXAxisLabelPosition()); return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition());
};
c3_chart_internal_fn.textAnchorForYAxisLabel = function () {
var $$ = this;
return $$.textAnchorForAxisLabel($$.config.axis_rotated, $$.getYAxisLabelPosition());
};
c3_chart_internal_fn.textAnchorForY2AxisLabel = function () {
var $$ = this;
return $$.textAnchorForAxisLabel($$.config.axis_rotated, $$.getY2AxisLabelPosition());
};
c3_chart_internal_fn.xForRotatedTickText = function (r) {
return 8 * Math.sin(Math.PI * (r / 180));
}; };
c3_chart_internal_fn.yForRotatedTickText = function (r) { Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() {
return 11.5 - 2.5 * (r / 15) * (r > 0 ? 1 : -1); var $$ = this.owner;
return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition());
}; };
c3_chart_internal_fn.rotateTickText = function (axis, transition, rotate) { Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() {
if (axis.classed(c3_chart_internal_fn.CLASS.axisY) && !this.config.axis_rotated) { var $$ = this.owner;
axis.selectAll('.tick text') return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition());
.style("text-anchor", rotate > 0 ? "end" : "start");
} else {
axis.selectAll('.tick text')
.style("text-anchor", rotate > 0 ? "start" : "end");
}
transition.selectAll('.tick text')
.attr("y", this.yForRotatedTickText(rotate))
.attr("transform", "rotate(" + rotate + ")")
.selectAll('tspan')
.attr('dx', this.xForRotatedTickText(rotate));
}; };
Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) {
c3_chart_internal_fn.getMaxTickWidth = function (id, withoutRecompute) { var $$ = this.owner, config = $$.config,
var $$ = this, config = $$.config, maxWidth = 0, targetsToShow, scale, axis, dummy, svg;
maxWidth = 0, targetsToShow, scale, axis, body, svg;
if (withoutRecompute && $$.currentMaxTickWidths[id]) { if (withoutRecompute && $$.currentMaxTickWidths[id]) {
return $$.currentMaxTickWidths[id]; return $$.currentMaxTickWidths[id];
} }
@ -285,71 +276,67 @@ c3_chart_internal_fn.getMaxTickWidth = function (id, withoutRecompute) {
targetsToShow = $$.filterTargetsToShow($$.data.targets); targetsToShow = $$.filterTargetsToShow($$.data.targets);
if (id === 'y') { if (id === 'y') {
scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')); scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y'));
axis = $$.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues); axis = this.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, false, true);
} else if (id === 'y2') { } else if (id === 'y2') {
scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')); scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2'));
axis = $$.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues); axis = this.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, false, true);
} else { } else {
scale = $$.x.copy().domain($$.getXDomain(targetsToShow)); scale = $$.x.copy().domain($$.getXDomain(targetsToShow));
axis = $$.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues); axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true);
$$.updateXAxisTickValues(targetsToShow, axis); this.updateXAxisTickValues(targetsToShow, axis);
} }
body = this.d3.select('body').classed('c3', true); dummy = $$.d3.select('body').append('div').classed('c3', true);
svg = body.append('svg').style('visibility', 'hidden').style('height', 0); svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0),
svg.append('g').call(axis).each(function () { svg.append('g').call(axis).each(function () {
$$.d3.select(this).selectAll('text tspan').each(function () { $$.d3.select(this).selectAll('text').each(function () {
var box = this.getBoundingClientRect(); var box = this.getBoundingClientRect();
if (box.left >= 0 && maxWidth < box.width) { maxWidth = box.width; } if (maxWidth < box.width) { maxWidth = box.width; }
}); });
dummy.remove();
}); });
// TODO: time lag to get maxWidth
window.setTimeout(function () {
svg.remove();
}, 100);
body.classed('c3', false);
} }
$$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth; $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth;
return $$.currentMaxTickWidths[id]; return $$.currentMaxTickWidths[id];
}; };
c3_chart_internal_fn.updateAxisLabels = function (withTransition) { Axis.prototype.updateLabels = function updateLabels(withTransition) {
var $$ = this; var $$ = this.owner;
var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel),
axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel),
axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label); axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label);
(withTransition ? axisXLabel.transition() : axisXLabel) (withTransition ? axisXLabel.transition() : axisXLabel)
.attr("x", $$.xForXAxisLabel.bind($$)) .attr("x", this.xForXAxisLabel.bind(this))
.attr("dx", $$.dxForXAxisLabel.bind($$)) .attr("dx", this.dxForXAxisLabel.bind(this))
.attr("dy", $$.dyForXAxisLabel.bind($$)) .attr("dy", this.dyForXAxisLabel.bind(this))
.text($$.textForXAxisLabel.bind($$)); .text(this.textForXAxisLabel.bind(this));
(withTransition ? axisYLabel.transition() : axisYLabel) (withTransition ? axisYLabel.transition() : axisYLabel)
.attr("x", $$.xForYAxisLabel.bind($$)) .attr("x", this.xForYAxisLabel.bind(this))
.attr("dx", $$.dxForYAxisLabel.bind($$)) .attr("dx", this.dxForYAxisLabel.bind(this))
.attr("dy", $$.dyForYAxisLabel.bind($$)) .attr("dy", this.dyForYAxisLabel.bind(this))
.text($$.textForYAxisLabel.bind($$)); .text(this.textForYAxisLabel.bind(this));
(withTransition ? axisY2Label.transition() : axisY2Label) (withTransition ? axisY2Label.transition() : axisY2Label)
.attr("x", $$.xForY2AxisLabel.bind($$)) .attr("x", this.xForY2AxisLabel.bind(this))
.attr("dx", $$.dxForY2AxisLabel.bind($$)) .attr("dx", this.dxForY2AxisLabel.bind(this))
.attr("dy", $$.dyForY2AxisLabel.bind($$)) .attr("dy", this.dyForY2AxisLabel.bind(this))
.text($$.textForY2AxisLabel.bind($$)); .text(this.textForY2AxisLabel.bind(this));
}; };
Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) {
c3_chart_internal_fn.getAxisPadding = function (padding, key, defaultValue, domainLength) { var p = typeof padding === 'number' ? padding : padding[key];
if (!isValue(padding[key])) { if (!isValue(p)) {
return defaultValue; return defaultValue;
} }
if (padding.unit === 'ratio') { if (padding.unit === 'ratio') {
return padding[key] * domainLength; return padding[key] * domainLength;
} }
// assume padding is pixels if unit is not specified // assume padding is pixels if unit is not specified
return this.convertPixelsToAxisPadding(padding[key], domainLength); return this.convertPixelsToAxisPadding(p, domainLength);
}; };
c3_chart_internal_fn.convertPixelsToAxisPadding = function (pixels, domainLength) { Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) {
var length = this.config.axis_rotated ? this.width : this.height; var $$ = this.owner,
length = $$.config.axis_rotated ? $$.width : $$.height;
return domainLength * (pixels / length); return domainLength * (pixels / length);
}; };
Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) {
c3_chart_internal_fn.generateTickValues = function (values, tickCount, forTimeSeries) {
var tickValues = values, targetCount, start, end, count, interval, i, tickValue; var tickValues = values, targetCount, start, end, count, interval, i, tickValue;
if (tickCount) { if (tickCount) {
targetCount = isFunction(tickCount) ? tickCount() : tickCount; targetCount = isFunction(tickCount) ? tickCount() : tickCount;
@ -375,8 +362,8 @@ c3_chart_internal_fn.generateTickValues = function (values, tickCount, forTimeSe
if (!forTimeSeries) { tickValues = tickValues.sort(function (a, b) { return a - b; }); } if (!forTimeSeries) { tickValues = tickValues.sort(function (a, b) { return a - b; }); }
return tickValues; return tickValues;
}; };
c3_chart_internal_fn.generateAxisTransitions = function (duration) { Axis.prototype.generateTransitions = function generateTransitions(duration) {
var $$ = this, axes = $$.axes; var $$ = this.owner, axes = $$.axes;
return { return {
axisX: duration ? axes.x.transition().duration(duration) : axes.x, axisX: duration ? axes.x.transition().duration(duration) : axes.x,
axisY: duration ? axes.y.transition().duration(duration) : axes.y, axisY: duration ? axes.y.transition().duration(duration) : axes.y,
@ -384,8 +371,8 @@ c3_chart_internal_fn.generateAxisTransitions = function (duration) {
axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx
}; };
}; };
c3_chart_internal_fn.redrawAxis = function (transitions, isHidden) { Axis.prototype.redraw = function redraw(transitions, isHidden) {
var $$ = this, config = $$.config; var $$ = this.owner;
$$.axes.x.style("opacity", isHidden ? 0 : 1); $$.axes.x.style("opacity", isHidden ? 0 : 1);
$$.axes.y.style("opacity", isHidden ? 0 : 1); $$.axes.y.style("opacity", isHidden ? 0 : 1);
$$.axes.y2.style("opacity", isHidden ? 0 : 1); $$.axes.y2.style("opacity", isHidden ? 0 : 1);
@ -394,13 +381,4 @@ c3_chart_internal_fn.redrawAxis = function (transitions, isHidden) {
transitions.axisY.call($$.yAxis); transitions.axisY.call($$.yAxis);
transitions.axisY2.call($$.y2Axis); transitions.axisY2.call($$.y2Axis);
transitions.axisSubX.call($$.subXAxis); 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);
$$.rotateTickText($$.axes.subx, transitions.axisSubX, config.axis_x_tick_rotate);
}
// we may want to rotate y axis when chart in horizontal
if (config.axis_y_tick_rotate) {
$$.rotateTickText($$.axes.y, transitions.axisY, config.axis_y_tick_rotate);
}
}; };

44
src/c3.axis.js

@ -72,6 +72,9 @@ function c3_axis(d3, params) {
tickTextCharSize = size; tickTextCharSize = size;
return size; return size;
} }
function transitionise(selection) {
return params.withoutTransition ? selection : d3.transition(selection);
}
function axis(g) { function axis(g) {
g.each(function () { g.each(function () {
var g = axis.g = d3.select(this); var g = axis.g = d3.select(this);
@ -83,12 +86,12 @@ function c3_axis(d3, params) {
tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6),
// MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks. // 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(), tickExit = tick.exit().remove(),
tickUpdate = d3.transition(tick).style("opacity", 1), tickUpdate = transitionise(tick).style("opacity", 1),
tickTransform, tickX, tickY; tickTransform, tickX, tickY;
var range = scale.rangeExtent ? scale.rangeExtent() : scaleExtent(scale.range()), var range = scale.rangeExtent ? scale.rangeExtent() : scaleExtent(scale.range()),
path = g.selectAll(".domain").data([ 0 ]), path = g.selectAll(".domain").data([ 0 ]),
pathUpdate = (path.enter().append("path").attr("class", "domain"), d3.transition(path)); pathUpdate = (path.enter().append("path").attr("class", "domain"), transitionise(path));
tickEnter.append("line"); tickEnter.append("line");
tickEnter.append("text"); tickEnter.append("text");
@ -174,6 +177,33 @@ function c3_axis(d3, params) {
tspan.exit().remove(); tspan.exit().remove();
tspan.text(function (d) { return d.splitted; }); tspan.text(function (d) { return d.splitted; });
var rotate = params.tickTextRotate;
function textAnchorForText(rotate) {
if (!rotate) {
return 'middle';
}
return rotate > 0 ? "start" : "end";
}
function textTransform(rotate) {
if (!rotate) {
return '';
}
return "rotate(" + rotate + ")";
}
function dxForText(rotate) {
if (!rotate) {
return 0;
}
return 8 * Math.sin(Math.PI * (rotate / 180));
}
function yForText(rotate) {
if (!rotate) {
return tickLength;
}
return 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1);
}
switch (orient) { switch (orient) {
case "bottom": case "bottom":
{ {
@ -181,14 +211,16 @@ function c3_axis(d3, params) {
lineEnter.attr("y2", innerTickSize); lineEnter.attr("y2", innerTickSize);
textEnter.attr("y", tickLength); textEnter.attr("y", tickLength);
lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize); lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize);
textUpdate.attr("x", 0).attr("y", tickLength); textUpdate.attr("x", 0).attr("y", yForText(rotate))
text.style("text-anchor", "middle"); .style("text-anchor", textAnchorForText(rotate))
tspan.attr('x', 0).attr("dy", tspanDy); .attr("transform", textTransform(rotate));
tspan.attr('x', 0).attr("dy", tspanDy).attr('dx', dxForText(rotate));
pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize); pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
break; break;
} }
case "top": case "top":
{ {
// TODO: rotated tick text
tickTransform = axisX; tickTransform = axisX;
lineEnter.attr("y2", -innerTickSize); lineEnter.attr("y2", -innerTickSize);
textEnter.attr("y", -tickLength); textEnter.attr("y", -tickLength);
@ -270,7 +302,7 @@ function c3_axis(d3, params) {
length = axis.g.select('path.domain').node().getTotalLength() - outerTickSize * 2; length = axis.g.select('path.domain').node().getTotalLength() - outerTickSize * 2;
interval = length / axis.g.selectAll('line').size(); interval = length / axis.g.selectAll('line').size();
} }
return interval; return interval === Infinity ? 0 : interval;
}; };
axis.ticks = function () { axis.ticks = function () {
if (!arguments.length) { return tickArguments; } if (!arguments.length) { return tickArguments; }

1
src/class.js

@ -26,6 +26,7 @@ var CLASS = c3_chart_internal_fn.CLASS = {
defocused: 'c3-defocused', defocused: 'c3-defocused',
region: 'c3-region', region: 'c3-region',
regions: 'c3-regions', regions: 'c3-regions',
title: 'c3-title',
tooltipContainer: 'c3-tooltip-container', tooltipContainer: 'c3-tooltip-container',
tooltip: 'c3-tooltip', tooltip: 'c3-tooltip',
tooltipName: 'c3-tooltip-name', tooltipName: 'c3-tooltip-name',

33
src/config.js

@ -1,12 +1,14 @@
c3_chart_internal_fn.getDefaultConfig = function () { c3_chart_internal_fn.getDefaultConfig = function () {
var config = { var config = {
bindto: '#chart', bindto: '#chart',
svg_classname: undefined,
size_width: undefined, size_width: undefined,
size_height: undefined, size_height: undefined,
padding_left: undefined, padding_left: undefined,
padding_right: undefined, padding_right: undefined,
padding_top: undefined, padding_top: undefined,
padding_bottom: undefined, padding_bottom: undefined,
resize_auto: true,
zoom_enabled: false, zoom_enabled: false,
zoom_extent: undefined, zoom_extent: undefined,
zoom_privileged: false, zoom_privileged: false,
@ -14,6 +16,8 @@ c3_chart_internal_fn.getDefaultConfig = function () {
zoom_onzoom: function () {}, zoom_onzoom: function () {},
zoom_onzoomstart: function () {}, zoom_onzoomstart: function () {},
zoom_onzoomend: function () {}, zoom_onzoomend: function () {},
zoom_x_min: undefined,
zoom_x_max: undefined,
interaction_enabled: true, interaction_enabled: true,
onmouseover: function () {}, onmouseover: function () {},
onmouseout: function () {}, onmouseout: function () {},
@ -62,6 +66,7 @@ c3_chart_internal_fn.getDefaultConfig = function () {
// subchart // subchart
subchart_show: false, subchart_show: false,
subchart_size_height: 60, subchart_size_height: 60,
subchart_axis_x_show: true,
subchart_onbrush: function () {}, subchart_onbrush: function () {},
// color // color
color_pattern: [], color_pattern: [],
@ -78,6 +83,9 @@ c3_chart_internal_fn.getDefaultConfig = function () {
legend_item_onmouseover: undefined, legend_item_onmouseover: undefined,
legend_item_onmouseout: undefined, legend_item_onmouseout: undefined,
legend_equally: false, legend_equally: false,
legend_padding: 0,
legend_item_tile_width: 10,
legend_item_tile_height: 10,
// axis // axis
axis_rotated: false, axis_rotated: false,
axis_x_show: true, axis_x_show: true,
@ -145,6 +153,7 @@ c3_chart_internal_fn.getDefaultConfig = function () {
// point - point of each data // point - point of each data
point_show: true, point_show: true,
point_r: 2.5, point_r: 2.5,
point_sensitivity: 10,
point_focus_expand_enabled: true, point_focus_expand_enabled: true,
point_focus_expand_r: undefined, point_focus_expand_r: undefined,
point_select_r: undefined, point_select_r: undefined,
@ -162,22 +171,27 @@ c3_chart_internal_fn.getDefaultConfig = function () {
pie_label_show: true, pie_label_show: true,
pie_label_format: undefined, pie_label_format: undefined,
pie_label_threshold: 0.05, pie_label_threshold: 0.05,
pie_expand: true, pie_expand: {},
pie_expand_duration: 50,
// gauge // gauge
gauge_label_show: true, gauge_label_show: true,
gauge_label_format: undefined, gauge_label_format: undefined,
gauge_expand: true,
gauge_min: 0, gauge_min: 0,
gauge_max: 100, gauge_max: 100,
gauge_units: undefined, gauge_units: undefined,
gauge_width: undefined, gauge_width: undefined,
gauge_expand: {},
gauge_expand_duration: 50,
// donut // donut
donut_label_show: true, donut_label_show: true,
donut_label_format: undefined, donut_label_format: undefined,
donut_label_threshold: 0.05, donut_label_threshold: 0.05,
donut_width: undefined, donut_width: undefined,
donut_expand: true,
donut_title: "", donut_title: "",
donut_expand: {},
donut_expand_duration: 50,
// spline
spline_interpolation_type: 'cardinal',
// region - region to change style // region - region to change style
regions: [], regions: [],
// tooltip - show when mouseover on each data // tooltip - show when mouseover on each data
@ -192,7 +206,18 @@ c3_chart_internal_fn.getDefaultConfig = function () {
}, },
tooltip_init_show: false, tooltip_init_show: false,
tooltip_init_x: 0, tooltip_init_x: 0,
tooltip_init_position: {top: '0px', left: '50px'} tooltip_init_position: {top: '0px', left: '50px'},
tooltip_onshow: function () {},
tooltip_onhide: function () {},
// title
title_text: undefined,
title_padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
title_position: 'top-center',
}; };
Object.keys(this.additionalConfig).forEach(function (key) { Object.keys(this.additionalConfig).forEach(function (key) {

144
src/core.js

@ -1,11 +1,35 @@
var c3 = { version: "0.4.10-rc1" }; var c3 = { version: "0.4.11-rc4" };
var c3_chart_fn, c3_chart_internal_fn; var c3_chart_fn,
c3_chart_internal_fn,
c3_chart_internal_axis_fn;
function API(owner) {
this.owner = owner;
}
function inherit(base, derived) {
if (Object.create) {
derived.prototype = Object.create(base.prototype);
} else {
var f = function f() {};
f.prototype = base.prototype;
derived.prototype = new f();
}
derived.prototype.constructor = derived;
return derived;
}
function Chart(config) { function Chart(config) {
var $$ = this.internal = new ChartInternal(this); var $$ = this.internal = new ChartInternal(this);
$$.loadConfig(config); $$.loadConfig(config);
$$.beforeInit(config);
$$.init(); $$.init();
$$.afterInit(config);
// bind "this" to nested API // bind "this" to nested API
(function bindThis(fn, target, argThis) { (function bindThis(fn, target, argThis) {
@ -35,13 +59,22 @@ c3.generate = function (config) {
c3.chart = { c3.chart = {
fn: Chart.prototype, fn: Chart.prototype,
internal: { internal: {
fn: ChartInternal.prototype fn: ChartInternal.prototype,
axis: {
fn: Axis.prototype
}
} }
}; };
c3_chart_fn = c3.chart.fn; c3_chart_fn = c3.chart.fn;
c3_chart_internal_fn = c3.chart.internal.fn; c3_chart_internal_fn = c3.chart.internal.fn;
c3_chart_internal_axis_fn = c3.chart.internal.axis.fn;
c3_chart_internal_fn.beforeInit = function () {
// can do something
};
c3_chart_internal_fn.afterInit = function () {
// can do something
};
c3_chart_internal_fn.init = function () { c3_chart_internal_fn.init = function () {
var $$ = this, config = $$.config; var $$ = this, config = $$.config;
@ -149,6 +182,8 @@ c3_chart_internal_fn.initWithData = function (data) {
var $$ = this, d3 = $$.d3, config = $$.config; var $$ = this, d3 = $$.d3, config = $$.config;
var defs, main, binding = true; var defs, main, binding = true;
$$.axis = new Axis($$);
if ($$.initPie) { $$.initPie(); } if ($$.initPie) { $$.initPie(); }
if ($$.initBrush) { $$.initBrush(); } if ($$.initBrush) { $$.initBrush(); }
if ($$.initZoom) { $$.initZoom(); } if ($$.initZoom) { $$.initZoom(); }
@ -217,6 +252,10 @@ c3_chart_internal_fn.initWithData = function (data) {
.on('mouseenter', function () { return config.onmouseover.call($$); }) .on('mouseenter', function () { return config.onmouseover.call($$); })
.on('mouseleave', function () { return config.onmouseout.call($$); }); .on('mouseleave', function () { return config.onmouseout.call($$); });
if ($$.config.svg_classname) {
$$.svg.attr('class', $$.config.svg_classname);
}
// Define defs // Define defs
defs = $$.svg.append("defs"); defs = $$.svg.append("defs");
$$.clipChart = $$.appendClip(defs, $$.clipId); $$.clipChart = $$.appendClip(defs, $$.clipId);
@ -232,6 +271,7 @@ c3_chart_internal_fn.initWithData = function (data) {
if ($$.initSubchart) { $$.initSubchart(); } if ($$.initSubchart) { $$.initSubchart(); }
if ($$.initTooltip) { $$.initTooltip(); } if ($$.initTooltip) { $$.initTooltip(); }
if ($$.initLegend) { $$.initLegend(); } if ($$.initLegend) { $$.initLegend(); }
if ($$.initTitle) { $$.initTitle(); }
/*-- Main Region --*/ /*-- Main Region --*/
@ -274,7 +314,7 @@ c3_chart_internal_fn.initWithData = function (data) {
if (config.axis_x_extent) { $$.brush.extent($$.getDefaultExtent()); } if (config.axis_x_extent) { $$.brush.extent($$.getDefaultExtent()); }
// Add Axis // Add Axis
$$.initAxis(); $$.axis.init();
// Set targets // Set targets
$$.updateTargets($$.data.targets); $$.updateTargets($$.data.targets);
@ -284,6 +324,7 @@ c3_chart_internal_fn.initWithData = function (data) {
$$.updateDimension(); $$.updateDimension();
$$.config.oninit.call($$); $$.config.oninit.call($$);
$$.redraw({ $$.redraw({
withTransition: false,
withTransform: true, withTransform: true,
withUpdateXDomain: true, withUpdateXDomain: true,
withUpdateOrgXDomain: true, withUpdateOrgXDomain: true,
@ -292,20 +333,7 @@ c3_chart_internal_fn.initWithData = function (data) {
} }
// Bind resize event // Bind resize event
if (window.onresize == null) { $$.bindResize();
window.onresize = $$.generateResize();
}
if (window.onresize.add) {
window.onresize.add(function () {
config.onresize.call($$);
});
window.onresize.add(function () {
$$.api.flush();
});
window.onresize.add(function () {
config.onresized.call($$);
});
}
// export element of the chart // export element of the chart
$$.api.element = $$.selectChart.node(); $$.api.element = $$.selectChart.node();
@ -464,7 +492,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
durationForExit = withTransitionForExit ? duration : 0; durationForExit = withTransitionForExit ? duration : 0;
durationForAxis = withTransitionForAxis ? duration : 0; durationForAxis = withTransitionForAxis ? duration : 0;
transitions = transitions || $$.generateAxisTransitions(durationForAxis); transitions = transitions || $$.axis.generateTransitions(durationForAxis);
// update legend and transform each g // update legend and transform each g
if (withLegend && config.legend_show) { if (withLegend && config.legend_show) {
@ -483,7 +511,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
if (targetsToShow.length) { if (targetsToShow.length) {
$$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain); $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain);
if (!config.axis_x_tick_values) { if (!config.axis_x_tick_values) {
tickValues = $$.updateXAxisTickValues(targetsToShow); tickValues = $$.axis.updateXAxisTickValues(targetsToShow);
} }
} else { } else {
$$.xAxis.tickValues([]); $$.xAxis.tickValues([]);
@ -498,17 +526,17 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
$$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom)); $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom));
if (!config.axis_y_tick_values && config.axis_y_tick_count) { if (!config.axis_y_tick_values && config.axis_y_tick_count) {
$$.yAxis.tickValues($$.generateTickValues($$.y.domain(), config.axis_y_tick_count)); $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count));
} }
if (!config.axis_y2_tick_values && config.axis_y2_tick_count) { if (!config.axis_y2_tick_values && config.axis_y2_tick_count) {
$$.y2Axis.tickValues($$.generateTickValues($$.y2.domain(), config.axis_y2_tick_count)); $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count));
} }
// axes // axes
$$.redrawAxis(transitions, hideAxis); $$.axis.redraw(transitions, hideAxis);
// Update axis label // Update axis label
$$.updateAxisLabels(withTransition); $$.axis.updateLabels(withTransition);
// show/hide if manual culling needed // show/hide if manual culling needed
if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) { if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) {
@ -543,9 +571,6 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
$$.subY2.domain($$.getYDomain(targetsToShow, 'y2')); $$.subY2.domain($$.getYDomain(targetsToShow, 'y2'));
} }
// tooltip
$$.tooltip.style("display", "none");
// xgrid focus // xgrid focus
$$.updateXgridFocus(); $$.updateXgridFocus();
@ -576,6 +601,9 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
$$.updateText(durationForExit); $$.updateText(durationForExit);
} }
// title
if ($$.redrawTitle) { $$.redrawTitle(); }
// arc // arc
if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); } if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); }
@ -607,7 +635,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
flow = $$.generateFlow({ flow = $$.generateFlow({
targets: targetsToShow, targets: targetsToShow,
flow: options.flow, flow: options.flow,
duration: duration, duration: options.flow.duration,
drawBar: drawBar, drawBar: drawBar,
drawLine: drawLine, drawLine: drawLine,
drawArea: drawArea, drawArea: drawArea,
@ -619,7 +647,7 @@ c3_chart_internal_fn.redraw = function (options, transitions) {
}); });
} }
if (duration && $$.isTabVisible()) { // Only use transition if tab visible. See #938. if ((duration || flow) && $$.isTabVisible()) { // Only use transition if tab visible. See #938.
// transition should be derived from one transition // transition should be derived from one transition
d3.transition().duration(duration).each(function () { d3.transition().duration(duration).each(function () {
var transitionsToWait = []; var transitionsToWait = [];
@ -689,7 +717,7 @@ c3_chart_internal_fn.updateAndRedraw = function (options) {
$$.updateSizes(); $$.updateSizes();
// MEMO: called in updateLegend in redraw if withLegend // MEMO: called in updateLegend in redraw if withLegend
if (!(options.withLegend && config.legend_show)) { if (!(options.withLegend && config.legend_show)) {
transitions = $$.generateAxisTransitions(options.withTransitionForAxis ? config.transition_duration : 0); transitions = $$.axis.generateTransitions(options.withTransitionForAxis ? config.transition_duration : 0);
// Update scales // Update scales
$$.updateScales(); $$.updateScales();
$$.updateSvgSize(); $$.updateSvgSize();
@ -882,6 +910,7 @@ c3_chart_internal_fn.observeInserted = function (selection) {
if (selection.node().parentNode) { if (selection.node().parentNode) {
window.clearInterval($$.intervalForObserveInserted); window.clearInterval($$.intervalForObserveInserted);
$$.updateDimension(); $$.updateDimension();
if ($$.brush) { $$.brush.update(); }
$$.config.oninit.call($$); $$.config.oninit.call($$);
$$.redraw({ $$.redraw({
withTransform: true, withTransform: true,
@ -900,6 +929,49 @@ c3_chart_internal_fn.observeInserted = function (selection) {
observer.observe(selection.node(), {attributes: true, childList: true, characterData: true}); observer.observe(selection.node(), {attributes: true, childList: true, characterData: true});
}; };
c3_chart_internal_fn.bindResize = function () {
var $$ = this, config = $$.config;
$$.resizeFunction = $$.generateResize();
$$.resizeFunction.add(function () {
config.onresize.call($$);
});
if (config.resize_auto) {
$$.resizeFunction.add(function () {
if ($$.resizeTimeout !== undefined) {
window.clearTimeout($$.resizeTimeout);
}
$$.resizeTimeout = window.setTimeout(function () {
delete $$.resizeTimeout;
$$.api.flush();
}, 100);
});
}
$$.resizeFunction.add(function () {
config.onresized.call($$);
});
if (window.attachEvent) {
window.attachEvent('onresize', $$.resizeFunction);
} else if (window.addEventListener) {
window.addEventListener('resize', $$.resizeFunction, false);
} else {
// fallback to this, if this is a very old browser
var wrapper = window.onresize;
if (!wrapper) {
// create a wrapper that will call all charts
wrapper = $$.generateResize();
} else if (!wrapper.add || !wrapper.remove) {
// there is already a handler registered, make sure we call it too
wrapper = $$.generateResize();
wrapper.add(window.onresize);
}
// add this graph to the wrapper, we will be removed if the user calls destroy
wrapper.add($$.resizeFunction);
window.onresize = wrapper;
}
};
c3_chart_internal_fn.generateResize = function () { c3_chart_internal_fn.generateResize = function () {
var resizeFunctions = []; var resizeFunctions = [];
@ -911,6 +983,14 @@ c3_chart_internal_fn.generateResize = function () {
callResizeFunctions.add = function (f) { callResizeFunctions.add = function (f) {
resizeFunctions.push(f); resizeFunctions.push(f);
}; };
callResizeFunctions.remove = function (f) {
for (var i = 0; i < resizeFunctions.length; i++) {
if (resizeFunctions[i] === f) {
resizeFunctions.splice(i, 1);
break;
}
}
};
return callResizeFunctions; return callResizeFunctions;
}; };
@ -956,7 +1036,7 @@ c3_chart_internal_fn.parseDate = function (date) {
parsedDate = date; parsedDate = date;
} else if (typeof date === 'string') { } else if (typeof date === 'string') {
parsedDate = $$.dataTimeFormat($$.config.data_xFormat).parse(date); parsedDate = $$.dataTimeFormat($$.config.data_xFormat).parse(date);
} else if (typeof date === 'number' || !isNaN(date)) { } else if (typeof date === 'number' && !isNaN(date)) {
parsedDate = new Date(+date); parsedDate = new Date(+date);
} }
if (!parsedDate || isNaN(+parsedDate)) { if (!parsedDate || isNaN(+parsedDate)) {

14
src/data.convert.js

@ -37,10 +37,11 @@ c3_chart_internal_fn.convertJsonToData = function (json, keys) {
var $$ = this, var $$ = this,
new_rows = [], targetKeys, data; new_rows = [], targetKeys, data;
if (keys) { // when keys specified, json would be an array that includes objects if (keys) { // when keys specified, json would be an array that includes objects
targetKeys = keys.value;
if (keys.x) { if (keys.x) {
targetKeys.push(keys.x); targetKeys = keys.value.concat(keys.x);
$$.config.data_x = keys.x; $$.config.data_x = keys.x;
} else {
targetKeys = keys.value;
} }
new_rows.push(targetKeys); new_rows.push(targetKeys);
json.forEach(function (o) { json.forEach(function (o) {
@ -139,7 +140,8 @@ c3_chart_internal_fn.convertDataToTargets = function (data, appendXs) {
id: convertedId, id: convertedId,
id_org: id, id_org: id,
values: data.map(function (d, i) { values: data.map(function (d, i) {
var xKey = $$.getXKey(id), rawX = d[xKey], x = $$.generateTargetX(rawX, id, i); var xKey = $$.getXKey(id), rawX = d[xKey], x = $$.generateTargetX(rawX, id, i),
value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null;
// use x as categories if custom x and categorized // use x as categories if custom x and categorized
if ($$.isCustomX() && $$.isCategorized() && index === 0 && rawX) { if ($$.isCustomX() && $$.isCategorized() && index === 0 && rawX) {
if (i === 0) { config.axis_x_categories = []; } if (i === 0) { config.axis_x_categories = []; }
@ -149,7 +151,7 @@ c3_chart_internal_fn.convertDataToTargets = function (data, appendXs) {
if (isUndefined(d[id]) || $$.data.xs[id].length <= i) { if (isUndefined(d[id]) || $$.data.xs[id].length <= i) {
x = undefined; x = undefined;
} }
return {x: x, value: d[id] !== null && !isNaN(d[id]) ? +d[id] : null, id: convertedId}; return {x: x, value: value, id: convertedId};
}).filter(function (v) { return isDefined(v.x); }) }).filter(function (v) { return isDefined(v.x); })
}; };
}); });
@ -176,6 +178,10 @@ c3_chart_internal_fn.convertDataToTargets = function (data, appendXs) {
}); });
}); });
// cache information about values
$$.hasNegativeValue = $$.hasNegativeValueInTargets(targets);
$$.hasPositiveValue = $$.hasPositiveValueInTargets(targets);
// set target types // set target types
if (config.data_type) { if (config.data_type) {
$$.setTargetType($$.mapToIds(targets).filter(function (id) { return ! (id in config.data_types); }), config.data_type); $$.setTargetType($$.mapToIds(targets).filter(function (id) { return ! (id in config.data_types); }), config.data_type);

11
src/data.js

@ -53,7 +53,7 @@ c3_chart_internal_fn.addName = function (data) {
var $$ = this, name; var $$ = this, name;
if (data) { if (data) {
name = $$.config.data_names[data.id]; name = $$.config.data_names[data.id];
data.name = name ? name : data.id; data.name = name !== undefined ? name : data.id;
} }
return data; return data;
}; };
@ -147,7 +147,7 @@ c3_chart_internal_fn.mapToIds = function (targets) {
}; };
c3_chart_internal_fn.mapToTargetIds = function (ids) { c3_chart_internal_fn.mapToTargetIds = function (ids) {
var $$ = this; var $$ = this;
return ids ? (isString(ids) ? [ids] : ids) : $$.mapToIds($$.data.targets); return ids ? [].concat(ids) : $$.mapToIds($$.data.targets);
}; };
c3_chart_internal_fn.hasTarget = function (targets, id) { c3_chart_internal_fn.hasTarget = function (targets, id) {
var ids = this.mapToIds(targets), i; var ids = this.mapToIds(targets), i;
@ -171,7 +171,8 @@ c3_chart_internal_fn.filterTargetsToShow = function (targets) {
c3_chart_internal_fn.mapTargetsToUniqueXs = function (targets) { c3_chart_internal_fn.mapTargetsToUniqueXs = function (targets) {
var $$ = this; var $$ = this;
var xs = $$.d3.set($$.d3.merge(targets.map(function (t) { return t.values.map(function (v) { return +v.x; }); }))).values(); 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; }); xs = $$.isTimeSeries() ? xs.map(function (x) { return new Date(+x); }) : xs.map(function (x) { return +x; });
return xs.sort(function (a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; });
}; };
c3_chart_internal_fn.addHiddenTargetIds = function (targetIds) { c3_chart_internal_fn.addHiddenTargetIds = function (targetIds) {
this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds); this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds);
@ -305,7 +306,7 @@ c3_chart_internal_fn.findClosestFromTargets = function (targets, pos) {
return $$.findClosest(candidates, pos); return $$.findClosest(candidates, pos);
}; };
c3_chart_internal_fn.findClosest = function (values, pos) { c3_chart_internal_fn.findClosest = function (values, pos) {
var $$ = this, minDist = 100, closest; var $$ = this, minDist = $$.config.point_sensitivity, closest;
// find mouseovering bar // find mouseovering bar
values.filter(function (v) { return v && $$.isBarType(v.id); }).forEach(function (v) { values.filter(function (v) { return v && $$.isBarType(v.id); }).forEach(function (v) {
@ -332,7 +333,7 @@ c3_chart_internal_fn.dist = function (data, pos) {
yIndex = config.axis_rotated ? 0 : 1, yIndex = config.axis_rotated ? 0 : 1,
y = $$.circleY(data, data.index), y = $$.circleY(data, data.index),
x = $$.x(data.x); x = $$.x(data.x);
return Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2); return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2));
}; };
c3_chart_internal_fn.convertValuesToStep = function (values) { c3_chart_internal_fn.convertValuesToStep = function (values) {
var converted = [].concat(values), i; var converted = [].concat(values), i;

3
src/data.load.js

@ -8,7 +8,8 @@ c3_chart_internal_fn.load = function (targets, args) {
// set type if args.types || args.type specified // set type if args.types || args.type specified
if (args.type || args.types) { if (args.type || args.types) {
targets.forEach(function (t) { targets.forEach(function (t) {
$$.setTargetType(t.id, args.types ? args.types[t.id] : args.type); var type = args.types && args.types[t.id] ? args.types[t.id] : args.type;
$$.setTargetType(t.id, type);
}); });
} }
// Update/Add data // Update/Add data

35
src/domain.js

@ -20,7 +20,7 @@ c3_chart_internal_fn.getYDomainMin = function (targets) {
id = idsInGroup[k]; id = idsInGroup[k];
if (! ys[id]) { continue; } if (! ys[id]) { continue; }
ys[id].forEach(function (v, i) { ys[id].forEach(function (v, i) {
if ($$.getAxisId(id) === $$.getAxisId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) { if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) {
ys[baseId][i] += +v; ys[baseId][i] += +v;
} }
}); });
@ -51,7 +51,7 @@ c3_chart_internal_fn.getYDomainMax = function (targets) {
id = idsInGroup[k]; id = idsInGroup[k];
if (! ys[id]) { continue; } if (! ys[id]) { continue; }
ys[id].forEach(function (v, i) { ys[id].forEach(function (v, i) {
if ($$.getAxisId(id) === $$.getAxisId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) { if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) {
ys[baseId][i] += +v; ys[baseId][i] += +v;
} }
}); });
@ -62,7 +62,7 @@ c3_chart_internal_fn.getYDomainMax = function (targets) {
}; };
c3_chart_internal_fn.getYDomain = function (targets, axisId, xDomain) { c3_chart_internal_fn.getYDomain = function (targets, axisId, xDomain) {
var $$ = this, config = $$.config, var $$ = this, config = $$.config,
targetsByAxisId = targets.filter(function (t) { return $$.getAxisId(t.id) === axisId; }), targetsByAxisId = targets.filter(function (t) { return $$.axis.getId(t.id) === axisId; }),
yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId, yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId,
yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min, yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min,
yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max, yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max,
@ -123,16 +123,16 @@ c3_chart_internal_fn.getYDomain = function (targets, axisId, xDomain) {
padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1])); padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1]));
} else if (showVerticalDataLabel) { } else if (showVerticalDataLabel) {
lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height'); lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height');
padding_top += this.convertPixelsToAxisPadding(lengths[1], domainLength); padding_top += $$.axis.convertPixelsToAxisPadding(lengths[1], domainLength);
padding_bottom += this.convertPixelsToAxisPadding(lengths[0], domainLength); padding_bottom += $$.axis.convertPixelsToAxisPadding(lengths[0], domainLength);
} }
if (axisId === 'y' && notEmpty(config.axis_y_padding)) { if (axisId === 'y' && notEmpty(config.axis_y_padding)) {
padding_top = $$.getAxisPadding(config.axis_y_padding, 'top', padding_top, domainLength); padding_top = $$.axis.getPadding(config.axis_y_padding, 'top', padding_top, domainLength);
padding_bottom = $$.getAxisPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength); padding_bottom = $$.axis.getPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength);
} }
if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) { if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) {
padding_top = $$.getAxisPadding(config.axis_y2_padding, 'top', padding_top, domainLength); padding_top = $$.axis.getPadding(config.axis_y2_padding, 'top', padding_top, domainLength);
padding_bottom = $$.getAxisPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength); padding_bottom = $$.axis.getPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength);
} }
// Bar/Area chart should be 0-based if all positive|negative // Bar/Area chart should be 0-based if all positive|negative
if (isZeroBased) { if (isZeroBased) {
@ -221,14 +221,15 @@ c3_chart_internal_fn.updateXDomain = function (targets, withUpdateXDomain, withU
return $$.x.domain(); return $$.x.domain();
}; };
c3_chart_internal_fn.trimXDomain = function (domain) { c3_chart_internal_fn.trimXDomain = function (domain) {
var $$ = this; var zoomDomain = this.getZoomDomain(),
if (domain[0] <= $$.orgXDomain[0]) { min = zoomDomain[0], max = zoomDomain[1];
domain[1] = +domain[1] + ($$.orgXDomain[0] - domain[0]); if (domain[0] <= min) {
domain[0] = $$.orgXDomain[0]; domain[1] = +domain[1] + (min - domain[0]);
} domain[0] = min;
if ($$.orgXDomain[1] <= domain[1]) { }
domain[0] = +domain[0] - (domain[1] - $$.orgXDomain[1]); if (max <= domain[1]) {
domain[1] = $$.orgXDomain[1]; domain[0] = +domain[0] - (domain[1] - max);
domain[1] = max;
} }
return domain; return domain;
}; };

2
src/format.js

@ -3,7 +3,7 @@ c3_chart_internal_fn.getYFormat = function (forArc) {
formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat, formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat,
formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format; formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format;
return function (v, ratio, id) { return function (v, ratio, id) {
var format = $$.getAxisId(id) === 'y2' ? formatForY2 : formatForY; var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY;
return format.call($$, v, ratio); return format.call($$, v, ratio);
}; };
}; };

9
src/interaction.js

@ -130,6 +130,7 @@ c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) {
}) })
.on('mouseout', function (d) { .on('mouseout', function (d) {
var index = d.index; var index = d.index;
if (!$$.config) { return; } // chart is destroyed
if ($$.hasArcType()) { return; } if ($$.hasArcType()) { return; }
$$.hideXGridFocus(); $$.hideXGridFocus();
$$.hideTooltip(); $$.hideTooltip();
@ -241,6 +242,7 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter)
.attr('height', $$.height) .attr('height', $$.height)
.attr('class', CLASS.eventRect) .attr('class', CLASS.eventRect)
.on('mouseout', function () { .on('mouseout', function () {
if (!$$.config) { return; } // chart is destroyed
if ($$.hasArcType()) { return; } if ($$.hasArcType()) { return; }
mouseout(); mouseout();
}) })
@ -286,7 +288,7 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter)
$$.showXGridFocus(selectedData); $$.showXGridFocus(selectedData);
// Show cursor as pointer if point is close to mouse position // Show cursor as pointer if point is close to mouse position
if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < 100) { if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) {
$$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer'); $$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer');
if (!$$.mouseover) { if (!$$.mouseover) {
config.data_onmouseover.call($$.api, closest); config.data_onmouseover.call($$.api, closest);
@ -297,16 +299,13 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter)
.on('click', function () { .on('click', function () {
var targetsToShow = $$.filterTargetsToShow($$.data.targets); var targetsToShow = $$.filterTargetsToShow($$.data.targets);
var mouse, closest; var mouse, closest;
if ($$.hasArcType(targetsToShow)) { return; } if ($$.hasArcType(targetsToShow)) { return; }
mouse = d3.mouse(this); mouse = d3.mouse(this);
closest = $$.findClosestFromTargets(targetsToShow, mouse); closest = $$.findClosestFromTargets(targetsToShow, mouse);
if (! closest) { return; } if (! closest) { return; }
// select if selection enabled // select if selection enabled
if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < 100) { if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) {
$$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id)).selectAll('.' + CLASS.shape + '-' + closest.index).each(function () { $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id)).selectAll('.' + CLASS.shape + '-' + closest.index).each(function () {
if (config.data_selection_grouped || $$.isWithinShape(this, closest)) { if (config.data_selection_grouped || $$.isWithinShape(this, closest)) {
$$.toggleShape(this, closest, closest.index); $$.toggleShape(this, closest, closest.index);

71
src/legend.js

@ -1,5 +1,6 @@
c3_chart_internal_fn.initLegend = function () { c3_chart_internal_fn.initLegend = function () {
var $$ = this; var $$ = this;
$$.legendItemTextBox = {};
$$.legendHasRendered = false; $$.legendHasRendered = false;
$$.legend = $$.svg.append("g").attr("transform", $$.getTranslate('legend')); $$.legend = $$.svg.append("g").attr("transform", $$.getTranslate('legend'));
if (!$$.config.legend_show) { if (!$$.config.legend_show) {
@ -107,33 +108,37 @@ c3_chart_internal_fn.hideLegend = function (targetIds) {
.style('opacity', 0) .style('opacity', 0)
.style('visibility', 'hidden'); .style('visibility', 'hidden');
}; };
var legendItemTextBox = {};
c3_chart_internal_fn.clearLegendItemTextBoxCache = function () { c3_chart_internal_fn.clearLegendItemTextBoxCache = function () {
legendItemTextBox = {}; this.legendItemTextBox = {};
}; };
c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
var $$ = this, config = $$.config; var $$ = this, config = $$.config;
var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect; var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile;
var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = 15; var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5;
var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0; var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0;
var withTransition, withTransitionForTransform; var withTransition, withTransitionForTransform;
var texts, rects, tiles, background; var texts, rects, tiles, background;
// Skip elements when their name is set to null
targetIds = targetIds.filter(function(id) {
return !isDefined(config.data_names[id]) || config.data_names[id] !== null;
});
options = options || {}; options = options || {};
withTransition = getOption(options, "withTransition", true); withTransition = getOption(options, "withTransition", true);
withTransitionForTransform = getOption(options, "withTransitionForTransform", true); withTransitionForTransform = getOption(options, "withTransitionForTransform", true);
function getTextBox(textElement, id) { function getTextBox(textElement, id) {
if (!legendItemTextBox[id]) { if (!$$.legendItemTextBox[id]) {
legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem); $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem, textElement);
} }
return legendItemTextBox[id]; return $$.legendItemTextBox[id];
} }
function updatePositions(textElement, id, index) { function updatePositions(textElement, id, index) {
var reset = index === 0, isLast = index === targetIds.length - 1, var reset = index === 0, isLast = index === targetIds.length - 1,
box = getTextBox(textElement, id), box = getTextBox(textElement, id),
itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight), itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding,
itemHeight = box.height + paddingTop, itemHeight = box.height + paddingTop,
itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth,
areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(),
@ -206,10 +211,13 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
xForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; xForLegend = function (id) { return margins[steps[id]] + offsets[id]; };
yForLegend = function (id) { return maxHeight * steps[id]; }; yForLegend = function (id) { return maxHeight * steps[id]; };
} }
xForLegendText = function (id, i) { return xForLegend(id, i) + 14; }; xForLegendText = function (id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width; };
yForLegendText = function (id, i) { return yForLegend(id, i) + 9; }; yForLegendText = function (id, i) { return yForLegend(id, i) + 9; };
xForLegendRect = function (id, i) { return xForLegend(id, i); }; xForLegendRect = function (id, i) { return xForLegend(id, i); };
yForLegendRect = function (id, i) { return yForLegend(id, i) - 5; }; yForLegendRect = function (id, i) { return yForLegend(id, i) - 5; };
x1ForLegendTile = function (id, i) { return xForLegend(id, i) - 2; };
x2ForLegendTile = function (id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width; };
yForLegendTile = function (id, i) { return yForLegend(id, i) + 4; };
// Define g for legend area // Define g for legend area
l = $$.legend.selectAll('.' + CLASS.legendItem) l = $$.legend.selectAll('.' + CLASS.legendItem)
@ -232,20 +240,24 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
} }
}) })
.on('mouseover', function (id) { .on('mouseover', function (id) {
$$.d3.select(this).classed(CLASS.legendItemFocused, true);
if (!$$.transiting && $$.isTargetToShow(id)) {
$$.api.focus(id);
}
if (config.legend_item_onmouseover) { if (config.legend_item_onmouseover) {
config.legend_item_onmouseover.call($$, id); config.legend_item_onmouseover.call($$, id);
} }
else {
$$.d3.select(this).classed(CLASS.legendItemFocused, true);
if (!$$.transiting && $$.isTargetToShow(id)) {
$$.api.focus(id);
}
}
}) })
.on('mouseout', function (id) { .on('mouseout', function (id) {
$$.d3.select(this).classed(CLASS.legendItemFocused, false);
$$.api.revert();
if (config.legend_item_onmouseout) { if (config.legend_item_onmouseout) {
config.legend_item_onmouseout.call($$, id); config.legend_item_onmouseout.call($$, id);
} }
else {
$$.d3.select(this).classed(CLASS.legendItemFocused, false);
$$.api.revert();
}
}); });
l.append('text') l.append('text')
.text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; })
@ -258,14 +270,15 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
.style('fill-opacity', 0) .style('fill-opacity', 0)
.attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200)
.attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect);
l.append('rect') l.append('line')
.attr("class", CLASS.legendItemTile) .attr('class', CLASS.legendItemTile)
.style('stroke', $$.color)
.style("pointer-events", "none") .style("pointer-events", "none")
.style('fill', $$.color) .attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200)
.attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) .attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile)
.attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegend) .attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200)
.attr('width', 10) .attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile)
.attr('height', 10); .attr('stroke-width', config.legend_item_tile_height);
// Set background for inset legend // Set background for inset legend
background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); background = $$.legend.select('.' + CLASS.legendBackground + ' rect');
@ -291,12 +304,14 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
.attr('x', xForLegendRect) .attr('x', xForLegendRect)
.attr('y', yForLegendRect); .attr('y', yForLegendRect);
tiles = $$.legend.selectAll('rect.' + CLASS.legendItemTile) tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile)
.data(targetIds); .data(targetIds);
(withTransition ? tiles.transition() : tiles) (withTransition ? tiles.transition() : tiles)
.style('fill', $$.color) .style('stroke', $$.color)
.attr('x', xForLegend) .attr('x1', x1ForLegendTile)
.attr('y', yForLegend); .attr('y1', yForLegendTile)
.attr('x2', x2ForLegendTile)
.attr('y2', yForLegendTile);
if (background) { if (background) {
(withTransition ? background.transition() : background) (withTransition ? background.transition() : background)

32
src/polyfill.js

@ -1,7 +1,25 @@
// fix problems using c3 with phantomjs #578 // PhantomJS doesn't have support for Function.prototype.bind, which has caused confusion. Use
Function.prototype.bind = Function.prototype.bind || function (thisp) { // this polyfill to avoid the confusion.
var fn = this; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill
return function () {
return fn.apply(thisp, arguments); if (!Function.prototype.bind) {
}; Function.prototype.bind = function(oThis) {
}; if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}

20
src/scale.js

@ -44,10 +44,10 @@ c3_chart_internal_fn.getY = function (min, max, domain) {
return scale; return scale;
}; };
c3_chart_internal_fn.getYScale = function (id) { c3_chart_internal_fn.getYScale = function (id) {
return this.getAxisId(id) === 'y2' ? this.y2 : this.y; return this.axis.getId(id) === 'y2' ? this.y2 : this.y;
}; };
c3_chart_internal_fn.getSubYScale = function (id) { c3_chart_internal_fn.getSubYScale = function (id) {
return this.getAxisId(id) === 'y2' ? this.subY2 : this.subY; return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY;
}; };
c3_chart_internal_fn.updateScales = function () { c3_chart_internal_fn.updateScales = function () {
var $$ = this, config = $$.config, var $$ = this, config = $$.config,
@ -69,15 +69,15 @@ c3_chart_internal_fn.updateScales = function () {
$$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y_default : $$.subY.domain()); $$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y_default : $$.subY.domain());
$$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain()); $$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain());
// update axes // update axes
$$.xAxisTickFormat = $$.getXAxisTickFormat(); $$.xAxisTickFormat = $$.axis.getXAxisTickFormat();
$$.xAxisTickValues = $$.getXAxisTickValues(); $$.xAxisTickValues = $$.axis.getXAxisTickValues();
$$.yAxisTickValues = $$.getYAxisTickValues(); $$.yAxisTickValues = $$.axis.getYAxisTickValues();
$$.y2AxisTickValues = $$.getY2AxisTickValues(); $$.y2AxisTickValues = $$.axis.getY2AxisTickValues();
$$.xAxis = $$.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); $$.xAxis = $$.axis.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
$$.subXAxis = $$.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); $$.subXAxis = $$.axis.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
$$.yAxis = $$.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, config.axis_y_tick_outer); $$.yAxis = $$.axis.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); $$.y2Axis = $$.axis.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, config.axis_y2_tick_outer);
// Set initialized scales to brush and zoom // Set initialized scales to brush and zoom
if (!forInit) { if (!forInit) {

1
src/scss/chart.scss

@ -1,5 +1,6 @@
.c3 svg { .c3 svg {
font: 10px sans-serif; font: 10px sans-serif;
-webkit-tap-highlight-color: rgba(0,0,0,0);
} }
.c3 path, .c3 line { .c3 path, .c3 line {
fill: none; fill: none;

4
src/scss/main.scss

@ -46,6 +46,10 @@
@import 'legend'; @import 'legend';
/*-- Title --*/
@import 'title';
/*-- Tooltip --*/ /*-- Tooltip --*/
@import 'tooltip'; @import 'tooltip';

3
src/scss/title.scss

@ -0,0 +1,3 @@
.c3-title {
font: 14px sans-serif;
}

2
src/selection.js

@ -18,7 +18,7 @@ c3_chart_internal_fn.selectPoint = function (target, d, i) {
}; };
c3_chart_internal_fn.unselectPoint = function (target, d, i) { c3_chart_internal_fn.unselectPoint = function (target, d, i) {
var $$ = this; var $$ = this;
$$.config.data_onunselected(d, target.node()); $$.config.data_onunselected.call($$.api, d, target.node());
// remove selected-circle from low layer g // remove selected-circle from low layer g
$$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i) $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i)
.transition().duration(100).attr('r', 0) .transition().duration(100).attr('r', 0)

2
src/shape.bar.js

@ -42,7 +42,7 @@ c3_chart_internal_fn.updateBar = function (durationForExit) {
}; };
c3_chart_internal_fn.redrawBar = function (drawBar, withTransition) { c3_chart_internal_fn.redrawBar = function (drawBar, withTransition) {
return [ return [
(withTransition ? this.mainBar.transition() : this.mainBar) (withTransition ? this.mainBar.transition(Math.random().toString()) : this.mainBar)
.attr('d', drawBar) .attr('d', drawBar)
.style("fill", this.color) .style("fill", this.color)
.style("opacity", 1) .style("opacity", 1)

17
src/shape.js

@ -41,7 +41,17 @@ c3_chart_internal_fn.getShapeOffset = function (typeFilter, indices, isSub) {
var values = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values; var values = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values;
if (t.id === d.id || indices[t.id] !== indices[d.id]) { return; } if (t.id === d.id || indices[t.id] !== indices[d.id]) { return; }
if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) { if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) {
if (values[i].value * d.value >= 0) { // check if the x values line up
if (typeof values[i] === 'undefined' || +values[i].x !== +d.x) { // "+" for timeseries
// if not, try to find the value that does line up
i = -1;
values.forEach(function (v, j) {
if (v.x === d.x) {
i = j;
}
});
}
if (i in values && values[i].value * d.value >= 0) {
offset += scale(values[i].value) - y0; offset += scale(values[i].value) - y0;
} }
} }
@ -66,6 +76,7 @@ c3_chart_internal_fn.isWithinShape = function (that, d) {
c3_chart_internal_fn.getInterpolate = function (d) { c3_chart_internal_fn.getInterpolate = function (d) {
var $$ = this; var $$ = this,
return $$.isSplineType(d) ? "cardinal" : $$.isStepType(d) ? $$.config.line_step_type : "linear"; interpolation = $$.isInterpolationType($$.config.spline_interpolation_type) ? $$.config.spline_interpolation_type : 'cardinal';
return $$.isSplineType(d) ? interpolation : $$.isStepType(d) ? $$.config.line_step_type : "linear";
}; };

32
src/shape.line.js

@ -56,7 +56,7 @@ c3_chart_internal_fn.updateLine = function (durationForExit) {
}; };
c3_chart_internal_fn.redrawLine = function (drawLine, withTransition) { c3_chart_internal_fn.redrawLine = function (drawLine, withTransition) {
return [ return [
(withTransition ? this.mainLine.transition() : this.mainLine) (withTransition ? this.mainLine.transition(Math.random().toString()) : this.mainLine)
.attr("d", drawLine) .attr("d", drawLine)
.style("stroke", this.color) .style("stroke", this.color)
.style("opacity", 1) .style("opacity", 1)
@ -125,6 +125,7 @@ c3_chart_internal_fn.lineWithRegions = function (d, x, y, _regions) {
prev = -1, i, j, prev = -1, i, j,
s = "M", sWithRegion, s = "M", sWithRegion,
xp, yp, dx, dy, dd, diff, diffx2, xp, yp, dx, dy, dd, diff, diffx2,
xOffset = $$.isCategorized() ? 0.5 : 0,
xValue, yValue, xValue, yValue,
regions = []; regions = [];
@ -158,16 +159,31 @@ c3_chart_internal_fn.lineWithRegions = function (d, x, y, _regions) {
yValue = config.axis_rotated ? function (d) { return x(d.x); } : function (d) { return y(d.value); }; yValue = config.axis_rotated ? function (d) { return x(d.x); } : function (d) { return y(d.value); };
// Define svg generator function for region // Define svg generator function for region
function generateM(points) {
return 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1];
}
if ($$.isTimeSeries()) { if ($$.isTimeSeries()) {
sWithRegion = function (d0, d1, j, diff) { sWithRegion = function (d0, d1, j, diff) {
var x0 = d0.x.getTime(), x_diff = d1.x - d0.x, var x0 = d0.x.getTime(), x_diff = d1.x - d0.x,
xv0 = new Date(x0 + x_diff * j), xv0 = new Date(x0 + x_diff * j),
xv1 = new Date(x0 + x_diff * (j + diff)); xv1 = new Date(x0 + x_diff * (j + diff)),
return "M" + x(xv0) + " " + y(yp(j)) + " " + x(xv1) + " " + y(yp(j + diff)); points;
if (config.axis_rotated) {
points = [[y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)]];
} else {
points = [[x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))]];
}
return generateM(points);
}; };
} else { } else {
sWithRegion = function (d0, d1, j, diff) { sWithRegion = function (d0, d1, j, diff) {
return "M" + x(xp(j), true) + " " + y(yp(j)) + " " + x(xp(j + diff), true) + " " + y(yp(j + diff)); var points;
if (config.axis_rotated) {
points = [[y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))]];
} else {
points = [[x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))]];
}
return generateM(points);
}; };
} }
@ -180,7 +196,7 @@ c3_chart_internal_fn.lineWithRegions = function (d, x, y, _regions) {
} }
// Draw with region // TODO: Fix for horizotal charts // Draw with region // TODO: Fix for horizotal charts
else { else {
xp = $$.getScale(d[i - 1].x, d[i].x, $$.isTimeSeries()); xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries());
yp = $$.getScale(d[i - 1].value, d[i].value); yp = $$.getScale(d[i - 1].value, d[i].value);
dx = x(d[i].x) - x(d[i - 1].x); dx = x(d[i].x) - x(d[i - 1].x);
@ -216,7 +232,7 @@ c3_chart_internal_fn.updateArea = function (durationForExit) {
}; };
c3_chart_internal_fn.redrawArea = function (drawArea, withTransition) { c3_chart_internal_fn.redrawArea = function (drawArea, withTransition) {
return [ return [
(withTransition ? this.mainArea.transition() : this.mainArea) (withTransition ? this.mainArea.transition(Math.random().toString()) : this.mainArea)
.attr("d", drawArea) .attr("d", drawArea)
.style("fill", this.color) .style("fill", this.color)
.style("opacity", this.orgAreaOpacity) .style("opacity", this.orgAreaOpacity)
@ -299,12 +315,12 @@ c3_chart_internal_fn.updateCircle = function () {
c3_chart_internal_fn.redrawCircle = function (cx, cy, withTransition) { c3_chart_internal_fn.redrawCircle = function (cx, cy, withTransition) {
var selectedCircles = this.main.selectAll('.' + CLASS.selectedCircle); var selectedCircles = this.main.selectAll('.' + CLASS.selectedCircle);
return [ return [
(withTransition ? this.mainCircle.transition() : this.mainCircle) (withTransition ? this.mainCircle.transition(Math.random().toString()) : this.mainCircle)
.style('opacity', this.opacityForCircle.bind(this)) .style('opacity', this.opacityForCircle.bind(this))
.style("fill", this.color) .style("fill", this.color)
.attr("cx", cx) .attr("cx", cx)
.attr("cy", cy), .attr("cy", cy),
(withTransition ? selectedCircles.transition() : selectedCircles) (withTransition ? selectedCircles.transition(Math.random().toString()) : selectedCircles)
.attr("cx", cx) .attr("cx", cx)
.attr("cy", cy) .attr("cy", cy)
]; ];

33
src/size.js

@ -8,8 +8,13 @@ c3_chart_internal_fn.getCurrentHeight = function () {
return h > 0 ? h : 320 / ($$.hasType('gauge') ? 2 : 1); return h > 0 ? h : 320 / ($$.hasType('gauge') ? 2 : 1);
}; };
c3_chart_internal_fn.getCurrentPaddingTop = function () { c3_chart_internal_fn.getCurrentPaddingTop = function () {
var config = this.config; var $$ = this,
return isValue(config.padding_top) ? config.padding_top : 0; config = $$.config,
padding = isValue(config.padding_top) ? config.padding_top : 0;
if ($$.title && $$.title.node()) {
padding += $$.getTitlePadding();
}
return padding;
}; };
c3_chart_internal_fn.getCurrentPaddingBottom = function () { c3_chart_internal_fn.getCurrentPaddingBottom = function () {
var config = this.config; var config = this.config;
@ -22,7 +27,7 @@ c3_chart_internal_fn.getCurrentPaddingLeft = function (withoutRecompute) {
} else if (config.axis_rotated) { } else if (config.axis_rotated) {
return !config.axis_x_show ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40); return !config.axis_x_show ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40);
} else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated } else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated
return $$.getYAxisLabelPosition().isOuter ? 30 : 1; return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1;
} else { } else {
return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute)); return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute));
} }
@ -35,7 +40,7 @@ c3_chart_internal_fn.getCurrentPaddingRight = function () {
} else if (config.axis_rotated) { } else if (config.axis_rotated) {
return defaultPadding + legendWidthOnRight; return defaultPadding + legendWidthOnRight;
} else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated } else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated
return 2 + legendWidthOnRight + ($$.getY2AxisLabelPosition().isOuter ? 20 : 0); return 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0);
} else { } else {
return ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight; return ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight;
} }
@ -44,7 +49,15 @@ c3_chart_internal_fn.getCurrentPaddingRight = function () {
c3_chart_internal_fn.getParentRectValue = function (key) { c3_chart_internal_fn.getParentRectValue = function (key) {
var parent = this.selectChart.node(), v; var parent = this.selectChart.node(), v;
while (parent && parent.tagName !== 'BODY') { while (parent && parent.tagName !== 'BODY') {
v = parent.getBoundingClientRect()[key]; try {
v = parent.getBoundingClientRect()[key];
} catch(e) {
if (key === 'width') {
// In IE in certain cases getBoundingClientRect
// will cause an "unspecified error"
v = parent.offsetWidth;
}
}
if (v) { if (v) {
break; break;
} }
@ -75,8 +88,8 @@ c3_chart_internal_fn.getSvgLeft = function (withoutRecompute) {
c3_chart_internal_fn.getAxisWidthByAxisId = function (id, withoutRecompute) { c3_chart_internal_fn.getAxisWidthByAxisId = function (id, withoutRecompute) {
var $$ = this, position = $$.getAxisLabelPositionById(id); var $$ = this, position = $$.axis.getLabelPositionById(id);
return $$.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40); return $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40);
}; };
c3_chart_internal_fn.getHorizontalAxisHeight = function (axisId) { c3_chart_internal_fn.getHorizontalAxisHeight = function (axisId) {
var $$ = this, config = $$.config, h = 30; var $$ = this, config = $$.config, h = 30;
@ -86,11 +99,11 @@ c3_chart_internal_fn.getHorizontalAxisHeight = function (axisId) {
if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top; } if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top; }
// Calculate x axis height when tick rotated // Calculate x axis height when tick rotated
if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) { 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); h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - config.axis_x_tick_rotate) / 180);
} }
return h + ($$.getAxisLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0); return h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0);
}; };
c3_chart_internal_fn.getEventRectWidth = function () { c3_chart_internal_fn.getEventRectWidth = function () {
return this.xAxis.tickInterval(); return Math.max(0, this.xAxis.tickInterval());
}; };

14
src/subchart.js

@ -11,9 +11,10 @@ c3_chart_internal_fn.initBrush = function () {
}; };
c3_chart_internal_fn.initSubchart = function () { c3_chart_internal_fn.initSubchart = function () {
var $$ = this, config = $$.config, var $$ = this, config = $$.config,
context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')); context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')),
visibility = config.subchart_show ? 'visible' : 'hidden';
context.style('visibility', config.subchart_show ? 'visible' : 'hidden'); context.style('visibility', visibility);
// Define g for chart area // Define g for chart area
context.append('g') context.append('g')
@ -39,7 +40,8 @@ c3_chart_internal_fn.initSubchart = function () {
$$.axes.subx = context.append("g") $$.axes.subx = context.append("g")
.attr("class", CLASS.axisX) .attr("class", CLASS.axisX)
.attr("transform", $$.getTranslate('subx')) .attr("transform", $$.getTranslate('subx'))
.attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis); .attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis)
.style("visibility", config.subchart_axis_x_show ? visibility : 'hidden');
}; };
c3_chart_internal_fn.updateTargetsForSubchart = function (targets) { c3_chart_internal_fn.updateTargetsForSubchart = function (targets) {
var $$ = this, context = $$.context, config = $$.config, var $$ = this, context = $$.context, config = $$.config,
@ -96,7 +98,7 @@ c3_chart_internal_fn.updateBarForSubchart = function (durationForExit) {
.remove(); .remove();
}; };
c3_chart_internal_fn.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) { c3_chart_internal_fn.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) {
(withTransition ? this.contextBar.transition().duration(duration) : this.contextBar) (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar)
.attr('d', drawBarOnSub) .attr('d', drawBarOnSub)
.style('opacity', 1); .style('opacity', 1);
}; };
@ -114,7 +116,7 @@ c3_chart_internal_fn.updateLineForSubchart = function (durationForExit) {
.remove(); .remove();
}; };
c3_chart_internal_fn.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) { c3_chart_internal_fn.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) {
(withTransition ? this.contextLine.transition().duration(duration) : this.contextLine) (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine)
.attr("d", drawLineOnSub) .attr("d", drawLineOnSub)
.style('opacity', 1); .style('opacity', 1);
}; };
@ -133,7 +135,7 @@ c3_chart_internal_fn.updateAreaForSubchart = function (durationForExit) {
.remove(); .remove();
}; };
c3_chart_internal_fn.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) { c3_chart_internal_fn.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) {
(withTransition ? this.contextArea.transition().duration(duration) : this.contextArea) (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea)
.attr("d", drawAreaOnSub) .attr("d", drawAreaOnSub)
.style("fill", this.color) .style("fill", this.color)
.style("opacity", this.orgAreaOpacity); .style("opacity", this.orgAreaOpacity);

2
src/tail.js

@ -1,5 +1,5 @@
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
define("c3", ["d3"], c3); define("c3", ["d3"], function () { return c3; });
} else if ('undefined' !== typeof exports && 'undefined' !== typeof module) { } else if ('undefined' !== typeof exports && 'undefined' !== typeof module) {
module.exports = c3; module.exports = c3;
} else { } else {

30
src/text.js

@ -47,22 +47,24 @@ c3_chart_internal_fn.redrawText = function (xForText, yForText, forFlow, withTra
.style("fill-opacity", forFlow ? 0 : this.opacityForText.bind(this)) .style("fill-opacity", forFlow ? 0 : this.opacityForText.bind(this))
]; ];
}; };
c3_chart_internal_fn.getTextRect = function (text, cls) { c3_chart_internal_fn.getTextRect = function (text, cls, element) {
var body = this.d3.select('body').classed('c3', true), var dummy = this.d3.select('body').append('div').classed('c3', true),
svg = body.append("svg").style('visibility', 'hidden').style('height', 0), rect; svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0),
font = this.d3.select(element).style('font'),
rect;
svg.selectAll('.dummy') svg.selectAll('.dummy')
.data([text]) .data([text])
.enter().append('text') .enter().append('text')
.classed(cls ? cls : "", true) .classed(cls ? cls : "", true)
.style('font', font)
.text(text) .text(text)
.each(function () { rect = this.getBoundingClientRect(); }); .each(function () { rect = this.getBoundingClientRect(); });
svg.remove(); dummy.remove();
body.classed('c3', false);
return rect; return rect;
}; };
c3_chart_internal_fn.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) { c3_chart_internal_fn.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) {
var $$ = this, var $$ = this,
getAreaPoints = $$.generateGetAreaPoints(barIndices, false), getAreaPoints = $$.generateGetAreaPoints(areaIndices, false),
getBarPoints = $$.generateGetBarPoints(barIndices, false), getBarPoints = $$.generateGetBarPoints(barIndices, false),
getLinePoints = $$.generateGetLinePoints(lineIndices, false), getLinePoints = $$.generateGetLinePoints(lineIndices, false),
getter = forX ? $$.getXForText : $$.getYForText; getter = forX ? $$.getXForText : $$.getYForText;
@ -92,11 +94,23 @@ c3_chart_internal_fn.getXForText = function (points, d, textElement) {
}; };
c3_chart_internal_fn.getYForText = function (points, d, textElement) { c3_chart_internal_fn.getYForText = function (points, d, textElement) {
var $$ = this, var $$ = this,
box = textElement.getBoundingClientRect(), yPos; box = textElement.getBoundingClientRect(),
yPos;
if ($$.config.axis_rotated) { if ($$.config.axis_rotated) {
yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2; yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2;
} else { } else {
yPos = points[2][1] + (d.value < 0 ? box.height : $$.isBarType(d) ? -3 : -6); yPos = points[2][1];
if (d.value < 0 || (d.value === 0 && !$$.hasPositiveValue)) {
yPos += box.height;
if ($$.isBarType(d) && $$.isSafari()) {
yPos -= 3;
}
else if (!$$.isBarType(d) && $$.isChrome()) {
yPos += 3;
}
} else {
yPos += $$.isBarType(d) ? -3 : -6;
}
} }
// show labels regardless of the domain if value is null // show labels regardless of the domain if value is null
if (d.value === null && !$$.config.axis_rotated) { if (d.value === null && !$$.config.axis_rotated) {

31
src/title.js

@ -0,0 +1,31 @@
c3_chart_internal_fn.initTitle = function () {
var $$ = this;
$$.title = $$.svg.append("text")
.text($$.config.title_text)
.attr("class", $$.CLASS.title);
};
c3_chart_internal_fn.redrawTitle = function () {
var $$ = this;
$$.title
.attr("x", $$.xForTitle.bind($$))
.attr("y", $$.yForTitle.bind($$));
};
c3_chart_internal_fn.xForTitle = function () {
var $$ = this, config = $$.config, position = config.title_position || 'left', x;
if (position.indexOf('right') >= 0) {
x = $$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width - config.title_padding.right;
} else if (position.indexOf('center') >= 0) {
x = ($$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width) / 2;
} else { // left
x = config.title_padding.left;
}
return x;
};
c3_chart_internal_fn.yForTitle = function () {
var $$ = this;
return $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).height;
};
c3_chart_internal_fn.getTitlePadding = function() {
var $$ = this;
return $$.yForTitle() + $$.config.title_padding.bottom;
};

34
src/tooltip.js

@ -18,7 +18,7 @@ c3_chart_internal_fn.initTooltip = function () {
} }
$$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) { $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) {
return $$.addName(d.values[config.tooltip_init_x]); return $$.addName(d.values[config.tooltip_init_x]);
}), $$.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color)); }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color));
$$.tooltip.style("top", config.tooltip_init_position.top) $$.tooltip.style("top", config.tooltip_init_position.top)
.style("left", config.tooltip_init_position.left) .style("left", config.tooltip_init_position.left)
.style("display", "block"); .style("display", "block");
@ -29,21 +29,42 @@ c3_chart_internal_fn.getTooltipContent = function (d, defaultTitleFormat, defaul
titleFormat = config.tooltip_format_title || defaultTitleFormat, titleFormat = config.tooltip_format_title || defaultTitleFormat,
nameFormat = config.tooltip_format_name || function (name) { return name; }, nameFormat = config.tooltip_format_name || function (name) { return name; },
valueFormat = config.tooltip_format_value || defaultValueFormat, valueFormat = config.tooltip_format_value || defaultValueFormat,
text, i, title, value, name, bgcolor; text, i, title, value, name, bgcolor,
orderAsc = $$.isOrderAsc();
if (config.data_groups.length === 0) {
d.sort(function(a,b){
return orderAsc ? a.value - b.value : b.value - a.value;
});
} else {
var ids = $$.orderTargets($$.data.targets).map(function (i) {
return i.id;
});
d.sort(function(a, b) {
if (a.value > 0 && b.value > 0) {
return orderAsc ? ids.indexOf(a.id) - ids.indexOf(b.id) : ids.indexOf(b.id) - ids.indexOf(a.id);
} else {
return orderAsc ? a.value - b.value : b.value - a.value;
}
});
}
for (i = 0; i < d.length; i++) { for (i = 0; i < d.length; i++) {
if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; } if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
if (! text) { if (! text) {
title = titleFormat ? titleFormat(d[i].x) : d[i].x; title = titleFormat ? titleFormat(d[i].x) : d[i].x;
text = "<table class='" + CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : ""); text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
} }
value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index); value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
if (value !== undefined) { if (value !== undefined) {
// Skip elements when their name is set to null
if (d[i].name === null) { continue; }
name = nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index); name = nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index);
bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
text += "<tr class='" + CLASS.tooltipName + "-" + d[i].id + "'>"; text += "<tr class='" + $$.CLASS.tooltipName + "-" + $$.getTargetSelectorSuffix(d[i].id) + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>"; text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
text += "<td class='value'>" + value + "</td>"; text += "<td class='value'>" + value + "</td>";
text += "</tr>"; text += "</tr>";
@ -75,7 +96,8 @@ c3_chart_internal_fn.tooltipPosition = function (dataToShow, tWidth, tHeight, el
} }
if (tooltipRight > chartRight) { if (tooltipRight > chartRight) {
tooltipLeft -= tooltipRight - chartRight; // 20 is needed for Firefox to keep tooltip width
tooltipLeft -= tooltipRight - chartRight + 20;
} }
if (tooltipTop + tHeight > $$.currentHeight) { if (tooltipTop + tHeight > $$.currentHeight) {
tooltipTop -= tHeight + 30; tooltipTop -= tHeight + 30;
@ -95,7 +117,7 @@ c3_chart_internal_fn.showTooltip = function (selectedData, element) {
if (dataToShow.length === 0 || !config.tooltip_show) { if (dataToShow.length === 0 || !config.tooltip_show) {
return; return;
} }
$$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block"); $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block");
// Get tooltip dimensions // Get tooltip dimensions
tWidth = $$.tooltip.property('offsetWidth'); tWidth = $$.tooltip.property('offsetWidth');

3
src/type.js

@ -89,3 +89,6 @@ c3_chart_internal_fn.lineOrScatterData = function (d) {
c3_chart_internal_fn.barOrLineData = function (d) { c3_chart_internal_fn.barOrLineData = function (d) {
return this.isBarType(d) || this.isLineType(d) ? d.values : []; return this.isBarType(d) || this.isLineType(d) ? d.values : [];
}; };
c3_chart_internal_fn.isInterpolationType = function (type) {
return ['linear', 'linear-closed', 'basis', 'basis-open', 'basis-closed', 'bundle', 'cardinal', 'cardinal-open', 'cardinal-closed', 'monotone'].indexOf(type) >= 0;
};

8
src/ua.js

@ -0,0 +1,8 @@
c3_chart_internal_fn.isSafari = function () {
var ua = window.navigator.userAgent;
return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0;
};
c3_chart_internal_fn.isChrome = function () {
var ua = window.navigator.userAgent;
return ua.indexOf('Chrome') >= 0;
};

4
src/util.js

@ -23,10 +23,10 @@ var isValue = c3_chart_internal_fn.isValue = function (v) {
return d[1] - d[0]; return d[1] - d[0];
}, },
isEmpty = c3_chart_internal_fn.isEmpty = function (o) { isEmpty = c3_chart_internal_fn.isEmpty = function (o) {
return !o || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0); return typeof o === 'undefined' || o === null || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0);
}, },
notEmpty = c3_chart_internal_fn.notEmpty = function (o) { notEmpty = c3_chart_internal_fn.notEmpty = function (o) {
return Object.keys(o).length > 0; return !c3_chart_internal_fn.isEmpty(o);
}, },
getOption = c3_chart_internal_fn.getOption = function (options, key, defaultValue) { getOption = c3_chart_internal_fn.getOption = function (options, key, defaultValue) {
return isDefined(options[key]) ? options[key] : defaultValue; return isDefined(options[key]) ? options[key] : defaultValue;

8
src/zoom.js

@ -28,12 +28,18 @@ c3_chart_internal_fn.initZoom = function () {
return [extent[0], Math.max($$.getMaxDataCount() / extent[1], extent[1])]; return [extent[0], Math.max($$.getMaxDataCount() / extent[1], extent[1])];
}; };
$$.zoom.updateScaleExtent = function () { $$.zoom.updateScaleExtent = function () {
var ratio = diffDomain($$.x.orgDomain()) / diffDomain($$.orgXDomain), var ratio = diffDomain($$.x.orgDomain()) / diffDomain($$.getZoomDomain()),
extent = this.orgScaleExtent(); extent = this.orgScaleExtent();
this.scaleExtent([extent[0] * ratio, extent[1] * ratio]); this.scaleExtent([extent[0] * ratio, extent[1] * ratio]);
return this; return this;
}; };
}; };
c3_chart_internal_fn.getZoomDomain = function () {
var $$ = this, config = $$.config, d3 = $$.d3,
min = d3.min([$$.orgXDomain[0], config.zoom_x_min]),
max = d3.max([$$.orgXDomain[1], config.zoom_x_max]);
return [min, max];
};
c3_chart_internal_fn.updateZoom = function () { c3_chart_internal_fn.updateZoom = function () {
var $$ = this, z = $$.config.zoom_enabled ? $$.zoom : function () {}; var $$ = this, z = $$.config.zoom_enabled ? $$.zoom : function () {};
$$.main.select('.' + CLASS.zoomRect).call(z).on("dblclick.zoom", null); $$.main.select('.' + CLASS.zoomRect).call(z).on("dblclick.zoom", null);

Loading…
Cancel
Save