mirror of https://github.com/masayuki0812/c3.git
Browse Source
separates Chart.prototype fns from ChartInternal.prototype fns (api.flow and api.transform) separates Chart.prototype fns from ChartInternal.prototype fns (api.flow and api.transform) separates sources into 3 modules + core moves API and inherit functions to their own file, inside axis folder moves ChartInternal constructor from core.js to chartinternal/index.js file moves Chart constructor from core.js to chart/index.js file moves Chart constructor from core.js to chart/index.js file separates core.js into start.js and end.js separates remaining core.js logic into head.js and tail.js polyfill should be in the global namespace concatenate using the new paths creates multitask to generate modules axis, chart and chartinternal runs tests against new module structures adds rollup script module concat tasks also add their imports and exports es6_modules folder, which also contains rollup.entry skeleton adds rollup task renames api_inherits multitask build_rollupv1-dev
Amenadiel
9 years ago
committed by
Ændrew Rininsland
67 changed files with 13211 additions and 5464 deletions
@ -0,0 +1,736 @@
|
||||
import {CLASS,isValue,isFunction,isString,isUndefined,isDefined,ceil10,asHalfPixel,diffDomain,isEmpty,notEmpty,getOption,hasValue,sanitise,getPathBox, ChartInternal} from './chartinternal.js'; |
||||
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; |
||||
} |
||||
|
||||
// Features:
|
||||
// 1. category axis
|
||||
// 2. ceil values of translate/x/y to int for half pixel antialiasing
|
||||
// 3. multiline tick text
|
||||
var tickTextCharSize; |
||||
function c3_axis(d3, params) { |
||||
var scale = d3.scale.linear(), orient = "bottom", innerTickSize = 6, outerTickSize, tickPadding = 3, tickValues = null, tickFormat, tickArguments; |
||||
|
||||
var tickOffset = 0, tickCulling = true, tickCentered; |
||||
|
||||
params = params || {}; |
||||
outerTickSize = params.withOuterTick ? 6 : 0; |
||||
|
||||
function axisX(selection, x) { |
||||
selection.attr("transform", function (d) { |
||||
return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)"; |
||||
}); |
||||
} |
||||
function axisY(selection, y) { |
||||
selection.attr("transform", function (d) { |
||||
return "translate(0," + Math.ceil(y(d)) + ")"; |
||||
}); |
||||
} |
||||
function scaleExtent(domain) { |
||||
var start = domain[0], stop = domain[domain.length - 1]; |
||||
return start < stop ? [ start, stop ] : [ stop, start ]; |
||||
} |
||||
function generateTicks(scale) { |
||||
var i, domain, ticks = []; |
||||
if (scale.ticks) { |
||||
return scale.ticks.apply(scale, tickArguments); |
||||
} |
||||
domain = scale.domain(); |
||||
for (i = Math.ceil(domain[0]); i < domain[1]; i++) { |
||||
ticks.push(i); |
||||
} |
||||
if (ticks.length > 0 && ticks[0] > 0) { |
||||
ticks.unshift(ticks[0] - (ticks[1] - ticks[0])); |
||||
} |
||||
return ticks; |
||||
} |
||||
function copyScale() { |
||||
var newScale = scale.copy(), domain; |
||||
if (params.isCategory) { |
||||
domain = scale.domain(); |
||||
newScale.domain([domain[0], domain[1] - 1]); |
||||
} |
||||
return newScale; |
||||
} |
||||
function textFormatted(v) { |
||||
var formatted = tickFormat ? tickFormat(v) : v; |
||||
return typeof formatted !== 'undefined' ? formatted : ''; |
||||
} |
||||
function getSizeFor1Char(tick) { |
||||
if (tickTextCharSize) { |
||||
return tickTextCharSize; |
||||
} |
||||
var size = { |
||||
h: 11.5, |
||||
w: 5.5 |
||||
}; |
||||
tick.select('text').text(textFormatted).each(function (d) { |
||||
var box = this.getBoundingClientRect(), |
||||
text = textFormatted(d), |
||||
h = box.height, |
||||
w = text ? (box.width / text.length) : undefined; |
||||
if (h && w) { |
||||
size.h = h; |
||||
size.w = w; |
||||
} |
||||
}).text(''); |
||||
tickTextCharSize = size; |
||||
return size; |
||||
} |
||||
function transitionise(selection) { |
||||
return params.withoutTransition ? selection : d3.transition(selection); |
||||
} |
||||
function axis(g) { |
||||
g.each(function () { |
||||
var g = axis.g = d3.select(this); |
||||
|
||||
var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = copyScale(); |
||||
|
||||
var ticks = tickValues ? tickValues : generateTicks(scale1), |
||||
tick = g.selectAll(".tick").data(ticks, scale1), |
||||
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.
|
||||
tickExit = tick.exit().remove(), |
||||
tickUpdate = transitionise(tick).style("opacity", 1), |
||||
tickTransform, tickX, tickY; |
||||
|
||||
var range = scale.rangeExtent ? scale.rangeExtent() : scaleExtent(scale.range()), |
||||
path = g.selectAll(".domain").data([ 0 ]), |
||||
pathUpdate = (path.enter().append("path").attr("class", "domain"), transitionise(path)); |
||||
tickEnter.append("line"); |
||||
tickEnter.append("text"); |
||||
|
||||
var lineEnter = tickEnter.select("line"), |
||||
lineUpdate = tickUpdate.select("line"), |
||||
textEnter = tickEnter.select("text"), |
||||
textUpdate = tickUpdate.select("text"); |
||||
|
||||
if (params.isCategory) { |
||||
tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2); |
||||
tickX = tickCentered ? 0 : tickOffset; |
||||
tickY = tickCentered ? tickOffset : 0; |
||||
} else { |
||||
tickOffset = tickX = 0; |
||||
} |
||||
|
||||
var text, tspan, sizeFor1Char = getSizeFor1Char(g.select('.tick')), counts = []; |
||||
var tickLength = Math.max(innerTickSize, 0) + tickPadding, |
||||
isVertical = orient === 'left' || orient === 'right'; |
||||
|
||||
// this should be called only when category axis
|
||||
function splitTickText(d, maxWidth) { |
||||
var tickText = textFormatted(d), |
||||
subtext, spaceIndex, textWidth, splitted = []; |
||||
|
||||
if (Object.prototype.toString.call(tickText) === "[object Array]") { |
||||
return tickText; |
||||
} |
||||
|
||||
if (!maxWidth || maxWidth <= 0) { |
||||
maxWidth = isVertical ? 95 : params.isCategory ? (Math.ceil(scale1(ticks[1]) - scale1(ticks[0])) - 12) : 110; |
||||
} |
||||
|
||||
function split(splitted, text) { |
||||
spaceIndex = undefined; |
||||
for (var i = 1; i < text.length; i++) { |
||||
if (text.charAt(i) === ' ') { |
||||
spaceIndex = i; |
||||
} |
||||
subtext = text.substr(0, i + 1); |
||||
textWidth = sizeFor1Char.w * subtext.length; |
||||
// if text width gets over tick width, split by space index or crrent index
|
||||
if (maxWidth < textWidth) { |
||||
return split( |
||||
splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), |
||||
text.slice(spaceIndex ? spaceIndex + 1 : i) |
||||
); |
||||
} |
||||
} |
||||
return splitted.concat(text); |
||||
} |
||||
|
||||
return split(splitted, tickText + ""); |
||||
} |
||||
|
||||
function tspanDy(d, i) { |
||||
var dy = sizeFor1Char.h; |
||||
if (i === 0) { |
||||
if (orient === 'left' || orient === 'right') { |
||||
dy = -((counts[d.index] - 1) * (sizeFor1Char.h / 2) - 3); |
||||
} else { |
||||
dy = ".71em"; |
||||
} |
||||
} |
||||
return dy; |
||||
} |
||||
|
||||
function tickSize(d) { |
||||
var tickPosition = scale(d) + (tickCentered ? 0 : tickOffset); |
||||
return range[0] < tickPosition && tickPosition < range[1] ? innerTickSize : 0; |
||||
} |
||||
|
||||
text = tick.select("text"); |
||||
tspan = text.selectAll('tspan') |
||||
.data(function (d, i) { |
||||
var splitted = params.tickMultiline ? splitTickText(d, params.tickWidth) : [].concat(textFormatted(d)); |
||||
counts[i] = splitted.length; |
||||
return splitted.map(function (s) { |
||||
return { index: i, splitted: s }; |
||||
}); |
||||
}); |
||||
tspan.enter().append('tspan'); |
||||
tspan.exit().remove(); |
||||
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) { |
||||
case "bottom": |
||||
{ |
||||
tickTransform = axisX; |
||||
lineEnter.attr("y2", innerTickSize); |
||||
textEnter.attr("y", tickLength); |
||||
lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize); |
||||
textUpdate.attr("x", 0).attr("y", yForText(rotate)) |
||||
.style("text-anchor", textAnchorForText(rotate)) |
||||
.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); |
||||
break; |
||||
} |
||||
case "top": |
||||
{ |
||||
// TODO: rotated tick text
|
||||
tickTransform = axisX; |
||||
lineEnter.attr("y2", -innerTickSize); |
||||
textEnter.attr("y", -tickLength); |
||||
lineUpdate.attr("x2", 0).attr("y2", -innerTickSize); |
||||
textUpdate.attr("x", 0).attr("y", -tickLength); |
||||
text.style("text-anchor", "middle"); |
||||
tspan.attr('x', 0).attr("dy", "0em"); |
||||
pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize); |
||||
break; |
||||
} |
||||
case "left": |
||||
{ |
||||
tickTransform = axisY; |
||||
lineEnter.attr("x2", -innerTickSize); |
||||
textEnter.attr("x", -tickLength); |
||||
lineUpdate.attr("x2", -innerTickSize).attr("y1", tickY).attr("y2", tickY); |
||||
textUpdate.attr("x", -tickLength).attr("y", tickOffset); |
||||
text.style("text-anchor", "end"); |
||||
tspan.attr('x', -tickLength).attr("dy", tspanDy); |
||||
pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize); |
||||
break; |
||||
} |
||||
case "right": |
||||
{ |
||||
tickTransform = axisY; |
||||
lineEnter.attr("x2", innerTickSize); |
||||
textEnter.attr("x", tickLength); |
||||
lineUpdate.attr("x2", innerTickSize).attr("y2", 0); |
||||
textUpdate.attr("x", tickLength).attr("y", 0); |
||||
text.style("text-anchor", "start"); |
||||
tspan.attr('x', tickLength).attr("dy", tspanDy); |
||||
pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize); |
||||
break; |
||||
} |
||||
} |
||||
if (scale1.rangeBand) { |
||||
var x = scale1, dx = x.rangeBand() / 2; |
||||
scale0 = scale1 = function (d) { |
||||
return x(d) + dx; |
||||
}; |
||||
} else if (scale0.rangeBand) { |
||||
scale0 = scale1; |
||||
} else { |
||||
tickExit.call(tickTransform, scale1); |
||||
} |
||||
tickEnter.call(tickTransform, scale0); |
||||
tickUpdate.call(tickTransform, scale1); |
||||
}); |
||||
} |
||||
axis.scale = function (x) { |
||||
if (!arguments.length) { return scale; } |
||||
scale = x; |
||||
return axis; |
||||
}; |
||||
axis.orient = function (x) { |
||||
if (!arguments.length) { return orient; } |
||||
orient = x in {top: 1, right: 1, bottom: 1, left: 1} ? x + "" : "bottom"; |
||||
return axis; |
||||
}; |
||||
axis.tickFormat = function (format) { |
||||
if (!arguments.length) { return tickFormat; } |
||||
tickFormat = format; |
||||
return axis; |
||||
}; |
||||
axis.tickCentered = function (isCentered) { |
||||
if (!arguments.length) { return tickCentered; } |
||||
tickCentered = isCentered; |
||||
return axis; |
||||
}; |
||||
axis.tickOffset = function () { |
||||
return tickOffset; |
||||
}; |
||||
axis.tickInterval = function () { |
||||
var interval, length; |
||||
if (params.isCategory) { |
||||
interval = tickOffset * 2; |
||||
} |
||||
else { |
||||
length = axis.g.select('path.domain').node().getTotalLength() - outerTickSize * 2; |
||||
interval = length / axis.g.selectAll('line').size(); |
||||
} |
||||
return interval === Infinity ? 0 : interval; |
||||
}; |
||||
axis.ticks = function () { |
||||
if (!arguments.length) { return tickArguments; } |
||||
tickArguments = arguments; |
||||
return axis; |
||||
}; |
||||
axis.tickCulling = function (culling) { |
||||
if (!arguments.length) { return tickCulling; } |
||||
tickCulling = culling; |
||||
return axis; |
||||
}; |
||||
axis.tickValues = function (x) { |
||||
if (typeof x === 'function') { |
||||
tickValues = function () { |
||||
return x(scale.domain()); |
||||
}; |
||||
} |
||||
else { |
||||
if (!arguments.length) { return tickValues; } |
||||
tickValues = x; |
||||
} |
||||
return axis; |
||||
}; |
||||
return axis; |
||||
} |
||||
|
||||
function Axis(owner) { |
||||
API.call(this, owner); |
||||
} |
||||
|
||||
inherit(API, Axis); |
||||
|
||||
Axis.prototype.init = function init() { |
||||
|
||||
var $$ = this.owner, config = $$.config, main = $$.main; |
||||
$$.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", this.textAnchorForXAxisLabel.bind(this)); |
||||
$$.axes.y = main.append("g") |
||||
.attr("class", CLASS.axis + ' ' + CLASS.axisY) |
||||
.attr("clip-path", config.axis_y_inner ? "" : $$.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", this.textAnchorForYAxisLabel.bind(this)); |
||||
|
||||
$$.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", this.textAnchorForY2AxisLabel.bind(this)); |
||||
}; |
||||
Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { |
||||
var $$ = this.owner, config = $$.config, |
||||
axisParams = { |
||||
isCategory: $$.isCategorized(), |
||||
withOuterTick: withOuterTick, |
||||
tickMultiline: config.axis_x_tick_multiline, |
||||
tickWidth: config.axis_x_tick_width, |
||||
tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate, |
||||
withoutTransition: withoutTransition, |
||||
}, |
||||
axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient); |
||||
|
||||
if ($$.isTimeSeries() && tickValues && typeof tickValues !== "function") { |
||||
tickValues = tickValues.map(function (v) { return $$.parseDate(v); }); |
||||
} |
||||
|
||||
// Set tick
|
||||
axis.tickFormat(tickFormat).tickValues(tickValues); |
||||
if ($$.isCategorized()) { |
||||
axis.tickCentered(config.axis_x_tick_centered); |
||||
if (isEmpty(config.axis_x_tick_culling)) { |
||||
config.axis_x_tick_culling = false; |
||||
} |
||||
} |
||||
|
||||
return axis; |
||||
}; |
||||
Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) { |
||||
var $$ = this.owner, config = $$.config, tickValues; |
||||
if (config.axis_x_tick_fit || config.axis_x_tick_count) { |
||||
tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries()); |
||||
} |
||||
if (axis) { |
||||
axis.tickValues(tickValues); |
||||
} else { |
||||
$$.xAxis.tickValues(tickValues); |
||||
$$.subXAxis.tickValues(tickValues); |
||||
} |
||||
return tickValues; |
||||
}; |
||||
Axis.prototype.getYAxis = function getYAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { |
||||
var $$ = this.owner, config = $$.config, |
||||
axisParams = { |
||||
withOuterTick: withOuterTick, |
||||
withoutTransition: withoutTransition, |
||||
tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate |
||||
}, |
||||
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 { |
||||
axis.tickValues(tickValues); |
||||
} |
||||
return axis; |
||||
}; |
||||
Axis.prototype.getId = function getId(id) { |
||||
var config = this.owner.config; |
||||
return id in config.data_axes ? config.data_axes[id] : 'y'; |
||||
}; |
||||
Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() { |
||||
var $$ = this.owner, config = $$.config, |
||||
format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { return v < 0 ? v.toFixed(0) : v; }; |
||||
if (config.axis_x_tick_format) { |
||||
if (isFunction(config.axis_x_tick_format)) { |
||||
format = config.axis_x_tick_format; |
||||
} else if ($$.isTimeSeries()) { |
||||
format = function (date) { |
||||
return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : ""; |
||||
}; |
||||
} |
||||
} |
||||
return isFunction(format) ? function (v) { return format.call($$, v); } : format; |
||||
}; |
||||
Axis.prototype.getTickValues = function getTickValues(tickValues, axis) { |
||||
return tickValues ? tickValues : axis ? axis.tickValues() : undefined; |
||||
}; |
||||
Axis.prototype.getXAxisTickValues = function getXAxisTickValues() { |
||||
return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis); |
||||
}; |
||||
Axis.prototype.getYAxisTickValues = function getYAxisTickValues() { |
||||
return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis); |
||||
}; |
||||
Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() { |
||||
return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis); |
||||
}; |
||||
Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) { |
||||
var $$ = this.owner, config = $$.config, option; |
||||
if (axisId === 'y') { |
||||
option = config.axis_y_label; |
||||
} else if (axisId === 'y2') { |
||||
option = config.axis_y2_label; |
||||
} else if (axisId === 'x') { |
||||
option = config.axis_x_label; |
||||
} |
||||
return option; |
||||
}; |
||||
Axis.prototype.getLabelText = function getLabelText(axisId) { |
||||
var option = this.getLabelOptionByAxisId(axisId); |
||||
return isString(option) ? option : option ? option.text : null; |
||||
}; |
||||
Axis.prototype.setLabelText = function setLabelText(axisId, text) { |
||||
var $$ = this.owner, config = $$.config, |
||||
option = this.getLabelOptionByAxisId(axisId); |
||||
if (isString(option)) { |
||||
if (axisId === 'y') { |
||||
config.axis_y_label = text; |
||||
} else if (axisId === 'y2') { |
||||
config.axis_y2_label = text; |
||||
} else if (axisId === 'x') { |
||||
config.axis_x_label = text; |
||||
} |
||||
} else if (option) { |
||||
option.text = text; |
||||
} |
||||
}; |
||||
Axis.prototype.getLabelPosition = function getLabelPosition(axisId, defaultPosition) { |
||||
var option = this.getLabelOptionByAxisId(axisId), |
||||
position = (option && typeof option === 'object' && option.position) ? option.position : defaultPosition; |
||||
return { |
||||
isInner: position.indexOf('inner') >= 0, |
||||
isOuter: position.indexOf('outer') >= 0, |
||||
isLeft: position.indexOf('left') >= 0, |
||||
isCenter: position.indexOf('center') >= 0, |
||||
isRight: position.indexOf('right') >= 0, |
||||
isTop: position.indexOf('top') >= 0, |
||||
isMiddle: position.indexOf('middle') >= 0, |
||||
isBottom: position.indexOf('bottom') >= 0 |
||||
}; |
||||
}; |
||||
Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() { |
||||
return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right'); |
||||
}; |
||||
Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() { |
||||
return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); |
||||
}; |
||||
Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() { |
||||
return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); |
||||
}; |
||||
Axis.prototype.getLabelPositionById = function getLabelPositionById(id) { |
||||
return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition(); |
||||
}; |
||||
Axis.prototype.textForXAxisLabel = function textForXAxisLabel() { |
||||
return this.getLabelText('x'); |
||||
}; |
||||
Axis.prototype.textForYAxisLabel = function textForYAxisLabel() { |
||||
return this.getLabelText('y'); |
||||
}; |
||||
Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() { |
||||
return this.getLabelText('y2'); |
||||
}; |
||||
Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) { |
||||
var $$ = this.owner; |
||||
if (forHorizontal) { |
||||
return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width; |
||||
} else { |
||||
return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0; |
||||
} |
||||
}; |
||||
Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) { |
||||
if (forHorizontal) { |
||||
return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0"; |
||||
} else { |
||||
return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0"; |
||||
} |
||||
}; |
||||
Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) { |
||||
if (forHorizontal) { |
||||
return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end'; |
||||
} else { |
||||
return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end'; |
||||
} |
||||
}; |
||||
Axis.prototype.xForXAxisLabel = function xForXAxisLabel() { |
||||
return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.xForYAxisLabel = function xForYAxisLabel() { |
||||
return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() { |
||||
return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() { |
||||
return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() { |
||||
return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() { |
||||
return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() { |
||||
var $$ = this.owner, config = $$.config, |
||||
position = this.getXAxisLabelPosition(); |
||||
if (config.axis_rotated) { |
||||
return position.isInner ? "1.2em" : -25 - this.getMaxTickWidth('x'); |
||||
} else { |
||||
return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em"; |
||||
} |
||||
}; |
||||
Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() { |
||||
var $$ = this.owner, |
||||
position = this.getYAxisLabelPosition(); |
||||
if ($$.config.axis_rotated) { |
||||
return position.isInner ? "-0.5em" : "3em"; |
||||
} else { |
||||
return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : (this.getMaxTickWidth('y') + 10)); |
||||
} |
||||
}; |
||||
Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() { |
||||
var $$ = this.owner, |
||||
position = this.getY2AxisLabelPosition(); |
||||
if ($$.config.axis_rotated) { |
||||
return position.isInner ? "1.2em" : "-2.2em"; |
||||
} else { |
||||
return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : (this.getMaxTickWidth('y2') + 15)); |
||||
} |
||||
}; |
||||
Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() { |
||||
var $$ = this.owner; |
||||
return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() { |
||||
var $$ = this.owner; |
||||
return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() { |
||||
var $$ = this.owner; |
||||
return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition()); |
||||
}; |
||||
Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) { |
||||
var $$ = this.owner, config = $$.config, |
||||
maxWidth = 0, targetsToShow, scale, axis, dummy, svg; |
||||
if (withoutRecompute && $$.currentMaxTickWidths[id]) { |
||||
return $$.currentMaxTickWidths[id]; |
||||
} |
||||
if ($$.svg) { |
||||
targetsToShow = $$.filterTargetsToShow($$.data.targets); |
||||
if (id === 'y') { |
||||
scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')); |
||||
axis = this.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, false, true, true); |
||||
} else if (id === 'y2') { |
||||
scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')); |
||||
axis = this.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, false, true, true); |
||||
} else { |
||||
scale = $$.x.copy().domain($$.getXDomain(targetsToShow)); |
||||
axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true); |
||||
this.updateXAxisTickValues(targetsToShow, axis); |
||||
} |
||||
dummy = $$.d3.select('body').append('div').classed('c3', true); |
||||
svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0), |
||||
svg.append('g').call(axis).each(function () { |
||||
$$.d3.select(this).selectAll('text').each(function () { |
||||
var box = this.getBoundingClientRect(); |
||||
if (maxWidth < box.width) { maxWidth = box.width; } |
||||
}); |
||||
dummy.remove(); |
||||
}); |
||||
} |
||||
$$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth; |
||||
return $$.currentMaxTickWidths[id]; |
||||
}; |
||||
|
||||
Axis.prototype.updateLabels = function updateLabels(withTransition) { |
||||
var $$ = this.owner; |
||||
var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), |
||||
axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), |
||||
axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label); |
||||
(withTransition ? axisXLabel.transition() : axisXLabel) |
||||
.attr("x", this.xForXAxisLabel.bind(this)) |
||||
.attr("dx", this.dxForXAxisLabel.bind(this)) |
||||
.attr("dy", this.dyForXAxisLabel.bind(this)) |
||||
.text(this.textForXAxisLabel.bind(this)); |
||||
(withTransition ? axisYLabel.transition() : axisYLabel) |
||||
.attr("x", this.xForYAxisLabel.bind(this)) |
||||
.attr("dx", this.dxForYAxisLabel.bind(this)) |
||||
.attr("dy", this.dyForYAxisLabel.bind(this)) |
||||
.text(this.textForYAxisLabel.bind(this)); |
||||
(withTransition ? axisY2Label.transition() : axisY2Label) |
||||
.attr("x", this.xForY2AxisLabel.bind(this)) |
||||
.attr("dx", this.dxForY2AxisLabel.bind(this)) |
||||
.attr("dy", this.dyForY2AxisLabel.bind(this)) |
||||
.text(this.textForY2AxisLabel.bind(this)); |
||||
}; |
||||
Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) { |
||||
var p = typeof padding === 'number' ? padding : padding[key]; |
||||
if (!isValue(p)) { |
||||
return defaultValue; |
||||
} |
||||
if (padding.unit === 'ratio') { |
||||
return padding[key] * domainLength; |
||||
} |
||||
// assume padding is pixels if unit is not specified
|
||||
return this.convertPixelsToAxisPadding(p, domainLength); |
||||
}; |
||||
Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) { |
||||
var $$ = this.owner, |
||||
length = $$.config.axis_rotated ? $$.width : $$.height; |
||||
return domainLength * (pixels / length); |
||||
}; |
||||
Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) { |
||||
var tickValues = values, targetCount, start, end, count, interval, i, tickValue; |
||||
if (tickCount) { |
||||
targetCount = isFunction(tickCount) ? tickCount() : tickCount; |
||||
// compute ticks according to tickCount
|
||||
if (targetCount === 1) { |
||||
tickValues = [values[0]]; |
||||
} else if (targetCount === 2) { |
||||
tickValues = [values[0], values[values.length - 1]]; |
||||
} else if (targetCount > 2) { |
||||
count = targetCount - 2; |
||||
start = values[0]; |
||||
end = values[values.length - 1]; |
||||
interval = (end - start) / (count + 1); |
||||
// re-construct unique values
|
||||
tickValues = [start]; |
||||
for (i = 0; i < count; i++) { |
||||
tickValue = +start + interval * (i + 1); |
||||
tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue); |
||||
} |
||||
tickValues.push(end); |
||||
} |
||||
} |
||||
if (!forTimeSeries) { tickValues = tickValues.sort(function (a, b) { return a - b; }); } |
||||
return tickValues; |
||||
}; |
||||
Axis.prototype.generateTransitions = function generateTransitions(duration) { |
||||
var $$ = this.owner, 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 |
||||
}; |
||||
}; |
||||
Axis.prototype.redraw = function redraw(transitions, isHidden) { |
||||
var $$ = this.owner; |
||||
$$.axes.x.style("opacity", isHidden ? 0 : 1); |
||||
$$.axes.y.style("opacity", isHidden ? 0 : 1); |
||||
$$.axes.y2.style("opacity", isHidden ? 0 : 1); |
||||
$$.axes.subx.style("opacity", isHidden ? 0 : 1); |
||||
transitions.axisX.call($$.xAxis); |
||||
transitions.axisY.call($$.yAxis); |
||||
transitions.axisY2.call($$.y2Axis); |
||||
transitions.axisSubX.call($$.subXAxis); |
||||
}; |
||||
export {Axis}; |
||||
export default Axis; |
@ -0,0 +1,764 @@
|
||||
import {CLASS,isValue,isFunction,isString,isUndefined,isDefined,ceil10,asHalfPixel,diffDomain,isEmpty,notEmpty,getOption,hasValue,sanitise,getPathBox, ChartInternal} from './chartinternal.js'; |
||||
var c3_chart_fn; |
||||
|
||||
|
||||
function Chart(config) { |
||||
var $$ = this.internal = new ChartInternal(this); |
||||
$$.loadConfig(config); |
||||
|
||||
$$.beforeInit(config); |
||||
$$.init(); |
||||
$$.afterInit(config); |
||||
|
||||
// bind "this" to nested API
|
||||
(function bindThis(fn, target, argThis) { |
||||
Object.keys(fn).forEach(function(key) { |
||||
target[key] = fn[key].bind(argThis); |
||||
if (Object.keys(fn[key]).length > 0) { |
||||
bindThis(fn[key], target[key], argThis); |
||||
} |
||||
}); |
||||
})(c3_chart_fn, this, this); |
||||
} |
||||
|
||||
c3_chart_fn = Chart.prototype; |
||||
|
||||
c3_chart_fn.focus = function (targetIds) { |
||||
var $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), |
||||
|
||||
this.revert(); |
||||
this.defocus(); |
||||
candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); |
||||
if ($$.hasArcType()) { |
||||
$$.expandArc(targetIds); |
||||
} |
||||
$$.toggleFocusLegend(targetIds, true); |
||||
|
||||
$$.focusedTargetIds = targetIds; |
||||
$$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) { |
||||
return targetIds.indexOf(id) < 0; |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.defocus = function (targetIds) { |
||||
var $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), |
||||
|
||||
candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); |
||||
if ($$.hasArcType()) { |
||||
$$.unexpandArc(targetIds); |
||||
} |
||||
$$.toggleFocusLegend(targetIds, false); |
||||
|
||||
$$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) { |
||||
return targetIds.indexOf(id) < 0; |
||||
}); |
||||
$$.defocusedTargetIds = targetIds; |
||||
}; |
||||
|
||||
c3_chart_fn.revert = function (targetIds) { |
||||
var $$ = this.internal, candidates; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets
|
||||
|
||||
candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); |
||||
if ($$.hasArcType()) { |
||||
$$.unexpandArc(targetIds); |
||||
} |
||||
if ($$.config.legend_show) { |
||||
$$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); |
||||
$$.legend.selectAll($$.selectorLegends(targetIds)) |
||||
.filter(function () { |
||||
return $$.d3.select(this).classed(CLASS.legendItemFocused); |
||||
}) |
||||
.classed(CLASS.legendItemFocused, false); |
||||
} |
||||
|
||||
$$.focusedTargetIds = []; |
||||
$$.defocusedTargetIds = []; |
||||
}; |
||||
|
||||
c3_chart_fn.show = function (targetIds, options) { |
||||
var $$ = this.internal, targets; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
options = options || {}; |
||||
|
||||
$$.removeHiddenTargetIds(targetIds); |
||||
targets = $$.svg.selectAll($$.selectorTargets(targetIds)); |
||||
|
||||
targets.transition() |
||||
.style('opacity', 1, 'important') |
||||
.call($$.endall, function () { |
||||
targets.style('opacity', null).style('opacity', 1); |
||||
}); |
||||
|
||||
if (options.withLegend) { |
||||
$$.showLegend(targetIds); |
||||
} |
||||
|
||||
$$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true}); |
||||
}; |
||||
|
||||
c3_chart_fn.hide = function (targetIds, options) { |
||||
var $$ = this.internal, targets; |
||||
|
||||
targetIds = $$.mapToTargetIds(targetIds); |
||||
options = options || {}; |
||||
|
||||
$$.addHiddenTargetIds(targetIds); |
||||
targets = $$.svg.selectAll($$.selectorTargets(targetIds)); |
||||
|
||||
targets.transition() |
||||
.style('opacity', 0, 'important') |
||||
.call($$.endall, function () { |
||||
targets.style('opacity', null).style('opacity', 0); |
||||
}); |
||||
|
||||
if (options.withLegend) { |
||||
$$.hideLegend(targetIds); |
||||
} |
||||
|
||||
$$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true}); |
||||
}; |
||||
|
||||
c3_chart_fn.toggle = function (targetIds, options) { |
||||
var that = this, $$ = this.internal; |
||||
$$.mapToTargetIds(targetIds).forEach(function (targetId) { |
||||
$$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.zoom = function (domain) { |
||||
var $$ = this.internal; |
||||
if (domain) { |
||||
if ($$.isTimeSeries()) { |
||||
domain = domain.map(function (x) { return $$.parseDate(x); }); |
||||
} |
||||
$$.brush.extent(domain); |
||||
$$.redraw({withUpdateXDomain: true, withY: $$.config.zoom_rescale}); |
||||
$$.config.zoom_onzoom.call(this, $$.x.orgDomain()); |
||||
} |
||||
return $$.brush.extent(); |
||||
}; |
||||
c3_chart_fn.zoom.enable = function (enabled) { |
||||
var $$ = this.internal; |
||||
$$.config.zoom_enabled = enabled; |
||||
$$.updateAndRedraw(); |
||||
}; |
||||
c3_chart_fn.unzoom = function () { |
||||
var $$ = this.internal; |
||||
$$.brush.clear().update(); |
||||
$$.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() |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
c3_chart_fn.load = function (args) { |
||||
var $$ = this.internal, config = $$.config; |
||||
// update xs if specified
|
||||
if (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
|
||||
if ('classes' in args) { |
||||
Object.keys(args.classes).forEach(function (id) { |
||||
config.data_classes[id] = args.classes[id]; |
||||
}); |
||||
} |
||||
// update categories if exists
|
||||
if ('categories' in args && $$.isCategorized()) { |
||||
config.axis_x_categories = args.categories; |
||||
} |
||||
// update axes if exists
|
||||
if ('axes' in args) { |
||||
Object.keys(args.axes).forEach(function (id) { |
||||
config.data_axes[id] = args.axes[id]; |
||||
}); |
||||
} |
||||
// 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
|
||||
if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { |
||||
$$.load($$.getCaches(args.cacheIds), args.done); |
||||
return; |
||||
} |
||||
// unload if needed
|
||||
if ('unload' in args) { |
||||
// TODO: do not unload if target will load (included in url/rows/columns)
|
||||
$$.unload($$.mapToTargetIds((typeof args.unload === 'boolean' && args.unload) ? null : args.unload), function () { |
||||
$$.loadFromArgs(args); |
||||
}); |
||||
} else { |
||||
$$.loadFromArgs(args); |
||||
} |
||||
}; |
||||
|
||||
c3_chart_fn.unload = function (args) { |
||||
var $$ = this.internal; |
||||
args = args || {}; |
||||
if (args instanceof Array) { |
||||
args = {ids: args}; |
||||
} else if (typeof args === 'string') { |
||||
args = {ids: [args]}; |
||||
} |
||||
$$.unload($$.mapToTargetIds(args.ids), function () { |
||||
$$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true}); |
||||
if (args.done) { args.done(); } |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.flow = function(args) { |
||||
var $$ = this.internal, |
||||
targets, data, notfoundIds = [], |
||||
orgDataCount = $$.getMaxDataCount(), |
||||
dataCount, domain, baseTarget, baseValue, length = 0, |
||||
tail = 0, |
||||
diff, to; |
||||
|
||||
if (args.json) { |
||||
data = $$.convertJsonToData(args.json, args.keys); |
||||
} else if (args.rows) { |
||||
data = $$.convertRowsToData(args.rows); |
||||
} else if (args.columns) { |
||||
data = $$.convertColumnsToData(args.columns); |
||||
} else { |
||||
return; |
||||
} |
||||
targets = $$.convertDataToTargets(data, true); |
||||
|
||||
// Update/Add data
|
||||
$$.data.targets.forEach(function(t) { |
||||
var found = false, |
||||
i, j; |
||||
for (i = 0; i < targets.length; i++) { |
||||
if (t.id === targets[i].id) { |
||||
found = true; |
||||
|
||||
if (t.values[t.values.length - 1]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
} |
||||
length = targets[i].values.length; |
||||
|
||||
for (j = 0; j < length; j++) { |
||||
targets[i].values[j].index = tail + j; |
||||
if (!$$.isTimeSeries()) { |
||||
targets[i].values[j].x = tail + j; |
||||
} |
||||
} |
||||
t.values = t.values.concat(targets[i].values); |
||||
|
||||
targets.splice(i, 1); |
||||
break; |
||||
} |
||||
} |
||||
if (!found) { notfoundIds.push(t.id); } |
||||
}); |
||||
|
||||
// Append null for not found targets
|
||||
$$.data.targets.forEach(function(t) { |
||||
var i, j; |
||||
for (i = 0; i < notfoundIds.length; i++) { |
||||
if (t.id === notfoundIds[i]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
for (j = 0; j < length; j++) { |
||||
t.values.push({ |
||||
id: t.id, |
||||
index: tail + j, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, |
||||
value: null |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// Generate null values for new target
|
||||
if ($$.data.targets.length) { |
||||
targets.forEach(function(t) { |
||||
var i, missing = []; |
||||
for (i = $$.data.targets[0].values[0].index; i < tail; i++) { |
||||
missing.push({ |
||||
id: t.id, |
||||
index: i, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, |
||||
value: null |
||||
}); |
||||
} |
||||
t.values.forEach(function(v) { |
||||
v.index += tail; |
||||
if (!$$.isTimeSeries()) { |
||||
v.x += tail; |
||||
} |
||||
}); |
||||
t.values = missing.concat(t.values); |
||||
}); |
||||
} |
||||
$$.data.targets = $$.data.targets.concat(targets); // add remained
|
||||
|
||||
// check data count because behavior needs to change when it's only one
|
||||
dataCount = $$.getMaxDataCount(); |
||||
baseTarget = $$.data.targets[0]; |
||||
baseValue = baseTarget.values[0]; |
||||
|
||||
// Update length to flow if needed
|
||||
if (isDefined(args.to)) { |
||||
length = 0; |
||||
to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; |
||||
baseTarget.values.forEach(function(v) { |
||||
if (v.x < to) { length++; } |
||||
}); |
||||
} else if (isDefined(args.length)) { |
||||
length = args.length; |
||||
} |
||||
|
||||
// If only one data, update the domain to flow from left edge of the chart
|
||||
if (!orgDataCount) { |
||||
if ($$.isTimeSeries()) { |
||||
if (baseTarget.values.length > 1) { |
||||
diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; |
||||
} else { |
||||
diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; |
||||
} |
||||
} else { |
||||
diff = 1; |
||||
} |
||||
domain = [baseValue.x - diff, baseValue.x]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} else if (orgDataCount === 1) { |
||||
if ($$.isTimeSeries()) { |
||||
diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; |
||||
domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} |
||||
} |
||||
|
||||
// Set targets
|
||||
$$.updateTargets($$.data.targets); |
||||
|
||||
// Redraw with new targets
|
||||
$$.redraw({ |
||||
flow: { |
||||
index: baseValue.index, |
||||
length: length, |
||||
duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, |
||||
done: args.done, |
||||
orgDataCount: orgDataCount, |
||||
}, |
||||
withLegend: true, |
||||
withTransition: orgDataCount > 1, |
||||
withTrimXDomain: false, |
||||
withUpdateXAxis: true, |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.selected = function (targetId) { |
||||
var $$ = this.internal, d3 = $$.d3; |
||||
return d3.merge( |
||||
$$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape) |
||||
.filter(function () { return d3.select(this).classed(CLASS.SELECTED); }) |
||||
.map(function (d) { return d.map(function (d) { var data = d.__data__; return data.data ? data.data : data; }); }) |
||||
); |
||||
}; |
||||
c3_chart_fn.select = function (ids, indices, resetOther) { |
||||
var $$ = this.internal, d3 = $$.d3, config = $$.config; |
||||
if (! config.data_selection_enabled) { return; } |
||||
$$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { |
||||
var shape = d3.select(this), id = d.data ? d.data.id : d.id, |
||||
toggle = $$.getToggle(this, d).bind($$), |
||||
isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, |
||||
isTargetIndex = !indices || indices.indexOf(i) >= 0, |
||||
isSelected = shape.classed(CLASS.SELECTED); |
||||
// line/area selection not supported yet
|
||||
if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { |
||||
return; |
||||
} |
||||
if (isTargetId && isTargetIndex) { |
||||
if (config.data_selection_isselectable(d) && !isSelected) { |
||||
toggle(true, shape.classed(CLASS.SELECTED, true), d, i); |
||||
} |
||||
} else if (isDefined(resetOther) && resetOther) { |
||||
if (isSelected) { |
||||
toggle(false, shape.classed(CLASS.SELECTED, false), d, i); |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
c3_chart_fn.unselect = function (ids, indices) { |
||||
var $$ = this.internal, d3 = $$.d3, config = $$.config; |
||||
if (! config.data_selection_enabled) { return; } |
||||
$$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { |
||||
var shape = d3.select(this), id = d.data ? d.data.id : d.id, |
||||
toggle = $$.getToggle(this, d).bind($$), |
||||
isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, |
||||
isTargetIndex = !indices || indices.indexOf(i) >= 0, |
||||
isSelected = shape.classed(CLASS.SELECTED); |
||||
// line/area selection not supported yet
|
||||
if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { |
||||
return; |
||||
} |
||||
if (isTargetId && isTargetIndex) { |
||||
if (config.data_selection_isselectable(d)) { |
||||
if (isSelected) { |
||||
toggle(false, shape.classed(CLASS.SELECTED, false), d, i); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_fn.transform = function(type, targetIds) { |
||||
var $$ = this.internal, |
||||
options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; |
||||
$$.transformTo(targetIds, type, options); |
||||
}; |
||||
|
||||
c3_chart_fn.groups = function (groups) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (isUndefined(groups)) { return config.data_groups; } |
||||
config.data_groups = groups; |
||||
$$.redraw(); |
||||
return config.data_groups; |
||||
}; |
||||
|
||||
c3_chart_fn.xgrids = function (grids) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (! grids) { return config.grid_x_lines; } |
||||
config.grid_x_lines = grids; |
||||
$$.redrawWithoutRescale(); |
||||
return config.grid_x_lines; |
||||
}; |
||||
c3_chart_fn.xgrids.add = function (grids) { |
||||
var $$ = this.internal; |
||||
return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])); |
||||
}; |
||||
c3_chart_fn.xgrids.remove = function (params) { // TODO: multiple
|
||||
var $$ = this.internal; |
||||
$$.removeGridLines(params, true); |
||||
}; |
||||
|
||||
c3_chart_fn.ygrids = function (grids) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (! grids) { return config.grid_y_lines; } |
||||
config.grid_y_lines = grids; |
||||
$$.redrawWithoutRescale(); |
||||
return config.grid_y_lines; |
||||
}; |
||||
c3_chart_fn.ygrids.add = function (grids) { |
||||
var $$ = this.internal; |
||||
return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])); |
||||
}; |
||||
c3_chart_fn.ygrids.remove = function (params) { // TODO: multiple
|
||||
var $$ = this.internal; |
||||
$$.removeGridLines(params, false); |
||||
}; |
||||
|
||||
c3_chart_fn.regions = function (regions) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (!regions) { return config.regions; } |
||||
config.regions = regions; |
||||
$$.redrawWithoutRescale(); |
||||
return config.regions; |
||||
}; |
||||
c3_chart_fn.regions.add = function (regions) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (!regions) { return config.regions; } |
||||
config.regions = config.regions.concat(regions); |
||||
$$.redrawWithoutRescale(); |
||||
return config.regions; |
||||
}; |
||||
c3_chart_fn.regions.remove = function (options) { |
||||
var $$ = this.internal, config = $$.config, |
||||
duration, classes, regions; |
||||
|
||||
options = options || {}; |
||||
duration = $$.getOption(options, "duration", config.transition_duration); |
||||
classes = $$.getOption(options, "classes", [CLASS.region]); |
||||
|
||||
regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) { return '.' + c; })); |
||||
(duration ? regions.transition().duration(duration) : regions) |
||||
.style('opacity', 0) |
||||
.remove(); |
||||
|
||||
config.regions = config.regions.filter(function (region) { |
||||
var found = false; |
||||
if (!region['class']) { |
||||
return true; |
||||
} |
||||
region['class'].split(' ').forEach(function (c) { |
||||
if (classes.indexOf(c) >= 0) { found = true; } |
||||
}); |
||||
return !found; |
||||
}); |
||||
|
||||
return config.regions; |
||||
}; |
||||
|
||||
c3_chart_fn.data = function (targetIds) { |
||||
var targets = this.internal.data.targets; |
||||
return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) { |
||||
return [].concat(targetIds).indexOf(t.id) >= 0; |
||||
}); |
||||
}; |
||||
c3_chart_fn.data.shown = function (targetIds) { |
||||
return this.internal.filterTargetsToShow(this.data(targetIds)); |
||||
}; |
||||
c3_chart_fn.data.values = function (targetId) { |
||||
var targets, values = null; |
||||
if (targetId) { |
||||
targets = this.data(targetId); |
||||
values = targets[0] ? targets[0].values.map(function (d) { return d.value; }) : null; |
||||
} |
||||
return values; |
||||
}; |
||||
c3_chart_fn.data.names = function (names) { |
||||
this.internal.clearLegendItemTextBoxCache(); |
||||
return this.internal.updateDataAttributes('names', names); |
||||
}; |
||||
c3_chart_fn.data.colors = function (colors) { |
||||
return this.internal.updateDataAttributes('colors', colors); |
||||
}; |
||||
c3_chart_fn.data.axes = function (axes) { |
||||
return this.internal.updateDataAttributes('axes', axes); |
||||
}; |
||||
|
||||
c3_chart_fn.category = function (i, category) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (arguments.length > 1) { |
||||
config.axis_x_categories[i] = category; |
||||
$$.redraw(); |
||||
} |
||||
return config.axis_x_categories[i]; |
||||
}; |
||||
c3_chart_fn.categories = function (categories) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (!arguments.length) { return config.axis_x_categories; } |
||||
config.axis_x_categories = categories; |
||||
$$.redraw(); |
||||
return config.axis_x_categories; |
||||
}; |
||||
|
||||
// TODO: fix
|
||||
c3_chart_fn.color = function (id) { |
||||
var $$ = this.internal; |
||||
return $$.color(id); // more patterns
|
||||
}; |
||||
|
||||
c3_chart_fn.x = function (x) { |
||||
var $$ = this.internal; |
||||
if (arguments.length) { |
||||
$$.updateTargetX($$.data.targets, x); |
||||
$$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true}); |
||||
} |
||||
return $$.data.xs; |
||||
}; |
||||
c3_chart_fn.xs = function (xs) { |
||||
var $$ = this.internal; |
||||
if (arguments.length) { |
||||
$$.updateTargetXs($$.data.targets, xs); |
||||
$$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true}); |
||||
} |
||||
return $$.data.xs; |
||||
}; |
||||
|
||||
c3_chart_fn.axis = function () {}; |
||||
c3_chart_fn.axis.labels = function (labels) { |
||||
var $$ = this.internal; |
||||
if (arguments.length) { |
||||
Object.keys(labels).forEach(function (axisId) { |
||||
$$.axis.setLabelText(axisId, labels[axisId]); |
||||
}); |
||||
$$.axis.updateLabels(); |
||||
} |
||||
// TODO: return some values?
|
||||
}; |
||||
c3_chart_fn.axis.max = function (max) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (arguments.length) { |
||||
if (typeof max === 'object') { |
||||
if (isValue(max.x)) { config.axis_x_max = max.x; } |
||||
if (isValue(max.y)) { config.axis_y_max = max.y; } |
||||
if (isValue(max.y2)) { config.axis_y2_max = max.y2; } |
||||
} else { |
||||
config.axis_y_max = config.axis_y2_max = max; |
||||
} |
||||
$$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true}); |
||||
} else { |
||||
return { |
||||
x: config.axis_x_max, |
||||
y: config.axis_y_max, |
||||
y2: config.axis_y2_max |
||||
}; |
||||
} |
||||
}; |
||||
c3_chart_fn.axis.min = function (min) { |
||||
var $$ = this.internal, config = $$.config; |
||||
if (arguments.length) { |
||||
if (typeof min === 'object') { |
||||
if (isValue(min.x)) { config.axis_x_min = min.x; } |
||||
if (isValue(min.y)) { config.axis_y_min = min.y; } |
||||
if (isValue(min.y2)) { config.axis_y2_min = min.y2; } |
||||
} else { |
||||
config.axis_y_min = config.axis_y2_min = min; |
||||
} |
||||
$$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true}); |
||||
} else { |
||||
return { |
||||
x: config.axis_x_min, |
||||
y: config.axis_y_min, |
||||
y2: config.axis_y2_min |
||||
}; |
||||
} |
||||
}; |
||||
c3_chart_fn.axis.range = function (range) { |
||||
if (arguments.length) { |
||||
if (isDefined(range.max)) { this.axis.max(range.max); } |
||||
if (isDefined(range.min)) { this.axis.min(range.min); } |
||||
} else { |
||||
return { |
||||
max: this.axis.max(), |
||||
min: this.axis.min() |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
c3_chart_fn.legend = function () {}; |
||||
c3_chart_fn.legend.show = function (targetIds) { |
||||
var $$ = this.internal; |
||||
$$.showLegend($$.mapToTargetIds(targetIds)); |
||||
$$.updateAndRedraw({withLegend: true}); |
||||
}; |
||||
c3_chart_fn.legend.hide = function (targetIds) { |
||||
var $$ = this.internal; |
||||
$$.hideLegend($$.mapToTargetIds(targetIds)); |
||||
$$.updateAndRedraw({withLegend: true}); |
||||
}; |
||||
|
||||
c3_chart_fn.resize = function (size) { |
||||
var $$ = this.internal, config = $$.config; |
||||
config.size_width = size ? size.width : null; |
||||
config.size_height = size ? size.height : null; |
||||
this.flush(); |
||||
}; |
||||
|
||||
c3_chart_fn.flush = function () { |
||||
var $$ = this.internal; |
||||
$$.updateAndRedraw({withLegend: true, withTransition: false, withTransitionForTransform: false}); |
||||
}; |
||||
|
||||
c3_chart_fn.destroy = function () { |
||||
var $$ = this.internal; |
||||
|
||||
window.clearInterval($$.intervalForObserveInserted); |
||||
|
||||
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(""); |
||||
|
||||
// MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen.
|
||||
Object.keys($$).forEach(function (key) { |
||||
$$[key] = null; |
||||
}); |
||||
|
||||
return null; |
||||
}; |
||||
|
||||
c3_chart_fn.tooltip = function () {}; |
||||
c3_chart_fn.tooltip.show = function (args) { |
||||
var $$ = this.internal, index, mouse; |
||||
|
||||
// determine mouse position on the chart
|
||||
if (args.mouse) { |
||||
mouse = args.mouse; |
||||
} |
||||
|
||||
// determine focus data
|
||||
if (args.data) { |
||||
if ($$.isMultipleX()) { |
||||
// if multiple xs, target point will be determined by mouse
|
||||
mouse = [$$.x(args.data.x), $$.getYScale(args.data.id)(args.data.value)]; |
||||
index = null; |
||||
} else { |
||||
// TODO: when tooltip_grouped = false
|
||||
index = isValue(args.data.index) ? args.data.index : $$.getIndexByX(args.data.x); |
||||
} |
||||
} |
||||
else if (typeof args.x !== 'undefined') { |
||||
index = $$.getIndexByX(args.x); |
||||
} |
||||
else if (typeof args.index !== 'undefined') { |
||||
index = args.index; |
||||
} |
||||
|
||||
// emulate mouse events to show
|
||||
$$.dispatchEvent('mouseover', index, mouse); |
||||
$$.dispatchEvent('mousemove', index, mouse); |
||||
|
||||
$$.config.tooltip_onshow.call($$, args.data); |
||||
}; |
||||
c3_chart_fn.tooltip.hide = function () { |
||||
// TODO: get target data by checking the state of focus
|
||||
this.internal.dispatchEvent('mouseout', 0); |
||||
|
||||
this.internal.config.tooltip_onhide.call(this); |
||||
}; |
||||
export {Chart}; |
||||
export default Chart; |
@ -0,0 +1,31 @@
|
||||
// This file is only the entry point for rollup
|
||||
import { |
||||
ChartInternal |
||||
} from './chartinternal.js'; |
||||
import { |
||||
Chart |
||||
} from './chart.js'; |
||||
import { |
||||
Axis |
||||
} from './axis.js'; |
||||
|
||||
|
||||
var version = "0.4.11", |
||||
generate = function(config) { |
||||
return new Chart(config); |
||||
}, |
||||
chart = { |
||||
fn: Chart.prototype, |
||||
internal: { |
||||
fn: ChartInternal.prototype, |
||||
axis: { |
||||
fn: Axis.prototype |
||||
} |
||||
} |
||||
}; |
||||
|
||||
export { |
||||
version, |
||||
generate, |
||||
chart |
||||
} |
@ -1,292 +0,0 @@
|
||||
c3_chart_fn.flow = function (args) { |
||||
var $$ = this.internal, |
||||
targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(), |
||||
dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to; |
||||
|
||||
if (args.json) { |
||||
data = $$.convertJsonToData(args.json, args.keys); |
||||
} |
||||
else if (args.rows) { |
||||
data = $$.convertRowsToData(args.rows); |
||||
} |
||||
else if (args.columns) { |
||||
data = $$.convertColumnsToData(args.columns); |
||||
} |
||||
else { |
||||
return; |
||||
} |
||||
targets = $$.convertDataToTargets(data, true); |
||||
|
||||
// Update/Add data
|
||||
$$.data.targets.forEach(function (t) { |
||||
var found = false, i, j; |
||||
for (i = 0; i < targets.length; i++) { |
||||
if (t.id === targets[i].id) { |
||||
found = true; |
||||
|
||||
if (t.values[t.values.length - 1]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
} |
||||
length = targets[i].values.length; |
||||
|
||||
for (j = 0; j < length; j++) { |
||||
targets[i].values[j].index = tail + j; |
||||
if (!$$.isTimeSeries()) { |
||||
targets[i].values[j].x = tail + j; |
||||
} |
||||
} |
||||
t.values = t.values.concat(targets[i].values); |
||||
|
||||
targets.splice(i, 1); |
||||
break; |
||||
} |
||||
} |
||||
if (!found) { notfoundIds.push(t.id); } |
||||
}); |
||||
|
||||
// Append null for not found targets
|
||||
$$.data.targets.forEach(function (t) { |
||||
var i, j; |
||||
for (i = 0; i < notfoundIds.length; i++) { |
||||
if (t.id === notfoundIds[i]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
for (j = 0; j < length; j++) { |
||||
t.values.push({ |
||||
id: t.id, |
||||
index: tail + j, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, |
||||
value: null |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// Generate null values for new target
|
||||
if ($$.data.targets.length) { |
||||
targets.forEach(function (t) { |
||||
var i, missing = []; |
||||
for (i = $$.data.targets[0].values[0].index; i < tail; i++) { |
||||
missing.push({ |
||||
id: t.id, |
||||
index: i, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, |
||||
value: null |
||||
}); |
||||
} |
||||
t.values.forEach(function (v) { |
||||
v.index += tail; |
||||
if (!$$.isTimeSeries()) { |
||||
v.x += tail; |
||||
} |
||||
}); |
||||
t.values = missing.concat(t.values); |
||||
}); |
||||
} |
||||
$$.data.targets = $$.data.targets.concat(targets); // add remained
|
||||
|
||||
// check data count because behavior needs to change when it's only one
|
||||
dataCount = $$.getMaxDataCount(); |
||||
baseTarget = $$.data.targets[0]; |
||||
baseValue = baseTarget.values[0]; |
||||
|
||||
// Update length to flow if needed
|
||||
if (isDefined(args.to)) { |
||||
length = 0; |
||||
to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; |
||||
baseTarget.values.forEach(function (v) { |
||||
if (v.x < to) { length++; } |
||||
}); |
||||
} else if (isDefined(args.length)) { |
||||
length = args.length; |
||||
} |
||||
|
||||
// If only one data, update the domain to flow from left edge of the chart
|
||||
if (!orgDataCount) { |
||||
if ($$.isTimeSeries()) { |
||||
if (baseTarget.values.length > 1) { |
||||
diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; |
||||
} else { |
||||
diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; |
||||
} |
||||
} else { |
||||
diff = 1; |
||||
} |
||||
domain = [baseValue.x - diff, baseValue.x]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} else if (orgDataCount === 1) { |
||||
if ($$.isTimeSeries()) { |
||||
diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; |
||||
domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} |
||||
} |
||||
|
||||
// Set targets
|
||||
$$.updateTargets($$.data.targets); |
||||
|
||||
// Redraw with new targets
|
||||
$$.redraw({ |
||||
flow: { |
||||
index: baseValue.index, |
||||
length: length, |
||||
duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, |
||||
done: args.done, |
||||
orgDataCount: orgDataCount, |
||||
}, |
||||
withLegend: true, |
||||
withTransition: orgDataCount > 1, |
||||
withTrimXDomain: false, |
||||
withUpdateXAxis: true, |
||||
}); |
||||
}; |
||||
|
||||
c3_chart_internal_fn.generateFlow = function (args) { |
||||
var $$ = this, config = $$.config, d3 = $$.d3; |
||||
|
||||
return function () { |
||||
var targets = args.targets, |
||||
flow = args.flow, |
||||
drawBar = args.drawBar, |
||||
drawLine = args.drawLine, |
||||
drawArea = args.drawArea, |
||||
cx = args.cx, |
||||
cy = args.cy, |
||||
xv = args.xv, |
||||
xForText = args.xForText, |
||||
yForText = args.yForText, |
||||
duration = args.duration; |
||||
|
||||
var translateX, scaleX = 1, transform, |
||||
flowIndex = flow.index, |
||||
flowLength = flow.length, |
||||
flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), |
||||
flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength), |
||||
orgDomain = $$.x.domain(), domain, |
||||
durationForFlow = flow.duration || duration, |
||||
done = flow.done || function () {}, |
||||
wait = $$.generateWait(); |
||||
|
||||
var xgrid = $$.xgrid || d3.selectAll([]), |
||||
xgridLines = $$.xgridLines || d3.selectAll([]), |
||||
mainRegion = $$.mainRegion || d3.selectAll([]), |
||||
mainText = $$.mainText || d3.selectAll([]), |
||||
mainBar = $$.mainBar || d3.selectAll([]), |
||||
mainLine = $$.mainLine || d3.selectAll([]), |
||||
mainArea = $$.mainArea || d3.selectAll([]), |
||||
mainCircle = $$.mainCircle || d3.selectAll([]); |
||||
|
||||
// set flag
|
||||
$$.flowing = true; |
||||
|
||||
// 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(targets, true, true); |
||||
// update elements related to x scale
|
||||
if ($$.updateXGrid) { $$.updateXGrid(true); } |
||||
|
||||
// generate transform to flow
|
||||
if (!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 (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && 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)'; |
||||
|
||||
$$.hideXGridFocus(); |
||||
|
||||
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", cx) |
||||
.attr("cy", cy); |
||||
mainText |
||||
.attr('transform', null) |
||||
.attr('x', xForText) |
||||
.attr('y', yForText) |
||||
.style('fill-opacity', $$.opacityForText.bind($$)); |
||||
mainRegion |
||||
.attr('transform', null); |
||||
mainRegion.select('rect').filter($$.isRegionOnX) |
||||
.attr("x", $$.regionX.bind($$)) |
||||
.attr("width", $$.regionWidth.bind($$)); |
||||
|
||||
if (config.interaction_enabled) { |
||||
$$.redrawEventRect(); |
||||
} |
||||
|
||||
// callback for end of flow
|
||||
done(); |
||||
|
||||
$$.flowing = false; |
||||
}); |
||||
}; |
||||
}; |
@ -1,16 +0,0 @@
|
||||
c3_chart_fn.transform = function (type, targetIds) { |
||||
var $$ = this.internal, |
||||
options = ['pie', 'donut'].indexOf(type) >= 0 ? {withTransform: true} : null; |
||||
$$.transformTo(targetIds, type, options); |
||||
}; |
||||
|
||||
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); |
||||
$$.updateTargets($$.data.targets); // this is needed when transforming to arc
|
||||
$$.updateAndRedraw(options); |
||||
}; |
@ -0,0 +1,18 @@
|
||||
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; |
||||
} |
@ -0,0 +1,143 @@
|
||||
c3_chart_fn.flow = function(args) { |
||||
var $$ = this.internal, |
||||
targets, data, notfoundIds = [], |
||||
orgDataCount = $$.getMaxDataCount(), |
||||
dataCount, domain, baseTarget, baseValue, length = 0, |
||||
tail = 0, |
||||
diff, to; |
||||
|
||||
if (args.json) { |
||||
data = $$.convertJsonToData(args.json, args.keys); |
||||
} else if (args.rows) { |
||||
data = $$.convertRowsToData(args.rows); |
||||
} else if (args.columns) { |
||||
data = $$.convertColumnsToData(args.columns); |
||||
} else { |
||||
return; |
||||
} |
||||
targets = $$.convertDataToTargets(data, true); |
||||
|
||||
// Update/Add data
|
||||
$$.data.targets.forEach(function(t) { |
||||
var found = false, |
||||
i, j; |
||||
for (i = 0; i < targets.length; i++) { |
||||
if (t.id === targets[i].id) { |
||||
found = true; |
||||
|
||||
if (t.values[t.values.length - 1]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
} |
||||
length = targets[i].values.length; |
||||
|
||||
for (j = 0; j < length; j++) { |
||||
targets[i].values[j].index = tail + j; |
||||
if (!$$.isTimeSeries()) { |
||||
targets[i].values[j].x = tail + j; |
||||
} |
||||
} |
||||
t.values = t.values.concat(targets[i].values); |
||||
|
||||
targets.splice(i, 1); |
||||
break; |
||||
} |
||||
} |
||||
if (!found) { notfoundIds.push(t.id); } |
||||
}); |
||||
|
||||
// Append null for not found targets
|
||||
$$.data.targets.forEach(function(t) { |
||||
var i, j; |
||||
for (i = 0; i < notfoundIds.length; i++) { |
||||
if (t.id === notfoundIds[i]) { |
||||
tail = t.values[t.values.length - 1].index + 1; |
||||
for (j = 0; j < length; j++) { |
||||
t.values.push({ |
||||
id: t.id, |
||||
index: tail + j, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, |
||||
value: null |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// Generate null values for new target
|
||||
if ($$.data.targets.length) { |
||||
targets.forEach(function(t) { |
||||
var i, missing = []; |
||||
for (i = $$.data.targets[0].values[0].index; i < tail; i++) { |
||||
missing.push({ |
||||
id: t.id, |
||||
index: i, |
||||
x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, |
||||
value: null |
||||
}); |
||||
} |
||||
t.values.forEach(function(v) { |
||||
v.index += tail; |
||||
if (!$$.isTimeSeries()) { |
||||
v.x += tail; |
||||
} |
||||
}); |
||||
t.values = missing.concat(t.values); |
||||
}); |
||||
} |
||||
$$.data.targets = $$.data.targets.concat(targets); // add remained
|
||||
|
||||
// check data count because behavior needs to change when it's only one
|
||||
dataCount = $$.getMaxDataCount(); |
||||
baseTarget = $$.data.targets[0]; |
||||
baseValue = baseTarget.values[0]; |
||||
|
||||
// Update length to flow if needed
|
||||
if (isDefined(args.to)) { |
||||
length = 0; |
||||
to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; |
||||
baseTarget.values.forEach(function(v) { |
||||
if (v.x < to) { length++; } |
||||
}); |
||||
} else if (isDefined(args.length)) { |
||||
length = args.length; |
||||
} |
||||
|
||||
// If only one data, update the domain to flow from left edge of the chart
|
||||
if (!orgDataCount) { |
||||
if ($$.isTimeSeries()) { |
||||
if (baseTarget.values.length > 1) { |
||||
diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; |
||||
} else { |
||||
diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; |
||||
} |
||||
} else { |
||||
diff = 1; |
||||
} |
||||
domain = [baseValue.x - diff, baseValue.x]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} else if (orgDataCount === 1) { |
||||
if ($$.isTimeSeries()) { |
||||
diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; |
||||
domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; |
||||
$$.updateXDomain(null, true, true, false, domain); |
||||
} |
||||
} |
||||
|
||||
// Set targets
|
||||
$$.updateTargets($$.data.targets); |
||||
|
||||
// Redraw with new targets
|
||||
$$.redraw({ |
||||
flow: { |
||||
index: baseValue.index, |
||||
length: length, |
||||
duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, |
||||
done: args.done, |
||||
orgDataCount: orgDataCount, |
||||
}, |
||||
withLegend: true, |
||||
withTransition: orgDataCount > 1, |
||||
withTrimXDomain: false, |
||||
withUpdateXAxis: true, |
||||
}); |
||||
}; |
@ -0,0 +1,5 @@
|
||||
c3_chart_fn.transform = function(type, targetIds) { |
||||
var $$ = this.internal, |
||||
options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; |
||||
$$.transformTo(targetIds, type, options); |
||||
}; |
@ -0,0 +1,23 @@
|
||||
var c3_chart_fn; |
||||
|
||||
|
||||
function Chart(config) { |
||||
var $$ = this.internal = new ChartInternal(this); |
||||
$$.loadConfig(config); |
||||
|
||||
$$.beforeInit(config); |
||||
$$.init(); |
||||
$$.afterInit(config); |
||||
|
||||
// bind "this" to nested API
|
||||
(function bindThis(fn, target, argThis) { |
||||
Object.keys(fn).forEach(function(key) { |
||||
target[key] = fn[key].bind(argThis); |
||||
if (Object.keys(fn[key]).length > 0) { |
||||
bindThis(fn[key], target[key], argThis); |
||||
} |
||||
}); |
||||
})(c3_chart_fn, this, this); |
||||
} |
||||
|
||||
c3_chart_fn = Chart.prototype; |
@ -0,0 +1,155 @@
|
||||
c3_chart_internal_fn.generateFlow = function(args) { |
||||
var $$ = this, |
||||
config = $$.config, |
||||
d3 = $$.d3; |
||||
|
||||
return function() { |
||||
var targets = args.targets, |
||||
flow = args.flow, |
||||
drawBar = args.drawBar, |
||||
drawLine = args.drawLine, |
||||
drawArea = args.drawArea, |
||||
cx = args.cx, |
||||
cy = args.cy, |
||||
xv = args.xv, |
||||
xForText = args.xForText, |
||||
yForText = args.yForText, |
||||
duration = args.duration; |
||||
|
||||
var translateX, scaleX = 1, |
||||
transform, |
||||
flowIndex = flow.index, |
||||
flowLength = flow.length, |
||||
flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), |
||||
flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength), |
||||
orgDomain = $$.x.domain(), |
||||
domain, |
||||
durationForFlow = flow.duration || duration, |
||||
done = flow.done || function() {}, |
||||
wait = $$.generateWait(); |
||||
|
||||
var xgrid = $$.xgrid || d3.selectAll([]), |
||||
xgridLines = $$.xgridLines || d3.selectAll([]), |
||||
mainRegion = $$.mainRegion || d3.selectAll([]), |
||||
mainText = $$.mainText || d3.selectAll([]), |
||||
mainBar = $$.mainBar || d3.selectAll([]), |
||||
mainLine = $$.mainLine || d3.selectAll([]), |
||||
mainArea = $$.mainArea || d3.selectAll([]), |
||||
mainCircle = $$.mainCircle || d3.selectAll([]); |
||||
|
||||
// set flag
|
||||
$$.flowing = true; |
||||
|
||||
// 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(targets, true, true); |
||||
// update elements related to x scale
|
||||
if ($$.updateXGrid) { $$.updateXGrid(true); } |
||||
|
||||
// generate transform to flow
|
||||
if (!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 (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && 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)'; |
||||
|
||||
$$.hideXGridFocus(); |
||||
|
||||
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", cx) |
||||
.attr("cy", cy); |
||||
mainText |
||||
.attr('transform', null) |
||||
.attr('x', xForText) |
||||
.attr('y', yForText) |
||||
.style('fill-opacity', $$.opacityForText.bind($$)); |
||||
mainRegion |
||||
.attr('transform', null); |
||||
mainRegion.select('rect').filter($$.isRegionOnX) |
||||
.attr("x", $$.regionX.bind($$)) |
||||
.attr("width", $$.regionWidth.bind($$)); |
||||
|
||||
if (config.interaction_enabled) { |
||||
$$.redrawEventRect(); |
||||
} |
||||
|
||||
// callback for end of flow
|
||||
done(); |
||||
|
||||
$$.flowing = false; |
||||
}); |
||||
}; |
||||
}; |
@ -0,0 +1,10 @@
|
||||
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); |
||||
$$.updateTargets($$.data.targets); // this is needed when transforming to arc
|
||||
$$.updateAndRedraw(options); |
||||
}; |
@ -1,4 +1,8 @@
|
||||
(function (window) { |
||||
'use strict'; |
||||
|
||||
/*global define, module, exports, require */ |
||||
/*global define, module, exports, require */ |
||||
(function (global, factory) { |
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('d3')) : |
||||
typeof define === 'function' && define.amd ? define(['d3'],factory) : |
||||
(global.c3 = factory(global.d3)); |
||||
}(this, function (d3) {
|
||||
'use strict'; |
||||
var c3 = { version: "0.4.11-rc4" }; |
||||
|
@ -1,9 +1,17 @@
|
||||
if (typeof define === 'function' && define.amd) { |
||||
define("c3", ["d3"], function () { return c3; }); |
||||
} else if ('undefined' !== typeof exports && 'undefined' !== typeof module) { |
||||
module.exports = c3; |
||||
} else { |
||||
window.c3 = c3; |
||||
} |
||||
c3.generate = function(config) { |
||||
return new Chart(config); |
||||
}; |
||||
|
||||
})(window); |
||||
c3.chart = { |
||||
fn: Chart.prototype, |
||||
internal: { |
||||
fn: ChartInternal.prototype, |
||||
axis: { |
||||
fn: Axis.prototype |
||||
} |
||||
} |
||||
}; |
||||
|
||||
return c3; |
||||
|
||||
})); |
Loading…
Reference in new issue