You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1325 lines
34 KiB

import { ChartInternal } from './chart-internal'
import { Chart } from './chart'
import { AxisInternal } from './axis-internal'
import Axis from './axis'
import CLASS from './class'
import {
asHalfPixel,
getOption,
getPathBox,
isFunction,
isValue,
notEmpty
} from './util'
var c3 = {
version: '0.7.20',
chart: {
fn: Chart.prototype,
internal: {
fn: ChartInternal.prototype,
axis: {
fn: Axis.prototype,
internal: {
fn: AxisInternal.prototype
}
}
}
},
generate: function(config) {
return new Chart(config)
}
}
export { c3 }
ChartInternal.prototype.beforeInit = function() {
// can do something
}
ChartInternal.prototype.afterInit = function() {
// can do something
}
ChartInternal.prototype.init = function() {
var $$ = this,
config = $$.config
$$.initParams()
if (config.data_url) {
$$.convertUrlToData(
config.data_url,
config.data_mimeType,
config.data_headers,
config.data_keys,
$$.initWithData
)
} else if (config.data_json) {
$$.initWithData($$.convertJsonToData(config.data_json, config.data_keys))
} else if (config.data_rows) {
$$.initWithData($$.convertRowsToData(config.data_rows))
} else if (config.data_columns) {
$$.initWithData($$.convertColumnsToData(config.data_columns))
} else {
throw Error('url or json or rows or columns is required.')
}
}
ChartInternal.prototype.initParams = function() {
var $$ = this,
d3 = $$.d3,
config = $$.config
// MEMO: clipId needs to be unique because it conflicts when multiple charts exist
$$.clipId = 'c3-' + new Date().valueOf() + '-clip'
$$.clipIdForXAxis = $$.clipId + '-xaxis'
$$.clipIdForYAxis = $$.clipId + '-yaxis'
$$.clipIdForGrid = $$.clipId + '-grid'
$$.clipIdForSubchart = $$.clipId + '-subchart'
$$.clipPath = $$.getClipPath($$.clipId)
$$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis)
$$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis)
$$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid)
$$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart)
$$.dragStart = null
$$.dragging = false
$$.flowing = false
$$.cancelClick = false
$$.mouseover = undefined
$$.transiting = false
$$.color = $$.generateColor()
$$.levelColor = $$.generateLevelColor()
$$.dataTimeParse = (config.data_xLocaltime ? d3.timeParse : d3.utcParse)(
$$.config.data_xFormat
)
$$.axisTimeFormat = config.axis_x_localtime ? d3.timeFormat : d3.utcFormat
$$.defaultAxisTimeFormat = function(date) {
if (date.getMilliseconds()) {
return d3.timeFormat('.%L')(date)
}
if (date.getSeconds()) {
return d3.timeFormat(':%S')(date)
}
if (date.getMinutes()) {
return d3.timeFormat('%I:%M')(date)
}
if (date.getHours()) {
return d3.timeFormat('%I %p')(date)
}
if (date.getDay() && date.getDate() !== 1) {
return d3.timeFormat('%-m/%-d')(date)
}
if (date.getDate() !== 1) {
return d3.timeFormat('%-m/%-d')(date)
}
if (date.getMonth()) {
return d3.timeFormat('%-m/%-d')(date)
}
return d3.timeFormat('%Y/%-m/%-d')(date)
}
$$.hiddenTargetIds = []
$$.hiddenLegendIds = []
$$.focusedTargetIds = []
$$.defocusedTargetIds = []
$$.xOrient = config.axis_rotated
? config.axis_x_inner
? 'right'
: 'left'
: config.axis_x_inner
? 'top'
: 'bottom'
$$.yOrient = config.axis_rotated
? config.axis_y_inner
? 'top'
: 'bottom'
: config.axis_y_inner
? 'right'
: 'left'
$$.y2Orient = config.axis_rotated
? config.axis_y2_inner
? 'bottom'
: 'top'
: config.axis_y2_inner
? 'left'
: 'right'
$$.subXOrient = config.axis_rotated ? 'left' : 'bottom'
$$.isLegendRight = config.legend_position === 'right'
$$.isLegendInset = config.legend_position === 'inset'
$$.isLegendTop =
config.legend_inset_anchor === 'top-left' ||
config.legend_inset_anchor === 'top-right'
$$.isLegendLeft =
config.legend_inset_anchor === 'top-left' ||
config.legend_inset_anchor === 'bottom-left'
$$.legendStep = 0
$$.legendItemWidth = 0
$$.legendItemHeight = 0
$$.currentMaxTickWidths = {
x: 0,
y: 0,
y2: 0
}
$$.rotated_padding_left = 30
$$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30
$$.rotated_padding_top = 5
$$.withoutFadeIn = {}
$$.intervalForObserveInserted = undefined
$$.axes.subx = d3.selectAll([]) // needs when excluding subchart.js
}
ChartInternal.prototype.initChartElements = function() {
if (this.initBar) {
this.initBar()
}
if (this.initLine) {
this.initLine()
}
if (this.initArc) {
this.initArc()
}
if (this.initGauge) {
this.initGauge()
}
if (this.initText) {
this.initText()
}
}
ChartInternal.prototype.initWithData = function(data) {
var $$ = this,
d3 = $$.d3,
config = $$.config
var defs,
main,
binding = true
$$.axis = new Axis($$)
if (!config.bindto) {
$$.selectChart = d3.selectAll([])
} else if (typeof config.bindto.node === 'function') {
$$.selectChart = config.bindto
} else {
$$.selectChart = d3.select(config.bindto)
}
if ($$.selectChart.empty()) {
$$.selectChart = d3
.select(document.createElement('div'))
.style('opacity', 0)
$$.observeInserted($$.selectChart)
binding = false
}
$$.selectChart.html('').classed('c3', true)
// Init data as targets
$$.data.xs = {}
$$.data.targets = $$.convertDataToTargets(data)
if (config.data_filter) {
$$.data.targets = $$.data.targets.filter(config.data_filter)
}
// Set targets to hide if needed
if (config.data_hide) {
$$.addHiddenTargetIds(
config.data_hide === true
? $$.mapToIds($$.data.targets)
: config.data_hide
)
}
if (config.legend_hide) {
$$.addHiddenLegendIds(
config.legend_hide === true
? $$.mapToIds($$.data.targets)
: config.legend_hide
)
}
if ($$.isStanfordGraphType()) {
$$.initStanfordData()
}
// Init sizes and scales
$$.updateSizes()
$$.updateScales()
// Set domains for each scale
$$.x.domain(d3.extent($$.getXDomain($$.data.targets)))
$$.y.domain($$.getYDomain($$.data.targets, 'y'))
$$.y2.domain($$.getYDomain($$.data.targets, 'y2'))
$$.subX.domain($$.x.domain())
$$.subY.domain($$.y.domain())
$$.subY2.domain($$.y2.domain())
// Save original x domain for zoom update
$$.orgXDomain = $$.x.domain()
/*-- Basic Elements --*/
// Define svgs
$$.svg = $$.selectChart
.append('svg')
.style('overflow', 'hidden')
.on('mouseenter', function() {
return config.onmouseover.call($$)
})
.on('mouseleave', function() {
return config.onmouseout.call($$)
})
if ($$.config.svg_classname) {
$$.svg.attr('class', $$.config.svg_classname)
}
// Define defs
defs = $$.svg.append('defs')
$$.clipChart = $$.appendClip(defs, $$.clipId)
$$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis)
$$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis)
$$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid)
$$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart)
$$.updateSvgSize()
// Define regions
main = $$.main = $$.svg.append('g').attr('transform', $$.getTranslate('main'))
if ($$.initPie) {
$$.initPie()
}
if ($$.initDragZoom) {
$$.initDragZoom()
}
if (config.subchart_show && $$.initSubchart) {
$$.initSubchart()
}
if ($$.initTooltip) {
$$.initTooltip()
}
if ($$.initLegend) {
$$.initLegend()
}
if ($$.initTitle) {
$$.initTitle()
}
if ($$.initZoom) {
$$.initZoom()
}
if ($$.isStanfordGraphType()) {
$$.drawColorScale()
}
// Update selection based on size and scale
// TODO: currently this must be called after initLegend because of update of sizes, but it should be done in initSubchart.
if (config.subchart_show && $$.initSubchartBrush) {
$$.initSubchartBrush()
}
/*-- Main Region --*/
// text when empty
main
.append('text')
.attr('class', CLASS.text + ' ' + CLASS.empty)
.attr('text-anchor', 'middle') // horizontal centering of text at x position in all browsers.
.attr('dominant-baseline', 'middle') // vertical centering of text at y position in all browsers, except IE.
// Regions
$$.initRegion()
// Grids
$$.initGrid()
// Define g for chart area
main
.append('g')
.attr('clip-path', $$.clipPath)
.attr('class', CLASS.chart)
// Grid lines
if (config.grid_lines_front) {
$$.initGridLines()
}
$$.initStanfordElements()
// Cover whole with rects for events
$$.initEventRect()
// Define g for chart
$$.initChartElements()
// Add Axis
$$.axis.init()
// Set targets
$$.updateTargets($$.data.targets)
// Set default extent if defined
if (config.axis_x_selection) {
$$.brush.selectionAsValue($$.getDefaultSelection())
}
// Draw with targets
if (binding) {
$$.updateDimension()
$$.config.oninit.call($$)
$$.redraw({
withTransition: false,
withTransform: true,
withUpdateXDomain: true,
withUpdateOrgXDomain: true,
withTransitionForAxis: false
})
}
// Bind to resize event
$$.bindResize()
// Bind to window focus event
$$.bindWindowFocus()
// export element of the chart
$$.api.element = $$.selectChart.node()
}
ChartInternal.prototype.smoothLines = function(el, type) {
var $$ = this
if (type === 'grid') {
el.each(function() {
var g = $$.d3.select(this),
x1 = g.attr('x1'),
x2 = g.attr('x2'),
y1 = g.attr('y1'),
y2 = g.attr('y2')
g.attr({
x1: Math.ceil(x1),
x2: Math.ceil(x2),
y1: Math.ceil(y1),
y2: Math.ceil(y2)
})
})
}
}
ChartInternal.prototype.updateSizes = function() {
var $$ = this,
config = $$.config
var legendHeight = $$.legend ? $$.getLegendHeight() : 0,
legendWidth = $$.legend ? $$.getLegendWidth() : 0,
legendHeightForBottom =
$$.isLegendRight || $$.isLegendInset ? 0 : legendHeight,
hasArc = $$.hasArcType(),
xAxisHeight =
config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'),
subchartXAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x',true),
subchartHeight =
config.subchart_show && !hasArc
? config.subchart_size_height + subchartXAxisHeight
: 0
$$.currentWidth = $$.getCurrentWidth()
$$.currentHeight = $$.getCurrentHeight()
// for main
$$.margin = config.axis_rotated
? {
top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(),
right: hasArc ? 0 : $$.getCurrentPaddingRight(),
bottom:
$$.getHorizontalAxisHeight('y') +
legendHeightForBottom +
$$.getCurrentPaddingBottom(),
left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft())
}
: {
top: 4 + $$.getCurrentPaddingTop(), // for top tick text
right: hasArc ? 0 : $$.getCurrentPaddingRight(),
bottom:
xAxisHeight +
subchartHeight +
legendHeightForBottom +
$$.getCurrentPaddingBottom(),
left: hasArc ? 0 : $$.getCurrentPaddingLeft()
}
// for subchart
$$.margin2 = config.axis_rotated
? {
top: $$.margin.top,
right: NaN,
bottom: 20 + legendHeightForBottom,
left: $$.rotated_padding_left
}
: {
top: $$.currentHeight - subchartHeight - legendHeightForBottom,
right: NaN,
bottom: subchartXAxisHeight + legendHeightForBottom,
left: $$.margin.left
}
// for legend
$$.margin3 = {
top: 0,
right: NaN,
bottom: 0,
left: 0
}
if ($$.updateSizeForLegend) {
$$.updateSizeForLegend(legendHeight, legendWidth)
}
$$.width = $$.currentWidth - $$.margin.left - $$.margin.right
$$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom
if ($$.width < 0) {
$$.width = 0
}
if ($$.height < 0) {
$$.height = 0
}
$$.width2 = config.axis_rotated
? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right
: $$.width
$$.height2 = config.axis_rotated
? $$.height
: $$.currentHeight - $$.margin2.top - $$.margin2.bottom
if ($$.width2 < 0) {
$$.width2 = 0
}
if ($$.height2 < 0) {
$$.height2 = 0
}
// for arc
$$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0)
$$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10)
if ($$.hasType('gauge') && !config.gauge_fullCircle) {
$$.arcHeight += $$.height - $$.getGaugeLabelHeight()
}
if ($$.updateRadius) {
$$.updateRadius()
}
if ($$.isLegendRight && hasArc) {
$$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1
}
}
ChartInternal.prototype.updateTargets = function(targets) {
var $$ = this,
config = $$.config
/*-- Main --*/
//-- Text --//
$$.updateTargetsForText(targets)
//-- Bar --//
$$.updateTargetsForBar(targets)
//-- Line --//
$$.updateTargetsForLine(targets)
//-- Arc --//
if ($$.hasArcType() && $$.updateTargetsForArc) {
$$.updateTargetsForArc(targets)
}
/*-- Sub --*/
if (config.subchart_show && $$.updateTargetsForSubchart) {
$$.updateTargetsForSubchart(targets)
}
// Fade-in each chart
$$.showTargets()
}
ChartInternal.prototype.showTargets = function() {
var $$ = this
$$.svg
.selectAll('.' + CLASS.target)
.filter(function(d) {
return $$.isTargetToShow(d.id)
})
.transition()
.duration($$.config.transition_duration)
.style('opacity', 1)
}
ChartInternal.prototype.redraw = function(options, transitions) {
var $$ = this,
main = $$.main,
d3 = $$.d3,
config = $$.config
var areaIndices = $$.getShapeIndices($$.isAreaType),
barIndices = $$.getShapeIndices($$.isBarType),
lineIndices = $$.getShapeIndices($$.isLineType)
var withY,
withSubchart,
withTransition,
withTransitionForExit,
withTransitionForAxis,
withTransform,
withUpdateXDomain,
withUpdateOrgXDomain,
withTrimXDomain,
withLegend,
withEventRect,
withDimension,
withUpdateXAxis
var hideAxis = $$.hasArcType()
var drawArea, drawBar, drawLine, xForText, yForText
var duration, durationForExit, durationForAxis
var transitionsToWait, waitForDraw, flow, transition
var targetsToShow = $$.filterTargetsToShow($$.data.targets),
tickValues,
i,
intervalForCulling,
xDomainForZoom
var xv = $$.xv.bind($$),
cx,
cy
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)
withTrimXDomain = getOption(options, 'withTrimXDomain', true)
withUpdateXAxis = getOption(options, 'withUpdateXAxis', withUpdateXDomain)
withLegend = getOption(options, 'withLegend', false)
withEventRect = getOption(options, 'withEventRect', true)
withDimension = getOption(options, 'withDimension', true)
withTransitionForExit = getOption(
options,
'withTransitionForExit',
withTransition
)
withTransitionForAxis = getOption(
options,
'withTransitionForAxis',
withTransition
)
duration = withTransition ? config.transition_duration : 0
durationForExit = withTransitionForExit ? duration : 0
durationForAxis = withTransitionForAxis ? duration : 0
transitions = transitions || $$.axis.generateTransitions(durationForAxis)
// update legend and transform each g
if (withLegend && config.legend_show) {
$$.updateLegend($$.mapToIds($$.data.targets), options, transitions)
} else if (withDimension) {
// need to update dimension (e.g. axis.y.tick.values) because y tick values should change
// no need to update axis in it because they will be updated in redraw()
$$.updateDimension(true)
}
// MEMO: needed for grids calculation
if ($$.isCategorized() && targetsToShow.length === 0) {
$$.x.domain([0, $$.axes.x.selectAll('.tick').size()])
}
if (targetsToShow.length) {
$$.updateXDomain(
targetsToShow,
withUpdateXDomain,
withUpdateOrgXDomain,
withTrimXDomain
)
if (!config.axis_x_tick_values) {
tickValues = $$.axis.updateXAxisTickValues(targetsToShow)
}
} else {
$$.xAxis.tickValues([])
$$.subXAxis.tickValues([])
}
if (config.zoom_rescale && !options.flow) {
xDomainForZoom = $$.x.orgDomain()
}
$$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom))
$$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom))
if (!config.axis_y_tick_values && config.axis_y_tick_count) {
$$.yAxis.tickValues(
$$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count)
)
}
if (!config.axis_y2_tick_values && config.axis_y2_tick_count) {
$$.y2Axis.tickValues(
$$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count)
)
}
// axes
$$.axis.redraw(durationForAxis, hideAxis)
// Update axis label
$$.axis.updateLabels(withTransition)
// show/hide if manual culling needed
if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) {
if (config.axis_x_tick_culling && tickValues) {
for (i = 1; i < tickValues.length; i++) {
if (tickValues.length / i < config.axis_x_tick_culling_max) {
intervalForCulling = i
break
}
}
$$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function(e) {
var index = tickValues.indexOf(e)
if (index >= 0) {
d3.select(this).style(
'display',
index % intervalForCulling ? 'none' : 'block'
)
}
})
} else {
$$.svg
.selectAll('.' + CLASS.axisX + ' .tick text')
.style('display', 'block')
}
}
// setup drawer - MEMO: these must be called after axis updated
drawArea = $$.generateDrawArea
? $$.generateDrawArea(areaIndices, false)
: undefined
drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined
drawLine = $$.generateDrawLine
? $$.generateDrawLine(lineIndices, false)
: undefined
xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true)
yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false)
// update circleY based on updated parameters
$$.updateCircleY()
// generate circle x/y functions depending on updated params
cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$)
cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$)
// Update sub domain
if (withY) {
$$.subY.domain($$.getYDomain(targetsToShow, 'y'))
$$.subY2.domain($$.getYDomain(targetsToShow, 'y2'))
}
// xgrid focus
$$.updateXgridFocus()
// Data empty label positioning and text.
main
.select('text.' + CLASS.text + '.' + CLASS.empty)
.attr('x', $$.width / 2)
.attr('y', $$.height / 2)
.text(config.data_empty_label_text)
.transition()
.style('opacity', targetsToShow.length ? 0 : 1)
// event rect
if (withEventRect) {
$$.redrawEventRect()
}
// grid
$$.updateGrid(duration)
$$.updateStanfordElements(duration)
// rect for regions
$$.updateRegion(duration)
// bars
$$.updateBar(durationForExit)
// lines, areas and circles
$$.updateLine(durationForExit)
$$.updateArea(durationForExit)
$$.updateCircle(cx, cy)
// text
if ($$.hasDataLabel()) {
$$.updateText(xForText, yForText, durationForExit)
}
// title
if ($$.redrawTitle) {
$$.redrawTitle()
}
// arc
if ($$.redrawArc) {
$$.redrawArc(duration, durationForExit, withTransform)
}
// subchart
if (config.subchart_show && $$.redrawSubchart) {
$$.redrawSubchart(
withSubchart,
transitions,
duration,
durationForExit,
areaIndices,
barIndices,
lineIndices
)
}
if ($$.isStanfordGraphType()) {
$$.drawColorScale()
}
// circles for select
main
.selectAll('.' + CLASS.selectedCircles)
.filter($$.isBarType.bind($$))
.selectAll('circle')
.remove()
if (options.flow) {
flow = $$.generateFlow({
targets: targetsToShow,
flow: options.flow,
duration: options.flow.duration,
drawBar: drawBar,
drawLine: drawLine,
drawArea: drawArea,
cx: cx,
cy: cy,
xv: xv,
xForText: xForText,
yForText: yForText
})
}
if (duration && $$.isTabVisible()) {
// Only use transition if tab visible. See #938.
// transition should be derived from one transition
transition = d3.transition().duration(duration)
transitionsToWait = []
;[
$$.redrawBar(drawBar, true, transition),
$$.redrawLine(drawLine, true, transition),
$$.redrawArea(drawArea, true, transition),
$$.redrawCircle(cx, cy, true, transition),
$$.redrawText(xForText, yForText, options.flow, true, transition),
$$.redrawRegion(true, transition),
$$.redrawGrid(true, transition)
].forEach(function(transitions) {
transitions.forEach(function(transition) {
transitionsToWait.push(transition)
})
})
// Wait for end of transitions to call flow and onrendered callback
waitForDraw = $$.generateWait()
transitionsToWait.forEach(function(t) {
waitForDraw.add(t)
})
waitForDraw(function() {
if (flow) {
flow()
}
if (config.onrendered) {
config.onrendered.call($$)
}
})
} else {
$$.redrawBar(drawBar)
$$.redrawLine(drawLine)
$$.redrawArea(drawArea)
$$.redrawCircle(cx, cy)
$$.redrawText(xForText, yForText, options.flow)
$$.redrawRegion()
$$.redrawGrid()
if (flow) {
flow()
}
if (config.onrendered) {
config.onrendered.call($$)
}
}
// update fadein condition
$$.mapToIds($$.data.targets).forEach(function(id) {
$$.withoutFadeIn[id] = true
})
}
ChartInternal.prototype.updateAndRedraw = function(options) {
var $$ = this,
config = $$.config,
transitions
options = options || {}
// same with redraw
options.withTransition = getOption(options, 'withTransition', true)
options.withTransform = getOption(options, 'withTransform', false)
options.withLegend = getOption(options, 'withLegend', false)
// NOT same with redraw
options.withUpdateXDomain = getOption(options, 'withUpdateXDomain', true)
options.withUpdateOrgXDomain = getOption(
options,
'withUpdateOrgXDomain',
true
)
options.withTransitionForExit = false
options.withTransitionForTransform = getOption(
options,
'withTransitionForTransform',
options.withTransition
)
// MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called)
$$.updateSizes()
// MEMO: called in updateLegend in redraw if withLegend
if (!(options.withLegend && config.legend_show)) {
transitions = $$.axis.generateTransitions(
options.withTransitionForAxis ? config.transition_duration : 0
)
// Update scales
$$.updateScales()
$$.updateSvgSize()
// Update g positions
$$.transformAll(options.withTransitionForTransform, transitions)
}
// Draw with new sizes & scales
$$.redraw(options, transitions)
}
ChartInternal.prototype.redrawWithoutRescale = function() {
this.redraw({
withY: false,
withSubchart: false,
withEventRect: false,
withTransitionForAxis: false
})
}
ChartInternal.prototype.isTimeSeries = function() {
return this.config.axis_x_type === 'timeseries'
}
ChartInternal.prototype.isCategorized = function() {
return this.config.axis_x_type.indexOf('categor') >= 0
}
ChartInternal.prototype.isCustomX = function() {
var $$ = this,
config = $$.config
return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs))
}
ChartInternal.prototype.isTimeSeriesY = function() {
return this.config.axis_y_type === 'timeseries'
}
ChartInternal.prototype.getTranslate = function(target) {
var $$ = this,
config = $$.config,
x,
y
if (target === 'main') {
x = asHalfPixel($$.margin.left)
y = asHalfPixel($$.margin.top)
} else if (target === 'context') {
x = asHalfPixel($$.margin2.left)
y = asHalfPixel($$.margin2.top)
} else if (target === 'legend') {
x = $$.margin3.left
y = $$.margin3.top
} else if (target === 'x') {
x = 0
y = config.axis_rotated ? 0 : $$.height
} else if (target === 'y') {
x = 0
y = config.axis_rotated ? $$.height : 0
} else if (target === 'y2') {
x = config.axis_rotated ? 0 : $$.width
y = config.axis_rotated ? 1 : 0
} else if (target === 'subx') {
x = 0
y = config.axis_rotated ? 0 : $$.height2
} else if (target === 'arc') {
x = $$.arcWidth / 2
y = $$.arcHeight / 2 - ($$.hasType('gauge') ? 6 : 0) // to prevent wrong display of min and max label
}
return 'translate(' + x + ',' + y + ')'
}
ChartInternal.prototype.initialOpacity = function(d) {
return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0
}
ChartInternal.prototype.initialOpacityForCircle = function(d) {
return d.value !== null && this.withoutFadeIn[d.id]
? this.opacityForCircle(d)
: 0
}
ChartInternal.prototype.opacityForCircle = function(d) {
var isPointShouldBeShown = isFunction(this.config.point_show)
? this.config.point_show(d)
: this.config.point_show
var opacity = isPointShouldBeShown || this.isStanfordType(d) ? 1 : 0
return isValue(d.value) ? (this.isScatterType(d) ? 0.5 : opacity) : 0
}
ChartInternal.prototype.opacityForText = function() {
return this.hasDataLabel() ? 1 : 0
}
ChartInternal.prototype.xx = function(d) {
return d ? this.x(d.x) : null
}
ChartInternal.prototype.xvCustom = function(d, xyValue) {
var $$ = this,
value = xyValue ? d[xyValue] : d.value
if ($$.isTimeSeries()) {
value = $$.parseDate(d.value)
} else if ($$.isCategorized() && typeof d.value === 'string') {
value = $$.config.axis_x_categories.indexOf(d.value)
}
return Math.ceil($$.x(value))
}
ChartInternal.prototype.yvCustom = function(d, xyValue) {
var $$ = this,
yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y,
value = xyValue ? d[xyValue] : d.value
return Math.ceil(yScale(value))
}
ChartInternal.prototype.xv = function(d) {
var $$ = this,
value = d.value
if ($$.isTimeSeries()) {
value = $$.parseDate(d.value)
} else if ($$.isCategorized() && typeof d.value === 'string') {
value = $$.config.axis_x_categories.indexOf(d.value)
}
return Math.ceil($$.x(value))
}
ChartInternal.prototype.yv = function(d) {
var $$ = this,
yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y
return Math.ceil(yScale(d.value))
}
ChartInternal.prototype.subxx = function(d) {
return d ? this.subX(d.x) : null
}
ChartInternal.prototype.transformMain = function(withTransition, transitions) {
var $$ = this,
xAxis,
yAxis,
y2Axis
if (transitions && transitions.axisX) {
xAxis = transitions.axisX
} else {
xAxis = $$.main.select('.' + CLASS.axisX)
if (withTransition) {
xAxis = xAxis.transition()
}
}
if (transitions && transitions.axisY) {
yAxis = transitions.axisY
} else {
yAxis = $$.main.select('.' + CLASS.axisY)
if (withTransition) {
yAxis = yAxis.transition()
}
}
if (transitions && transitions.axisY2) {
y2Axis = transitions.axisY2
} else {
y2Axis = $$.main.select('.' + CLASS.axisY2)
if (withTransition) {
y2Axis = y2Axis.transition()
}
}
;(withTransition ? $$.main.transition() : $$.main).attr(
'transform',
$$.getTranslate('main')
)
xAxis.attr('transform', $$.getTranslate('x'))
yAxis.attr('transform', $$.getTranslate('y'))
y2Axis.attr('transform', $$.getTranslate('y2'))
$$.main
.select('.' + CLASS.chartArcs)
.attr('transform', $$.getTranslate('arc'))
}
ChartInternal.prototype.transformAll = function(withTransition, transitions) {
var $$ = this
$$.transformMain(withTransition, transitions)
if ($$.config.subchart_show) {
$$.transformContext(withTransition, transitions)
}
if ($$.legend) {
$$.transformLegend(withTransition)
}
}
ChartInternal.prototype.updateSvgSize = function() {
var $$ = this,
brush = $$.svg.select(`.${CLASS.brush} .overlay`)
$$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight)
$$.svg
.selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid])
.select('rect')
.attr('width', $$.width)
.attr('height', $$.height)
$$.svg
.select('#' + $$.clipIdForXAxis)
.select('rect')
.attr('x', $$.getXAxisClipX.bind($$))
.attr('y', $$.getXAxisClipY.bind($$))
.attr('width', $$.getXAxisClipWidth.bind($$))
.attr('height', $$.getXAxisClipHeight.bind($$))
$$.svg
.select('#' + $$.clipIdForYAxis)
.select('rect')
.attr('x', $$.getYAxisClipX.bind($$))
.attr('y', $$.getYAxisClipY.bind($$))
.attr('width', $$.getYAxisClipWidth.bind($$))
.attr('height', $$.getYAxisClipHeight.bind($$))
$$.svg
.select('#' + $$.clipIdForSubchart)
.select('rect')
.attr('width', $$.width)
.attr('height', (brush.size() && brush.attr('height')) || 0)
// MEMO: parent div's height will be bigger than svg when <!DOCTYPE html>
$$.selectChart.style('max-height', $$.currentHeight + 'px')
}
ChartInternal.prototype.updateDimension = function(withoutAxis) {
var $$ = this
if (!withoutAxis) {
if ($$.config.axis_rotated) {
$$.axes.x.call($$.xAxis)
$$.axes.subx.call($$.subXAxis)
} else {
$$.axes.y.call($$.yAxis)
$$.axes.y2.call($$.y2Axis)
}
}
$$.updateSizes()
$$.updateScales()
$$.updateSvgSize()
$$.transformAll(false)
}
ChartInternal.prototype.observeInserted = function(selection) {
var $$ = this,
observer
if (typeof MutationObserver === 'undefined') {
window.console.error('MutationObserver not defined.')
return
}
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
$$.intervalForObserveInserted = window.setInterval(function() {
// parentNode will NOT be null when completed
if (selection.node().parentNode) {
window.clearInterval($$.intervalForObserveInserted)
$$.updateDimension()
if ($$.brush) {
$$.brush.update()
}
$$.config.oninit.call($$)
$$.redraw({
withTransform: true,
withUpdateXDomain: true,
withUpdateOrgXDomain: true,
withTransition: false,
withTransitionForTransform: false,
withLegend: true
})
selection.transition().style('opacity', 1)
}
}, 10)
}
})
})
observer.observe(selection.node(), {
attributes: true,
childList: true,
characterData: true
})
}
/**
* Binds handlers to the window resize event.
*/
ChartInternal.prototype.bindResize = function() {
var $$ = this,
config = $$.config
$$.resizeFunction = $$.generateResize() // need to call .remove
$$.resizeFunction.add(function() {
config.onresize.call($$)
})
if (config.resize_auto) {
$$.resizeFunction.add(function() {
if ($$.resizeTimeout !== undefined) {
window.clearTimeout($$.resizeTimeout)
}
$$.resizeTimeout = window.setTimeout(function() {
delete $$.resizeTimeout
$$.updateAndRedraw({
withUpdateXDomain: false,
withUpdateOrgXDomain: false,
withTransition: false,
withTransitionForTransform: false,
withLegend: true
})
if ($$.brush) {
$$.brush.update()
}
}, 100)
})
}
$$.resizeFunction.add(function() {
config.onresized.call($$)
})
$$.resizeIfElementDisplayed = function() {
// if element not displayed skip it
if ($$.api == null || !$$.api.element.offsetParent) {
return
}
$$.resizeFunction()
}
window.addEventListener('resize', $$.resizeIfElementDisplayed, false)
}
/**
* Binds handlers to the window focus event.
*/
ChartInternal.prototype.bindWindowFocus = function() {
if (this.windowFocusHandler) {
// The handler is already set
return
}
this.windowFocusHandler = () => {
this.redraw()
}
window.addEventListener('focus', this.windowFocusHandler)
}
/**
* Unbinds from the window focus event.
*/
ChartInternal.prototype.unbindWindowFocus = function() {
window.removeEventListener('focus', this.windowFocusHandler)
delete this.windowFocusHandler
}
ChartInternal.prototype.generateResize = function() {
var resizeFunctions = []
function callResizeFunctions() {
resizeFunctions.forEach(function(f) {
f()
})
}
callResizeFunctions.add = function(f) {
resizeFunctions.push(f)
}
callResizeFunctions.remove = function(f) {
for (var i = 0; i < resizeFunctions.length; i++) {
if (resizeFunctions[i] === f) {
resizeFunctions.splice(i, 1)
break
}
}
}
return callResizeFunctions
}
ChartInternal.prototype.endall = function(transition, callback) {
var n = 0
transition
.each(function() {
++n
})
.on('end', function() {
if (!--n) {
callback.apply(this, arguments)
}
})
}
ChartInternal.prototype.generateWait = function() {
var $$ = this
var transitionsToWait = [],
f = function(callback) {
var timer = setInterval(function() {
if (!$$.isTabVisible()) {
return
}
var done = 0
transitionsToWait.forEach(function(t) {
if (t.empty()) {
done += 1
return
}
try {
t.transition()
} catch (e) {
done += 1
}
})
if (done === transitionsToWait.length) {
clearInterval(timer)
if (callback) {
callback()
}
}
}, 50)
}
;(f as any).add = function(transition) {
transitionsToWait.push(transition)
}
return f
}
ChartInternal.prototype.parseDate = function(date) {
var $$ = this,
parsedDate
if (date instanceof Date) {
parsedDate = date
} else if (typeof date === 'string') {
parsedDate = $$.dataTimeParse(date)
} else if (typeof date === 'object') {
parsedDate = new Date(+date)
} else if (typeof date === 'number' && !isNaN(date)) {
parsedDate = new Date(+date)
}
if (!parsedDate || isNaN(+parsedDate)) {
window.console.error("Failed to parse x '" + date + "' to Date object")
}
return parsedDate
}
ChartInternal.prototype.isTabVisible = function() {
return !document.hidden
}
ChartInternal.prototype.getPathBox = getPathBox
ChartInternal.prototype.CLASS = CLASS
export { Chart }
export { ChartInternal }