( function ( window ) {
'use strict' ;
/*global define, module, exports, require */
var c3 = {
version : "0.1.42"
} ;
var CLASS = {
target : 'c3-target' ,
chart : 'c3-chart' ,
chartLine : 'c3-chart-line' ,
chartLines : 'c3-chart-lines' ,
chartBar : 'c3-chart-bar' ,
chartBars : 'c3-chart-bars' ,
chartText : 'c3-chart-text' ,
chartTexts : 'c3-chart-texts' ,
chartArc : 'c3-chart-arc' ,
chartArcs : 'c3-chart-arcs' ,
chartArcsTitle : 'c3-chart-arcs-title' ,
gaugeArc : 'c3-gauge-arc' ,
gaugeArcs : 'c3-gauge-arcs' ,
gaugeArcsTitle : 'c3-gauge-arcs-title' ,
selectedCircle : 'c3-selected-circle' ,
selectedCircles : 'c3-selected-circles' ,
eventRect : 'c3-event-rect' ,
eventRects : 'c3-event-rects' ,
eventRectsSingle : 'c3-event-rects-single' ,
eventRectsMultiple : 'c3-event-rects-multiple' ,
zoomRect : 'c3-zoom-rect' ,
brush : 'c3-brush' ,
focused : 'c3-focused' ,
region : 'c3-region' ,
regions : 'c3-regions' ,
tooltip : 'c3-tooltip' ,
tooltipName : 'c3-tooltip-name' ,
shape : 'c3-shape' ,
shapes : 'c3-shapes' ,
line : 'c3-line' ,
lines : 'c3-lines' ,
step : 'c3-step' ,
steps : 'c3-steps' ,
bar : 'c3-bar' ,
bars : 'c3-bars' ,
circle : 'c3-circle' ,
circles : 'c3-circles' ,
arc : 'c3-arc' ,
arcs : 'c3-arcs' ,
area : 'c3-area' ,
areas : 'c3-areas' ,
text : 'c3-text' ,
texts : 'c3-texts' ,
grid : 'c3-grid' ,
xgrid : 'c3-xgrid' ,
xgrids : 'c3-xgrids' ,
xgridLine : 'c3-xgrid-line' ,
xgridLines : 'c3-xgrid-lines' ,
xgridFocus : 'c3-xgrid-focus' ,
ygrid : 'c3-ygrid' ,
ygrids : 'c3-ygrids' ,
ygridLine : 'c3-ygrid-line' ,
ygridLines : 'c3-ygrid-lines' ,
axisX : 'c3-axis-x' ,
axisXLabel : 'c3-axis-x-label' ,
axisY : 'c3-axis-y' ,
axisYLabel : 'c3-axis-y-label' ,
axisY2 : 'c3-axis-y2' ,
axisY2Label : 'c3-axis-y2-label' ,
legendItem : 'c3-legend-item' ,
legendItemEvent : 'c3-legend-item-event' ,
legendItemTile : 'c3-legend-item-tile' ,
legendItemHidden : 'c3-legend-item-hidden' ,
legendItemFocused : 'c3-legend-item-focused' ,
dragarea : 'c3-dragarea' ,
EXPANDED : '_expanded_' ,
SELECTED : '_selected_' ,
INCLUDED : '_included_' ,
} ;
/ *
* Generate chart according to config
* /
c3 . generate = function ( config ) {
var d3 = window . d3 ? window . d3 : 'undefined' !== typeof require ? require ( "d3" ) : undefined ;
var c3 = { data : { } , axis : { } , legend : { } } ,
cache = { } ;
/*-- Handle Config --*/
function checkConfig ( key , message ) {
if ( ! ( key in config ) ) { throw Error ( message ) ; }
}
function getConfig ( keys , defaultValue ) {
var target = config , i , isLast , nextTarget ;
for ( i = 0 ; i < keys . length ; i ++ ) {
// return default if key not found
if ( typeof target === 'object' && ! ( keys [ i ] in target ) ) { return defaultValue ; }
// Check next key's value
isLast = ( i === keys . length - 1 ) ;
nextTarget = target [ keys [ i ] ] ;
if ( ! isLast && typeof nextTarget !== 'object' ) {
return defaultValue ;
}
target = nextTarget ;
}
return target ;
}
// bindto - id to bind the chart
var _ _bindto = getConfig ( [ 'bindto' ] , '#chart' ) ;
var _ _size _width = getConfig ( [ 'size' , 'width' ] ) ,
_ _size _height = getConfig ( [ 'size' , 'height' ] ) ;
var _ _padding _left = getConfig ( [ 'padding' , 'left' ] ) ,
_ _padding _right = getConfig ( [ 'padding' , 'right' ] ) ,
_ _padding _top = getConfig ( [ 'padding' , 'top' ] ) ,
_ _padding _bottom = getConfig ( [ 'padding' , 'bottom' ] ) ;
var _ _zoom _enabled = getConfig ( [ 'zoom' , 'enabled' ] , false ) ,
_ _zoom _extent = getConfig ( [ 'zoom' , 'extent' ] ) ,
_ _zoom _privileged = getConfig ( [ 'zoom' , 'privileged' ] , false ) ;
var _ _interaction _enabled = getConfig ( [ 'interaction' , 'enabled' ] , true ) ;
var _ _onenter = getConfig ( [ 'onenter' ] , function ( ) { } ) ,
_ _onleave = getConfig ( [ 'onleave' ] , function ( ) { } ) ,
_ _onresize = getConfig ( [ 'onresize' ] , function ( ) { } ) ,
_ _onresized = getConfig ( [ 'onresized' ] , function ( ) { } ) ;
var _ _transition _duration = getConfig ( [ 'transition' , 'duration' ] , 350 ) ;
// data - data configuration
checkConfig ( 'data' , 'data is required in config' ) ;
var _ _data _x = getConfig ( [ 'data' , 'x' ] ) ,
_ _data _xs = getConfig ( [ 'data' , 'xs' ] , { } ) ,
_ _data _x _format = getConfig ( [ 'data' , 'x_format' ] ) ,
_ _data _id _converter = getConfig ( [ 'data' , 'id_converter' ] , function ( id ) { return id ; } ) ,
_ _data _names = getConfig ( [ 'data' , 'names' ] , { } ) ,
_ _data _classes = getConfig ( [ 'data' , 'classes' ] , { } ) ,
_ _data _groups = getConfig ( [ 'data' , 'groups' ] , [ ] ) ,
_ _data _axes = getConfig ( [ 'data' , 'axes' ] , { } ) ,
_ _data _type = getConfig ( [ 'data' , 'type' ] ) ,
_ _data _types = getConfig ( [ 'data' , 'types' ] , { } ) ,
_ _data _labels = getConfig ( [ 'data' , 'labels' ] , { } ) ,
_ _data _order = getConfig ( [ 'data' , 'order' ] ) ,
_ _data _regions = getConfig ( [ 'data' , 'regions' ] , { } ) ,
_ _data _color = getConfig ( [ 'data' , 'color' ] ) ,
_ _data _colors = getConfig ( [ 'data' , 'colors' ] , { } ) ,
_ _data _hide = getConfig ( [ 'data' , 'hide' ] , false ) ,
_ _data _filter = getConfig ( [ 'data' , 'filter' ] ) ,
_ _data _selection _enabled = getConfig ( [ 'data' , 'selection' , 'enabled' ] , false ) ,
_ _data _selection _grouped = getConfig ( [ 'data' , 'selection' , 'grouped' ] , false ) ,
_ _data _selection _isselectable = getConfig ( [ 'data' , 'selection' , 'isselectable' ] , function ( ) { return true ; } ) ,
_ _data _selection _multiple = getConfig ( [ 'data' , 'selection' , 'multiple' ] , true ) ,
_ _data _onclick = getConfig ( [ 'data' , 'onclick' ] , function ( ) { } ) ,
_ _data _onenter = getConfig ( [ 'data' , 'onenter' ] , function ( ) { } ) ,
_ _data _onleave = getConfig ( [ 'data' , 'onleave' ] , function ( ) { } ) ,
_ _data _onselected = getConfig ( [ 'data' , 'onselected' ] , function ( ) { } ) ,
_ _data _onunselected = getConfig ( [ 'data' , 'onunselected' ] , function ( ) { } ) ,
_ _data _ondragstart = getConfig ( [ 'data' , 'ondragstart' ] , function ( ) { } ) ,
_ _data _ondragend = getConfig ( [ 'data' , 'ondragend' ] , function ( ) { } ) ;
// configuration for no plot-able data supplied.
var _ _data _empty _abort = getConfig ( [ 'data' , 'empty' , 'abort' ] , true ) ,
_ _data _empty _label _text = getConfig ( [ 'data' , 'empty' , 'label' , 'text' ] , "" ) ,
_ _data _empty _label _size = getConfig ( [ 'data' , 'empty' , 'label' , 'size' ] , false ) ,
_ _data _empty _label _fill = getConfig ( [ 'data' , 'empty' , 'label' , 'fill' ] , false ) ;
// subchart
var _ _subchart _show = getConfig ( [ 'subchart' , 'show' ] , false ) ,
_ _subchart _size _height = getConfig ( [ 'subchart' , 'size' , 'height' ] , 60 ) ;
// color
var _ _color _pattern = getConfig ( [ 'color' , 'pattern' ] , [ ] ) ,
_ _color _opacity = getConfig ( [ 'color' , 'opacity' ] , null ) ,
_ _color _values = getConfig ( [ 'color' , 'values' ] , [ ] ) ;
// legend
var _ _legend _show = getConfig ( [ 'legend' , 'show' ] , true ) ,
_ _legend _position = getConfig ( [ 'legend' , 'position' ] , 'bottom' ) ,
_ _legend _item _onclick = getConfig ( [ 'legend' , 'item' , 'onclick' ] ) ,
_ _legend _item _onmouseover = getConfig ( [ 'legend' , 'item' , 'onmouseover' ] ) ,
_ _legend _item _onmouseout = getConfig ( [ 'legend' , 'item' , 'onmouseout' ] ) ,
_ _legend _equally = getConfig ( [ 'legend' , 'equally' ] , false ) ;
// axis
var _ _axis _rotated = getConfig ( [ 'axis' , 'rotated' ] , false ) ,
_ _axis _x _show = getConfig ( [ 'axis' , 'x' , 'show' ] , true ) ,
_ _axis _x _type = getConfig ( [ 'axis' , 'x' , 'type' ] , 'indexed' ) ,
_ _axis _x _localtime = getConfig ( [ 'axis' , 'x' , 'localtime' ] , true ) ,
_ _axis _x _categories = getConfig ( [ 'axis' , 'x' , 'categories' ] , [ ] ) ,
_ _axis _x _tick _centered = getConfig ( [ 'axis' , 'x' , 'tick' , 'centered' ] , false ) ,
_ _axis _x _tick _format = getConfig ( [ 'axis' , 'x' , 'tick' , 'format' ] ) ,
_ _axis _x _tick _culling = getConfig ( [ 'axis' , 'x' , 'tick' , 'culling' ] , { } ) ,
_ _axis _x _tick _culling _max = getConfig ( [ 'axis' , 'x' , 'tick' , 'culling' , 'max' ] , 10 ) ,
_ _axis _x _tick _count = getConfig ( [ 'axis' , 'x' , 'tick' , 'count' ] ) ,
_ _axis _x _tick _fit = getConfig ( [ 'axis' , 'x' , 'tick' , 'fit' ] , true ) ,
_ _axis _x _tick _values = getConfig ( [ 'axis' , 'x' , 'tick' , 'values' ] , null ) ,
_ _axis _x _tick _rotate = getConfig ( [ 'axis' , 'x' , 'tick' , 'rotate' ] ) ,
_ _axis _x _max = getConfig ( [ 'axis' , 'x' , 'max' ] , null ) ,
_ _axis _x _min = getConfig ( [ 'axis' , 'x' , 'min' ] , null ) ,
_ _axis _x _padding = getConfig ( [ 'axis' , 'x' , 'padding' ] , { } ) ,
_ _axis _x _height = getConfig ( [ 'axis' , 'x' , 'height' ] ) ,
_ _axis _x _default = getConfig ( [ 'axis' , 'x' , 'default' ] ) ,
_ _axis _x _label = getConfig ( [ 'axis' , 'x' , 'label' ] , { } ) ,
_ _axis _y _show = getConfig ( [ 'axis' , 'y' , 'show' ] , true ) ,
_ _axis _y _max = getConfig ( [ 'axis' , 'y' , 'max' ] ) ,
_ _axis _y _min = getConfig ( [ 'axis' , 'y' , 'min' ] ) ,
_ _axis _y _center = getConfig ( [ 'axis' , 'y' , 'center' ] ) ,
_ _axis _y _label = getConfig ( [ 'axis' , 'y' , 'label' ] , { } ) ,
_ _axis _y _inner = getConfig ( [ 'axis' , 'y' , 'inner' ] , false ) ,
_ _axis _y _tick _format = getConfig ( [ 'axis' , 'y' , 'tick' , 'format' ] ) ,
_ _axis _y _padding = getConfig ( [ 'axis' , 'y' , 'padding' ] ) ,
_ _axis _y _ticks = getConfig ( [ 'axis' , 'y' , 'ticks' ] , 10 ) ,
_ _axis _y2 _show = getConfig ( [ 'axis' , 'y2' , 'show' ] , false ) ,
_ _axis _y2 _max = getConfig ( [ 'axis' , 'y2' , 'max' ] ) ,
_ _axis _y2 _min = getConfig ( [ 'axis' , 'y2' , 'min' ] ) ,
_ _axis _y2 _center = getConfig ( [ 'axis' , 'y2' , 'center' ] ) ,
_ _axis _y2 _label = getConfig ( [ 'axis' , 'y2' , 'label' ] , { } ) ,
_ _axis _y2 _inner = getConfig ( [ 'axis' , 'y2' , 'inner' ] , false ) ,
_ _axis _y2 _tick _format = getConfig ( [ 'axis' , 'y2' , 'tick' , 'format' ] ) ,
_ _axis _y2 _padding = getConfig ( [ 'axis' , 'y2' , 'padding' ] ) ,
_ _axis _y2 _ticks = getConfig ( [ 'axis' , 'y2' , 'ticks' ] , 10 ) ;
// grid
var _ _grid _x _show = getConfig ( [ 'grid' , 'x' , 'show' ] , false ) ,
_ _grid _x _type = getConfig ( [ 'grid' , 'x' , 'type' ] , 'tick' ) ,
_ _grid _x _lines = getConfig ( [ 'grid' , 'x' , 'lines' ] , [ ] ) ,
_ _grid _y _show = getConfig ( [ 'grid' , 'y' , 'show' ] , false ) ,
// not used
// __grid_y_type = getConfig(['grid', 'y', 'type'], 'tick'),
_ _grid _y _lines = getConfig ( [ 'grid' , 'y' , 'lines' ] , [ ] ) ,
_ _grid _y _ticks = getConfig ( [ 'grid' , 'y' , 'ticks' ] , 10 ) ;
// point - point of each data
var _ _point _show = getConfig ( [ 'point' , 'show' ] , true ) ,
_ _point _r = getConfig ( [ 'point' , 'r' ] , 2.5 ) ,
_ _point _focus _line _enabled = getConfig ( [ 'point' , 'focus' , 'line' , 'enabled' ] , true ) ,
_ _point _focus _expand _enabled = getConfig ( [ 'point' , 'focus' , 'expand' , 'enabled' ] , true ) ,
_ _point _focus _expand _r = getConfig ( [ 'point' , 'focus' , 'expand' , 'r' ] ) ,
_ _point _select _r = getConfig ( [ 'point' , 'focus' , 'select' , 'r' ] ) ;
var _ _line _connect _null = getConfig ( [ 'line' , 'connect_null' ] , false ) ;
// bar
var _ _bar _width = getConfig ( [ 'bar' , 'width' ] ) ,
_ _bar _width _ratio = getConfig ( [ 'bar' , 'width' , 'ratio' ] , 0.6 ) ;
// pie
var _ _pie _label _show = getConfig ( [ 'pie' , 'label' , 'show' ] , true ) ,
_ _pie _label _format = getConfig ( [ 'pie' , 'label' , 'format' ] ) ,
_ _pie _expand = getConfig ( [ 'pie' , 'expand' ] , true ) ,
_ _pie _onclick = getConfig ( [ 'pie' , 'onclick' ] , function ( ) { } ) ,
_ _pie _onmouseover = getConfig ( [ 'pie' , 'onmouseover' ] , function ( ) { } ) ,
_ _pie _onmouseout = getConfig ( [ 'pie' , 'onmouseout' ] , function ( ) { } ) ;
// gauge
var _ _gauge _color = getConfig ( [ 'gauge' , 'color' ] , "#e0e0e0" ) ,
_ _gauge _label _show = getConfig ( [ 'gauge' , 'label' , 'show' ] , true ) ,
_ _gauge _label _format = getConfig ( [ 'gauge' , 'label' , 'format' ] ) ,
_ _gauge _max = getConfig ( [ 'gauge' , 'max' ] ) ,
_ _gauge _min = getConfig ( [ 'gauge' , 'min' ] ) ,
_ _gauge _onclick = getConfig ( [ 'gauge' , 'onclick' ] , function ( ) { } ) ,
_ _gauge _onmouseover = getConfig ( [ 'gauge' , 'onmouseover' ] , function ( ) { } ) ,
_ _gauge _onmouseout = getConfig ( [ 'gauge' , 'onmouseout' ] , function ( ) { } ) ,
_ _gauge _style = getConfig ( [ 'gauge' , 'style' ] ) ,
_ _gauge _units = getConfig ( [ 'gauge' , 'units' ] ) ,
_ _gauge _width = getConfig ( [ 'gauge' , 'width' ] , false ) ;
// donut
var _ _donut _label _show = getConfig ( [ 'donut' , 'label' , 'show' ] , true ) ,
_ _donut _label _format = getConfig ( [ 'donut' , 'label' , 'format' ] ) ,
_ _donut _expand = getConfig ( [ 'donut' , 'expand' ] , true ) ,
_ _donut _title = getConfig ( [ 'donut' , 'title' ] , "" ) ,
_ _donut _onclick = getConfig ( [ 'donut' , 'onclick' ] , function ( ) { } ) ,
_ _donut _onmouseover = getConfig ( [ 'donut' , 'onmouseover' ] , function ( ) { } ) ,
_ _donut _onmouseout = getConfig ( [ 'donut' , 'onmouseout' ] , function ( ) { } ) ;
// region - region to change style
var _ _regions = getConfig ( [ 'regions' ] , [ ] ) ;
// tooltip - show when mouseover on each data
var _ _tooltip _show = getConfig ( [ 'tooltip' , 'show' ] , true ) ,
_ _tooltip _format _title = getConfig ( [ 'tooltip' , 'format' , 'title' ] ) ,
_ _tooltip _format _value = getConfig ( [ 'tooltip' , 'format' , 'value' ] ) ,
_ _tooltip _contents = getConfig ( [ 'tooltip' , 'contents' ] , function ( d , defaultTitleFormat , defaultValueFormat , color ) {
var titleFormat = _ _tooltip _format _title ? _ _tooltip _format _title : defaultTitleFormat ,
valueFormat = _ _tooltip _format _value ? _ _tooltip _format _value : defaultValueFormat ,
text , i , title , value , name , bgcolor ;
for ( i = 0 ; i < d . length ; i ++ ) {
if ( ! ( d [ i ] && ( d [ i ] . value || d [ i ] . value === 0 ) ) ) { continue ; }
if ( ! text ) {
title = titleFormat ? titleFormat ( d [ i ] . x ) : d [ i ] . x ;
text = "<table class='" + CLASS . tooltip + "'>" + ( title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "" ) ;
}
name = d [ i ] . name ;
value = valueFormat ( d [ i ] . value , d [ i ] . ratio , d [ i ] . id , d [ i ] . index ) ;
bgcolor = ( _ _gauge _style === 'arc' && _ _color _values ) ? levelColor ( d [ i ] . value ) : color ( d [ i ] . id ) ;
text += "<tr class='" + CLASS . tooltipName + "-" + d [ i ] . id + "'>" ;
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>" ;
text += "<td class='value'>" + value + "</td>" ;
text += "</tr>" ;
}
return text + "</table>" ;
} ) ,
_ _tooltip _init _show = getConfig ( [ 'tooltip' , 'init' , 'show' ] , false ) ,
_ _tooltip _init _x = getConfig ( [ 'tooltip' , 'init' , 'x' ] , 0 ) ,
_ _tooltip _init _position = getConfig ( [ 'tooltip' , 'init' , 'position' ] , { top : '0px' , left : '50px' } ) ;
/*-- Set Variables --*/
// MEMO: clipId needs to be unique because it conflicts when multiple charts exist
var clipId = "c3-" + ( + new Date ( ) ) + '-clip' ,
clipIdForXAxis = clipId + '-xaxis' ,
clipIdForYAxis = clipId + '-yaxis' ,
clipPath = getClipPath ( clipId ) ,
clipPathForXAxis = getClipPath ( clipIdForXAxis ) ,
clipPathForYAxis = getClipPath ( clipIdForYAxis ) ;
var isTimeSeries = ( _ _axis _x _type === 'timeseries' ) ,
isCategorized = ( _ _axis _x _type === 'categorized' ) ,
isCustomX = ! isTimeSeries && ( _ _data _x || notEmpty ( _ _data _xs ) ) ;
var dragStart = null , dragging = false , cancelClick = false , mouseover = false , transiting = false ;
var defaultColorPattern = d3 . scale . category10 ( ) . range ( ) ,
color = generateColor ( _ _data _colors , notEmpty ( _ _color _pattern ) ? _ _color _pattern : defaultColorPattern , _ _data _color ) ,
levelColor = generateLevelColor ( _ _color _pattern , _ _color _values ) ;
var timeFormat = _ _axis _x _localtime ? d3 . time . format : d3 . time . format . utc ,
defaultTimeFormat = timeFormat . 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 ; } ]
] ) ;
var hiddenTargetIds = [ ] , hiddenLegendIds = [ ] ;
/*-- Set Chart Params --*/
var margin , margin2 , margin3 , width , width2 , height , height2 , currentWidth , currentHeight ;
var radius , radiusExpanded , innerRadius , arcWidth , arcHeight , svgArc , svgArcExpanded , svgArcExpandedSub , pie ;
var xMin , xMax , yMin , yMax , subXMin , subXMax , subYMin , subYMax ;
var x , y , y2 , subX , subY , subY2 , xAxis , yAxis , y2Axis , subXAxis ;
var axes = { } ;
var xOrient = _ _axis _rotated ? "left" : "bottom" ,
yOrient = _ _axis _rotated ? ( _ _axis _y _inner ? "top" : "bottom" ) : ( _ _axis _y _inner ? "right" : "left" ) ,
y2Orient = _ _axis _rotated ? ( _ _axis _y2 _inner ? "bottom" : "top" ) : ( _ _axis _y2 _inner ? "left" : "right" ) ,
subXOrient = _ _axis _rotated ? "left" : "bottom" ;
var translate = {
main : function ( ) { return "translate(" + asHalfPixel ( margin . left ) + "," + asHalfPixel ( margin . top ) + ")" ; } ,
context : function ( ) { return "translate(" + asHalfPixel ( margin2 . left ) + "," + asHalfPixel ( margin2 . top ) + ")" ; } ,
legend : function ( ) { return "translate(" + margin3 . left + "," + margin3 . top + ")" ; } ,
x : function ( ) { return "translate(0," + ( _ _axis _rotated ? 0 : height ) + ")" ; } ,
y : function ( ) { return "translate(0," + ( _ _axis _rotated ? height : 0 ) + ")" ; } ,
y2 : function ( ) { return "translate(" + ( _ _axis _rotated ? 0 : width ) + "," + ( _ _axis _rotated ? 1 : 0 ) + ")" ; } ,
subx : function ( ) { return "translate(0," + ( _ _axis _rotated ? 0 : height2 ) + ")" ; } ,
arc : function ( ) { return "translate(" + ( arcWidth / 2 ) + "," + ( arcHeight / 2 ) + ")" ; }
} ;
var isLegendRight = _ _legend _position === 'right' ;
var legendStep = 0 , legendItemWidth = 0 , legendItemHeight = 0 , legendOpacityForHidden = 0.15 ;
/*-- Define Functions --*/
function getClipPath ( id ) {
var isIE9 = window . navigator . appVersion . toLowerCase ( ) . indexOf ( "msie 9." ) >= 0 ;
return "url(" + ( isIE9 ? "" : document . URL . split ( '#' ) [ 0 ] ) + "#" + id + ")" ;
}
function asHalfPixel ( n ) {
return Math . ceil ( n ) + 0.5 ;
}
function transformMain ( withTransition , transitions ) {
var 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 ( ) ; }
}
main . attr ( "transform" , translate . main ) ;
xAxis . attr ( "transform" , translate . x ) ;
yAxis . attr ( "transform" , translate . y ) ;
y2Axis . attr ( "transform" , translate . y2 ) ;
main . select ( '.' + CLASS . chartArcs ) . attr ( "transform" , translate . arc ) ;
}
function transformContext ( withTransition , transitions ) {
var subXAxis ;
if ( transitions && transitions . axisSubX ) {
subXAxis = transitions . axisSubX ;
} else {
subXAxis = context . select ( '.' + CLASS . axisX ) ;
if ( withTransition ) { subXAxis = subXAxis . transition ( ) ; }
}
context . attr ( "transform" , translate . context ) ;
subXAxis . attr ( "transform" , translate . subx ) ;
}
function transformLegend ( withTransition ) {
( withTransition ? legend . transition ( ) : legend ) . attr ( "transform" , translate . legend ) ;
}
function transformAll ( withTransition , transitions ) {
transformMain ( withTransition , transitions ) ;
if ( _ _subchart _show ) { transformContext ( withTransition , transitions ) ; }
transformLegend ( withTransition ) ;
}
//-- Sizes --//
// TODO: configurabale
var rotated _padding _left = 30 , rotated _padding _right = _ _axis _rotated && ! _ _axis _x _show ? 0 : 30 , rotated _padding _top = 5 ;
// MEMO: each value should be int to avoid disabling antialiasing
function updateSizes ( ) {
var legendHeight = getLegendHeight ( ) , legendWidth = getLegendWidth ( ) ,
legendHeightForBottom = isLegendRight ? 0 : legendHeight ,
hasArc = hasArcType ( c3 . data . targets ) ,
xAxisHeight = _ _axis _rotated || hasArc ? 0 : getHorizontalAxisHeight ( 'x' ) ,
subchartHeight = _ _subchart _show && ! hasArc ? ( _ _subchart _size _height + xAxisHeight ) : 0 ;
currentWidth = getCurrentWidth ( ) ;
currentHeight = getCurrentHeight ( ) ;
// for main, context
if ( _ _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
margin3 = {
top : isLegendRight ? 0 : currentHeight - legendHeight ,
right : NaN ,
bottom : 0 ,
left : isLegendRight ? currentWidth - legendWidth : 0
} ;
width = currentWidth - margin . left - margin . right ;
height = currentHeight - margin . top - margin . bottom ;
if ( width < 0 ) { width = 0 ; }
if ( height < 0 ) { height = 0 ; }
width2 = _ _axis _rotated ? margin . left - rotated _padding _left - rotated _padding _right : width ;
height2 = _ _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 ) ;
updateRadius ( ) ;
if ( isLegendRight && hasArc ) {
margin3 . left = arcWidth / 2 + radiusExpanded * 1.1 ;
}
}
function updateXgridFocus ( ) {
main . select ( 'line.' + CLASS . xgridFocus )
. attr ( "x1" , _ _axis _rotated ? 0 : - 10 )
. attr ( "x2" , _ _axis _rotated ? width : - 10 )
. attr ( "y1" , _ _axis _rotated ? - 10 : margin . top )
. attr ( "y2" , _ _axis _rotated ? - 10 : height ) ;
}
function updateRadius ( ) {
radiusExpanded = Math . min ( arcWidth , arcHeight ) / 2 ;
radius = radiusExpanded * 0.95 ;
var innerRadiusRatio = _ _gauge _width ? ( ( radius - _ _gauge _width ) / radius ) : 0.6 ;
innerRadius = hasDonutType ( c3 . data . targets ) || hasGaugeType ( c3 . data . targets ) ? radius * innerRadiusRatio : 0 ;
}
function getSvgLeft ( ) {
var leftAxisClass = _ _axis _rotated ? CLASS . axisX : CLASS . axisY ,
leftAxis = main . select ( '.' + leftAxisClass ) . node ( ) ,
svgRect = leftAxis ? leftAxis . getBoundingClientRect ( ) : { right : 0 } ,
chartRect = selectChart . node ( ) . getBoundingClientRect ( ) ,
hasArc = hasArcType ( c3 . data . targets ) ,
svgLeft = svgRect . right - chartRect . left - ( hasArc ? 0 : getCurrentPaddingLeft ( ) ) ;
return svgLeft > 0 ? svgLeft : 0 ;
}
function getCurrentWidth ( ) {
return _ _size _width ? _ _size _width : getParentWidth ( ) ;
}
function getCurrentHeight ( ) {
var h = _ _size _height ? _ _size _height : getParentHeight ( ) ;
return h > 0 ? h : 320 ;
}
function getCurrentPaddingTop ( ) {
return _ _padding _top ? _ _padding _top : 0 ;
}
function getCurrentPaddingBottom ( ) {
return _ _padding _bottom ? _ _padding _bottom : 0 ;
}
function getCurrentPaddingLeft ( ) {
if ( _ _padding _left ) {
return _ _padding _left ;
} else if ( _ _axis _rotated ) {
return ! _ _axis _x _show ? 1 : Math . max ( ceil10 ( getAxisWidthByAxisId ( 'x' ) ) , 40 ) ;
} else {
return ! _ _axis _y _show || _ _axis _y _inner ? 1 : ceil10 ( getAxisWidthByAxisId ( 'y' ) ) ;
}
}
function getCurrentPaddingRight ( ) {
var defaultPadding = 10 , legendWidthOnRight = isLegendRight ? getLegendWidth ( ) + 20 : 0 ;
if ( _ _padding _right ) {
return _ _padding _right ;
} else if ( _ _axis _rotated ) {
return defaultPadding + legendWidthOnRight ;
} else {
return ( ! _ _axis _y2 _show || _ _axis _y2 _inner ? defaultPadding : ceil10 ( getAxisWidthByAxisId ( 'y2' ) ) ) + legendWidthOnRight ;
}
}
function getAxisWidthByAxisId ( id ) {
var position = getAxisLabelPositionById ( id ) ;
return position . isInner ? 20 + getMaxTickWidth ( id ) : 40 + getMaxTickWidth ( id ) ;
}
function getHorizontalAxisHeight ( axisId ) {
if ( axisId === 'x' && ! _ _axis _x _show ) { return 0 ; }
if ( axisId === 'x' && _ _axis _x _height ) { return _ _axis _x _height ; }
if ( axisId === 'y' && ! _ _axis _y _show ) { return _ _legend _show && ! isLegendRight ? 10 : 1 ; }
if ( axisId === 'y2' && ! _ _axis _y2 _show ) { return rotated _padding _top ; }
return ( getAxisLabelPositionById ( axisId ) . isInner ? 30 : 40 ) + ( axisId === 'y2' ? - 10 : 0 ) ;
}
function getParentRectValue ( key ) {
var parent = selectChart . node ( ) , v ;
while ( parent && parent . tagName !== 'BODY' ) {
v = parent . getBoundingClientRect ( ) [ key ] ;
if ( v ) {
break ;
}
parent = parent . parentNode ;
}
return v ;
}
function getParentWidth ( ) {
return getParentRectValue ( 'width' ) ;
}
function getParentHeight ( ) {
var h = selectChart . style ( 'height' ) ;
return h . indexOf ( 'px' ) > 0 ? + h . replace ( 'px' , '' ) : 0 ;
}
function getAxisClipX ( forHorizontal ) {
// axis line width + padding for left
return forHorizontal ? - ( 1 + 30 ) : - ( margin . left - 1 ) ;
}
function getAxisClipY ( forHorizontal ) {
return forHorizontal ? - 20 : - 4 ;
}
function getXAxisClipX ( ) {
return getAxisClipX ( ! _ _axis _rotated ) ;
}
function getXAxisClipY ( ) {
return getAxisClipY ( ! _ _axis _rotated ) ;
}
function getYAxisClipX ( ) {
return getAxisClipX ( _ _axis _rotated ) ;
}
function getYAxisClipY ( ) {
return getAxisClipY ( _ _axis _rotated ) ;
}
function getAxisClipWidth ( forHorizontal ) {
// width + axis line width + padding for left/right
return forHorizontal ? width + 2 + 30 + 30 : margin . left + 20 ;
}
function getAxisClipHeight ( forHorizontal ) {
return forHorizontal ? ( _ _axis _x _height ? _ _axis _x _height : 0 ) + 80 : height + 8 ;
}
function getXAxisClipWidth ( ) {
return getAxisClipWidth ( ! _ _axis _rotated ) ;
}
function getXAxisClipHeight ( ) {
return getAxisClipHeight ( ! _ _axis _rotated ) ;
}
function getYAxisClipWidth ( ) {
return getAxisClipWidth ( _ _axis _rotated ) ;
}
function getYAxisClipHeight ( ) {
return getAxisClipHeight ( _ _axis _rotated ) ;
}
function getEventRectWidth ( ) {
var target = getMaxDataCountTarget ( c3 . data . targets ) ,
firstData , lastData , base , maxDataCount , ratio , w ;
if ( ! target ) {
return 0 ;
}
firstData = target . values [ 0 ] , lastData = target . values [ target . values . length - 1 ] ;
base = x ( lastData . x ) - x ( firstData . x ) ;
if ( base === 0 ) {
return _ _axis _rotated ? height : width ;
}
maxDataCount = getMaxDataCount ( ) ;
ratio = ( hasBarType ( c3 . data . targets ) ? ( maxDataCount - ( isCategorized ? 0.25 : 1 ) ) / maxDataCount : 1 ) ;
w = maxDataCount > 1 ? ( base * ratio ) / ( maxDataCount - 1 ) : base ;
return w < 1 ? 1 : w ;
}
function updateLegendStep ( step ) {
legendStep = step ;
}
function updateLegendItemWidth ( w ) {
legendItemWidth = w ;
}
function updateLegendItemHeight ( h ) {
legendItemHeight = h ;
}
function getLegendWidth ( ) {
return _ _legend _show ? isLegendRight ? legendItemWidth * ( legendStep + 1 ) : currentWidth : 0 ;
}
function getLegendHeight ( ) {
return _ _legend _show ? isLegendRight ? currentHeight : legendItemHeight * ( legendStep + 1 ) : 0 ;
}
//-- Scales --//
function updateScales ( ) {
var xAxisTickFormat , xAxisTickValues , forInit = ! x ;
// update edges
xMin = _ _axis _rotated ? 1 : 0 ;
xMax = _ _axis _rotated ? height : width ;
yMin = _ _axis _rotated ? 0 : height ;
yMax = _ _axis _rotated ? width : 1 ;
subXMin = xMin ;
subXMax = xMax ;
subYMin = _ _axis _rotated ? 0 : height2 ;
subYMax = _ _axis _rotated ? width2 : 1 ;
// update scales
x = getX ( xMin , xMax , forInit ? undefined : x . domain ( ) , function ( ) { return xAxis . tickOffset ( ) ; } ) ;
y = getY ( yMin , yMax , forInit ? undefined : y . domain ( ) ) ;
y2 = getY ( yMin , yMax , forInit ? undefined : y2 . domain ( ) ) ;
subX = getX ( xMin , xMax , orgXDomain , function ( d ) { return d % 1 ? 0 : subXAxis . tickOffset ( ) ; } ) ;
subY = getY ( subYMin , subYMax , forInit ? undefined : subY . domain ( ) ) ;
subY2 = getY ( subYMin , subYMax , forInit ? undefined : subY2 . domain ( ) ) ;
// update axes
xAxisTickFormat = getXAxisTickFormat ( ) ;
xAxisTickValues = _ _axis _x _tick _values ? _ _axis _x _tick _values : ( forInit ? undefined : xAxis . tickValues ( ) ) ;
xAxis = getXAxis ( x , xOrient , xAxisTickFormat , xAxisTickValues ) ;
subXAxis = getXAxis ( subX , subXOrient , xAxisTickFormat , xAxisTickValues ) ;
yAxis = getYAxis ( y , yOrient , _ _axis _y _tick _format , _ _axis _y _ticks ) ;
y2Axis = getYAxis ( y2 , y2Orient , _ _axis _y2 _tick _format , _ _axis _y2 _ticks ) ;
// Set initialized scales to brush and zoom
if ( ! forInit ) {
brush . scale ( subX ) ;
if ( _ _zoom _enabled ) { zoom . scale ( x ) ; }
}
// update for arc
updateArc ( ) ;
}
function updateArc ( ) {
svgArc = getSvgArc ( ) ;
svgArcExpanded = getSvgArcExpanded ( ) ;
svgArcExpandedSub = getSvgArcExpanded ( 0.98 ) ;
}
function getScale ( min , max , forTimeseries ) {
return ( forTimeseries ? d3 . time . scale ( ) : d3 . scale . linear ( ) ) . range ( [ min , max ] ) ;
}
function getX ( min , max , domain , offset ) {
var scale = getScale ( min , max , isTimeSeries ) , //(isTimeSeries ? d3.time.scale() : d3.scale.linear()).range([min, max]),
_scale = domain ? scale . domain ( domain ) : scale , key ;
// Define customized scale if categorized axis
if ( isCategorized ) {
offset = offset || function ( ) { return 0 ; } ;
scale = function ( d , raw ) {
var v = _scale ( d ) + offset ( d ) ;
return raw ? v : Math . ceil ( v ) ;
} ;
} else {
scale = function ( d , raw ) {
var v = _scale ( d ) ;
return raw ? v : Math . ceil ( v ) ;
} ;
}
// define functions
for ( key in _scale ) {
scale [ key ] = _scale [ key ] ;
}
scale . orgDomain = function ( ) {
var domain = _scale . domain ( ) ;
if ( orgXDomain && orgXDomain [ 0 ] === domain [ 0 ] && orgXDomain [ 1 ] < domain [ 1 ] ) {
domain [ 1 ] = orgXDomain [ 1 ] ;
}
return domain ;
} ;
// define custom domain() for categorized axis
if ( isCategorized ) {
scale . domain = function ( domain ) {
if ( ! arguments . length ) {
domain = this . orgDomain ( ) ;
return [ domain [ 0 ] , domain [ 1 ] + 1 ] ;
}
orgXDomain = domain ;
_scale . domain ( domain ) ;
return scale ;
} ;
}
return scale ;
}
function getY ( min , max , domain ) {
var scale = getScale ( min , max ) ;
if ( domain ) { scale . domain ( domain ) ; }
return scale ;
}
function getYScale ( id ) {
return getAxisId ( id ) === 'y2' ? y2 : y ;
}
function getSubYScale ( id ) {
return getAxisId ( id ) === 'y2' ? subY2 : subY ;
}
//-- Axes --//
function getXAxis ( scale , orient , tickFormat , tickValues ) {
var axis = c3 _axis ( d3 , isCategorized ) . scale ( scale ) . orient ( orient ) ;
// Set tick
axis . tickFormat ( tickFormat ) . tickValues ( tickValues ) ;
if ( isCategorized ) {
axis . tickCentered ( _ _axis _x _tick _centered ) ;
if ( isEmpty ( _ _axis _x _tick _culling ) ) {
_ _axis _x _tick _culling = false ;
}
axis . categories ( _ _axis _x _categories ) ;
} else {
// TODO: move this to c3_axis
axis . tickOffset = function ( ) {
var edgeX = getEdgeX ( c3 . data . targets ) , diff = x ( edgeX [ 1 ] ) - x ( edgeX [ 0 ] ) ,
base = diff ? diff : ( _ _axis _rotated ? height : width ) ;
return ( base / getMaxDataCount ( ) ) / 2 ;
} ;
}
return axis ;
}
function getYAxis ( scale , orient , tickFormat , ticks ) {
return c3 _axis ( d3 ) . scale ( scale ) . orient ( orient ) . tickFormat ( tickFormat ) . ticks ( ticks ) ;
}
function getAxisId ( id ) {
return id in _ _data _axes ? _ _data _axes [ id ] : 'y' ;
}
function getXAxisTickFormat ( ) {
var format = isTimeSeries ? defaultTimeFormat : isCategorized ? categoryName : function ( v ) { return v < 0 ? v . toFixed ( 0 ) : v ; } ;
if ( _ _axis _x _tick _format ) {
if ( typeof _ _axis _x _tick _format === 'function' ) {
format = _ _axis _x _tick _format ;
} else if ( isTimeSeries ) {
format = function ( date ) {
return date ? timeFormat ( _ _axis _x _tick _format ) ( date ) : "" ;
} ;
}
}
return format ;
}
function getAxisLabelOptionByAxisId ( axisId ) {
var option ;
if ( axisId === 'y' ) {
option = _ _axis _y _label ;
} else if ( axisId === 'y2' ) {
option = _ _axis _y2 _label ;
} else if ( axisId === 'x' ) {
option = _ _axis _x _label ;
}
return option ;
}
function getAxisLabelText ( axisId ) {
var option = getAxisLabelOptionByAxisId ( axisId ) ;
return typeof option === 'string' ? option : option ? option . text : null ;
}
function setAxisLabelText ( axisId , text ) {
var option = getAxisLabelOptionByAxisId ( axisId ) ;
if ( typeof option === 'string' ) {
if ( axisId === 'y' ) {
_ _axis _y _label = text ;
} else if ( axisId === 'y2' ) {
_ _axis _y2 _label = text ;
} else if ( axisId === 'x' ) {
_ _axis _x _label = text ;
}
} else if ( option ) {
option . text = text ;
}
}
function xForRotatedTickText ( r ) {
return 10 * Math . sin ( Math . PI * ( r / 180 ) ) ;
}
function yForRotatedTickText ( r ) {
return 11.5 - 2.5 * ( r / 15 ) ;
}
function rotateTickText ( axis , transition , rotate ) {
axis . selectAll ( '.tick text' )
. style ( "text-anchor" , "start" ) ;
transition . selectAll ( '.tick text' )
. attr ( "y" , yForRotatedTickText ( rotate ) )
. attr ( "x" , xForRotatedTickText ( rotate ) )
. attr ( "transform" , "rotate(" + rotate + ")" ) ;
}
function getAxisLabelPosition ( axisId , defaultPosition ) {
var option = getAxisLabelOptionByAxisId ( 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
} ;
}
function getXAxisLabelPosition ( ) {
return getAxisLabelPosition ( 'x' , _ _axis _rotated ? 'inner-top' : 'inner-right' ) ;
}
function getYAxisLabelPosition ( ) {
return getAxisLabelPosition ( 'y' , _ _axis _rotated ? 'inner-right' : 'inner-top' ) ;
}
function getY2AxisLabelPosition ( ) {
return getAxisLabelPosition ( 'y2' , _ _axis _rotated ? 'inner-right' : 'inner-top' ) ;
}
function getAxisLabelPositionById ( id ) {
return id === 'y2' ? getY2AxisLabelPosition ( ) : id === 'y' ? getYAxisLabelPosition ( ) : getXAxisLabelPosition ( ) ;
}
function textForXAxisLabel ( ) {
return getAxisLabelText ( 'x' ) ;
}
function textForYAxisLabel ( ) {
return getAxisLabelText ( 'y' ) ;
}
function textForY2AxisLabel ( ) {
return getAxisLabelText ( 'y2' ) ;
}
function xForAxisLabel ( forHorizontal , position ) {
if ( forHorizontal ) {
return position . isLeft ? 0 : position . isCenter ? width / 2 : width ;
} else {
return position . isBottom ? - height : position . isMiddle ? - height / 2 : 0 ;
}
}
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" ;
}
}
function textAnchorForAxisLabel ( forHorizontal , position ) {
if ( forHorizontal ) {
return position . isLeft ? 'start' : position . isCenter ? 'middle' : 'end' ;
} else {
return position . isBottom ? 'start' : position . isMiddle ? 'middle' : 'end' ;
}
}
function xForXAxisLabel ( ) {
return xForAxisLabel ( ! _ _axis _rotated , getXAxisLabelPosition ( ) ) ;
}
function xForYAxisLabel ( ) {
return xForAxisLabel ( _ _axis _rotated , getYAxisLabelPosition ( ) ) ;
}
function xForY2AxisLabel ( ) {
return xForAxisLabel ( _ _axis _rotated , getY2AxisLabelPosition ( ) ) ;
}
function dxForXAxisLabel ( ) {
return dxForAxisLabel ( ! _ _axis _rotated , getXAxisLabelPosition ( ) ) ;
}
function dxForYAxisLabel ( ) {
return dxForAxisLabel ( _ _axis _rotated , getYAxisLabelPosition ( ) ) ;
}
function dxForY2AxisLabel ( ) {
return dxForAxisLabel ( _ _axis _rotated , getY2AxisLabelPosition ( ) ) ;
}
function dyForXAxisLabel ( ) {
var position = getXAxisLabelPosition ( ) ;
if ( _ _axis _rotated ) {
return position . isInner ? "1.2em" : - 25 - getMaxTickWidth ( 'x' ) ;
} else {
return position . isInner ? "-0.5em" : _ _axis _x _height ? _ _axis _x _height - 10 : "3em" ;
}
}
function dyForYAxisLabel ( ) {
var position = getYAxisLabelPosition ( ) ;
if ( _ _axis _rotated ) {
return position . isInner ? "-0.5em" : "3em" ;
} else {
return position . isInner ? "1.2em" : - 20 - getMaxTickWidth ( 'y' ) ;
}
}
function dyForY2AxisLabel ( ) {
var position = getY2AxisLabelPosition ( ) ;
if ( _ _axis _rotated ) {
return position . isInner ? "1.2em" : "-2.2em" ;
} else {
return position . isInner ? "-0.5em" : 30 + getMaxTickWidth ( 'y2' ) ;
}
}
function textAnchorForXAxisLabel ( ) {
return textAnchorForAxisLabel ( ! _ _axis _rotated , getXAxisLabelPosition ( ) ) ;
}
function textAnchorForYAxisLabel ( ) {
return textAnchorForAxisLabel ( _ _axis _rotated , getYAxisLabelPosition ( ) ) ;
}
function textAnchorForY2AxisLabel ( ) {
return textAnchorForAxisLabel ( _ _axis _rotated , getY2AxisLabelPosition ( ) ) ;
}
function getMaxTickWidth ( id ) {
var maxWidth = 0 , axisClass = id === 'x' ? CLASS . axisX : id === 'y' ? CLASS . axisY : CLASS . axisY2 ;
d3 . selectAll ( '.' + axisClass + ' .tick text' ) . each ( function ( ) {
var box = this . getBoundingClientRect ( ) ;
if ( maxWidth < box . width ) { maxWidth = box . width ; }
} ) ;
return maxWidth < 0 ? 0 : maxWidth ;
}
function updateAxisLabels ( withTransition ) {
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" , xForXAxisLabel )
. attr ( "dx" , dxForXAxisLabel )
. attr ( "dy" , dyForXAxisLabel )
. text ( textForXAxisLabel ) ;
( withTransition ? axisYLabel . transition ( ) : axisYLabel )
. attr ( "x" , xForYAxisLabel )
. attr ( "dx" , dxForYAxisLabel )
. attr ( "dy" , dyForYAxisLabel )
. attr ( "dy" , dyForYAxisLabel )
. text ( textForYAxisLabel ) ;
( withTransition ? axisY2Label . transition ( ) : axisY2Label )
. attr ( "x" , xForY2AxisLabel )
. attr ( "dx" , dxForY2AxisLabel )
. attr ( "dy" , dyForY2AxisLabel )
. text ( textForY2AxisLabel ) ;
}
//-- Arc --//
pie = d3 . layout . pie ( ) . value ( function ( d ) {
return d . values . reduce ( function ( a , b ) { return a + b . value ; } , 0 ) ;
} ) ;
function updateAngle ( d ) {
var found = false ;
pie ( filterTargetsToShow ( c3 . data . targets ) ) . forEach ( function ( t ) {
if ( ! found && t . data . id === d . data . id ) {
found = true ;
d = t ;
}
} ) ;
if ( isNaN ( d . endAngle ) ) d . endAngle = d . startAngle ;
if ( isGaugeType ( d . data ) ) {
var sA = d . startAngle , eA = d . endAngle ;
var gMin = _ _gauge _min , gMax = _ _gauge _max ,
gF = Math . abs ( gMin ) + gMax , fA = Math . abs ( sA ) + eA ,
aTic = ( Math . PI ) / gF ;
d . startAngle = ( - 1 * ( Math . PI / 2 ) ) + ( aTic * Math . abs ( gMin ) ) ;
d . endAngle = d . startAngle + ( aTic * ( ( d . value > gMax ) ? gMax : d . value ) ) ;
}
return found ? d : null ;
}
function getSvgArc ( ) {
var arc = d3 . svg . arc ( ) . outerRadius ( radius ) . innerRadius ( innerRadius ) ,
newArc = function ( d , withoutUpdate ) {
var updated ;
if ( withoutUpdate ) { return arc ( d ) ; } // for interpolate
updated = updateAngle ( d ) ;
return updated ? arc ( updated ) : "M 0 0" ;
} ;
// TODO: extends all function
newArc . centroid = arc . centroid ;
return newArc ;
}
function getSvgArcExpanded ( rate ) {
var arc = d3 . svg . arc ( ) . outerRadius ( radiusExpanded * ( rate ? rate : 1 ) ) . innerRadius ( innerRadius ) ;
return function ( d ) {
var updated = updateAngle ( d ) ;
return updated ? arc ( updated ) : "M 0 0" ;
} ;
}
function getArc ( d , withoutUpdate ) {
return isArcType ( d . data ) ? svgArc ( d , withoutUpdate ) : "M 0 0" ;
}
function transformForArcLabel ( d ) {
var updated = updateAngle ( d ) , c , x , y , h , ratio , translate = "" ;
if ( updated ) {
c = svgArc . centroid ( updated ) ;
x = c [ 0 ] ;
y = c [ 1 ] ;
h = Math . sqrt ( x * x + y * y ) ;
// TODO: ratio should be an option?
ratio = ( 36 / radius > 0.375 ? 1.175 - 36 / radius : 0.8 ) * radius / h ;
translate = _ _gauge _style == 'arc' ? "translate(1,1)" : "translate(" + ( x * ratio ) + ',' + ( y * ratio ) + ")" ;
}
return translate ;
}
function getArcRatio ( d ) {
var whole = _ _gauge _style == 'arc' ? Math . PI : ( Math . PI * 2 ) ;
return d ? ( d . endAngle - d . startAngle ) / whole : null ;
}
function convertToArcData ( d ) {
return addName ( {
id : d . data . id ,
value : d . value ,
ratio : getArcRatio ( d )
} ) ;
}
function textForArcLabel ( d ) {
var updated , value , ratio , format ;
if ( ! shouldShowArcLable ( ) ) { return "" ; }
updated = updateAngle ( d ) ;
value = updated ? updated . value : null ;
ratio = getArcRatio ( updated ) ;
format = getArcLabelFormat ( ) ;
return format ? format ( value , ratio ) : defaultArcValueFormat ( value , ratio ) ;
}
function expandArc ( id , withoutFadeOut ) {
var target = svg . selectAll ( '.' + CLASS . chartArc + selectorTarget ( id ) ) ,
noneTargets = svg . selectAll ( '.' + CLASS . arc ) . filter ( function ( data ) { return data . data . id !== id ; } ) ;
if ( shouldExpand ( id ) ) {
target . selectAll ( 'path' )
. transition ( ) . duration ( 50 )
. attr ( "d" , svgArcExpanded )
. transition ( ) . duration ( 100 )
. attr ( "d" , svgArcExpandedSub )
. each ( function ( d ) {
if ( isDonutType ( d . data ) ) {
// callback here
}
} ) ;
}
if ( ! withoutFadeOut ) {
noneTargets . style ( "opacity" , 0.3 ) ;
}
}
function unexpandArc ( id ) {
var target = svg . selectAll ( '.' + CLASS . chartArc + selectorTarget ( id ) ) ;
target . selectAll ( 'path.c3-arc' )
. transition ( ) . duration ( 50 )
. attr ( "d" , svgArc ) ;
svg . selectAll ( '.' + CLASS . arc )
. style ( "opacity" , ( _ _color _opacity ) ? _ _color _opacity : 1 ) ;
}
function shouldShowArcLable ( ) {
if ( hasGaugeType ( c3 . data . targets ) ) {
return true ;
}
return hasDonutType ( c3 . data . targets ) ? _ _donut _label _show : _ _pie _label _show ;
}
function getArcLabelFormat ( ) {
if ( hasGaugeType ( c3 . data . targets ) ) {
return _ _gauge _label _format ;
}
return hasDonutType ( c3 . data . targets ) ? _ _donut _label _format : _ _pie _label _format ;
}
function getArcTitle ( ) {
return hasDonutType ( c3 . data . targets ) ? _ _donut _title : "" ;
}
function getArcOnClick ( ) {
if ( hasGaugeType ( c3 . data . targets ) ) {
return typeof _ _gauge _onclick === 'function' ? _ _gauge _onclick : function ( ) { } ;
}
var callback = hasDonutType ( c3 . data . targets ) ? _ _donut _onclick : _ _pie _onclick ;
return typeof callback === 'function' ? callback : function ( ) { } ;
}
function getArcOnMouseOver ( ) {
if ( hasGaugeType ( c3 . data . targets ) ) {
return typeof _ _gauge _onmouseover === 'function' ? _ _gauge _onmouseover : function ( ) { } ;
}
var callback = hasDonutType ( c3 . data . targets ) ? _ _donut _onmouseover : _ _pie _onmouseover ;
return typeof callback === 'function' ? callback : function ( ) { } ;
}
function getArcOnMouseOut ( ) {
if ( hasGaugeType ( c3 . data . targets ) ) {
return typeof _ _gauge _onmouseout === 'function' ? _ _gauge _onmouseout : function ( ) { } ;
}
var callback = hasDonutType ( c3 . data . targets ) ? _ _donut _onmouseout : _ _pie _onmouseout ;
return typeof callback === 'function' ? callback : function ( ) { } ;
}
//-- Domain --//
function getYDomainMin ( targets ) {
var ids = mapToIds ( targets ) , ys = getValuesAsIdKeyed ( targets ) , j , k , baseId , idsInGroup , id , hasNegativeValue ;
if ( _ _data _groups . length > 0 ) {
hasNegativeValue = hasNegativeValueInTargets ( targets ) ;
for ( j = 0 ; j < _ _data _groups . length ; j ++ ) {
// Determine baseId
idsInGroup = _ _data _groups [ j ] . filter ( function ( id ) { return ids . indexOf ( id ) >= 0 ; } ) ;
if ( idsInGroup . length === 0 ) { continue ; }
baseId = idsInGroup [ 0 ] ;
// Consider negative values
if ( hasNegativeValue && ys [ baseId ] ) {
ys [ baseId ] . forEach ( function ( v , i ) {
ys [ baseId ] [ i ] = v < 0 ? v : 0 ;
} ) ;
}
// Compute min
for ( k = 1 ; k < idsInGroup . length ; k ++ ) {
id = idsInGroup [ k ] ;
if ( ! ys [ id ] ) { continue ; }
ys [ id ] . forEach ( function ( v , i ) {
if ( getAxisId ( id ) === getAxisId ( baseId ) && ys [ baseId ] && ! ( hasNegativeValue && + v > 0 ) ) {
ys [ baseId ] [ i ] += + v ;
}
} ) ;
}
}
}
return d3 . min ( Object . keys ( ys ) . map ( function ( key ) { return d3 . min ( ys [ key ] ) ; } ) ) ;
}
function getYDomainMax ( targets ) {
var ids = mapToIds ( targets ) , ys = getValuesAsIdKeyed ( targets ) , j , k , baseId , idsInGroup , id , hasPositiveValue ;
if ( _ _data _groups . length > 0 ) {
hasPositiveValue = hasPositiveValueInTargets ( targets ) ;
for ( j = 0 ; j < _ _data _groups . length ; j ++ ) {
// Determine baseId
idsInGroup = _ _data _groups [ j ] . filter ( function ( id ) { return ids . indexOf ( id ) >= 0 ; } ) ;
if ( idsInGroup . length === 0 ) { continue ; }
baseId = idsInGroup [ 0 ] ;
// Consider positive values
if ( hasPositiveValue && ys [ baseId ] ) {
ys [ baseId ] . forEach ( function ( v , i ) {
ys [ baseId ] [ i ] = v > 0 ? v : 0 ;
} ) ;
}
// Compute max
for ( k = 1 ; k < idsInGroup . length ; k ++ ) {
id = idsInGroup [ k ] ;
if ( ! ys [ id ] ) { continue ; }
ys [ id ] . forEach ( function ( v , i ) {
if ( getAxisId ( id ) === getAxisId ( baseId ) && ys [ baseId ] && ! ( hasPositiveValue && + v < 0 ) ) {
ys [ baseId ] [ i ] += + v ;
}
} ) ;
}
}
}
return d3 . max ( Object . keys ( ys ) . map ( function ( key ) { return d3 . max ( ys [ key ] ) ; } ) ) ;
}
function getYDomain ( targets , axisId ) {
var yTargets = targets . filter ( function ( d ) { return getAxisId ( d . id ) === axisId ; } ) ,
yMin = axisId === 'y2' ? _ _axis _y2 _min : _ _axis _y _min ,
yMax = axisId === 'y2' ? _ _axis _y2 _max : _ _axis _y _max ,
yDomainMin = isValue ( yMin ) ? yMin : getYDomainMin ( yTargets ) ,
yDomainMax = isValue ( yMax ) ? yMax : getYDomainMax ( yTargets ) ,
domainLength , padding , padding _top , padding _bottom ,
center = axisId === 'y2' ? _ _axis _y2 _center : _ _axis _y _center ,
yDomainAbs , widths , diff , ratio ,
showHorizontalDataLabel = hasDataLabel ( ) && _ _axis _rotated ;
if ( yTargets . length === 0 ) { // use current domain if target of axisId is none
return axisId === 'y2' ? y2 . domain ( ) : y . domain ( ) ;
}
if ( yDomainMin === yDomainMax ) {
yDomainMin < 0 ? yDomainMax = 0 : yDomainMin = 0 ;
}
domainLength = Math . abs ( yDomainMax - yDomainMin ) ;
padding = padding _top = padding _bottom = showHorizontalDataLabel ? 0 : domainLength * 0.1 ;
if ( center ) {
yDomainAbs = Math . max ( Math . abs ( yDomainMin ) , Math . abs ( yDomainMax ) ) ;
yDomainMax = yDomainAbs - center ;
yDomainMin = center - yDomainAbs ;
}
// add padding for data label
if ( showHorizontalDataLabel ) {
widths = getDataLabelWidth ( yDomainMin , yDomainMax ) ;
diff = diffDomain ( y . range ( ) ) ;
ratio = [ widths [ 0 ] / diff , widths [ 1 ] / diff ] ;
padding _top += domainLength * ( ratio [ 1 ] / ( 1 - ratio [ 0 ] - ratio [ 1 ] ) ) ;
padding _bottom += domainLength * ( ratio [ 0 ] / ( 1 - ratio [ 0 ] - ratio [ 1 ] ) ) ;
}
if ( axisId === 'y' && _ _axis _y _padding ) {
padding _top = isValue ( _ _axis _y _padding . top ) ? _ _axis _y _padding . top : padding ;
padding _bottom = isValue ( _ _axis _y _padding . bottom ) ? _ _axis _y _padding . bottom : padding ;
}
if ( axisId === 'y2' && _ _axis _y2 _padding ) {
padding _top = isValue ( _ _axis _y2 _padding . top ) ? _ _axis _y2 _padding . top : padding ;
padding _bottom = isValue ( _ _axis _y2 _padding . bottom ) ? _ _axis _y2 _padding . bottom : padding ;
}
// Bar chart with only positive values should be 0-based
if ( hasBarType ( yTargets ) && ! hasNegativeValueInTargets ( yTargets ) ) {
padding _bottom = yDomainMin ;
}
if ( ! hasNegativeValueInTargets ( yTargets ) ) {
padding _bottom -= padding _bottom ;
}
return [ yDomainMin - padding _bottom , yDomainMax + padding _top ] ;
}
function getXDomainMin ( targets ) {
return _ _axis _x _min ? ( isTimeSeries ? parseDate ( _ _axis _x _min ) : _ _axis _x _min ) : d3 . min ( targets , function ( t ) { return d3 . min ( t . values , function ( v ) { return v . x ; } ) ; } ) ;
}
function getXDomainMax ( targets ) {
return _ _axis _x _max ? ( isTimeSeries ? parseDate ( _ _axis _x _max ) : _ _axis _x _max ) : d3 . max ( targets , function ( t ) { return d3 . max ( t . values , function ( v ) { return v . x ; } ) ; } ) ;
}
function getXDomainPadding ( targets ) {
var edgeX = getEdgeX ( targets ) , diff = edgeX [ 1 ] - edgeX [ 0 ] ,
maxDataCount , padding , paddingLeft , paddingRight ;
if ( isCategorized ) {
padding = 0 ;
} else if ( hasBarType ( targets ) ) {
maxDataCount = getMaxDataCount ( ) ;
padding = maxDataCount > 1 ? ( diff / ( maxDataCount - 1 ) ) / 2 : 0.5 ;
} else {
padding = diff * 0.01 ;
}
if ( typeof _ _axis _x _padding === 'object' && notEmpty ( _ _axis _x _padding ) ) {
paddingLeft = isValue ( _ _axis _x _padding . left ) ? _ _axis _x _padding . left : padding ;
paddingRight = isValue ( _ _axis _x _padding . right ) ? _ _axis _x _padding . right : padding ;
} else if ( typeof _ _axis _x _padding === 'number' ) {
paddingLeft = paddingRight = _ _axis _x _padding ;
} else {
paddingLeft = paddingRight = padding ;
}
return { left : paddingLeft , right : paddingRight } ;
}
function getXDomain ( targets ) {
var xDomain = [ getXDomainMin ( targets ) , getXDomainMax ( targets ) ] ,
firstX = xDomain [ 0 ] , lastX = xDomain [ 1 ] ,
padding = getXDomainPadding ( targets ) ,
min = 0 , max = 0 ;
// show center of x domain if min and max are the same
if ( ( firstX - lastX ) === 0 && ! isCategorized ) {
firstX = isTimeSeries ? new Date ( firstX . getTime ( ) * 0.5 ) : - 0.5 ;
lastX = isTimeSeries ? new Date ( lastX . getTime ( ) * 1.5 ) : 0.5 ;
}
if ( firstX || firstX === 0 ) {
min = isTimeSeries ? new Date ( firstX . getTime ( ) - padding . left ) : firstX - padding . left ;
}
if ( lastX || lastX === 0 ) {
max = isTimeSeries ? new Date ( lastX . getTime ( ) + padding . right ) : lastX + padding . right ;
}
return [ min , max ] ;
}
function diffDomain ( d ) {
return d [ 1 ] - d [ 0 ] ;
}
//-- Cache --//
function hasCaches ( ids ) {
for ( var i = 0 ; i < ids . length ; i ++ ) {
if ( ! ( ids [ i ] in cache ) ) { return false ; }
}
return true ;
}
function addCache ( id , target ) {
cache [ id ] = cloneTarget ( target ) ;
}
function getCaches ( ids ) {
var targets = [ ] ;
for ( var i = 0 ; i < ids . length ; i ++ ) {
if ( ids [ i ] in cache ) { targets . push ( cloneTarget ( cache [ ids [ i ] ] ) ) ; }
}
return targets ;
}
//-- Regions --//
function regionX ( d ) {
var xPos , yScale = d . axis === 'y' ? y : y2 ;
if ( d . axis === 'y' || d . axis === 'y2' ) {
xPos = _ _axis _rotated ? ( 'start' in d ? yScale ( d . start ) : 0 ) : 0 ;
} else {
xPos = _ _axis _rotated ? 0 : ( 'start' in d ? x ( isTimeSeries ? parseDate ( d . start ) : d . start ) : 0 ) ;
}
return xPos ;
}
function regionY ( d ) {
var yPos , yScale = d . axis === 'y' ? y : y2 ;
if ( d . axis === 'y' || d . axis === 'y2' ) {
yPos = _ _axis _rotated ? 0 : ( 'end' in d ? yScale ( d . end ) : 0 ) ;
} else {
yPos = _ _axis _rotated ? ( 'start' in d ? x ( isTimeSeries ? parseDate ( d . start ) : d . start ) : 0 ) : 0 ;
}
return yPos ;
}
function regionWidth ( d ) {
var start = regionX ( d ) , end , yScale = d . axis === 'y' ? y : y2 ;
if ( d . axis === 'y' || d . axis === 'y2' ) {
end = _ _axis _rotated ? ( 'end' in d ? yScale ( d . end ) : width ) : width ;
} else {
end = _ _axis _rotated ? width : ( 'end' in d ? x ( isTimeSeries ? parseDate ( d . end ) : d . end ) : width ) ;
}
return end < start ? 0 : end - start ;
}
function regionHeight ( d ) {
var start = regionY ( d ) , end , yScale = d . axis === 'y' ? y : y2 ;
if ( d . axis === 'y' || d . axis === 'y2' ) {
end = _ _axis _rotated ? height : ( 'start' in d ? yScale ( d . start ) : height ) ;
} else {
end = _ _axis _rotated ? ( 'end' in d ? x ( isTimeSeries ? parseDate ( d . end ) : d . end ) : height ) : height ;
}
return end < start ? 0 : end - start ;
}
//-- Data --//
function isX ( key ) {
return ( _ _data _x && key === _ _data _x ) || ( notEmpty ( _ _data _xs ) && hasValue ( _ _data _xs , key ) ) ;
}
function isNotX ( key ) {
return ! isX ( key ) ;
}
function getXKey ( id ) {
return _ _data _x ? _ _data _x : notEmpty ( _ _data _xs ) ? _ _data _xs [ id ] : null ;
}
function getXValuesOfXKey ( key , targets ) {
var xValues , ids = targets && notEmpty ( targets ) ? mapToIds ( targets ) : [ ] ;
ids . forEach ( function ( id ) {
if ( getXKey ( id ) === key ) {
xValues = c3 . data . xs [ id ] ;
}
} ) ;
return xValues ;
}
function getXValue ( id , i ) {
return id in c3 . data . xs && c3 . data . xs [ id ] && c3 . data . xs [ id ] [ i ] ? c3 . data . xs [ id ] [ i ] : i ;
}
function addXs ( xs ) {
Object . keys ( xs ) . forEach ( function ( id ) {
_ _data _xs [ id ] = xs [ id ] ;
} ) ;
}
function isSingleX ( xs ) {
return d3 . set ( Object . keys ( xs ) . map ( function ( id ) { return xs [ id ] ; } ) ) . size ( ) === 1 ;
}
function addName ( data ) {
var name ;
if ( data ) {
name = _ _data _names [ data . id ] ;
data . name = name ? name : data . id ;
}
return data ;
}
function updateTargetX ( targets , x ) {
targets . forEach ( function ( t ) {
t . values . forEach ( function ( v , i ) {
v . x = generateTargetX ( x [ i ] , t . id , i ) ;
} ) ;
c3 . data . xs [ t . id ] = x ;
} ) ;
}
function updateTargetXs ( targets , xs ) {
targets . forEach ( function ( t ) {
if ( xs [ t . id ] ) {
updateTargetX ( [ t ] , xs [ t . id ] ) ;
}
} ) ;
}
function generateTargetX ( rawX , id , index ) {
var x ;
if ( isTimeSeries ) {
x = rawX ? rawX instanceof Date ? rawX : parseDate ( rawX ) : parseDate ( getXValue ( id , index ) ) ;
}
else if ( isCustomX && ! isCategorized ) {
x = isValue ( rawX ) ? + rawX : getXValue ( id , index ) ;
}
else {
x = index ;
}
return x ;
}
function convertRowsToData ( rows ) {
var keys = rows [ 0 ] , new _row = { } , new _rows = [ ] , i , j ;
for ( i = 1 ; i < rows . length ; i ++ ) {
new _row = { } ;
for ( j = 0 ; j < rows [ i ] . length ; j ++ ) {
new _row [ keys [ j ] ] = rows [ i ] [ j ] ;
}
new _rows . push ( new _row ) ;
}
return new _rows ;
}
function convertColumnsToData ( columns ) {
var new _rows = [ ] , i , j , key ;
for ( i = 0 ; i < columns . length ; i ++ ) {
key = columns [ i ] [ 0 ] ;
for ( j = 1 ; j < columns [ i ] . length ; j ++ ) {
if ( isUndefined ( new _rows [ j - 1 ] ) ) {
new _rows [ j - 1 ] = { } ;
}
new _rows [ j - 1 ] [ key ] = columns [ i ] [ j ] ;
}
}
return new _rows ;
}
function convertDataToTargets ( data ) {
var ids = d3 . keys ( data [ 0 ] ) . filter ( isNotX ) , xs = d3 . keys ( data [ 0 ] ) . filter ( isX ) , targets ;
// save x for update data by load when custom x and c3.x API
ids . forEach ( function ( id ) {
var xKey = getXKey ( id ) , idsForX ;
if ( isCustomX || isTimeSeries ) {
// if included in input data
if ( xs . indexOf ( xKey ) >= 0 ) {
c3 . data . xs [ id ] = data . map ( function ( d ) { return d [ xKey ] ; } ) . filter ( isValue ) . map ( function ( rawX , i ) { return generateTargetX ( rawX , id , i ) ; } ) ;
}
// if not included in input data, find from preloaded data of other id's x
else if ( _ _data _x ) {
idsForX = Object . keys ( c3 . data . xs ) ;
c3 . data . xs [ id ] = idsForX . length > 0 ? c3 . data . xs [ idsForX [ 0 ] ] : undefined ;
}
// if not included in input data, find from preloaded data
else if ( notEmpty ( _ _data _xs ) ) {
c3 . data . xs [ id ] = getXValuesOfXKey ( xKey , c3 . data . targets ) ;
}
// MEMO: if no x included, use same x of current will be used
} else {
c3 . data . xs [ id ] = data . map ( function ( d , i ) { return i ; } ) ;
}
} ) ;
// check x is defined
ids . forEach ( function ( id ) {
if ( ! c3 . data . xs [ id ] ) {
throw new Error ( 'x is not defined for id = "' + id + '".' ) ;
}
} ) ;
// convert to target
targets = ids . map ( function ( id , index ) {
var convertedId = _ _data _id _converter ( id ) ;
return {
id : convertedId ,
id _org : id ,
values : data . map ( function ( d , i ) {
var xKey = getXKey ( id ) , rawX = d [ xKey ] , x = generateTargetX ( rawX , id , i ) ;
// use x as categories if custom x and categorized
if ( isCustomX && isCategorized && index === 0 && rawX ) {
if ( i === 0 ) { _ _axis _x _categories = [ ] ; }
_ _axis _x _categories . push ( rawX ) ;
}
// mark as x = undefined if value is undefined and filter to remove after mapped
if ( typeof d [ id ] === 'undefined' || c3 . data . xs [ id ] . length <= i ) {
x = undefined ;
}
return { x : x , value : d [ id ] !== null && ! isNaN ( d [ id ] ) ? + d [ id ] : null , id : convertedId } ;
} ) . filter ( function ( v ) { return typeof v . x !== 'undefined' ; } )
} ;
} ) ;
// finish targets
targets . forEach ( function ( t ) {
var i ;
// sort values by its x
t . values = t . values . sort ( function ( v1 , v2 ) {
var x1 = v1 . x || v1 . x === 0 ? v1 . x : Infinity ,
x2 = v2 . x || v2 . x === 0 ? v2 . x : Infinity ;
return x1 - x2 ;
} ) ;
// indexing each value
i = 0 ;
t . values . forEach ( function ( v ) {
v . index = i ++ ;
} ) ;
} ) ;
// set target types
if ( _ _data _type ) {
setTargetType ( mapToIds ( targets ) . filter ( function ( id ) { return ! ( id in _ _data _types ) ; } ) , _ _data _type ) ;
}
// cache as original id keyed
targets . forEach ( function ( d ) {
addCache ( d . id _org , d ) ;
} ) ;
return targets ;
}
function cloneTarget ( target ) {
return {
id : target . id ,
id _org : target . id _org ,
values : target . values . map ( function ( d ) {
return { x : d . x , value : d . value , id : d . id } ;
} )
} ;
}
function getPrevX ( i ) {
return i > 0 && c3 . data . targets [ 0 ] . values [ i - 1 ] ? c3 . data . targets [ 0 ] . values [ i - 1 ] . x : undefined ;
}
function getNextX ( i ) {
return i < getMaxDataCount ( ) - 1 ? c3 . data . targets [ 0 ] . values [ i + 1 ] . x : undefined ;
}
function getMaxDataCount ( ) {
return d3 . max ( c3 . data . targets , function ( t ) { return t . values . length ; } ) ;
}
function getMaxDataCountTarget ( targets ) {
var length = targets . length , max = 0 , maxTarget ;
if ( length > 1 ) {
targets . forEach ( function ( t ) {
if ( t . values . length > max ) {
maxTarget = t ;
max = t . values . length ;
}
} ) ;
} else {
maxTarget = length ? targets [ 0 ] : null ;
}
return maxTarget ;
}
function getEdgeX ( targets ) {
var target = getMaxDataCountTarget ( targets ) , firstData , lastData ;
if ( ! target ) {
return [ 0 , 0 ] ;
}
firstData = target . values [ 0 ] , lastData = target . values [ target . values . length - 1 ] ;
return [ firstData . x , lastData . x ] ;
}
function mapToIds ( targets ) {
return targets . map ( function ( d ) { return d . id ; } ) ;
}
function mapToTargetIds ( ids ) {
return ids ? ( typeof ids === 'string' ? [ ids ] : ids ) : mapToIds ( c3 . data . targets ) ;
}
function hasTarget ( targets , id ) {
var ids = mapToIds ( targets ) , i ;
for ( i = 0 ; i < ids . length ; i ++ ) {
if ( ids [ i ] === id ) {
return true ;
}
}
return false ;
}
function isTargetToShow ( targetId ) {
return hiddenTargetIds . indexOf ( targetId ) < 0 ;
}
function isLegendToShow ( targetId ) {
return hiddenLegendIds . indexOf ( targetId ) < 0 ;
}
function filterTargetsToShow ( targets ) {
return targets . filter ( function ( t ) { return isTargetToShow ( t . id ) ; } ) ;
}
function mapTargetsToUniqueXs ( targets ) {
var xs = d3 . set ( d3 . merge ( targets . map ( function ( t ) { return t . values . map ( function ( v ) { return v . x ; } ) ; } ) ) ) . values ( ) ;
return isTimeSeries ? xs . map ( function ( x ) { return new Date ( x ) ; } ) : xs . map ( function ( x ) { return + x ; } ) ;
}
function generateTickValues ( xs , tickCount ) {
var tickValues = xs , targetCount , start , end , count , interval , i , tickValue ;
if ( tickCount ) {
targetCount = typeof tickCount === 'function' ? tickCount ( ) : tickCount ;
// compute ticks according to __axis_x_tick_count
if ( targetCount === 1 ) {
tickValues = [ xs [ 0 ] ] ;
} else if ( targetCount === 2 ) {
tickValues = [ xs [ 0 ] , xs [ xs . length - 1 ] ] ;
} else if ( targetCount > 2 ) {
count = targetCount - 2 ;
start = xs [ 0 ] ;
end = xs [ xs . length - 1 ] ;
interval = ( end - start ) / ( count + 1 ) ;
// re-construct uniqueXs
tickValues = [ start ] ;
for ( i = 0 ; i < count ; i ++ ) {
tickValue = + start + interval * ( i + 1 ) ;
tickValues . push ( isTimeSeries ? new Date ( tickValue ) : tickValue ) ;
}
tickValues . push ( end ) ;
}
}
if ( ! isTimeSeries ) { tickValues = tickValues . sort ( function ( a , b ) { return a - b ; } ) ; }
return tickValues ;
}
function addHiddenTargetIds ( targetIds ) {
hiddenTargetIds = hiddenTargetIds . concat ( targetIds ) ;
}
function removeHiddenTargetIds ( targetIds ) {
hiddenTargetIds = hiddenTargetIds . filter ( function ( id ) { return targetIds . indexOf ( id ) < 0 ; } ) ;
}
function addHiddenLegendIds ( targetIds ) {
hiddenLegendIds = hiddenLegendIds . concat ( targetIds ) ;
}
function removeHiddenLegendIds ( targetIds ) {
hiddenLegendIds = hiddenLegendIds . filter ( function ( id ) { return targetIds . indexOf ( id ) < 0 ; } ) ;
}
function getValuesAsIdKeyed ( targets ) {
var ys = { } ;
targets . forEach ( function ( t ) {
ys [ t . id ] = [ ] ;
t . values . forEach ( function ( v ) {
ys [ t . id ] . push ( v . value ) ;
} ) ;
} ) ;
return ys ;
}
function checkValueInTargets ( targets , checker ) {
var ids = Object . keys ( targets ) , i , j , values ;
for ( i = 0 ; i < ids . length ; i ++ ) {
values = targets [ ids [ i ] ] . values ;
for ( j = 0 ; j < values . length ; j ++ ) {
if ( checker ( values [ j ] . value ) ) {
return true ;
}
}
}
return false ;
}
function hasNegativeValueInTargets ( targets ) {
return checkValueInTargets ( targets , function ( v ) { return v < 0 ; } ) ;
}
function hasPositiveValueInTargets ( targets ) {
return checkValueInTargets ( targets , function ( v ) { return v > 0 ; } ) ;
}
function categoryName ( i ) {
return i < _ _axis _x _categories . length ? _ _axis _x _categories [ i ] : i ;
}
function generateClass ( prefix , targetId ) {
return " " + prefix + " " + prefix + getTargetSelectorSuffix ( targetId ) ;
}
function classText ( d ) { return generateClass ( CLASS . text , d . id ) ; }
function classTexts ( d ) { return generateClass ( CLASS . texts , d . id ) ; }
function classShape ( d , i ) { return generateClass ( CLASS . shape , i ) ; }
function classShapes ( d ) { return generateClass ( CLASS . shapes , d . id ) ; }
function classLine ( d ) { return classShape ( d ) + generateClass ( CLASS . line , d . id ) ; }
function classLines ( d ) { return classShapes ( d ) + generateClass ( CLASS . lines , d . id ) ; }
function classCircle ( d , i ) { return classShape ( d , i ) + generateClass ( CLASS . circle , i ) ; }
function classCircles ( d ) { return classShapes ( d ) + generateClass ( CLASS . circles , d . id ) ; }
function classBar ( d , i ) { return classShape ( d , i ) + generateClass ( CLASS . bar , i ) ; }
function classBars ( d ) { return classShapes ( d ) + generateClass ( CLASS . bars , d . id ) ; }
function classArc ( d ) { return classShape ( d . data ) + generateClass ( CLASS . arc , d . data . id ) ; }
function classArcs ( d ) { return classShapes ( d . data ) + generateClass ( CLASS . arcs , d . data . id ) ; }
function classArea ( d ) { return classShape ( d ) + generateClass ( CLASS . area , d . id ) ; }
function classAreas ( d ) { return classShapes ( d ) + generateClass ( CLASS . areas , d . id ) ; }
function classRegion ( d , i ) { return generateClass ( CLASS . region , i ) + ' ' + ( 'class' in d ? d . class : '' ) ; }
function classEvent ( d , i ) { return generateClass ( CLASS . eventRect , i ) ; }
function classStep ( d ) { return classShape ( d ) + generateClass ( CLASS . step , d . id ) ; }
function classSteps ( d ) { return classShapes ( d ) + generateClass ( CLASS . steps , d . id ) ; }
function classTarget ( id ) {
var additionalClassSuffix = _ _data _classes [ id ] , additionalClass = '' ;
if ( additionalClassSuffix ) {
additionalClass = ' ' + CLASS . target + '-' + additionalClassSuffix ;
}
return generateClass ( CLASS . target , id ) + additionalClass ;
}
function classChartText ( d ) { return CLASS . chartText + classTarget ( d . id ) ; }
function classChartLine ( d ) { return CLASS . chartLine + classTarget ( d . id ) ; }
function classChartBar ( d ) { return CLASS . chartBar + classTarget ( d . id ) ; }
function classChartArc ( d ) { return CLASS . chartArc + classTarget ( d . data . id ) ; }
function getTargetSelectorSuffix ( targetId ) {
return targetId || targetId === 0 ? '-' + ( targetId . replace ? targetId . replace ( /([^a-zA-Z0-9-_])/g , '-' ) : targetId ) : '' ;
}
function selectorTarget ( id ) { return '.' + CLASS . target + getTargetSelectorSuffix ( id ) ; }
function selectorTargets ( ids ) { return ids . length ? ids . map ( function ( id ) { return selectorTarget ( id ) ; } ) : null ; }
function selectorLegend ( id ) { return '.' + CLASS . legendItem + getTargetSelectorSuffix ( id ) ; }
function selectorLegends ( ids ) { return ids . length ? ids . map ( function ( id ) { return selectorLegend ( id ) ; } ) : null ; }
function initialOpacity ( d ) {
return d . value !== null && withoutFadeIn [ d . id ] ? 1 : 0 ;
}
function initialOpacityForText ( d ) {
var targetOpacity = opacityForText ( d ) ;
return initialOpacity ( d ) * targetOpacity ;
}
function opacityForCircle ( d ) {
return isValue ( d . value ) ? isScatterType ( d ) ? 0.5 : 1 : 0 ;
}
function opacityForText ( ) {
return hasDataLabel ( ) ? 1 : 0 ;
}
function hasDataLabel ( ) {
if ( typeof _ _data _labels === 'boolean' && _ _data _labels ) {
return true ;
} else if ( typeof _ _data _labels === 'object' && notEmpty ( _ _data _labels ) ) {
return true ;
}
return false ;
}
function getDataLabelWidth ( min , max ) {
var widths = [ ] , paddingCoef = 1.3 ;
selectChart . select ( 'svg' ) . selectAll ( '.dummy' )
. data ( [ min , max ] )
. enter ( ) . append ( 'text' )
. text ( function ( d ) { return formatByAxisId ( d . id ) ( d . value , d . id ) ; } )
. each ( function ( d , i ) { widths [ i ] = this . getBoundingClientRect ( ) . width * paddingCoef ; } )
. remove ( ) ;
return widths ;
}
function getYFormat ( forArc ) {
var formatForY = forArc && ! hasGaugeType ( c3 . data . targets ) ? defaultArcValueFormat : yFormat ,
formatForY2 = forArc && ! hasGaugeType ( c3 . data . targets ) ? defaultArcValueFormat : y2Format ;
return function ( v , ratio , id ) {
var format = getAxisId ( id ) === 'y2' ? formatForY2 : formatForY ;
return format ( v , ratio ) ;
} ;
}
function yFormat ( v ) {
var format = _ _axis _y _tick _format ? _ _axis _y _tick _format : defaultValueFormat ;
return format ( v ) ;
}
function y2Format ( v ) {
var format = _ _axis _y2 _tick _format ? _ _axis _y2 _tick _format : defaultValueFormat ;
return format ( v ) ;
}
function defaultValueFormat ( v ) {
return isValue ( v ) ? + v : "" ;
}
function defaultArcValueFormat ( v , ratio ) {
return ( ratio * 100 ) . toFixed ( 1 ) + '%' ;
}
function formatByAxisId ( id ) {
var defaultFormat = function ( v ) { return isValue ( v ) ? + v : "" ; } , axisId = getAxisId ( id ) , format = defaultFormat ;
// find format according to axis id
if ( typeof _ _data _labels . format === 'function' ) {
format = _ _data _labels . format ;
} else if ( typeof _ _data _labels . format === 'object' ) {
if ( typeof _ _data _labels . format [ axisId ] === 'function' ) {
format = _ _data _labels . format [ axisId ] ;
}
}
return format ;
}
function xx ( d ) {
return d ? x ( d . x ) : null ;
}
function xv ( d ) {
return Math . ceil ( x ( isTimeSeries ? parseDate ( d . value ) : d . value ) ) ;
}
function yv ( d ) {
var yScale = d . axis && d . axis === 'y2' ? y2 : y ;
return Math . ceil ( yScale ( d . value ) ) ;
}
function subxx ( d ) {
return d ? subX ( d . x ) : null ;
}
function findSameXOfValues ( values , index ) {
var i , targetX = values [ index ] . x , sames = [ ] ;
for ( i = index - 1 ; i >= 0 ; i -- ) {
if ( targetX !== values [ i ] . x ) { break ; }
sames . push ( values [ i ] ) ;
}
for ( i = index ; i < values . length ; i ++ ) {
if ( targetX !== values [ i ] . x ) { break ; }
sames . push ( values [ i ] ) ;
}
return sames ;
}
function findClosestOfValues ( values , pos , _min , _max ) { // MEMO: values must be sorted by x
var min = _min ? _min : 0 ,
max = _max ? _max : values . length - 1 ,
med = Math . floor ( ( max - min ) / 2 ) + min ,
value = values [ med ] ,
diff = x ( value . x ) - pos [ _ _axis _rotated ? 1 : 0 ] ,
candidates ;
// Update range for search
diff > 0 ? max = med : min = med ;
// if candidates are two closest min and max, stop recursive call
if ( ( max - min ) === 1 || ( min === 0 && max === 0 ) ) {
// Get candidates that has same min and max index
candidates = [ ] ;
if ( values [ min ] . x || values [ min ] . x === 0 ) {
candidates = candidates . concat ( findSameXOfValues ( values , min ) ) ;
}
if ( values [ max ] . x || values [ max ] . x === 0 ) {
candidates = candidates . concat ( findSameXOfValues ( values , max ) ) ;
}
// Determine the closest and return
return findClosest ( candidates , pos ) ;
}
return findClosestOfValues ( values , pos , min , max ) ;
}
function findClosestFromTargets ( targets , pos ) {
var candidates ;
// map to array of closest points of each target
candidates = targets . map ( function ( target ) {
return findClosestOfValues ( target . values , pos ) ;
} ) ;
// decide closest point and return
return findClosest ( candidates , pos ) ;
}
function findClosest ( values , pos ) {
var minDist , closest ;
values . forEach ( function ( v ) {
var d = dist ( v , pos ) ;
if ( d < minDist || ! minDist ) {
minDist = d ;
closest = v ;
}
} ) ;
return closest ;
}
function filterSameX ( targets , x ) {
return d3 . merge ( targets . map ( function ( t ) { return t . values ; } ) ) . filter ( function ( v ) { return v . x - x === 0 ; } ) ;
}
function getPathBox ( path ) {
var box = path . getBoundingClientRect ( ) ,
items = [ path . pathSegList . getItem ( 0 ) , path . pathSegList . getItem ( 1 ) ] ,
minX = items [ 0 ] . x , minY = Math . min ( items [ 0 ] . y , items [ 1 ] . y ) ;
return { x : minX , y : minY , width : box . width , height : box . height } ;
}
function isOrderDesc ( ) {
return _ _data _order && _ _data _order . toLowerCase ( ) === 'desc' ;
}
function isOrderAsc ( ) {
return _ _data _order && _ _data _order . toLowerCase ( ) === 'asc' ;
}
function orderTargets ( targets ) {
var orderAsc = isOrderAsc ( ) , orderDesc = isOrderDesc ( ) ;
if ( orderAsc || orderDesc ) {
targets . sort ( function ( t1 , t2 ) {
var reducer = function ( p , c ) { return p + Math . abs ( c . value ) ; } ;
var t1Sum = t1 . values . reduce ( reducer , 0 ) ,
t2Sum = t2 . values . reduce ( reducer , 0 ) ;
return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum ;
} ) ;
} else if ( typeof _ _data _order === 'function' ) {
targets . sort ( _ _data _order ) ;
} // TODO: accept name array for order
return targets ;
}
//-- Tooltip --//
function showTooltip ( selectedData , mouse ) {
var tWidth , tHeight , svgLeft , tooltipLeft , tooltipRight , tooltipTop , chartRight ;
var forArc = hasArcType ( c3 . data . targets ) ,
dataToShow = selectedData . filter ( function ( d ) { return d && isValue ( d . value ) ; } ) ;
if ( dataToShow . length === 0 || ! _ _tooltip _show ) {
return ;
}
tooltip . html ( _ _tooltip _contents ( selectedData , getXAxisTickFormat ( ) , getYFormat ( forArc ) , color ) ) . style ( "display" , "block" ) ;
// Get tooltip dimensions
tWidth = tooltip . property ( 'offsetWidth' ) ;
tHeight = tooltip . property ( 'offsetHeight' ) ;
// Determin tooltip position
if ( forArc ) {
tooltipLeft = ( width / 2 ) + mouse [ 0 ] ;
tooltipTop = ( height / 2 ) + mouse [ 1 ] + 20 ;
} else {
if ( _ _axis _rotated ) {
svgLeft = getSvgLeft ( ) ;
tooltipLeft = svgLeft + mouse [ 0 ] + 100 ;
tooltipRight = tooltipLeft + tWidth ;
chartRight = getCurrentWidth ( ) - getCurrentPaddingRight ( ) ;
tooltipTop = x ( dataToShow [ 0 ] . x ) + 20 ;
} else {
svgLeft = getSvgLeft ( ) ;
tooltipLeft = svgLeft + getCurrentPaddingLeft ( ) + x ( dataToShow [ 0 ] . x ) + 20 ;
tooltipRight = tooltipLeft + tWidth ;
chartRight = svgLeft + getCurrentWidth ( ) - getCurrentPaddingRight ( ) ;
tooltipTop = mouse [ 1 ] + 15 ;
}
if ( tooltipRight > chartRight ) {
tooltipLeft -= tooltipRight - chartRight ;
}
if ( tooltipTop + tHeight > getCurrentHeight ( ) ) {
tooltipTop -= tHeight + 30 ;
}
}
// Set tooltip
tooltip
. style ( "top" , tooltipTop + "px" )
. style ( "left" , tooltipLeft + 'px' ) ;
}
function hideTooltip ( ) {
tooltip . style ( "display" , "none" ) ;
}
function showXGridFocus ( selectedData ) {
var dataToShow = selectedData . filter ( function ( d ) { return d && isValue ( d . value ) ; } ) ;
if ( ! _ _tooltip _show ) { return ; }
// Hide when scatter plot exists
if ( hasScatterType ( c3 . data . targets ) || hasArcType ( c3 . data . targets ) ) { return ; }
var focusEl = main . selectAll ( 'line.' + CLASS . xgridFocus ) ;
focusEl
. style ( "visibility" , "visible" )
. data ( [ dataToShow [ 0 ] ] )
. attr ( _ _axis _rotated ? 'y1' : 'x1' , xx )
. attr ( _ _axis _rotated ? 'y2' : 'x2' , xx ) ;
smoothLines ( focusEl , 'grid' ) ;
}
function hideXGridFocus ( ) {
main . select ( 'line.' + CLASS . xgridFocus ) . style ( "visibility" , "hidden" ) ;
}
function generateGridData ( type , scale ) {
var gridData = [ ] , xDomain , firstYear , lastYear , i ,
tickNum = main . select ( "." + CLASS . axisX ) . selectAll ( '.tick' ) . size ( ) ;
if ( type === 'year' ) {
xDomain = getXDomain ( ) ;
firstYear = xDomain [ 0 ] . getFullYear ( ) ;
lastYear = xDomain [ 1 ] . getFullYear ( ) ;
for ( i = firstYear ; i <= lastYear ; i ++ ) {
gridData . push ( new Date ( i + '-01-01 00:00:00' ) ) ;
}
} else {
gridData = scale . ticks ( 10 ) ;
}
return gridData . slice ( 0 , tickNum ) ;
}
//-- Circle --//
function circleX ( d ) {
return d . x || d . x === 0 ? x ( d . x ) : null ;
}
function circleY ( d , i ) {
var lineIndices = getLineIndices ( ) , getPoint = generateGetLinePoint ( lineIndices ) ;
if ( _ _data _groups . length > 0 ) { var point = getPoint ( d , i ) ; return point [ 0 ] [ 1 ] ; }
return getYScale ( d . id ) ( d . value ) ;
}
//-- Bar --//
function getBarIndices ( ) {
var indices = { } , i = 0 , j , k ;
filterTargetsToShow ( c3 . data . targets . filter ( isBarType ) ) . forEach ( function ( d ) {
for ( j = 0 ; j < _ _data _groups . length ; j ++ ) {
if ( _ _data _groups [ j ] . indexOf ( d . id ) < 0 ) { continue ; }
for ( k = 0 ; k < _ _data _groups [ j ] . length ; k ++ ) {
if ( _ _data _groups [ j ] [ k ] in indices ) {
indices [ d . id ] = indices [ _ _data _groups [ j ] [ k ] ] ;
break ;
}
}
}
if ( isUndefined ( indices [ d . id ] ) ) { indices [ d . id ] = i ++ ; }
} ) ;
indices . _ _max _ _ = i - 1 ;
return indices ;
}
function getBarX ( barW , barTargetsNum , barIndices , isSub ) {
var scale = isSub ? subX : x ;
return function ( d ) {
var barIndex = d . id in barIndices ? barIndices [ d . id ] : 0 ;
return d . x || d . x === 0 ? scale ( d . x ) - barW * ( barTargetsNum / 2 - barIndex ) : 0 ;
} ;
}
function getBarY ( isSub ) {
return function ( d ) {
var scale = isSub ? getSubYScale ( d . id ) : getYScale ( d . id ) ;
return scale ( d . value ) ;
} ;
}
function getBarOffset ( barIndices , isSub ) {
var targets = orderTargets ( filterTargetsToShow ( c3 . data . targets . filter ( isBarType ) ) ) ,
targetIds = targets . map ( function ( t ) { return t . id ; } ) ;
return function ( d , i ) {
var scale = isSub ? getSubYScale ( d . id ) : getYScale ( d . id ) ,
y0 = scale ( 0 ) , offset = y0 ;
targets . forEach ( function ( t ) {
if ( t . id === d . id || barIndices [ t . id ] !== barIndices [ d . id ] ) { return ; }
if ( targetIds . indexOf ( t . id ) < targetIds . indexOf ( d . id ) && t . values [ i ] . value * d . value > 0 ) {
offset += scale ( t . values [ i ] . value ) - y0 ;
}
} ) ;
return offset ;
} ;
}
function getBarW ( axis , barTargetsNum ) {
return typeof _ _bar _width === 'number' ? _ _bar _width : barTargetsNum ? ( axis . tickOffset ( ) * 2 * _ _bar _width _ratio ) / barTargetsNum : 0 ;
}
//-- Area --//
function getAreaIndices ( ) { // replication of getBarIndices
var indices = { } , i = 0 , j , k ;
filterTargetsToShow ( c3 . data . targets . filter ( isAreaType ) ) . forEach ( function ( d ) {
for ( j = 0 ; j < _ _data _groups . length ; j ++ ) {
if ( _ _data _groups [ j ] . indexOf ( d . id ) < 0 ) { continue ; }
for ( k = 0 ; k < _ _data _groups [ j ] . length ; k ++ ) {
if ( _ _data _groups [ j ] [ k ] in indices ) {
indices [ d . id ] = indices [ _ _data _groups [ j ] [ k ] ] ;
break ;
}
}
}
if ( isUndefined ( indices [ d . id ] ) ) { indices [ d . id ] = i ++ ; }
} ) ;
indices . _ _max _ _ = i - 1 ;
return indices ;
}
function getAreaX ( areaTargetsNum , areaIndices , isSub ) { // partial duplication of getBarX
var scale = isSub ? subX : x ;
return function ( d ) {
var areaIndex = d . id in areaIndices ? areaIndices [ d . id ] : 0 ;
return d . x || d . x === 0 ? scale ( d . x ) - 0 * ( areaTargetsNum / 2 - areaIndex ) : 0 ;
} ;
}
function getAreaY ( isSub ) { // replication of getBarY
return function ( d ) {
var scale = isSub ? getSubYScale ( d . id ) : getYScale ( d . id ) ;
return scale ( d . value ) ;
} ;
}
function getAreaOffset ( areaIndices , isSub ) { // partial duplication of getBarOffset
var targets = orderTargets ( filterTargetsToShow ( c3 . data . targets . filter ( isAreaType ) ) ) ,
targetIds = targets . map ( function ( t ) { return t . id ; } ) ;
return function ( d , i ) {
var scale = isSub ? getSubYScale ( d . id ) : getYScale ( d . id ) ,
y0 = scale ( 0 ) , offset = y0 ;
targets . forEach ( function ( t ) {
if ( t . id === d . id || areaIndices [ t . id ] !== areaIndices [ d . id ] ) { return ; }
if ( targetIds . indexOf ( t . id ) < targetIds . indexOf ( d . id ) && t . values [ i ] . value > 0 ) {
offset += scale ( t . values [ i ] . value ) - y0 ;
}
} ) ;
return offset ;
} ;
}
//-- Line --//
function getLineIndices ( ) { // replication of getBarIndices
var indices = { } , i = 0 , j , k ;
filterTargetsToShow ( c3 . data . targets . filter ( isLineType ) ) . forEach ( function ( d ) {
for ( j = 0 ; j < _ _data _groups . length ; j ++ ) {
if ( _ _data _groups [ j ] . indexOf ( d . id ) < 0 ) { continue ; }
for ( k = 0 ; k < _ _data _groups [ j ] . length ; k ++ ) {
if ( _ _data _groups [ j ] [ k ] in indices ) {
indices [ d . id ] = indices [ _ _data _groups [ j ] [ k ] ] ;
break ;
}
}
}
if ( isUndefined ( indices [ d . id ] ) ) { indices [ d . id ] = i ++ ; }
} ) ;
indices . _ _max _ _ = i - 1 ;
return indices ;
}
function getLineX ( lineTargetsNum , lineIndices , isSub ) { // partial duplication of getBarX
var scale = isSub ? subX : x ;
return function ( d ) {
var lineIndex = d . id in lineIndices ? lineIndices [ d . id ] : 0 ;
return d . x || d . x === 0 ? scale ( d . x ) - 0 * ( lineTargetsNum / 2 - lineIndex ) : 0 ;
} ;
}
function getLineY ( isSub ) { // replication of getBarY
return function ( d ) {
var scale = isSub ? getSubYScale ( d . id ) : getYScale ( d . id ) ;
return scale ( d . value ) ;
} ;
}
function getLineOffset ( lineIndices , isSub ) { // partial duplication of getBarOffset
var targets = orderTargets ( filterTargetsToShow ( c3 . data . targets . filter ( isLineType ) ) ) ,
targetIds = targets . map ( function ( t ) { return t . id ; } ) ;
return function ( d , i ) {
var scale = isSub ? getSubYScale ( d . id ) : getYScale ( d . id ) ,
y0 = scale ( 0 ) , offset = y0 ;
targets . forEach ( function ( t ) {
if ( t . id === d . id || lineIndices [ t . id ] !== lineIndices [ d . id ] ) { return ; }
if ( targetIds . indexOf ( t . id ) < targetIds . indexOf ( d . id ) && t . values [ i ] . value > 0 ) {
offset += scale ( t . values [ i ] . value ) - y0 ;
}
} ) ;
return offset ;
} ;
}
//-- Type --//
function setTargetType ( targetIds , type ) {
mapToTargetIds ( targetIds ) . forEach ( function ( id ) {
withoutFadeIn [ id ] = ( type === _ _data _types [ id ] ) ;
_ _data _types [ id ] = type ;
} ) ;
if ( ! targetIds ) {
_ _data _type = type ;
}
}
function hasType ( targets , type ) {
var has = false ;
targets . forEach ( function ( t ) {
if ( _ _data _types [ t . id ] === type ) { has = true ; }
if ( ! ( t . id in _ _data _types ) && type === 'line' ) { has = true ; }
} ) ;
return has ;
}
/ * n o t u s e d
function hasLineType ( targets ) {
return hasType ( targets , 'line' ) ;
}
* /
function hasBarType ( targets ) {
return hasType ( targets , 'bar' ) ;
}
function hasScatterType ( targets ) {
return hasType ( targets , 'scatter' ) ;
}
function hasPieType ( targets ) {
return _ _data _type === 'pie' || hasType ( targets , 'pie' ) ;
}
function hasGaugeType ( targets ) {
return hasType ( targets , 'gauge' ) ;
}
function hasDonutType ( targets ) {
return _ _data _type === 'donut' || hasType ( targets , 'donut' ) ;
}
function hasArcType ( targets ) {
return hasPieType ( targets ) || hasDonutType ( targets ) || hasGaugeType ( targets ) ;
}
function isLineType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return ! ( id in _ _data _types ) || _ _data _types [ id ] === 'line' || _ _data _types [ id ] === 'spline' || _ _data _types [ id ] === 'area' || _ _data _types [ id ] === 'area-spline' ;
}
function isStepType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return ! ( id in _ _data _types ) || _ _data _types [ id ] === 'step' || _ _data _types [ id ] === 'area-step' ;
}
function isSplineType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return _ _data _types [ id ] === 'spline' || _ _data _types [ id ] === 'area-spline' ;
}
function isAreaType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return _ _data _types [ id ] === 'area' ;
}
function isBarType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return _ _data _types [ id ] === 'bar' ;
}
function isScatterType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return _ _data _types [ id ] === 'scatter' ;
}
function isPieType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return _ _data _types [ id ] === 'pie' ;
}
function isGaugeType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return _ _data _types [ id ] === 'gauge' ;
}
function isDonutType ( d ) {
var id = ( typeof d === 'string' ) ? d : d . id ;
return _ _data _types [ id ] === 'donut' ;
}
function isArcType ( d ) {
return isPieType ( d ) || isDonutType ( d ) || isGaugeType ( d ) ;
}
function lineData ( d ) {
return isLineType ( d ) ? [ d ] : [ ] ;
}
function stepData ( d ) {
return isStepType ( d ) ? [ d ] : [ ] ;
}
function arcData ( d ) {
return isArcType ( d . data ) ? [ d ] : [ ] ;
}
/ * n o t u s e d
function scatterData ( d ) {
return isScatterType ( d ) ? d . values : [ ] ;
}
* /
function barData ( d ) {
return isBarType ( d ) ? d . values : [ ] ;
}
function lineOrScatterData ( d ) {
return isLineType ( d ) || isScatterType ( d ) ? d . values : [ ] ;
}
function lineOrStepData ( d ) {
return isLineType ( d ) || isStepType ( d ) ? [ d ] : [ ] ;
}
function barOrLineData ( d ) {
return isBarType ( d ) || isLineType ( d ) ? d . values : [ ] ;
}
function shouldExpand ( id ) {
return ( isDonutType ( id ) && _ _donut _expand ) || ( isPieType ( id ) && _ _pie _expand ) ;
}
//-- Color --//
function generateColor ( colors , pattern , callback ) {
var ids = [ ] ;
return function ( d ) {
var id = d . id || d , color ;
// if callback function is provided
if ( colors [ id ] instanceof Function ) {
color = colors [ id ] ( d ) ;
}
// if specified, choose that color
else if ( colors [ id ] ) {
color = colors [ id ] ;
}
// if not specified, choose from pattern
else {
if ( ids . indexOf ( id ) < 0 ) { ids . push ( id ) ; }
color = pattern [ ids . indexOf ( id ) % pattern . length ] ;
}
return callback instanceof Function ? callback ( color , d ) : color ;
} ;
}
function generateLevelColor ( _colors , _values ) {
var colors = _colors ,
levels = _values ;
return function ( value ) {
for ( var a = 1 ; a < levels . length ; a ++ ) {
if ( levels [ 0 ] === 'percentage' && ( ( value / _ _gauge _max ) * 100 ) < levels [ a ] ) {
return colors [ a - 1 ] ;
}
if ( levels [ 0 ] === 'whole' && value < levels [ a ] ) {
return colors [ a - 1 ] ;
}
}
return colors [ colors . length - 1 ] ;
} ;
}
//-- Date --//
function parseDate ( date ) {
var parsedDate ;
try {
parsedDate = _ _data _x _format ? d3 . time . format ( _ _data _x _format ) . parse ( date ) : new Date ( date ) ;
} catch ( e ) {
window . console . error ( "Failed to parse x '" + date + "' to Date with format " + _ _data _x _format ) ;
}
return parsedDate ;
}
//-- Util --//
function isWithinCircle ( _this , _r ) {
var mouse = d3 . mouse ( _this ) , d3 _this = d3 . select ( _this ) ;
var cx = d3 _this . attr ( "cx" ) * 1 , cy = d3 _this . attr ( "cy" ) * 1 ;
return Math . sqrt ( Math . pow ( cx - mouse [ 0 ] , 2 ) + Math . pow ( cy - mouse [ 1 ] , 2 ) ) < _r ;
}
function isWithinBar ( _this ) {
var mouse = d3 . mouse ( _this ) , box = _this . getBoundingClientRect ( ) ,
seg0 = _this . pathSegList . getItem ( 0 ) , seg1 = _this . pathSegList . getItem ( 1 ) ;
var x = seg0 . x , y = Math . min ( seg0 . y , seg1 . y ) , w = box . width , h = box . height , offset = 2 ;
var sx = x - offset , ex = x + w + offset , sy = y + h + offset , ey = y - offset ;
return sx < mouse [ 0 ] && mouse [ 0 ] < ex && ey < mouse [ 1 ] && mouse [ 1 ] < sy ;
}
function isWithinRegions ( x , regions ) {
var i ;
for ( i = 0 ; i < regions . length ; i ++ ) {
if ( regions [ i ] . start < x && x <= regions [ i ] . end ) { return true ; }
}
return false ;
}
function isEmpty ( o ) {
return ! o || ( typeof o === 'string' && o . length === 0 ) || ( typeof o === 'object' && Object . keys ( o ) . length === 0 ) ;
}
function notEmpty ( o ) {
return Object . keys ( o ) . length > 0 ;
}
function hasValue ( dict , value ) {
var found = false ;
Object . keys ( dict ) . forEach ( function ( key ) {
if ( dict [ key ] === value ) { found = true ; }
} ) ;
return found ;
}
function dist ( data , pos ) {
var yScale = getAxisId ( data . id ) === 'y' ? y : y2 ,
xIndex = _ _axis _rotated ? 1 : 0 ,
yIndex = _ _axis _rotated ? 0 : 1 ;
return Math . pow ( x ( data . x ) - pos [ xIndex ] , 2 ) + Math . pow ( yScale ( data . value ) - pos [ yIndex ] , 2 ) ;
}
function endall ( transition , callback ) {
var n = 0 ;
transition
. each ( function ( ) { ++ n ; } )
. each ( "end" , function ( ) {
if ( ! -- n ) { callback . apply ( this , arguments ) ; }
} ) ;
}
function getOption ( options , key , defaultValue ) {
return isDefined ( options [ key ] ) ? options [ key ] : defaultValue ;
}
function ceil10 ( v ) {
return Math . ceil ( v / 10 ) * 10 ;
}
function getTextRect ( text , cls ) {
var rect ;
d3 . select ( 'body' ) . selectAll ( '.dummy' )
. data ( [ text ] )
. enter ( ) . append ( 'text' )
. classed ( cls ? cls : "" , true )
. text ( text )
. each ( function ( ) { rect = this . getBoundingClientRect ( ) ; } )
. remove ( ) ;
return rect ;
}
//-- Selection --//
function selectPoint ( target , d , i ) {
_ _data _onselected ( d , target . node ( ) ) ;
// add selected-circle on low layer g
main . select ( '.' + CLASS . selectedCircles + getTargetSelectorSuffix ( d . id ) ) . selectAll ( '.' + CLASS . selectedCircle + '-' + i )
. data ( [ d ] )
. enter ( ) . append ( 'circle' )
. attr ( "class" , function ( ) { return generateClass ( CLASS . selectedCircle , i ) ; } )
. attr ( "cx" , _ _axis _rotated ? circleY : circleX )
. attr ( "cy" , _ _axis _rotated ? circleX : circleY )
. attr ( "stroke" , function ( ) { return color ( d ) ; } )
. attr ( "r" , pointSelectR ( d ) * 1.4 )
. transition ( ) . duration ( 100 )
. attr ( "r" , pointSelectR ) ;
}
function unselectPoint ( target , d , i ) {
_ _data _onunselected ( d , target . node ( ) ) ;
// remove selected-circle from low layer g
main . select ( '.' + CLASS . selectedCircles + getTargetSelectorSuffix ( d . id ) ) . selectAll ( '.' + CLASS . selectedCircle + '-' + i )
. transition ( ) . duration ( 100 ) . attr ( 'r' , 0 )
. remove ( ) ;
}
function togglePoint ( selected , target , d , i ) {
selected ? selectPoint ( target , d , i ) : unselectPoint ( target , d , i ) ;
}
function selectBar ( target , d ) {
_ _data _onselected ( d , target . node ( ) ) ;
target . transition ( ) . duration ( 100 ) . style ( "fill" , function ( ) { return d3 . rgb ( color ( d ) ) . brighter ( 0.75 ) ; } ) ;
}
function unselectBar ( target , d ) {
_ _data _onunselected ( d , target . node ( ) ) ;
target . transition ( ) . duration ( 100 ) . style ( "fill" , function ( ) { return color ( d ) ; } ) ;
}
function toggleBar ( selected , target , d , i ) {
selected ? selectBar ( target , d , i ) : unselectBar ( target , d , i ) ;
}
function toggleArc ( selected , target , d , i ) {
toggleBar ( selected , target , d . data , i ) ;
}
function getToggle ( that ) {
// path selection not supported yet
return that . nodeName === 'circle' ? togglePoint : ( d3 . select ( that ) . classed ( CLASS . bar ) ? toggleBar : toggleArc ) ;
}
function filterRemoveNull ( data ) {
return data . filter ( function ( d ) { return isValue ( d . value ) ; } ) ;
}
//-- Point --//
function pointR ( d ) {
return _ _point _show ? ( typeof _ _point _r === 'function' ? _ _point _r ( d ) : _ _point _r ) : 0 ;
}
function pointExpandedR ( d ) {
return _ _point _focus _expand _enabled ? ( _ _point _focus _expand _r ? _ _point _focus _expand _r : pointR ( d ) * 1.75 ) : pointR ( d ) ;
}
function pointSelectR ( d ) {
return _ _point _select _r ? _ _point _select _r : pointR ( d ) * 4 ;
}
//-- Shape --//
function getCircles ( i , id ) {
return ( id ? main . selectAll ( '.' + CLASS . circles + getTargetSelectorSuffix ( id ) ) : main ) . selectAll ( '.' + CLASS . circle + ( isValue ( i ) ? '-' + i : '' ) ) ;
}
function expandCircles ( i , id ) {
getCircles ( i , id )
. classed ( CLASS . EXPANDED , true )
. attr ( 'r' , pointExpandedR ) ;
}
function unexpandCircles ( i ) {
getCircles ( i )
. filter ( function ( ) { return d3 . select ( this ) . classed ( CLASS . EXPANDED ) ; } )
. classed ( CLASS . EXPANDED , false )
. attr ( 'r' , pointR ) ;
}
function getBars ( i ) {
return main . selectAll ( '.' + CLASS . bar + ( isValue ( i ) ? '-' + i : '' ) ) ;
}
function expandBars ( i ) {
getBars ( i ) . classed ( CLASS . EXPANDED , true ) ;
}
function unexpandBars ( i ) {
getBars ( i ) . classed ( CLASS . EXPANDED , false ) ;
}
function generateDrawArea ( areaIndices , isSub ) {
var area ,
getPoint = generateGetAreaPoint ( areaIndices , isSub ) ,
yScaleGetter = isSub ? getSubYScale : getYScale ;
if ( _ _axis _rotated ) {
area = d3 . svg . area ( )
. x0 ( function ( d , i ) { return yScaleGetter ( d . id ) ( 0 ) ; } )
. x1 ( function ( d , i ) { return yScaleGetter ( d . id ) ( d . value ) ; } )
. y ( xx ) ;
} else {
area = d3 . svg . area ( )
. x ( xx )
. y0 ( function ( d , i ) { if ( _ _data _groups . length > 0 ) { var point = getPoint ( d , i ) ; return point [ 0 ] [ 1 ] ; } return yScaleGetter ( d . id ) ( 0 ) ; } )
. y1 ( function ( d , i ) { if ( _ _data _groups . length > 0 ) { var point = getPoint ( d , i ) ; return point [ 1 ] [ 1 ] ; } return yScaleGetter ( d . id ) ( d . value ) ; } ) ;
}
return function ( d , i ) {
var data = filterRemoveNull ( d . values ) , x0 , y0 ;
if ( hasType ( [ d ] , 'area' ) || hasType ( [ d ] , 'area-spline' ) ) {
isSplineType ( d ) ? area . interpolate ( "cardinal" ) : area . interpolate ( "linear" ) ;
return area ( data ) ;
} else if ( hasType ( [ d ] , 'area-step' ) ) {
isStepType ( d ) ? area . interpolate ( "step-after" ) : area . interpolate ( "linear" ) ;
return area ( data ) ;
} else {
x0 = x ( data [ 0 ] . x ) ;
y0 = getYScale ( d . id ) ( data [ 0 ] . value ) ;
return _ _axis _rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0 ;
}
} ;
}
function generateDrawLine ( lineIndices , isSub ) {
var getPoint = generateGetLinePoint ( lineIndices , isSub ) ,
yScaleGetter = isSub ? getSubYScale : getYScale ,
xValue = isSub ? subxx : xx ,
yValue = function ( d , i ) { if ( _ _data _groups . length > 0 ) { var point = getPoint ( d , i ) ; return point [ 0 ] [ 1 ] ; } return yScaleGetter ( d . id ) ( d . value ) ; } ,
line = d3 . svg . line ( )
. x ( _ _axis _rotated ? yValue : xValue )
. y ( _ _axis _rotated ? xValue : yValue ) ;
if ( ! _ _line _connect _null ) { line = line . defined ( function ( d ) { return d . value != null ; } ) ; }
return function ( d ) {
var data = _ _line _connect _null ? filterRemoveNull ( d . values ) : d . values ,
x = isSub ? x : subX , y = yScaleGetter ( d . id ) , x0 = 0 , y0 = 0 ;
if ( isLineType ( d ) ) {
if ( _ _data _regions [ d . id ] ) {
return lineWithRegions ( data , x , y , _ _data _regions [ d . id ] ) ;
} else {
line . interpolate ( isSplineType ( d ) ? "cardinal" : "linear" ) ;
return line ( data ) ;
}
} else if ( isStepType ( d ) ) {
line . interpolate ( "step-after" ) ;
return line ( data ) ;
} else {
if ( data [ 0 ] ) {
x0 = x ( data [ 0 ] . x ) ;
y0 = y ( data [ 0 ] . value ) ;
}
return _ _axis _rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0 ;
}
} ;
}
function generateDrawBar ( barIndices , isSub ) {
var getPoints = generateGetBarPoints ( barIndices , isSub ) ;
return function ( d , i ) {
// 4 points that make a bar
var points = getPoints ( d , i ) ;
// switch points if axis is rotated, not applicable for sub chart
var indexX = _ _axis _rotated ? 1 : 0 ;
var indexY = _ _axis _rotated ? 0 : 1 ;
var path = 'M ' + points [ 0 ] [ indexX ] + ',' + points [ 0 ] [ indexY ] + ' ' +
'L' + points [ 1 ] [ indexX ] + ',' + points [ 1 ] [ indexY ] + ' ' +
'L' + points [ 2 ] [ indexX ] + ',' + points [ 2 ] [ indexY ] + ' ' +
'L' + points [ 3 ] [ indexX ] + ',' + points [ 3 ] [ indexY ] + ' ' +
'z' ;
return path ;
} ;
}
function generateXYForText ( barIndices , forX ) {
var getPoints = generateGetBarPoints ( barIndices , false ) ,
getter = forX ? getXForText : getYForText ;
return function ( d , i ) {
return getter ( getPoints ( d , i ) , d , this ) ;
} ;
}
function getXForText ( points , d ) {
var padding ;
if ( _ _axis _rotated ) {
padding = isBarType ( d ) ? 4 : 6 ;
return points [ 2 ] [ 1 ] + padding * ( d . value < 0 ? - 1 : 1 ) ;
} else {
return points [ 0 ] [ 0 ] + ( points [ 2 ] [ 0 ] - points [ 0 ] [ 0 ] ) / 2 ;
}
}
function getYForText ( points , d , textElement ) {
var box = textElement . getBoundingClientRect ( ) ;
if ( _ _axis _rotated ) {
return ( points [ 0 ] [ 0 ] + points [ 2 ] [ 0 ] + box . height * 0.6 ) / 2 ;
} else {
return points [ 2 ] [ 1 ] + ( d . value < 0 ? box . height : isBarType ( d ) ? - 3 : - 6 ) ;
}
}
function generateGetAreaPoint ( areaIndices , isSub ) { // partial duplication of generateGetBarPoints
var areaTargetsNum = areaIndices . _ _max _ _ + 1 ,
x = getAreaX ( areaTargetsNum , areaIndices , ! ! isSub ) ,
y = getAreaY ( ! ! isSub ) ,
areaOffset = getAreaOffset ( areaIndices , ! ! isSub ) ,
yScale = isSub ? getSubYScale : getYScale ;
return function ( d , i ) {
var y0 = yScale ( d . id ) ( 0 ) ,
offset = areaOffset ( d , i ) || y0 , // offset is for stacked area chart
posX = x ( d ) , posY = y ( d ) ;
// fix posY not to overflow opposite quadrant
if ( _ _axis _rotated ) {
if ( ( d . value > 0 && posY < offset ) || ( d . value < 0 && posY > offset ) ) { posY = offset ; }
}
// 1 point that marks the area position
return [
[ posX , offset ] ,
[ posX , posY - ( y0 - offset ) ]
] ;
} ;
}
function generateGetBarPoints ( barIndices , isSub ) {
var barTargetsNum = barIndices . _ _max _ _ + 1 ,
barW = getBarW ( xAxis , barTargetsNum ) ,
barX = getBarX ( barW , barTargetsNum , barIndices , ! ! isSub ) ,
barY = getBarY ( ! ! isSub ) ,
barOffset = getBarOffset ( barIndices , ! ! isSub ) ,
yScale = isSub ? getSubYScale : getYScale ;
return function ( d , i ) {
var y0 = yScale ( d . id ) ( 0 ) ,
offset = barOffset ( d , i ) || y0 , // offset is for stacked bar chart
posX = barX ( d ) , posY = barY ( d ) ;
// fix posY not to overflow opposite quadrant
if ( _ _axis _rotated ) {
if ( ( 0 < d . value && posY < y0 ) || ( d . value < 0 && y0 < posY ) ) { posY = y0 ; }
}
// 4 points that make a bar
return [
[ posX , offset ] ,
[ posX , posY - ( y0 - offset ) ] ,
[ posX + barW , posY - ( y0 - offset ) ] ,
[ posX + barW , offset ]
] ;
} ;
}
function generateGetLinePoint ( lineIndices , isSub ) { // partial duplication of generateGetBarPoints
var lineTargetsNum = lineIndices . _ _max _ _ + 1 ,
x = getLineX ( lineTargetsNum , lineIndices , ! ! isSub ) ,
y = getLineY ( ! ! isSub ) ,
lineOffset = getLineOffset ( lineIndices , ! ! isSub ) ,
yScale = isSub ? getSubYScale : getYScale ;
return function ( d , i ) {
var y0 = yScale ( d . id ) ( 0 ) ,
offset = lineOffset ( d , i ) || y0 , // offset is for stacked area chart
posX = x ( d ) , posY = y ( d ) ;
// fix posY not to overflow opposite quadrant
if ( _ _axis _rotated ) {
if ( ( d . value > 0 && posY < offset ) || ( d . value < 0 && posY > offset ) ) { posY = offset ; }
}
// 1 point that marks the line position
return [
[ posX , posY - ( y0 - offset ) ]
] ;
} ;
}
function lineWithRegions ( d , x , y , _regions ) {
var prev = - 1 , i , j ;
var s = "M" , sWithRegion ;
var xp , yp , dx , dy , dd , diff , diffx2 ;
var xValue , yValue ;
var regions = [ ] ;
// Check start/end of regions
if ( isDefined ( _regions ) ) {
for ( i = 0 ; i < _regions . length ; i ++ ) {
regions [ i ] = { } ;
if ( isUndefined ( _regions [ i ] . start ) ) {
regions [ i ] . start = d [ 0 ] . x ;
} else {
regions [ i ] . start = isTimeSeries ? parseDate ( _regions [ i ] . start ) : _regions [ i ] . start ;
}
if ( isUndefined ( _regions [ i ] . end ) ) {
regions [ i ] . end = d [ d . length - 1 ] . x ;
} else {
regions [ i ] . end = isTimeSeries ? parseDate ( _regions [ i ] . end ) : _regions [ i ] . end ;
}
}
}
// Set scales
xValue = _ _axis _rotated ? function ( d ) { return y ( d . value ) ; } : function ( d ) { return x ( d . x ) ; } ;
yValue = _ _axis _rotated ? function ( d ) { return x ( d . x ) ; } : function ( d ) { return y ( d . value ) ; } ;
// Define svg generator function for region
if ( isTimeSeries ) {
sWithRegion = function ( d0 , d1 , j , diff ) {
var x0 = d0 . x . getTime ( ) , x _diff = d1 . x - d0 . x ,
xv0 = new Date ( x0 + x _diff * j ) ,
xv1 = new Date ( x0 + x _diff * ( j + diff ) ) ;
return "M" + x ( xv0 ) + " " + y ( yp ( j ) ) + " " + x ( xv1 ) + " " + y ( yp ( j + diff ) ) ;
} ;
} else {
sWithRegion = function ( d0 , d1 , j , diff ) {
return "M" + x ( xp ( j ) , true ) + " " + y ( yp ( j ) ) + " " + x ( xp ( j + diff ) , true ) + " " + y ( yp ( j + diff ) ) ;
} ;
}
// Generate
for ( i = 0 ; i < d . length ; i ++ ) {
// Draw as normal
if ( isUndefined ( regions ) || ! isWithinRegions ( d [ i ] . x , regions ) ) {
s += " " + xValue ( d [ i ] ) + " " + yValue ( d [ i ] ) ;
}
// Draw with region // TODO: Fix for horizotal charts
else {
xp = getScale ( d [ i - 1 ] . x , d [ i ] . x , isTimeSeries ) ;
yp = getScale ( d [ i - 1 ] . value , d [ i ] . value ) ;
dx = x ( d [ i ] . x ) - x ( d [ i - 1 ] . x ) ;
dy = y ( d [ i ] . value ) - y ( d [ i - 1 ] . value ) ;
dd = Math . sqrt ( Math . pow ( dx , 2 ) + Math . pow ( dy , 2 ) ) ;
diff = 2 / dd ;
diffx2 = diff * 2 ;
for ( j = diff ; j <= 1 ; j += diffx2 ) {
s += sWithRegion ( d [ i - 1 ] , d [ i ] , j , diff ) ;
}
}
prev = d [ i ] . x ;
}
return s ;
}
//-- Define brush/zoom -//
var brush , zoom = function ( ) { } ;
brush = d3 . svg . brush ( ) . on ( "brush" , redrawForBrush ) ;
brush . update = function ( ) {
if ( context ) { context . select ( '.' + CLASS . brush ) . call ( this ) ; }
return this ;
} ;
brush . scale = function ( scale ) {
return _ _axis _rotated ? this . y ( scale ) : this . x ( scale ) ;
} ;
if ( _ _zoom _enabled ) {
zoom = d3 . behavior . zoom ( )
. on ( "zoomstart" , function ( ) { zoom . altDomain = d3 . event . sourceEvent . altKey ? x . orgDomain ( ) : null ; } )
. on ( "zoom" , _ _zoom _enabled ? redrawForZoom : null ) ;
zoom . scale = function ( scale ) {
return _ _axis _rotated ? this . y ( scale ) : this . x ( scale ) ;
} ;
zoom . orgScaleExtent = function ( ) {
var extent = _ _zoom _extent ? _ _zoom _extent : [ 1 , 10 ] ;
return [ extent [ 0 ] , Math . max ( getMaxDataCount ( ) / extent [ 1 ] , extent [ 1 ] ) ] ;
} ;
zoom . updateScaleExtent = function ( ) {
var ratio = diffDomain ( x . orgDomain ( ) ) / diffDomain ( orgXDomain ) , extent = this . orgScaleExtent ( ) ;
this . scaleExtent ( [ extent [ 0 ] * ratio , extent [ 1 ] * ratio ] ) ;
return this ;
} ;
}
/*-- Draw Chart --*/
// for svg elements
var svg , defs , main , context , legend , tooltip , selectChart ;
// for brush area culculation
var orgXDomain ;
// for save value
var orgAreaOpacity , withoutFadeIn = { } ;
function observeInserted ( selection ) {
var 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 ) ;
redraw ( {
withUpdateTranslate : true ,
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 } ) ;
}
function init ( data ) {
var eventRect , grid , i , binding = true ;
selectChart = d3 . select ( _ _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
c3 . data . xs = { } ;
c3 . data . targets = convertDataToTargets ( data ) ;
if ( _ _data _filter ) {
c3 . data . targets = c3 . data . targets . filter ( _ _data _filter ) ;
}
// Set targets to hide if needed
if ( _ _data _hide ) {
addHiddenTargetIds ( _ _data _hide === true ? mapToIds ( c3 . data . targets ) : _ _data _hide ) ;
}
// Init sizes and scales
updateSizes ( ) ;
updateScales ( ) ;
// Set domains for each scale
x . domain ( d3 . extent ( getXDomain ( c3 . data . targets ) ) ) ;
y . domain ( getYDomain ( c3 . data . targets , 'y' ) ) ;
y2 . domain ( getYDomain ( c3 . 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
brush . scale ( subX ) ;
if ( _ _zoom _enabled ) { zoom . scale ( x ) ; }
/*-- Basic Elements --*/
// Define svgs
svg = selectChart . append ( "svg" )
. style ( "overflow" , "hidden" )
. on ( 'mouseenter' , _ _onenter )
. on ( 'mouseleave' , _ _onleave ) ;
// 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 = svg . append ( "g" ) . attr ( "transform" , translate . main ) ;
context = svg . append ( "g" ) . attr ( "transform" , translate . context ) ;
legend = svg . append ( "g" ) . attr ( "transform" , translate . legend ) ;
if ( ! _ _subchart _show ) {
context . style ( 'visibility' , 'hidden' ) ;
}
if ( ! _ _legend _show ) {
legend . style ( 'visibility' , 'hidden' ) ;
hiddenLegendIds = mapToIds ( c3 . data . targets ) ;
}
// Define tooltip
tooltip = selectChart
. style ( "position" , "relative" )
. append ( "div" )
. style ( "position" , "absolute" )
. style ( "pointer-events" , "none" )
. style ( "z-index" , "10" )
. style ( "display" , "none" ) ;
// MEMO: call here to update legend box and tranlate for all
// MEMO: translate will be upated by this, so transform not needed in updateLegend()
updateLegend ( mapToIds ( c3 . data . targets ) , { withTransform : false , withTransitionForTransform : false } ) ;
/*-- Main Region --*/
if ( c3 . data . targets . length == 0 ) {
main . append ( "text" )
. attr ( "class" , CLASS . text )
. attr ( "x" , ( main [ 0 ] [ 0 ] . parentNode . width . baseVal . value / 2 ) - margin . left )
. attr ( "y" , ( main [ 0 ] [ 0 ] . parentNode . height . baseVal . value / 2 ) - margin . top )
. attr ( "text-anchor" , "middle" )
. attr ( "style" , ( _ _data _empty _label _fill ? "fill:" + _ _data _empty _label _fill + "; " : "" ) + ( _ _data _empty _label _size ? "font-size:" + _ _data _empty _label _size + "; " : "" ) )
. text ( _ _data _empty _label _text ) ;
}
// Grids
grid = main . append ( 'g' )
. attr ( "clip-path" , clipPath )
. attr ( 'class' , CLASS . grid ) ;
// X-Grid
if ( _ _grid _x _show ) {
grid . append ( "g" ) . attr ( "class" , CLASS . xgrids ) ;
}
if ( notEmpty ( _ _grid _x _lines ) ) {
grid . append ( 'g' ) . attr ( "class" , CLASS . xgridLines ) ;
}
if ( _ _point _focus _line _enabled ) {
grid . append ( 'g' )
. attr ( "class" , CLASS . xgridFocus )
. append ( 'line' )
. attr ( 'class' , CLASS . xgridFocus ) ;
}
// Y-Grid
if ( _ _grid _y _show ) {
grid . append ( 'g' ) . attr ( 'class' , CLASS . ygrids ) ;
}
if ( notEmpty ( _ _grid _y _lines ) ) {
grid . append ( 'g' ) . attr ( 'class' , CLASS . ygridLines ) ;
}
// Regions
main . append ( 'g' )
. attr ( "clip-path" , clipPath )
. attr ( "class" , CLASS . regions ) ;
// 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 )
. style ( 'cursor' , _ _zoom _enabled ? _ _axis _rotated ? 'ns-resize' : 'ew-resize' : null ) ;
// 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
main . select ( '.' + CLASS . chart ) . append ( "g" )
. attr ( "class" , CLASS . chartArcs )
. attr ( "transform" , translate . arc )
. append ( 'text' )
. attr ( 'class' , CLASS . chartArcsTitle )
. style ( "text-anchor" , "middle" )
. text ( getArcTitle ( ) ) ;
main . select ( '.' + CLASS . chart ) . append ( "g" )
. attr ( "class" , CLASS . chartTexts ) ;
if ( _ _zoom _enabled ) { // TODO: __zoom_privileged here?
// if zoom privileged, insert rect to forefront
main . insert ( 'rect' , _ _zoom _privileged ? null : 'g.' + CLASS . grid )
. attr ( 'class' , CLASS . zoomRect )
. attr ( 'width' , width )
. attr ( 'height' , height )
. style ( 'opacity' , 0 )
. style ( 'cursor' , _ _axis _rotated ? 'ns-resize' : 'ew-resize' )
. call ( zoom ) . on ( "dblclick.zoom" , null ) ;
}
// Set default extent if defined
if ( _ _axis _x _default ) {
brush . extent ( typeof _ _axis _x _default !== 'function' ? _ _axis _x _default : _ _axis _x _default ( getXDomain ( ) ) ) ;
}
// Add Axis
axes . x = main . append ( "g" )
. attr ( "class" , CLASS . axisX )
. attr ( "clip-path" , clipPathForXAxis )
. attr ( "transform" , translate . x )
. style ( "visibility" , _ _axis _x _show ? 'visible' : 'hidden' ) ;
axes . x . append ( "text" )
. attr ( "class" , CLASS . axisXLabel )
. attr ( "transform" , _ _axis _rotated ? "rotate(-90)" : "" )
. style ( "text-anchor" , textAnchorForXAxisLabel ) ;
axes . y = main . append ( "g" )
. attr ( "class" , CLASS . axisY )
. attr ( "clip-path" , clipPathForYAxis )
. attr ( "transform" , translate . y )
. style ( "visibility" , _ _axis _y _show ? 'visible' : 'hidden' ) ;
axes . y . append ( "text" )
. attr ( "class" , CLASS . axisYLabel )
. attr ( "transform" , _ _axis _rotated ? "" : "rotate(-90)" )
. style ( "text-anchor" , textAnchorForYAxisLabel ) ;
axes . y2 = main . append ( "g" )
. attr ( "class" , CLASS . axisY2 )
// clip-path?
. attr ( "transform" , translate . y2 )
. style ( "visibility" , _ _axis _y2 _show ? 'visible' : 'hidden' ) ;
axes . y2 . append ( "text" )
. attr ( "class" , CLASS . axisY2Label )
. attr ( "transform" , _ _axis _rotated ? "" : "rotate(-90)" )
. style ( "text-anchor" , textAnchorForY2AxisLabel ) ;
/*-- Context Region --*/
// Define g for chart area
context . append ( 'g' )
. attr ( "clip-path" , clipPath )
. attr ( 'class' , CLASS . chart ) ;
// Define g for bar chart area
context . select ( '.' + CLASS . chart ) . append ( "g" )
. attr ( "class" , CLASS . chartBars ) ;
// Define g for line chart area
context . select ( '.' + CLASS . chart ) . append ( "g" )
. attr ( "class" , CLASS . chartLines ) ;
// Add extent rect for Brush
context . append ( "g" )
. attr ( "clip-path" , clipPath )
. attr ( "class" , CLASS . brush )
. call ( brush )
. selectAll ( "rect" )
. attr ( _ _axis _rotated ? "width" : "height" , _ _axis _rotated ? width2 : height2 ) ;
// ATTENTION: This must be called AFTER chart added
// Add Axis
axes . subx = context . append ( "g" )
. attr ( "class" , CLASS . axisX )
. attr ( "transform" , translate . subx )
. attr ( "clip-path" , _ _axis _rotated ? "" : clipPathForXAxis ) ;
// Set targets
updateTargets ( c3 . data . targets ) ;
// Draw with targets
if ( binding ) {
redraw ( { withUpdateTranslate : true , withTransform : true , withUpdateXDomain : true , withUpdateOrgXDomain : true , withTransitionForAxis : false } ) ;
}
// Show tooltip if needed
if ( _ _tooltip _init _show ) {
if ( isTimeSeries && typeof _ _tooltip _init _x === 'string' ) {
_ _tooltip _init _x = parseDate ( _ _tooltip _init _x ) ;
for ( i = 0 ; i < c3 . data . targets [ 0 ] . values . length ; i ++ ) {
if ( ( c3 . data . targets [ 0 ] . values [ i ] . x - _ _tooltip _init _x ) === 0 ) { break ; }
}
_ _tooltip _init _x = i ;
}
tooltip . html ( _ _tooltip _contents ( c3 . data . targets . map ( function ( d ) {
return addName ( d . values [ _ _tooltip _init _x ] ) ;
} ) , getXAxisTickFormat ( ) , getYFormat ( hasArcType ( c3 . data . targets ) ) , color ) ) ;
tooltip . style ( "top" , _ _tooltip _init _position . top )
. style ( "left" , _ _tooltip _init _position . left )
. style ( "display" , "block" ) ;
}
// Bind resize event
if ( window . onresize == null ) {
window . onresize = generateResize ( ) ;
}
if ( window . onresize . add ) {
window . onresize . add ( _ _onresize ) ;
window . onresize . add ( function ( ) {
c3 . flush ( ) ;
} ) ;
window . onresize . add ( _ _onresized ) ;
}
// export element of the chart
c3 . element = selectChart . node ( ) ;
}
function generateEventRectsForSingleX ( eventRectEnter ) {
eventRectEnter . append ( "rect" )
. attr ( "class" , classEvent )
. style ( "cursor" , _ _data _selection _enabled && _ _data _selection _grouped ? "pointer" : null )
. on ( 'mouseover' , function ( _ , i ) {
if ( dragging ) { return ; } // do nothing if dragging
if ( hasArcType ( c3 . data . targets ) ) { return ; }
var selectedData = c3 . data . targets . map ( function ( d ) { return addName ( d . values [ i ] ) ; } ) ,
newData = [ ] ;
// Sort selectedData as names order
Object . keys ( _ _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 if needed
if ( _ _point _focus _expand _enabled ) { expandCircles ( i ) ; }
expandBars ( i ) ;
// Call event handler
main . selectAll ( '.' + CLASS . shape + '-' + i ) . each ( function ( d ) {
_ _data _onenter ( d ) ;
} ) ;
} )
. on ( 'mouseout' , function ( _ , i ) {
if ( hasArcType ( c3 . data . targets ) ) { return ; }
hideXGridFocus ( ) ;
hideTooltip ( ) ;
// Undo expanded shapes
unexpandCircles ( i ) ;
unexpandBars ( ) ;
// Call event handler
main . selectAll ( '.' + CLASS . shape + '-' + i ) . each ( function ( d ) {
_ _data _onleave ( d ) ;
} ) ;
} )
. on ( 'mousemove' , function ( _ , i ) {
var selectedData ;
if ( dragging ) { return ; } // do nothing when dragging
if ( hasArcType ( c3 . data . targets ) ) { return ; }
// Show tooltip
selectedData = filterTargetsToShow ( c3 . data . targets ) . map ( function ( d ) {
return addName ( d . values [ i ] ) ;
} ) ;
showTooltip ( selectedData , d3 . mouse ( this ) ) ;
// Show xgrid focus line
showXGridFocus ( selectedData ) ;
if ( ! _ _data _selection _enabled ) { return ; }
if ( _ _data _selection _grouped ) { return ; } // nothing to do when grouped
main . selectAll ( '.' + CLASS . shape + '-' + i )
. filter ( function ( d ) { return _ _data _selection _isselectable ( d ) ; } )
. each ( function ( ) {
var _this = d3 . select ( this ) . classed ( CLASS . EXPANDED , true ) ;
if ( this . nodeName === 'circle' ) { _this . attr ( 'r' , pointExpandedR ) ; }
svg . select ( '.' + CLASS . eventRect + '-' + i ) . style ( 'cursor' , null ) ;
} )
. filter ( function ( d ) {
if ( this . nodeName === 'circle' ) {
return isWithinCircle ( this , pointSelectR ( d ) ) ;
}
else if ( this . nodeName === 'path' ) {
return isWithinBar ( this ) ;
}
} )
. each ( function ( ) {
var _this = d3 . select ( this ) ;
if ( ! _this . classed ( CLASS . EXPANDED ) ) {
_this . classed ( CLASS . EXPANDED , true ) ;
if ( this . nodeName === 'circle' ) { _this . attr ( 'r' , pointSelectR ) ; }
}
svg . select ( '.' + CLASS . eventRect + '-' + i ) . style ( 'cursor' , 'pointer' ) ;
} ) ;
} )
. on ( 'click' , function ( _ , i ) {
if ( hasArcType ( c3 . data . targets ) ) { return ; }
if ( cancelClick ) {
cancelClick = false ;
return ;
}
main . selectAll ( '.' + CLASS . shape + '-' + i ) . each ( function ( d ) { toggleShape ( this , d , i ) ; } ) ;
} )
. call (
d3 . behavior . drag ( ) . origin ( Object )
. on ( 'drag' , function ( ) { drag ( d3 . mouse ( this ) ) ; } )
. on ( 'dragstart' , function ( ) { dragstart ( d3 . mouse ( this ) ) ; } )
. on ( 'dragend' , function ( ) { dragend ( ) ; } )
)
. call ( zoom ) . on ( "dblclick.zoom" , null ) ;
}
function generateEventRectsForMultipleXs ( eventRectEnter ) {
eventRectEnter . append ( 'rect' )
. attr ( 'x' , 0 )
. attr ( 'y' , 0 )
. attr ( 'width' , width )
. attr ( 'height' , height )
. attr ( 'class' , CLASS . eventRect )
. on ( 'mouseout' , function ( ) {
if ( hasArcType ( c3 . data . targets ) ) { return ; }
hideXGridFocus ( ) ;
hideTooltip ( ) ;
unexpandCircles ( ) ;
} )
. on ( 'mousemove' , function ( ) {
var targetsToShow = filterTargetsToShow ( c3 . 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 ( _ _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 ) {
_ _data _onenter ( closest ) ;
mouseover = true ;
}
} else {
svg . select ( '.' + CLASS . eventRect ) . style ( 'cursor' , null ) ;
_ _data _onleave ( closest ) ;
mouseover = false ;
}
} )
. on ( 'click' , function ( ) {
var targetsToShow = filterTargetsToShow ( c3 . 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 ) {
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 ( ) ; } )
)
. call ( zoom ) . on ( "dblclick.zoom" , null ) ;
}
function toggleShape ( that , d , i ) {
var shape = d3 . select ( that ) , isSelected = shape . classed ( CLASS . SELECTED ) , isWithin , toggle ;
if ( that . nodeName === 'circle' ) {
isWithin = isWithinCircle ( that , pointSelectR ( d ) * 1.5 ) ;
toggle = togglePoint ;
}
else if ( that . nodeName === 'path' ) {
if ( shape . classed ( CLASS . bar ) ) {
isWithin = isWithinBar ( that ) ;
toggle = toggleBar ;
} else { // would be arc
isWithin = true ;
toggle = toggleArc ;
}
}
if ( _ _data _selection _grouped || isWithin ) {
if ( _ _data _selection _enabled && _ _data _selection _isselectable ( d ) ) {
if ( ! _ _data _selection _multiple ) {
main . selectAll ( '.' + CLASS . shapes + ( _ _data _selection _grouped ? getTargetSelectorSuffix ( d . id ) : "" ) ) . selectAll ( '.' + CLASS . shape ) . each ( function ( d , i ) {
var shape = d3 . select ( this ) ;
if ( shape . classed ( CLASS . SELECTED ) ) { toggle ( false , shape . classed ( CLASS . SELECTED , false ) , d , i ) ; }
} ) ;
}
shape . classed ( CLASS . SELECTED , ! isSelected ) ;
toggle ( ! isSelected , shape , d , i ) ;
}
_ _data _onclick ( d , that ) ;
}
}
function drag ( mouse ) {
var sx , sy , mx , my , minX , maxX , minY , maxY ;
if ( hasArcType ( c3 . data . targets ) ) { return ; }
if ( ! _ _data _selection _enabled ) { return ; } // do nothing if not selectable
if ( _ _zoom _enabled && ! zoom . altDomain ) { return ; } // skip if zoomable because of conflict drag dehavior
if ( ! _ _data _selection _multiple ) { return ; } // skip when single selection becuase drag is used for multiple selection
sx = dragStart [ 0 ] ;
sy = dragStart [ 1 ] ;
mx = mouse [ 0 ] ;
my = mouse [ 1 ] ;
minX = Math . min ( sx , mx ) ;
maxX = Math . max ( sx , mx ) ;
minY = ( _ _data _selection _grouped ) ? margin . top : Math . min ( sy , my ) ;
maxY = ( _ _data _selection _grouped ) ? height : Math . max ( sy , my ) ;
main . select ( '.' + CLASS . dragarea )
. attr ( 'x' , minX )
. attr ( 'y' , minY )
. attr ( 'width' , maxX - minX )
. attr ( 'height' , maxY - minY ) ;
// TODO: binary search when multiple xs
main . selectAll ( '.' + CLASS . shapes ) . selectAll ( '.' + CLASS . shape )
. filter ( function ( d ) { return _ _data _selection _isselectable ( d ) ; } )
. each ( function ( d , i ) {
var shape = d3 . select ( this ) ,
isSelected = shape . classed ( CLASS . SELECTED ) ,
isIncluded = shape . classed ( CLASS . INCLUDED ) ,
_x , _y , _w , _h , toggle , isWithin = false , box ;
if ( shape . classed ( CLASS . circle ) ) {
_x = shape . attr ( "cx" ) * 1 ;
_y = shape . attr ( "cy" ) * 1 ;
toggle = togglePoint ;
isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY ;
}
else if ( shape . classed ( CLASS . bar ) ) {
box = getPathBox ( this ) ;
_x = box . x ;
_y = box . y ;
_w = box . width ;
_h = box . height ;
toggle = toggleBar ;
isWithin = ! ( maxX < _x || _x + _w < minX ) && ! ( maxY < _y || _y + _h < minY ) ;
} else {
// line/area selection not supported yet
return ;
}
if ( isWithin ^ isIncluded ) {
shape . classed ( CLASS . INCLUDED , ! isIncluded ) ;
// TODO: included/unincluded callback here
shape . classed ( CLASS . SELECTED , ! isSelected ) ;
toggle ( ! isSelected , shape , d , i ) ;
}
} ) ;
}
function dragstart ( mouse ) {
if ( hasArcType ( c3 . data . targets ) ) { return ; }
if ( ! _ _data _selection _enabled ) { return ; } // do nothing if not selectable
dragStart = mouse ;
main . select ( '.' + CLASS . chart ) . append ( 'rect' )
. attr ( 'class' , CLASS . dragarea )
. style ( 'opacity' , 0.1 ) ;
dragging = true ;
_ _data _ondragstart ( ) ;
}
function dragend ( ) {
if ( hasArcType ( c3 . data . targets ) ) { return ; }
if ( ! _ _data _selection _enabled ) { return ; } // do nothing if not selectable
main . select ( '.' + CLASS . dragarea )
. transition ( ) . duration ( 100 )
. style ( 'opacity' , 0 )
. remove ( ) ;
main . selectAll ( '.' + CLASS . shape )
. classed ( CLASS . INCLUDED , false ) ;
dragging = false ;
_ _data _ondragend ( ) ;
}
function smoothLines ( el , type ) {
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 ) ,
} ) ;
} ) ;
}
}
function redraw ( options , transitions ) {
var xgrid , xgridAttr , xgridData , xgridLines , xgridLine , ygrid , ygridLines , ygridLine ;
var mainLine , mainStep , mainArea , mainCircle , mainBar , mainArc , mainRegion , mainText , contextLine , contextStep , contextArea , contextBar , eventRect , eventRectUpdate ;
var areaIndices = getAreaIndices ( ) , barIndices = getBarIndices ( ) , lineIndices = getLineIndices ( ) , maxDataCountTarget , tickOffset ;
var rectX , rectW ;
var withY , withSubchart , withTransition , withTransitionForExit , withTransitionForAxis , withTransform , withUpdateXDomain , withUpdateOrgXDomain , withLegend , withUpdateTranslate ;
var hideAxis = hasArcType ( c3 . data . targets ) ;
var drawArea , drawAreaOnSub , drawBar , drawBarOnSub , drawLine , drawLineOnSub , xForText , yForText ;
var duration , durationForExit , durationForAxis ;
var targetsToShow = filterTargetsToShow ( c3 . data . targets ) , tickValues , i , intervalForCulling ;
// abort if no targets to show
if ( targetsToShow . length === 0 && _ _data _empty _abort ) {
return ;
}
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 ) ;
withUpdateTranslate = getOption ( options , "withUpdateTranslate" , false ) ;
withLegend = getOption ( options , "withLegend" , false ) ;
withTransitionForExit = getOption ( options , "withTransitionForExit" , withTransition ) ;
withTransitionForAxis = getOption ( options , "withTransitionForAxis" , withTransition ) ;
duration = withTransition ? _ _transition _duration : 0 ;
durationForExit = withTransitionForExit ? duration : 0 ;
durationForAxis = withTransitionForAxis ? duration : 0 ;
transitions = transitions || generateAxisTransitions ( durationForAxis ) ;
// MEMO: call axis to generate ticks and get those length, then update translate with them
if ( withUpdateTranslate ) {
if ( _ _axis _rotated ) {
axes . x . call ( xAxis ) ;
axes . subx . call ( subXAxis ) ;
} else {
axes . y . call ( yAxis ) ;
axes . y2 . call ( y2Axis ) ;
}
updateSizes ( ) ;
updateScales ( ) ;
updateSvgSize ( ) ;
transformAll ( false ) ;
}
// update legend and transform each g
if ( withLegend && _ _legend _show ) {
updateLegend ( mapToIds ( c3 . 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 ) {
if ( withUpdateOrgXDomain ) {
x . domain ( d3 . extent ( getXDomain ( targetsToShow ) ) ) ;
orgXDomain = x . domain ( ) ;
if ( _ _zoom _enabled ) { zoom . scale ( x ) . updateScaleExtent ( ) ; }
subX . domain ( x . domain ( ) ) ;
brush . scale ( subX ) ;
}
// ATTENTION: call here to update tickOffset
if ( withUpdateXDomain ) {
x . domain ( brush . empty ( ) ? orgXDomain : brush . extent ( ) ) ;
if ( _ _zoom _enabled ) { zoom . scale ( x ) . updateScaleExtent ( ) ; }
}
// update axis tick values according to options
if ( ! _ _axis _x _tick _values && ( _ _axis _x _tick _fit || _ _axis _x _tick _count ) ) {
tickValues = generateTickValues ( mapTargetsToUniqueXs ( targetsToShow ) , _ _axis _x _tick _count ) ;
xAxis . tickValues ( tickValues ) ;
subXAxis . tickValues ( 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 ( _ _axis _x _tick _culling && tickValues ) {
for ( i = 1 ; i < tickValues . length ; i ++ ) {
if ( tickValues . length / i < _ _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 ( ! _ _axis _rotated && _ _axis _x _tick _rotate ) {
rotateTickText ( axes . x , transitions . axisX , _ _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 ( ) ;
// grid
main . select ( 'line.' + CLASS . xgridFocus ) . style ( "visibility" , "hidden" ) ;
if ( _ _grid _x _show ) {
xgridData = generateGridData ( _ _grid _x _type , x ) ;
tickOffset = isCategorized ? xAxis . tickOffset ( ) : 0 ;
xgridAttr = _ _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' : margin . top ,
'y2' : height
} ;
xgrid = main . select ( '.' + CLASS . xgrids ) . selectAll ( '.' + CLASS . xgrid )
. data ( xgridData ) ;
xgrid . enter ( ) . append ( 'line' ) . attr ( "class" , CLASS . xgrid ) ;
xgrid . attr ( xgridAttr )
. style ( "opacity" , function ( ) { return + d3 . select ( this ) . attr ( _ _axis _rotated ? 'y1' : 'x1' ) === ( _ _axis _rotated ? height : 0 ) ? 0 : 1 ; } ) ;
xgrid . exit ( ) . remove ( ) ;
}
if ( notEmpty ( _ _grid _x _lines ) ) {
xgridLines = main . select ( '.' + CLASS . xgridLines ) . selectAll ( '.' + CLASS . xgridLine )
. data ( _ _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" , _ _axis _rotated ? "" : "rotate(-90)" )
. attr ( 'dx' , _ _axis _rotated ? 0 : - margin . top )
. attr ( 'dy' , - 5 )
. style ( "opacity" , 0 ) ;
// udpate
xgridLines . select ( 'line' )
. transition ( ) . duration ( duration )
. attr ( "x1" , _ _axis _rotated ? 0 : xv )
. attr ( "x2" , _ _axis _rotated ? width : xv )
. attr ( "y1" , _ _axis _rotated ? xv : margin . top )
. attr ( "y2" , _ _axis _rotated ? xv : height )
. style ( "opacity" , 1 ) ;
xgridLines . select ( 'text' )
. transition ( ) . duration ( duration )
. attr ( "x" , _ _axis _rotated ? width : 0 )
. attr ( "y" , xv )
. text ( function ( d ) { return d . text ; } )
. style ( "opacity" , 1 ) ;
// exit
xgridLines . exit ( ) . transition ( ) . duration ( duration )
. style ( "opacity" , 0 )
. remove ( ) ;
}
// Y-Grid
if ( withY && _ _grid _y _show ) {
ygrid = main . select ( '.' + CLASS . ygrids ) . selectAll ( '.' + CLASS . ygrid )
. data ( y . ticks ( _ _grid _y _ticks ) ) ;
ygrid . enter ( ) . append ( 'line' )
. attr ( 'class' , CLASS . ygrid ) ;
ygrid . attr ( "x1" , _ _axis _rotated ? y : 0 )
. attr ( "x2" , _ _axis _rotated ? y : width )
. attr ( "y1" , _ _axis _rotated ? 0 : y )
. attr ( "y2" , _ _axis _rotated ? height : y ) ;
ygrid . exit ( ) . remove ( ) ;
smoothLines ( ygrid , 'grid' ) ;
}
if ( withY && notEmpty ( _ _grid _y _lines ) ) {
ygridLines = main . select ( '.' + CLASS . ygridLines ) . selectAll ( '.' + CLASS . ygridLine )
. data ( _ _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" , _ _axis _rotated ? "rotate(-90)" : "" )
. attr ( 'dx' , _ _axis _rotated ? 0 : - margin . top )
. attr ( 'dy' , - 5 )
. style ( "opacity" , 0 ) ;
// update
ygridLines . select ( 'line' )
. transition ( ) . duration ( duration )
. attr ( "x1" , _ _axis _rotated ? yv : 0 )
. attr ( "x2" , _ _axis _rotated ? yv : width )
. attr ( "y1" , _ _axis _rotated ? 0 : yv )
. attr ( "y2" , _ _axis _rotated ? height : yv )
. style ( "opacity" , 1 ) ;
ygridLines . select ( 'text' )
. transition ( ) . duration ( duration )
. attr ( "x" , _ _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 ( _ _regions ) ;
mainRegion . enter ( ) . append ( 'g' )
. attr ( 'class' , classRegion )
. append ( 'rect' )
. style ( "fill-opacity" , 0 ) ;
mainRegion . selectAll ( 'rect' )
. transition ( ) . duration ( duration )
. 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 ; } ) ;
mainRegion . exit ( ) . transition ( ) . duration ( duration )
. style ( "opacity" , 0 )
. remove ( ) ;
// bars
mainBar = main . selectAll ( '.' + CLASS . bars ) . selectAll ( '.' + CLASS . bar )
. data ( barData ) ;
mainBar . enter ( ) . append ( 'path' )
. attr ( "class" , classBar )
. style ( "stroke" , function ( d ) { return color ( d . id ) ; } )
. style ( "stroke-width" , 2 )
. style ( "opacity" , 0 )
. style ( "fill" , function ( d ) { return color ( d . id ) ; } )
. style ( "fill-opacity" , function ( ) { if ( _ _color _opacity ) { return _ _color _opacity ; } return initialOpacity ; } ) ;
mainBar
. style ( "opacity" , initialOpacity )
. transition ( ) . duration ( duration )
. attr ( 'd' , drawBar )
. style ( "fill" , color )
. style ( "opacity" , 1 ) ;
mainBar . exit ( ) . transition ( ) . duration ( durationForExit )
. style ( 'opacity' , 0 )
. remove ( ) ;
// lines and cricles
mainLine = main . selectAll ( '.' + CLASS . lines ) . selectAll ( '.' + CLASS . line )
. data ( lineData ) ;
mainLine . enter ( ) . append ( 'path' )
. attr ( 'class' , classLine )
. style ( "stroke" , color ) ;
mainLine
. style ( "opacity" , initialOpacity )
. transition ( ) . duration ( duration )
. attr ( "d" , drawLine )
. style ( "stroke" , color )
. style ( "opacity" , 1 ) ;
mainLine . exit ( ) . transition ( ) . duration ( durationForExit )
. style ( 'opacity' , 0 )
. remove ( ) ;
// steps
mainStep = main . selectAll ( '.' + CLASS . steps ) . selectAll ( '.' + CLASS . step )
. data ( stepData ) ;
mainStep . enter ( ) . append ( 'path' )
. attr ( 'class' , classStep )
. style ( "stroke" , color ) ;
mainStep
. style ( "opacity" , initialOpacity )
. transition ( ) . duration ( duration )
. attr ( "d" , drawLine )
. style ( "stroke" , color )
. style ( "opacity" , 1 ) ;
mainStep . exit ( ) . transition ( ) . duration ( durationForExit )
. style ( 'opacity' , 0 )
. remove ( ) ;
// area
mainArea = main . selectAll ( '.' + CLASS . areas ) . selectAll ( '.' + CLASS . area )
. data ( lineOrStepData ) ;
mainArea . enter ( ) . append ( 'path' )
. attr ( "class" , classArea )
. style ( "fill" , color )
. style ( "opacity" , function ( ) { orgAreaOpacity = + d3 . select ( this ) . style ( 'opacity' ) ; return 0 ; } ) ;
mainArea
. style ( "opacity" , 0 )
. transition ( ) . duration ( duration )
. attr ( "d" , drawArea )
. style ( "fill" , color )
. style ( "opacity" , orgAreaOpacity ) ;
mainArea . exit ( ) . transition ( ) . duration ( durationForExit )
. style ( 'opacity' , 0 )
. remove ( ) ;
if ( _ _point _show ) {
mainCircle = main . selectAll ( '.' + CLASS . circles ) . selectAll ( '.' + CLASS . circle )
. data ( lineOrScatterData ) ;
mainCircle . enter ( ) . append ( "circle" )
. attr ( "class" , classCircle )
. attr ( "r" , pointR )
. style ( "fill" , color ) ;
mainCircle
. style ( "opacity" , initialOpacity )
. transition ( ) . duration ( duration )
. style ( 'opacity' , opacityForCircle )
. style ( "fill" , color )
. attr ( "cx" , _ _axis _rotated ? circleY : circleX )
. attr ( "cy" , _ _axis _rotated ? circleX : circleY ) ;
mainCircle . exit ( ) . remove ( ) ;
}
if ( hasDataLabel ( ) ) {
mainText = main . selectAll ( '.' + CLASS . texts ) . selectAll ( '.' + CLASS . text )
. data ( barOrLineData ) ;
mainText . enter ( ) . append ( 'text' )
. attr ( "class" , classText )
. attr ( 'text-anchor' , function ( d ) { return _ _axis _rotated ? ( d . value < 0 ? 'end' : 'start' ) : 'middle' ; } )
. style ( "stroke" , 'none' )
. style ( "fill" , color )
. style ( "fill-opacity" , 0 ) ;
mainText
. text ( function ( d ) { return formatByAxisId ( d . id ) ( d . value , d . id ) ; } )
. style ( "fill-opacity" , initialOpacityForText )
. transition ( ) . duration ( duration )
. attr ( 'x' , xForText )
. attr ( 'y' , yForText )
. style ( "fill" , color )
. style ( "fill-opacity" , opacityForText ) ;
mainText . exit ( )
. transition ( ) . duration ( durationForExit )
. style ( 'fill-opacity' , 0 )
. remove ( ) ;
}
// arc
mainArc = main . selectAll ( '.' + CLASS . arcs ) . selectAll ( '.' + CLASS . arc )
. data ( arcData ) ;
if ( _ _gauge _style === "arc" ) {
mainArc . enter ( ) . append ( "path" )
. attr ( "class" , "" )
. style ( "opacity" , 1 )
. style ( "fill" , _ _gauge _color ) // Where background color would receive customization.
. style ( "cursor" , "pointer" )
. attr ( "transform" , "scale(1,1)" )
. attr ( "d" , function ( d ) {
d . value = _ _gauge _max ;
d . startAngle = - 1 * ( Math . PI / 2 ) ;
d . endAngle = Math . PI / 2 ;
return getArc ( d , true ) ;
} ) ;
mainArc . exit ( ) . transition ( ) . duration ( durationForExit )
. style ( 'opacity' , 0 )
. remove ( ) ;
}
mainArc . enter ( ) . append ( 'path' )
. attr ( "class" , classArc )
. style ( "fill" , function ( d ) { return color ( d . data ) ; } )
. style ( "cursor" , function ( d ) { return _ _data _selection _isselectable ( d ) ? "pointer" : null ; } )
. style ( "opacity" , 0 )
. each ( function ( d ) { this . _current = d ; } )
. on ( 'mouseover' , function ( d , i ) {
var updated , arcData , callback ;
if ( transiting ) { // skip while transiting
return ;
}
updated = updateAngle ( d ) ;
arcData = convertToArcData ( updated ) ;
callback = getArcOnMouseOver ( ) ;
// transitions
expandArc ( updated . data . id ) ;
toggleFocusLegend ( updated . data . id , true ) ;
callback ( arcData , i ) ;
} )
. on ( 'mousemove' , function ( d ) {
var updated = updateAngle ( d ) , arcData = convertToArcData ( updated ) , selectedData = [ arcData ] ;
showTooltip ( selectedData , d3 . mouse ( this ) ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
var updated , arcData , callback ;
if ( transiting ) { // skip while transiting
return ;
}
updated = updateAngle ( d ) ;
arcData = convertToArcData ( updated ) ;
callback = getArcOnMouseOut ( ) ;
// transitions
unexpandArc ( updated . data . id ) ;
revertLegend ( ) ;
hideTooltip ( ) ;
callback ( arcData , i ) ;
} )
. on ( 'click' , function ( d , i ) {
var updated = updateAngle ( d ) , arcData = convertToArcData ( updated ) , callback = getArcOnClick ( ) ;
toggleShape ( this , d , i ) ;
callback ( arcData , i ) ;
} ) ;
mainArc
. attr ( "transform" , withTransform ? "scale(0)" : "" )
. style ( "opacity" , function ( d ) { return d === this . _current ? 0 : 1 ; } )
. each ( function ( ) { transiting = true ; } )
. transition ( ) . duration ( duration )
. attrTween ( "d" , function ( d ) {
var updated = updateAngle ( d ) , interpolate ;
if ( ! updated ) {
return function ( ) { return "M 0 0" ; } ;
}
/ *
if ( this . _current === d ) {
this . _current = {
startAngle : Math . PI * 2 ,
endAngle : Math . PI * 2 ,
} ;
}
* /
if ( isNaN ( this . _current . endAngle ) ) this . _current . endAngle = this . _current . startAngle ;
interpolate = d3 . interpolate ( this . _current , updated ) ;
this . _current = interpolate ( 0 ) ;
return function ( t ) { return getArc ( interpolate ( t ) , true ) ; } ;
} )
. attr ( "transform" , withTransform ? "scale(1)" : "" )
. style ( "fill" , function ( d ) { return ( _ _gauge _style === 'arc' && _ _color _values ) ? levelColor ( d . data . values [ 0 ] . value ) : color ( d . data . id ) ; } ) // Where gauge reading color would receive customization.//color(d.data); })
. style ( "opacity" , ( _ _color _opacity ) ? _ _color _opacity : 1 )
. call ( endall , function ( ) {
transiting = false ;
} ) ;
mainArc . exit ( ) . transition ( ) . duration ( durationForExit )
. style ( 'opacity' , 0 )
. remove ( ) ;
main . selectAll ( '.' + CLASS . chartArc ) . select ( 'text' )
. style ( "opacity" , 0 )
. text ( textForArcLabel )
. attr ( "transform" , transformForArcLabel )
. transition ( ) . duration ( duration )
. style ( "opacity" , function ( d ) { return isTargetToShow ( d . data . id ) && isArcType ( d . data ) ? 1 : 0 ; } ) ;
if ( _ _gauge _style === "arc" ) {
main . selectAll ( '.' + CLASS . chartArc ) . select ( 'text.units' )
. attr ( "transform" , transformForArcLabel )
. style ( "opacity" , 0 )
. transition ( ) . duration ( duration )
. text ( ( _ _gauge _label _show ) ? _ _gauge _units : '' )
. style ( "opacity" , function ( d ) { return isTargetToShow ( d . data . id ) && isArcType ( d . data ) ? 1 : 0 ; } ) ;
main . selectAll ( '.' + CLASS . chartArc ) . select ( 'text.min' )
. attr ( "transform" , transformForArcLabel )
. style ( "opacity" , 0 )
. transition ( ) . duration ( duration )
. text ( ( _ _gauge _label _show ) ? _ _gauge _min : '' )
. style ( "opacity" , function ( d ) { return isTargetToShow ( d . data . id ) && isArcType ( d . data ) ? 1 : 0 ; } ) ;
main . selectAll ( '.' + CLASS . chartArc ) . select ( 'text.max' )
. attr ( "transform" , transformForArcLabel )
. style ( "opacity" , 0 )
. transition ( ) . duration ( duration )
. text ( ( _ _gauge _label _show ) ? _ _gauge _max : '' )
. style ( "opacity" , function ( d ) { return isTargetToShow ( d . data . id ) && isArcType ( d . data ) ? 1 : 0 ; } ) ;
}
main . select ( '.' + CLASS . chartArcsTitle )
. style ( "opacity" , hasDonutType ( c3 . data . targets ) || hasGaugeType ( c3 . data . targets ) ? 1 : 0 ) ;
// subchart
if ( _ _subchart _show ) {
// reflect main chart to extent on subchart if zoomed
if ( d3 . event && d3 . event . type === 'zoom' ) {
brush . extent ( x . orgDomain ( ) ) . update ( ) ;
}
// update subchart elements if needed
if ( withSubchart ) {
// rotate tick text if needed
if ( ! _ _axis _rotated && _ _axis _x _tick _rotate ) {
rotateTickText ( axes . subx , transitions . axisSubX , _ _axis _x _tick _rotate ) ;
}
// extent rect
if ( ! brush . empty ( ) ) {
brush . extent ( x . orgDomain ( ) ) . update ( ) ;
}
// setup drawer - MEMO: this must be called after axis updated
drawAreaOnSub = generateDrawArea ( areaIndices , true ) ;
drawBarOnSub = generateDrawBar ( barIndices , true ) ;
drawLineOnSub = generateDrawLine ( lineIndices , true ) ;
// bars
contextBar = context . selectAll ( '.' + CLASS . bars ) . selectAll ( '.' + CLASS . bar )
. data ( barData ) ;
contextBar . enter ( ) . append ( 'path' )
. attr ( "class" , classBar )
. style ( "stroke" , 'none' )
. style ( "fill" , color ) ;
contextBar
. style ( "opacity" , initialOpacity )
. transition ( ) . duration ( duration )
. attr ( 'd' , drawBarOnSub )
. style ( 'opacity' , 1 ) ;
contextBar . exit ( ) . transition ( ) . duration ( duration )
. style ( 'opacity' , 0 )
. remove ( ) ;
// lines
contextLine = context . selectAll ( '.' + CLASS . lines ) . selectAll ( '.' + CLASS . line )
. data ( lineData ) ;
contextLine . enter ( ) . append ( 'path' )
. attr ( 'class' , classLine )
. style ( 'stroke' , color ) ;
contextLine
. style ( "opacity" , initialOpacity )
. transition ( ) . duration ( duration )
. attr ( "d" , drawLineOnSub )
. style ( 'opacity' , 1 ) ;
contextLine . exit ( ) . transition ( ) . duration ( duration )
. style ( 'opacity' , 0 )
. remove ( ) ;
// steps
contextStep = context . selectAll ( '.' + CLASS . steps ) . selectAll ( '.' + CLASS . step )
. data ( stepData ) ;
contextStep . enter ( ) . append ( 'path' )
. attr ( 'class' , classLine )
. style ( 'stroke' , color ) ;
contextStep
. style ( "opacity" , initialOpacity )
. transition ( ) . duration ( duration )
. attr ( "d" , drawLineOnSub )
. style ( 'opacity' , 1 ) ;
contextStep . exit ( ) . transition ( ) . duration ( duration )
. style ( 'opacity' , 0 )
. remove ( ) ;
// area
contextArea = context . selectAll ( '.' + CLASS . areas ) . selectAll ( '.' + CLASS . area )
. data ( ( isStepType ? stepData : lineData ) ) ;
contextArea . enter ( ) . append ( 'path' )
. attr ( "class" , classArea )
. style ( "fill" , color )
. style ( "opacity" , function ( ) { orgAreaOpacity = + d3 . select ( this ) . style ( 'opacity' ) ; return 0 ; } ) ;
contextArea
. style ( "opacity" , 0 )
. transition ( ) . duration ( duration )
. attr ( "d" , drawAreaOnSub )
. style ( "fill" , color )
. style ( "opacity" , orgAreaOpacity ) ;
contextArea . exit ( ) . transition ( ) . duration ( durationForExit )
. style ( 'opacity' , 0 )
. remove ( ) ;
}
}
// circles for select
main . selectAll ( '.' + CLASS . selectedCircles )
. filter ( function ( d ) { return isBarType ( d ) ; } )
. selectAll ( 'circle' )
. remove ( ) ;
main . selectAll ( '.' + CLASS . selectedCircle )
. transition ( ) . duration ( duration )
. attr ( "cx" , _ _axis _rotated ? circleY : circleX )
. attr ( "cy" , _ _axis _rotated ? circleX : circleY ) ;
if ( _ _interaction _enabled ) {
// rect for mouseover
eventRect = main . select ( '.' + CLASS . eventRects ) ;
if ( notEmpty ( _ _data _xs ) && ! isSingleX ( _ _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 becuase 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 , i ) {
var prevX = getPrevX ( i ) , nextX = getNextX ( i ) , dx = c3 . data . xs [ d . id ] [ i ] ;
var xnX = x ( nextX ? nextX : dx ) ;
var xpX = x ( prevX ? prevX : dx ) ;
return ( xnX - xpX ) / 2 ;
} ;
rectX = function ( d , i ) {
var prevX = getPrevX ( i ) , dx = c3 . data . xs [ d . id ] [ i ] ;
return ( x ( dx ) + x ( prevX ? prevX : dx ) ) / 2 ;
} ;
} else {
rectW = getEventRectWidth ( ) ;
rectX = function ( d ) {
return x ( d . x ) - ( rectW / 2 ) ;
} ;
}
// Set data
maxDataCountTarget = getMaxDataCountTarget ( c3 . 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' , classEvent )
. attr ( "x" , _ _axis _rotated ? 0 : rectX )
. attr ( "y" , _ _axis _rotated ? rectX : 0 )
. attr ( "width" , _ _axis _rotated ? width : rectW )
. attr ( "height" , _ _axis _rotated ? rectW : height ) ;
// exit
eventRectUpdate . exit ( ) . remove ( ) ;
}
}
// update fadein condition
mapToIds ( c3 . data . targets ) . forEach ( function ( id ) {
withoutFadeIn [ id ] = true ;
} ) ;
}
function redrawForBrush ( ) {
redraw ( {
withTransition : false ,
withY : false ,
withSubchart : false ,
withUpdateXDomain : true
} ) ;
}
function redrawForZoom ( ) {
if ( filterTargetsToShow ( c3 . data . targets ) . length === 0 ) {
return ;
}
if ( d3 . event . sourceEvent . type === 'mousemove' && zoom . altDomain ) {
x . domain ( zoom . altDomain ) ;
zoom . scale ( x ) . updateScaleExtent ( ) ;
return ;
}
if ( isCategorized && x . orgDomain ( ) [ 0 ] === orgXDomain [ 0 ] ) {
x . domain ( [ orgXDomain [ 0 ] - 1e-10 , x . orgDomain ( ) [ 1 ] ] ) ;
}
redraw ( {
withTransition : false ,
withY : false ,
withSubchart : false
} ) ;
if ( d3 . event . sourceEvent . type === 'mousemove' ) {
cancelClick = true ;
}
}
function generateResize ( ) {
var resizeFunctions = [ ] ;
function callResizeFunctions ( ) {
resizeFunctions . forEach ( function ( f ) {
f ( ) ;
} ) ;
}
callResizeFunctions . add = function ( f ) {
resizeFunctions . push ( f ) ;
} ;
return callResizeFunctions ;
}
function updateSvgSize ( ) {
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' , getXAxisClipX )
. attr ( 'y' , getXAxisClipY )
. attr ( 'width' , getXAxisClipWidth )
. attr ( 'height' , getXAxisClipHeight ) ;
svg . select ( '#' + clipIdForYAxis ) . select ( 'rect' )
. attr ( 'x' , getYAxisClipX )
. attr ( 'y' , getYAxisClipY )
. attr ( 'width' , getYAxisClipWidth )
. attr ( 'height' , 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" ) ;
}
function generateAxisTransitions ( duration ) {
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 ,
} ;
}
function updateAndRedraw ( options ) {
var 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 ;
// MEMO: called in updateLegend in redraw if withLegend
if ( ! ( options . withLegend && _ _legend _show ) ) {
transitions = generateAxisTransitions ( options . withTransitionForAxis ? _ _transition _duration : 0 ) ;
// Update sizes and scales
updateSizes ( ) ;
updateScales ( ) ;
updateSvgSize ( ) ;
// Update g positions
transformAll ( options . withTransition , transitions ) ;
}
// Draw with new sizes & scales
redraw ( options , transitions ) ;
}
function updateTargets ( targets ) {
var mainLineEnter , mainLineUpdate , mainBarEnter , mainBarUpdate , mainPieEnter , mainPieUpdate , mainTextUpdate , mainTextEnter ;
var contextLineEnter , contextLineUpdate , contextBarEnter , contextBarUpdate ;
/*-- Main --*/
//-- Text --//
mainTextUpdate = main . select ( '.' + CLASS . chartTexts ) . selectAll ( '.' + CLASS . chartText )
. data ( targets )
. attr ( 'class' , classChartText ) ;
mainTextEnter = mainTextUpdate . enter ( ) . append ( 'g' )
. attr ( 'class' , classChartText )
. style ( 'opacity' , 0 )
. style ( "pointer-events" , "none" ) ;
mainTextEnter . append ( 'g' )
. attr ( 'class' , classTexts ) ;
//-- Bar --//
mainBarUpdate = main . select ( '.' + CLASS . chartBars ) . selectAll ( '.' + CLASS . chartBar )
. data ( targets )
. attr ( 'class' , classChartBar ) ;
mainBarEnter = mainBarUpdate . enter ( ) . append ( 'g' )
. attr ( 'class' , classChartBar )
. style ( 'opacity' , 0 )
. style ( "pointer-events" , "none" ) ;
// Bars for each data
mainBarEnter . append ( 'g' )
. attr ( "class" , classBars )
. style ( "cursor" , function ( d ) { return _ _data _selection _isselectable ( d ) ? "pointer" : null ; } ) ;
//-- Line --//
mainLineUpdate = main . select ( '.' + CLASS . chartLines ) . selectAll ( '.' + CLASS . chartLine )
. data ( targets )
. attr ( 'class' , classChartLine ) ;
mainLineEnter = mainLineUpdate . enter ( ) . append ( 'g' )
. attr ( 'class' , classChartLine )
. style ( 'opacity' , 0 )
. style ( "pointer-events" , "none" ) ;
// Lines for each data
mainLineEnter . append ( 'g' )
. attr ( "class" , classLines ) ;
// Steps
mainLineEnter . append ( 'g' )
. attr ( 'class' , classSteps ) ;
// Areas
mainLineEnter . append ( 'g' )
. attr ( 'class' , 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" , classCircles )
. style ( "cursor" , function ( d ) { return _ _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();
//-- Pie --//
mainPieUpdate = main . select ( '.' + CLASS . chartArcs ) . selectAll ( '.' + CLASS . chartArc )
. data ( pie ( targets ) )
. attr ( "class" , classChartArc ) ;
mainPieEnter = mainPieUpdate . enter ( ) . append ( "g" )
. attr ( "class" , classChartArc ) ;
mainPieEnter . append ( 'g' )
. attr ( 'class' , classArcs ) ;
mainPieEnter . append ( "text" )
. attr ( "dy" , _ _gauge _style === "arc" ? "-0.35em" : ".35em" )
. style ( "opacity" , 0 )
. style ( "text-anchor" , "middle" )
. style ( "pointer-events" , "none" )
. style ( "font-size" , width / 10 + "px" ) ;
if ( _ _gauge _style === "arc" ) {
mainPieEnter . select ( 'text' ) . style ( 'fill' , '#000' ) ;
mainPieEnter . append ( "text" )
. attr ( "dy" , ".75em" )
. attr ( "class" , "units" )
. style ( "opacity" , 0 )
. style ( "text-anchor" , "middle" )
. style ( "pointer-events" , "none" )
. style ( 'fill' , '#000' )
. style ( "font-size" , width / 15 + "px" ) ;
mainPieEnter . append ( "text" )
. attr ( "dx" , - 1 * ( innerRadius + ( ( radius - innerRadius ) / 2 ) ) + "px" )
. attr ( "dy" , "1em" )
. attr ( "class" , "min" )
. style ( "opacity" , 0 )
. style ( "text-anchor" , "middle" )
. style ( "pointer-events" , "none" )
. style ( 'fill' , '#777' )
. style ( "font-size" , width / 20 + "px" ) ;
mainPieEnter . append ( "text" )
. attr ( "dx" , innerRadius + ( ( radius - innerRadius ) / 2 ) + "px" )
. attr ( "dy" , "1em" )
. attr ( "class" , "max" )
. style ( "opacity" , 0 )
. style ( "text-anchor" , "middle" )
. style ( "pointer-events" , "none" )
. style ( 'fill' , '#777' )
. style ( "font-size" , width / 20 + "px" ) ;
}
// MEMO: can not keep same color..., but not bad to update color in redraw
//mainPieUpdate.exit().remove();
/*-- Context --*/
if ( _ _subchart _show ) {
contextBarUpdate = context . select ( '.' + CLASS . chartBars ) . selectAll ( '.' + CLASS . chartBar )
. data ( targets )
. attr ( 'class' , classChartBar ) ;
contextBarEnter = contextBarUpdate . enter ( ) . append ( 'g' )
. style ( 'opacity' , 0 )
. attr ( 'class' , classChartBar ) ;
// Bars for each data
contextBarEnter . append ( 'g' )
. attr ( "class" , classBars ) ;
//-- Line --//
contextLineUpdate = context . select ( '.' + CLASS . chartLines ) . selectAll ( '.' + CLASS . chartLine )
. data ( targets )
. attr ( 'class' , classChartLine ) ;
contextLineEnter = contextLineUpdate . enter ( ) . append ( 'g' )
. style ( 'opacity' , 0 )
. attr ( 'class' , classChartLine ) ;
// Lines for each data
contextLineEnter . append ( "g" )
. attr ( "class" , classLines ) ;
// Steps
contextLineEnter . append ( "g" )
. attr ( "class" , classSteps ) ;
// Area
contextLineEnter . append ( "g" )
. attr ( "class" , classAreas ) ;
}
/*-- Show --*/
// Fade-in each chart
svg . selectAll ( '.' + CLASS . target ) . filter ( function ( d ) { return isTargetToShow ( d . id ) ; } )
. transition ( ) . duration ( _ _transition _duration )
. style ( "opacity" , 1 ) ;
}
function load ( targets , args ) {
// filter loading targets if needed
if ( args . filter ) {
targets = targets . filter ( args . filter ) ;
}
// set type if args.types || args.type specified
if ( args . type || args . types ) {
targets . forEach ( function ( t ) {
args . types ? setTargetType ( t . id , args . types [ t . id ] ) : setTargetType ( t . id , args . type ) ;
} ) ;
}
// Update/Add data
c3 . data . targets . forEach ( function ( d ) {
for ( var i = 0 ; i < targets . length ; i ++ ) {
if ( d . id === targets [ i ] . id ) {
d . values = targets [ i ] . values ;
targets . splice ( i , 1 ) ;
break ;
}
}
} ) ;
c3 . data . targets = c3 . data . targets . concat ( targets ) ; // add remained
// Set targets
updateTargets ( c3 . data . targets ) ;
// Redraw with new targets
redraw ( { withUpdateOrgXDomain : true , withUpdateXDomain : true , withLegend : true } ) ;
if ( typeof args . done === 'function' ) {
args . done ( ) ;
}
}
function loadFromArgs ( args ) {
// load data
if ( 'data' in args ) {
load ( convertDataToTargets ( args . data ) , args ) ;
}
else if ( 'url' in args ) {
d3 . csv ( args . url , function ( error , data ) {
load ( convertDataToTargets ( data ) , args ) ;
} ) ;
}
else if ( 'rows' in args ) {
load ( convertDataToTargets ( convertRowsToData ( args . rows ) ) , args ) ;
}
else if ( 'columns' in args ) {
load ( convertDataToTargets ( convertColumnsToData ( args . columns ) ) , args ) ;
}
else {
throw Error ( 'url or rows or columns is required.' ) ;
}
}
function unload ( targetIds , done ) {
if ( typeof done !== 'function' ) {
done = function ( ) { } ;
}
// filter existing target
targetIds = targetIds . filter ( function ( id ) { return hasTarget ( c3 . data . targets , id ) ; } ) ;
// If no target, call done and return
if ( ! targetIds || targetIds . length === 0 ) {
done ( ) ;
return ;
}
svg . selectAll ( targetIds . map ( function ( id ) { return selectorTarget ( id ) ; } ) )
. transition ( )
. style ( 'opacity' , 0 )
. remove ( )
. call ( endall , done ) ;
targetIds . forEach ( function ( id ) {
// Reset fadein for future load
withoutFadeIn [ id ] = false ;
// Remove target's elements
legend . selectAll ( '.' + CLASS . legendItem + getTargetSelectorSuffix ( id ) ) . remove ( ) ;
// Remove target
c3 . data . targets = c3 . data . targets . filter ( function ( t ) {
return t . id !== id ;
} ) ;
} ) ;
}
/*-- Draw Legend --*/
function opacityForLegend ( legendItem ) {
return legendItem . classed ( CLASS . legendItemHidden ) ? legendOpacityForHidden : 1 ;
}
function opacityForUnfocusedLegend ( legendItem ) {
return legendItem . classed ( CLASS . legendItemHidden ) ? legendOpacityForHidden : 0.3 ;
}
function toggleFocusLegend ( id , focus ) {
legend . selectAll ( '.' + CLASS . legendItem )
. transition ( ) . duration ( 100 )
. style ( 'opacity' , function ( _id ) {
var This = d3 . select ( this ) ;
if ( id && _id !== id ) {
return focus ? opacityForUnfocusedLegend ( This ) : opacityForLegend ( This ) ;
} else {
return focus ? opacityForLegend ( This ) : opacityForUnfocusedLegend ( This ) ;
}
} ) ;
}
function revertLegend ( ) {
legend . selectAll ( '.' + CLASS . legendItem )
. transition ( ) . duration ( 100 )
. style ( 'opacity' , function ( ) { return opacityForLegend ( d3 . select ( this ) ) ; } ) ;
}
function showLegend ( targetIds ) {
if ( ! _ _legend _show ) {
_ _legend _show = true ;
legend . style ( 'visibility' , 'visible' ) ;
}
removeHiddenLegendIds ( targetIds ) ;
legend . selectAll ( selectorLegends ( targetIds ) )
. style ( 'visibility' , 'visible' )
. transition ( )
. style ( 'opacity' , function ( ) { return opacityForLegend ( d3 . select ( this ) ) ; } ) ;
}
function hideLegend ( targetIds ) {
if ( _ _legend _show && isEmpty ( targetIds ) ) {
_ _legend _show = false ;
legend . style ( 'visibility' , 'hidden' ) ;
}
addHiddenLegendIds ( targetIds ) ;
legend . selectAll ( selectorLegends ( targetIds ) )
. style ( 'opacity' , 0 )
. style ( 'visibility' , 'hidden' ) ;
}
function updateLegend ( targetIds , options , transitions ) {
var xForLegend , xForLegendText , xForLegendRect , yForLegend , yForLegendText , yForLegendRect ;
var paddingTop = 4 , paddingRight = 26 , maxWidth = 0 , maxHeight = 0 , posMin = 10 ;
var l , totalLength = 0 , offsets = { } , widths = { } , heights = { } , margins = [ 0 ] , steps = { } , step = 0 ;
var withTransition , withTransitionForTransform ;
var hasFocused = legend . selectAll ( '.' + CLASS . legendItemFocused ) . size ( ) ;
var texts , rects , tiles ;
options = options || { } ;
withTransition = getOption ( options , "withTransition" , true ) ;
withTransitionForTransform = getOption ( options , "withTransitionForTransform" , true ) ;
function updatePositions ( textElement , id , reset ) {
var box = getTextRect ( textElement . textContent , CLASS . legendItem ) ,
itemWidth = Math . ceil ( ( box . width + paddingRight ) / 10 ) * 10 ,
itemHeight = Math . ceil ( ( box . height + paddingTop ) / 10 ) * 10 ,
itemLength = isLegendRight ? itemHeight : itemWidth ,
areaLength = isLegendRight ? getLegendHeight ( ) : getLegendWidth ( ) ,
margin , maxLength ;
// MEMO: care about condifion of step, totalLength
function updateValues ( id , withoutStep ) {
if ( ! withoutStep ) {
margin = ( areaLength - totalLength - itemLength ) / 2 ;
if ( margin < posMin ) {
margin = ( areaLength - itemLength ) / 2 ;
totalLength = 0 ;
step ++ ;
}
}
steps [ id ] = step ;
margins [ step ] = margin ;
offsets [ id ] = totalLength ;
totalLength += itemLength ;
}
if ( reset ) {
totalLength = 0 ;
step = 0 ;
maxWidth = 0 ;
maxHeight = 0 ;
}
if ( _ _legend _show && ! isLegendToShow ( id ) ) {
widths [ id ] = heights [ id ] = steps [ id ] = offsets [ id ] = 0 ;
return ;
}
widths [ id ] = itemWidth ;
heights [ id ] = itemHeight ;
if ( ! maxWidth || itemWidth >= maxWidth ) { maxWidth = itemWidth ; }
if ( ! maxHeight || itemHeight >= maxHeight ) { maxHeight = itemHeight ; }
maxLength = isLegendRight ? maxHeight : maxWidth ;
if ( _ _legend _equally ) {
Object . keys ( widths ) . forEach ( function ( id ) { widths [ id ] = maxWidth ; } ) ;
Object . keys ( heights ) . forEach ( function ( id ) { heights [ id ] = maxHeight ; } ) ;
margin = ( areaLength - maxLength * targetIds . length ) / 2 ;
if ( margin < posMin ) {
totalLength = 0 ;
step = 0 ;
targetIds . forEach ( function ( id ) { updateValues ( id ) ; } ) ;
}
else {
updateValues ( id , true ) ;
}
} else {
updateValues ( id ) ;
}
}
if ( isLegendRight ) {
xForLegend = function ( id ) { return maxWidth * steps [ id ] ; } ;
yForLegend = function ( id ) { return margins [ steps [ id ] ] + offsets [ id ] ; } ;
} else {
xForLegend = function ( id ) { return margins [ steps [ id ] ] + offsets [ id ] ; } ;
yForLegend = function ( id ) { return maxHeight * steps [ id ] ; } ;
}
xForLegendText = function ( id , i ) { return xForLegend ( id , i ) + 14 ; } ;
yForLegendText = function ( id , i ) { return yForLegend ( id , i ) + 9 ; } ;
xForLegendRect = function ( id , i ) { return xForLegend ( id , i ) - 4 ; } ;
yForLegendRect = function ( id , i ) { return yForLegend ( id , i ) - 7 ; } ;
// Define g for legend area
l = legend . selectAll ( '.' + CLASS . legendItem )
. data ( targetIds )
. enter ( ) . append ( 'g' )
. attr ( 'class' , function ( id ) { return generateClass ( CLASS . legendItem , id ) ; } )
. style ( 'visibility' , function ( id ) { return isLegendToShow ( id ) ? 'visible' : 'hidden' ; } )
. style ( 'cursor' , 'pointer' )
. on ( 'click' , function ( id ) {
typeof _ _legend _item _onclick === 'function' ? _ _legend _item _onclick ( id ) : c3 . toggle ( id ) ;
} )
. on ( 'mouseover' , function ( id ) {
d3 . select ( this ) . classed ( CLASS . legendItemFocused , true ) ;
if ( ! transiting ) {
c3 . focus ( id ) ;
}
if ( typeof _ _legend _item _onmouseover === 'function' ) {
_ _legend _item _onmouseover ( id ) ;
}
} )
. on ( 'mouseout' , function ( id ) {
d3 . select ( this ) . classed ( CLASS . legendItemFocused , false ) ;
if ( ! transiting ) {
c3 . revert ( ) ;
}
if ( typeof _ _legend _item _onmouseout === 'function' ) {
_ _legend _item _onmouseout ( id ) ;
}
} ) ;
l . append ( 'text' )
. text ( function ( id ) { return isDefined ( _ _data _names [ id ] ) ? _ _data _names [ id ] : id ; } )
. each ( function ( id , i ) { updatePositions ( this , id , i === 0 ) ; } )
. style ( "pointer-events" , "none" )
. attr ( 'x' , isLegendRight ? xForLegendText : - 200 )
. attr ( 'y' , isLegendRight ? - 200 : yForLegendText ) ;
l . append ( 'rect' )
. attr ( "class" , CLASS . legendItemEvent )
. style ( 'fill-opacity' , 0 )
. attr ( 'x' , isLegendRight ? xForLegendRect : - 200 )
. attr ( 'y' , isLegendRight ? - 200 : yForLegendRect ) ;
l . append ( 'rect' )
. attr ( "class" , CLASS . legendItemTile )
. style ( "pointer-events" , "none" )
. style ( 'fill' , color )
. attr ( 'x' , isLegendRight ? xForLegendText : - 200 )
. attr ( 'y' , isLegendRight ? - 200 : yForLegend )
. attr ( 'width' , 10 )
. attr ( 'height' , 10 ) ;
texts = legend . selectAll ( 'text' )
. data ( targetIds )
. text ( function ( id ) { return isDefined ( _ _data _names [ id ] ) ? _ _data _names [ id ] : id ; } ) // MEMO: needed for update
. each ( function ( id , i ) { updatePositions ( this , id , i === 0 ) ; } ) ;
( withTransition ? texts . transition ( ) : texts )
. attr ( 'x' , xForLegendText )
. attr ( 'y' , yForLegendText ) ;
rects = legend . selectAll ( 'rect.' + CLASS . legendItemEvent )
. data ( targetIds ) ;
( withTransition ? rects . transition ( ) : rects )
. attr ( 'width' , function ( id ) { return widths [ id ] ; } )
. attr ( 'height' , function ( id ) { return heights [ id ] ; } )
. attr ( 'x' , xForLegendRect )
. attr ( 'y' , yForLegendRect ) ;
tiles = legend . selectAll ( 'rect.' + CLASS . legendItemTile )
. data ( targetIds ) ;
( withTransition ? tiles . transition ( ) : tiles )
. style ( 'fill' , color )
. attr ( 'x' , xForLegend )
. attr ( 'y' , yForLegend ) ;
// toggle legend state
legend . selectAll ( '.' + CLASS . legendItem )
. classed ( CLASS . legendItemHidden , function ( id ) { return ! isTargetToShow ( id ) ; } )
. transition ( )
. style ( 'opacity' , function ( id ) {
var This = d3 . select ( this ) ;
if ( isTargetToShow ( id ) ) {
return ! hasFocused || This . classed ( CLASS . legendItemFocused ) ? opacityForLegend ( This ) : opacityForUnfocusedLegend ( This ) ;
} else {
return legendOpacityForHidden ;
}
} ) ;
// Update all to reflect change of legend
updateLegendItemWidth ( maxWidth ) ;
updateLegendItemHeight ( maxHeight ) ;
updateLegendStep ( step ) ;
// Update size and scale
updateSizes ( ) ;
updateScales ( ) ;
updateSvgSize ( ) ;
// Update g positions
transformAll ( withTransitionForTransform , transitions ) ;
}
/*-- Event Handling --*/
function isNoneArc ( d ) {
return hasTarget ( c3 . data . targets , d . id ) ;
}
function isArc ( d ) {
return 'data' in d && hasTarget ( c3 . data . targets , d . data . id ) ;
}
function getGridFilter ( params ) {
var value = params && params . value ? params . value : null ,
klass = params && params [ 'class' ] ? params [ 'class' ] : null ;
return value ? function ( line ) { return line . value !== value ; } : klass ? function ( line ) { return line [ 'class' ] !== klass ; } : function ( ) { return true ; } ;
}
function transformTo ( targetIds , type , optionsForRedraw ) {
var withTransitionForAxis = ! hasArcType ( c3 . data . targets ) ;
transiting = false ;
setTargetType ( targetIds , type ) ;
updateAndRedraw ( optionsForRedraw || { withTransitionForAxis : withTransitionForAxis } ) ;
}
c3 . focus = function ( targetId ) {
var candidates = svg . selectAll ( selectorTarget ( targetId ) ) ,
candidatesForNoneArc = candidates . filter ( isNoneArc ) ,
candidatesForArc = candidates . filter ( isArc ) ;
function focus ( targets ) {
filterTargetsToShow ( targets ) . transition ( ) . duration ( 100 ) . style ( 'opacity' , 1 ) ;
}
c3 . revert ( ) ;
c3 . defocus ( ) ;
focus ( candidatesForNoneArc . classed ( CLASS . focused , true ) ) ;
focus ( candidatesForArc ) ;
if ( hasArcType ( c3 . data . targets ) ) {
expandArc ( targetId , true ) ;
}
toggleFocusLegend ( targetId , true ) ;
} ;
c3 . defocus = function ( targetId ) {
var candidates = svg . selectAll ( selectorTarget ( targetId ) ) ,
candidatesForNoneArc = candidates . filter ( isNoneArc ) ,
candidatesForArc = candidates . filter ( isArc ) ;
function defocus ( targets ) {
filterTargetsToShow ( targets ) . transition ( ) . duration ( 100 ) . style ( 'opacity' , 0.3 ) ;
}
c3 . revert ( ) ;
defocus ( candidatesForNoneArc . classed ( CLASS . focused , false ) ) ;
defocus ( candidatesForArc ) ;
if ( hasArcType ( c3 . data . targets ) ) {
unexpandArc ( targetId ) ;
}
toggleFocusLegend ( targetId , false ) ;
} ;
c3 . revert = function ( targetId ) {
var candidates = svg . selectAll ( selectorTarget ( targetId ) ) ,
candidatesForNoneArc = candidates . filter ( isNoneArc ) ,
candidatesForArc = candidates . filter ( isArc ) ;
function revert ( targets ) {
filterTargetsToShow ( targets ) . transition ( ) . duration ( 100 ) . style ( 'opacity' , 1 ) ;
}
revert ( candidatesForNoneArc . classed ( CLASS . focused , false ) ) ;
revert ( candidatesForArc ) ;
if ( hasArcType ( c3 . data . targets ) ) {
unexpandArc ( targetId ) ;
}
revertLegend ( ) ;
} ;
c3 . show = function ( targetIds , options ) {
targetIds = mapToTargetIds ( targetIds ) ;
options = options || { } ;
removeHiddenTargetIds ( targetIds ) ;
svg . selectAll ( selectorTargets ( targetIds ) )
. transition ( )
. style ( 'opacity' , 1 ) ;
if ( options . withLegend ) {
showLegend ( targetIds ) ;
}
redraw ( { withUpdateOrgXDomain : true , withUpdateXDomain : true , withLegend : true } ) ;
} ;
c3 . hide = function ( targetIds , options ) {
targetIds = mapToTargetIds ( targetIds ) ;
options = options || { } ;
addHiddenTargetIds ( targetIds ) ;
svg . selectAll ( selectorTargets ( targetIds ) )
. transition ( )
. style ( 'opacity' , 0 ) ;
if ( options . withLegend ) {
hideLegend ( targetIds ) ;
}
redraw ( { withUpdateOrgXDomain : true , withUpdateXDomain : true , withLegend : true } ) ;
} ;
c3 . toggle = function ( targetId ) {
isTargetToShow ( targetId ) ? c3 . hide ( targetId ) : c3 . show ( targetId ) ;
} ;
c3 . unzoom = function ( ) {
brush . clear ( ) . update ( ) ;
redraw ( { withUpdateXDomain : true } ) ;
} ;
c3 . load = function ( args ) {
// update xs if specified
if ( args . xs ) {
addXs ( args . xs ) ;
}
// update classes if exists
if ( 'classes' in args ) {
Object . keys ( args . classes ) . forEach ( function ( id ) {
_ _data _classes [ id ] = args . classes [ id ] ;
} ) ;
}
// update categories if exists
if ( 'categories' in args && isCategorized ) {
_ _axis _x _categories = args . categories ;
xAxis . categories ( _ _axis _x _categories ) ;
}
// 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 . unload = function ( targetIds , done ) {
unload ( mapToTargetIds ( targetIds ) , function ( ) {
redraw ( { withUpdateOrgXDomain : true , withUpdateXDomain : true , withLegend : true } ) ;
if ( typeof done === 'function' ) { done ( ) ; }
} ) ;
} ;
c3 . selected = function ( targetId ) {
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 . select = function ( ids , indices , resetOther ) {
if ( ! _ _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 ) ,
isTargetId = _ _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 ( _ _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 . unselect = function ( ids , indices ) {
if ( ! _ _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 ) ,
isTargetId = _ _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 ( _ _data _selection _isselectable ( d ) ) {
if ( isSelected ) {
toggle ( false , shape . classed ( CLASS . SELECTED , false ) , d , i ) ;
}
}
}
} ) ;
} ;
c3 . toLine = function ( targetIds ) {
transformTo ( targetIds , 'line' ) ;
} ;
c3 . toSpline = function ( targetIds ) {
transformTo ( targetIds , 'spline' ) ;
} ;
c3 . toBar = function ( targetIds ) {
transformTo ( targetIds , 'bar' ) ;
} ;
c3 . toScatter = function ( targetIds ) {
transformTo ( targetIds , 'scatter' ) ;
} ;
c3 . toArea = function ( targetIds ) {
transformTo ( targetIds , 'area' ) ;
} ;
c3 . toAreaSpline = function ( targetIds ) {
transformTo ( targetIds , 'area-spline' ) ;
} ;
c3 . toPie = function ( targetIds ) {
transformTo ( targetIds , 'pie' , { withTransform : true } ) ;
} ;
c3 . toDonut = function ( targetIds ) {
transformTo ( targetIds , 'donut' , { withTransform : true } ) ;
} ;
c3 . groups = function ( groups ) {
if ( isUndefined ( groups ) ) { return _ _data _groups ; }
_ _data _groups = groups ;
redraw ( ) ;
return _ _data _groups ;
} ;
c3 . xgrids = function ( grids ) {
if ( ! grids ) { return _ _grid _x _lines ; }
_ _grid _x _lines = grids ;
redraw ( ) ;
return _ _grid _x _lines ;
} ;
c3 . xgrids . add = function ( grids ) {
if ( ! grids ) { return ; }
return c3 . xgrids ( _ _grid _x _lines . concat ( grids ) ) ;
} ;
c3 . xgrids . remove = function ( params ) { // TODO: multiple
var filter = getGridFilter ( params ) ;
return c3 . xgrids ( _ _grid _x _lines . filter ( filter ) ) ;
} ;
c3 . ygrids = function ( grids ) {
if ( ! grids ) { return _ _grid _y _lines ; }
_ _grid _y _lines = grids ;
redraw ( ) ;
return _ _grid _y _lines ;
} ;
c3 . ygrids . add = function ( grids ) {
if ( ! grids ) { return ; }
return c3 . ygrids ( _ _grid _y _lines . concat ( grids ) ) ;
} ;
c3 . ygrids . remove = function ( params ) { // TODO: multiple
var filter = getGridFilter ( params ) ;
return c3 . ygrids ( _ _grid _y _lines . filter ( filter ) ) ;
} ;
c3 . regions = function ( regions ) {
if ( ! regions ) { return _ _regions ; }
_ _regions = regions ;
redraw ( ) ;
return _ _regions ;
} ;
c3 . regions . add = function ( regions ) {
if ( ! regions ) { return _ _regions ; }
_ _regions = _ _regions . concat ( regions ) ;
redraw ( ) ;
return _ _regions ;
} ;
c3 . regions . remove = function ( options ) {
var duration , classes , regions ;
options = options || { } ;
duration = getOption ( options , "duration" , _ _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 ( ) ;
_ _regions = _ _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 _ _regions ;
} ;
c3 . data . get = function ( targetId ) {
var target = c3 . data . getAsTarget ( targetId ) ;
return isDefined ( target ) ? target . values . map ( function ( d ) { return d . value ; } ) : undefined ;
} ;
c3 . data . getAsTarget = function ( targetId ) {
var targets = c3 . data . targets . filter ( function ( t ) { return t . id === targetId ; } ) ;
return targets . length > 0 ? targets [ 0 ] : undefined ;
} ;
c3 . data . names = function ( names ) {
if ( ! arguments . length ) { return _ _data _names ; }
Object . keys ( names ) . forEach ( function ( id ) {
_ _data _names [ id ] = names [ id ] ;
} ) ;
redraw ( { withLegend : true } ) ;
return _ _data _names ;
} ;
c3 . data . colors = function ( colors ) {
if ( ! arguments . length ) { return _ _data _colors ; }
Object . keys ( colors ) . forEach ( function ( id ) {
_ _data _colors [ id ] = colors [ id ] ;
} ) ;
redraw ( { withLegend : true } ) ;
return _ _data _colors ;
} ;
c3 . x = function ( x ) {
if ( arguments . length ) {
updateTargetX ( c3 . data . targets , x ) ;
redraw ( { withUpdateOrgXDomain : true , withUpdateXDomain : true } ) ;
}
return c3 . data . xs ;
} ;
c3 . xs = function ( xs ) {
if ( arguments . length ) {
updateTargetXs ( c3 . data . targets , xs ) ;
redraw ( { withUpdateOrgXDomain : true , withUpdateXDomain : true } ) ;
}
return c3 . data . xs ;
} ;
c3 . axis . labels = function ( labels ) {
if ( arguments . length ) {
Object . keys ( labels ) . forEach ( function ( axisId ) {
setAxisLabelText ( axisId , labels [ axisId ] ) ;
} ) ;
updateAxisLabels ( ) ;
}
// TODO: return some values?
} ;
c3 . axis . max = function ( max ) {
if ( arguments . length ) {
if ( typeof max === 'object' ) {
if ( isValue ( max . x ) ) { _ _axis _x _max = + max . x ; }
if ( isValue ( max . y ) ) { _ _axis _y _max = + max . y ; }
if ( isValue ( max . y2 ) ) { _ _axis _y2 _max = + max . y2 ; }
} else {
_ _axis _y _max = _ _axis _y2 _max = + max ;
}
redraw ( { withUpdateOrgXDomain : true , withUpdateXDomain : true } ) ;
}
} ;
c3 . axis . min = function ( min ) {
if ( arguments . length ) {
if ( typeof min === 'object' ) {
if ( isValue ( min . x ) ) { _ _axis _x _min = + min . x ; }
if ( isValue ( min . y ) ) { _ _axis _y _min = + min . y ; }
if ( isValue ( min . y2 ) ) { _ _axis _y2 _min = + min . y2 ; }
} else {
_ _axis _y _min = _ _axis _y2 _min = + min ;
}
redraw ( { withUpdateOrgXDomain : true , withUpdateXDomain : true } ) ;
}
} ;
c3 . axis . range = function ( range ) {
if ( arguments . length ) {
if ( typeof range . max !== 'undefined' ) { c3 . axis . max ( range . max ) ; }
if ( typeof range . min !== 'undefined' ) { c3 . axis . min ( range . min ) ; }
}
} ;
c3 . legend . show = function ( targetIds ) {
showLegend ( mapToTargetIds ( targetIds ) ) ;
redraw ( { withLegend : true } ) ;
} ;
c3 . legend . hide = function ( targetIds ) {
hideLegend ( mapToTargetIds ( targetIds ) ) ;
redraw ( { withLegend : true } ) ;
} ;
c3 . resize = function ( size ) {
_ _size _width = size ? size . width : null ;
_ _size _height = size ? size . height : null ;
c3 . flush ( ) ; // TODO: need to be called twice because of update of legend
c3 . flush ( ) ;
} ;
c3 . flush = function ( ) {
updateAndRedraw ( { withLegend : true , withTransition : false , withTransitionForTransform : false } ) ;
} ;
c3 . destroy = function ( ) {
c3 . data . targets = undefined ;
c3 . data . xs = { } ;
selectChart . classed ( 'c3' , false ) . html ( "" ) ;
window . onresize = null ;
} ;
/*-- Load data and init chart with defined functions --*/
if ( 'url' in config . data ) {
d3 . xhr ( config . data . url , function ( error , data ) {
// TODO: other mine/type
var rows = d3 . csv . parseRows ( data . response ) , d ;
if ( rows . length === 1 ) {
d = [ { } ] ;
rows [ 0 ] . forEach ( function ( id ) {
d [ 0 ] [ id ] = null ;
} ) ;
} else {
d = d3 . csv . parse ( data . response ) ;
}
init ( d ) ;
} ) ;
}
else if ( 'rows' in config . data ) {
init ( convertRowsToData ( config . data . rows ) ) ;
}
else if ( 'columns' in config . data ) {
init ( convertColumnsToData ( config . data . columns ) ) ;
}
else {
throw Error ( 'url or rows or columns is required.' ) ;
}
return c3 ;
} ;
function isValue ( v ) {
return v || v === 0 ;
}
function isUndefined ( v ) {
return typeof v === 'undefined' ;
}
function isDefined ( v ) {
return typeof v !== 'undefined' ;
}
if ( typeof define === "function" && define . amd ) {
define ( "c3" , [ "d3" ] , c3 ) ;
} else if ( 'undefined' !== typeof exports && 'undefined' !== typeof module ) {
module . exports = c3 ;
} else {
window . c3 = c3 ;
}
// Features:
// 1. category axis
// 2. ceil values of translate/x/y to int for half pixel antialiasing
function c3 _axis ( d3 , isCategory ) {
var scale = d3 . scale . linear ( ) , orient = "bottom" , innerTickSize = 6 , outerTickSize = 6 , tickPadding = 3 , tickValues = null , tickFormat , tickArguments ;
var tickOffset = 0 , tickCulling = true ;
var categories = [ ] , tickCentered ;
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 && tickArguments != undefined ) {
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 ( isCategory ) {
domain = scale . domain ( ) ;
newScale . domain ( [ domain [ 0 ] , domain [ 1 ] - 1 ] ) ;
}
return newScale ;
}
function textFormatted ( i ) {
var v = isCategory && i < categories . length ? categories [ i ] : i ;
return tickFormat ? tickFormat ( v ) : v ;
}
function axis ( g ) {
g . each ( function ( ) {
var 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 ) ,
tickExit = d3 . transition ( tick . exit ( ) ) . style ( "opacity" , 1e-6 ) . remove ( ) ,
tickUpdate = d3 . transition ( tick ) . style ( "opacity" , 1 ) ,
tickTransform , tickX ;
var range = scale . rangeExtent ? scale . rangeExtent ( ) : scaleExtent ( scale . range ( ) ) ,
path = g . selectAll ( ".domain" ) . data ( [ 0 ] ) ,
pathUpdate = ( path . enter ( ) . append ( "path" ) . attr ( "class" , "domain" ) , d3 . transition ( path ) ) ;
tickEnter . append ( "line" ) ;
tickEnter . append ( "text" ) ;
var lineEnter = tickEnter . select ( "line" ) ,
lineUpdate = tickUpdate . select ( "line" ) ,
text = tick . select ( "text" ) . text ( textFormatted ) ,
textEnter = tickEnter . select ( "text" ) ,
textUpdate = tickUpdate . select ( "text" ) ;
if ( isCategory ) {
tickOffset = Math . ceil ( ( scale1 ( 1 ) - scale1 ( 0 ) ) / 2 ) ;
tickX = tickCentered ? 0 : tickOffset ;
} else {
tickOffset = tickX = 0 ;
}
function tickSize ( d ) {
var tickPosition = scale ( d ) + tickOffset ;
return range [ 0 ] < tickPosition && tickPosition < range [ 1 ] ? innerTickSize : 0 ;
}
switch ( orient ) {
case "bottom" :
{
tickTransform = axisX ;
lineEnter . attr ( "y2" , innerTickSize ) ;
textEnter . attr ( "y" , Math . max ( innerTickSize , 0 ) + tickPadding ) ;
lineUpdate . attr ( "x1" , tickX ) . attr ( "x2" , tickX ) . attr ( "y2" , tickSize ) ;
textUpdate . attr ( "x" , 0 ) . attr ( "y" , Math . max ( innerTickSize , 0 ) + tickPadding ) ;
text . attr ( "dy" , ".71em" ) . style ( "text-anchor" , "middle" ) ;
pathUpdate . attr ( "d" , "M" + range [ 0 ] + "," + outerTickSize + "V0H" + range [ 1 ] + "V" + outerTickSize ) ;
break ;
}
case "top" :
{
tickTransform = axisX ;
lineEnter . attr ( "y2" , - innerTickSize ) ;
textEnter . attr ( "y" , - ( Math . max ( innerTickSize , 0 ) + tickPadding ) ) ;
lineUpdate . attr ( "x2" , 0 ) . attr ( "y2" , - innerTickSize ) ;
textUpdate . attr ( "x" , 0 ) . attr ( "y" , - ( Math . max ( innerTickSize , 0 ) + tickPadding ) ) ;
text . attr ( "dy" , "0em" ) . style ( "text-anchor" , "middle" ) ;
pathUpdate . attr ( "d" , "M" + range [ 0 ] + "," + - outerTickSize + "V0H" + range [ 1 ] + "V" + - outerTickSize ) ;
break ;
}
case "left" :
{
tickTransform = axisY ;
lineEnter . attr ( "x2" , - innerTickSize ) ;
textEnter . attr ( "x" , - ( Math . max ( innerTickSize , 0 ) + tickPadding ) ) ;
lineUpdate . attr ( "x2" , - innerTickSize ) . attr ( "y2" , 0 ) ;
textUpdate . attr ( "x" , - ( Math . max ( innerTickSize , 0 ) + tickPadding ) ) . attr ( "y" , tickOffset ) ;
text . attr ( "dy" , ".32em" ) . style ( "text-anchor" , "end" ) ;
pathUpdate . attr ( "d" , "M" + - outerTickSize + "," + range [ 0 ] + "H0V" + range [ 1 ] + "H" + - outerTickSize ) ;
break ;
}
case "right" :
{
tickTransform = axisY ;
lineEnter . attr ( "x2" , innerTickSize ) ;
textEnter . attr ( "x" , Math . max ( innerTickSize , 0 ) + tickPadding ) ;
lineUpdate . attr ( "x2" , innerTickSize ) . attr ( "y2" , 0 ) ;
textUpdate . attr ( "x" , Math . max ( innerTickSize , 0 ) + tickPadding ) . attr ( "y" , 0 ) ;
text . attr ( "dy" , ".32em" ) . style ( "text-anchor" , "start" ) ;
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 ( ) { // This will be overwritten when normal x axis
return tickOffset ;
} ;
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 ( ! arguments . length ) { return tickValues ; }
tickValues = x ;
return axis ;
} ;
axis . categories = function ( x ) {
if ( ! arguments . length ) { return categories ; }
categories = x ;
return axis ;
} ;
return axis ;
}
} ) ( window ) ;