UNPKG

259 kBJavaScriptView Raw
1(function (window) {
2 'use strict';
3
4 /*global define, module, exports, require */
5
6 var c3 = { version: "0.3.0" };
7
8 var c3_chart_fn, c3_chart_internal_fn;
9
10 function Chart(config) {
11 var $$ = this.internal = new ChartInternal(this);
12 $$.loadConfig(config);
13 $$.init();
14
15 // bind "this" to nested API
16 (function bindThis(fn, target, argThis) {
17 for (var key in fn) {
18 target[key] = fn[key].bind(argThis);
19 if (Object.keys(fn[key]).length > 0) {
20 bindThis(fn[key], target[key], argThis);
21 }
22 }
23 })(c3_chart_fn, this, this);
24 }
25
26 function ChartInternal(api) {
27 var $$ = this;
28 $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require("d3") : undefined;
29 $$.api = api;
30 $$.config = $$.getDefaultConfig();
31 $$.data = {};
32 $$.cache = {};
33 $$.axes = {};
34 }
35
36 c3.generate = function (config) {
37 return new Chart(config);
38 };
39
40 c3.chart = {
41 fn: Chart.prototype,
42 internal: {
43 fn: ChartInternal.prototype
44 }
45 };
46 c3_chart_fn = c3.chart.fn;
47 c3_chart_internal_fn = c3.chart.internal.fn;
48
49
50 c3_chart_internal_fn.init = function () {
51 var $$ = this, config = $$.config;
52
53 $$.initParams();
54
55 if (config.data_url) {
56 $$.convertUrlToData(config.data_url, config.data_mimeType, config.data_keys, $$.initWithData);
57 }
58 else if (config.data_json) {
59 $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys));
60 }
61 else if (config.data_rows) {
62 $$.initWithData($$.convertRowsToData(config.data_rows));
63 }
64 else if (config.data_columns) {
65 $$.initWithData($$.convertColumnsToData(config.data_columns));
66 }
67 else {
68 throw Error('url or json or rows or columns is required.');
69 }
70 };
71
72 c3_chart_internal_fn.initParams = function () {
73 var $$ = this, d3 = $$.d3, config = $$.config;
74
75 // MEMO: clipId needs to be unique because it conflicts when multiple charts exist
76 $$.clipId = "c3-" + (+new Date()) + '-clip',
77 $$.clipIdForXAxis = $$.clipId + '-xaxis',
78 $$.clipIdForYAxis = $$.clipId + '-yaxis',
79 $$.clipPath = $$.getClipPath($$.clipId),
80 $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis),
81 $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis);
82
83 $$.dragStart = null;
84 $$.dragging = false;
85 $$.cancelClick = false;
86 $$.mouseover = false;
87 $$.transiting = false;
88
89 $$.color = $$.generateColor();
90 $$.levelColor = $$.generateLevelColor();
91
92 $$.dataTimeFormat = config.data_xLocaltime ? d3.time.format : d3.time.format.utc;
93 $$.axisTimeFormat = config.axis_x_localtime ? d3.time.format : d3.time.format.utc;
94 $$.defaultAxisTimeFormat = $$.axisTimeFormat.multi([
95 [".%L", function (d) { return d.getMilliseconds(); }],
96 [":%S", function (d) { return d.getSeconds(); }],
97 ["%I:%M", function (d) { return d.getMinutes(); }],
98 ["%I %p", function (d) { return d.getHours(); }],
99 ["%-m/%-d", function (d) { return d.getDay() && d.getDate() !== 1; }],
100 ["%-m/%-d", function (d) { return d.getDate() !== 1; }],
101 ["%-m/%-d", function (d) { return d.getMonth(); }],
102 ["%Y/%-m/%-d", function () { return true; }]
103 ]);
104
105 $$.hiddenTargetIds = [];
106 $$.hiddenLegendIds = [];
107
108 $$.xOrient = config.axis_rotated ? "left" : "bottom";
109 $$.yOrient = config.axis_rotated ? "bottom" : "left";
110 $$.y2Orient = config.axis_rotated ? "top" : "right";
111 $$.subXOrient = config.axis_rotated ? "left" : "bottom";
112
113 $$.isLegendRight = config.legend_position === 'right';
114 $$.isLegendInset = config.legend_position === 'inset';
115 $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right';
116 $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left';
117 $$.legendStep = 0;
118 $$.legendItemWidth = 0;
119 $$.legendItemHeight = 0;
120 $$.legendOpacityForHidden = 0.15;
121
122 $$.currentMaxTickWidth = 0;
123
124 $$.rotated_padding_left = 30;
125 $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30;
126 $$.rotated_padding_top = 5;
127
128 $$.withoutFadeIn = {};
129
130 $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js
131 };
132
133 c3_chart_internal_fn.initWithData = function (data) {
134 var $$ = this, d3 = $$.d3, config = $$.config;
135 var main, binding = true;
136
137 if ($$.initPie) { $$.initPie(); }
138 if ($$.initBrush) { $$.initBrush(); }
139 if ($$.initZoom) { $$.initZoom(); }
140
141 $$.selectChart = d3.select(config.bindto);
142 if ($$.selectChart.empty()) {
143 $$.selectChart = d3.select(document.createElement('div')).style('opacity', 0);
144 $$.observeInserted($$.selectChart);
145 binding = false;
146 }
147 $$.selectChart.html("").classed("c3", true);
148
149 // Init data as targets
150 $$.data.xs = {};
151 $$.data.targets = $$.convertDataToTargets(data);
152
153 if (config.data_filter) {
154 $$.data.targets = $$.data.targets.filter(config.data_filter);
155 }
156
157 // Set targets to hide if needed
158 if (config.data_hide) {
159 $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide);
160 }
161
162 // when gauge, hide legend // TODO: fix
163 if ($$.hasType('gauge')) {
164 config.legend_show = false;
165 }
166
167 // Init sizes and scales
168 $$.updateSizes();
169 $$.updateScales();
170
171 // Set domains for each scale
172 $$.x.domain(d3.extent($$.getXDomain($$.data.targets)));
173 $$.y.domain($$.getYDomain($$.data.targets, 'y'));
174 $$.y2.domain($$.getYDomain($$.data.targets, 'y2'));
175 $$.subX.domain($$.x.domain());
176 $$.subY.domain($$.y.domain());
177 $$.subY2.domain($$.y2.domain());
178
179 // Save original x domain for zoom update
180 $$.orgXDomain = $$.x.domain();
181
182 // Set initialized scales to brush and zoom
183 if ($$.brush) { $$.brush.scale($$.subX); }
184 if (config.zoom_enabled) { $$.zoom.scale($$.x); }
185
186 /*-- Basic Elements --*/
187
188 // Define svgs
189 $$.svg = $$.selectChart.append("svg")
190 .style("overflow", "hidden")
191 .on('mouseenter', function () { return config.onmouseover.call($$); })
192 .on('mouseleave', function () { return config.onmouseout.call($$); });
193
194 // Define defs
195 $$.defs = $$.svg.append("defs");
196 $$.defs.append("clipPath").attr("id", $$.clipId).append("rect");
197 $$.defs.append("clipPath").attr("id", $$.clipIdForXAxis).append("rect");
198 $$.defs.append("clipPath").attr("id", $$.clipIdForYAxis).append("rect");
199 $$.updateSvgSize();
200
201 // Define regions
202 main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main'));
203
204 if ($$.initSubchart) { $$.initSubchart(); }
205 if ($$.initTooltip) { $$.initTooltip(); }
206 if ($$.initLegend) { $$.initLegend(); }
207
208 /*-- Main Region --*/
209
210 // text when empty
211 main.append("text")
212 .attr("class", CLASS.text + ' ' + CLASS.empty)
213 .attr("text-anchor", "middle") // horizontal centering of text at x position in all browsers.
214 .attr("dominant-baseline", "middle"); // vertical centering of text at y position in all browsers, except IE.
215
216 // Regions
217 $$.initRegion();
218
219 // Grids
220 $$.initGrid();
221
222 // Define g for chart area
223 main.append('g')
224 .attr("clip-path", $$.clipPath)
225 .attr('class', CLASS.chart);
226
227 // Cover whole with rects for events
228 $$.initEventRect();
229
230 // Define g for bar chart area
231 if ($$.initBar) { $$.initBar(); }
232
233 // Define g for line chart area
234 if ($$.initLine) { $$.initLine(); }
235
236 // Define g for arc chart area
237 if ($$.initArc) { $$.initArc(); }
238 if ($$.initGauge) { $$.initGauge(); }
239
240 // Define g for text area
241 if ($$.initText) { $$.initText(); }
242
243 // if zoom privileged, insert rect to forefront
244 // TODO: is this needed?
245 main.insert('rect', config.zoom_privileged ? null : 'g.' + CLASS.regions)
246 .attr('class', CLASS.zoomRect)
247 .attr('width', $$.width)
248 .attr('height', $$.height)
249 .style('opacity', 0)
250 .on("dblclick.zoom", null);
251
252 // Set default extent if defined
253 if (config.axis_x_default) {
254 $$.brush.extent(isFunction(config.axis_x_default) ? config.axis_x_default($$.getXDomain()) : config.axis_x_default);
255 }
256
257 // Add Axis
258 $$.initAxis();
259
260 // Set targets
261 $$.updateTargets($$.data.targets);
262
263 // Draw with targets
264 if (binding) {
265 $$.updateDimension();
266 $$.redraw({
267 withTransform: true,
268 withUpdateXDomain: true,
269 withUpdateOrgXDomain: true,
270 withTransitionForAxis: false
271 });
272 }
273
274 // Bind resize event
275 if (window.onresize == null) {
276 window.onresize = $$.generateResize();
277 }
278 if (window.onresize.add) {
279 window.onresize.add(function () {
280 config.onresize.call($$);
281 });
282 window.onresize.add(function () {
283 $$.api.flush();
284 });
285 window.onresize.add(function () {
286 config.onresized.call($$);
287 });
288 }
289
290 // export element of the chart
291 $$.api.element = $$.selectChart.node();
292 };
293
294 c3_chart_internal_fn.smoothLines = function (el, type) {
295 var $$ = this;
296 if (type === 'grid') {
297 el.each(function () {
298 var g = $$.d3.select(this),
299 x1 = g.attr('x1'),
300 x2 = g.attr('x2'),
301 y1 = g.attr('y1'),
302 y2 = g.attr('y2');
303 g.attr({
304 'x1': Math.ceil(x1),
305 'x2': Math.ceil(x2),
306 'y1': Math.ceil(y1),
307 'y2': Math.ceil(y2)
308 });
309 });
310 }
311 };
312
313
314 c3_chart_internal_fn.updateSizes = function () {
315 var $$ = this, config = $$.config;
316 var legendHeight = $$.legend ? $$.getLegendHeight() : 0,
317 legendWidth = $$.legend ? $$.getLegendWidth() : 0,
318 legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight,
319 hasArc = $$.hasArcType(),
320 xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'),
321 subchartHeight = config.subchart_show && !hasArc ? (config.subchart_size_height + xAxisHeight) : 0;
322
323 $$.currentWidth = $$.getCurrentWidth();
324 $$.currentHeight = $$.getCurrentHeight();
325
326 // for main
327 $$.margin = config.axis_rotated ? {
328 top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(),
329 right: hasArc ? 0 : $$.getCurrentPaddingRight(),
330 bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(),
331 left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft())
332 } : {
333 top: 4 + $$.getCurrentPaddingTop(), // for top tick text
334 right: hasArc ? 0 : $$.getCurrentPaddingRight(),
335 bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(),
336 left: hasArc ? 0 : $$.getCurrentPaddingLeft()
337 };
338
339 // for subchart
340 $$.margin2 = config.axis_rotated ? {
341 top: $$.margin.top,
342 right: NaN,
343 bottom: 20 + legendHeightForBottom,
344 left: $$.rotated_padding_left
345 } : {
346 top: $$.currentHeight - subchartHeight - legendHeightForBottom,
347 right: NaN,
348 bottom: xAxisHeight + legendHeightForBottom,
349 left: $$.margin.left
350 };
351
352 // for legend
353 $$.margin3 = {
354 top: 0,
355 right: NaN,
356 bottom: 0,
357 left: 0
358 };
359 if ($$.updateSizeForLegend) { $$.updateSizeForLegend(legendHeight, legendWidth); }
360
361 $$.width = $$.currentWidth - $$.margin.left - $$.margin.right;
362 $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom;
363 if ($$.width < 0) { $$.width = 0; }
364 if ($$.height < 0) { $$.height = 0; }
365
366 $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width;
367 $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom;
368 if ($$.width2 < 0) { $$.width2 = 0; }
369 if ($$.height2 < 0) { $$.height2 = 0; }
370
371 // for arc
372 $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0);
373 $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10);
374 if ($$.updateRadius) { $$.updateRadius(); }
375
376 if ($$.isLegendRight && hasArc) {
377 $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1;
378 }
379 };
380
381 c3_chart_internal_fn.updateTargets = function (targets) {
382 var $$ = this, config = $$.config;
383
384 /*-- Main --*/
385
386 //-- Text --//
387 $$.updateTargetsForText(targets);
388
389 //-- Bar --//
390 $$.updateTargetsForBar(targets);
391
392 //-- Line --//
393 $$.updateTargetsForLine(targets);
394
395 //-- Arc --//
396 if ($$.updateTargetsForArc) { $$.updateTargetsForArc(targets); }
397 if ($$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); }
398
399 /*-- Show --*/
400
401 // Fade-in each chart
402 $$.svg.selectAll('.' + CLASS.target).filter(function (d) { return $$.isTargetToShow(d.id); })
403 .transition().duration(config.transition_duration)
404 .style("opacity", 1);
405 };
406
407 c3_chart_internal_fn.redraw = function (options, transitions) {
408 var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config;
409 var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType);
410 var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withLegend;
411 var hideAxis = $$.hasArcType();
412 var drawArea, drawBar, drawLine, xForText, yForText;
413 var duration, durationForExit, durationForAxis;
414 var waitForDraw, flow;
415 var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling;
416 var xv = $$.xv.bind($$),
417 cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$),
418 cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$);
419
420 options = options || {};
421 withY = getOption(options, "withY", true);
422 withSubchart = getOption(options, "withSubchart", true);
423 withTransition = getOption(options, "withTransition", true);
424 withTransform = getOption(options, "withTransform", false);
425 withUpdateXDomain = getOption(options, "withUpdateXDomain", false);
426 withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", false);
427 withLegend = getOption(options, "withLegend", false);
428 withTransitionForExit = getOption(options, "withTransitionForExit", withTransition);
429 withTransitionForAxis = getOption(options, "withTransitionForAxis", withTransition);
430
431 duration = withTransition ? config.transition_duration : 0;
432 durationForExit = withTransitionForExit ? duration : 0;
433 durationForAxis = withTransitionForAxis ? duration : 0;
434
435 transitions = transitions || $$.generateAxisTransitions(durationForAxis);
436
437 // update legend and transform each g
438 if (withLegend && config.legend_show) {
439 $$.updateLegend($$.mapToIds($$.data.targets), options, transitions);
440 }
441
442 // MEMO: needed for grids calculation
443 if ($$.isCategorized() && targetsToShow.length === 0) {
444 $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]);
445 }
446
447 if (targetsToShow.length) {
448 $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain);
449 // update axis tick values according to options
450 if (!config.axis_x_tick_values && (config.axis_x_tick_fit || config.axis_x_tick_count)) {
451 tickValues = $$.generateTickValues($$.mapTargetsToUniqueXs(targetsToShow), config.axis_x_tick_count);
452 $$.xAxis.tickValues(tickValues);
453 $$.subXAxis.tickValues(tickValues);
454 }
455 } else {
456 $$.xAxis.tickValues([]);
457 $$.subXAxis.tickValues([]);
458 }
459
460 $$.y.domain($$.getYDomain(targetsToShow, 'y'));
461 $$.y2.domain($$.getYDomain(targetsToShow, 'y2'));
462
463 // axes
464 $$.redrawAxis(transitions, hideAxis);
465
466 // Update axis label
467 $$.updateAxisLabels(withTransition);
468
469 // show/hide if manual culling needed
470 if (withUpdateXDomain && targetsToShow.length) {
471 if (config.axis_x_tick_culling && tickValues) {
472 for (i = 1; i < tickValues.length; i++) {
473 if (tickValues.length / i < config.axis_x_tick_culling_max) {
474 intervalForCulling = i;
475 break;
476 }
477 }
478 $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) {
479 var index = tickValues.indexOf(e);
480 if (index >= 0) {
481 d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block');
482 }
483 });
484 } else {
485 $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').style('display', 'block');
486 }
487 }
488
489 // rotate tick text if needed
490 if (!config.axis_rotated && config.axis_x_tick_rotate) {
491 $$.rotateTickText($$.axes.x, transitions.axisX, config.axis_x_tick_rotate);
492 }
493
494 // setup drawer - MEMO: these must be called after axis updated
495 drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined;
496 drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined;
497 drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined;
498 xForText = $$.generateXYForText(barIndices, true);
499 yForText = $$.generateXYForText(barIndices, false);
500
501 // Update sub domain
502 $$.subY.domain($$.y.domain());
503 $$.subY2.domain($$.y2.domain());
504
505 // tooltip
506 $$.tooltip.style("display", "none");
507
508 // xgrid focus
509 $$.updateXgridFocus();
510
511 // Data empty label positioning and text.
512 main.select("text." + CLASS.text + '.' + CLASS.empty)
513 .attr("x", $$.width / 2)
514 .attr("y", $$.height / 2)
515 .text(config.data_empty_label_text)
516 .transition()
517 .style('opacity', targetsToShow.length ? 0 : 1);
518
519 // grid
520 $$.redrawGrid(duration, withY);
521
522 // rect for regions
523 $$.redrawRegion(duration);
524
525 // bars
526 $$.redrawBar(durationForExit);
527
528 // lines, areas and cricles
529 $$.redrawLine(durationForExit);
530 $$.redrawArea(durationForExit);
531 if (config.point_show) { $$.redrawCircle(); }
532
533 // text
534 if ($$.hasDataLabel()) {
535 $$.redrawText(durationForExit);
536 }
537
538 // arc
539 if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); }
540
541 // subchart
542 if ($$.redrawSubchart) {
543 $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices);
544 }
545
546 // circles for select
547 main.selectAll('.' + CLASS.selectedCircles)
548 .filter($$.isBarType.bind($$))
549 .selectAll('circle')
550 .remove();
551
552 // event rect
553 if (config.interaction_enabled) {
554 $$.redrawEventRect();
555 }
556
557 // transition should be derived from one transition
558 d3.transition().duration(duration).each(function () {
559 var transitions = [];
560
561 $$.addTransitionForBar(transitions, drawBar);
562 $$.addTransitionForLine(transitions, drawLine);
563 $$.addTransitionForArea(transitions, drawArea);
564 if (config.point_show) { $$.addTransitionForCircle(transitions, cx, cy); }
565 $$.addTransitionForText(transitions, xForText, yForText, options.flow);
566 $$.addTransitionForRegion(transitions);
567 $$.addTransitionForGrid(transitions);
568
569 // Wait for end of transitions if called from flow API
570 if (options.flow) {
571 waitForDraw = $$.generateWait();
572 transitions.forEach(function (t) {
573 waitForDraw.add(t);
574 });
575 flow = $$.generateFlow({
576 targets: targetsToShow,
577 flow: options.flow,
578 duration: duration,
579 drawBar: drawBar,
580 drawLine: drawLine,
581 drawArea: drawArea,
582 cx: cx,
583 cy: cy,
584 xv: xv,
585 xForText: xForText,
586 yForText: yForText
587 });
588 }
589 })
590 .call(waitForDraw || function () {}, flow || function () {});
591
592 // update fadein condition
593 $$.mapToIds($$.data.targets).forEach(function (id) {
594 $$.withoutFadeIn[id] = true;
595 });
596
597 if ($$.updateZoom) { $$.updateZoom(); }
598 };
599
600 c3_chart_internal_fn.updateAndRedraw = function (options) {
601 var $$ = this, config = $$.config, transitions;
602 options = options || {};
603 // same with redraw
604 options.withTransition = getOption(options, "withTransition", true);
605 options.withTransform = getOption(options, "withTransform", false);
606 options.withLegend = getOption(options, "withLegend", false);
607 // NOT same with redraw
608 options.withUpdateXDomain = true;
609 options.withUpdateOrgXDomain = true;
610 options.withTransitionForExit = false;
611 options.withTransitionForTransform = getOption(options, "withTransitionForTransform", options.withTransition);
612 // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called)
613 $$.updateSizes();
614 // MEMO: called in updateLegend in redraw if withLegend
615 if (!(options.withLegend && config.legend_show)) {
616 transitions = $$.generateAxisTransitions(options.withTransitionForAxis ? config.transition_duration : 0);
617 // Update scales
618 $$.updateScales();
619 $$.updateSvgSize();
620 // Update g positions
621 $$.transformAll(options.withTransitionForTransform, transitions);
622 }
623 // Draw with new sizes & scales
624 $$.redraw(options, transitions);
625 };
626
627 c3_chart_internal_fn.isTimeSeries = function () {
628 return this.config.axis_x_type === 'timeseries';
629 };
630 c3_chart_internal_fn.isCategorized = function () {
631 return this.config.axis_x_type.indexOf('categor') >= 0;
632 };
633 c3_chart_internal_fn.isCustomX = function () {
634 var $$ = this, config = $$.config;
635 return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs));
636 };
637
638 c3_chart_internal_fn.getTranslate = function (target) {
639 var $$ = this, config = $$.config, x, y;
640 if (target === 'main') {
641 x = asHalfPixel($$.margin.left);
642 y = asHalfPixel($$.margin.top);
643 } else if (target === 'context') {
644 x = asHalfPixel($$.margin2.left);
645 y = asHalfPixel($$.margin2.top);
646 } else if (target === 'legend') {
647 x = $$.margin3.left;
648 y = $$.margin3.top;
649 } else if (target === 'x') {
650 x = 0;
651 y = config.axis_rotated ? 0 : $$.height;
652 } else if (target === 'y') {
653 x = 0;
654 y = config.axis_rotated ? $$.height : 0;
655 } else if (target === 'y2') {
656 x = config.axis_rotated ? 0 : $$.width;
657 y = config.axis_rotated ? 1 : 0;
658 } else if (target === 'subx') {
659 x = 0;
660 y = config.axis_rotated ? 0 : $$.height2;
661 } else if (target === 'arc') {
662 x = $$.arcWidth / 2;
663 y = $$.arcHeight / 2;
664 }
665 return "translate(" + x + "," + y + ")";
666 };
667 c3_chart_internal_fn.initialOpacity = function (d) {
668 return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0;
669 };
670 c3_chart_internal_fn.opacityForCircle = function (d) {
671 var $$ = this;
672 return isValue(d.value) ? $$.isScatterType(d) ? 0.5 : 1 : 0;
673 };
674 c3_chart_internal_fn.opacityForText = function () {
675 return this.hasDataLabel() ? 1 : 0;
676 };
677 c3_chart_internal_fn.xx = function (d) {
678 return d ? this.x(d.x) : null;
679 };
680 c3_chart_internal_fn.xv = function (d) {
681 var $$ = this;
682 return Math.ceil($$.x($$.isTimeSeries() ? $$.parseDate(d.value) : d.value));
683 };
684 c3_chart_internal_fn.yv = function (d) {
685 var $$ = this,
686 yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y;
687 return Math.ceil(yScale(d.value));
688 };
689 c3_chart_internal_fn.subxx = function (d) {
690 return d ? this.subX(d.x) : null;
691 };
692
693 c3_chart_internal_fn.transformMain = function (withTransition, transitions) {
694 var $$ = this,
695 xAxis, yAxis, y2Axis;
696 if (transitions && transitions.axisX) {
697 xAxis = transitions.axisX;
698 } else {
699 xAxis = $$.main.select('.' + CLASS.axisX);
700 if (withTransition) { xAxis = xAxis.transition(); }
701 }
702 if (transitions && transitions.axisY) {
703 yAxis = transitions.axisY;
704 } else {
705 yAxis = $$.main.select('.' + CLASS.axisY);
706 if (withTransition) { yAxis = yAxis.transition(); }
707 }
708 if (transitions && transitions.axisY2) {
709 y2Axis = transitions.axisY2;
710 } else {
711 y2Axis = $$.main.select('.' + CLASS.axisY2);
712 if (withTransition) { y2Axis = y2Axis.transition(); }
713 }
714 (withTransition ? $$.main.transition() : $$.main).attr("transform", $$.getTranslate('main'));
715 xAxis.attr("transform", $$.getTranslate('x'));
716 yAxis.attr("transform", $$.getTranslate('y'));
717 y2Axis.attr("transform", $$.getTranslate('y2'));
718 $$.main.select('.' + CLASS.chartArcs).attr("transform", $$.getTranslate('arc'));
719 };
720 c3_chart_internal_fn.transformAll = function (withTransition, transitions) {
721 var $$ = this;
722 $$.transformMain(withTransition, transitions);
723 if ($$.config.subchart_show) { $$.transformContext(withTransition, transitions); }
724 if ($$.legend) { $$.transformLegend(withTransition); }
725 };
726
727 c3_chart_internal_fn.updateSvgSize = function () {
728 var $$ = this;
729 $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight);
730 $$.svg.select('#' + $$.clipId).select('rect')
731 .attr('width', $$.width)
732 .attr('height', $$.height);
733 $$.svg.select('#' + $$.clipIdForXAxis).select('rect')
734 .attr('x', $$.getXAxisClipX.bind($$))
735 .attr('y', $$.getXAxisClipY.bind($$))
736 .attr('width', $$.getXAxisClipWidth.bind($$))
737 .attr('height', $$.getXAxisClipHeight.bind($$));
738 $$.svg.select('#' + $$.clipIdForYAxis).select('rect')
739 .attr('x', $$.getYAxisClipX.bind($$))
740 .attr('y', $$.getYAxisClipY.bind($$))
741 .attr('width', $$.getYAxisClipWidth.bind($$))
742 .attr('height', $$.getYAxisClipHeight.bind($$));
743 $$.svg.select('.' + CLASS.zoomRect)
744 .attr('width', $$.width)
745 .attr('height', $$.height);
746 // MEMO: parent div's height will be bigger than svg when <!DOCTYPE html>
747 $$.selectChart.style('max-height', $$.currentHeight + "px");
748 };
749
750
751 c3_chart_internal_fn.updateDimension = function () {
752 var $$ = this;
753 if ($$.config.axis_rotated) {
754 $$.axes.x.call($$.xAxis);
755 $$.axes.subx.call($$.subXAxis);
756 } else {
757 $$.axes.y.call($$.yAxis);
758 $$.axes.y2.call($$.y2Axis);
759 }
760 $$.updateSizes();
761 $$.updateScales();
762 $$.updateSvgSize();
763 $$.transformAll(false);
764 };
765
766 c3_chart_internal_fn.observeInserted = function (selection) {
767 var $$ = this, observer = new MutationObserver(function (mutations) {
768 mutations.forEach(function (mutation) {
769 if (mutation.type === 'childList' && mutation.previousSibling) {
770 observer.disconnect();
771 // need to wait for completion of load because size calculation requires the actual sizes determined after that completion
772 var interval = window.setInterval(function () {
773 // parentNode will NOT be null when completed
774 if (selection.node().parentNode) {
775 window.clearInterval(interval);
776 $$.updateDimension();
777 $$.redraw({
778 withTransform: true,
779 withUpdateXDomain: true,
780 withUpdateOrgXDomain: true,
781 withTransition: false,
782 withTransitionForTransform: false,
783 withLegend: true
784 });
785 selection.transition().style('opacity', 1);
786 }
787 }, 10);
788 }
789 });
790 });
791 observer.observe(selection.node(), {attributes: true, childList: true, characterData: true});
792 };
793
794
795 c3_chart_internal_fn.generateResize = function () {
796 var resizeFunctions = [];
797 function callResizeFunctions() {
798 resizeFunctions.forEach(function (f) {
799 f();
800 });
801 }
802 callResizeFunctions.add = function (f) {
803 resizeFunctions.push(f);
804 };
805 return callResizeFunctions;
806 };
807
808 c3_chart_internal_fn.endall = function (transition, callback) {
809 var n = 0;
810 transition
811 .each(function () { ++n; })
812 .each("end", function () {
813 if (!--n) { callback.apply(this, arguments); }
814 });
815 };
816 c3_chart_internal_fn.generateWait = function () {
817 var transitionsToWait = [],
818 f = function (transition, callback) {
819 var timer = setInterval(function () {
820 var done = 0;
821 transitionsToWait.forEach(function (t) {
822 if (t.empty()) {
823 done += 1;
824 return;
825 }
826 try {
827 t.transition();
828 } catch (e) {
829 done += 1;
830 }
831 });
832 if (done === transitionsToWait.length) {
833 clearInterval(timer);
834 if (callback) { callback(); }
835 }
836 }, 10);
837 };
838 f.add = function (transition) {
839 transitionsToWait.push(transition);
840 };
841 return f;
842 };
843
844 c3_chart_internal_fn.parseDate = function (date) {
845 var $$ = this, parsedDate;
846 if (date instanceof Date) {
847 parsedDate = date;
848 } else if (typeof date === 'number') {
849 parsedDate = new Date(date);
850 } else {
851 parsedDate = $$.dataTimeFormat($$.config.data_xFormat).parse(date);
852 }
853 if (!parsedDate || isNaN(+parsedDate)) {
854 window.console.error("Failed to parse x '" + date + "' to Date object");
855 }
856 return parsedDate;
857 };
858
859 c3_chart_internal_fn.getDefaultConfig = function () {
860 var config = {
861 bindto: '#chart',
862 size_width: undefined,
863 size_height: undefined,
864 padding_left: undefined,
865 padding_right: undefined,
866 padding_top: undefined,
867 padding_bottom: undefined,
868 zoom_enabled: false,
869 zoom_extent: undefined,
870 zoom_privileged: false,
871 zoom_onzoom: function () {},
872 interaction_enabled: true,
873 onmouseover: function () {},
874 onmouseout: function () {},
875 onresize: function () {},
876 onresized: function () {},
877 transition_duration: 350,
878 data_x: undefined,
879 data_xs: {},
880 data_xFormat: '%Y-%m-%d',
881 data_xLocaltime: true,
882 data_idConverter: function (id) { return id; },
883 data_names: {},
884 data_classes: {},
885 data_groups: [],
886 data_axes: {},
887 data_type: undefined,
888 data_types: {},
889 data_labels: {},
890 data_order: 'desc',
891 data_regions: {},
892 data_color: undefined,
893 data_colors: {},
894 data_hide: false,
895 data_filter: undefined,
896 data_selection_enabled: false,
897 data_selection_grouped: false,
898 data_selection_isselectable: function () { return true; },
899 data_selection_multiple: true,
900 data_onclick: function () {},
901 data_onmouseover: function () {},
902 data_onmouseout: function () {},
903 data_onselected: function () {},
904 data_onunselected: function () {},
905 data_ondragstart: function () {},
906 data_ondragend: function () {},
907 data_url: undefined,
908 data_json: undefined,
909 data_rows: undefined,
910 data_columns: undefined,
911 data_mimeType: undefined,
912 data_keys: undefined,
913 // configuration for no plot-able data supplied.
914 data_empty_label_text: "",
915 // subchart
916 subchart_show: false,
917 subchart_size_height: 60,
918 subchart_onbrush: function () {},
919 // color
920 color_pattern: [],
921 color_threshold: {},
922 // legend
923 legend_show: true,
924 legend_position: 'bottom',
925 legend_inset_anchor: 'top-left',
926 legend_inset_x: 10,
927 legend_inset_y: 0,
928 legend_inset_step: undefined,
929 legend_item_onclick: undefined,
930 legend_item_onmouseover: undefined,
931 legend_item_onmouseout: undefined,
932 legend_equally: false,
933 // axis
934 axis_rotated: false,
935 axis_x_show: true,
936 axis_x_type: 'indexed',
937 axis_x_localtime: true,
938 axis_x_categories: [],
939 axis_x_tick_centered: false,
940 axis_x_tick_format: undefined,
941 axis_x_tick_culling: {},
942 axis_x_tick_culling_max: 10,
943 axis_x_tick_count: undefined,
944 axis_x_tick_fit: true,
945 axis_x_tick_values: null,
946 axis_x_tick_rotate: undefined,
947 axis_x_tick_outer: true,
948 axis_x_max: null,
949 axis_x_min: null,
950 axis_x_padding: {},
951 axis_x_height: undefined,
952 axis_x_default: undefined,
953 axis_x_label: {},
954 axis_y_show: true,
955 axis_y_max: undefined,
956 axis_y_min: undefined,
957 axis_y_center: undefined,
958 axis_y_label: {},
959 axis_y_tick_format: undefined,
960 axis_y_tick_outer: true,
961 axis_y_padding: undefined,
962 axis_y_ticks: 10,
963 axis_y2_show: false,
964 axis_y2_max: undefined,
965 axis_y2_min: undefined,
966 axis_y2_center: undefined,
967 axis_y2_label: {},
968 axis_y2_tick_format: undefined,
969 axis_y2_tick_outer: true,
970 axis_y2_padding: undefined,
971 axis_y2_ticks: 10,
972 // grid
973 grid_x_show: false,
974 grid_x_type: 'tick',
975 grid_x_lines: [],
976 grid_y_show: false,
977 // not used
978 // grid_y_type: 'tick',
979 grid_y_lines: [],
980 grid_y_ticks: 10,
981 grid_focus_show: true,
982 // point - point of each data
983 point_show: true,
984 point_r: 2.5,
985 point_focus_expand_enabled: true,
986 point_focus_expand_r: undefined,
987 point_select_r: undefined,
988 line_connect_null: false,
989 // bar
990 bar_width: undefined,
991 bar_width_ratio: 0.6,
992 bar_width_max: undefined,
993 bar_zerobased: true,
994 // area
995 area_zerobased: true,
996 // pie
997 pie_label_show: true,
998 pie_label_format: undefined,
999 pie_label_threshold: 0.05,
1000 pie_sort: true,
1001 pie_expand: true,
1002 // gauge
1003 gauge_label_show: true,
1004 gauge_label_format: undefined,
1005 gauge_expand: true,
1006 gauge_min: 0,
1007 gauge_max: 100,
1008 gauge_units: undefined,
1009 gauge_width: undefined,
1010 // donut
1011 donut_label_show: true,
1012 donut_label_format: undefined,
1013 donut_label_threshold: 0.05,
1014 donut_width: undefined,
1015 donut_sort: true,
1016 donut_expand: true,
1017 donut_title: "",
1018 // region - region to change style
1019 regions: [],
1020 // tooltip - show when mouseover on each data
1021 tooltip_show: true,
1022 tooltip_grouped: true,
1023 tooltip_format_title: undefined,
1024 tooltip_format_name: undefined,
1025 tooltip_format_value: undefined,
1026 tooltip_contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
1027 return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : '';
1028 },
1029 tooltip_init_show: false,
1030 tooltip_init_x: 0,
1031 tooltip_init_position: {top: '0px', left: '50px'}
1032 };
1033
1034 Object.keys(this.additionalConfig).forEach(function (key) {
1035 config[key] = this.additionalConfig[key];
1036 }, this);
1037
1038 return config;
1039 };
1040 c3_chart_internal_fn.additionalConfig = {};
1041
1042 c3_chart_internal_fn.loadConfig = function (config) {
1043 var this_config = this.config, target, keys, read;
1044 function find() {
1045 var key = keys.shift();
1046 // console.log("key =>", key, ", target =>", target);
1047 if (key && target && typeof target === 'object' && key in target) {
1048 target = target[key];
1049 return find();
1050 }
1051 else if (!key) {
1052 return target;
1053 }
1054 else {
1055 return undefined;
1056 }
1057 }
1058 Object.keys(this_config).forEach(function (key) {
1059 target = config;
1060 keys = key.split('_');
1061 read = find();
1062 // console.log("CONFIG : ", key, read);
1063 if (isDefined(read)) {
1064 this_config[key] = read;
1065 }
1066 });
1067 };
1068
1069 c3_chart_internal_fn.getScale = function (min, max, forTimeseries) {
1070 return (forTimeseries ? this.d3.time.scale() : this.d3.scale.linear()).range([min, max]);
1071 };
1072 c3_chart_internal_fn.getX = function (min, max, domain, offset) {
1073 var $$ = this,
1074 scale = $$.getScale(min, max, $$.isTimeSeries()),
1075 _scale = domain ? scale.domain(domain) : scale, key;
1076 // Define customized scale if categorized axis
1077 if ($$.isCategorized()) {
1078 offset = offset || function () { return 0; };
1079 scale = function (d, raw) {
1080 var v = _scale(d) + offset(d);
1081 return raw ? v : Math.ceil(v);
1082 };
1083 } else {
1084 scale = function (d, raw) {
1085 var v = _scale(d);
1086 return raw ? v : Math.ceil(v);
1087 };
1088 }
1089 // define functions
1090 for (key in _scale) {
1091 scale[key] = _scale[key];
1092 }
1093 scale.orgDomain = function () {
1094 return _scale.domain();
1095 };
1096 // define custom domain() for categorized axis
1097 if ($$.isCategorized()) {
1098 scale.domain = function (domain) {
1099 if (!arguments.length) {
1100 domain = this.orgDomain();
1101 return [domain[0], domain[1] + 1];
1102 }
1103 _scale.domain(domain);
1104 return scale;
1105 };
1106 }
1107 return scale;
1108 };
1109 c3_chart_internal_fn.getY = function (min, max, domain) {
1110 var scale = this.getScale(min, max);
1111 if (domain) { scale.domain(domain); }
1112 return scale;
1113 };
1114 c3_chart_internal_fn.getYScale = function (id) {
1115 return this.getAxisId(id) === 'y2' ? this.y2 : this.y;
1116 };
1117 c3_chart_internal_fn.getSubYScale = function (id) {
1118 return this.getAxisId(id) === 'y2' ? this.subY2 : this.subY;
1119 };
1120 c3_chart_internal_fn.updateScales = function () {
1121 var $$ = this, config = $$.config,
1122 forInit = !$$.x;
1123 // update edges
1124 $$.xMin = config.axis_rotated ? 1 : 0;
1125 $$.xMax = config.axis_rotated ? $$.height : $$.width;
1126 $$.yMin = config.axis_rotated ? 0 : $$.height;
1127 $$.yMax = config.axis_rotated ? $$.width : 1;
1128 $$.subXMin = $$.xMin;
1129 $$.subXMax = $$.xMax;
1130 $$.subYMin = config.axis_rotated ? 0 : $$.height2;
1131 $$.subYMax = config.axis_rotated ? $$.width2 : 1;
1132 // update scales
1133 $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () { return $$.xAxis.tickOffset(); });
1134 $$.y = $$.getY($$.yMin, $$.yMax, forInit ? undefined : $$.y.domain());
1135 $$.y2 = $$.getY($$.yMin, $$.yMax, forInit ? undefined : $$.y2.domain());
1136 $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function (d) { return d % 1 ? 0 : $$.subXAxis.tickOffset(); });
1137 $$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? undefined : $$.subY.domain());
1138 $$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? undefined : $$.subY2.domain());
1139 // update axes
1140 $$.xAxisTickFormat = $$.getXAxisTickFormat();
1141 $$.xAxisTickValues = config.axis_x_tick_values ? config.axis_x_tick_values : (forInit ? undefined : $$.xAxis.tickValues());
1142 $$.xAxis = $$.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues);
1143 $$.subXAxis = $$.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues);
1144 $$.yAxis = $$.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, config.axis_y_ticks);
1145 $$.y2Axis = $$.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, config.axis_y2_ticks);
1146 // Set initialized scales to brush and zoom
1147 if (!forInit) {
1148 if ($$.brush) { $$.brush.scale($$.subX); }
1149 if (config.zoom_enabled) { $$.zoom.scale($$.x); }
1150 }
1151 // update for arc
1152 if ($$.updateArc) { $$.updateArc(); }
1153 };
1154
1155 c3_chart_internal_fn.getYDomainMin = function (targets) {
1156 var $$ = this, config = $$.config,
1157 ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets),
1158 j, k, baseId, idsInGroup, id, hasNegativeValue;
1159 if (config.data_groups.length > 0) {
1160 hasNegativeValue = $$.hasNegativeValueInTargets(targets);
1161 for (j = 0; j < config.data_groups.length; j++) {
1162 // Determine baseId
1163 idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; });
1164 if (idsInGroup.length === 0) { continue; }
1165 baseId = idsInGroup[0];
1166 // Consider negative values
1167 if (hasNegativeValue && ys[baseId]) {
1168 ys[baseId].forEach(function (v, i) {
1169 ys[baseId][i] = v < 0 ? v : 0;
1170 });
1171 }
1172 // Compute min
1173 for (k = 1; k < idsInGroup.length; k++) {
1174 id = idsInGroup[k];
1175 if (! ys[id]) { continue; }
1176 ys[id].forEach(function (v, i) {
1177 if ($$.getAxisId(id) === $$.getAxisId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) {
1178 ys[baseId][i] += +v;
1179 }
1180 });
1181 }
1182 }
1183 }
1184 return $$.d3.min(Object.keys(ys).map(function (key) { return $$.d3.min(ys[key]); }));
1185 };
1186 c3_chart_internal_fn.getYDomainMax = function (targets) {
1187 var $$ = this, config = $$.config,
1188 ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets),
1189 j, k, baseId, idsInGroup, id, hasPositiveValue;
1190 if (config.data_groups.length > 0) {
1191 hasPositiveValue = $$.hasPositiveValueInTargets(targets);
1192 for (j = 0; j < config.data_groups.length; j++) {
1193 // Determine baseId
1194 idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; });
1195 if (idsInGroup.length === 0) { continue; }
1196 baseId = idsInGroup[0];
1197 // Consider positive values
1198 if (hasPositiveValue && ys[baseId]) {
1199 ys[baseId].forEach(function (v, i) {
1200 ys[baseId][i] = v > 0 ? v : 0;
1201 });
1202 }
1203 // Compute max
1204 for (k = 1; k < idsInGroup.length; k++) {
1205 id = idsInGroup[k];
1206 if (! ys[id]) { continue; }
1207 ys[id].forEach(function (v, i) {
1208 if ($$.getAxisId(id) === $$.getAxisId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) {
1209 ys[baseId][i] += +v;
1210 }
1211 });
1212 }
1213 }
1214 }
1215 return $$.d3.max(Object.keys(ys).map(function (key) { return $$.d3.max(ys[key]); }));
1216 };
1217 c3_chart_internal_fn.getYDomain = function (targets, axisId) {
1218 var $$ = this, config = $$.config,
1219 yTargets = targets.filter(function (d) { return $$.getAxisId(d.id) === axisId; }),
1220 yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min,
1221 yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max,
1222 yDomainMin = isValue(yMin) ? yMin : $$.getYDomainMin(yTargets),
1223 yDomainMax = isValue(yMax) ? yMax : $$.getYDomainMax(yTargets),
1224 domainLength, padding, padding_top, padding_bottom,
1225 center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center,
1226 yDomainAbs, lengths, diff, ratio, isAllPositive, isAllNegative,
1227 isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased),
1228 showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated,
1229 showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated;
1230 if (yTargets.length === 0) { // use current domain if target of axisId is none
1231 return axisId === 'y2' ? $$.y2.domain() : $$.y.domain();
1232 }
1233 if (yDomainMin === yDomainMax) {
1234 yDomainMin < 0 ? yDomainMax = 0 : yDomainMin = 0;
1235 }
1236 isAllPositive = yDomainMin >= 0 && yDomainMax >= 0;
1237 isAllNegative = yDomainMin <= 0 && yDomainMax <= 0;
1238
1239 // Bar/Area chart should be 0-based if all positive|negative
1240 if (isZeroBased) {
1241 if (isAllPositive) { yDomainMin = 0; }
1242 if (isAllNegative) { yDomainMax = 0; }
1243 }
1244
1245 domainLength = Math.abs(yDomainMax - yDomainMin);
1246 padding = padding_top = padding_bottom = domainLength * 0.1;
1247
1248 if (center) {
1249 yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax));
1250 yDomainMax = yDomainAbs - center;
1251 yDomainMin = center - yDomainAbs;
1252 }
1253 // add padding for data label
1254 if (showHorizontalDataLabel) {
1255 lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, axisId, 'width');
1256 diff = diffDomain($$.y.range());
1257 ratio = [lengths[0] / diff, lengths[1] / diff];
1258 padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1]));
1259 padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1]));
1260 } else if (showVerticalDataLabel) {
1261 lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, axisId, 'height');
1262 padding_top += lengths[1];
1263 padding_bottom += lengths[0];
1264 }
1265 if (axisId === 'y' && config.axis_y_padding) {
1266 padding_top = $$.getAxisPadding(config.axis_y_padding, 'top', padding, domainLength);
1267 padding_bottom = $$.getAxisPadding(config.axis_y_padding, 'bottom', padding, domainLength);
1268 }
1269 if (axisId === 'y2' && config.axis_y2_padding) {
1270 padding_top = $$.getAxisPadding(config.axis_y2_padding, 'top', padding, domainLength);
1271 padding_bottom = $$.getAxisPadding(config.axis_y2_padding, 'bottom', padding, domainLength);
1272 }
1273 // Bar/Area chart should be 0-based if all positive|negative
1274 if (isZeroBased) {
1275 if (isAllPositive) { padding_bottom = yDomainMin; }
1276 if (isAllNegative) { padding_top = -yDomainMax; }
1277 }
1278 return [yDomainMin - padding_bottom, yDomainMax + padding_top];
1279 };
1280 c3_chart_internal_fn.getXDomainMin = function (targets) {
1281 var $$ = this, config = $$.config;
1282 return config.axis_x_min ?
1283 ($$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min) :
1284 $$.d3.min(targets, function (t) { return $$.d3.min(t.values, function (v) { return v.x; }); });
1285 };
1286 c3_chart_internal_fn.getXDomainMax = function (targets) {
1287 var $$ = this, config = $$.config;
1288 return config.axis_x_max ?
1289 ($$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max) :
1290 $$.d3.max(targets, function (t) { return $$.d3.max(t.values, function (v) { return v.x; }); });
1291 };
1292 c3_chart_internal_fn.getXDomainPadding = function (targets) {
1293 var $$ = this, config = $$.config,
1294 edgeX = this.getEdgeX(targets), diff = edgeX[1] - edgeX[0],
1295 maxDataCount, padding, paddingLeft, paddingRight;
1296 if ($$.isCategorized()) {
1297 padding = 0;
1298 } else if ($$.hasType('bar', targets)) {
1299 maxDataCount = $$.getMaxDataCount();
1300 padding = maxDataCount > 1 ? (diff / (maxDataCount - 1)) / 2 : 0.5;
1301 } else {
1302 padding = diff * 0.01;
1303 }
1304 if (typeof config.axis_x_padding === 'object' && notEmpty(config.axis_x_padding)) {
1305 paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding;
1306 paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding;
1307 } else if (typeof config.axis_x_padding === 'number') {
1308 paddingLeft = paddingRight = config.axis_x_padding;
1309 } else {
1310 paddingLeft = paddingRight = padding;
1311 }
1312 return {left: paddingLeft, right: paddingRight};
1313 };
1314 c3_chart_internal_fn.getXDomain = function (targets) {
1315 var $$ = this,
1316 xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)],
1317 firstX = xDomain[0], lastX = xDomain[1],
1318 padding = $$.getXDomainPadding(targets),
1319 min = 0, max = 0;
1320 // show center of x domain if min and max are the same
1321 if ((firstX - lastX) === 0 && !$$.isCategorized()) {
1322 firstX = $$.isTimeSeries() ? new Date(firstX.getTime() * 0.5) : -0.5;
1323 lastX = $$.isTimeSeries() ? new Date(lastX.getTime() * 1.5) : 0.5;
1324 }
1325 if (firstX || firstX === 0) {
1326 min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left;
1327 }
1328 if (lastX || lastX === 0) {
1329 max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right;
1330 }
1331 return [min, max];
1332 };
1333 c3_chart_internal_fn.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, domain) {
1334 var $$ = this, config = $$.config;
1335 if (withUpdateOrgXDomain) {
1336 $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets)));
1337 $$.orgXDomain = $$.x.domain();
1338 if (config.zoom_enabled) { $$.zoom.scale($$.x).updateScaleExtent(); }
1339 $$.subX.domain($$.x.domain());
1340 if ($$.brush) { $$.brush.scale($$.subX); }
1341 }
1342 if (withUpdateXDomain) {
1343 $$.x.domain(domain ? domain : (!$$.brush || $$.brush.empty()) ? $$.orgXDomain : $$.brush.extent());
1344 if (config.zoom_enabled) { $$.zoom.scale($$.x).updateScaleExtent(); }
1345 }
1346 return $$.x.domain();
1347 };
1348
1349 c3_chart_internal_fn.isX = function (key) {
1350 var $$ = this, config = $$.config;
1351 return (config.data_x && key === config.data_x) || (notEmpty(config.data_xs) && hasValue(config.data_xs, key));
1352 };
1353 c3_chart_internal_fn.isNotX = function (key) {
1354 return !this.isX(key);
1355 };
1356 c3_chart_internal_fn.getXKey = function (id) {
1357 var $$ = this, config = $$.config;
1358 return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null;
1359 };
1360 c3_chart_internal_fn.getXValuesOfXKey = function (key, targets) {
1361 var $$ = this,
1362 xValues, ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : [];
1363 ids.forEach(function (id) {
1364 if ($$.getXKey(id) === key) {
1365 xValues = $$.data.xs[id];
1366 }
1367 });
1368 return xValues;
1369 };
1370 c3_chart_internal_fn.getXValue = function (id, i) {
1371 var $$ = this;
1372 return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i;
1373 };
1374 c3_chart_internal_fn.getOtherTargetXs = function () {
1375 var $$ = this,
1376 idsForX = Object.keys($$.data.xs);
1377 return idsForX.length ? $$.data.xs[idsForX[0]] : null;
1378 };
1379 c3_chart_internal_fn.getOtherTargetX = function (index) {
1380 var xs = this.getOtherTargetXs();
1381 return xs && index < xs.length ? xs[index] : null;
1382 };
1383 c3_chart_internal_fn.addXs = function (xs) {
1384 var $$ = this;
1385 Object.keys(xs).forEach(function (id) {
1386 $$.config.data_xs[id] = xs[id];
1387 });
1388 };
1389 c3_chart_internal_fn.hasMultipleX = function (xs) {
1390 return this.d3.set(Object.keys(xs).map(function (id) { return xs[id]; })).size() > 1;
1391 };
1392 c3_chart_internal_fn.isMultipleX = function () {
1393 var $$ = this, config = $$.config;
1394 return notEmpty(config.data_xs) && $$.hasMultipleX(config.data_xs);
1395 };
1396 c3_chart_internal_fn.addName = function (data) {
1397 var $$ = this, name;
1398 if (data) {
1399 name = $$.config.data_names[data.id];
1400 data.name = name ? name : data.id;
1401 }
1402 return data;
1403 };
1404 c3_chart_internal_fn.getValueOnIndex = function (values, index) {
1405 var valueOnIndex = values.filter(function (v) { return v.index === index; });
1406 return valueOnIndex.length ? valueOnIndex[0] : null;
1407 };
1408 c3_chart_internal_fn.updateTargetX = function (targets, x) {
1409 var $$ = this;
1410 targets.forEach(function (t) {
1411 t.values.forEach(function (v, i) {
1412 v.x = $$.generateTargetX(x[i], t.id, i);
1413 });
1414 $$.data.xs[t.id] = x;
1415 });
1416 };
1417 c3_chart_internal_fn.updateTargetXs = function (targets, xs) {
1418 var $$ = this;
1419 targets.forEach(function (t) {
1420 if (xs[t.id]) {
1421 $$.updateTargetX([t], xs[t.id]);
1422 }
1423 });
1424 };
1425 c3_chart_internal_fn.generateTargetX = function (rawX, id, index) {
1426 var $$ = this, x;
1427 if ($$.isTimeSeries()) {
1428 x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index));
1429 }
1430 else if ($$.isCustomX() && !$$.isCategorized()) {
1431 x = isValue(rawX) ? +rawX : $$.getXValue(id, index);
1432 }
1433 else {
1434 x = index;
1435 }
1436 return x;
1437 };
1438 c3_chart_internal_fn.cloneTarget = function (target) {
1439 return {
1440 id : target.id,
1441 id_org : target.id_org,
1442 values : target.values.map(function (d) {
1443 return {x: d.x, value: d.value, id: d.id};
1444 })
1445 };
1446 };
1447 c3_chart_internal_fn.getPrevX = function (i) {
1448 var $$ = this, value = $$.getValueOnIndex($$.data.targets[0].values, i - 1);
1449 return value ? value.x : null;
1450 };
1451 c3_chart_internal_fn.getNextX = function (i) {
1452 var $$ = this, value = $$.getValueOnIndex($$.data.targets[0].values, i + 1);
1453 return value ? value.x : null;
1454 };
1455 c3_chart_internal_fn.getMaxDataCount = function () {
1456 var $$ = this;
1457 return $$.d3.max($$.data.targets, function (t) { return t.values.length; });
1458 };
1459 c3_chart_internal_fn.getMaxDataCountTarget = function (targets) {
1460 var length = targets.length, max = 0, maxTarget;
1461 if (length > 1) {
1462 targets.forEach(function (t) {
1463 if (t.values.length > max) {
1464 maxTarget = t;
1465 max = t.values.length;
1466 }
1467 });
1468 } else {
1469 maxTarget = length ? targets[0] : null;
1470 }
1471 return maxTarget;
1472 };
1473 c3_chart_internal_fn.getEdgeX = function (targets) {
1474 var target = this.getMaxDataCountTarget(targets), firstData, lastData;
1475 if (!target) {
1476 return [0, 0];
1477 }
1478 firstData = target.values[0], lastData = target.values[target.values.length - 1];
1479 return [firstData.x, lastData.x];
1480 };
1481 c3_chart_internal_fn.mapToIds = function (targets) {
1482 return targets.map(function (d) { return d.id; });
1483 };
1484 c3_chart_internal_fn.mapToTargetIds = function (ids) {
1485 var $$ = this;
1486 return ids ? (isString(ids) ? [ids] : ids) : $$.mapToIds($$.data.targets);
1487 };
1488 c3_chart_internal_fn.hasTarget = function (targets, id) {
1489 var ids = this.mapToIds(targets), i;
1490 for (i = 0; i < ids.length; i++) {
1491 if (ids[i] === id) {
1492 return true;
1493 }
1494 }
1495 return false;
1496 };
1497 c3_chart_internal_fn.isTargetToShow = function (targetId) {
1498 return this.hiddenTargetIds.indexOf(targetId) < 0;
1499 };
1500 c3_chart_internal_fn.isLegendToShow = function (targetId) {
1501 return this.hiddenLegendIds.indexOf(targetId) < 0;
1502 };
1503 c3_chart_internal_fn.filterTargetsToShow = function (targets) {
1504 var $$ = this;
1505 return targets.filter(function (t) { return $$.isTargetToShow(t.id); });
1506 };
1507 c3_chart_internal_fn.mapTargetsToUniqueXs = function (targets) {
1508 var $$ = this;
1509 var xs = $$.d3.set($$.d3.merge(targets.map(function (t) { return t.values.map(function (v) { return v.x; }); }))).values();
1510 return $$.isTimeSeries() ? xs.map(function (x) { return new Date(x); }) : xs.map(function (x) { return +x; });
1511 };
1512 c3_chart_internal_fn.addHiddenTargetIds = function (targetIds) {
1513 this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds);
1514 };
1515 c3_chart_internal_fn.removeHiddenTargetIds = function (targetIds) {
1516 this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; });
1517 };
1518 c3_chart_internal_fn.addHiddenLegendIds = function (targetIds) {
1519 this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds);
1520 };
1521 c3_chart_internal_fn.removeHiddenLegendIds = function (targetIds) {
1522 this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) { return targetIds.indexOf(id) < 0; });
1523 };
1524 c3_chart_internal_fn.getValuesAsIdKeyed = function (targets) {
1525 var ys = {};
1526 targets.forEach(function (t) {
1527 ys[t.id] = [];
1528 t.values.forEach(function (v) {
1529 ys[t.id].push(v.value);
1530 });
1531 });
1532 return ys;
1533 };
1534 c3_chart_internal_fn.checkValueInTargets = function (targets, checker) {
1535 var ids = Object.keys(targets), i, j, values;
1536 for (i = 0; i < ids.length; i++) {
1537 values = targets[ids[i]].values;
1538 for (j = 0; j < values.length; j++) {
1539 if (checker(values[j].value)) {
1540 return true;
1541 }
1542 }
1543 }
1544 return false;
1545 };
1546 c3_chart_internal_fn.hasNegativeValueInTargets = function (targets) {
1547 return this.checkValueInTargets(targets, function (v) { return v < 0; });
1548 };
1549 c3_chart_internal_fn.hasPositiveValueInTargets = function (targets) {
1550 return this.checkValueInTargets(targets, function (v) { return v > 0; });
1551 };
1552 c3_chart_internal_fn.isOrderDesc = function () {
1553 var config = this.config;
1554 return config.data_order && config.data_order.toLowerCase() === 'desc';
1555 };
1556 c3_chart_internal_fn.isOrderAsc = function () {
1557 var config = this.config;
1558 return config.data_order && config.data_order.toLowerCase() === 'asc';
1559 };
1560 c3_chart_internal_fn.orderTargets = function (targets) {
1561 var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc();
1562 if (orderAsc || orderDesc) {
1563 targets.sort(function (t1, t2) {
1564 var reducer = function (p, c) { return p + Math.abs(c.value); };
1565 var t1Sum = t1.values.reduce(reducer, 0),
1566 t2Sum = t2.values.reduce(reducer, 0);
1567 return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum;
1568 });
1569 } else if (isFunction(config.data_order)) {
1570 targets.sort(config.data_order);
1571 } // TODO: accept name array for order
1572 return targets;
1573 };
1574 c3_chart_internal_fn.filterSameX = function (targets, x) {
1575 return this.d3.merge(targets.map(function (t) { return t.values; })).filter(function (v) { return v.x - x === 0; });
1576 };
1577 c3_chart_internal_fn.filterRemoveNull = function (data) {
1578 return data.filter(function (d) { return isValue(d.value); });
1579 };
1580 c3_chart_internal_fn.hasDataLabel = function () {
1581 var config = this.config;
1582 if (typeof config.data_labels === 'boolean' && config.data_labels) {
1583 return true;
1584 } else if (typeof config.data_labels === 'object' && notEmpty(config.data_labels)) {
1585 return true;
1586 }
1587 return false;
1588 };
1589 c3_chart_internal_fn.getDataLabelLength = function (min, max, axisId, key) {
1590 var $$ = this,
1591 lengths = [0, 0], paddingCoef = 1.3;
1592 $$.selectChart.select('svg').selectAll('.dummy')
1593 .data([min, max])
1594 .enter().append('text')
1595 .text(function (d) { return $$.formatByAxisId(axisId)(d); })
1596 .each(function (d, i) {
1597 lengths[i] = this.getBoundingClientRect()[key] * paddingCoef;
1598 })
1599 .remove();
1600 return lengths;
1601 };
1602 c3_chart_internal_fn.isNoneArc = function (d) {
1603 return this.hasTarget(this.data.targets, d.id);
1604 },
1605 c3_chart_internal_fn.isArc = function (d) {
1606 return 'data' in d && this.hasTarget(this.data.targets, d.data.id);
1607 };
1608 c3_chart_internal_fn.findSameXOfValues = function (values, index) {
1609 var i, targetX = values[index].x, sames = [];
1610 for (i = index - 1; i >= 0; i--) {
1611 if (targetX !== values[i].x) { break; }
1612 sames.push(values[i]);
1613 }
1614 for (i = index; i < values.length; i++) {
1615 if (targetX !== values[i].x) { break; }
1616 sames.push(values[i]);
1617 }
1618 return sames;
1619 };
1620
1621 c3_chart_internal_fn.findClosestOfValues = function (values, pos, _min, _max) { // MEMO: values must be sorted by x
1622 var $$ = this,
1623 min = _min ? _min : 0,
1624 max = _max ? _max : values.length - 1,
1625 med = Math.floor((max - min) / 2) + min,
1626 value = values[med],
1627 diff = $$.x(value.x) - pos[$$.config.axis_rotated ? 1 : 0],
1628 candidates;
1629
1630 // Update range for search
1631 diff > 0 ? max = med : min = med;
1632
1633 // if candidates are two closest min and max, stop recursive call
1634 if ((max - min) === 1 || (min === 0 && max === 0)) {
1635
1636 // Get candidates that has same min and max index
1637 candidates = [];
1638 if (values[min].x || values[min].x === 0) {
1639 candidates = candidates.concat($$.findSameXOfValues(values, min));
1640 }
1641 if (values[max].x || values[max].x === 0) {
1642 candidates = candidates.concat($$.findSameXOfValues(values, max));
1643 }
1644
1645 // Determine the closest and return
1646 return $$.findClosest(candidates, pos);
1647 }
1648
1649 return $$.findClosestOfValues(values, pos, min, max);
1650 };
1651 c3_chart_internal_fn.findClosestFromTargets = function (targets, pos) {
1652 var $$ = this, candidates;
1653
1654 // map to array of closest points of each target
1655 candidates = targets.map(function (target) {
1656 return $$.findClosestOfValues(target.values, pos);
1657 });
1658
1659 // decide closest point and return
1660 return $$.findClosest(candidates, pos);
1661 };
1662 c3_chart_internal_fn.findClosest = function (values, pos) {
1663 var $$ = this, minDist, closest;
1664 values.forEach(function (v) {
1665 var d = $$.dist(v, pos);
1666 if (d < minDist || ! minDist) {
1667 minDist = d;
1668 closest = v;
1669 }
1670 });
1671 return closest;
1672 };
1673 c3_chart_internal_fn.dist = function (data, pos) {
1674 var $$ = this, config = $$.config,
1675 yScale = $$.getAxisId(data.id) === 'y' ? $$.y : $$.y2,
1676 xIndex = config.axis_rotated ? 1 : 0,
1677 yIndex = config.axis_rotated ? 0 : 1;
1678 return Math.pow($$.x(data.x) - pos[xIndex], 2) + Math.pow(yScale(data.value) - pos[yIndex], 2);
1679 };
1680
1681 c3_chart_internal_fn.convertUrlToData = function (url, mimeType, keys, done) {
1682 var $$ = this, type = mimeType ? mimeType : 'csv';
1683 $$.d3.xhr(url, function (error, data) {
1684 var d;
1685 if (type === 'json') {
1686 d = $$.convertJsonToData(JSON.parse(data.response), keys);
1687 } else {
1688 d = $$.convertCsvToData(data.response);
1689 }
1690 done.call($$, d);
1691 });
1692 };
1693 c3_chart_internal_fn.convertCsvToData = function (csv) {
1694 var d3 = this.d3, rows = d3.csv.parseRows(csv), d;
1695 if (rows.length === 1) {
1696 d = [{}];
1697 rows[0].forEach(function (id) {
1698 d[0][id] = null;
1699 });
1700 } else {
1701 d = d3.csv.parse(csv);
1702 }
1703 return d;
1704 };
1705 c3_chart_internal_fn.convertJsonToData = function (json, keys) {
1706 var $$ = this,
1707 new_rows = [], targetKeys, data;
1708 if (keys) { // when keys specified, json would be an array that includes objects
1709 targetKeys = keys.value;
1710 if (keys.x) {
1711 targetKeys.push(keys.x);
1712 $$.config.data_x = keys.x;
1713 }
1714 new_rows.push(targetKeys);
1715 json.forEach(function (o) {
1716 var new_row = [];
1717 targetKeys.forEach(function (key) {
1718 // convert undefined to null because undefined data will be removed in convertDataToTargets()
1719 var v = isUndefined(o[key]) ? null : o[key];
1720 new_row.push(v);
1721 });
1722 new_rows.push(new_row);
1723 });
1724 data = $$.convertRowsToData(new_rows);
1725 } else {
1726 Object.keys(json).forEach(function (key) {
1727 new_rows.push([key].concat(json[key]));
1728 });
1729 data = $$.convertColumnsToData(new_rows);
1730 }
1731 return data;
1732 };
1733 c3_chart_internal_fn.convertRowsToData = function (rows) {
1734 var keys = rows[0], new_row = {}, new_rows = [], i, j;
1735 for (i = 1; i < rows.length; i++) {
1736 new_row = {};
1737 for (j = 0; j < rows[i].length; j++) {
1738 if (isUndefined(rows[i][j])) {
1739 throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
1740 }
1741 new_row[keys[j]] = rows[i][j];
1742 }
1743 new_rows.push(new_row);
1744 }
1745 return new_rows;
1746 };
1747 c3_chart_internal_fn.convertColumnsToData = function (columns) {
1748 var new_rows = [], i, j, key;
1749 for (i = 0; i < columns.length; i++) {
1750 key = columns[i][0];
1751 for (j = 1; j < columns[i].length; j++) {
1752 if (isUndefined(new_rows[j - 1])) {
1753 new_rows[j - 1] = {};
1754 }
1755 if (isUndefined(columns[i][j])) {
1756 throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
1757 }
1758 new_rows[j - 1][key] = columns[i][j];
1759 }
1760 }
1761 return new_rows;
1762 };
1763 c3_chart_internal_fn.convertDataToTargets = function (data, appendXs) {
1764 var $$ = this, config = $$.config,
1765 ids = $$.d3.keys(data[0]).filter($$.isNotX, $$),
1766 xs = $$.d3.keys(data[0]).filter($$.isX, $$),
1767 targets;
1768
1769 // save x for update data by load when custom x and c3.x API
1770 ids.forEach(function (id) {
1771 var xKey = $$.getXKey(id);
1772
1773 if ($$.isCustomX() || $$.isTimeSeries()) {
1774 // if included in input data
1775 if (xs.indexOf(xKey) >= 0) {
1776 $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : []).concat(
1777 data.map(function (d) { return d[xKey]; })
1778 .filter(isValue)
1779 .map(function (rawX, i) { return $$.generateTargetX(rawX, id, i); })
1780 );
1781 }
1782 // if not included in input data, find from preloaded data of other id's x
1783 else if (config.data_x) {
1784 $$.data.xs[id] = $$.getOtherTargetXs();
1785 }
1786 // if not included in input data, find from preloaded data
1787 else if (notEmpty(config.data_xs)) {
1788 $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets);
1789 }
1790 // MEMO: if no x included, use same x of current will be used
1791 } else {
1792 $$.data.xs[id] = data.map(function (d, i) { return i; });
1793 }
1794 });
1795
1796
1797 // check x is defined
1798 ids.forEach(function (id) {
1799 if (!$$.data.xs[id]) {
1800 throw new Error('x is not defined for id = "' + id + '".');
1801 }
1802 });
1803
1804 // convert to target
1805 targets = ids.map(function (id, index) {
1806 var convertedId = config.data_idConverter(id);
1807 return {
1808 id: convertedId,
1809 id_org: id,
1810 values: data.map(function (d, i) {
1811 var xKey = $$.getXKey(id), rawX = d[xKey], x = $$.generateTargetX(rawX, id, i);
1812 // use x as categories if custom x and categorized
1813 if ($$.isCustomX() && $$.isCategorized() && index === 0 && rawX) {
1814 if (i === 0) { config.axis_x_categories = []; }
1815 config.axis_x_categories.push(rawX);
1816 }
1817 // mark as x = undefined if value is undefined and filter to remove after mapped
1818 if (isUndefined(d[id]) || $$.data.xs[id].length <= i) {
1819 x = undefined;
1820 }
1821 return {x: x, value: d[id] !== null && !isNaN(d[id]) ? +d[id] : null, id: convertedId};
1822 }).filter(function (v) { return isDefined(v.x); })
1823 };
1824 });
1825
1826 // finish targets
1827 targets.forEach(function (t) {
1828 var i;
1829 // sort values by its x
1830 t.values = t.values.sort(function (v1, v2) {
1831 var x1 = v1.x || v1.x === 0 ? v1.x : Infinity,
1832 x2 = v2.x || v2.x === 0 ? v2.x : Infinity;
1833 return x1 - x2;
1834 });
1835 // indexing each value
1836 i = 0;
1837 t.values.forEach(function (v) {
1838 v.index = i++;
1839 });
1840 // this needs to be sorted because its index and value.index is identical
1841 $$.data.xs[t.id].sort(function (v1, v2) {
1842 return v1 - v2;
1843 });
1844 });
1845
1846 // set target types
1847 if (config.data_type) {
1848 $$.setTargetType($$.mapToIds(targets).filter(function (id) { return ! (id in config.data_types); }), config.data_type);
1849 }
1850
1851 // cache as original id keyed
1852 targets.forEach(function (d) {
1853 $$.addCache(d.id_org, d);
1854 });
1855
1856 return targets;
1857 };
1858
1859 c3_chart_internal_fn.load = function (targets, args) {
1860 var $$ = this;
1861 if (targets) {
1862 // filter loading targets if needed
1863 if (args.filter) {
1864 targets = targets.filter(args.filter);
1865 }
1866 // set type if args.types || args.type specified
1867 if (args.type || args.types) {
1868 targets.forEach(function (t) {
1869 $$.setTargetType(t.id, args.types ? args.types[t.id] : args.type);
1870 });
1871 }
1872 // Update/Add data
1873 $$.data.targets.forEach(function (d) {
1874 for (var i = 0; i < targets.length; i++) {
1875 if (d.id === targets[i].id) {
1876 d.values = targets[i].values;
1877 targets.splice(i, 1);
1878 break;
1879 }
1880 }
1881 });
1882 $$.data.targets = $$.data.targets.concat(targets); // add remained
1883 }
1884
1885 // Set targets
1886 $$.updateTargets($$.data.targets);
1887
1888 // Redraw with new targets
1889 $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
1890
1891 if (args.done) { args.done(); }
1892 };
1893 c3_chart_internal_fn.loadFromArgs = function (args) {
1894 var $$ = this;
1895 if (args.data) {
1896 $$.load($$.convertDataToTargets(args.data), args);
1897 }
1898 else if (args.url) {
1899 $$.convertUrlToData(args.url, args.mimeType, args.keys, function (data) {
1900 $$.load($$.convertDataToTargets(data), args);
1901 });
1902 }
1903 else if (args.json) {
1904 $$.load($$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args);
1905 }
1906 else if (args.rows) {
1907 $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args);
1908 }
1909 else if (args.columns) {
1910 $$.load($$.convertDataToTargets($$.convertColumnsToData(args.columns)), args);
1911 }
1912 else {
1913 $$.load(null, args);
1914 }
1915 };
1916 c3_chart_internal_fn.unload = function (targetIds, done) {
1917 var $$ = this;
1918 if (!done) {
1919 done = function () {};
1920 }
1921 // filter existing target
1922 targetIds = targetIds.filter(function (id) { return $$.hasTarget($$.data.targets, id); });
1923 // If no target, call done and return
1924 if (!targetIds || targetIds.length === 0) {
1925 done();
1926 return;
1927 }
1928 $$.svg.selectAll(targetIds.map(function (id) { return $$.selectorTarget(id); }))
1929 .transition()
1930 .style('opacity', 0)
1931 .remove()
1932 .call($$.endall, done);
1933 targetIds.forEach(function (id) {
1934 // Reset fadein for future load
1935 $$.withoutFadeIn[id] = false;
1936 // Remove target's elements
1937 if ($$.legend) {
1938 $$.legend.selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)).remove();
1939 }
1940 // Remove target
1941 $$.data.targets = $$.data.targets.filter(function (t) {
1942 return t.id !== id;
1943 });
1944 });
1945 };
1946
1947 c3_chart_internal_fn.categoryName = function (i) {
1948 var config = this.config;
1949 return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i;
1950 };
1951
1952 c3_chart_internal_fn.initEventRect = function () {
1953 var $$ = this;
1954 $$.main.select('.' + CLASS.chart).append("g")
1955 .attr("class", CLASS.eventRects)
1956 .style('fill-opacity', 0);
1957 };
1958 c3_chart_internal_fn.redrawEventRect = function () {
1959 var $$ = this, config = $$.config,
1960 eventRectUpdate, maxDataCountTarget,
1961 isMultipleX = $$.isMultipleX();
1962
1963 // rects for mouseover
1964 var eventRects = $$.main.select('.' + CLASS.eventRects)
1965 .style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null)
1966 .classed(CLASS.eventRectsMultiple, isMultipleX)
1967 .classed(CLASS.eventRectsSingle, !isMultipleX);
1968
1969 // clear old rects
1970 eventRects.selectAll('.' + CLASS.eventRect).remove();
1971
1972 // open as public variable
1973 $$.eventRect = eventRects.selectAll('.' + CLASS.eventRect);
1974
1975 if (isMultipleX) {
1976 eventRectUpdate = $$.eventRect.data([0]);
1977 // enter : only one rect will be added
1978 $$.generateEventRectsForMultipleXs(eventRectUpdate.enter());
1979 // update
1980 $$.updateEventRect(eventRectUpdate);
1981 // exit : not needed because always only one rect exists
1982 }
1983 else {
1984 // Set data and update $$.eventRect
1985 maxDataCountTarget = $$.getMaxDataCountTarget($$.data.targets);
1986 eventRects.datum(maxDataCountTarget ? maxDataCountTarget.values : []);
1987 $$.eventRect = eventRects.selectAll('.' + CLASS.eventRect);
1988 eventRectUpdate = $$.eventRect.data(function (d) { return d; });
1989 // enter
1990 $$.generateEventRectsForSingleX(eventRectUpdate.enter());
1991 // update
1992 $$.updateEventRect(eventRectUpdate);
1993 // exit
1994 eventRectUpdate.exit().remove();
1995 }
1996 };
1997 c3_chart_internal_fn.updateEventRect = function (eventRectUpdate) {
1998 var $$ = this, config = $$.config,
1999 x, y, w, h, rectW, rectX;
2000
2001 // set update selection if null
2002 eventRectUpdate = eventRectUpdate || $$.eventRect.data(function (d) { return d; });
2003
2004 if ($$.isMultipleX()) {
2005 // TODO: rotated not supported yet
2006 x = 0;
2007 y = 0;
2008 w = $$.width;
2009 h = $$.height;
2010 }
2011 else {
2012 if (($$.isCustomX() || $$.isTimeSeries()) && !$$.isCategorized()) {
2013 rectW = function (d) {
2014 var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index), dx = $$.data.xs[d.id][d.index],
2015 w = ($$.x(nextX ? nextX : dx) - $$.x(prevX ? prevX : dx)) / 2;
2016 return w < 0 ? 0 : w;
2017 };
2018 rectX = function (d) {
2019 var prevX = $$.getPrevX(d.index), dx = $$.data.xs[d.id][d.index];
2020 return ($$.x(dx) + $$.x(prevX ? prevX : dx)) / 2;
2021 };
2022 } else {
2023 rectW = $$.getEventRectWidth();
2024 rectX = function (d) {
2025 return $$.x(d.x) - (rectW / 2);
2026 };
2027 }
2028 x = config.axis_rotated ? 0 : rectX;
2029 y = config.axis_rotated ? rectX : 0;
2030 w = config.axis_rotated ? $$.width : rectW;
2031 h = config.axis_rotated ? rectW : $$.height;
2032 }
2033
2034 eventRectUpdate
2035 .attr('class', $$.classEvent.bind($$))
2036 .attr("x", x)
2037 .attr("y", y)
2038 .attr("width", w)
2039 .attr("height", h);
2040 };
2041 c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) {
2042 var $$ = this, d3 = $$.d3, config = $$.config;
2043 eventRectEnter.append("rect")
2044 .attr("class", $$.classEvent.bind($$))
2045 .style("cursor", config.data_selection_enabled && config.data_selection_grouped ? "pointer" : null)
2046 .on('mouseover', function (d) {
2047 var index = d.index, selectedData, newData;
2048
2049 if ($$.dragging) { return; } // do nothing if dragging
2050 if ($$.hasArcType()) { return; }
2051
2052 selectedData = $$.data.targets.map(function (t) {
2053 return $$.addName($$.getValueOnIndex(t.values, index));
2054 });
2055
2056 // Sort selectedData as names order
2057 newData = [];
2058 Object.keys(config.data_names).forEach(function (id) {
2059 for (var j = 0; j < selectedData.length; j++) {
2060 if (selectedData[j] && selectedData[j].id === id) {
2061 newData.push(selectedData[j]);
2062 selectedData.shift(j);
2063 break;
2064 }
2065 }
2066 });
2067 selectedData = newData.concat(selectedData); // Add remained
2068
2069 // Expand shapes for selection
2070 if (config.point_focus_expand_enabled) { $$.expandCircles(index); }
2071 $$.expandBars(index);
2072
2073 // Call event handler
2074 $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) {
2075 config.data_onmouseover.call($$, d);
2076 });
2077 })
2078 .on('mouseout', function (d) {
2079 var index = d.index;
2080 if ($$.hasArcType()) { return; }
2081 $$.hideXGridFocus();
2082 $$.hideTooltip();
2083 // Undo expanded shapes
2084 $$.unexpandCircles(index);
2085 $$.unexpandBars();
2086 // Call event handler
2087 $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) {
2088 config.data_onmouseout.call($$, d);
2089 });
2090 })
2091 .on('mousemove', function (d) {
2092 var selectedData, index = d.index,
2093 eventRect = $$.svg.select('.' + CLASS.eventRect + '-' + index);
2094
2095 if ($$.dragging) { return; } // do nothing when dragging
2096 if ($$.hasArcType()) { return; }
2097
2098 // Show tooltip
2099 selectedData = $$.filterTargetsToShow($$.data.targets).map(function (t) {
2100 return $$.addName($$.getValueOnIndex(t.values, index));
2101 });
2102
2103 if (config.tooltip_grouped) {
2104 $$.showTooltip(selectedData, d3.mouse(this));
2105 $$.showXGridFocus(selectedData);
2106 }
2107
2108 if (config.tooltip_grouped && (!config.data_selection_enabled || config.data_selection_grouped)) {
2109 return;
2110 }
2111
2112 $$.main.selectAll('.' + CLASS.shape + '-' + index)
2113 .each(function () {
2114 d3.select(this).classed(CLASS.EXPANDED, true);
2115 if (config.data_selection_enabled) {
2116 eventRect.style('cursor', config.data_selection_grouped ? 'pointer' : null);
2117 }
2118 if (!config.tooltip_grouped) {
2119 $$.hideXGridFocus();
2120 $$.hideTooltip();
2121 if (!config.data_selection_grouped) {
2122 $$.unexpandCircles(index);
2123 $$.unexpandBars();
2124 }
2125 }
2126 })
2127 .filter(function (d) {
2128 if (this.nodeName === 'circle') {
2129 return $$.isWithinCircle(this, $$.pointSelectR(d));
2130 }
2131 else if (this.nodeName === 'path') {
2132 return $$.isWithinBar(this);
2133 }
2134 })
2135 .each(function (d) {
2136 if (config.data_selection_enabled && (config.data_selection_grouped || config.data_selection_isselectable(d))) {
2137 eventRect.style('cursor', 'pointer');
2138 }
2139 if (!config.tooltip_grouped) {
2140 $$.showTooltip([d], d3.mouse(this));
2141 $$.showXGridFocus([d]);
2142 if (config.point_focus_expand_enabled) { $$.expandCircles(index, d.id); }
2143 $$.expandBars(index, d.id);
2144 }
2145 });
2146 })
2147 .on('click', function (d) {
2148 var index = d.index;
2149 if ($$.hasArcType() || !$$.toggleShape) { return; }
2150 if ($$.cancelClick) {
2151 $$.cancelClick = false;
2152 return;
2153 }
2154 $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) {
2155 $$.toggleShape(this, d, index);
2156 });
2157 })
2158 .call(
2159 d3.behavior.drag().origin(Object)
2160 .on('drag', function () { $$.drag(d3.mouse(this)); })
2161 .on('dragstart', function () { $$.dragstart(d3.mouse(this)); })
2162 .on('dragend', function () { $$.dragend(); })
2163 )
2164 .on("dblclick.zoom", null);
2165 };
2166
2167 c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter) {
2168 var $$ = this, d3 = $$.d3, config = $$.config;
2169 eventRectEnter.append('rect')
2170 .attr('x', 0)
2171 .attr('y', 0)
2172 .attr('width', $$.width)
2173 .attr('height', $$.height)
2174 .attr('class', CLASS.eventRect)
2175 .on('mouseout', function () {
2176 if ($$.hasArcType()) { return; }
2177 $$.hideXGridFocus();
2178 $$.hideTooltip();
2179 $$.unexpandCircles();
2180 })
2181 .on('mousemove', function () {
2182 var targetsToShow = $$.filterTargetsToShow($$.data.targets);
2183 var mouse, closest, sameXData, selectedData;
2184
2185 if ($$.dragging) { return; } // do nothing when dragging
2186 if ($$.hasArcType(targetsToShow)) { return; }
2187
2188 mouse = d3.mouse(this);
2189 closest = $$.findClosestFromTargets(targetsToShow, mouse);
2190
2191 if (! closest) { return; }
2192
2193 if ($$.isScatterType(closest)) {
2194 sameXData = [closest];
2195 } else {
2196 sameXData = $$.filterSameX(targetsToShow, closest.x);
2197 }
2198
2199 // show tooltip when cursor is close to some point
2200 selectedData = sameXData.map(function (d) {
2201 return $$.addName(d);
2202 });
2203 $$.showTooltip(selectedData, mouse);
2204
2205 // expand points
2206 if (config.point_focus_expand_enabled) {
2207 $$.unexpandCircles();
2208 $$.expandCircles(closest.index, closest.id);
2209 }
2210
2211 // Show xgrid focus line
2212 $$.showXGridFocus(selectedData);
2213
2214 // Show cursor as pointer if point is close to mouse position
2215 if ($$.dist(closest, mouse) < 100) {
2216 $$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer');
2217 if (!$$.mouseover) {
2218 config.data_onmouseover.call($$, closest);
2219 $$.mouseover = true;
2220 }
2221 } else if ($$.mouseover) {
2222 $$.svg.select('.' + CLASS.eventRect).style('cursor', null);
2223 config.data_onmouseout.call($$, closest);
2224 $$.mouseover = false;
2225 }
2226 })
2227 .on('click', function () {
2228 var targetsToShow = $$.filterTargetsToShow($$.data.targets);
2229 var mouse, closest;
2230
2231 if ($$.hasArcType(targetsToShow)) { return; }
2232
2233 mouse = d3.mouse(this);
2234 closest = $$.findClosestFromTargets(targetsToShow, mouse);
2235
2236 if (! closest) { return; }
2237
2238 // select if selection enabled
2239 if ($$.dist(closest, mouse) < 100 && $$.toggleShape) {
2240 $$.main.select('.' + CLASS.circles + $$.getTargetSelectorSuffix(closest.id)).select('.' + CLASS.circle + '-' + closest.index).each(function () {
2241 $$.toggleShape(this, closest, closest.index);
2242 });
2243 }
2244 })
2245 .call(
2246 d3.behavior.drag().origin(Object)
2247 .on('drag', function () { $$.drag(d3.mouse(this)); })
2248 .on('dragstart', function () { $$.dragstart(d3.mouse(this)); })
2249 .on('dragend', function () { $$.dragend(); })
2250 )
2251 .on("dblclick.zoom", null);
2252 };
2253
2254 c3_chart_internal_fn.getCurrentWidth = function () {
2255 var $$ = this, config = $$.config;
2256 return config.size_width ? config.size_width : $$.getParentWidth();
2257 };
2258 c3_chart_internal_fn.getCurrentHeight = function () {
2259 var $$ = this, config = $$.config,
2260 h = config.size_height ? config.size_height : $$.getParentHeight();
2261 return h > 0 ? h : 320;
2262 };
2263 c3_chart_internal_fn.getCurrentPaddingTop = function () {
2264 var config = this.config;
2265 return isValue(config.padding_top) ? config.padding_top : 0;
2266 };
2267 c3_chart_internal_fn.getCurrentPaddingBottom = function () {
2268 var config = this.config;
2269 return isValue(config.padding_bottom) ? config.padding_bottom : 0;
2270 };
2271 c3_chart_internal_fn.getCurrentPaddingLeft = function () {
2272 var $$ = this, config = $$.config;
2273 if (isValue(config.padding_left)) {
2274 return config.padding_left;
2275 } else if (config.axis_rotated) {
2276 return !config.axis_x_show ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x')), 40);
2277 } else {
2278 return !config.axis_y_show ? 1 : ceil10($$.getAxisWidthByAxisId('y'));
2279 }
2280 };
2281 c3_chart_internal_fn.getCurrentPaddingRight = function () {
2282 var $$ = this, config = $$.config,
2283 defaultPadding = 10, legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0;
2284 if (isValue(config.padding_right)) {
2285 return config.padding_right + 1; // 1 is needed not to hide tick line
2286 } else if (config.axis_rotated) {
2287 return defaultPadding + legendWidthOnRight;
2288 } else {
2289 return (!config.axis_y2_show ? defaultPadding : ceil10($$.getAxisWidthByAxisId('y2'))) + legendWidthOnRight;
2290 }
2291 };
2292
2293 c3_chart_internal_fn.getParentRectValue = function (key) {
2294 var parent = this.selectChart.node(), v;
2295 while (parent && parent.tagName !== 'BODY') {
2296 v = parent.getBoundingClientRect()[key];
2297 if (v) {
2298 break;
2299 }
2300 parent = parent.parentNode;
2301 }
2302 return v;
2303 };
2304 c3_chart_internal_fn.getParentWidth = function () {
2305 return this.getParentRectValue('width');
2306 };
2307 c3_chart_internal_fn.getParentHeight = function () {
2308 var h = this.selectChart.style('height');
2309 return h.indexOf('px') > 0 ? +h.replace('px', '') : 0;
2310 };
2311
2312
2313 c3_chart_internal_fn.getSvgLeft = function () {
2314 var $$ = this, config = $$.config,
2315 leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY,
2316 leftAxis = $$.main.select('.' + leftAxisClass).node(),
2317 svgRect = leftAxis ? leftAxis.getBoundingClientRect() : {right: 0},
2318 chartRect = $$.selectChart.node().getBoundingClientRect(),
2319 hasArc = $$.hasArcType(),
2320 svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft());
2321 return svgLeft > 0 ? svgLeft : 0;
2322 };
2323
2324
2325 c3_chart_internal_fn.getAxisWidthByAxisId = function (id) {
2326 var $$ = this, position = $$.getAxisLabelPositionById(id);
2327 return position.isInner ? 20 + $$.getMaxTickWidth(id) : 40 + $$.getMaxTickWidth(id);
2328 };
2329 c3_chart_internal_fn.getHorizontalAxisHeight = function (axisId) {
2330 var $$ = this, config = $$.config;
2331 if (axisId === 'x' && !config.axis_x_show) { return 0; }
2332 if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height; }
2333 if (axisId === 'y' && !config.axis_y_show) { return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1; }
2334 if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top; }
2335 return ($$.getAxisLabelPositionById(axisId).isInner ? 30 : 40) + (axisId === 'y2' ? -10 : 0);
2336 };
2337
2338 c3_chart_internal_fn.getEventRectWidth = function () {
2339 var $$ = this;
2340 var target = $$.getMaxDataCountTarget($$.data.targets),
2341 firstData, lastData, base, maxDataCount, ratio, w;
2342 if (!target) {
2343 return 0;
2344 }
2345 firstData = target.values[0], lastData = target.values[target.values.length - 1];
2346 base = $$.x(lastData.x) - $$.x(firstData.x);
2347 if (base === 0) {
2348 return $$.config.axis_rotated ? $$.height : $$.width;
2349 }
2350 maxDataCount = $$.getMaxDataCount();
2351 ratio = ($$.hasType('bar') ? (maxDataCount - ($$.isCategorized() ? 0.25 : 1)) / maxDataCount : 1);
2352 w = maxDataCount > 1 ? (base * ratio) / (maxDataCount - 1) : base;
2353 return w < 1 ? 1 : w;
2354 };
2355
2356 c3_chart_internal_fn.getShapeIndices = function (typeFilter) {
2357 var $$ = this, config = $$.config,
2358 indices = {}, i = 0, j, k;
2359 $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach(function (d) {
2360 for (j = 0; j < config.data_groups.length; j++) {
2361 if (config.data_groups[j].indexOf(d.id) < 0) { continue; }
2362 for (k = 0; k < config.data_groups[j].length; k++) {
2363 if (config.data_groups[j][k] in indices) {
2364 indices[d.id] = indices[config.data_groups[j][k]];
2365 break;
2366 }
2367 }
2368 }
2369 if (isUndefined(indices[d.id])) { indices[d.id] = i++; }
2370 });
2371 indices.__max__ = i - 1;
2372 return indices;
2373 };
2374 c3_chart_internal_fn.getShapeX = function (offset, targetsNum, indices, isSub) {
2375 var $$ = this, scale = isSub ? $$.subX : $$.x;
2376 return function (d) {
2377 var index = d.id in indices ? indices[d.id] : 0;
2378 return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0;
2379 };
2380 };
2381 c3_chart_internal_fn.getShapeY = function (isSub) {
2382 var $$ = this;
2383 return function (d) {
2384 var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id);
2385 return scale(d.value);
2386 };
2387 };
2388 c3_chart_internal_fn.getShapeOffset = function (typeFilter, indices, isSub) {
2389 var $$ = this,
2390 targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))),
2391 targetIds = targets.map(function (t) { return t.id; });
2392 return function (d, i) {
2393 var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id),
2394 y0 = scale(0), offset = y0;
2395 targets.forEach(function (t) {
2396 if (t.id === d.id || indices[t.id] !== indices[d.id]) { return; }
2397 if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id) && t.values[i].value * d.value >= 0) {
2398 offset += scale(t.values[i].value) - y0;
2399 }
2400 });
2401 return offset;
2402 };
2403 };
2404
2405 c3_chart_internal_fn.getInterpolate = function (d) {
2406 var $$ = this;
2407 return $$.isSplineType(d) ? "cardinal" : $$.isStepType(d) ? "step-after" : "linear";
2408 };
2409
2410 c3_chart_internal_fn.initLine = function () {
2411 var $$ = this;
2412 $$.main.select('.' + CLASS.chart).append("g")
2413 .attr("class", CLASS.chartLines);
2414 };
2415 c3_chart_internal_fn.updateTargetsForLine = function (targets) {
2416 var $$ = this, config = $$.config,
2417 mainLineUpdate, mainLineEnter,
2418 classChartLine = $$.classChartLine.bind($$),
2419 classLines = $$.classLines.bind($$),
2420 classAreas = $$.classAreas.bind($$),
2421 classCircles = $$.classCircles.bind($$);
2422 mainLineUpdate = $$.main.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine)
2423 .data(targets)
2424 .attr('class', classChartLine);
2425 mainLineEnter = mainLineUpdate.enter().append('g')
2426 .attr('class', classChartLine)
2427 .style('opacity', 0)
2428 .style("pointer-events", "none");
2429 // Lines for each data
2430 mainLineEnter.append('g')
2431 .attr("class", classLines);
2432 // Areas
2433 mainLineEnter.append('g')
2434 .attr('class', classAreas);
2435 // Circles for each data point on lines
2436 mainLineEnter.append('g')
2437 .attr("class", function (d) { return $$.generateClass(CLASS.selectedCircles, d.id); });
2438 mainLineEnter.append('g')
2439 .attr("class", classCircles)
2440 .style("cursor", function (d) { return config.data_selection_isselectable(d) ? "pointer" : null; });
2441 // Update date for selected circles
2442 targets.forEach(function (t) {
2443 $$.main.selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)).selectAll('.' + CLASS.selectedCircle).each(function (d) {
2444 d.value = t.values[d.index].value;
2445 });
2446 });
2447 // MEMO: can not keep same color...
2448 //mainLineUpdate.exit().remove();
2449 };
2450 c3_chart_internal_fn.redrawLine = function (durationForExit) {
2451 var $$ = this;
2452 $$.mainLine = $$.main.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line)
2453 .data($$.lineData.bind($$));
2454 $$.mainLine.enter().append('path')
2455 .attr('class', $$.classLine.bind($$))
2456 .style("stroke", $$.color);
2457 $$.mainLine
2458 .style("opacity", $$.initialOpacity.bind($$))
2459 .attr('transform', null);
2460 $$.mainLine.exit().transition().duration(durationForExit)
2461 .style('opacity', 0)
2462 .remove();
2463 };
2464 c3_chart_internal_fn.addTransitionForLine = function (transitions, drawLine) {
2465 var $$ = this;
2466 transitions.push($$.mainLine.transition()
2467 .attr("d", drawLine)
2468 .style("stroke", $$.color)
2469 .style("opacity", 1));
2470 };
2471 c3_chart_internal_fn.generateDrawLine = function (lineIndices, isSub) {
2472 var $$ = this, config = $$.config,
2473 line = $$.d3.svg.line(),
2474 getPoint = $$.generateGetLinePoint(lineIndices, isSub),
2475 yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale,
2476 xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); },
2477 yValue = function (d, i) {
2478 return config.data_groups.length > 0 ? getPoint(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value);
2479 };
2480
2481 line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue);
2482 if (!config.line_connect_null) { line = line.defined(function (d) { return d.value != null; }); }
2483 return function (d) {
2484 var data = config.line_connect_null ? $$.filterRemoveNull(d.values) : d.values,
2485 x = isSub ? $$.x : $$.subX, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path;
2486 if ($$.isLineType(d)) {
2487 if (config.data_regions[d.id]) {
2488 path = $$.lineWithRegions(data, x, y, config.data_regions[d.id]);
2489 } else {
2490 path = line.interpolate($$.getInterpolate(d))(data);
2491 }
2492 } else {
2493 if (data[0]) {
2494 x0 = x(data[0].x);
2495 y0 = y(data[0].value);
2496 }
2497 path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
2498 }
2499 return path ? path : "M 0 0";
2500 };
2501 };
2502 c3_chart_internal_fn.generateGetLinePoint = function (lineIndices, isSub) { // partial duplication of generateGetBarPoints
2503 var $$ = this, config = $$.config,
2504 lineTargetsNum = lineIndices.__max__ + 1,
2505 x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub),
2506 y = $$.getShapeY(!!isSub),
2507 lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub),
2508 yScale = isSub ? $$.getSubYScale : $$.getYScale;
2509 return function (d, i) {
2510 var y0 = yScale.call($$, d.id)(0),
2511 offset = lineOffset(d, i) || y0, // offset is for stacked area chart
2512 posX = x(d), posY = y(d);
2513 // fix posY not to overflow opposite quadrant
2514 if (config.axis_rotated) {
2515 if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; }
2516 }
2517 // 1 point that marks the line position
2518 return [
2519 [posX, posY - (y0 - offset)]
2520 ];
2521 };
2522 };
2523
2524
2525 c3_chart_internal_fn.lineWithRegions = function (d, x, y, _regions) {
2526 var $$ = this, config = $$.config,
2527 prev = -1, i, j,
2528 s = "M", sWithRegion,
2529 xp, yp, dx, dy, dd, diff, diffx2,
2530 xValue, yValue,
2531 regions = [];
2532
2533 function isWithinRegions(x, regions) {
2534 var i;
2535 for (i = 0; i < regions.length; i++) {
2536 if (regions[i].start < x && x <= regions[i].end) { return true; }
2537 }
2538 return false;
2539 }
2540
2541 // Check start/end of regions
2542 if (isDefined(_regions)) {
2543 for (i = 0; i < _regions.length; i++) {
2544 regions[i] = {};
2545 if (isUndefined(_regions[i].start)) {
2546 regions[i].start = d[0].x;
2547 } else {
2548 regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start;
2549 }
2550 if (isUndefined(_regions[i].end)) {
2551 regions[i].end = d[d.length - 1].x;
2552 } else {
2553 regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end;
2554 }
2555 }
2556 }
2557
2558 // Set scales
2559 xValue = config.axis_rotated ? function (d) { return y(d.value); } : function (d) { return x(d.x); };
2560 yValue = config.axis_rotated ? function (d) { return x(d.x); } : function (d) { return y(d.value); };
2561
2562 // Define svg generator function for region
2563 if ($$.isTimeSeries()) {
2564 sWithRegion = function (d0, d1, j, diff) {
2565 var x0 = d0.x.getTime(), x_diff = d1.x - d0.x,
2566 xv0 = new Date(x0 + x_diff * j),
2567 xv1 = new Date(x0 + x_diff * (j + diff));
2568 return "M" + x(xv0) + " " + y(yp(j)) + " " + x(xv1) + " " + y(yp(j + diff));
2569 };
2570 } else {
2571 sWithRegion = function (d0, d1, j, diff) {
2572 return "M" + x(xp(j), true) + " " + y(yp(j)) + " " + x(xp(j + diff), true) + " " + y(yp(j + diff));
2573 };
2574 }
2575
2576 // Generate
2577 for (i = 0; i < d.length; i++) {
2578
2579 // Draw as normal
2580 if (isUndefined(regions) || ! isWithinRegions(d[i].x, regions)) {
2581 s += " " + xValue(d[i]) + " " + yValue(d[i]);
2582 }
2583 // Draw with region // TODO: Fix for horizotal charts
2584 else {
2585 xp = $$.getScale(d[i - 1].x, d[i].x, $$.isTimeSeries());
2586 yp = $$.getScale(d[i - 1].value, d[i].value);
2587
2588 dx = x(d[i].x) - x(d[i - 1].x);
2589 dy = y(d[i].value) - y(d[i - 1].value);
2590 dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
2591 diff = 2 / dd;
2592 diffx2 = diff * 2;
2593
2594 for (j = diff; j <= 1; j += diffx2) {
2595 s += sWithRegion(d[i - 1], d[i], j, diff);
2596 }
2597 }
2598 prev = d[i].x;
2599 }
2600
2601 return s;
2602 };
2603
2604
2605 c3_chart_internal_fn.redrawArea = function (durationForExit) {
2606 var $$ = this, d3 = $$.d3;
2607 $$.mainArea = $$.main.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area)
2608 .data($$.lineData.bind($$));
2609 $$.mainArea.enter().append('path')
2610 .attr("class", $$.classArea.bind($$))
2611 .style("fill", $$.color)
2612 .style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; });
2613 $$.mainArea
2614 .style("opacity", $$.orgAreaOpacity);
2615 $$.mainArea.exit().transition().duration(durationForExit)
2616 .style('opacity', 0)
2617 .remove();
2618 };
2619 c3_chart_internal_fn.addTransitionForArea = function (transitions, drawArea) {
2620 var $$ = this;
2621 transitions.push($$.mainArea.transition()
2622 .attr("d", drawArea)
2623 .style("fill", $$.color)
2624 .style("opacity", $$.orgAreaOpacity));
2625 };
2626 c3_chart_internal_fn.generateDrawArea = function (areaIndices, isSub) {
2627 var $$ = this, config = $$.config, area = $$.d3.svg.area(),
2628 getPoint = $$.generateGetAreaPoint(areaIndices, isSub),
2629 yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale,
2630 xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); },
2631 value0 = function (d, i) {
2632 return config.data_groups.length > 0 ? getPoint(d, i)[0][1] : yScaleGetter.call($$, d.id)(0);
2633 },
2634 value1 = function (d, i) {
2635 return config.data_groups.length > 0 ? getPoint(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value);
2636 };
2637
2638 area = config.axis_rotated ? area.x0(value0).x1(value1).y(xValue) : area.x(xValue).y0(value0).y1(value1);
2639 if (!config.line_connect_null) {
2640 area = area.defined(function (d) { return d.value !== null; });
2641 }
2642
2643 return function (d) {
2644 var data = config.line_connect_null ? $$.filterRemoveNull(d.values) : d.values, x0 = 0, y0 = 0, path;
2645 if ($$.isAreaType(d)) {
2646 path = area.interpolate($$.getInterpolate(d))(data);
2647 } else {
2648 if (data[0]) {
2649 x0 = $$.x(data[0].x);
2650 y0 = $$.getYScale(d.id)(data[0].value);
2651 }
2652 path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
2653 }
2654 return path ? path : "M 0 0";
2655 };
2656 };
2657
2658 c3_chart_internal_fn.generateGetAreaPoint = function (areaIndices, isSub) { // partial duplication of generateGetBarPoints
2659 var $$ = this, config = $$.config,
2660 areaTargetsNum = areaIndices.__max__ + 1,
2661 x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub),
2662 y = $$.getShapeY(!!isSub),
2663 areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub),
2664 yScale = isSub ? $$.getSubYScale : $$.getYScale;
2665 return function (d, i) {
2666 var y0 = yScale.call($$, d.id)(0),
2667 offset = areaOffset(d, i) || y0, // offset is for stacked area chart
2668 posX = x(d), posY = y(d);
2669 // fix posY not to overflow opposite quadrant
2670 if (config.axis_rotated) {
2671 if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; }
2672 }
2673 // 1 point that marks the area position
2674 return [
2675 [posX, offset],
2676 [posX, posY - (y0 - offset)]
2677 ];
2678 };
2679 };
2680
2681
2682 c3_chart_internal_fn.redrawCircle = function () {
2683 var $$ = this;
2684 $$.mainCircle = $$.main.selectAll('.' + CLASS.circles).selectAll('.' + CLASS.circle)
2685 .data($$.lineOrScatterData.bind($$));
2686 $$.mainCircle.enter().append("circle")
2687 .attr("class", $$.classCircle.bind($$))
2688 .attr("r", $$.pointR.bind($$))
2689 .style("fill", $$.color);
2690 $$.mainCircle
2691 .style("opacity", $$.initialOpacity.bind($$));
2692 $$.mainCircle.exit().remove();
2693 };
2694 c3_chart_internal_fn.addTransitionForCircle = function (transitions, cx, cy) {
2695 var $$ = this;
2696 transitions.push($$.mainCircle.transition()
2697 .style('opacity', $$.opacityForCircle.bind($$))
2698 .style("fill", $$.color)
2699 .attr("cx", cx)
2700 .attr("cy", cy));
2701 transitions.push($$.main.selectAll('.' + CLASS.selectedCircle).transition()
2702 .attr("cx", cx)
2703 .attr("cy", cy));
2704 };
2705 c3_chart_internal_fn.circleX = function (d) {
2706 return d.x || d.x === 0 ? this.x(d.x) : null;
2707 };
2708 c3_chart_internal_fn.circleY = function (d, i) {
2709 var $$ = this,
2710 lineIndices = $$.getShapeIndices($$.isLineType), getPoint = $$.generateGetLinePoint(lineIndices);
2711 return $$.config.data_groups.length > 0 ? getPoint(d, i)[0][1] : $$.getYScale(d.id)(d.value);
2712 };
2713 c3_chart_internal_fn.getCircles = function (i, id) {
2714 var $$ = this;
2715 return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : ''));
2716 };
2717 c3_chart_internal_fn.expandCircles = function (i, id) {
2718 var $$ = this,
2719 r = $$.pointExpandedR.bind($$);
2720 $$.getCircles(i, id)
2721 .classed(CLASS.EXPANDED, true)
2722 .attr('r', r);
2723 };
2724 c3_chart_internal_fn.unexpandCircles = function (i) {
2725 var $$ = this,
2726 r = $$.pointR.bind($$);
2727 $$.getCircles(i)
2728 .filter(function () { return $$.d3.select(this).classed(CLASS.EXPANDED); })
2729 .classed(CLASS.EXPANDED, false)
2730 .attr('r', r);
2731 };
2732 c3_chart_internal_fn.pointR = function (d) {
2733 var $$ = this, config = $$.config;
2734 return config.point_show && !$$.isStepType(d) ? (isFunction(config.point_r) ? config.point_r(d) : config.point_r) : 0;
2735 };
2736 c3_chart_internal_fn.pointExpandedR = function (d) {
2737 var $$ = this, config = $$.config;
2738 return config.point_focus_expand_enabled ? (config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75) : $$.pointR(d);
2739 };
2740 c3_chart_internal_fn.pointSelectR = function (d) {
2741 var $$ = this, config = $$.config;
2742 return config.point_select_r ? config.point_select_r : $$.pointR(d) * 4;
2743 };
2744 c3_chart_internal_fn.isWithinCircle = function (_this, _r) {
2745 var d3 = this.d3,
2746 mouse = d3.mouse(_this), d3_this = d3.select(_this),
2747 cx = d3_this.attr("cx") * 1, cy = d3_this.attr("cy") * 1;
2748 return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < _r;
2749 };
2750
2751 c3_chart_internal_fn.initBar = function () {
2752 var $$ = this;
2753 $$.main.select('.' + CLASS.chart).append("g")
2754 .attr("class", CLASS.chartBars);
2755 };
2756 c3_chart_internal_fn.updateTargetsForBar = function (targets) {
2757 var $$ = this, config = $$.config,
2758 mainBarUpdate, mainBarEnter,
2759 classChartBar = $$.classChartBar.bind($$),
2760 classBars = $$.classBars.bind($$);
2761 mainBarUpdate = $$.main.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar)
2762 .data(targets)
2763 .attr('class', classChartBar);
2764 mainBarEnter = mainBarUpdate.enter().append('g')
2765 .attr('class', classChartBar)
2766 .style('opacity', 0)
2767 .style("pointer-events", "none");
2768 // Bars for each data
2769 mainBarEnter.append('g')
2770 .attr("class", classBars)
2771 .style("cursor", function (d) { return config.data_selection_isselectable(d) ? "pointer" : null; });
2772
2773 };
2774 c3_chart_internal_fn.redrawBar = function (durationForExit) {
2775 var $$ = this,
2776 barData = $$.barData.bind($$),
2777 classBar = $$.classBar.bind($$),
2778 initialOpacity = $$.initialOpacity.bind($$),
2779 color = function (d) { return $$.color(d.id); };
2780 $$.mainBar = $$.main.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar)
2781 .data(barData);
2782 $$.mainBar.enter().append('path')
2783 .attr("class", classBar)
2784 .style("stroke", color)
2785 .style("fill", color);
2786 $$.mainBar
2787 .style("opacity", initialOpacity);
2788 $$.mainBar.exit().transition().duration(durationForExit)
2789 .style('opacity', 0)
2790 .remove();
2791 };
2792 c3_chart_internal_fn.addTransitionForBar = function (transitions, drawBar) {
2793 var $$ = this;
2794 transitions.push($$.mainBar.transition()
2795 .attr('d', drawBar)
2796 .style("fill", $$.color)
2797 .style("opacity", 1));
2798 };
2799 c3_chart_internal_fn.getBarW = function (axis, barTargetsNum) {
2800 var $$ = this, config = $$.config,
2801 w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickOffset() * 2 * config.bar_width_ratio) / barTargetsNum : 0;
2802 return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w;
2803 };
2804 c3_chart_internal_fn.getBars = function (i) {
2805 var $$ = this;
2806 return $$.main.selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : ''));
2807 };
2808 c3_chart_internal_fn.expandBars = function (i) {
2809 var $$ = this;
2810 $$.getBars(i).classed(CLASS.EXPANDED, true);
2811 };
2812 c3_chart_internal_fn.unexpandBars = function (i) {
2813 var $$ = this;
2814 $$.getBars(i).classed(CLASS.EXPANDED, false);
2815 };
2816 c3_chart_internal_fn.generateDrawBar = function (barIndices, isSub) {
2817 var $$ = this, config = $$.config,
2818 getPoints = $$.generateGetBarPoints(barIndices, isSub);
2819 return function (d, i) {
2820 // 4 points that make a bar
2821 var points = getPoints(d, i);
2822
2823 // switch points if axis is rotated, not applicable for sub chart
2824 var indexX = config.axis_rotated ? 1 : 0;
2825 var indexY = config.axis_rotated ? 0 : 1;
2826
2827 var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' +
2828 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' +
2829 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' +
2830 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' +
2831 'z';
2832
2833 return path;
2834 };
2835 };
2836 c3_chart_internal_fn.generateGetBarPoints = function (barIndices, isSub) {
2837 var $$ = this,
2838 barTargetsNum = barIndices.__max__ + 1,
2839 barW = $$.getBarW($$.xAxis, barTargetsNum),
2840 barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub),
2841 barY = $$.getShapeY(!!isSub),
2842 barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub),
2843 yScale = isSub ? $$.getSubYScale : $$.getYScale;
2844 return function (d, i) {
2845 var y0 = yScale.call($$, d.id)(0),
2846 offset = barOffset(d, i) || y0, // offset is for stacked bar chart
2847 posX = barX(d), posY = barY(d);
2848 // fix posY not to overflow opposite quadrant
2849 if ($$.config.axis_rotated) {
2850 if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; }
2851 }
2852 // 4 points that make a bar
2853 return [
2854 [posX, offset],
2855 [posX, posY - (y0 - offset)],
2856 [posX + barW, posY - (y0 - offset)],
2857 [posX + barW, offset]
2858 ];
2859 };
2860 };
2861 c3_chart_internal_fn.isWithinBar = function (_this) {
2862 var d3 = this.d3,
2863 mouse = d3.mouse(_this), box = _this.getBoundingClientRect(),
2864 seg0 = _this.pathSegList.getItem(0), seg1 = _this.pathSegList.getItem(1),
2865 x = seg0.x, y = Math.min(seg0.y, seg1.y), w = box.width, h = box.height, offset = 2,
2866 sx = x - offset, ex = x + w + offset, sy = y + h + offset, ey = y - offset;
2867 return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
2868 };
2869
2870 c3_chart_internal_fn.initText = function () {
2871 var $$ = this;
2872 $$.main.select('.' + CLASS.chart).append("g")
2873 .attr("class", CLASS.chartTexts);
2874 $$.mainText = $$.d3.selectAll([]);
2875 };
2876 c3_chart_internal_fn.updateTargetsForText = function (targets) {
2877 var $$ = this, mainTextUpdate, mainTextEnter,
2878 classChartText = $$.classChartText.bind($$),
2879 classTexts = $$.classTexts.bind($$);
2880 mainTextUpdate = $$.main.select('.' + CLASS.chartTexts).selectAll('.' + CLASS.chartText)
2881 .data(targets)
2882 .attr('class', classChartText);
2883 mainTextEnter = mainTextUpdate.enter().append('g')
2884 .attr('class', classChartText)
2885 .style('opacity', 0)
2886 .style("pointer-events", "none");
2887 mainTextEnter.append('g')
2888 .attr('class', classTexts);
2889 };
2890 c3_chart_internal_fn.redrawText = function (durationForExit) {
2891 var $$ = this, config = $$.config,
2892 barOrLineData = $$.barOrLineData.bind($$),
2893 classText = $$.classText.bind($$);
2894 $$.mainText = $$.main.selectAll('.' + CLASS.texts).selectAll('.' + CLASS.text)
2895 .data(barOrLineData);
2896 $$.mainText.enter().append('text')
2897 .attr("class", classText)
2898 .attr('text-anchor', function (d) { return config.axis_rotated ? (d.value < 0 ? 'end' : 'start') : 'middle'; })
2899 .style("stroke", 'none')
2900 .style("fill", function (d) { return $$.color(d); })
2901 .style("fill-opacity", 0);
2902 $$.mainText
2903 .text(function (d) { return $$.formatByAxisId($$.getAxisId(d.id))(d.value, d.id); });
2904 $$.mainText.exit()
2905 .transition().duration(durationForExit)
2906 .style('fill-opacity', 0)
2907 .remove();
2908 };
2909 c3_chart_internal_fn.addTransitionForText = function (transitions, xForText, yForText, forFlow) {
2910 var $$ = this,
2911 opacityForText = forFlow ? 0 : $$.opacityForText.bind($$);
2912 transitions.push($$.mainText.transition()
2913 .attr('x', xForText)
2914 .attr('y', yForText)
2915 .style("fill", $$.color)
2916 .style("fill-opacity", opacityForText));
2917 };
2918 c3_chart_internal_fn.getTextRect = function (text, cls) {
2919 var rect;
2920 this.d3.select('body').selectAll('.dummy')
2921 .data([text])
2922 .enter().append('text')
2923 .classed(cls ? cls : "", true)
2924 .text(text)
2925 .each(function () { rect = this.getBoundingClientRect(); })
2926 .remove();
2927 return rect;
2928 };
2929 c3_chart_internal_fn.generateXYForText = function (barIndices, forX) {
2930 var $$ = this,
2931 getPoints = $$.generateGetBarPoints(barIndices, false),
2932 getter = forX ? $$.getXForText : $$.getYForText;
2933 return function (d, i) {
2934 return getter.call($$, getPoints(d, i), d, this);
2935 };
2936 };
2937 c3_chart_internal_fn.getXForText = function (points, d, textElement) {
2938 var $$ = this,
2939 box = textElement.getBoundingClientRect(), xPos, padding;
2940 if ($$.config.axis_rotated) {
2941 padding = $$.isBarType(d) ? 4 : 6;
2942 xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1);
2943 } else {
2944 xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0];
2945 }
2946 return xPos > $$.width ? $$.width - box.width : xPos;
2947 };
2948 c3_chart_internal_fn.getYForText = function (points, d, textElement) {
2949 var $$ = this,
2950 box = textElement.getBoundingClientRect(), yPos;
2951 if ($$.config.axis_rotated) {
2952 yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2;
2953 } else {
2954 yPos = points[2][1] + (d.value < 0 ? box.height : $$.isBarType(d) ? -3 : -6);
2955 }
2956 return yPos < box.height ? box.height : yPos;
2957 };
2958
2959 c3_chart_internal_fn.setTargetType = function (targetIds, type) {
2960 var $$ = this, config = $$.config;
2961 $$.mapToTargetIds(targetIds).forEach(function (id) {
2962 $$.withoutFadeIn[id] = (type === config.data_types[id]);
2963 config.data_types[id] = type;
2964 });
2965 if (!targetIds) {
2966 config.data_type = type;
2967 }
2968 };
2969 c3_chart_internal_fn.hasType = function (type, targets) {
2970 var $$ = this, types = $$.config.data_types, has = false;
2971 (targets || $$.data.targets).forEach(function (t) {
2972 if ((types[t.id] && types[t.id].indexOf(type) >= 0) || (!(t.id in types) && type === 'line')) {
2973 has = true;
2974 }
2975 });
2976 return has;
2977 };
2978 c3_chart_internal_fn.hasArcType = function (targets) {
2979 return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
2980 };
2981 c3_chart_internal_fn.isLineType = function (d) {
2982 var config = this.config, id = isString(d) ? d : d.id;
2983 return !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0;
2984 };
2985 c3_chart_internal_fn.isStepType = function (d) {
2986 var id = isString(d) ? d : d.id;
2987 return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0;
2988 };
2989 c3_chart_internal_fn.isSplineType = function (d) {
2990 var id = isString(d) ? d : d.id;
2991 return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0;
2992 };
2993 c3_chart_internal_fn.isAreaType = function (d) {
2994 var id = isString(d) ? d : d.id;
2995 return ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0;
2996 };
2997 c3_chart_internal_fn.isBarType = function (d) {
2998 var id = isString(d) ? d : d.id;
2999 return this.config.data_types[id] === 'bar';
3000 };
3001 c3_chart_internal_fn.isScatterType = function (d) {
3002 var id = isString(d) ? d : d.id;
3003 return this.config.data_types[id] === 'scatter';
3004 };
3005 c3_chart_internal_fn.isPieType = function (d) {
3006 var id = isString(d) ? d : d.id;
3007 return this.config.data_types[id] === 'pie';
3008 };
3009 c3_chart_internal_fn.isGaugeType = function (d) {
3010 var id = isString(d) ? d : d.id;
3011 return this.config.data_types[id] === 'gauge';
3012 };
3013 c3_chart_internal_fn.isDonutType = function (d) {
3014 var id = isString(d) ? d : d.id;
3015 return this.config.data_types[id] === 'donut';
3016 };
3017 c3_chart_internal_fn.isArcType = function (d) {
3018 return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
3019 };
3020 c3_chart_internal_fn.lineData = function (d) {
3021 return this.isLineType(d) ? [d] : [];
3022 };
3023 c3_chart_internal_fn.arcData = function (d) {
3024 return this.isArcType(d.data) ? [d] : [];
3025 };
3026 /* not used
3027 function scatterData(d) {
3028 return isScatterType(d) ? d.values : [];
3029 }
3030 */
3031 c3_chart_internal_fn.barData = function (d) {
3032 return this.isBarType(d) ? d.values : [];
3033 };
3034 c3_chart_internal_fn.lineOrScatterData = function (d) {
3035 return this.isLineType(d) || this.isScatterType(d) ? d.values : [];
3036 };
3037 c3_chart_internal_fn.barOrLineData = function (d) {
3038 return this.isBarType(d) || this.isLineType(d) ? d.values : [];
3039 };
3040
3041 c3_chart_internal_fn.initGrid = function () {
3042 var $$ = this, config = $$.config, d3 = $$.d3;
3043 $$.grid = $$.main.append('g')
3044 .attr("clip-path", $$.clipPath)
3045 .attr('class', CLASS.grid);
3046 if (config.grid_x_show) {
3047 $$.grid.append("g").attr("class", CLASS.xgrids);
3048 }
3049 if (config.grid_y_show) {
3050 $$.grid.append('g').attr('class', CLASS.ygrids);
3051 }
3052 $$.grid.append('g').attr("class", CLASS.xgridLines);
3053 $$.grid.append('g').attr('class', CLASS.ygridLines);
3054 if (config.grid_focus_show) {
3055 $$.grid.append('g')
3056 .attr("class", CLASS.xgridFocus)
3057 .append('line')
3058 .attr('class', CLASS.xgridFocus);
3059 }
3060 $$.xgrid = d3.selectAll([]);
3061 $$.xgridLines = d3.selectAll([]);
3062 };
3063
3064 c3_chart_internal_fn.updateXGrid = function (withoutUpdate) {
3065 var $$ = this, config = $$.config, d3 = $$.d3,
3066 xgridData = $$.generateGridData(config.grid_x_type, $$.x),
3067 tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0;
3068
3069 $$.xgridAttr = config.axis_rotated ? {
3070 'x1': 0,
3071 'x2': $$.width,
3072 'y1': function (d) { return $$.x(d) - tickOffset; },
3073 'y2': function (d) { return $$.x(d) - tickOffset; }
3074 } : {
3075 'x1': function (d) { return $$.x(d) + tickOffset; },
3076 'x2': function (d) { return $$.x(d) + tickOffset; },
3077 'y1': 0,
3078 'y2': $$.height
3079 };
3080
3081 $$.xgrid = $$.main.select('.' + CLASS.xgrids).selectAll('.' + CLASS.xgrid)
3082 .data(xgridData);
3083 $$.xgrid.enter().append('line').attr("class", CLASS.xgrid);
3084 if (!withoutUpdate) {
3085 $$.xgrid.attr($$.xgridAttr)
3086 .style("opacity", function () { return +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1') === (config.axis_rotated ? $$.height : 0) ? 0 : 1; });
3087 }
3088 $$.xgrid.exit().remove();
3089 };
3090
3091 c3_chart_internal_fn.updateYGrid = function () {
3092 var $$ = this, config = $$.config;
3093 $$.ygrid = $$.main.select('.' + CLASS.ygrids).selectAll('.' + CLASS.ygrid)
3094 .data($$.y.ticks(config.grid_y_ticks));
3095 $$.ygrid.enter().append('line')
3096 .attr('class', CLASS.ygrid);
3097 $$.ygrid.attr("x1", config.axis_rotated ? $$.y : 0)
3098 .attr("x2", config.axis_rotated ? $$.y : $$.width)
3099 .attr("y1", config.axis_rotated ? 0 : $$.y)
3100 .attr("y2", config.axis_rotated ? $$.height : $$.y);
3101 $$.ygrid.exit().remove();
3102 $$.smoothLines($$.ygrid, 'grid');
3103 };
3104
3105
3106 c3_chart_internal_fn.redrawGrid = function (duration, withY) {
3107 var $$ = this, main = $$.main, config = $$.config,
3108 xgridLine, ygridLine, yv;
3109 main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
3110 if (config.grid_x_show) {
3111 $$.updateXGrid();
3112 }
3113 $$.xgridLines = main.select('.' + CLASS.xgridLines).selectAll('.' + CLASS.xgridLine)
3114 .data(config.grid_x_lines);
3115 // enter
3116 xgridLine = $$.xgridLines.enter().append('g')
3117 .attr("class", function (d) { return CLASS.xgridLine + (d.class ? ' ' + d.class : ''); });
3118 xgridLine.append('line')
3119 .style("opacity", 0);
3120 xgridLine.append('text')
3121 .attr("text-anchor", "end")
3122 .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
3123 .attr('dx', config.axis_rotated ? 0 : -$$.margin.top)
3124 .attr('dy', -5)
3125 .style("opacity", 0);
3126 // udpate
3127 // done in d3.transition() of the end of this function
3128 // exit
3129 $$.xgridLines.exit().transition().duration(duration)
3130 .style("opacity", 0)
3131 .remove();
3132
3133 // Y-Grid
3134 if (withY && config.grid_y_show) {
3135 $$.updateYGrid();
3136 }
3137 if (withY) {
3138 $$.ygridLines = main.select('.' + CLASS.ygridLines).selectAll('.' + CLASS.ygridLine)
3139 .data(config.grid_y_lines);
3140 // enter
3141 ygridLine = $$.ygridLines.enter().append('g')
3142 .attr("class", function (d) { return CLASS.ygridLine + (d.class ? ' ' + d.class : ''); });
3143 ygridLine.append('line')
3144 .style("opacity", 0);
3145 ygridLine.append('text')
3146 .attr("text-anchor", "end")
3147 .attr("transform", config.axis_rotated ? "rotate(-90)" : "")
3148 .attr('dx', config.axis_rotated ? 0 : -$$.margin.top)
3149 .attr('dy', -5)
3150 .style("opacity", 0);
3151 // update
3152 yv = $$.yv.bind($$);
3153 $$.ygridLines.select('line')
3154 .transition().duration(duration)
3155 .attr("x1", config.axis_rotated ? yv : 0)
3156 .attr("x2", config.axis_rotated ? yv : $$.width)
3157 .attr("y1", config.axis_rotated ? 0 : yv)
3158 .attr("y2", config.axis_rotated ? $$.height : yv)
3159 .style("opacity", 1);
3160 $$.ygridLines.select('text')
3161 .transition().duration(duration)
3162 .attr("x", config.axis_rotated ? 0 : $$.width)
3163 .attr("y", yv)
3164 .text(function (d) { return d.text; })
3165 .style("opacity", 1);
3166 // exit
3167 $$.ygridLines.exit().transition().duration(duration)
3168 .style("opacity", 0)
3169 .remove();
3170 }
3171 };
3172 c3_chart_internal_fn.addTransitionForGrid = function (transitions) {
3173 var $$ = this, config = $$.config, xv = $$.xv.bind($$);
3174 transitions.push($$.xgridLines.select('line').transition()
3175 .attr("x1", config.axis_rotated ? 0 : xv)
3176 .attr("x2", config.axis_rotated ? $$.width : xv)
3177 .attr("y1", config.axis_rotated ? xv : $$.margin.top)
3178 .attr("y2", config.axis_rotated ? xv : $$.height)
3179 .style("opacity", 1));
3180 transitions.push($$.xgridLines.select('text').transition()
3181 .attr("x", config.axis_rotated ? $$.width : 0)
3182 .attr("y", xv)
3183 .text(function (d) { return d.text; })
3184 .style("opacity", 1));
3185 };
3186 c3_chart_internal_fn.showXGridFocus = function (selectedData) {
3187 var $$ = this, config = $$.config,
3188 dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }),
3189 focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus),
3190 xx = $$.xx.bind($$);
3191 if (! config.tooltip_show) { return; }
3192 // Hide when scatter plot exists
3193 if ($$.hasType('scatter') || $$.hasArcType()) { return; }
3194 focusEl
3195 .style("visibility", "visible")
3196 .data([dataToShow[0]])
3197 .attr(config.axis_rotated ? 'y1' : 'x1', xx)
3198 .attr(config.axis_rotated ? 'y2' : 'x2', xx);
3199 $$.smoothLines(focusEl, 'grid');
3200 };
3201 c3_chart_internal_fn.hideXGridFocus = function () {
3202 this.main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
3203 };
3204 c3_chart_internal_fn.updateXgridFocus = function () {
3205 var $$ = this, config = $$.config;
3206 $$.main.select('line.' + CLASS.xgridFocus)
3207 .attr("x1", config.axis_rotated ? 0 : -10)
3208 .attr("x2", config.axis_rotated ? $$.width : -10)
3209 .attr("y1", config.axis_rotated ? -10 : 0)
3210 .attr("y2", config.axis_rotated ? -10 : $$.height);
3211 };
3212 c3_chart_internal_fn.generateGridData = function (type, scale) {
3213 var $$ = this,
3214 gridData = [], xDomain, firstYear, lastYear, i,
3215 tickNum = $$.main.select("." + CLASS.axisX).selectAll('.tick').size();
3216 if (type === 'year') {
3217 xDomain = $$.getXDomain();
3218 firstYear = xDomain[0].getFullYear();
3219 lastYear = xDomain[1].getFullYear();
3220 for (i = firstYear; i <= lastYear; i++) {
3221 gridData.push(new Date(i + '-01-01 00:00:00'));
3222 }
3223 } else {
3224 gridData = scale.ticks(10);
3225 if (gridData.length > tickNum) { // use only int
3226 gridData = gridData.filter(function (d) { return ("" + d).indexOf('.') < 0; });
3227 }
3228 }
3229 return gridData;
3230 };
3231 c3_chart_internal_fn.getGridFilterToRemove = function (params) {
3232 return params ? function (line) {
3233 var found = false;
3234 [].concat(params).forEach(function (param) {
3235 if ((('value' in param && line.value === params.value) || ('class' in param && line.class === params.class))) {
3236 found = true;
3237 }
3238 });
3239 return found;
3240 } : function () { return true; };
3241 };
3242 c3_chart_internal_fn.removeGridLines = function (params, forX) {
3243 var $$ = this, config = $$.config,
3244 toRemove = $$.getGridFilterToRemove(params),
3245 toShow = function (line) { return !toRemove(line); },
3246 classLines = forX ? CLASS.xgridLines : CLASS.ygridLines,
3247 classLine = forX ? CLASS.xgridLine : CLASS.ygridLine;
3248 $$.main.select('.' + classLines).selectAll('.' + classLine).filter(toRemove)
3249 .transition().duration(config.transition_duration)
3250 .style('opacity', 0).remove();
3251 if (forX) {
3252 config.grid_x_lines = config.grid_x_lines.filter(toShow);
3253 } else {
3254 config.grid_y_lines = config.grid_y_lines.filter(toShow);
3255 }
3256 };
3257
3258 c3_chart_internal_fn.initTooltip = function () {
3259 var $$ = this, config = $$.config, i;
3260 $$.tooltip = $$.selectChart
3261 .style("position", "relative")
3262 .append("div")
3263 .style("position", "absolute")
3264 .style("pointer-events", "none")
3265 .style("z-index", "10")
3266 .style("display", "none");
3267 // Show tooltip if needed
3268 if (config.tooltip_init_show) {
3269 if ($$.isTimeSeries() && isString(config.tooltip_init_x)) {
3270 config.tooltip_init_x = $$.parseDate(config.tooltip_init_x);
3271 for (i = 0; i < $$.data.targets[0].values.length; i++) {
3272 if (($$.data.targets[0].values[i].x - config.tooltip_init_x) === 0) { break; }
3273 }
3274 config.tooltip_init_x = i;
3275 }
3276 $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) {
3277 return $$.addName(d.values[config.tooltip_init_x]);
3278 }), $$.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color));
3279 $$.tooltip.style("top", config.tooltip_init_position.top)
3280 .style("left", config.tooltip_init_position.left)
3281 .style("display", "block");
3282 }
3283 };
3284 c3_chart_internal_fn.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) {
3285 var $$ = this, config = $$.config,
3286 titleFormat = config.tooltip_format_title || defaultTitleFormat,
3287 nameFormat = config.tooltip_format_name || function (name) { return name; },
3288 valueFormat = config.tooltip_format_value || defaultValueFormat,
3289 text, i, title, value, name, bgcolor;
3290 for (i = 0; i < d.length; i++) {
3291 if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
3292
3293 if (! text) {
3294 title = titleFormat ? titleFormat(d[i].x) : d[i].x;
3295 text = "<table class='" + CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
3296 }
3297
3298 name = nameFormat(d[i].name);
3299 value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
3300 bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
3301
3302 text += "<tr class='" + CLASS.tooltipName + "-" + d[i].id + "'>";
3303 text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
3304 text += "<td class='value'>" + value + "</td>";
3305 text += "</tr>";
3306 }
3307 return text + "</table>";
3308 };
3309 c3_chart_internal_fn.showTooltip = function (selectedData, mouse) {
3310 var $$ = this, config = $$.config;
3311 var tWidth, tHeight, svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight;
3312 var forArc = $$.hasArcType(),
3313 dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); });
3314 if (dataToShow.length === 0 || !config.tooltip_show) {
3315 return;
3316 }
3317 $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block");
3318
3319 // Get tooltip dimensions
3320 tWidth = $$.tooltip.property('offsetWidth');
3321 tHeight = $$.tooltip.property('offsetHeight');
3322 // Determin tooltip position
3323 if (forArc) {
3324 tooltipLeft = ($$.width / 2) + mouse[0];
3325 tooltipTop = ($$.height / 2) + mouse[1] + 20;
3326 } else {
3327 if (config.axis_rotated) {
3328 svgLeft = $$.getSvgLeft();
3329 tooltipLeft = svgLeft + mouse[0] + 100;
3330 tooltipRight = tooltipLeft + tWidth;
3331 chartRight = $$.getCurrentWidth() - $$.getCurrentPaddingRight();
3332 tooltipTop = $$.x(dataToShow[0].x) + 20;
3333 } else {
3334 svgLeft = $$.getSvgLeft();
3335 tooltipLeft = svgLeft + $$.getCurrentPaddingLeft() + $$.x(dataToShow[0].x) + 20;
3336 tooltipRight = tooltipLeft + tWidth;
3337 chartRight = svgLeft + $$.getCurrentWidth() - $$.getCurrentPaddingRight();
3338 tooltipTop = mouse[1] + 15;
3339 }
3340
3341 if (tooltipRight > chartRight) {
3342 tooltipLeft -= tooltipRight - chartRight;
3343 }
3344 if (tooltipTop + tHeight > $$.getCurrentHeight() && tooltipTop > tHeight + 30) {
3345 tooltipTop -= tHeight + 30;
3346 }
3347 }
3348 // Set tooltip
3349 $$.tooltip
3350 .style("top", tooltipTop + "px")
3351 .style("left", tooltipLeft + 'px');
3352 };
3353 c3_chart_internal_fn.hideTooltip = function () {
3354 this.tooltip.style("display", "none");
3355 };
3356
3357 c3_chart_internal_fn.initLegend = function () {
3358 var $$ = this;
3359 $$.legend = $$.svg.append("g").attr("transform", $$.getTranslate('legend'));
3360 if (!$$.config.legend_show) {
3361 $$.legend.style('visibility', 'hidden');
3362 $$.hiddenLegendIds = $$.mapToIds($$.data.targets);
3363 }
3364 // MEMO: call here to update legend box and tranlate for all
3365 // MEMO: translate will be upated by this, so transform not needed in updateLegend()
3366 $$.updateLegend($$.mapToIds($$.data.targets), {withTransform: false, withTransitionForTransform: false, withTransition: false});
3367 };
3368 c3_chart_internal_fn.updateSizeForLegend = function (legendHeight, legendWidth) {
3369 var $$ = this, config = $$.config, insetLegendPosition = {
3370 top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y,
3371 left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5
3372 };
3373 $$.margin3 = {
3374 top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight,
3375 right: NaN,
3376 bottom: 0,
3377 left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0
3378 };
3379 };
3380 c3_chart_internal_fn.transformLegend = function (withTransition) {
3381 var $$ = this;
3382 (withTransition ? $$.legend.transition() : $$.legend).attr("transform", $$.getTranslate('legend'));
3383 };
3384 c3_chart_internal_fn.updateLegendStep = function (step) {
3385 this.legendStep = step;
3386 };
3387 c3_chart_internal_fn.updateLegendItemWidth = function (w) {
3388 this.legendItemWidth = w;
3389 };
3390 c3_chart_internal_fn.updateLegendItemHeight = function (h) {
3391 this.legendItemHeight = h;
3392 };
3393 c3_chart_internal_fn.getLegendWidth = function () {
3394 var $$ = this;
3395 return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0;
3396 };
3397 c3_chart_internal_fn.getLegendHeight = function () {
3398 var $$ = this, config = $$.config, h = 0;
3399 if (config.legend_show) {
3400 if ($$.isLegendRight) {
3401 h = $$.currentHeight;
3402 } else if ($$.isLegendInset) {
3403 h = config.legend_inset_step ? Math.max(20, $$.legendItemHeight) * (config.legend_inset_step + 1) : $$.height;
3404 } else {
3405 h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1);
3406 }
3407 }
3408 return h;
3409 };
3410 c3_chart_internal_fn.opacityForLegend = function (legendItem) {
3411 var $$ = this;
3412 return legendItem.classed(CLASS.legendItemHidden) ? $$.legendOpacityForHidden : 1;
3413 };
3414 c3_chart_internal_fn.opacityForUnfocusedLegend = function (legendItem) {
3415 var $$ = this;
3416 return legendItem.classed(CLASS.legendItemHidden) ? $$.legendOpacityForHidden : 0.3;
3417 };
3418 c3_chart_internal_fn.toggleFocusLegend = function (id, focus) {
3419 var $$ = this;
3420 $$.legend.selectAll('.' + CLASS.legendItem)
3421 .transition().duration(100)
3422 .style('opacity', function (_id) {
3423 var This = $$.d3.select(this);
3424 if (id && _id !== id) {
3425 return focus ? $$.opacityForUnfocusedLegend(This) : $$.opacityForLegend(This);
3426 } else {
3427 return focus ? $$.opacityForLegend(This) : $$.opacityForUnfocusedLegend(This);
3428 }
3429 });
3430 };
3431 c3_chart_internal_fn.revertLegend = function () {
3432 var $$ = this, d3 = $$.d3;
3433 $$.legend.selectAll('.' + CLASS.legendItem)
3434 .transition().duration(100)
3435 .style('opacity', function () { return $$.opacityForLegend(d3.select(this)); });
3436 };
3437 c3_chart_internal_fn.showLegend = function (targetIds) {
3438 var $$ = this, config = $$.config;
3439 if (!config.legend_show) {
3440 config.legend_show = true;
3441 $$.legend.style('visibility', 'visible');
3442 }
3443 $$.removeHiddenLegendIds(targetIds);
3444 $$.legend.selectAll($$.selectorLegends(targetIds))
3445 .style('visibility', 'visible')
3446 .transition()
3447 .style('opacity', function () { return $$.opacityForLegend($$.d3.select(this)); });
3448 };
3449 c3_chart_internal_fn.hideLegend = function (targetIds) {
3450 var $$ = this, config = $$.config;
3451 if (config.legend_show && isEmpty(targetIds)) {
3452 config.legend_show = false;
3453 $$.legend.style('visibility', 'hidden');
3454 }
3455 $$.addHiddenLegendIds(targetIds);
3456 $$.legend.selectAll($$.selectorLegends(targetIds))
3457 .style('opacity', 0)
3458 .style('visibility', 'hidden');
3459 };
3460 c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
3461 var $$ = this, config = $$.config;
3462 var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect;
3463 var paddingTop = 4, paddingRight = 36, maxWidth = 0, maxHeight = 0, posMin = 10;
3464 var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0;
3465 var withTransition, withTransitionForTransform;
3466 var hasFocused = $$.legend.selectAll('.' + CLASS.legendItemFocused).size();
3467 var texts, rects, tiles;
3468
3469 options = options || {};
3470 withTransition = getOption(options, "withTransition", true);
3471 withTransitionForTransform = getOption(options, "withTransitionForTransform", true);
3472
3473 function updatePositions(textElement, id, reset) {
3474 var box = $$.getTextRect(textElement.textContent, CLASS.legendItem),
3475 itemWidth = Math.ceil((box.width + paddingRight) / 10) * 10,
3476 itemHeight = Math.ceil((box.height + paddingTop) / 10) * 10,
3477 itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth,
3478 areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(),
3479 margin, maxLength;
3480
3481 // MEMO: care about condifion of step, totalLength
3482 function updateValues(id, withoutStep) {
3483 if (!withoutStep) {
3484 margin = (areaLength - totalLength - itemLength) / 2;
3485 if (margin < posMin) {
3486 margin = (areaLength - itemLength) / 2;
3487 totalLength = 0;
3488 step++;
3489 }
3490 }
3491 steps[id] = step;
3492 margins[step] = $$.isLegendInset ? 10 : margin;
3493 offsets[id] = totalLength;
3494 totalLength += itemLength;
3495 }
3496
3497 if (reset) {
3498 totalLength = 0;
3499 step = 0;
3500 maxWidth = 0;
3501 maxHeight = 0;
3502 }
3503
3504 if (config.legend_show && !$$.isLegendToShow(id)) {
3505 widths[id] = heights[id] = steps[id] = offsets[id] = 0;
3506 return;
3507 }
3508
3509 widths[id] = itemWidth;
3510 heights[id] = itemHeight;
3511
3512 if (!maxWidth || itemWidth >= maxWidth) { maxWidth = itemWidth; }
3513 if (!maxHeight || itemHeight >= maxHeight) { maxHeight = itemHeight; }
3514 maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth;
3515
3516 if (config.legend_equally) {
3517 Object.keys(widths).forEach(function (id) { widths[id] = maxWidth; });
3518 Object.keys(heights).forEach(function (id) { heights[id] = maxHeight; });
3519 margin = (areaLength - maxLength * targetIds.length) / 2;
3520 if (margin < posMin) {
3521 totalLength = 0;
3522 step = 0;
3523 targetIds.forEach(function (id) { updateValues(id); });
3524 }
3525 else {
3526 updateValues(id, true);
3527 }
3528 } else {
3529 updateValues(id);
3530 }
3531 }
3532
3533 if ($$.isLegendRight) {
3534 xForLegend = function (id) { return maxWidth * steps[id]; };
3535 yForLegend = function (id) { return margins[steps[id]] + offsets[id]; };
3536 } else if ($$.isLegendInset) {
3537 xForLegend = function (id) { return maxWidth * steps[id] + 10; };
3538 yForLegend = function (id) { return margins[steps[id]] + offsets[id]; };
3539 } else {
3540 xForLegend = function (id) { return margins[steps[id]] + offsets[id]; };
3541 yForLegend = function (id) { return maxHeight * steps[id]; };
3542 }
3543 xForLegendText = function (id, i) { return xForLegend(id, i) + 14; };
3544 yForLegendText = function (id, i) { return yForLegend(id, i) + 9; };
3545 xForLegendRect = function (id, i) { return xForLegend(id, i) - 4; };
3546 yForLegendRect = function (id, i) { return yForLegend(id, i) - 7; };
3547
3548 // Define g for legend area
3549 l = $$.legend.selectAll('.' + CLASS.legendItem)
3550 .data(targetIds)
3551 .enter().append('g')
3552 .attr('class', function (id) { return $$.generateClass(CLASS.legendItem, id); })
3553 .style('visibility', function (id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden'; })
3554 .style('cursor', 'pointer')
3555 .on('click', function (id) {
3556 config.legend_item_onclick ? config.legend_item_onclick.call($$, id) : $$.api.toggle(id);
3557 })
3558 .on('mouseover', function (id) {
3559 $$.d3.select(this).classed(CLASS.legendItemFocused, true);
3560 if (!$$.transiting) {
3561 $$.api.focus(id);
3562 }
3563 if (config.legend_item_onmouseover) {
3564 config.legend_item_onmouseover.call($$, id);
3565 }
3566 })
3567 .on('mouseout', function (id) {
3568 $$.d3.select(this).classed(CLASS.legendItemFocused, false);
3569 if (!$$.transiting) {
3570 $$.api.revert();
3571 }
3572 if (config.legend_item_onmouseout) {
3573 config.legend_item_onmouseout.call($$, id);
3574 }
3575 });
3576 l.append('text')
3577 .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; })
3578 .each(function (id, i) { updatePositions(this, id, i === 0); })
3579 .style("pointer-events", "none")
3580 .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200)
3581 .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText);
3582 l.append('rect')
3583 .attr("class", CLASS.legendItemEvent)
3584 .style('fill-opacity', 0)
3585 .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200)
3586 .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect);
3587 l.append('rect')
3588 .attr("class", CLASS.legendItemTile)
3589 .style("pointer-events", "none")
3590 .style('fill', $$.color)
3591 .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200)
3592 .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegend)
3593 .attr('width', 10)
3594 .attr('height', 10);
3595 // Set background for inset legend
3596 if ($$.isLegendInset && maxWidth !== 0) {
3597 $$.legend.insert('g', '.' + CLASS.legendItem)
3598 .attr("class", CLASS.legendBackground)
3599 .append('rect')
3600 .attr('height', $$.getLegendHeight() - 10)
3601 .attr('width', maxWidth * (step + 1) + 10);
3602 }
3603
3604 texts = $$.legend.selectAll('text')
3605 .data(targetIds)
3606 .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) // MEMO: needed for update
3607 .each(function (id, i) { updatePositions(this, id, i === 0); });
3608 (withTransition ? texts.transition() : texts)
3609 .attr('x', xForLegendText)
3610 .attr('y', yForLegendText);
3611
3612 rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent)
3613 .data(targetIds);
3614 (withTransition ? rects.transition() : rects)
3615 .attr('width', function (id) { return widths[id]; })
3616 .attr('height', function (id) { return heights[id]; })
3617 .attr('x', xForLegendRect)
3618 .attr('y', yForLegendRect);
3619
3620 tiles = $$.legend.selectAll('rect.' + CLASS.legendItemTile)
3621 .data(targetIds);
3622 (withTransition ? tiles.transition() : tiles)
3623 .style('fill', $$.color)
3624 .attr('x', xForLegend)
3625 .attr('y', yForLegend);
3626
3627 // toggle legend state
3628 $$.legend.selectAll('.' + CLASS.legendItem)
3629 .classed(CLASS.legendItemHidden, function (id) { return !$$.isTargetToShow(id); })
3630 .transition()
3631 .style('opacity', function (id) {
3632 var This = $$.d3.select(this);
3633 if ($$.isTargetToShow(id)) {
3634 return !hasFocused || This.classed(CLASS.legendItemFocused) ? $$.opacityForLegend(This) : $$.opacityForUnfocusedLegend(This);
3635 } else {
3636 return $$.legendOpacityForHidden;
3637 }
3638 });
3639
3640 // Update all to reflect change of legend
3641 $$.updateLegendItemWidth(maxWidth);
3642 $$.updateLegendItemHeight(maxHeight);
3643 $$.updateLegendStep(step);
3644 // Update size and scale
3645 $$.updateSizes();
3646 $$.updateScales();
3647 $$.updateSvgSize();
3648 // Update g positions
3649 $$.transformAll(withTransitionForTransform, transitions);
3650 };
3651
3652 c3_chart_internal_fn.initAxis = function () {
3653 var $$ = this, config = $$.config, main = $$.main;
3654 $$.axes.x = main.append("g")
3655 .attr("class", CLASS.axis + ' ' + CLASS.axisX)
3656 .attr("clip-path", $$.clipPathForXAxis)
3657 .attr("transform", $$.getTranslate('x'))
3658 .style("visibility", config.axis_x_show ? 'visible' : 'hidden');
3659 $$.axes.x.append("text")
3660 .attr("class", CLASS.axisXLabel)
3661 .attr("transform", config.axis_rotated ? "rotate(-90)" : "")
3662 .style("text-anchor", $$.textAnchorForXAxisLabel.bind($$));
3663
3664 $$.axes.y = main.append("g")
3665 .attr("class", CLASS.axis + ' ' + CLASS.axisY)
3666 .attr("clip-path", $$.clipPathForYAxis)
3667 .attr("transform", $$.getTranslate('y'))
3668 .style("visibility", config.axis_y_show ? 'visible' : 'hidden');
3669 $$.axes.y.append("text")
3670 .attr("class", CLASS.axisYLabel)
3671 .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
3672 .style("text-anchor", $$.textAnchorForYAxisLabel.bind($$));
3673
3674 $$.axes.y2 = main.append("g")
3675 .attr("class", CLASS.axis + ' ' + CLASS.axisY2)
3676 // clip-path?
3677 .attr("transform", $$.getTranslate('y2'))
3678 .style("visibility", config.axis_y2_show ? 'visible' : 'hidden');
3679 $$.axes.y2.append("text")
3680 .attr("class", CLASS.axisY2Label)
3681 .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
3682 .style("text-anchor", $$.textAnchorForY2AxisLabel.bind($$));
3683 };
3684 c3_chart_internal_fn.getXAxis = function (scale, orient, tickFormat, tickValues) {
3685 var $$ = this, config = $$.config,
3686 axis = c3_axis($$.d3, $$.isCategorized()).scale(scale).orient(orient);
3687
3688 // Set tick
3689 axis.tickFormat(tickFormat).tickValues(tickValues);
3690 if ($$.isCategorized()) {
3691 axis.tickCentered(config.axis_x_tick_centered);
3692 if (isEmpty(config.axis_x_tick_culling)) {
3693 config.axis_x_tick_culling = false;
3694 }
3695 } else {
3696 // TODO: move this to c3_axis
3697 axis.tickOffset = function () {
3698 var edgeX = $$.getEdgeX($$.data.targets), diff = $$.x(edgeX[1]) - $$.x(edgeX[0]),
3699 base = diff ? diff : (config.axis_rotated ? $$.height : $$.width);
3700 return (base / $$.getMaxDataCount()) / 2;
3701 };
3702 }
3703
3704 return axis;
3705 };
3706 c3_chart_internal_fn.getYAxis = function (scale, orient, tickFormat, ticks) {
3707 return c3_axis(this.d3).scale(scale).orient(orient).tickFormat(tickFormat).ticks(ticks);
3708 };
3709 c3_chart_internal_fn.getAxisId = function (id) {
3710 var config = this.config;
3711 return id in config.data_axes ? config.data_axes[id] : 'y';
3712 };
3713 c3_chart_internal_fn.getXAxisTickFormat = function () {
3714 var $$ = this, config = $$.config,
3715 format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { return v < 0 ? v.toFixed(0) : v; };
3716 if (config.axis_x_tick_format) {
3717 if (isFunction(config.axis_x_tick_format)) {
3718 format = config.axis_x_tick_format;
3719 } else if ($$.isTimeSeries()) {
3720 format = function (date) {
3721 return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : "";
3722 };
3723 }
3724 }
3725 return isFunction(format) ? function (v) { return format.call($$, v); } : format;
3726 };
3727 c3_chart_internal_fn.getAxisLabelOptionByAxisId = function (axisId) {
3728 var $$ = this, config = $$.config, option;
3729 if (axisId === 'y') {
3730 option = config.axis_y_label;
3731 } else if (axisId === 'y2') {
3732 option = config.axis_y2_label;
3733 } else if (axisId === 'x') {
3734 option = config.axis_x_label;
3735 }
3736 return option;
3737 };
3738 c3_chart_internal_fn.getAxisLabelText = function (axisId) {
3739 var option = this.getAxisLabelOptionByAxisId(axisId);
3740 return isString(option) ? option : option ? option.text : null;
3741 };
3742 c3_chart_internal_fn.setAxisLabelText = function (axisId, text) {
3743 var $$ = this, config = $$.config,
3744 option = $$.getAxisLabelOptionByAxisId(axisId);
3745 if (isString(option)) {
3746 if (axisId === 'y') {
3747 config.axis_y_label = text;
3748 } else if (axisId === 'y2') {
3749 config.axis_y2_label = text;
3750 } else if (axisId === 'x') {
3751 config.axis_x_label = text;
3752 }
3753 } else if (option) {
3754 option.text = text;
3755 }
3756 };
3757 c3_chart_internal_fn.getAxisLabelPosition = function (axisId, defaultPosition) {
3758 var option = this.getAxisLabelOptionByAxisId(axisId),
3759 position = (option && typeof option === 'object' && option.position) ? option.position : defaultPosition;
3760 return {
3761 isInner: position.indexOf('inner') >= 0,
3762 isOuter: position.indexOf('outer') >= 0,
3763 isLeft: position.indexOf('left') >= 0,
3764 isCenter: position.indexOf('center') >= 0,
3765 isRight: position.indexOf('right') >= 0,
3766 isTop: position.indexOf('top') >= 0,
3767 isMiddle: position.indexOf('middle') >= 0,
3768 isBottom: position.indexOf('bottom') >= 0
3769 };
3770 };
3771 c3_chart_internal_fn.getXAxisLabelPosition = function () {
3772 return this.getAxisLabelPosition('x', this.config.axis_rotated ? 'inner-top' : 'inner-right');
3773 };
3774 c3_chart_internal_fn.getYAxisLabelPosition = function () {
3775 return this.getAxisLabelPosition('y', this.config.axis_rotated ? 'inner-right' : 'inner-top');
3776 };
3777 c3_chart_internal_fn.getY2AxisLabelPosition = function () {
3778 return this.getAxisLabelPosition('y2', this.config.axis_rotated ? 'inner-right' : 'inner-top');
3779 };
3780 c3_chart_internal_fn.getAxisLabelPositionById = function (id) {
3781 return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition();
3782 };
3783 c3_chart_internal_fn.textForXAxisLabel = function () {
3784 return this.getAxisLabelText('x');
3785 };
3786 c3_chart_internal_fn.textForYAxisLabel = function () {
3787 return this.getAxisLabelText('y');
3788 };
3789 c3_chart_internal_fn.textForY2AxisLabel = function () {
3790 return this.getAxisLabelText('y2');
3791 };
3792 c3_chart_internal_fn.xForAxisLabel = function (forHorizontal, position) {
3793 var $$ = this;
3794 if (forHorizontal) {
3795 return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width;
3796 } else {
3797 return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0;
3798 }
3799 };
3800 c3_chart_internal_fn.dxForAxisLabel = function (forHorizontal, position) {
3801 if (forHorizontal) {
3802 return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0";
3803 } else {
3804 return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0";
3805 }
3806 };
3807 c3_chart_internal_fn.textAnchorForAxisLabel = function (forHorizontal, position) {
3808 if (forHorizontal) {
3809 return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end';
3810 } else {
3811 return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end';
3812 }
3813 };
3814 c3_chart_internal_fn.xForXAxisLabel = function () {
3815 return this.xForAxisLabel(!this.config.axis_rotated, this.getXAxisLabelPosition());
3816 };
3817 c3_chart_internal_fn.xForYAxisLabel = function () {
3818 return this.xForAxisLabel(this.config.axis_rotated, this.getYAxisLabelPosition());
3819 };
3820 c3_chart_internal_fn.xForY2AxisLabel = function () {
3821 return this.xForAxisLabel(this.config.axis_rotated, this.getY2AxisLabelPosition());
3822 };
3823 c3_chart_internal_fn.dxForXAxisLabel = function () {
3824 return this.dxForAxisLabel(!this.config.axis_rotated, this.getXAxisLabelPosition());
3825 };
3826 c3_chart_internal_fn.dxForYAxisLabel = function () {
3827 return this.dxForAxisLabel(this.config.axis_rotated, this.getYAxisLabelPosition());
3828 };
3829 c3_chart_internal_fn.dxForY2AxisLabel = function () {
3830 return this.dxForAxisLabel(this.config.axis_rotated, this.getY2AxisLabelPosition());
3831 };
3832 c3_chart_internal_fn.dyForXAxisLabel = function () {
3833 var $$ = this, config = $$.config,
3834 position = $$.getXAxisLabelPosition();
3835 if (config.axis_rotated) {
3836 return position.isInner ? "1.2em" : -25 - $$.getMaxTickWidth('x');
3837 } else {
3838 return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em";
3839 }
3840 };
3841 c3_chart_internal_fn.dyForYAxisLabel = function () {
3842 var $$ = this,
3843 position = $$.getYAxisLabelPosition();
3844 if ($$.config.axis_rotated) {
3845 return position.isInner ? "-0.5em" : "3em";
3846 } else {
3847 return position.isInner ? "1.2em" : -20 - $$.getMaxTickWidth('y');
3848 }
3849 };
3850 c3_chart_internal_fn.dyForY2AxisLabel = function () {
3851 var $$ = this,
3852 position = $$.getY2AxisLabelPosition();
3853 if ($$.config.axis_rotated) {
3854 return position.isInner ? "1.2em" : "-2.2em";
3855 } else {
3856 return position.isInner ? "-0.5em" : 30 + this.getMaxTickWidth('y2');
3857 }
3858 };
3859 c3_chart_internal_fn.textAnchorForXAxisLabel = function () {
3860 var $$ = this;
3861 return $$.textAnchorForAxisLabel(!$$.config.axis_rotated, $$.getXAxisLabelPosition());
3862 };
3863 c3_chart_internal_fn.textAnchorForYAxisLabel = function () {
3864 var $$ = this;
3865 return $$.textAnchorForAxisLabel($$.config.axis_rotated, $$.getYAxisLabelPosition());
3866 };
3867 c3_chart_internal_fn.textAnchorForY2AxisLabel = function () {
3868 var $$ = this;
3869 return $$.textAnchorForAxisLabel($$.config.axis_rotated, $$.getY2AxisLabelPosition());
3870 };
3871
3872 c3_chart_internal_fn.xForRotatedTickText = function (r) {
3873 return 10 * Math.sin(Math.PI * (r / 180));
3874 };
3875 c3_chart_internal_fn.yForRotatedTickText = function (r) {
3876 return 11.5 - 2.5 * (r / 15);
3877 };
3878 c3_chart_internal_fn.rotateTickText = function (axis, transition, rotate) {
3879 axis.selectAll('.tick text')
3880 .style("text-anchor", "start");
3881 transition.selectAll('.tick text')
3882 .attr("y", this.yForRotatedTickText(rotate))
3883 .attr("x", this.xForRotatedTickText(rotate))
3884 .attr("transform", "rotate(" + rotate + ")");
3885 };
3886
3887 c3_chart_internal_fn.getMaxTickWidth = function (id) {
3888 var $$ = this, config = $$.config,
3889 maxWidth = 0, targetsToShow, scale, axis;
3890 if ($$.svg) {
3891 targetsToShow = $$.filterTargetsToShow($$.data.targets);
3892 if (id === 'y') {
3893 scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y'));
3894 axis = $$.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, config.axis_y_ticks);
3895 } else if (id === 'y2') {
3896 scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2'));
3897 axis = $$.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, config.axis_y2_ticks);
3898 } else {
3899 scale = $$.x.copy().domain($$.getXDomain(targetsToShow));
3900 axis = $$.getXAxis(scale, $$.xOrient, $$.getXAxisTickFormat(), config.axis_x_tick_values ? config.axis_x_tick_values : $$.xAxis.tickValues());
3901 }
3902 $$.main.append("g").call(axis).each(function () {
3903 $$.d3.select(this).selectAll('text').each(function () {
3904 var box = this.getBoundingClientRect();
3905 if (maxWidth < box.width) { maxWidth = box.width; }
3906 });
3907 }).remove();
3908 }
3909 $$.currentMaxTickWidth = maxWidth <= 0 ? $$.currentMaxTickWidth : maxWidth;
3910 return $$.currentMaxTickWidth;
3911 };
3912
3913 c3_chart_internal_fn.updateAxisLabels = function (withTransition) {
3914 var $$ = this;
3915 var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel),
3916 axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel),
3917 axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label);
3918 (withTransition ? axisXLabel.transition() : axisXLabel)
3919 .attr("x", $$.xForXAxisLabel.bind($$))
3920 .attr("dx", $$.dxForXAxisLabel.bind($$))
3921 .attr("dy", $$.dyForXAxisLabel.bind($$))
3922 .text($$.textForXAxisLabel.bind($$));
3923 (withTransition ? axisYLabel.transition() : axisYLabel)
3924 .attr("x", $$.xForYAxisLabel.bind($$))
3925 .attr("dx", $$.dxForYAxisLabel.bind($$))
3926 .attr("dy", $$.dyForYAxisLabel.bind($$))
3927 .text($$.textForYAxisLabel.bind($$));
3928 (withTransition ? axisY2Label.transition() : axisY2Label)
3929 .attr("x", $$.xForY2AxisLabel.bind($$))
3930 .attr("dx", $$.dxForY2AxisLabel.bind($$))
3931 .attr("dy", $$.dyForY2AxisLabel.bind($$))
3932 .text($$.textForY2AxisLabel.bind($$));
3933 };
3934
3935 c3_chart_internal_fn.getAxisPadding = function (padding, key, defaultValue, all) {
3936 var ratio = padding.unit === 'ratio' ? all : 1;
3937 return isValue(padding[key]) ? padding[key] * ratio : defaultValue;
3938 };
3939
3940 c3_chart_internal_fn.generateTickValues = function (xs, tickCount) {
3941 var $$ = this;
3942 var tickValues = xs, targetCount, start, end, count, interval, i, tickValue;
3943 if (tickCount) {
3944 targetCount = isFunction(tickCount) ? tickCount() : tickCount;
3945 // compute ticks according to $$.config.axis_x_tick_count
3946 if (targetCount === 1) {
3947 tickValues = [xs[0]];
3948 } else if (targetCount === 2) {
3949 tickValues = [xs[0], xs[xs.length - 1]];
3950 } else if (targetCount > 2) {
3951 count = targetCount - 2;
3952 start = xs[0];
3953 end = xs[xs.length - 1];
3954 interval = (end - start) / (count + 1);
3955 // re-construct uniqueXs
3956 tickValues = [start];
3957 for (i = 0; i < count; i++) {
3958 tickValue = +start + interval * (i + 1);
3959 tickValues.push($$.isTimeSeries() ? new Date(tickValue) : tickValue);
3960 }
3961 tickValues.push(end);
3962 }
3963 }
3964 if (!$$.isTimeSeries()) { tickValues = tickValues.sort(function (a, b) { return a - b; }); }
3965 return tickValues;
3966 };
3967 c3_chart_internal_fn.generateAxisTransitions = function (duration) {
3968 var $$ = this, axes = $$.axes;
3969 return {
3970 axisX: duration ? axes.x.transition().duration(duration) : axes.x,
3971 axisY: duration ? axes.y.transition().duration(duration) : axes.y,
3972 axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2,
3973 axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx
3974 };
3975 };
3976 c3_chart_internal_fn.redrawAxis = function (transitions, isHidden) {
3977 var $$ = this;
3978 $$.axes.x.style("opacity", isHidden ? 0 : 1);
3979 $$.axes.y.style("opacity", isHidden ? 0 : 1);
3980 $$.axes.y2.style("opacity", isHidden ? 0 : 1);
3981 $$.axes.subx.style("opacity", isHidden ? 0 : 1);
3982 transitions.axisX.call($$.xAxis);
3983 transitions.axisY.call($$.yAxis);
3984 transitions.axisY2.call($$.y2Axis);
3985 transitions.axisSubX.call($$.subXAxis);
3986 };
3987
3988 c3_chart_internal_fn.getClipPath = function (id) {
3989 var isIE9 = window.navigator.appVersion.toLowerCase().indexOf("msie 9.") >= 0;
3990 return "url(" + (isIE9 ? "" : document.URL.split('#')[0]) + "#" + id + ")";
3991 };
3992 c3_chart_internal_fn.getAxisClipX = function (forHorizontal) {
3993 // axis line width + padding for left
3994 return forHorizontal ? -(1 + 30) : -(this.margin.left - 1);
3995 };
3996 c3_chart_internal_fn.getAxisClipY = function (forHorizontal) {
3997 return forHorizontal ? -20 : -4;
3998 };
3999 c3_chart_internal_fn.getXAxisClipX = function () {
4000 var $$ = this;
4001 return $$.getAxisClipX(!$$.config.axis_rotated);
4002 };
4003 c3_chart_internal_fn.getXAxisClipY = function () {
4004 var $$ = this;
4005 return $$.getAxisClipY(!$$.config.axis_rotated);
4006 };
4007 c3_chart_internal_fn.getYAxisClipX = function () {
4008 var $$ = this;
4009 return $$.getAxisClipX($$.config.axis_rotated);
4010 };
4011 c3_chart_internal_fn.getYAxisClipY = function () {
4012 var $$ = this;
4013 return $$.getAxisClipY($$.config.axis_rotated);
4014 };
4015 c3_chart_internal_fn.getAxisClipWidth = function (forHorizontal) {
4016 var $$ = this;
4017 // width + axis line width + padding for left/right
4018 return forHorizontal ? $$.width + 2 + 30 + 30 : $$.margin.left + 20;
4019 };
4020 c3_chart_internal_fn.getAxisClipHeight = function (forHorizontal) {
4021 var $$ = this, config = $$.config;
4022 return forHorizontal ? (config.axis_x_height ? config.axis_x_height : 0) + 80 : $$.height + 8;
4023 };
4024 c3_chart_internal_fn.getXAxisClipWidth = function () {
4025 var $$ = this;
4026 return $$.getAxisClipWidth(!$$.config.axis_rotated);
4027 };
4028 c3_chart_internal_fn.getXAxisClipHeight = function () {
4029 var $$ = this;
4030 return $$.getAxisClipHeight(!$$.config.axis_rotated);
4031 };
4032 c3_chart_internal_fn.getYAxisClipWidth = function () {
4033 var $$ = this;
4034 return $$.getAxisClipWidth($$.config.axis_rotated);
4035 };
4036 c3_chart_internal_fn.getYAxisClipHeight = function () {
4037 var $$ = this;
4038 return $$.getAxisClipHeight($$.config.axis_rotated);
4039 };
4040
4041 c3_chart_internal_fn.initPie = function () {
4042 var $$ = this, d3 = $$.d3, config = $$.config;
4043 $$.pie = d3.layout.pie().value(function (d) {
4044 return d.values.reduce(function (a, b) { return a + b.value; }, 0);
4045 });
4046 if (!config.data_order || !config.pie_sort || !config.donut_sort) {
4047 $$.pie.sort(null);
4048 }
4049 };
4050
4051 c3_chart_internal_fn.updateRadius = function () {
4052 var $$ = this, config = $$.config,
4053 w = config.gauge_width || config.donut_width;
4054 $$.radiusExpanded = Math.min($$.arcWidth, $$.arcHeight) / 2;
4055 $$.radius = $$.radiusExpanded * 0.95;
4056 $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6;
4057 $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0;
4058 };
4059
4060 c3_chart_internal_fn.updateArc = function () {
4061 var $$ = this;
4062 $$.svgArc = $$.getSvgArc();
4063 $$.svgArcExpanded = $$.getSvgArcExpanded();
4064 $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98);
4065 };
4066
4067 c3_chart_internal_fn.updateAngle = function (d) {
4068 var $$ = this, config = $$.config,
4069 found = false, index = 0;
4070 $$.pie($$.filterTargetsToShow($$.data.targets)).sort($$.descByStartAngle).forEach(function (t) {
4071 if (! found && t.data.id === d.data.id) {
4072 found = true;
4073 d = t;
4074 d.index = index;
4075 }
4076 index++;
4077 });
4078 if (isNaN(d.endAngle)) {
4079 d.endAngle = d.startAngle;
4080 }
4081 if ($$.isGaugeType(d.data)) {
4082 var gMin = config.gauge_min, gMax = config.gauge_max,
4083 gF = Math.abs(gMin) + gMax,
4084 aTic = (Math.PI) / gF;
4085 d.startAngle = (-1 * (Math.PI / 2)) + (aTic * Math.abs(gMin));
4086 d.endAngle = d.startAngle + (aTic * ((d.value > gMax) ? gMax : d.value));
4087 }
4088 return found ? d : null;
4089 };
4090
4091 c3_chart_internal_fn.getSvgArc = function () {
4092 var $$ = this,
4093 arc = $$.d3.svg.arc().outerRadius($$.radius).innerRadius($$.innerRadius),
4094 newArc = function (d, withoutUpdate) {
4095 var updated;
4096 if (withoutUpdate) { return arc(d); } // for interpolate
4097 updated = $$.updateAngle(d);
4098 return updated ? arc(updated) : "M 0 0";
4099 };
4100 // TODO: extends all function
4101 newArc.centroid = arc.centroid;
4102 return newArc;
4103 };
4104
4105 c3_chart_internal_fn.getSvgArcExpanded = function (rate) {
4106 var $$ = this,
4107 arc = $$.d3.svg.arc().outerRadius($$.radiusExpanded * (rate ? rate : 1)).innerRadius($$.innerRadius);
4108 return function (d) {
4109 var updated = $$.updateAngle(d);
4110 return updated ? arc(updated) : "M 0 0";
4111 };
4112 };
4113
4114 c3_chart_internal_fn.getArc = function (d, withoutUpdate, force) {
4115 return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : "M 0 0";
4116 };
4117
4118
4119 c3_chart_internal_fn.transformForArcLabel = function (d) {
4120 var $$ = this,
4121 updated = $$.updateAngle(d), c, x, y, h, ratio, translate = "";
4122 if (updated && !$$.hasType('gauge')) {
4123 c = this.svgArc.centroid(updated);
4124 x = isNaN(c[0]) ? 0 : c[0];
4125 y = isNaN(c[1]) ? 0 : c[1];
4126 h = Math.sqrt(x * x + y * y);
4127 // TODO: ratio should be an option?
4128 ratio = $$.radius && h ? (36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius / h : 0;
4129 translate = "translate(" + (x * ratio) + ',' + (y * ratio) + ")";
4130 }
4131 return translate;
4132 };
4133
4134 c3_chart_internal_fn.getArcRatio = function (d) {
4135 var $$ = this,
4136 whole = $$.hasType('gauge') ? Math.PI : (Math.PI * 2);
4137 return d ? (d.endAngle - d.startAngle) / whole : null;
4138 };
4139
4140 c3_chart_internal_fn.convertToArcData = function (d) {
4141 return this.addName({
4142 id: d.data.id,
4143 value: d.value,
4144 ratio: this.getArcRatio(d),
4145 index: d.index
4146 });
4147 };
4148
4149 c3_chart_internal_fn.textForArcLabel = function (d) {
4150 var $$ = this,
4151 updated, value, ratio, format;
4152 if (! $$.shouldShowArcLabel()) { return ""; }
4153 updated = $$.updateAngle(d);
4154 value = updated ? updated.value : null;
4155 ratio = $$.getArcRatio(updated);
4156 if (! $$.hasType('gauge') && ! $$.meetsArcLabelThreshold(ratio)) { return ""; }
4157 format = $$.getArcLabelFormat();
4158 return format ? format(value, ratio) : $$.defaultArcValueFormat(value, ratio);
4159 };
4160
4161 c3_chart_internal_fn.expandArc = function (id, withoutFadeOut) {
4162 var $$ = this,
4163 target = $$.svg.selectAll('.' + CLASS.chartArc + $$.selectorTarget(id)),
4164 noneTargets = $$.svg.selectAll('.' + CLASS.arc).filter(function (data) { return data.data.id !== id; });
4165
4166 if ($$.shouldExpand(id)) {
4167 target.selectAll('path')
4168 .transition().duration(50)
4169 .attr("d", $$.svgArcExpanded)
4170 .transition().duration(100)
4171 .attr("d", $$.svgArcExpandedSub)
4172 .each(function (d) {
4173 if ($$.isDonutType(d.data)) {
4174 // callback here
4175 }
4176 });
4177 }
4178 if (!withoutFadeOut) {
4179 noneTargets.style("opacity", 0.3);
4180 }
4181 };
4182
4183 c3_chart_internal_fn.unexpandArc = function (id) {
4184 var $$ = this,
4185 target = $$.svg.selectAll('.' + CLASS.chartArc + $$.selectorTarget(id));
4186 target.selectAll('path.' + CLASS.arc)
4187 .transition().duration(50)
4188 .attr("d", $$.svgArc);
4189 $$.svg.selectAll('.' + CLASS.arc)
4190 .style("opacity", 1);
4191 };
4192
4193 c3_chart_internal_fn.shouldExpand = function (id) {
4194 var $$ = this, config = $$.config;
4195 return ($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand);
4196 };
4197
4198 c3_chart_internal_fn.shouldShowArcLabel = function () {
4199 var $$ = this, config = $$.config, shouldShow = true;
4200 if ($$.hasType('donut')) {
4201 shouldShow = config.donut_label_show;
4202 } else if ($$.hasType('pie')) {
4203 shouldShow = config.pie_label_show;
4204 }
4205 // when gauge, always true
4206 return shouldShow;
4207 };
4208
4209 c3_chart_internal_fn.meetsArcLabelThreshold = function (ratio) {
4210 var $$ = this, config = $$.config,
4211 threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold;
4212 return ratio >= threshold;
4213 };
4214
4215 c3_chart_internal_fn.getArcLabelFormat = function () {
4216 var $$ = this, config = $$.config,
4217 format = config.pie_label_format;
4218 if ($$.hasType('gauge')) {
4219 format = config.gauge_label_format;
4220 } else if ($$.hasType('donut')) {
4221 format = config.donut_label_format;
4222 }
4223 return format;
4224 };
4225
4226 c3_chart_internal_fn.getArcTitle = function () {
4227 var $$ = this;
4228 return $$.hasType('donut') ? $$.config.donut_title : "";
4229 };
4230
4231 c3_chart_internal_fn.descByStartAngle = function (a, b) {
4232 return a.startAngle - b.startAngle;
4233 };
4234
4235 c3_chart_internal_fn.updateTargetsForArc = function (targets) {
4236 var $$ = this, main = $$.main,
4237 mainPieUpdate, mainPieEnter,
4238 classChartArc = $$.classChartArc.bind($$),
4239 classArcs = $$.classArcs.bind($$);
4240 mainPieUpdate = main.select('.' + CLASS.chartArcs).selectAll('.' + CLASS.chartArc)
4241 .data($$.pie(targets))
4242 .attr("class", classChartArc);
4243 mainPieEnter = mainPieUpdate.enter().append("g")
4244 .attr("class", classChartArc);
4245 mainPieEnter.append('g')
4246 .attr('class', classArcs);
4247 mainPieEnter.append("text")
4248 .attr("dy", $$.hasType('gauge') ? "-0.35em" : ".35em")
4249 .style("opacity", 0)
4250 .style("text-anchor", "middle")
4251 .style("pointer-events", "none");
4252 // MEMO: can not keep same color..., but not bad to update color in redraw
4253 //mainPieUpdate.exit().remove();
4254 };
4255
4256 c3_chart_internal_fn.initArc = function () {
4257 var $$ = this;
4258 $$.arcs = $$.main.select('.' + CLASS.chart).append("g")
4259 .attr("class", CLASS.chartArcs)
4260 .attr("transform", $$.getTranslate('arc'));
4261 $$.arcs.append('text')
4262 .attr('class', CLASS.chartArcsTitle)
4263 .style("text-anchor", "middle")
4264 .text($$.getArcTitle());
4265 };
4266
4267 c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransform) {
4268 var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main,
4269 mainArc;
4270 mainArc = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arc)
4271 .data($$.arcData.bind($$));
4272 mainArc.enter().append('path')
4273 .attr("class", $$.classArc.bind($$))
4274 .style("fill", function (d) { return $$.color(d.data); })
4275 .style("cursor", function (d) { return config.data_selection_isselectable(d) ? "pointer" : null; })
4276 .style("opacity", 0)
4277 .each(function (d) {
4278 if ($$.isGaugeType(d.data)) {
4279 d.startAngle = d.endAngle = -1 * (Math.PI / 2);
4280 }
4281 this._current = d;
4282 })
4283 .on('mouseover', function (d) {
4284 var updated, arcData;
4285 if ($$.transiting) { // skip while transiting
4286 return;
4287 }
4288 updated = $$.updateAngle(d);
4289 arcData = $$.convertToArcData(updated);
4290 // transitions
4291 $$.expandArc(updated.data.id);
4292 $$.toggleFocusLegend(updated.data.id, true);
4293 $$.config.data_onmouseover(arcData, this);
4294 })
4295 .on('mousemove', function (d) {
4296 var updated = $$.updateAngle(d),
4297 arcData = $$.convertToArcData(updated),
4298 selectedData = [arcData];
4299 $$.showTooltip(selectedData, d3.mouse(this));
4300 })
4301 .on('mouseout', function (d) {
4302 var updated, arcData;
4303 if ($$.transiting) { // skip while transiting
4304 return;
4305 }
4306 updated = $$.updateAngle(d);
4307 arcData = $$.convertToArcData(updated);
4308 // transitions
4309 $$.unexpandArc(updated.data.id);
4310 $$.revertLegend();
4311 $$.hideTooltip();
4312 $$.config.data_onmouseout(arcData, this);
4313 })
4314 .on('click', function (d, i) {
4315 var updated, arcData;
4316 if (!$$.toggleShape) {
4317 return;
4318 }
4319 updated = $$.updateAngle(d);
4320 arcData = $$.convertToArcData(updated);
4321 $$.toggleShape(this, arcData, i); // onclick called in toogleShape()
4322 });
4323 mainArc
4324 .attr("transform", function (d) { return !$$.isGaugeType(d.data) && withTransform ? "scale(0)" : ""; })
4325 .style("opacity", function (d) { return d === this._current ? 0 : 1; })
4326 .each(function () { $$.transiting = true; })
4327 .transition().duration(duration)
4328 .attrTween("d", function (d) {
4329 var updated = $$.updateAngle(d), interpolate;
4330 if (! updated) {
4331 return function () { return "M 0 0"; };
4332 }
4333 // if (this._current === d) {
4334 // this._current = {
4335 // startAngle: Math.PI*2,
4336 // endAngle: Math.PI*2,
4337 // };
4338 // }
4339 if (isNaN(this._current.endAngle)) {
4340 this._current.endAngle = this._current.startAngle;
4341 }
4342 interpolate = d3.interpolate(this._current, updated);
4343 this._current = interpolate(0);
4344 return function (t) { return $$.getArc(interpolate(t), true); };
4345 })
4346 .attr("transform", withTransform ? "scale(1)" : "")
4347 .style("fill", function (d) {
4348 return $$.levelColor ? $$.levelColor(d.data.values[0].value) : $$.color(d.data.id);
4349 }) // Where gauge reading color would receive customization.
4350 .style("opacity", 1)
4351 .call($$.endall, function () {
4352 $$.transiting = false;
4353 });
4354 mainArc.exit().transition().duration(durationForExit)
4355 .style('opacity', 0)
4356 .remove();
4357 main.selectAll('.' + CLASS.chartArc).select('text')
4358 .style("opacity", 0)
4359 .attr('class', function (d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : ''; })
4360 .text($$.textForArcLabel.bind($$))
4361 .attr("transform", $$.transformForArcLabel.bind($$))
4362 .transition().duration(duration)
4363 .style("opacity", function (d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0; });
4364 main.select('.' + CLASS.chartArcsTitle)
4365 .style("opacity", $$.hasType('donut') || $$.hasType('gauge') ? 1 : 0);
4366
4367 };
4368 c3_chart_internal_fn.initGauge = function () {
4369 var $$ = this, config = $$.config, arcs = $$.arcs;
4370 if ($$.hasType('gauge')) {
4371 arcs.append('path')
4372 .attr("class", CLASS.chartArcsBackground)
4373 .attr("d", function () {
4374 var d = {
4375 data: [{value: config.gauge_max}],
4376 startAngle: -1 * (Math.PI / 2),
4377 endAngle: Math.PI / 2
4378 };
4379 return $$.getArc(d, true, true);
4380 });
4381 arcs.append("text")
4382 .attr("dy", ".75em")
4383 .attr("class", CLASS.chartArcsGaugeUnit)
4384 .style("text-anchor", "middle")
4385 .style("pointer-events", "none")
4386 .text(config.gauge_label_show ? config.gauge_units : '');
4387 arcs.append("text")
4388 .attr("dx", -1 * ($$.innerRadius + (($$.radius - $$.innerRadius) / 2)) + "px")
4389 .attr("dy", "1.2em")
4390 .attr("class", CLASS.chartArcsGaugeMin)
4391 .style("text-anchor", "middle")
4392 .style("pointer-events", "none")
4393 .text(config.gauge_label_show ? config.gauge_min : '');
4394 arcs.append("text")
4395 .attr("dx", $$.innerRadius + (($$.radius - $$.innerRadius) / 2) + "px")
4396 .attr("dy", "1.2em")
4397 .attr("class", CLASS.chartArcsGaugeMax)
4398 .style("text-anchor", "middle")
4399 .style("pointer-events", "none")
4400 .text(config.gauge_label_show ? config.gauge_max : '');
4401 }
4402 };
4403
4404 c3_chart_internal_fn.initRegion = function () {
4405 var $$ = this;
4406 $$.main.append('g')
4407 .attr("clip-path", $$.clipPath)
4408 .attr("class", CLASS.regions);
4409 };
4410 c3_chart_internal_fn.redrawRegion = function (duration) {
4411 var $$ = this, config = $$.config;
4412 $$.mainRegion = $$.main.select('.' + CLASS.regions).selectAll('.' + CLASS.region)
4413 .data(config.regions);
4414 $$.mainRegion.enter().append('g')
4415 .attr('class', $$.classRegion.bind($$))
4416 .append('rect')
4417 .style("fill-opacity", 0);
4418 $$.mainRegion.exit().transition().duration(duration)
4419 .style("opacity", 0)
4420 .remove();
4421 };
4422 c3_chart_internal_fn.addTransitionForRegion = function (transitions) {
4423 var $$ = this,
4424 x = $$.regionX.bind($$),
4425 y = $$.regionY.bind($$),
4426 w = $$.regionWidth.bind($$),
4427 h = $$.regionHeight.bind($$);
4428 transitions.push($$.mainRegion.selectAll('rect').transition()
4429 .attr("x", x)
4430 .attr("y", y)
4431 .attr("width", w)
4432 .attr("height", h)
4433 .style("fill-opacity", function (d) { return isValue(d.opacity) ? d.opacity : 0.1; }));
4434 };
4435 c3_chart_internal_fn.regionX = function (d) {
4436 var $$ = this, config = $$.config,
4437 xPos, yScale = d.axis === 'y' ? $$.y : $$.y2;
4438 if (d.axis === 'y' || d.axis === 'y2') {
4439 xPos = config.axis_rotated ? ('start' in d ? yScale(d.start) : 0) : 0;
4440 } else {
4441 xPos = config.axis_rotated ? 0 : ('start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0);
4442 }
4443 return xPos;
4444 };
4445 c3_chart_internal_fn.regionY = function (d) {
4446 var $$ = this, config = $$.config,
4447 yPos, yScale = d.axis === 'y' ? $$.y : $$.y2;
4448 if (d.axis === 'y' || d.axis === 'y2') {
4449 yPos = config.axis_rotated ? 0 : ('end' in d ? yScale(d.end) : 0);
4450 } else {
4451 yPos = config.axis_rotated ? ('start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0) : 0;
4452 }
4453 return yPos;
4454 };
4455 c3_chart_internal_fn.regionWidth = function (d) {
4456 var $$ = this, config = $$.config,
4457 start = $$.regionX(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2;
4458 if (d.axis === 'y' || d.axis === 'y2') {
4459 end = config.axis_rotated ? ('end' in d ? yScale(d.end) : $$.width) : $$.width;
4460 } else {
4461 end = config.axis_rotated ? $$.width : ('end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width);
4462 }
4463 return end < start ? 0 : end - start;
4464 };
4465 c3_chart_internal_fn.regionHeight = function (d) {
4466 var $$ = this, config = $$.config,
4467 start = this.regionY(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2;
4468 if (d.axis === 'y' || d.axis === 'y2') {
4469 end = config.axis_rotated ? $$.height : ('start' in d ? yScale(d.start) : $$.height);
4470 } else {
4471 end = config.axis_rotated ? ('end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height) : $$.height;
4472 }
4473 return end < start ? 0 : end - start;
4474 };
4475 c3_chart_internal_fn.isRegionOnX = function (d) {
4476 return !d.axis || d.axis === 'x';
4477 };
4478
4479 c3_chart_internal_fn.drag = function (mouse) {
4480 var $$ = this, config = $$.config, main = $$.main, d3 = $$.d3;
4481 var sx, sy, mx, my, minX, maxX, minY, maxY;
4482
4483 if ($$.hasArcType()) { return; }
4484 if (! config.data_selection_enabled) { return; } // do nothing if not selectable
4485 if (config.zoom_enabled && ! $$.zoom.altDomain) { return; } // skip if zoomable because of conflict drag dehavior
4486 if (!config.data_selection_multiple) { return; } // skip when single selection because drag is used for multiple selection
4487
4488 sx = $$.dragStart[0];
4489 sy = $$.dragStart[1];
4490 mx = mouse[0];
4491 my = mouse[1];
4492 minX = Math.min(sx, mx);
4493 maxX = Math.max(sx, mx);
4494 minY = (config.data_selection_grouped) ? $$.margin.top : Math.min(sy, my);
4495 maxY = (config.data_selection_grouped) ? $$.height : Math.max(sy, my);
4496
4497 main.select('.' + CLASS.dragarea)
4498 .attr('x', minX)
4499 .attr('y', minY)
4500 .attr('width', maxX - minX)
4501 .attr('height', maxY - minY);
4502 // TODO: binary search when multiple xs
4503 main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape)
4504 .filter(function (d) { return config.data_selection_isselectable(d); })
4505 .each(function (d, i) {
4506 var shape = d3.select(this),
4507 isSelected = shape.classed(CLASS.SELECTED),
4508 isIncluded = shape.classed(CLASS.INCLUDED),
4509 _x, _y, _w, _h, toggle, isWithin = false, box;
4510 if (shape.classed(CLASS.circle)) {
4511 _x = shape.attr("cx") * 1;
4512 _y = shape.attr("cy") * 1;
4513 toggle = $$.togglePoint;
4514 isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY;
4515 }
4516 else if (shape.classed(CLASS.bar)) {
4517 box = getPathBox(this);
4518 _x = box.x;
4519 _y = box.y;
4520 _w = box.width;
4521 _h = box.height;
4522 toggle = $$.toggleBar;
4523 isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY);
4524 } else {
4525 // line/area selection not supported yet
4526 return;
4527 }
4528 if (isWithin ^ isIncluded) {
4529 shape.classed(CLASS.INCLUDED, !isIncluded);
4530 // TODO: included/unincluded callback here
4531 shape.classed(CLASS.SELECTED, !isSelected);
4532 toggle.call($$, !isSelected, shape, d, i);
4533 }
4534 });
4535 };
4536
4537 c3_chart_internal_fn.dragstart = function (mouse) {
4538 var $$ = this, config = $$.config;
4539 if ($$.hasArcType()) { return; }
4540 if (! config.data_selection_enabled) { return; } // do nothing if not selectable
4541 $$.dragStart = mouse;
4542 $$.main.select('.' + CLASS.chart).append('rect')
4543 .attr('class', CLASS.dragarea)
4544 .style('opacity', 0.1);
4545 $$.dragging = true;
4546 $$.config.data_ondragstart();
4547 };
4548
4549 c3_chart_internal_fn.dragend = function () {
4550 var $$ = this, config = $$.config;
4551 if ($$.hasArcType()) { return; }
4552 if (! config.data_selection_enabled) { return; } // do nothing if not selectable
4553 $$.main.select('.' + CLASS.dragarea)
4554 .transition().duration(100)
4555 .style('opacity', 0)
4556 .remove();
4557 $$.main.selectAll('.' + CLASS.shape)
4558 .classed(CLASS.INCLUDED, false);
4559 $$.dragging = false;
4560 $$.config.data_ondragend();
4561 };
4562
4563
4564 c3_chart_internal_fn.selectPoint = function (target, d, i) {
4565 var $$ = this, config = $$.config,
4566 cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$),
4567 cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$),
4568 r = $$.pointSelectR.bind($$);
4569 config.data_onselected.call($$.api, d, target.node());
4570 // add selected-circle on low layer g
4571 $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i)
4572 .data([d])
4573 .enter().append('circle')
4574 .attr("class", function () { return $$.generateClass(CLASS.selectedCircle, i); })
4575 .attr("cx", cx)
4576 .attr("cy", cy)
4577 .attr("stroke", function () { return $$.color(d); })
4578 .attr("r", function (d) { return $$.pointSelectR(d) * 1.4; })
4579 .transition().duration(100)
4580 .attr("r", r);
4581 };
4582 c3_chart_internal_fn.unselectPoint = function (target, d, i) {
4583 var $$ = this;
4584 $$.config.data_onunselected(d, target.node());
4585 // remove selected-circle from low layer g
4586 $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i)
4587 .transition().duration(100).attr('r', 0)
4588 .remove();
4589 };
4590 c3_chart_internal_fn.togglePoint = function (selected, target, d, i) {
4591 selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i);
4592 };
4593 c3_chart_internal_fn.selectBar = function (target, d) {
4594 var $$ = this;
4595 $$.config.data_onselected.call($$, d, target.node());
4596 target.transition().duration(100)
4597 .style("fill", function () { return $$.d3.rgb($$.color(d)).brighter(0.75); });
4598 };
4599 c3_chart_internal_fn.unselectBar = function (target, d) {
4600 var $$ = this;
4601 $$.config.data_onunselected.call($$, d, target.node());
4602 target.transition().duration(100)
4603 .style("fill", function () { return $$.color(d); });
4604 };
4605 c3_chart_internal_fn.toggleBar = function (selected, target, d, i) {
4606 selected ? this.selectBar(target, d, i) : this.unselectBar(target, d, i);
4607 };
4608 c3_chart_internal_fn.toggleArc = function (selected, target, d, i) {
4609 this.toggleBar(selected, target, d.data, i);
4610 };
4611 c3_chart_internal_fn.getToggle = function (that) {
4612 var $$ = this;
4613 // path selection not supported yet
4614 return that.nodeName === 'circle' ? $$.togglePoint : ($$.d3.select(that).classed(CLASS.bar) ? $$.toggleBar : $$.toggleArc);
4615 };
4616 c3_chart_internal_fn.toggleShape = function (that, d, i) {
4617 var $$ = this, d3 = $$.d3, config = $$.config,
4618 shape = d3.select(that), isSelected = shape.classed(CLASS.SELECTED), isWithin, toggle;
4619 if (that.nodeName === 'circle') {
4620 isWithin = $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5);
4621 toggle = $$.togglePoint;
4622 }
4623 else if (that.nodeName === 'path') {
4624 if (shape.classed(CLASS.bar)) {
4625 isWithin = $$.isWithinBar(that);
4626 toggle = $$.toggleBar;
4627 } else { // would be arc
4628 isWithin = true;
4629 toggle = $$.toggleArc;
4630 }
4631 }
4632 if (config.data_selection_grouped || isWithin) {
4633 if (config.data_selection_enabled && config.data_selection_isselectable(d)) {
4634 if (!config.data_selection_multiple) {
4635 $$.main.selectAll('.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : "")).selectAll('.' + CLASS.shape).each(function (d, i) {
4636 var shape = d3.select(this);
4637 if (shape.classed(CLASS.SELECTED)) { toggle.call($$, false, shape.classed(CLASS.SELECTED, false), d, i); }
4638 });
4639 }
4640 shape.classed(CLASS.SELECTED, !isSelected);
4641 toggle.call($$, !isSelected, shape, d, i);
4642 }
4643 $$.config.data_onclick.call($$.api, d, that);
4644 }
4645 };
4646
4647 c3_chart_internal_fn.initBrush = function () {
4648 var $$ = this, d3 = $$.d3;
4649 $$.brush = d3.svg.brush().on("brush", function () { $$.redrawForBrush(); });
4650 $$.brush.update = function () {
4651 if ($$.context) { $$.context.select('.' + CLASS.brush).call(this); }
4652 return this;
4653 };
4654 $$.brush.scale = function (scale) {
4655 return $$.config.axis_rotated ? this.y(scale) : this.x(scale);
4656 };
4657 };
4658 c3_chart_internal_fn.initSubchart = function () {
4659 var $$ = this, config = $$.config,
4660 context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context'));
4661
4662 if (!config.subchart_show) {
4663 context.style('visibility', 'hidden');
4664 }
4665
4666 // Define g for chart area
4667 context.append('g')
4668 .attr("clip-path", $$.clipPath)
4669 .attr('class', CLASS.chart);
4670
4671 // Define g for bar chart area
4672 context.select('.' + CLASS.chart).append("g")
4673 .attr("class", CLASS.chartBars);
4674
4675 // Define g for line chart area
4676 context.select('.' + CLASS.chart).append("g")
4677 .attr("class", CLASS.chartLines);
4678
4679 // Add extent rect for Brush
4680 context.append("g")
4681 .attr("clip-path", $$.clipPath)
4682 .attr("class", CLASS.brush)
4683 .call($$.brush)
4684 .selectAll("rect")
4685 .attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2);
4686
4687 // ATTENTION: This must be called AFTER chart added
4688 // Add Axis
4689 $$.axes.subx = context.append("g")
4690 .attr("class", CLASS.axisX)
4691 .attr("transform", $$.getTranslate('subx'))
4692 .attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis);
4693 };
4694 c3_chart_internal_fn.updateTargetsForSubchart = function (targets) {
4695 var $$ = this, context = $$.context, config = $$.config,
4696 contextLineEnter, contextLineUpdate, contextBarEnter, contextBarUpdate,
4697 classChartBar = $$.classChartBar.bind($$),
4698 classBars = $$.classBars.bind($$),
4699 classChartLine = $$.classChartLine.bind($$),
4700 classLines = $$.classLines.bind($$),
4701 classAreas = $$.classAreas.bind($$);
4702
4703 if (config.subchart_show) {
4704 contextBarUpdate = context.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar)
4705 .data(targets)
4706 .attr('class', classChartBar);
4707 contextBarEnter = contextBarUpdate.enter().append('g')
4708 .style('opacity', 0)
4709 .attr('class', classChartBar);
4710 // Bars for each data
4711 contextBarEnter.append('g')
4712 .attr("class", classBars);
4713
4714 //-- Line --//
4715 contextLineUpdate = context.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine)
4716 .data(targets)
4717 .attr('class', classChartLine);
4718 contextLineEnter = contextLineUpdate.enter().append('g')
4719 .style('opacity', 0)
4720 .attr('class', classChartLine);
4721 // Lines for each data
4722 contextLineEnter.append("g")
4723 .attr("class", classLines);
4724 // Area
4725 contextLineEnter.append("g")
4726 .attr("class", classAreas);
4727 }
4728 };
4729 c3_chart_internal_fn.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) {
4730 var $$ = this, d3 = $$.d3, context = $$.context, config = $$.config,
4731 contextLine, contextArea, contextBar, drawAreaOnSub, drawBarOnSub, drawLineOnSub,
4732 barData = $$.barData.bind($$),
4733 lineData = $$.lineData.bind($$),
4734 classBar = $$.classBar.bind($$),
4735 classLine = $$.classLine.bind($$),
4736 classArea = $$.classArea.bind($$),
4737 initialOpacity = $$.initialOpacity.bind($$);
4738
4739 // subchart
4740 if (config.subchart_show) {
4741 // reflect main chart to extent on subchart if zoomed
4742 if (d3.event && d3.event.type === 'zoom') {
4743 $$.brush.extent($$.x.orgDomain()).update();
4744 }
4745 // update subchart elements if needed
4746 if (withSubchart) {
4747
4748 // rotate tick text if needed
4749 if (!config.axis_rotated && config.axis_x_tick_rotate) {
4750 $$.rotateTickText($$.axes.subx, transitions.axisSubX, config.axis_x_tick_rotate);
4751 }
4752
4753 // extent rect
4754 if (!$$.brush.empty()) {
4755 $$.brush.extent($$.x.orgDomain()).update();
4756 }
4757 // setup drawer - MEMO: this must be called after axis updated
4758 drawAreaOnSub = $$.generateDrawArea(areaIndices, true);
4759 drawBarOnSub = $$.generateDrawBar(barIndices, true);
4760 drawLineOnSub = $$.generateDrawLine(lineIndices, true);
4761 // bars
4762 contextBar = context.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar)
4763 .data(barData);
4764 contextBar.enter().append('path')
4765 .attr("class", classBar)
4766 .style("stroke", 'none')
4767 .style("fill", $$.color);
4768 contextBar
4769 .style("opacity", initialOpacity)
4770 .transition().duration(duration)
4771 .attr('d', drawBarOnSub)
4772 .style('opacity', 1);
4773 contextBar.exit().transition().duration(duration)
4774 .style('opacity', 0)
4775 .remove();
4776 // lines
4777 contextLine = context.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line)
4778 .data(lineData);
4779 contextLine.enter().append('path')
4780 .attr('class', classLine)
4781 .style('stroke', $$.color);
4782 contextLine
4783 .style("opacity", initialOpacity)
4784 .transition().duration(duration)
4785 .attr("d", drawLineOnSub)
4786 .style('opacity', 1);
4787 contextLine.exit().transition().duration(duration)
4788 .style('opacity', 0)
4789 .remove();
4790 // area
4791 contextArea = context.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area)
4792 .data(lineData);
4793 contextArea.enter().append('path')
4794 .attr("class", classArea)
4795 .style("fill", $$.color)
4796 .style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; });
4797 contextArea
4798 .style("opacity", 0)
4799 .transition().duration(duration)
4800 .attr("d", drawAreaOnSub)
4801 .style("fill", $$.color)
4802 .style("opacity", $$.orgAreaOpacity);
4803 contextArea.exit().transition().duration(durationForExit)
4804 .style('opacity', 0)
4805 .remove();
4806 }
4807 }
4808 };
4809 c3_chart_internal_fn.redrawForBrush = function () {
4810 var $$ = this, x = $$.x;
4811 $$.redraw({
4812 withTransition: false,
4813 withY: false,
4814 withSubchart: false,
4815 withUpdateXDomain: true
4816 });
4817 $$.config.subchart_onbrush.call($$.api, x.orgDomain());
4818 };
4819 c3_chart_internal_fn.transformContext = function (withTransition, transitions) {
4820 var $$ = this, subXAxis;
4821 if (transitions && transitions.axisSubX) {
4822 subXAxis = transitions.axisSubX;
4823 } else {
4824 subXAxis = $$.context.select('.' + CLASS.axisX);
4825 if (withTransition) { subXAxis = subXAxis.transition(); }
4826 }
4827 $$.context.attr("transform", $$.getTranslate('context'));
4828 subXAxis.attr("transform", $$.getTranslate('subx'));
4829 };
4830
4831 c3_chart_internal_fn.initZoom = function () {
4832 var $$ = this, d3 = $$.d3, config = $$.config;
4833 $$.zoom = d3.behavior.zoom()
4834 .on("zoomstart", function () {
4835 $$.zoom.altDomain = d3.event.sourceEvent.altKey ? $$.x.orgDomain() : null;
4836 })
4837 .on("zoom", function () { $$.redrawForZoom.call($$); });
4838 $$.zoom.scale = function (scale) {
4839 return config.axis_rotated ? this.y(scale) : this.x(scale);
4840 };
4841 $$.zoom.orgScaleExtent = function () {
4842 var extent = config.zoom_extent ? config.zoom_extent : [1, 10];
4843 return [extent[0], Math.max($$.getMaxDataCount() / extent[1], extent[1])];
4844 };
4845 $$.zoom.updateScaleExtent = function () {
4846 var ratio = diffDomain($$.x.orgDomain()) / diffDomain($$.orgXDomain),
4847 extent = this.orgScaleExtent();
4848 this.scaleExtent([extent[0] * ratio, extent[1] * ratio]);
4849 return this;
4850 };
4851 };
4852 c3_chart_internal_fn.updateZoom = function () {
4853 var $$ = this, z = $$.config.zoom_enabled ? $$.zoom : function () {};
4854 $$.main.select('.' + CLASS.zoomRect).call(z);
4855 $$.main.selectAll('.' + CLASS.eventRect).call(z);
4856 };
4857 c3_chart_internal_fn.redrawForZoom = function () {
4858 var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x, orgXDomain = $$.orgXDomain;
4859 if (!config.zoom_enabled) {
4860 return;
4861 }
4862 if ($$.filterTargetsToShow($$.data.targets).length === 0) {
4863 return;
4864 }
4865 if (d3.event.sourceEvent.type === 'mousemove' && zoom.altDomain) {
4866 x.domain(zoom.altDomain);
4867 zoom.scale(x).updateScaleExtent();
4868 return;
4869 }
4870 if ($$.isCategorized() && x.orgDomain()[0] === orgXDomain[0]) {
4871 x.domain([orgXDomain[0] - 1e-10, x.orgDomain()[1]]);
4872 }
4873 $$.redraw({
4874 withTransition: false,
4875 withY: false,
4876 withSubchart: false
4877 });
4878 if (d3.event.sourceEvent.type === 'mousemove') {
4879 $$.cancelClick = true;
4880 }
4881 config.zoom_onzoom.call($$.api, x.orgDomain());
4882 };
4883
4884 c3_chart_internal_fn.generateColor = function () {
4885 var $$ = this, config = $$.config, d3 = $$.d3,
4886 colors = config.data_colors,
4887 pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.scale.category10().range(),
4888 callback = config.data_color,
4889 ids = [];
4890
4891 return function (d) {
4892 var id = d.id || d, color;
4893
4894 // if callback function is provided
4895 if (colors[id] instanceof Function) {
4896 color = colors[id](d);
4897 }
4898 // if specified, choose that color
4899 else if (colors[id]) {
4900 color = colors[id];
4901 }
4902 // if not specified, choose from pattern
4903 else {
4904 if (ids.indexOf(id) < 0) { ids.push(id); }
4905 color = pattern[ids.indexOf(id) % pattern.length];
4906 colors[id] = color;
4907 }
4908 return callback instanceof Function ? callback(color, d) : color;
4909 };
4910 };
4911 c3_chart_internal_fn.generateLevelColor = function () {
4912 var $$ = this, config = $$.config,
4913 colors = config.color_pattern,
4914 threshold = config.color_threshold,
4915 asValue = threshold.unit === 'value',
4916 values = threshold.values && threshold.values.length ? threshold.values : [],
4917 max = threshold.max || 100;
4918 return notEmpty(config.color_threshold) ? function (value) {
4919 var i, v, color = colors[colors.length - 1];
4920 for (i = 0; i < values.length; i++) {
4921 v = asValue ? value : (value * 100 / max);
4922 if (v < values[i]) {
4923 color = colors[i];
4924 break;
4925 }
4926 }
4927 return color;
4928 } : null;
4929 };
4930
4931 c3_chart_internal_fn.getYFormat = function (forArc) {
4932 var $$ = this,
4933 formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat,
4934 formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format;
4935 return function (v, ratio, id) {
4936 var format = $$.getAxisId(id) === 'y2' ? formatForY2 : formatForY;
4937 return format.call($$, v, ratio);
4938 };
4939 };
4940 c3_chart_internal_fn.yFormat = function (v) {
4941 var $$ = this, config = $$.config,
4942 format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat;
4943 return format(v);
4944 };
4945 c3_chart_internal_fn.y2Format = function (v) {
4946 var $$ = this, config = $$.config,
4947 format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat;
4948 return format(v);
4949 };
4950 c3_chart_internal_fn.defaultValueFormat = function (v) {
4951 return isValue(v) ? +v : "";
4952 };
4953 c3_chart_internal_fn.defaultArcValueFormat = function (v, ratio) {
4954 return (ratio * 100).toFixed(1) + '%';
4955 };
4956 c3_chart_internal_fn.formatByAxisId = function (axisId) {
4957 var $$ = this, data_labels = $$.config.data_labels,
4958 format = function (v) { return isValue(v) ? +v : ""; };
4959 // find format according to axis id
4960 if (typeof data_labels.format === 'function') {
4961 format = data_labels.format;
4962 } else if (typeof data_labels.format === 'object') {
4963 if (data_labels.format[axisId]) {
4964 format = data_labels.format[axisId];
4965 }
4966 }
4967 return format;
4968 };
4969
4970 c3_chart_internal_fn.hasCaches = function (ids) {
4971 for (var i = 0; i < ids.length; i++) {
4972 if (! (ids[i] in this.cache)) { return false; }
4973 }
4974 return true;
4975 };
4976 c3_chart_internal_fn.addCache = function (id, target) {
4977 this.cache[id] = this.cloneTarget(target);
4978 };
4979 c3_chart_internal_fn.getCaches = function (ids) {
4980 var targets = [], i;
4981 for (i = 0; i < ids.length; i++) {
4982 if (ids[i] in this.cache) { targets.push(this.cloneTarget(this.cache[ids[i]])); }
4983 }
4984 return targets;
4985 };
4986
4987 var CLASS = c3_chart_internal_fn.CLASS = {
4988 target: 'c3-target',
4989 chart: 'c3-chart',
4990 chartLine: 'c3-chart-line',
4991 chartLines: 'c3-chart-lines',
4992 chartBar: 'c3-chart-bar',
4993 chartBars: 'c3-chart-bars',
4994 chartText: 'c3-chart-text',
4995 chartTexts: 'c3-chart-texts',
4996 chartArc: 'c3-chart-arc',
4997 chartArcs: 'c3-chart-arcs',
4998 chartArcsTitle: 'c3-chart-arcs-title',
4999 chartArcsBackground: 'c3-chart-arcs-background',
5000 chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit',
5001 chartArcsGaugeMax: 'c3-chart-arcs-gauge-max',
5002 chartArcsGaugeMin: 'c3-chart-arcs-gauge-min',
5003 selectedCircle: 'c3-selected-circle',
5004 selectedCircles: 'c3-selected-circles',
5005 eventRect: 'c3-event-rect',
5006 eventRects: 'c3-event-rects',
5007 eventRectsSingle: 'c3-event-rects-single',
5008 eventRectsMultiple: 'c3-event-rects-multiple',
5009 zoomRect: 'c3-zoom-rect',
5010 brush: 'c3-brush',
5011 focused: 'c3-focused',
5012 region: 'c3-region',
5013 regions: 'c3-regions',
5014 tooltip: 'c3-tooltip',
5015 tooltipName: 'c3-tooltip-name',
5016 shape: 'c3-shape',
5017 shapes: 'c3-shapes',
5018 line: 'c3-line',
5019 lines: 'c3-lines',
5020 bar: 'c3-bar',
5021 bars: 'c3-bars',
5022 circle: 'c3-circle',
5023 circles: 'c3-circles',
5024 arc: 'c3-arc',
5025 arcs: 'c3-arcs',
5026 area: 'c3-area',
5027 areas: 'c3-areas',
5028 empty: 'c3-empty',
5029 text: 'c3-text',
5030 texts: 'c3-texts',
5031 gaugeValue: 'c3-gauge-value',
5032 grid: 'c3-grid',
5033 xgrid: 'c3-xgrid',
5034 xgrids: 'c3-xgrids',
5035 xgridLine: 'c3-xgrid-line',
5036 xgridLines: 'c3-xgrid-lines',
5037 xgridFocus: 'c3-xgrid-focus',
5038 ygrid: 'c3-ygrid',
5039 ygrids: 'c3-ygrids',
5040 ygridLine: 'c3-ygrid-line',
5041 ygridLines: 'c3-ygrid-lines',
5042 axis: 'c3-axis',
5043 axisX: 'c3-axis-x',
5044 axisXLabel: 'c3-axis-x-label',
5045 axisY: 'c3-axis-y',
5046 axisYLabel: 'c3-axis-y-label',
5047 axisY2: 'c3-axis-y2',
5048 axisY2Label: 'c3-axis-y2-label',
5049 legendBackground: 'c3-legend-background',
5050 legendItem: 'c3-legend-item',
5051 legendItemEvent: 'c3-legend-item-event',
5052 legendItemTile: 'c3-legend-item-tile',
5053 legendItemHidden: 'c3-legend-item-hidden',
5054 legendItemFocused: 'c3-legend-item-focused',
5055 dragarea: 'c3-dragarea',
5056 EXPANDED: '_expanded_',
5057 SELECTED: '_selected_',
5058 INCLUDED: '_included_'
5059 };
5060 c3_chart_internal_fn.generateClass = function (prefix, targetId) {
5061 return " " + prefix + " " + prefix + this.getTargetSelectorSuffix(targetId);
5062 };
5063 c3_chart_internal_fn.classText = function (d) {
5064 return this.generateClass(CLASS.text, d.index);
5065 };
5066 c3_chart_internal_fn.classTexts = function (d) {
5067 return this.generateClass(CLASS.texts, d.id);
5068 };
5069 c3_chart_internal_fn.classShape = function (d) {
5070 return this.generateClass(CLASS.shape, d.index);
5071 };
5072 c3_chart_internal_fn.classShapes = function (d) {
5073 return this.generateClass(CLASS.shapes, d.id);
5074 };
5075 c3_chart_internal_fn.classLine = function (d) {
5076 return this.classShape(d) + this.generateClass(CLASS.line, d.id);
5077 };
5078 c3_chart_internal_fn.classLines = function (d) {
5079 return this.classShapes(d) + this.generateClass(CLASS.lines, d.id);
5080 };
5081 c3_chart_internal_fn.classCircle = function (d) {
5082 return this.classShape(d) + this.generateClass(CLASS.circle, d.index);
5083 };
5084 c3_chart_internal_fn.classCircles = function (d) {
5085 return this.classShapes(d) + this.generateClass(CLASS.circles, d.id);
5086 };
5087 c3_chart_internal_fn.classBar = function (d) {
5088 return this.classShape(d) + this.generateClass(CLASS.bar, d.index);
5089 };
5090 c3_chart_internal_fn.classBars = function (d) {
5091 return this.classShapes(d) + this.generateClass(CLASS.bars, d.id);
5092 };
5093 c3_chart_internal_fn.classArc = function (d) {
5094 return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id);
5095 };
5096 c3_chart_internal_fn.classArcs = function (d) {
5097 return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id);
5098 };
5099 c3_chart_internal_fn.classArea = function (d) {
5100 return this.classShape(d) + this.generateClass(CLASS.area, d.id);
5101 };
5102 c3_chart_internal_fn.classAreas = function (d) {
5103 return this.classShapes(d) + this.generateClass(CLASS.areas, d.id);
5104 };
5105 c3_chart_internal_fn.classRegion = function (d, i) {
5106 return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d.class : '');
5107 };
5108 c3_chart_internal_fn.classEvent = function (d) {
5109 return this.generateClass(CLASS.eventRect, d.index);
5110 };
5111 c3_chart_internal_fn.classTarget = function (id) {
5112 var $$ = this;
5113 var additionalClassSuffix = $$.config.data_classes[id], additionalClass = '';
5114 if (additionalClassSuffix) {
5115 additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix;
5116 }
5117 return $$.generateClass(CLASS.target, id) + additionalClass;
5118 };
5119 c3_chart_internal_fn.classChartText = function (d) {
5120 return CLASS.chartText + this.classTarget(d.id);
5121 };
5122 c3_chart_internal_fn.classChartLine = function (d) {
5123 return CLASS.chartLine + this.classTarget(d.id);
5124 };
5125 c3_chart_internal_fn.classChartBar = function (d) {
5126 return CLASS.chartBar + this.classTarget(d.id);
5127 };
5128 c3_chart_internal_fn.classChartArc = function (d) {
5129 return CLASS.chartArc + this.classTarget(d.data.id);
5130 };
5131 c3_chart_internal_fn.getTargetSelectorSuffix = function (targetId) {
5132 return targetId || targetId === 0 ? '-' + (targetId.replace ? targetId.replace(/([^a-zA-Z0-9-_])/g, '-') : targetId) : '';
5133 };
5134 c3_chart_internal_fn.selectorTarget = function (id) {
5135 return '.' + CLASS.target + this.getTargetSelectorSuffix(id);
5136 };
5137 c3_chart_internal_fn.selectorTargets = function (ids) {
5138 var $$ = this;
5139 return ids.length ? ids.map(function (id) { return $$.selectorTarget(id); }) : null;
5140 };
5141 c3_chart_internal_fn.selectorLegend = function (id) {
5142 return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id);
5143 };
5144 c3_chart_internal_fn.selectorLegends = function (ids) {
5145 var $$ = this;
5146 return ids.length ? ids.map(function (id) { return $$.selectorLegend(id); }) : null;
5147 };
5148
5149 var isValue = c3_chart_internal_fn.isValue = function (v) {
5150 return v || v === 0;
5151 },
5152 isFunction = c3_chart_internal_fn.isFunction = function (o) {
5153 return typeof o === 'function';
5154 },
5155 isString = c3_chart_internal_fn.isString = function (o) {
5156 return typeof o === 'string';
5157 },
5158 isUndefined = c3_chart_internal_fn.isUndefined = function (v) {
5159 return typeof v === 'undefined';
5160 },
5161 isDefined = c3_chart_internal_fn.isDefined = function (v) {
5162 return typeof v !== 'undefined';
5163 },
5164 ceil10 = c3_chart_internal_fn.ceil10 = function (v) {
5165 return Math.ceil(v / 10) * 10;
5166 },
5167 asHalfPixel = c3_chart_internal_fn.asHalfPixel = function (n) {
5168 return Math.ceil(n) + 0.5;
5169 },
5170 diffDomain = c3_chart_internal_fn.diffDomain = function (d) {
5171 return d[1] - d[0];
5172 },
5173 isEmpty = c3_chart_internal_fn.isEmpty = function (o) {
5174 return !o || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0);
5175 },
5176 notEmpty = c3_chart_internal_fn.notEmpty = function (o) {
5177 return Object.keys(o).length > 0;
5178 },
5179 getOption = c3_chart_internal_fn.getOption = function (options, key, defaultValue) {
5180 return isDefined(options[key]) ? options[key] : defaultValue;
5181 },
5182 hasValue = c3_chart_internal_fn.hasValue = function (dict, value) {
5183 var found = false;
5184 Object.keys(dict).forEach(function (key) {
5185 if (dict[key] === value) { found = true; }
5186 });
5187 return found;
5188 },
5189 getPathBox = c3_chart_internal_fn.getPathBox = function (path) {
5190 var box = path.getBoundingClientRect(),
5191 items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)],
5192 minX = items[0].x, minY = Math.min(items[0].y, items[1].y);
5193 return {x: minX, y: minY, width: box.width, height: box.height};
5194 };
5195
5196 c3_chart_fn.focus = function (targetId) {
5197 var $$ = this.internal,
5198 candidates = $$.svg.selectAll($$.selectorTarget(targetId)),
5199 candidatesForNoneArc = candidates.filter($$.isNoneArc.bind($$)),
5200 candidatesForArc = candidates.filter($$.isArc.bind($$));
5201 function focus(targets) {
5202 $$.filterTargetsToShow(targets).transition().duration(100).style('opacity', 1);
5203 }
5204 this.revert();
5205 this.defocus();
5206 focus(candidatesForNoneArc.classed(CLASS.focused, true));
5207 focus(candidatesForArc);
5208 if ($$.hasArcType()) {
5209 $$.expandArc(targetId, true);
5210 }
5211 $$.toggleFocusLegend(targetId, true);
5212 };
5213
5214 c3_chart_fn.defocus = function (targetId) {
5215 var $$ = this.internal,
5216 candidates = $$.svg.selectAll($$.selectorTarget(targetId)),
5217 candidatesForNoneArc = candidates.filter($$.isNoneArc.bind($$)),
5218 candidatesForArc = candidates.filter($$.isArc.bind($$));
5219 function defocus(targets) {
5220 $$.filterTargetsToShow(targets).transition().duration(100).style('opacity', 0.3);
5221 }
5222 this.revert();
5223 defocus(candidatesForNoneArc.classed(CLASS.focused, false));
5224 defocus(candidatesForArc);
5225 if ($$.hasArcType()) {
5226 $$.unexpandArc(targetId);
5227 }
5228 $$.toggleFocusLegend(targetId, false);
5229 };
5230
5231 c3_chart_fn.revert = function (targetId) {
5232 var $$ = this.internal,
5233 candidates = $$.svg.selectAll($$.selectorTarget(targetId)),
5234 candidatesForNoneArc = candidates.filter($$.isNoneArc.bind($$)),
5235 candidatesForArc = candidates.filter($$.isArc.bind($$));
5236 function revert(targets) {
5237 $$.filterTargetsToShow(targets).transition().duration(100).style('opacity', 1);
5238 }
5239 revert(candidatesForNoneArc.classed(CLASS.focused, false));
5240 revert(candidatesForArc);
5241 if ($$.hasArcType()) {
5242 $$.unexpandArc(targetId);
5243 }
5244 $$.revertLegend();
5245 };
5246
5247 c3_chart_fn.show = function (targetIds, options) {
5248 var $$ = this.internal;
5249
5250 targetIds = $$.mapToTargetIds(targetIds);
5251 options = options || {};
5252
5253 $$.removeHiddenTargetIds(targetIds);
5254 $$.svg.selectAll($$.selectorTargets(targetIds))
5255 .transition()
5256 .style('opacity', 1);
5257
5258 if (options.withLegend) {
5259 $$.showLegend(targetIds);
5260 }
5261
5262 $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
5263 };
5264
5265 c3_chart_fn.hide = function (targetIds, options) {
5266 var $$ = this.internal;
5267
5268 targetIds = $$.mapToTargetIds(targetIds);
5269 options = options || {};
5270
5271 $$.addHiddenTargetIds(targetIds);
5272 $$.svg.selectAll($$.selectorTargets(targetIds))
5273 .transition()
5274 .style('opacity', 0);
5275
5276 if (options.withLegend) {
5277 $$.hideLegend(targetIds);
5278 }
5279
5280 $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
5281 };
5282
5283 c3_chart_fn.toggle = function (targetId) {
5284 var $$ = this.internal;
5285 $$.isTargetToShow(targetId) ? this.hide(targetId) : this.show(targetId);
5286 };
5287
5288 c3_chart_fn.zoom = function () {
5289 };
5290 c3_chart_fn.zoom.enable = function (enabled) {
5291 var $$ = this.internal;
5292 $$.config.zoom_enabled = enabled;
5293 $$.updateAndRedraw();
5294 };
5295 c3_chart_fn.unzoom = function () {
5296 var $$ = this.internal;
5297 $$.brush.clear().update();
5298 $$.redraw({withUpdateXDomain: true});
5299 };
5300
5301 c3_chart_fn.load = function (args) {
5302 var $$ = this.internal, config = $$.config;
5303 // update xs if specified
5304 if (args.xs) {
5305 $$.addXs(args.xs);
5306 }
5307 // update classes if exists
5308 if ('classes' in args) {
5309 Object.keys(args.classes).forEach(function (id) {
5310 config.data_classes[id] = args.classes[id];
5311 });
5312 }
5313 // update categories if exists
5314 if ('categories' in args && $$.isCategorized()) {
5315 config.axis_x_categories = args.categories;
5316 }
5317 // use cache if exists
5318 if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) {
5319 $$.load($$.getCaches(args.cacheIds), args.done);
5320 return;
5321 }
5322 // unload if needed
5323 if ('unload' in args) {
5324 // TODO: do not unload if target will load (included in url/rows/columns)
5325 $$.unload($$.mapToTargetIds((typeof args.unload === 'boolean' && args.unload) ? null : args.unload), function () {
5326 $$.loadFromArgs(args);
5327 });
5328 } else {
5329 $$.loadFromArgs(args);
5330 }
5331 };
5332
5333 c3_chart_fn.unload = function (args) {
5334 var $$ = this.internal;
5335 args = args || {};
5336 $$.unload($$.mapToTargetIds(args.ids), function () {
5337 $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
5338 if (args.done) { args.done(); }
5339 });
5340 };
5341
5342 c3_chart_fn.flow = function (args) {
5343 var $$ = this.internal,
5344 targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(),
5345 dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to;
5346
5347 if (args.json) {
5348 data = $$.convertJsonToData(args.json, args.keys);
5349 }
5350 else if (args.rows) {
5351 data = $$.convertRowsToData(args.rows);
5352 }
5353 else if (args.columns) {
5354 data = $$.convertColumnsToData(args.columns);
5355 }
5356 else {
5357 return;
5358 }
5359 targets = $$.convertDataToTargets(data, true);
5360
5361 // Update/Add data
5362 $$.data.targets.forEach(function (t) {
5363 var found = false, i, j;
5364 for (i = 0; i < targets.length; i++) {
5365 if (t.id === targets[i].id) {
5366 found = true;
5367
5368 if (t.values[t.values.length - 1]) {
5369 tail = t.values[t.values.length - 1].index + 1;
5370 }
5371 length = targets[i].values.length;
5372
5373 for (j = 0; j < length; j++) {
5374 targets[i].values[j].index = tail + j;
5375 if (!$$.isTimeSeries()) {
5376 targets[i].values[j].x = tail + j;
5377 }
5378 }
5379 t.values = t.values.concat(targets[i].values);
5380
5381 targets.splice(i, 1);
5382 break;
5383 }
5384 }
5385 if (!found) { notfoundIds.push(t.id); }
5386 });
5387
5388 // Append null for not found targets
5389 $$.data.targets.forEach(function (t) {
5390 var i, j;
5391 for (i = 0; i < notfoundIds.length; i++) {
5392 if (t.id === notfoundIds[i]) {
5393 tail = t.values[t.values.length - 1].index + 1;
5394 for (j = 0; j < length; j++) {
5395 t.values.push({
5396 id: t.id,
5397 index: tail + j,
5398 x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j,
5399 value: null
5400 });
5401 }
5402 }
5403 }
5404 });
5405
5406 // Generate null values for new target
5407 if ($$.data.targets.length) {
5408 targets.forEach(function (t) {
5409 var i, missing = [];
5410 for (i = $$.data.targets[0].values[0].index; i < tail; i++) {
5411 missing.push({
5412 id: t.id,
5413 index: i,
5414 x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i,
5415 value: null
5416 });
5417 }
5418 t.values.forEach(function (v) {
5419 v.index += tail;
5420 if (!$$.isTimeSeries()) {
5421 v.x += tail;
5422 }
5423 });
5424 t.values = missing.concat(t.values);
5425 });
5426 }
5427 $$.data.targets = $$.data.targets.concat(targets); // add remained
5428
5429 // check data count because behavior needs to change when it's only one
5430 dataCount = $$.getMaxDataCount();
5431 baseTarget = $$.data.targets[0];
5432 baseValue = baseTarget.values[0];
5433
5434 // Update length to flow if needed
5435 if (isDefined(args.to)) {
5436 length = 0;
5437 to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to;
5438 baseTarget.values.forEach(function (v) {
5439 if (v.x < to) { length++; }
5440 });
5441 } else if (isDefined(args.length)) {
5442 length = args.length;
5443 }
5444
5445 // If only one data, update the domain to flow from left edge of the chart
5446 if (!orgDataCount) {
5447 if ($$.isTimeSeries()) {
5448 if (baseTarget.values.length > 1) {
5449 diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x;
5450 } else {
5451 diff = baseValue.x - $$.getXDomain($$.data.targets)[0];
5452 }
5453 } else {
5454 diff = 1;
5455 }
5456 domain = [baseValue.x - diff, baseValue.x];
5457 $$.updateXDomain(null, true, true, domain);
5458 } else if (orgDataCount === 1) {
5459 if ($$.isTimeSeries()) {
5460 diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2;
5461 domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)];
5462 $$.updateXDomain(null, true, true, domain);
5463 }
5464 }
5465
5466 // Set targets
5467 $$.updateTargets($$.data.targets);
5468
5469 // Redraw with new targets
5470 $$.redraw({
5471 flow: {
5472 index: baseValue.index,
5473 length: length,
5474 duration: isValue(args.duration) ? args.duration : $$.config.transition_duration,
5475 done: args.done,
5476 orgDataCount: orgDataCount,
5477 },
5478 withLegend: true,
5479 withTransition: orgDataCount > 1,
5480 });
5481 };
5482
5483 c3_chart_internal_fn.generateFlow = function (args) {
5484 var $$ = this, config = $$.config, d3 = $$.d3;
5485
5486 return function () {
5487 var targets = args.targets,
5488 flow = args.flow,
5489 drawBar = args.drawBar,
5490 drawLine = args.drawLine,
5491 drawArea = args.drawArea,
5492 cx = args.cx,
5493 cy = args.cy,
5494 xv = args.xv,
5495 xForText = args.xForText,
5496 yForText = args.yForText,
5497 duration = args.duration;
5498
5499 var translateX, scaleX = 1, transform,
5500 flowIndex = flow.index,
5501 flowLength = flow.length,
5502 flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex),
5503 flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength),
5504 orgDomain = $$.x.domain(), domain,
5505 durationForFlow = flow.duration || duration,
5506 done = flow.done || function () {},
5507 wait = $$.generateWait();
5508
5509 var xgrid = $$.xgrid || d3.selectAll([]),
5510 xgridLines = $$.xgridLines || d3.selectAll([]),
5511 mainRegion = $$.mainRegion || d3.selectAll([]),
5512 mainText = $$.mainText || d3.selectAll([]),
5513 mainBar = $$.mainBar || d3.selectAll([]),
5514 mainLine = $$.mainLine || d3.selectAll([]),
5515 mainArea = $$.mainArea || d3.selectAll([]),
5516 mainCircle = $$.mainCircle || d3.selectAll([]);
5517
5518 // remove head data after rendered
5519 $$.data.targets.forEach(function (d) {
5520 d.values.splice(0, flowLength);
5521 });
5522
5523 // update x domain to generate axis elements for flow
5524 domain = $$.updateXDomain(targets, true, true);
5525 // update elements related to x scale
5526 if ($$.updateXGrid) { $$.updateXGrid(true); }
5527
5528 // generate transform to flow
5529 if (!flow.orgDataCount) { // if empty
5530 if ($$.data.targets[0].values.length !== 1) {
5531 translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
5532 } else {
5533 if ($$.isTimeSeries()) {
5534 flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0);
5535 flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1);
5536 translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
5537 } else {
5538 translateX = diffDomain(domain) / 2;
5539 }
5540 }
5541 } else if (flow.orgDataCount === 1 || flowStart.x === flowEnd.x) {
5542 translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
5543 } else {
5544 if ($$.isTimeSeries()) {
5545 translateX = ($$.x(orgDomain[0]) - $$.x(domain[0]));
5546 } else {
5547 translateX = ($$.x(flowStart.x) - $$.x(flowEnd.x));
5548 }
5549 }
5550 scaleX = (diffDomain(orgDomain) / diffDomain(domain));
5551 transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)';
5552
5553 d3.transition().ease('linear').duration(durationForFlow).each(function () {
5554 wait.add($$.axes.x.transition().call($$.xAxis));
5555 wait.add(mainBar.transition().attr('transform', transform));
5556 wait.add(mainLine.transition().attr('transform', transform));
5557 wait.add(mainArea.transition().attr('transform', transform));
5558 wait.add(mainCircle.transition().attr('transform', transform));
5559 wait.add(mainText.transition().attr('transform', transform));
5560 wait.add(mainRegion.filter($$.isRegionOnX).transition().attr('transform', transform));
5561 wait.add(xgrid.transition().attr('transform', transform));
5562 wait.add(xgridLines.transition().attr('transform', transform));
5563 })
5564 .call(wait, function () {
5565 var i, shapes = [], texts = [], eventRects = [];
5566
5567 // remove flowed elements
5568 if (flowLength) {
5569 for (i = 0; i < flowLength; i++) {
5570 shapes.push('.' + CLASS.shape + '-' + (flowIndex + i));
5571 texts.push('.' + CLASS.text + '-' + (flowIndex + i));
5572 eventRects.push('.' + CLASS.eventRect + '-' + (flowIndex + i));
5573 }
5574 $$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove();
5575 $$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove();
5576 $$.svg.selectAll('.' + CLASS.eventRects).selectAll(eventRects).remove();
5577 $$.svg.select('.' + CLASS.xgrid).remove();
5578 }
5579
5580 // draw again for removing flowed elements and reverting attr
5581 xgrid
5582 .attr('transform', null)
5583 .attr($$.xgridAttr);
5584 xgridLines
5585 .attr('transform', null);
5586 xgridLines.select('line')
5587 .attr("x1", config.axis_rotated ? 0 : xv)
5588 .attr("x2", config.axis_rotated ? $$.width : xv);
5589 xgridLines.select('text')
5590 .attr("x", config.axis_rotated ? $$.width : 0)
5591 .attr("y", xv);
5592 mainBar
5593 .attr('transform', null)
5594 .attr("d", drawBar);
5595 mainLine
5596 .attr('transform', null)
5597 .attr("d", drawLine);
5598 mainArea
5599 .attr('transform', null)
5600 .attr("d", drawArea);
5601 mainCircle
5602 .attr('transform', null)
5603 .attr("cx", cx)
5604 .attr("cy", cy);
5605 mainText
5606 .attr('transform', null)
5607 .attr('x', xForText)
5608 .attr('y', yForText)
5609 .style('fill-opacity', $$.opacityForText.bind($$));
5610 mainRegion
5611 .attr('transform', null);
5612 mainRegion.select('rect').filter($$.isRegionOnX)
5613 .attr("x", $$.regionX.bind($$))
5614 .attr("width", $$.regionWidth.bind($$));
5615 $$.updateEventRect();
5616
5617 // callback for end of flow
5618 done();
5619 });
5620 };
5621 };
5622
5623 c3_chart_fn.selected = function (targetId) {
5624 var $$ = this.internal, d3 = $$.d3;
5625 return d3.merge(
5626 $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape)
5627 .filter(function () { return d3.select(this).classed(CLASS.SELECTED); })
5628 .map(function (d) { return d.map(function (d) { var data = d.__data__; return data.data ? data.data : data; }); })
5629 );
5630 };
5631 c3_chart_fn.select = function (ids, indices, resetOther) {
5632 var $$ = this.internal, d3 = $$.d3, config = $$.config;
5633 if (! config.data_selection_enabled) { return; }
5634 $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) {
5635 var shape = d3.select(this), id = d.data ? d.data.id : d.id,
5636 toggle = $$.getToggle(this).bind($$),
5637 isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0,
5638 isTargetIndex = !indices || indices.indexOf(i) >= 0,
5639 isSelected = shape.classed(CLASS.SELECTED);
5640 // line/area selection not supported yet
5641 if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) {
5642 return;
5643 }
5644 if (isTargetId && isTargetIndex) {
5645 if (config.data_selection_isselectable(d) && !isSelected) {
5646 toggle(true, shape.classed(CLASS.SELECTED, true), d, i);
5647 }
5648 } else if (isDefined(resetOther) && resetOther) {
5649 if (isSelected) {
5650 toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
5651 }
5652 }
5653 });
5654 };
5655 c3_chart_fn.unselect = function (ids, indices) {
5656 var $$ = this.internal, d3 = $$.d3, config = $$.config;
5657 if (! config.data_selection_enabled) { return; }
5658 $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) {
5659 var shape = d3.select(this), id = d.data ? d.data.id : d.id,
5660 toggle = $$.getToggle(this).bind($$),
5661 isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0,
5662 isTargetIndex = !indices || indices.indexOf(i) >= 0,
5663 isSelected = shape.classed(CLASS.SELECTED);
5664 // line/area selection not supported yet
5665 if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) {
5666 return;
5667 }
5668 if (isTargetId && isTargetIndex) {
5669 if (config.data_selection_isselectable(d)) {
5670 if (isSelected) {
5671 toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
5672 }
5673 }
5674 }
5675 });
5676 };
5677
5678 c3_chart_fn.transform = function (type, targetIds) {
5679 var $$ = this.internal,
5680 options = ['pie', 'donut'].indexOf(type) >= 0 ? {withTransform: true} : null;
5681 $$.transformTo(targetIds, type, options);
5682 };
5683
5684 c3_chart_internal_fn.transformTo = function (targetIds, type, optionsForRedraw) {
5685 var $$ = this,
5686 withTransitionForAxis = !$$.hasArcType(),
5687 options = optionsForRedraw || {withTransitionForAxis: withTransitionForAxis};
5688 options.withTransitionForTransform = false;
5689 $$.transiting = false;
5690 $$.setTargetType(targetIds, type);
5691 $$.updateAndRedraw(options);
5692 };
5693
5694 c3_chart_fn.groups = function (groups) {
5695 var $$ = this.internal, config = $$.config;
5696 if (isUndefined(groups)) { return config.data_groups; }
5697 config.data_groups = groups;
5698 $$.redraw();
5699 return config.data_groups;
5700 };
5701
5702 c3_chart_fn.xgrids = function (grids) {
5703 var $$ = this.internal, config = $$.config;
5704 if (! grids) { return config.grid_x_lines; }
5705 config.grid_x_lines = grids;
5706 $$.redraw();
5707 return config.grid_x_lines;
5708 };
5709 c3_chart_fn.xgrids.add = function (grids) {
5710 var $$ = this.internal;
5711 return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : []));
5712 };
5713 c3_chart_fn.xgrids.remove = function (params) { // TODO: multiple
5714 var $$ = this.internal;
5715 $$.removeGridLines(params, true);
5716 };
5717
5718 c3_chart_fn.ygrids = function (grids) {
5719 var $$ = this.internal, config = $$.config;
5720 if (! grids) { return config.grid_y_lines; }
5721 config.grid_y_lines = grids;
5722 $$.redraw();
5723 return config.grid_y_lines;
5724 };
5725 c3_chart_fn.ygrids.add = function (grids) {
5726 var $$ = this.internal;
5727 return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : []));
5728 };
5729 c3_chart_fn.ygrids.remove = function (params) { // TODO: multiple
5730 var $$ = this.internal;
5731 $$.removeGridLines(params, false);
5732 };
5733
5734 c3_chart_fn.regions = function (regions) {
5735 var $$ = this.internal, config = $$.config;
5736 if (!regions) { return config.regions; }
5737 config.regions = regions;
5738 $$.redraw();
5739 return config.regions;
5740 };
5741 c3_chart_fn.regions.add = function (regions) {
5742 var $$ = this.internal, config = $$.config;
5743 if (!regions) { return config.regions; }
5744 config.regions = config.regions.concat(regions);
5745 $$.redraw();
5746 return config.regions;
5747 };
5748 c3_chart_fn.regions.remove = function (options) {
5749 var $$ = this.internal, config = $$.config,
5750 duration, classes, regions;
5751
5752 options = options || {};
5753 duration = $$.getOption(options, "duration", config.transition_duration);
5754 classes = $$.getOption(options, "classes", [CLASS.region]);
5755
5756 regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) { return '.' + c; }));
5757 (duration ? regions.transition().duration(duration) : regions)
5758 .style('opacity', 0)
5759 .remove();
5760
5761 config.regions = config.regions.filter(function (region) {
5762 var found = false;
5763 if (!region.class) {
5764 return true;
5765 }
5766 region.class.split(' ').forEach(function (c) {
5767 if (classes.indexOf(c) >= 0) { found = true; }
5768 });
5769 return !found;
5770 });
5771
5772 return config.regions;
5773 };
5774
5775 c3_chart_fn.data = function () {};
5776 c3_chart_fn.data.get = function (targetId) {
5777 var target = this.data.getAsTarget(targetId);
5778 return isDefined(target) ? target.values.map(function (d) { return d.value; }) : undefined;
5779 };
5780 c3_chart_fn.data.getAsTarget = function (targetId) {
5781 var targets = this.data.targets.filter(function (t) { return t.id === targetId; });
5782 return targets.length > 0 ? targets[0] : undefined;
5783 };
5784 c3_chart_fn.data.names = function (names) {
5785 var $$ = this.internal, config = $$.config;
5786 if (!arguments.length) { return config.data_names; }
5787 Object.keys(names).forEach(function (id) {
5788 config.data_names[id] = names[id];
5789 });
5790 $$.redraw({withLegend: true});
5791 return config.data_names;
5792 };
5793 c3_chart_fn.data.colors = function (colors) {
5794 var $$ = this.internal, config = $$.config;
5795 if (!arguments.length) { return config.data_colors; }
5796 Object.keys(colors).forEach(function (id) {
5797 config.data_colors[id] = colors[id];
5798 });
5799 $$.redraw({withLegend: true});
5800 return config.data_colors;
5801 };
5802
5803 c3_chart_fn.category = function (i, category) {
5804 var $$ = this.internal, config = $$.config;
5805 if (arguments.length > 1) {
5806 config.axis_x_categories[i] = category;
5807 $$.redraw();
5808 }
5809 return config.axis_x_categories[i];
5810 };
5811 c3_chart_fn.categories = function (categories) {
5812 var $$ = this.internal, config = $$.config;
5813 if (!arguments.length) { return config.axis_x_categories; }
5814 config.axis_x_categories = categories;
5815 $$.redraw();
5816 return config.axis_x_categories;
5817 };
5818
5819 // TODO: fix
5820 c3_chart_fn.color = function (id) {
5821 var $$ = this.internal;
5822 return $$.color(id); // more patterns
5823 };
5824
5825 c3_chart_fn.x = function (x) {
5826 var $$ = this.internal;
5827 if (arguments.length) {
5828 $$.updateTargetX($$.data.targets, x);
5829 $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true});
5830 }
5831 return $$.data.xs;
5832 };
5833 c3_chart_fn.xs = function (xs) {
5834 var $$ = this.internal;
5835 if (arguments.length) {
5836 $$.updateTargetXs($$.data.targets, xs);
5837 $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true});
5838 }
5839 return $$.data.xs;
5840 };
5841
5842 c3_chart_fn.axis = function () {};
5843 c3_chart_fn.axis.labels = function (labels) {
5844 var $$ = this.internal;
5845 if (arguments.length) {
5846 Object.keys(labels).forEach(function (axisId) {
5847 $$.setAxisLabelText(axisId, labels[axisId]);
5848 });
5849 $$.updateAxisLabels();
5850 }
5851 // TODO: return some values?
5852 };
5853 c3_chart_fn.axis.max = function (max) {
5854 var $$ = this.internal, config = $$.config;
5855 if (arguments.length) {
5856 if (typeof max === 'object') {
5857 if (isValue(max.x)) { config.axis_x_max = max.x; }
5858 if (isValue(max.y)) { config.axis_y_max = max.y; }
5859 if (isValue(max.y2)) { config.axis_y2_max = max.y2; }
5860 } else {
5861 config.axis_y_max = config.axis_y2_max = max;
5862 }
5863 $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true});
5864 }
5865 };
5866 c3_chart_fn.axis.min = function (min) {
5867 var $$ = this.internal, config = $$.config;
5868 if (arguments.length) {
5869 if (typeof min === 'object') {
5870 if (isValue(min.x)) { config.axis_x_min = min.x; }
5871 if (isValue(min.y)) { config.axis_y_min = min.y; }
5872 if (isValue(min.y2)) { config.axis_y2_min = min.y2; }
5873 } else {
5874 config.axis_y_min = config.axis_y2_min = min;
5875 }
5876 $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true});
5877 }
5878 };
5879 c3_chart_fn.axis.range = function (range) {
5880 if (arguments.length) {
5881 if (isDefined(range.max)) { this.axis.max(range.max); }
5882 if (isDefined(range.min)) { this.axis.min(range.min); }
5883 }
5884 };
5885
5886 c3_chart_fn.legend = function () {};
5887 c3_chart_fn.legend.show = function (targetIds) {
5888 var $$ = this.internal;
5889 $$.showLegend($$.mapToTargetIds(targetIds));
5890 $$.updateAndRedraw({withLegend: true});
5891 };
5892 c3_chart_fn.legend.hide = function (targetIds) {
5893 var $$ = this.internal;
5894 $$.hideLegend($$.mapToTargetIds(targetIds));
5895 $$.updateAndRedraw({withLegend: true});
5896 };
5897
5898 c3_chart_fn.resize = function (size) {
5899 var $$ = this.internal, config = $$.config;
5900 config.size_width = size ? size.width : null;
5901 config.size_height = size ? size.height : null;
5902 this.flush();
5903 };
5904
5905 c3_chart_fn.flush = function () {
5906 var $$ = this.internal;
5907 $$.updateAndRedraw({withLegend: true, withTransition: false, withTransitionForTransform: false});
5908 };
5909
5910 c3_chart_fn.destroy = function () {
5911 var $$ = this.internal;
5912 $$.data.targets = undefined;
5913 $$.data.xs = {};
5914 $$.selectChart.classed('c3', false).html("");
5915 window.onresize = null;
5916 };
5917
5918 // Features:
5919 // 1. category axis
5920 // 2. ceil values of translate/x/y to int for half pixel antialiasing
5921 function c3_axis(d3, isCategory) {
5922 var scale = d3.scale.linear(), orient = "bottom", innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickValues = null, tickFormat, tickArguments;
5923
5924 var tickOffset = 0, tickCulling = true, tickCentered;
5925
5926 function axisX(selection, x) {
5927 selection.attr("transform", function (d) {
5928 return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)";
5929 });
5930 }
5931 function axisY(selection, y) {
5932 selection.attr("transform", function (d) {
5933 return "translate(0," + Math.ceil(y(d)) + ")";
5934 });
5935 }
5936 function scaleExtent(domain) {
5937 var start = domain[0], stop = domain[domain.length - 1];
5938 return start < stop ? [ start, stop ] : [ stop, start ];
5939 }
5940 function generateTicks(scale) {
5941 var i, domain, ticks = [];
5942 if (scale.ticks) {
5943 return scale.ticks.apply(scale, tickArguments);
5944 }
5945 domain = scale.domain();
5946 for (i = Math.ceil(domain[0]); i < domain[1]; i++) {
5947 ticks.push(i);
5948 }
5949 if (ticks.length > 0 && ticks[0] > 0) {
5950 ticks.unshift(ticks[0] - (ticks[1] - ticks[0]));
5951 }
5952 return ticks;
5953 }
5954 function copyScale() {
5955 var newScale = scale.copy(), domain;
5956 if (isCategory) {
5957 domain = scale.domain();
5958 newScale.domain([domain[0], domain[1] - 1]);
5959 }
5960 return newScale;
5961 }
5962 function textFormatted(v) {
5963 return tickFormat ? tickFormat(v) : v;
5964 }
5965 function axis(g) {
5966 g.each(function () {
5967 var g = d3.select(this);
5968 var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = copyScale();
5969
5970 var ticks = tickValues ? tickValues : generateTicks(scale1),
5971 tick = g.selectAll(".tick").data(ticks, scale1),
5972 tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6),
5973 // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks.
5974 tickExit = tick.exit().remove(),
5975 tickUpdate = d3.transition(tick).style("opacity", 1),
5976 tickTransform, tickX;
5977
5978 var range = scale.rangeExtent ? scale.rangeExtent() : scaleExtent(scale.range()),
5979 path = g.selectAll(".domain").data([ 0 ]),
5980 pathUpdate = (path.enter().append("path").attr("class", "domain"), d3.transition(path));
5981 tickEnter.append("line");
5982 tickEnter.append("text");
5983
5984 var lineEnter = tickEnter.select("line"),
5985 lineUpdate = tickUpdate.select("line"),
5986 text = tick.select("text").text(textFormatted),
5987 textEnter = tickEnter.select("text"),
5988 textUpdate = tickUpdate.select("text");
5989
5990 if (isCategory) {
5991 tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2);
5992 tickX = tickCentered ? 0 : tickOffset;
5993 } else {
5994 tickOffset = tickX = 0;
5995 }
5996
5997 function tickSize(d) {
5998 var tickPosition = scale(d) + tickOffset;
5999 return range[0] < tickPosition && tickPosition < range[1] ? innerTickSize : 0;
6000 }
6001
6002 switch (orient) {
6003 case "bottom":
6004 {
6005 tickTransform = axisX;
6006 lineEnter.attr("y2", innerTickSize);
6007 textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding);
6008 lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize);
6009 textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding);
6010 text.attr("dy", ".71em").style("text-anchor", "middle");
6011 pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
6012 break;
6013 }
6014 case "top":
6015 {
6016 tickTransform = axisX;
6017 lineEnter.attr("y2", -innerTickSize);
6018 textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
6019 lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);
6020 textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
6021 text.attr("dy", "0em").style("text-anchor", "middle");
6022 pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);
6023 break;
6024 }
6025 case "left":
6026 {
6027 tickTransform = axisY;
6028 lineEnter.attr("x2", -innerTickSize);
6029 textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding));
6030 lineUpdate.attr("x2", -innerTickSize).attr("y2", 0);
6031 textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", tickOffset);
6032 text.attr("dy", ".32em").style("text-anchor", "end");
6033 pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);
6034 break;
6035 }
6036 case "right":
6037 {
6038 tickTransform = axisY;
6039 lineEnter.attr("x2", innerTickSize);
6040 textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding);
6041 lineUpdate.attr("x2", innerTickSize).attr("y2", 0);
6042 textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0);
6043 text.attr("dy", ".32em").style("text-anchor", "start");
6044 pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);
6045 break;
6046 }
6047 }
6048 if (scale1.rangeBand) {
6049 var x = scale1, dx = x.rangeBand() / 2;
6050 scale0 = scale1 = function (d) {
6051 return x(d) + dx;
6052 };
6053 } else if (scale0.rangeBand) {
6054 scale0 = scale1;
6055 } else {
6056 tickExit.call(tickTransform, scale1);
6057 }
6058 tickEnter.call(tickTransform, scale0);
6059 tickUpdate.call(tickTransform, scale1);
6060 });
6061 }
6062 axis.scale = function (x) {
6063 if (!arguments.length) { return scale; }
6064 scale = x;
6065 return axis;
6066 };
6067 axis.orient = function (x) {
6068 if (!arguments.length) { return orient; }
6069 orient = x in {top: 1, right: 1, bottom: 1, left: 1} ? x + "" : "bottom";
6070 return axis;
6071 };
6072 axis.tickFormat = function (format) {
6073 if (!arguments.length) { return tickFormat; }
6074 tickFormat = format;
6075 return axis;
6076 };
6077 axis.tickCentered = function (isCentered) {
6078 if (!arguments.length) { return tickCentered; }
6079 tickCentered = isCentered;
6080 return axis;
6081 };
6082 axis.tickOffset = function () { // This will be overwritten when normal x axis
6083 return tickOffset;
6084 };
6085 axis.ticks = function () {
6086 if (!arguments.length) { return tickArguments; }
6087 tickArguments = arguments;
6088 return axis;
6089 };
6090 axis.tickCulling = function (culling) {
6091 if (!arguments.length) { return tickCulling; }
6092 tickCulling = culling;
6093 return axis;
6094 };
6095 axis.tickValues = function (x) {
6096 if (typeof x === 'function') {
6097 tickValues = function () {
6098 return x(scale.domain());
6099 };
6100 }
6101 else {
6102 if (!arguments.length) { return tickValues; }
6103 tickValues = x;
6104 }
6105 return axis;
6106 };
6107 return axis;
6108 }
6109
6110 if (typeof define === 'function' && define.amd) {
6111 define("c3", ["d3"], c3);
6112 } else if ('undefined' !== typeof exports && 'undefined' !== typeof module) {
6113 module.exports = c3;
6114 } else {
6115 window.c3 = c3;
6116 }
6117
6118})(window);