mirror of https://github.com/masayuki0812/c3.git
Quite good looking graph derived from d3.js
http://c3js.org
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1543 lines
60 KiB
1543 lines
60 KiB
10 years ago
|
var c3 = { version: "0.3.0" };
|
||
|
|
||
|
var c3_chart_fn, c3_chart_internal_fn;
|
||
|
|
||
|
function Chart(config) {
|
||
|
var $$ = this.internal = new ChartInternal(config, this);
|
||
|
$$.loadConfig(config);
|
||
|
$$.init();
|
||
|
}
|
||
|
|
||
|
function ChartInternal(config, api) {
|
||
|
var $$ = this;
|
||
|
$$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require("d3") : undefined;
|
||
|
$$.api = api;
|
||
|
$$.data = {};
|
||
|
$$.cache = {};
|
||
|
$$.axes = {};
|
||
|
}
|
||
|
|
||
|
c3.generate = function (config) {
|
||
|
return new Chart(config);
|
||
|
};
|
||
|
|
||
|
c3.chart = {
|
||
|
fn: Chart.prototype,
|
||
|
internal: {
|
||
|
fn: ChartInternal.prototype
|
||
|
}
|
||
|
};
|
||
|
c3_chart_fn = c3.chart.fn;
|
||
|
c3_chart_internal_fn = c3.chart.internal.fn;
|
||
|
|
||
|
|
||
|
c3_chart_internal_fn.init = function () {
|
||
|
var $$ = this, config = $$.config;
|
||
|
|
||
|
$$.initParams();
|
||
|
|
||
|
if (config[__data_url]) {
|
||
|
$$.convertUrlToData(config[__data_url], config[__data_mimeType], config[__data_keys], $$.initWithData);
|
||
|
}
|
||
|
else if (config[__data_json]) {
|
||
|
$$.initWithData($$.convertJsonToData(config[__data_json], config[__data_keys]));
|
||
|
}
|
||
|
else if (config[__data_rows]) {
|
||
|
$$.initWithData($$.convertRowsToData(config[__data_rows]));
|
||
|
}
|
||
|
else if (config[__data_columns]) {
|
||
|
$$.initWithData($$.convertColumnsToData(config[__data_columns]));
|
||
|
}
|
||
|
else {
|
||
|
throw Error('url or json or rows or columns is required.');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.initParams = function () {
|
||
|
var $$ = this, d3 = $$.d3;
|
||
|
|
||
|
// MEMO: clipId needs to be unique because it conflicts when multiple charts exist
|
||
|
$$.clipId = "c3-" + (+new Date()) + '-clip',
|
||
|
$$.clipIdForXAxis = $$.clipId + '-xaxis',
|
||
|
$$.clipIdForYAxis = $$.clipId + '-yaxis',
|
||
|
$$.clipPath = $$.getClipPath($$.clipId),
|
||
|
$$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis),
|
||
|
$$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis);
|
||
|
|
||
|
$$.dragStart = null;
|
||
|
$$.dragging = false;
|
||
|
$$.cancelClick = false;
|
||
|
$$.mouseover = false;
|
||
|
$$.transiting = false;
|
||
|
|
||
|
$$.color = $$.generateColor();
|
||
|
$$.levelColor = $$.generateLevelColor();
|
||
|
|
||
|
$$.dataTimeFormat = config[__data_x_localtime] ? d3.time.format : d3.time.format.utc;
|
||
|
$$.axisTimeFormat = config[__axis_x_localtime] ? d3.time.format : d3.time.format.utc;
|
||
|
$$.defaultAxisTimeFormat = $$.axisTimeFormat.multi([
|
||
|
[".%L", function (d) { return d.getMilliseconds(); }],
|
||
|
[":%S", function (d) { return d.getSeconds(); }],
|
||
|
["%I:%M", function (d) { return d.getMinutes(); }],
|
||
|
["%I %p", function (d) { return d.getHours(); }],
|
||
|
["%-m/%-d", function (d) { return d.getDay() && d.getDate() !== 1; }],
|
||
|
["%-m/%-d", function (d) { return d.getDate() !== 1; }],
|
||
|
["%-m/%-d", function (d) { return d.getMonth(); }],
|
||
|
["%Y/%-m/%-d", function () { return true; }]
|
||
|
]);
|
||
|
|
||
|
$$.hiddenTargetIds = [];
|
||
|
$$.hiddenLegendIds = [];
|
||
|
|
||
|
$$.xOrient = config[__axis_rotated] ? "left" : "bottom";
|
||
|
$$.yOrient = config[__axis_rotated] ? "bottom" : "left";
|
||
|
$$.y2Orient = config[__axis_rotated] ? "top" : "right";
|
||
|
$$.subXOrient = config[__axis_rotated] ? "left" : "bottom";
|
||
|
|
||
|
$$.isLegendRight = config[__legend_position] === 'right';
|
||
|
$$.isLegendInset = config[__legend_position] === 'inset';
|
||
|
$$.isLegendTop = config[__legend_inset_anchor] === 'top-left' || config[__legend_inset_anchor] === 'top-right';
|
||
|
$$.isLegendLeft = config[__legend_inset_anchor] === 'top-left' || config[__legend_inset_anchor] === 'bottom-left';
|
||
|
$$.legendStep = 0;
|
||
|
$$.legendItemWidth = 0;
|
||
|
$$.legendItemHeight = 0;
|
||
|
$$.legendOpacityForHidden = 0.15;
|
||
|
|
||
|
$$.currentMaxTickWidth = 0;
|
||
|
|
||
|
$$.rotated_padding_left = 30;
|
||
|
$$.rotated_padding_right = config[__axis_rotated] && !config[__axis_x_show] ? 0 : 30;
|
||
|
$$.rotated_padding_top = 5;
|
||
|
|
||
|
$$.withoutFadeIn = {};
|
||
|
|
||
|
$$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.initWithData = function (data) {
|
||
|
var $$ = this, d3 = $$.d3, config = $$.config;
|
||
|
var main, eventRect, binding = true;
|
||
|
|
||
|
if (isFunction($$.initPie)) { $$.initPie(); }
|
||
|
if (isFunction($$.initBrush)) { $$.initBrush(); }
|
||
|
if (isFunction($$.initZoom)) { $$.initZoom(); }
|
||
|
|
||
|
$$.selectChart = d3.select(config[__bindto]);
|
||
|
if ($$.selectChart.empty()) {
|
||
|
$$.selectChart = d3.select(document.createElement('div')).style('opacity', 0);
|
||
|
$$.observeInserted($$.selectChart);
|
||
|
binding = false;
|
||
|
}
|
||
|
$$.selectChart.html("").classed("c3", true);
|
||
|
|
||
|
// Init data as targets
|
||
|
$$.data.xs = {};
|
||
|
$$.data.targets = $$.convertDataToTargets(data);
|
||
|
|
||
|
if (config[__data_filter]) {
|
||
|
$$.data.targets = $$.data.targets.filter(config[__data_filter]);
|
||
|
}
|
||
|
|
||
|
// Set targets to hide if needed
|
||
|
if (config[__data_hide]) {
|
||
|
$$.addHiddenTargetIds(config[__data_hide] === true ? $$.mapToIds($$.data.targets) : config[__data_hide]);
|
||
|
}
|
||
|
|
||
|
// when gauge, hide legend // TODO: fix
|
||
|
if ($$.hasType('gauge')) {
|
||
|
config[__legend_show] = false;
|
||
|
}
|
||
|
|
||
|
// Init sizes and scales
|
||
|
$$.updateSizes();
|
||
|
$$.updateScales();
|
||
|
|
||
|
// Set domains for each scale
|
||
|
$$.x.domain(d3.extent($$.getXDomain($$.data.targets)));
|
||
|
$$.y.domain($$.getYDomain($$.data.targets, 'y'));
|
||
|
$$.y2.domain($$.getYDomain($$.data.targets, 'y2'));
|
||
|
$$.subX.domain($$.x.domain());
|
||
|
$$.subY.domain($$.y.domain());
|
||
|
$$.subY2.domain($$.y2.domain());
|
||
|
|
||
|
// Save original x domain for zoom update
|
||
|
$$.orgXDomain = $$.x.domain();
|
||
|
|
||
|
// Set initialized scales to brush and zoom
|
||
|
if ($$.brush) { $$.brush.scale($$.subX); }
|
||
|
if (config[__zoom_enabled]) { $$.zoom.scale($$.x); }
|
||
|
|
||
|
/*-- Basic Elements --*/
|
||
|
|
||
|
// Define svgs
|
||
|
$$.svg = $$.selectChart.append("svg")
|
||
|
.style("overflow", "hidden")
|
||
|
.on('mouseenter', function () { return config[__onmouseover].call($$); })
|
||
|
.on('mouseleave', function () { return config[__onmouseout].call($$); });
|
||
|
|
||
|
// Define defs
|
||
|
$$.defs = $$.svg.append("defs");
|
||
|
$$.defs.append("clipPath").attr("id", $$.clipId).append("rect");
|
||
|
$$.defs.append("clipPath").attr("id", $$.clipIdForXAxis).append("rect");
|
||
|
$$.defs.append("clipPath").attr("id", $$.clipIdForYAxis).append("rect");
|
||
|
$$.updateSvgSize();
|
||
|
|
||
|
// Define regions
|
||
|
main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main'));
|
||
|
|
||
|
if (isFunction($$.initSubchart)) { $$.initSubchart(); }
|
||
|
if (isFunction($$.initTooltip)) { $$.initTooltip(); }
|
||
|
if (isFunction($$.initLegend)) { $$.initLegend(); }
|
||
|
|
||
|
/*-- Main Region --*/
|
||
|
|
||
|
// text when empty
|
||
|
main.append("text")
|
||
|
.attr("class", CLASS[_text] + ' ' + CLASS[_empty])
|
||
|
.attr("text-anchor", "middle") // horizontal centering of text at x position in all browsers.
|
||
|
.attr("dominant-baseline", "middle"); // vertical centering of text at y position in all browsers, except IE.
|
||
|
|
||
|
// Regions
|
||
|
main.append('g')
|
||
|
.attr("clip-path", $$.clipPath)
|
||
|
.attr("class", CLASS[_regions]);
|
||
|
|
||
|
// Grids
|
||
|
$$.grid = main.append('g')
|
||
|
.attr("clip-path", $$.clipPath)
|
||
|
.attr('class', CLASS[_grid]);
|
||
|
if (config[__grid_x_show]) {
|
||
|
$$.grid.append("g").attr("class", CLASS[_xgrids]);
|
||
|
}
|
||
|
if (config[__grid_y_show]) {
|
||
|
$$.grid.append('g').attr('class', CLASS[_ygrids]);
|
||
|
}
|
||
|
$$.grid.append('g').attr("class", CLASS[_xgridLines]);
|
||
|
$$.grid.append('g').attr('class', CLASS[_ygridLines]);
|
||
|
if (config[__grid_focus_show]) {
|
||
|
$$.grid.append('g')
|
||
|
.attr("class", CLASS[_xgridFocus])
|
||
|
.append('line')
|
||
|
.attr('class', CLASS[_xgridFocus]);
|
||
|
}
|
||
|
|
||
|
// Define g for chart area
|
||
|
main.append('g')
|
||
|
.attr("clip-path", $$.clipPath)
|
||
|
.attr('class', CLASS[_chart]);
|
||
|
|
||
|
// Cover whole with rects for events
|
||
|
eventRect = main.select('.' + CLASS[_chart]).append("g")
|
||
|
.attr("class", CLASS[_eventRects])
|
||
|
.style('fill-opacity', 0);
|
||
|
|
||
|
// Define g for bar chart area
|
||
|
main.select('.' + CLASS[_chart]).append("g")
|
||
|
.attr("class", CLASS[_chartBars]);
|
||
|
|
||
|
// Define g for line chart area
|
||
|
main.select('.' + CLASS[_chart]).append("g")
|
||
|
.attr("class", CLASS[_chartLines]);
|
||
|
|
||
|
// Define g for arc chart area
|
||
|
if (isFunction($$.initArc)) {
|
||
|
$$.initArc();
|
||
|
}
|
||
|
if (isFunction($$.initGauge)) {
|
||
|
$$.initGauge();
|
||
|
}
|
||
|
|
||
|
main.select('.' + CLASS[_chart]).append("g")
|
||
|
.attr("class", CLASS[_chartTexts]);
|
||
|
|
||
|
// if zoom privileged, insert rect to forefront
|
||
|
// TODO: is this needed?
|
||
|
main.insert('rect', config[__zoom_privileged] ? null : 'g.' + CLASS[_regions])
|
||
|
.attr('class', CLASS[_zoomRect])
|
||
|
.attr('width', $$.width)
|
||
|
.attr('height', $$.height)
|
||
|
.style('opacity', 0)
|
||
|
.on("dblclick.zoom", null);
|
||
|
|
||
|
// Set default extent if defined
|
||
|
if (config[__axis_x_default]) {
|
||
|
$$.brush.extent(!isFunction(config[__axis_x_default]) ? config[__axis_x_default] : config[__axis_x_default]($$.getXDomain()));
|
||
|
}
|
||
|
|
||
|
// Add Axis
|
||
|
$$.axes.x = main.append("g")
|
||
|
.attr("class", CLASS[_axis] + ' ' + CLASS[_axisX])
|
||
|
.attr("clip-path", $$.clipPathForXAxis)
|
||
|
.attr("transform", $$.getTranslate('x'))
|
||
|
.style("visibility", config[__axis_x_show] ? 'visible' : 'hidden');
|
||
|
$$.axes.x.append("text")
|
||
|
.attr("class", CLASS[_axisXLabel])
|
||
|
.attr("transform", config[__axis_rotated] ? "rotate(-90)" : "")
|
||
|
.style("text-anchor", generateCall($$.textAnchorForXAxisLabel, $$));
|
||
|
|
||
|
$$.axes.y = main.append("g")
|
||
|
.attr("class", CLASS[_axis] + ' ' + CLASS[_axisY])
|
||
|
.attr("clip-path", $$.clipPathForYAxis)
|
||
|
.attr("transform", $$.getTranslate('y'))
|
||
|
.style("visibility", config[__axis_y_show] ? 'visible' : 'hidden');
|
||
|
$$.axes.y.append("text")
|
||
|
.attr("class", CLASS[_axisYLabel])
|
||
|
.attr("transform", config[__axis_rotated] ? "" : "rotate(-90)")
|
||
|
.style("text-anchor", generateCall($$.textAnchorForYAxisLabel, $$));
|
||
|
|
||
|
$$.axes.y2 = main.append("g")
|
||
|
.attr("class", CLASS[_axis] + ' ' + CLASS[_axisY2])
|
||
|
// clip-path?
|
||
|
.attr("transform", $$.getTranslate('y2'))
|
||
|
.style("visibility", config[__axis_y2_show] ? 'visible' : 'hidden');
|
||
|
$$.axes.y2.append("text")
|
||
|
.attr("class", CLASS[_axisY2Label])
|
||
|
.attr("transform", config[__axis_rotated] ? "" : "rotate(-90)")
|
||
|
.style("text-anchor", generateCall($$.textAnchorForY2AxisLabel, $$));
|
||
|
|
||
|
// Set targets
|
||
|
$$.updateTargets($$.data.targets);
|
||
|
|
||
|
// Draw with targets
|
||
|
if (binding) {
|
||
|
$$.updateDimension();
|
||
|
$$.redraw({
|
||
|
withTransform: true,
|
||
|
withUpdateXDomain: true,
|
||
|
withUpdateOrgXDomain: true,
|
||
|
withTransitionForAxis: false
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Bind resize event
|
||
|
if (window.onresize == null) {
|
||
|
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
|
||
|
$$.api.element = $$.selectChart.node();
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.smoothLines = function (el, type) {
|
||
|
var $$ = this;
|
||
|
if (type === 'grid') {
|
||
|
el.each(function () {
|
||
|
var g = $$.d3.select(this),
|
||
|
x1 = g.attr('x1'),
|
||
|
x2 = g.attr('x2'),
|
||
|
y1 = g.attr('y1'),
|
||
|
y2 = g.attr('y2');
|
||
|
g.attr({
|
||
|
'x1': Math.ceil(x1),
|
||
|
'x2': Math.ceil(x2),
|
||
|
'y1': Math.ceil(y1),
|
||
|
'y2': Math.ceil(y2)
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
c3_chart_internal_fn.updateSizes = function () {
|
||
|
var $$ = this, config = $$.config;
|
||
|
var legendHeight = $$.legend ? $$.getLegendHeight() : 0,
|
||
|
legendWidth = $$.legend ? $$.getLegendWidth() : 0,
|
||
|
legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight,
|
||
|
hasArc = $$.hasArcType(),
|
||
|
xAxisHeight = config[__axis_rotated] || hasArc ? 0 : $$.getHorizontalAxisHeight('x'),
|
||
|
subchartHeight = config[__subchart_show] && !hasArc ? (config[__subchart_size_height] + xAxisHeight) : 0;
|
||
|
|
||
|
$$.currentWidth = $$.getCurrentWidth();
|
||
|
$$.currentHeight = $$.getCurrentHeight();
|
||
|
|
||
|
// for main, context
|
||
|
if (config[__axis_rotated]) {
|
||
|
$$.margin = {
|
||
|
top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(),
|
||
|
right: hasArc ? 0 : $$.getCurrentPaddingRight(),
|
||
|
bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(),
|
||
|
left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft())
|
||
|
};
|
||
|
$$.margin2 = {
|
||
|
top: $$.margin.top,
|
||
|
right: NaN,
|
||
|
bottom: 20 + legendHeightForBottom,
|
||
|
left: $$.rotated_padding_left
|
||
|
};
|
||
|
} else {
|
||
|
$$.margin = {
|
||
|
top: 4 + $$.getCurrentPaddingTop(), // for top tick text
|
||
|
right: hasArc ? 0 : $$.getCurrentPaddingRight(),
|
||
|
bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(),
|
||
|
left: hasArc ? 0 : $$.getCurrentPaddingLeft()
|
||
|
};
|
||
|
$$.margin2 = {
|
||
|
top: $$.currentHeight - subchartHeight - legendHeightForBottom,
|
||
|
right: NaN,
|
||
|
bottom: xAxisHeight + legendHeightForBottom,
|
||
|
left: $$.margin.left
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// for legend
|
||
|
if (isFunction($$.updateSizeForLegend)) { $$.updateSizeForLegend(legendHeight, legendWidth); }
|
||
|
|
||
|
$$.width = $$.currentWidth - $$.margin.left - $$.margin.right;
|
||
|
$$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom;
|
||
|
if ($$.width < 0) { $$.width = 0; }
|
||
|
if ($$.height < 0) { $$.height = 0; }
|
||
|
|
||
|
$$.width2 = config[__axis_rotated] ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width;
|
||
|
$$.height2 = config[__axis_rotated] ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom;
|
||
|
if ($$.width2 < 0) { $$.width2 = 0; }
|
||
|
if ($$.height2 < 0) { $$.height2 = 0; }
|
||
|
|
||
|
// for arc
|
||
|
$$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0);
|
||
|
$$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10);
|
||
|
if (isFunction($$.updateRadius)) {
|
||
|
$$.updateRadius();
|
||
|
}
|
||
|
|
||
|
if ($$.isLegendRight && hasArc) {
|
||
|
$$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.updateTargets = function (targets) {
|
||
|
var mainLineEnter, mainLineUpdate, mainBarEnter, mainBarUpdate, mainTextUpdate, mainTextEnter;
|
||
|
var $$ = this, config = $$.config, main = $$.main;
|
||
|
|
||
|
/*-- Main --*/
|
||
|
|
||
|
//-- Text --//
|
||
|
mainTextUpdate = main.select('.' + CLASS[_chartTexts]).selectAll('.' + CLASS[_chartText])
|
||
|
.data(targets)
|
||
|
.attr('class', generateCall($$.classChartText, $$));
|
||
|
mainTextEnter = mainTextUpdate.enter().append('g')
|
||
|
.attr('class', generateCall($$.classChartText, $$))
|
||
|
.style('opacity', 0)
|
||
|
.style("pointer-events", "none");
|
||
|
mainTextEnter.append('g')
|
||
|
.attr('class', generateCall($$.classTexts, $$));
|
||
|
|
||
|
//-- Bar --//
|
||
|
mainBarUpdate = main.select('.' + CLASS[_chartBars]).selectAll('.' + CLASS[_chartBar])
|
||
|
.data(targets)
|
||
|
.attr('class', generateCall($$.classChartBar, $$));
|
||
|
mainBarEnter = mainBarUpdate.enter().append('g')
|
||
|
.attr('class', generateCall($$.classChartBar, $$))
|
||
|
.style('opacity', 0)
|
||
|
.style("pointer-events", "none");
|
||
|
// Bars for each data
|
||
|
mainBarEnter.append('g')
|
||
|
.attr("class", generateCall($$.classBars, $$))
|
||
|
.style("cursor", function (d) { return config[__data_selection_isselectable](d) ? "pointer" : null; });
|
||
|
|
||
|
//-- Line --//
|
||
|
mainLineUpdate = main.select('.' + CLASS[_chartLines]).selectAll('.' + CLASS[_chartLine])
|
||
|
.data(targets)
|
||
|
.attr('class', generateCall($$.classChartLine, $$));
|
||
|
mainLineEnter = mainLineUpdate.enter().append('g')
|
||
|
.attr('class', generateCall($$.classChartLine, $$))
|
||
|
.style('opacity', 0)
|
||
|
.style("pointer-events", "none");
|
||
|
// Lines for each data
|
||
|
mainLineEnter.append('g')
|
||
|
.attr("class", generateCall($$.classLines, $$));
|
||
|
// Areas
|
||
|
mainLineEnter.append('g')
|
||
|
.attr('class', generateCall($$.classAreas, $$));
|
||
|
// Circles for each data point on lines
|
||
|
mainLineEnter.append('g')
|
||
|
.attr("class", function (d) { return $$.generateClass(CLASS[_selectedCircles], d.id); });
|
||
|
mainLineEnter.append('g')
|
||
|
.attr("class", generateCall($$.classCircles, $$))
|
||
|
.style("cursor", function (d) { return config[__data_selection_isselectable](d) ? "pointer" : null; });
|
||
|
// Update date for selected circles
|
||
|
targets.forEach(function (t) {
|
||
|
main.selectAll('.' + CLASS[_selectedCircles] + $$.getTargetSelectorSuffix(t.id)).selectAll('.' + CLASS[_selectedCircle]).each(function (d) {
|
||
|
d.value = t.values[d.index].value;
|
||
|
});
|
||
|
});
|
||
|
// MEMO: can not keep same color...
|
||
|
//mainLineUpdate.exit().remove();
|
||
|
|
||
|
if (isFunction($$.updateTargetsForArc)) {
|
||
|
$$.updateTargetsForArc(targets);
|
||
|
}
|
||
|
if (isFunction($$.updateTargetsForSubchart)) {
|
||
|
$$.updateTargetsForSubchart(targets);
|
||
|
}
|
||
|
|
||
|
/*-- Show --*/
|
||
|
|
||
|
// Fade-in each chart
|
||
|
$$.svg.selectAll('.' + CLASS[_target]).filter(function (d) { return $$.isTargetToShow(d.id); })
|
||
|
.transition().duration(config[__transition_duration])
|
||
|
.style("opacity", 1);
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.redraw = function (options, transitions) {
|
||
|
var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config;
|
||
|
var xgrid, xgridAttr, xgridData, xgridLines, xgridLine, ygrid, ygridLines, ygridLine, flushXGrid;
|
||
|
var mainLine, mainArea, mainCircle, mainBar, mainRegion, mainText, eventRect, eventRectUpdate;
|
||
|
var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType), maxDataCountTarget, tickOffset;
|
||
|
var rectX, rectW;
|
||
|
var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withLegend;
|
||
|
var hideAxis = $$.hasArcType();
|
||
|
var drawArea, drawBar, drawLine, xForText, yForText;
|
||
|
var duration, durationForExit, durationForAxis, waitForDraw;
|
||
|
var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling;
|
||
|
|
||
|
xgrid = xgridLines = mainCircle = mainText = d3.selectAll([]);
|
||
|
|
||
|
options = options || {};
|
||
|
withY = getOption(options, "withY", true);
|
||
|
withSubchart = getOption(options, "withSubchart", true);
|
||
|
withTransition = getOption(options, "withTransition", true);
|
||
|
withTransform = getOption(options, "withTransform", false);
|
||
|
withUpdateXDomain = getOption(options, "withUpdateXDomain", false);
|
||
|
withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", false);
|
||
|
withLegend = getOption(options, "withLegend", false);
|
||
|
withTransitionForExit = getOption(options, "withTransitionForExit", withTransition);
|
||
|
withTransitionForAxis = getOption(options, "withTransitionForAxis", withTransition);
|
||
|
|
||
|
duration = withTransition ? config[__transition_duration] : 0;
|
||
|
durationForExit = withTransitionForExit ? duration : 0;
|
||
|
durationForAxis = withTransitionForAxis ? duration : 0;
|
||
|
|
||
|
transitions = transitions || $$.generateAxisTransitions(durationForAxis);
|
||
|
|
||
|
// update legend and transform each g
|
||
|
if (withLegend && config[__legend_show]) {
|
||
|
$$.updateLegend($$.mapToIds($$.data.targets), options, transitions);
|
||
|
}
|
||
|
|
||
|
// MEMO: needed for grids calculation
|
||
|
if ($$.isCategorized() && targetsToShow.length === 0) {
|
||
|
$$.x.domain([0, $$.axes.x.selectAll('.tick').size()]);
|
||
|
}
|
||
|
|
||
|
if (targetsToShow.length) {
|
||
|
$$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain);
|
||
|
// update axis tick values according to options
|
||
|
if (!config[__axis_x_tick_values] && (config[__axis_x_tick_fit] || config[__axis_x_tick_count])) {
|
||
|
tickValues = $$.generateTickValues($$.mapTargetsToUniqueXs(targetsToShow), config[__axis_x_tick_count]);
|
||
|
$$.xAxis.tickValues(tickValues);
|
||
|
$$.subXAxis.tickValues(tickValues);
|
||
|
}
|
||
|
} else {
|
||
|
$$.xAxis.tickValues([]);
|
||
|
$$.subXAxis.tickValues([]);
|
||
|
}
|
||
|
|
||
|
$$.y.domain($$.getYDomain(targetsToShow, 'y'));
|
||
|
$$.y2.domain($$.getYDomain(targetsToShow, 'y2'));
|
||
|
|
||
|
// axes
|
||
|
$$.axes.x.style("opacity", hideAxis ? 0 : 1);
|
||
|
$$.axes.y.style("opacity", hideAxis ? 0 : 1);
|
||
|
$$.axes.y2.style("opacity", hideAxis ? 0 : 1);
|
||
|
$$.axes.subx.style("opacity", hideAxis ? 0 : 1);
|
||
|
transitions.axisX.call($$.xAxis);
|
||
|
transitions.axisY.call($$.yAxis);
|
||
|
transitions.axisY2.call($$.y2Axis);
|
||
|
transitions.axisSubX.call($$.subXAxis);
|
||
|
|
||
|
// Update axis label
|
||
|
$$.updateAxisLabels(withTransition);
|
||
|
|
||
|
// show/hide if manual culling needed
|
||
|
if (withUpdateXDomain && targetsToShow.length) {
|
||
|
if (config[__axis_x_tick_culling] && tickValues) {
|
||
|
for (i = 1; i < tickValues.length; i++) {
|
||
|
if (tickValues.length / i < config[__axis_x_tick_culling_max]) {
|
||
|
intervalForCulling = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
$$.svg.selectAll('.' + CLASS[_axisX] + ' .tick text').each(function (e) {
|
||
|
var index = tickValues.indexOf(e);
|
||
|
if (index >= 0) {
|
||
|
d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block');
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
$$.svg.selectAll('.' + CLASS[_axisX] + ' .tick text').style('display', 'block');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// rotate tick text if needed
|
||
|
if (!config[__axis_rotated] && config[__axis_x_tick_rotate]) {
|
||
|
$$.rotateTickText($$.axes.x, transitions.axisX, config[__axis_x_tick_rotate]);
|
||
|
}
|
||
|
|
||
|
// setup drawer - MEMO: these must be called after axis updated
|
||
|
drawArea = $$.generateDrawArea(areaIndices, false);
|
||
|
drawBar = $$.generateDrawBar(barIndices);
|
||
|
drawLine = $$.generateDrawLine(lineIndices, false);
|
||
|
xForText = $$.generateXYForText(barIndices, true);
|
||
|
yForText = $$.generateXYForText(barIndices, false);
|
||
|
|
||
|
// Update sub domain
|
||
|
$$.subY.domain($$.y.domain());
|
||
|
$$.subY2.domain($$.y2.domain());
|
||
|
|
||
|
// tooltip
|
||
|
$$.tooltip.style("display", "none");
|
||
|
|
||
|
// xgrid focus
|
||
|
$$.updateXgridFocus();
|
||
|
|
||
|
// Data empty label positioning and text.
|
||
|
main.select("text." + CLASS[_text] + '.' + CLASS[_empty])
|
||
|
.attr("x", $$.width / 2)
|
||
|
.attr("y", $$.height / 2)
|
||
|
.text(config[__data_empty_label_text])
|
||
|
.transition()
|
||
|
.style('opacity', targetsToShow.length ? 0 : 1);
|
||
|
|
||
|
// grid
|
||
|
main.select('line.' + CLASS[_xgridFocus]).style("visibility", "hidden");
|
||
|
if (config[__grid_x_show]) {
|
||
|
xgridAttr = config[__axis_rotated] ? {
|
||
|
'x1': 0,
|
||
|
'x2': $$.width,
|
||
|
'y1': function (d) { return $$.x(d) - tickOffset; },
|
||
|
'y2': function (d) { return $$.x(d) - tickOffset; }
|
||
|
} : {
|
||
|
'x1': function (d) { return $$.x(d) + tickOffset; },
|
||
|
'x2': function (d) { return $$.x(d) + tickOffset; },
|
||
|
'y1': 0,
|
||
|
'y2': $$.height
|
||
|
};
|
||
|
// this is used to flow
|
||
|
flushXGrid = function (withoutUpdate) {
|
||
|
xgridData = $$.generateGridData(config[__grid_x_type], $$.x);
|
||
|
tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0;
|
||
|
xgrid = main.select('.' + CLASS[_xgrids]).selectAll('.' + CLASS[_xgrid])
|
||
|
.data(xgridData);
|
||
|
xgrid.enter().append('line').attr("class", CLASS[_xgrid]);
|
||
|
if (!withoutUpdate) {
|
||
|
xgrid.attr(xgridAttr)
|
||
|
.style("opacity", function () { return +d3.select(this).attr(config[__axis_rotated] ? 'y1' : 'x1') === (config[__axis_rotated] ? $$.height : 0) ? 0 : 1; });
|
||
|
}
|
||
|
xgrid.exit().remove();
|
||
|
};
|
||
|
flushXGrid();
|
||
|
}
|
||
|
xgridLines = main.select('.' + CLASS[_xgridLines]).selectAll('.' + CLASS[_xgridLine])
|
||
|
.data(config[__grid_x_lines]);
|
||
|
// enter
|
||
|
xgridLine = xgridLines.enter().append('g')
|
||
|
.attr("class", function (d) { return CLASS[_xgridLine] + (d.class ? ' ' + d.class : ''); });
|
||
|
xgridLine.append('line')
|
||
|
.style("opacity", 0);
|
||
|
xgridLine.append('text')
|
||
|
.attr("text-anchor", "end")
|
||
|
.attr("transform", config[__axis_rotated] ? "" : "rotate(-90)")
|
||
|
.attr('dx', config[__axis_rotated] ? 0 : -$$.margin.top)
|
||
|
.attr('dy', -5)
|
||
|
.style("opacity", 0);
|
||
|
// udpate
|
||
|
// done in d3.transition() of the end of this function
|
||
|
// exit
|
||
|
xgridLines.exit().transition().duration(duration)
|
||
|
.style("opacity", 0)
|
||
|
.remove();
|
||
|
// Y-Grid
|
||
|
if (withY && config[__grid_y_show]) {
|
||
|
ygrid = main.select('.' + CLASS[_ygrids]).selectAll('.' + CLASS[_ygrid])
|
||
|
.data($$.y.ticks(config[__grid_y_ticks]));
|
||
|
ygrid.enter().append('line')
|
||
|
.attr('class', CLASS[_ygrid]);
|
||
|
ygrid.attr("x1", config[__axis_rotated] ? $$.y : 0)
|
||
|
.attr("x2", config[__axis_rotated] ? $$.y : $$.width)
|
||
|
.attr("y1", config[__axis_rotated] ? 0 : $$.y)
|
||
|
.attr("y2", config[__axis_rotated] ? $$.height : $$.y);
|
||
|
ygrid.exit().remove();
|
||
|
$$.smoothLines(ygrid, 'grid');
|
||
|
}
|
||
|
if (withY) {
|
||
|
ygridLines = main.select('.' + CLASS[_ygridLines]).selectAll('.' + CLASS[_ygridLine])
|
||
|
.data(config[__grid_y_lines]);
|
||
|
// enter
|
||
|
ygridLine = ygridLines.enter().append('g')
|
||
|
.attr("class", function (d) { return CLASS[_ygridLine] + (d.class ? ' ' + d.class : ''); });
|
||
|
ygridLine.append('line')
|
||
|
.style("opacity", 0);
|
||
|
ygridLine.append('text')
|
||
|
.attr("text-anchor", "end")
|
||
|
.attr("transform", config[__axis_rotated] ? "rotate(-90)" : "")
|
||
|
.attr('dx', config[__axis_rotated] ? 0 : -$$.margin.top)
|
||
|
.attr('dy', -5)
|
||
|
.style("opacity", 0);
|
||
|
// update
|
||
|
var yv_ = generateCall($$.yv, $$);
|
||
|
ygridLines.select('line')
|
||
|
.transition().duration(duration)
|
||
|
.attr("x1", config[__axis_rotated] ? yv_ : 0)
|
||
|
.attr("x2", config[__axis_rotated] ? yv_ : $$.width)
|
||
|
.attr("y1", config[__axis_rotated] ? 0 : yv_)
|
||
|
.attr("y2", config[__axis_rotated] ? $$.height : yv_)
|
||
|
.style("opacity", 1);
|
||
|
ygridLines.select('text')
|
||
|
.transition().duration(duration)
|
||
|
.attr("x", config[__axis_rotated] ? 0 : $$.width)
|
||
|
.attr("y", yv_)
|
||
|
.text(function (d) { return d.text; })
|
||
|
.style("opacity", 1);
|
||
|
// exit
|
||
|
ygridLines.exit().transition().duration(duration)
|
||
|
.style("opacity", 0)
|
||
|
.remove();
|
||
|
}
|
||
|
|
||
|
// rect for regions
|
||
|
mainRegion = main.select('.' + CLASS[_regions]).selectAll('.' + CLASS[_region])
|
||
|
.data(config[__regions]);
|
||
|
mainRegion.enter().append('g')
|
||
|
.attr('class', generateCall($$.classRegion, $$))
|
||
|
.append('rect')
|
||
|
.style("fill-opacity", 0);
|
||
|
mainRegion.exit().transition().duration(duration)
|
||
|
.style("opacity", 0)
|
||
|
.remove();
|
||
|
|
||
|
// bars
|
||
|
mainBar = main.selectAll('.' + CLASS[_bars]).selectAll('.' + CLASS[_bar])
|
||
|
.data(generateCall($$.barData, $$));
|
||
|
mainBar.enter().append('path')
|
||
|
.attr("class", generateCall($$.classBar, $$))
|
||
|
.style("stroke", function (d) { return $$.color(d.id); })
|
||
|
.style("fill", function (d) { return $$.color(d.id); });
|
||
|
mainBar
|
||
|
.style("opacity", generateCall($$.initialOpacity, $$));
|
||
|
mainBar.exit().transition().duration(durationForExit)
|
||
|
.style('opacity', 0)
|
||
|
.remove();
|
||
|
|
||
|
// lines, areas and cricles
|
||
|
mainLine = main.selectAll('.' + CLASS[_lines]).selectAll('.' + CLASS[_line])
|
||
|
.data(generateCall($$.lineData, $$));
|
||
|
mainLine.enter().append('path')
|
||
|
.attr('class', generateCall($$.classLine, $$))
|
||
|
.style("stroke", $$.color);
|
||
|
mainLine
|
||
|
.style("opacity", generateCall($$.initialOpacity, $$))
|
||
|
.attr('transform', null);
|
||
|
mainLine.exit().transition().duration(durationForExit)
|
||
|
.style('opacity', 0)
|
||
|
.remove();
|
||
|
|
||
|
mainArea = main.selectAll('.' + CLASS[_areas]).selectAll('.' + CLASS[_area])
|
||
|
.data(generateCall($$.lineData, $$));
|
||
|
mainArea.enter().append('path')
|
||
|
.attr("class", generateCall($$.classArea, $$))
|
||
|
.style("fill", $$.color)
|
||
|
.style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; });
|
||
|
mainArea
|
||
|
.style("opacity", $$.orgAreaOpacity);
|
||
|
mainArea.exit().transition().duration(durationForExit)
|
||
|
.style('opacity', 0)
|
||
|
.remove();
|
||
|
|
||
|
if (config[__point_show]) {
|
||
|
mainCircle = main.selectAll('.' + CLASS[_circles]).selectAll('.' + CLASS[_circle])
|
||
|
.data(generateCall($$.lineOrScatterData, $$));
|
||
|
mainCircle.enter().append("circle")
|
||
|
.attr("class", generateCall($$.classCircle, $$))
|
||
|
.attr("r", generateCall($$.pointR, $$))
|
||
|
.style("fill", $$.color);
|
||
|
mainCircle
|
||
|
.style("opacity", generateCall($$.initialOpacity, $$));
|
||
|
mainCircle.exit().remove();
|
||
|
}
|
||
|
|
||
|
if ($$.hasDataLabel()) {
|
||
|
mainText = main.selectAll('.' + CLASS[_texts]).selectAll('.' + CLASS[_text])
|
||
|
.data(generateCall($$.barOrLineData, $$));
|
||
|
mainText.enter().append('text')
|
||
|
.attr("class", generateCall($$.classText, $$))
|
||
|
.attr('text-anchor', function (d) { return config[__axis_rotated] ? (d.value < 0 ? 'end' : 'start') : 'middle'; })
|
||
|
.style("stroke", 'none')
|
||
|
.style("fill", function (d) { return $$.color(d); })
|
||
|
.style("fill-opacity", 0);
|
||
|
mainText
|
||
|
.text(function (d) { return $$.formatByAxisId($$.getAxisId(d.id))(d.value, d.id); });
|
||
|
mainText.exit()
|
||
|
.transition().duration(durationForExit)
|
||
|
.style('fill-opacity', 0)
|
||
|
.remove();
|
||
|
}
|
||
|
|
||
|
// arc
|
||
|
if (isFunction($$.redrawArc)) {
|
||
|
$$.redrawArc(duration, durationForExit, withTransform);
|
||
|
}
|
||
|
|
||
|
// subchart
|
||
|
if (isFunction($$.redrawSubchart)) {
|
||
|
$$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices);
|
||
|
}
|
||
|
|
||
|
// circles for select
|
||
|
main.selectAll('.' + CLASS[_selectedCircles])
|
||
|
.filter(generateCall($$.isBarType, $$))
|
||
|
.selectAll('circle')
|
||
|
.remove();
|
||
|
|
||
|
if (config[__interaction_enabled]) {
|
||
|
// rect for mouseover
|
||
|
eventRect = main.select('.' + CLASS[_eventRects])
|
||
|
.style('cursor', config[__zoom_enabled] ? config[__axis_rotated] ? 'ns-resize' : 'ew-resize' : null);
|
||
|
if (notEmpty(config[__data_xs]) && !$$.isSingleX(config[__data_xs])) {
|
||
|
|
||
|
if (!eventRect.classed(CLASS[_eventRectsMultiple])) {
|
||
|
eventRect.classed(CLASS[_eventRectsMultiple], true).classed(CLASS[_eventRectsSingle], false)
|
||
|
.selectAll('.' + CLASS[_eventRect]).remove();
|
||
|
}
|
||
|
|
||
|
eventRectUpdate = main.select('.' + CLASS[_eventRects]).selectAll('.' + CLASS[_eventRect])
|
||
|
.data([0]);
|
||
|
// enter : only one rect will be added
|
||
|
$$.generateEventRectsForMultipleXs(eventRectUpdate.enter());
|
||
|
// update
|
||
|
eventRectUpdate
|
||
|
.attr('x', 0)
|
||
|
.attr('y', 0)
|
||
|
.attr('width', $$.width)
|
||
|
.attr('height', $$.height);
|
||
|
// exit : not needed because always only one rect exists
|
||
|
} else {
|
||
|
|
||
|
if (!eventRect.classed(CLASS[_eventRectsSingle])) {
|
||
|
eventRect.classed(CLASS[_eventRectsMultiple], false).classed(CLASS[_eventRectsSingle], true)
|
||
|
.selectAll('.' + CLASS[_eventRect]).remove();
|
||
|
}
|
||
|
|
||
|
if (($$.isCustomX() || $$.isTimeSeries()) && !$$.isCategorized()) {
|
||
|
rectW = function (d) {
|
||
|
var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index), dx = $$.data.xs[d.id][d.index],
|
||
|
w = ($$.x(nextX ? nextX : dx) - $$.x(prevX ? prevX : dx)) / 2;
|
||
|
return w < 0 ? 0 : w;
|
||
|
};
|
||
|
rectX = function (d) {
|
||
|
var prevX = $$.getPrevX(d.index), dx = $$.data.xs[d.id][d.index];
|
||
|
return ($$.x(dx) + $$.x(prevX ? prevX : dx)) / 2;
|
||
|
};
|
||
|
} else {
|
||
|
rectW = $$.getEventRectWidth();
|
||
|
rectX = function (d) {
|
||
|
return $$.x(d.x) - (rectW / 2);
|
||
|
};
|
||
|
}
|
||
|
// Set data
|
||
|
maxDataCountTarget = $$.getMaxDataCountTarget($$.data.targets);
|
||
|
main.select('.' + CLASS[_eventRects])
|
||
|
.datum(maxDataCountTarget ? maxDataCountTarget.values : []);
|
||
|
// Update rects
|
||
|
eventRectUpdate = main.select('.' + CLASS[_eventRects]).selectAll('.' + CLASS[_eventRect])
|
||
|
.data(function (d) { return d; });
|
||
|
// enter
|
||
|
$$.generateEventRectsForSingleX(eventRectUpdate.enter());
|
||
|
// update
|
||
|
eventRectUpdate
|
||
|
.attr('class', generateCall($$.classEvent, $$))
|
||
|
.attr("x", config[__axis_rotated] ? 0 : rectX)
|
||
|
.attr("y", config[__axis_rotated] ? rectX : 0)
|
||
|
.attr("width", config[__axis_rotated] ? $$.width : rectW)
|
||
|
.attr("height", config[__axis_rotated] ? rectW : $$.height);
|
||
|
// exit
|
||
|
eventRectUpdate.exit().remove();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var xv_ = generateCall($$.xv, $$);
|
||
|
|
||
|
// transition should be derived from one transition
|
||
|
d3.transition().duration(duration).each(function () {
|
||
|
var transitions = [],
|
||
|
cx = generateCall($$.config[__axis_rotated] ? $$.circleY : $$.circleX, $$),
|
||
|
cy = generateCall($$.config[__axis_rotated] ? $$.circleX : $$.circleY, $$);
|
||
|
|
||
|
transitions.push(mainBar.transition()
|
||
|
.attr('d', drawBar)
|
||
|
.style("fill", $$.color)
|
||
|
.style("opacity", 1));
|
||
|
transitions.push(mainLine.transition()
|
||
|
.attr("d", drawLine)
|
||
|
.style("stroke", $$.color)
|
||
|
.style("opacity", 1));
|
||
|
transitions.push(mainArea.transition()
|
||
|
.attr("d", drawArea)
|
||
|
.style("fill", $$.color)
|
||
|
.style("opacity", $$.orgAreaOpacity));
|
||
|
transitions.push(mainCircle.transition()
|
||
|
.style('opacity', generateCall($$.opacityForCircle, $$))
|
||
|
.style("fill", $$.color)
|
||
|
.attr("cx", cx)
|
||
|
.attr("cy", cy));
|
||
|
transitions.push(main.selectAll('.' + CLASS[_selectedCircle]).transition()
|
||
|
.attr("cx", cx)
|
||
|
.attr("cy", cy));
|
||
|
transitions.push(mainText.transition()
|
||
|
.attr('x', xForText)
|
||
|
.attr('y', yForText)
|
||
|
.style("fill", $$.color)
|
||
|
.style("fill-opacity", options.flow ? 0 : generateCall($$.opacityForText, $$)));
|
||
|
transitions.push(mainRegion.selectAll('rect').transition()
|
||
|
.attr("x", $$.regionX)
|
||
|
.attr("y", $$.regionY)
|
||
|
.attr("width", $$.regionWidth)
|
||
|
.attr("height", $$.regionHeight)
|
||
|
.style("fill-opacity", function (d) { return isValue(d.opacity) ? d.opacity : 0.1; }));
|
||
|
transitions.push(xgridLines.select('line').transition()
|
||
|
.attr("x1", config[__axis_rotated] ? 0 : xv_)
|
||
|
.attr("x2", config[__axis_rotated] ? $$.width : xv_)
|
||
|
.attr("y1", config[__axis_rotated] ? xv_ : $$.margin.top)
|
||
|
.attr("y2", config[__axis_rotated] ? xv_ : $$.height)
|
||
|
.style("opacity", 1));
|
||
|
transitions.push(xgridLines.select('text').transition()
|
||
|
.attr("x", config[__axis_rotated] ? $$.width : 0)
|
||
|
.attr("y", xv_)
|
||
|
.text(function (d) { return d.text; })
|
||
|
.style("opacity", 1));
|
||
|
// Wait for end of transitions if called from flow API
|
||
|
if (options.flow) {
|
||
|
waitForDraw = $$.generateWait();
|
||
|
transitions.forEach(function (t) {
|
||
|
waitForDraw.add(t);
|
||
|
});
|
||
|
}
|
||
|
})
|
||
|
.call(waitForDraw ? waitForDraw : function () {}, function () { // only for flow
|
||
|
var translateX, scaleX = 1, transform,
|
||
|
flowIndex = options.flow.index,
|
||
|
flowLength = options.flow.length,
|
||
|
flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex),
|
||
|
flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength),
|
||
|
orgDomain = $$.x.domain(), domain,
|
||
|
durationForFlow = options.flow.duration || duration,
|
||
|
done = options.flow.done || function () {},
|
||
|
wait = $$.generateWait();
|
||
|
|
||
|
// remove head data after rendered
|
||
|
$$.data.targets.forEach(function (d) {
|
||
|
d.values.splice(0, flowLength);
|
||
|
});
|
||
|
|
||
|
// update x domain to generate axis elements for flow
|
||
|
domain = $$.updateXDomain(targetsToShow, true, true);
|
||
|
// update elements related to x scale
|
||
|
if (flushXGrid) { flushXGrid(true); }
|
||
|
|
||
|
// generate transform to flow
|
||
|
if (!options.flow.orgDataCount) { // if empty
|
||
|
if ($$.data.targets[0].values.length !== 1) {
|
||
|
translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
|
||
|
} else {
|
||
|
if ($$.isTimeSeries()) {
|
||
|
flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0);
|
||
|
flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1);
|
||
|
translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
|
||
|
} else {
|
||
|
translateX = diffDomain(domain) / 2;
|
||
|
}
|
||
|
}
|
||
|
} else if (options.flow.orgDataCount === 1 || flowStart.x === flowEnd.x) {
|
||
|
translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
|
||
|
} else {
|
||
|
if ($$.isTimeSeries()) {
|
||
|
translateX = ($$.x(orgDomain[0]) - $$.x(domain[0]));
|
||
|
} else {
|
||
|
translateX = ($$.x(flowStart.x) - $$.x(flowEnd.x));
|
||
|
}
|
||
|
}
|
||
|
scaleX = (diffDomain(orgDomain) / diffDomain(domain));
|
||
|
transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)';
|
||
|
|
||
|
d3.transition().ease('linear').duration(durationForFlow).each(function () {
|
||
|
wait.add($$.axes.x.transition().call($$.xAxis));
|
||
|
wait.add(mainBar.transition().attr('transform', transform));
|
||
|
wait.add(mainLine.transition().attr('transform', transform));
|
||
|
wait.add(mainArea.transition().attr('transform', transform));
|
||
|
wait.add(mainCircle.transition().attr('transform', transform));
|
||
|
wait.add(mainText.transition().attr('transform', transform));
|
||
|
wait.add(mainRegion.filter($$.isRegionOnX).transition().attr('transform', transform));
|
||
|
wait.add(xgrid.transition().attr('transform', transform));
|
||
|
wait.add(xgridLines.transition().attr('transform', transform));
|
||
|
})
|
||
|
.call(wait, function () {
|
||
|
var i, shapes = [], texts = [], eventRects = [];
|
||
|
|
||
|
// remove flowed elements
|
||
|
if (flowLength) {
|
||
|
for (i = 0; i < flowLength; i++) {
|
||
|
shapes.push('.' + CLASS[_shape] + '-' + (flowIndex + i));
|
||
|
texts.push('.' + CLASS[_text] + '-' + (flowIndex + i));
|
||
|
eventRects.push('.' + CLASS[_eventRect] + '-' + (flowIndex + i));
|
||
|
}
|
||
|
$$.svg.selectAll('.' + CLASS[_shapes]).selectAll(shapes).remove();
|
||
|
$$.svg.selectAll('.' + CLASS[_texts]).selectAll(texts).remove();
|
||
|
$$.svg.selectAll('.' + CLASS[_eventRects]).selectAll(eventRects).remove();
|
||
|
$$.svg.select('.' + CLASS[_xgrid]).remove();
|
||
|
}
|
||
|
|
||
|
// draw again for removing flowed elements and reverting attr
|
||
|
xgrid
|
||
|
.attr('transform', null)
|
||
|
.attr(xgridAttr);
|
||
|
xgridLines
|
||
|
.attr('transform', null);
|
||
|
xgridLines.select('line')
|
||
|
.attr("x1", config[__axis_rotated] ? 0 : xv_)
|
||
|
.attr("x2", config[__axis_rotated] ? $$.width : xv_);
|
||
|
xgridLines.select('text')
|
||
|
.attr("x", config[__axis_rotated] ? $$.width : 0)
|
||
|
.attr("y", xv_);
|
||
|
mainBar
|
||
|
.attr('transform', null)
|
||
|
.attr("d", drawBar);
|
||
|
mainLine
|
||
|
.attr('transform', null)
|
||
|
.attr("d", drawLine);
|
||
|
mainArea
|
||
|
.attr('transform', null)
|
||
|
.attr("d", drawArea);
|
||
|
mainCircle
|
||
|
.attr('transform', null)
|
||
|
.attr("cx", config[__axis_rotated] ? $$.circleY : $$.circleX)
|
||
|
.attr("cy", config[__axis_rotated] ? $$.circleX : $$.circleY);
|
||
|
mainText
|
||
|
.attr('transform', null)
|
||
|
.attr('x', xForText)
|
||
|
.attr('y', yForText)
|
||
|
.style('fill-opacity', generateCall($$.opacityForText, $$));
|
||
|
mainRegion
|
||
|
.attr('transform', null);
|
||
|
mainRegion.select('rect').filter($$.isRegionOnX)
|
||
|
.attr("x", $$.regionX)
|
||
|
.attr("width", $$.regionWidth);
|
||
|
eventRectUpdate
|
||
|
.attr("x", config[__axis_rotated] ? 0 : rectX)
|
||
|
.attr("y", config[__axis_rotated] ? rectX : 0)
|
||
|
.attr("width", config[__axis_rotated] ? $$.width : rectW)
|
||
|
.attr("height", config[__axis_rotated] ? rectW : $$.height);
|
||
|
|
||
|
// callback for end of flow
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// update fadein condition
|
||
|
$$.mapToIds($$.data.targets).forEach(function (id) {
|
||
|
$$.withoutFadeIn[id] = true;
|
||
|
});
|
||
|
|
||
|
if (isFunction($$.updateZoom)) { $$.updateZoom(); }
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.updateAndRedraw = function (options) {
|
||
|
var $$ = this, config = $$.config, transitions;
|
||
|
options = options || {};
|
||
|
// same with redraw
|
||
|
options.withTransition = getOption(options, "withTransition", true);
|
||
|
options.withTransform = getOption(options, "withTransform", false);
|
||
|
options.withLegend = getOption(options, "withLegend", false);
|
||
|
// NOT same with redraw
|
||
|
options.withUpdateXDomain = true;
|
||
|
options.withUpdateOrgXDomain = true;
|
||
|
options.withTransitionForExit = false;
|
||
|
options.withTransitionForTransform = getOption(options, "withTransitionForTransform", options.withTransition);
|
||
|
// MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called)
|
||
|
$$.updateSizes();
|
||
|
// MEMO: called in updateLegend in redraw if withLegend
|
||
|
if (!(options.withLegend && config[__legend_show])) {
|
||
|
transitions = $$.generateAxisTransitions(options.withTransitionForAxis ? config[__transition_duration] : 0);
|
||
|
// Update scales
|
||
|
$$.updateScales();
|
||
|
$$.updateSvgSize();
|
||
|
// Update g positions
|
||
|
$$.transformAll(options.withTransitionForTransform, transitions);
|
||
|
}
|
||
|
// Draw with new sizes & scales
|
||
|
$$.redraw(options, transitions);
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) {
|
||
|
var $$ = this, d3 = $$.d3, config = $$.config;
|
||
|
eventRectEnter.append("rect")
|
||
|
.attr("class", generateCall($$.classEvent, $$))
|
||
|
.style("cursor", config[__data_selection_enabled] && config[__data_selection_grouped] ? "pointer" : null)
|
||
|
.on('mouseover', function (d) {
|
||
|
var index = d.index, selectedData, newData;
|
||
|
|
||
|
if ($$.dragging) { return; } // do nothing if dragging
|
||
|
if ($$.hasArcType()) { return; }
|
||
|
|
||
|
selectedData = $$.data.targets.map(function (t) {
|
||
|
return $$.addName($$.getValueOnIndex(t.values, index));
|
||
|
});
|
||
|
|
||
|
// Sort selectedData as names order
|
||
|
newData = [];
|
||
|
Object.keys(config[__data_names]).forEach(function (id) {
|
||
|
for (var j = 0; j < selectedData.length; j++) {
|
||
|
if (selectedData[j] && selectedData[j].id === id) {
|
||
|
newData.push(selectedData[j]);
|
||
|
selectedData.shift(j);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
selectedData = newData.concat(selectedData); // Add remained
|
||
|
|
||
|
// Expand shapes for selection
|
||
|
if (config[__point_focus_expand_enabled]) { $$.expandCircles(index); }
|
||
|
$$.expandBars(index);
|
||
|
|
||
|
// Call event handler
|
||
|
$$.main.selectAll('.' + CLASS[_shape] + '-' + index).each(function (d) {
|
||
|
config[__data_onmouseover].call(c3, d);
|
||
|
});
|
||
|
})
|
||
|
.on('mouseout', function (d) {
|
||
|
var index = d.index;
|
||
|
if ($$.hasArcType()) { return; }
|
||
|
$$.hideXGridFocus();
|
||
|
$$.hideTooltip();
|
||
|
// Undo expanded shapes
|
||
|
$$.unexpandCircles(index);
|
||
|
$$.unexpandBars();
|
||
|
// Call event handler
|
||
|
$$.main.selectAll('.' + CLASS[_shape] + '-' + index).each(function (d) {
|
||
|
config[__data_onmouseout].call($$, d);
|
||
|
});
|
||
|
})
|
||
|
.on('mousemove', function (d) {
|
||
|
var selectedData, index = d.index,
|
||
|
eventRect = $$.svg.select('.' + CLASS[_eventRect] + '-' + index);
|
||
|
|
||
|
if ($$.dragging) { return; } // do nothing when dragging
|
||
|
if ($$.hasArcType()) { return; }
|
||
|
|
||
|
// Show tooltip
|
||
|
selectedData = $$.filterTargetsToShow($$.data.targets).map(function (t) {
|
||
|
return $$.addName($$.getValueOnIndex(t.values, index));
|
||
|
});
|
||
|
|
||
|
if (config[__tooltip_grouped]) {
|
||
|
$$.showTooltip(selectedData, d3.mouse(this));
|
||
|
$$.showXGridFocus(selectedData);
|
||
|
}
|
||
|
|
||
|
if (config[__tooltip_grouped] && (!config[__data_selection_enabled] || config[__data_selection_grouped])) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$$.main.selectAll('.' + CLASS[_shape] + '-' + index)
|
||
|
.each(function () {
|
||
|
d3.select(this).classed(CLASS[_EXPANDED], true);
|
||
|
if (config[__data_selection_enabled]) {
|
||
|
eventRect.style('cursor', config[__data_selection_grouped] ? 'pointer' : null);
|
||
|
}
|
||
|
if (!config[__tooltip_grouped]) {
|
||
|
$$.hideXGridFocus();
|
||
|
$$.hideTooltip();
|
||
|
if (!config[__data_selection_grouped]) {
|
||
|
$$.unexpandCircles(index);
|
||
|
$$.unexpandBars();
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
.filter(function (d) {
|
||
|
if (this.nodeName === 'circle') {
|
||
|
return $$.isWithinCircle(this, $$.pointSelectR(d));
|
||
|
}
|
||
|
else if (this.nodeName === 'path') {
|
||
|
return $$.isWithinBar(this);
|
||
|
}
|
||
|
})
|
||
|
.each(function (d) {
|
||
|
if (config[__data_selection_enabled] && (config[__data_selection_grouped] || config[__data_selection_isselectable](d))) {
|
||
|
eventRect.style('cursor', 'pointer');
|
||
|
}
|
||
|
if (!config[__tooltip_grouped]) {
|
||
|
$$.showTooltip([d], d3.mouse(this));
|
||
|
$$.showXGridFocus([d]);
|
||
|
if (config[__point_focus_expand_enabled]) { $$.expandCircles(index, d.id); }
|
||
|
$$.expandBars(index, d.id);
|
||
|
}
|
||
|
});
|
||
|
})
|
||
|
.on('click', function (d) {
|
||
|
var index = d.index;
|
||
|
if ($$.hasArcType() || !$$.toggleShape) { return; }
|
||
|
if ($$.cancelClick) {
|
||
|
$$.cancelClick = false;
|
||
|
return;
|
||
|
}
|
||
|
$$.main.selectAll('.' + CLASS[_shape] + '-' + index).each(function (d) {
|
||
|
$$.toggleShape(this, d, index);
|
||
|
});
|
||
|
})
|
||
|
.call(
|
||
|
d3.behavior.drag().origin(Object)
|
||
|
.on('drag', function () { $$.drag(d3.mouse(this)); })
|
||
|
.on('dragstart', function () { $$.dragstart(d3.mouse(this)); })
|
||
|
.on('dragend', function () { $$.dragend(); })
|
||
|
)
|
||
|
.on("dblclick.zoom", null);
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter) {
|
||
|
var $$ = this, d3 = $$.d3, config = $$.config;
|
||
|
eventRectEnter.append('rect')
|
||
|
.attr('x', 0)
|
||
|
.attr('y', 0)
|
||
|
.attr('width', $$.width)
|
||
|
.attr('height', $$.height)
|
||
|
.attr('class', CLASS[_eventRect])
|
||
|
.on('mouseout', function () {
|
||
|
if ($$.hasArcType()) { return; }
|
||
|
$$.hideXGridFocus();
|
||
|
$$.hideTooltip();
|
||
|
$$.unexpandCircles();
|
||
|
})
|
||
|
.on('mousemove', function () {
|
||
|
var targetsToShow = $$.filterTargetsToShow($$.data.targets);
|
||
|
var mouse, closest, sameXData, selectedData;
|
||
|
|
||
|
if ($$.dragging) { return; } // do nothing when dragging
|
||
|
if ($$.hasArcType(targetsToShow)) { return; }
|
||
|
|
||
|
mouse = d3.mouse(this);
|
||
|
closest = $$.findClosestFromTargets(targetsToShow, mouse);
|
||
|
|
||
|
if (! closest) { return; }
|
||
|
|
||
|
if ($$.isScatterType(closest)) {
|
||
|
sameXData = [closest];
|
||
|
} else {
|
||
|
sameXData = $$.filterSameX(targetsToShow, closest.x);
|
||
|
}
|
||
|
|
||
|
// show tooltip when cursor is close to some point
|
||
|
selectedData = sameXData.map(function (d) {
|
||
|
return $$.addName(d);
|
||
|
});
|
||
|
$$.showTooltip(selectedData, mouse);
|
||
|
|
||
|
// expand points
|
||
|
if (config[__point_focus_expand_enabled]) {
|
||
|
$$.unexpandCircles();
|
||
|
$$.expandCircles(closest.index, closest.id);
|
||
|
}
|
||
|
|
||
|
// Show xgrid focus line
|
||
|
$$.showXGridFocus(selectedData);
|
||
|
|
||
|
// Show cursor as pointer if point is close to mouse position
|
||
|
if ($$.dist(closest, mouse) < 100) {
|
||
|
$$.svg.select('.' + CLASS[_eventRect]).style('cursor', 'pointer');
|
||
|
if (!$$.mouseover) {
|
||
|
config[__data_onmouseover].call($$, closest);
|
||
|
$$.mouseover = true;
|
||
|
}
|
||
|
} else if ($$.mouseover) {
|
||
|
$$.svg.select('.' + CLASS[_eventRect]).style('cursor', null);
|
||
|
config[__data_onmouseout].call($$, closest);
|
||
|
$$.mouseover = false;
|
||
|
}
|
||
|
})
|
||
|
.on('click', function () {
|
||
|
var targetsToShow = $$.filterTargetsToShow($$.data.targets);
|
||
|
var mouse, closest;
|
||
|
|
||
|
if ($$.hasArcType(targetsToShow)) { return; }
|
||
|
|
||
|
mouse = d3.mouse(this);
|
||
|
closest = $$.findClosestFromTargets(targetsToShow, mouse);
|
||
|
|
||
|
if (! closest) { return; }
|
||
|
|
||
|
// select if selection enabled
|
||
|
if ($$.dist(closest, mouse) < 100 && $$.toggleShape) {
|
||
|
$$.main.select('.' + CLASS[_circles] + $$.getTargetSelectorSuffix(closest.id)).select('.' + CLASS[_circle] + '-' + closest.index).each(function () {
|
||
|
$$.toggleShape(this, closest, closest.index);
|
||
|
});
|
||
|
}
|
||
|
})
|
||
|
.call(
|
||
|
d3.behavior.drag().origin(Object)
|
||
|
.on('drag', function () { $$.drag(d3.mouse(this)); })
|
||
|
.on('dragstart', function () { $$.dragstart(d3.mouse(this)); })
|
||
|
.on('dragend', function () { $$.dragend(); })
|
||
|
)
|
||
|
.on("dblclick.zoom", null);
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.isTimeSeries = function () {
|
||
|
return this.config[__axis_x_type] === 'timeseries';
|
||
|
};
|
||
|
c3_chart_internal_fn.isCategorized = function () {
|
||
|
return this.config[__axis_x_type].indexOf('categor') >= 0;
|
||
|
};
|
||
|
c3_chart_internal_fn.isCustomX = function () {
|
||
|
var $$ = this, config = $$.config;
|
||
|
return !$$.isTimeSeries() && (config[__data_x] || notEmpty(config[__data_xs]));
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.getTranslate = function (target) {
|
||
|
var $$ = this, config = $$.config, x, y;
|
||
|
if (target === 'main') {
|
||
|
x = asHalfPixel($$.margin.left);
|
||
|
y = asHalfPixel($$.margin.top);
|
||
|
} else if (target === 'context') {
|
||
|
x = asHalfPixel($$.margin2.left);
|
||
|
y = asHalfPixel($$.margin2.top);
|
||
|
} else if (target === 'legend') {
|
||
|
x = $$.margin3.left;
|
||
|
y = $$.margin3.top;
|
||
|
} else if (target === 'x') {
|
||
|
x = 0;
|
||
|
y = config[__axis_rotated] ? 0 : $$.height;
|
||
|
} else if (target === 'y') {
|
||
|
x = 0;
|
||
|
y = config[__axis_rotated] ? $$.height : 0;
|
||
|
} else if (target === 'y2') {
|
||
|
x = config[__axis_rotated] ? 0 : $$.width;
|
||
|
y = config[__axis_rotated] ? 1 : 0;
|
||
|
} else if (target === 'subx') {
|
||
|
x = 0;
|
||
|
y = config[__axis_rotated] ? 0 : $$.height2;
|
||
|
} else if (target === 'arc') {
|
||
|
x = $$.arcWidth / 2;
|
||
|
y = $$.arcHeight / 2;
|
||
|
}
|
||
|
return "translate(" + x + "," + y + ")";
|
||
|
};
|
||
|
c3_chart_internal_fn.initialOpacity = function (d) {
|
||
|
return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0;
|
||
|
};
|
||
|
c3_chart_internal_fn.opacityForCircle = function (d) {
|
||
|
var $$ = this;
|
||
|
return isValue(d.value) ? $$.isScatterType(d) ? 0.5 : 1 : 0;
|
||
|
};
|
||
|
c3_chart_internal_fn.opacityForText = function () {
|
||
|
return this.hasDataLabel() ? 1 : 0;
|
||
|
};
|
||
|
c3_chart_internal_fn.xx = function (d) {
|
||
|
return d ? this.x(d.x) : null;
|
||
|
};
|
||
|
c3_chart_internal_fn.xv = function (d) {
|
||
|
var $$ = this;
|
||
|
return Math.ceil($$.x($$.isTimeSeries() ? $$.parseDate(d.value) : d.value));
|
||
|
};
|
||
|
c3_chart_internal_fn.yv = function (d) {
|
||
|
var $$ = this,
|
||
|
yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y;
|
||
|
return Math.ceil(yScale(d.value));
|
||
|
};
|
||
|
c3_chart_internal_fn.subxx = function (d) {
|
||
|
return d ? this.subX(d.x) : null;
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.transformMain = function (withTransition, transitions) {
|
||
|
var $$ = this,
|
||
|
xAxis, yAxis, y2Axis;
|
||
|
if (transitions && transitions.axisX) {
|
||
|
xAxis = transitions.axisX;
|
||
|
} else {
|
||
|
xAxis = $$.main.select('.' + CLASS[_axisX]);
|
||
|
if (withTransition) { xAxis = xAxis.transition(); }
|
||
|
}
|
||
|
if (transitions && transitions.axisY) {
|
||
|
yAxis = transitions.axisY;
|
||
|
} else {
|
||
|
yAxis = $$.main.select('.' + CLASS[_axisY]);
|
||
|
if (withTransition) { yAxis = yAxis.transition(); }
|
||
|
}
|
||
|
if (transitions && transitions.axisY2) {
|
||
|
y2Axis = transitions.axisY2;
|
||
|
} else {
|
||
|
y2Axis = $$.main.select('.' + CLASS[_axisY2]);
|
||
|
if (withTransition) { y2Axis = y2Axis.transition(); }
|
||
|
}
|
||
|
(withTransition ? $$.main.transition() : $$.main).attr("transform", $$.getTranslate('main'));
|
||
|
xAxis.attr("transform", $$.getTranslate('x'));
|
||
|
yAxis.attr("transform", $$.getTranslate('y'));
|
||
|
y2Axis.attr("transform", $$.getTranslate('y2'));
|
||
|
$$.main.select('.' + CLASS[_chartArcs]).attr("transform", $$.getTranslate('arc'));
|
||
|
};
|
||
|
c3_chart_internal_fn.transformAll = function (withTransition, transitions) {
|
||
|
var $$ = this;
|
||
|
$$.transformMain(withTransition, transitions);
|
||
|
if (config[__subchart_show]) { $$.transformContext(withTransition, transitions); }
|
||
|
if ($$.legend) { $$.transformLegend(withTransition); }
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.updateSvgSize = function () {
|
||
|
var $$ = this;
|
||
|
$$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight);
|
||
|
$$.svg.select('#' + $$.clipId).select('rect')
|
||
|
.attr('width', $$.width)
|
||
|
.attr('height', $$.height);
|
||
|
$$.svg.select('#' + $$.clipIdForXAxis).select('rect')
|
||
|
.attr('x', generateCall($$.getXAxisClipX, $$))
|
||
|
.attr('y', generateCall($$.getXAxisClipY, $$))
|
||
|
.attr('width', generateCall($$.getXAxisClipWidth, $$))
|
||
|
.attr('height', generateCall($$.getXAxisClipHeight, $$));
|
||
|
$$.svg.select('#' + $$.clipIdForYAxis).select('rect')
|
||
|
.attr('x', generateCall($$.getYAxisClipX, $$))
|
||
|
.attr('y', generateCall($$.getYAxisClipY, $$))
|
||
|
.attr('width', generateCall($$.getYAxisClipWidth, $$))
|
||
|
.attr('height', generateCall($$.getYAxisClipHeight, $$));
|
||
|
$$.svg.select('.' + CLASS[_zoomRect])
|
||
|
.attr('width', $$.width)
|
||
|
.attr('height', $$.height);
|
||
|
// MEMO: parent div's height will be bigger than svg when <!DOCTYPE html>
|
||
|
$$.selectChart.style('max-height', $$.currentHeight + "px");
|
||
|
};
|
||
|
|
||
|
|
||
|
c3_chart_internal_fn.updateDimension = function () {
|
||
|
var $$ = this;
|
||
|
if ($$.config[__axis_rotated]) {
|
||
|
$$.axes.x.call($$.xAxis);
|
||
|
$$.axes.subx.call($$.subXAxis);
|
||
|
} else {
|
||
|
$$.axes.y.call($$.yAxis);
|
||
|
$$.axes.y2.call($$.y2Axis);
|
||
|
}
|
||
|
$$.updateSizes();
|
||
|
$$.updateScales();
|
||
|
$$.updateSvgSize();
|
||
|
$$.transformAll(false);
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.observeInserted = function (selection) {
|
||
|
var $$ = this, observer = new MutationObserver(function (mutations) {
|
||
|
mutations.forEach(function (mutation) {
|
||
|
if (mutation.type === 'childList' && mutation.previousSibling) {
|
||
|
observer.disconnect();
|
||
|
// need to wait for completion of load because size calculation requires the actual sizes determined after that completion
|
||
|
var interval = window.setInterval(function () {
|
||
|
// parentNode will NOT be null when completed
|
||
|
if (selection.node().parentNode) {
|
||
|
window.clearInterval(interval);
|
||
|
$$.updateDimension();
|
||
|
$$.redraw({
|
||
|
withTransform: true,
|
||
|
withUpdateXDomain: true,
|
||
|
withUpdateOrgXDomain: true,
|
||
|
withTransition: false,
|
||
|
withTransitionForTransform: false,
|
||
|
withLegend: true
|
||
|
});
|
||
|
selection.transition().style('opacity', 1);
|
||
|
}
|
||
|
}, 10);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
observer.observe(selection.node(), {attributes: true, childList: true, characterData: true});
|
||
|
};
|
||
|
|
||
|
|
||
|
c3_chart_internal_fn.generateResize = function () {
|
||
|
var resizeFunctions = [];
|
||
|
function callResizeFunctions() {
|
||
|
resizeFunctions.forEach(function (f) {
|
||
|
f();
|
||
|
});
|
||
|
}
|
||
|
callResizeFunctions.add = function (f) {
|
||
|
resizeFunctions.push(f);
|
||
|
};
|
||
|
return callResizeFunctions;
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.transformTo = function (targetIds, type, optionsForRedraw) {
|
||
|
var $$ = this,
|
||
|
withTransitionForAxis = !$$.hasArcType(),
|
||
|
options = optionsForRedraw || {withTransitionForAxis: withTransitionForAxis};
|
||
|
options.withTransitionForTransform = false;
|
||
|
$$.transiting = false;
|
||
|
$$.setTargetType(targetIds, type);
|
||
|
$$.updateAndRedraw(options);
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.generateAxisTransitions = function (duration) {
|
||
|
var $$ = this, axes = $$.axes;
|
||
|
return {
|
||
|
axisX: duration ? axes.x.transition().duration(duration) : axes.x,
|
||
|
axisY: duration ? axes.y.transition().duration(duration) : axes.y,
|
||
|
axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2,
|
||
|
axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx
|
||
|
};
|
||
|
};
|
||
|
c3_chart_internal_fn.endall = function (transition, callback) {
|
||
|
var n = 0;
|
||
|
transition
|
||
|
.each(function () { ++n; })
|
||
|
.each("end", function () {
|
||
|
if (!--n) { callback.apply(this, arguments); }
|
||
|
});
|
||
|
};
|
||
|
c3_chart_internal_fn.generateWait = function () {
|
||
|
var transitionsToWait = [],
|
||
|
f = function (transition, callback) {
|
||
|
var timer = setInterval(function () {
|
||
|
var done = 0;
|
||
|
transitionsToWait.forEach(function (t) {
|
||
|
if (t.empty()) {
|
||
|
done += 1;
|
||
|
return;
|
||
|
}
|
||
|
try {
|
||
|
t.transition();
|
||
|
} catch (e) {
|
||
|
done += 1;
|
||
|
}
|
||
|
});
|
||
|
if (done === transitionsToWait.length) {
|
||
|
clearInterval(timer);
|
||
|
if (callback) { callback(); }
|
||
|
}
|
||
|
}, 10);
|
||
|
};
|
||
|
f.add = function (transition) {
|
||
|
transitionsToWait.push(transition);
|
||
|
};
|
||
|
return f;
|
||
|
};
|
||
|
|
||
|
c3_chart_internal_fn.parseDate = function (date) {
|
||
|
var $$ = this, parsedDate;
|
||
|
if (date instanceof Date) {
|
||
|
parsedDate = date;
|
||
|
} else if (typeof date === 'number') {
|
||
|
parsedDate = new Date(date);
|
||
|
} else {
|
||
|
parsedDate = $$.dataTimeFormat(config[__data_x_format]).parse(date);
|
||
|
}
|
||
|
if (!parsedDate || isNaN(+parsedDate)) {
|
||
|
window.console.error("Failed to parse x '" + date + "' to Date object");
|
||
|
}
|
||
|
return parsedDate;
|
||
|
};
|