Project

General

Profile

1
(function (window) {
2
    'use strict';
3

    
4
    /*global define, module, exports, require */
5

    
6
    var c3 = { version: "0.4.10" };
7

    
8
    var c3_chart_fn,
9
        c3_chart_internal_fn,
10
        c3_chart_internal_axis_fn;
11

    
12
    function API(owner) {
13
        this.owner = owner;
14
    }
15

    
16
    function inherit(base, derived) {
17

    
18
        if (Object.create) {
19
            derived.prototype = Object.create(base.prototype);
20
        } else {
21
            var f = function f() {};
22
            f.prototype = base.prototype;
23
            derived.prototype = new f();
24
        }
25

    
26
        derived.prototype.constructor = derived;
27

    
28
        return derived;
29
    }
30

    
31
    function Chart(config) {
32
        var $$ = this.internal = new ChartInternal(this);
33
        $$.loadConfig(config);
34
        $$.init();
35

    
36
        // bind "this" to nested API
37
        (function bindThis(fn, target, argThis) {
38
            Object.keys(fn).forEach(function (key) {
39
                target[key] = fn[key].bind(argThis);
40
                if (Object.keys(fn[key]).length > 0) {
41
                    bindThis(fn[key], target[key], argThis);
42
                }
43
            });
44
        })(c3_chart_fn, this, this);
45
    }
46

    
47
    function ChartInternal(api) {
48
        var $$ = this;
49
        $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require("d3") : undefined;
50
        $$.api = api;
51
        $$.config = $$.getDefaultConfig();
52
        $$.data = {};
53
        $$.cache = {};
54
        $$.axes = {};
55
    }
56

    
57
    c3.generate = function (config) {
58
        return new Chart(config);
59
    };
60

    
61
    c3.chart = {
62
        fn: Chart.prototype,
63
        internal: {
64
            fn: ChartInternal.prototype,
65
            axis: {
66
                fn: Axis.prototype
67
            }
68
        }
69
    };
70
    c3_chart_fn = c3.chart.fn;
71
    c3_chart_internal_fn = c3.chart.internal.fn;
72
    c3_chart_internal_axis_fn = c3.chart.internal.axis.fn;
73

    
74
    c3_chart_internal_fn.init = function () {
75
        var $$ = this, config = $$.config;
76

    
77
        $$.initParams();
78

    
79
        if (config.data_url) {
80
            $$.convertUrlToData(config.data_url, config.data_mimeType, config.data_keys, $$.initWithData);
81
        }
82
        else if (config.data_json) {
83
            $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys));
84
        }
85
        else if (config.data_rows) {
86
            $$.initWithData($$.convertRowsToData(config.data_rows));
87
        }
88
        else if (config.data_columns) {
89
            $$.initWithData($$.convertColumnsToData(config.data_columns));
90
        }
91
        else {
92
            throw Error('url or json or rows or columns is required.');
93
        }
94
    };
95

    
96
    c3_chart_internal_fn.initParams = function () {
97
        var $$ = this, d3 = $$.d3, config = $$.config;
98

    
99
        // MEMO: clipId needs to be unique because it conflicts when multiple charts exist
100
        $$.clipId = "c3-" + (+new Date()) + '-clip',
101
        $$.clipIdForXAxis = $$.clipId + '-xaxis',
102
        $$.clipIdForYAxis = $$.clipId + '-yaxis',
103
        $$.clipIdForGrid = $$.clipId + '-grid',
104
        $$.clipIdForSubchart = $$.clipId + '-subchart',
105
        $$.clipPath = $$.getClipPath($$.clipId),
106
        $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis),
107
        $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis);
108
        $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid),
109
        $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart),
110

    
111
        $$.dragStart = null;
112
        $$.dragging = false;
113
        $$.flowing = false;
114
        $$.cancelClick = false;
115
        $$.mouseover = false;
116
        $$.transiting = false;
117

    
118
        $$.color = $$.generateColor();
119
        $$.levelColor = $$.generateLevelColor();
120

    
121
        $$.dataTimeFormat = config.data_xLocaltime ? d3.time.format : d3.time.format.utc;
122
        $$.axisTimeFormat = config.axis_x_localtime ? d3.time.format : d3.time.format.utc;
123
        $$.defaultAxisTimeFormat = $$.axisTimeFormat.multi([
124
            [".%L", function (d) { return d.getMilliseconds(); }],
125
            [":%S", function (d) { return d.getSeconds(); }],
126
            ["%I:%M", function (d) { return d.getMinutes(); }],
127
            ["%I %p", function (d) { return d.getHours(); }],
128
            ["%-m/%-d", function (d) { return d.getDay() && d.getDate() !== 1; }],
129
            ["%-m/%-d", function (d) { return d.getDate() !== 1; }],
130
            ["%-m/%-d", function (d) { return d.getMonth(); }],
131
            ["%Y/%-m/%-d", function () { return true; }]
132
        ]);
133

    
134
        $$.hiddenTargetIds = [];
135
        $$.hiddenLegendIds = [];
136
        $$.focusedTargetIds = [];
137
        $$.defocusedTargetIds = [];
138

    
139
        $$.xOrient = config.axis_rotated ? "left" : "bottom";
140
        $$.yOrient = config.axis_rotated ? (config.axis_y_inner ? "top" : "bottom") : (config.axis_y_inner ? "right" : "left");
141
        $$.y2Orient = config.axis_rotated ? (config.axis_y2_inner ? "bottom" : "top") : (config.axis_y2_inner ? "left" : "right");
142
        $$.subXOrient = config.axis_rotated ? "left" : "bottom";
143

    
144
        $$.isLegendRight = config.legend_position === 'right';
145
        $$.isLegendInset = config.legend_position === 'inset';
146
        $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right';
147
        $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left';
148
        $$.legendStep = 0;
149
        $$.legendItemWidth = 0;
150
        $$.legendItemHeight = 0;
151

    
152
        $$.currentMaxTickWidths = {
153
            x: 0,
154
            y: 0,
155
            y2: 0
156
        };
157

    
158
        $$.rotated_padding_left = 30;
159
        $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30;
160
        $$.rotated_padding_top = 5;
161

    
162
        $$.withoutFadeIn = {};
163

    
164
        $$.intervalForObserveInserted = undefined;
165

    
166
        $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js
167
    };
168

    
169
    c3_chart_internal_fn.initChartElements = function () {
170
        if (this.initBar) { this.initBar(); }
171
        if (this.initLine) { this.initLine(); }
172
        if (this.initArc) { this.initArc(); }
173
        if (this.initGauge) { this.initGauge(); }
174
        if (this.initText) { this.initText(); }
175
    };
176

    
177
    c3_chart_internal_fn.initWithData = function (data) {
178
        var $$ = this, d3 = $$.d3, config = $$.config;
179
        var defs, main, binding = true;
180

    
181
        $$.axis = new Axis($$);
182

    
183
        if ($$.initPie) { $$.initPie(); }
184
        if ($$.initBrush) { $$.initBrush(); }
185
        if ($$.initZoom) { $$.initZoom(); }
186

    
187
        if (!config.bindto) {
188
            $$.selectChart = d3.selectAll([]);
189
        }
190
        else if (typeof config.bindto.node === 'function') {
191
            $$.selectChart = config.bindto;
192
        }
193
        else {
194
            $$.selectChart = d3.select(config.bindto);
195
        }
196
        if ($$.selectChart.empty()) {
197
            $$.selectChart = d3.select(document.createElement('div')).style('opacity', 0);
198
            $$.observeInserted($$.selectChart);
199
            binding = false;
200
        }
201
        $$.selectChart.html("").classed("c3", true);
202

    
203
        // Init data as targets
204
        $$.data.xs = {};
205
        $$.data.targets = $$.convertDataToTargets(data);
206

    
207
        if (config.data_filter) {
208
            $$.data.targets = $$.data.targets.filter(config.data_filter);
209
        }
210

    
211
        // Set targets to hide if needed
212
        if (config.data_hide) {
213
            $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide);
214
        }
215
        if (config.legend_hide) {
216
            $$.addHiddenLegendIds(config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide);
217
        }
218

    
219
        // when gauge, hide legend // TODO: fix
220
        if ($$.hasType('gauge')) {
221
            config.legend_show = false;
222
        }
223

    
224
        // Init sizes and scales
225
        $$.updateSizes();
226
        $$.updateScales();
227

    
228
        // Set domains for each scale
229
        $$.x.domain(d3.extent($$.getXDomain($$.data.targets)));
230
        $$.y.domain($$.getYDomain($$.data.targets, 'y'));
231
        $$.y2.domain($$.getYDomain($$.data.targets, 'y2'));
232
        $$.subX.domain($$.x.domain());
233
        $$.subY.domain($$.y.domain());
234
        $$.subY2.domain($$.y2.domain());
235

    
236
        // Save original x domain for zoom update
237
        $$.orgXDomain = $$.x.domain();
238

    
239
        // Set initialized scales to brush and zoom
240
        if ($$.brush) { $$.brush.scale($$.subX); }
241
        if (config.zoom_enabled) { $$.zoom.scale($$.x); }
242

    
243
        /*-- Basic Elements --*/
244

    
245
        // Define svgs
246
        $$.svg = $$.selectChart.append("svg")
247
            .style("overflow", "hidden")
248
            .on('mouseenter', function () { return config.onmouseover.call($$); })
249
            .on('mouseleave', function () { return config.onmouseout.call($$); });
250

    
251
        // Define defs
252
        defs = $$.svg.append("defs");
253
        $$.clipChart = $$.appendClip(defs, $$.clipId);
254
        $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis);
255
        $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis);
256
        $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid);
257
        $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart);
258
        $$.updateSvgSize();
259

    
260
        // Define regions
261
        main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main'));
262

    
263
        if ($$.initSubchart) { $$.initSubchart(); }
264
        if ($$.initTooltip) { $$.initTooltip(); }
265
        if ($$.initLegend) { $$.initLegend(); }
266

    
267
        /*-- Main Region --*/
268

    
269
        // text when empty
270
        main.append("text")
271
            .attr("class", CLASS.text + ' ' + CLASS.empty)
272
            .attr("text-anchor", "middle") // horizontal centering of text at x position in all browsers.
273
            .attr("dominant-baseline", "middle"); // vertical centering of text at y position in all browsers, except IE.
274

    
275
        // Regions
276
        $$.initRegion();
277

    
278
        // Grids
279
        $$.initGrid();
280

    
281
        // Define g for chart area
282
        main.append('g')
283
            .attr("clip-path", $$.clipPath)
284
            .attr('class', CLASS.chart);
285

    
286
        // Grid lines
287
        if (config.grid_lines_front) { $$.initGridLines(); }
288

    
289
        // Cover whole with rects for events
290
        $$.initEventRect();
291

    
292
        // Define g for chart
293
        $$.initChartElements();
294

    
295
        // if zoom privileged, insert rect to forefront
296
        // TODO: is this needed?
297
        main.insert('rect', config.zoom_privileged ? null : 'g.' + CLASS.regions)
298
            .attr('class', CLASS.zoomRect)
299
            .attr('width', $$.width)
300
            .attr('height', $$.height)
301
            .style('opacity', 0)
302
            .on("dblclick.zoom", null);
303

    
304
        // Set default extent if defined
305
        if (config.axis_x_extent) { $$.brush.extent($$.getDefaultExtent()); }
306

    
307
        // Add Axis
308
        $$.axis.init();
309

    
310
        // Set targets
311
        $$.updateTargets($$.data.targets);
312

    
313
        // Draw with targets
314
        if (binding) {
315
            $$.updateDimension();
316
            $$.config.oninit.call($$);
317
            $$.redraw({
318
                withTransition: false,
319
                withTransform: true,
320
                withUpdateXDomain: true,
321
                withUpdateOrgXDomain: true,
322
                withTransitionForAxis: false
323
            });
324
        }
325

    
326
        // Bind resize event
327
        if (window.onresize == null) {
328
            window.onresize = $$.generateResize();
329
        }
330
        if (window.onresize.add) {
331
            window.onresize.add(function () {
332
                config.onresize.call($$);
333
            });
334
            window.onresize.add(function () {
335
                $$.api.flush();
336
            });
337
            window.onresize.add(function () {
338
                config.onresized.call($$);
339
            });
340
        }
341

    
342
        // export element of the chart
343
        $$.api.element = $$.selectChart.node();
344
    };
345

    
346
    c3_chart_internal_fn.smoothLines = function (el, type) {
347
        var $$ = this;
348
        if (type === 'grid') {
349
            el.each(function () {
350
                var g = $$.d3.select(this),
351
                    x1 = g.attr('x1'),
352
                    x2 = g.attr('x2'),
353
                    y1 = g.attr('y1'),
354
                    y2 = g.attr('y2');
355
                g.attr({
356
                    'x1': Math.ceil(x1),
357
                    'x2': Math.ceil(x2),
358
                    'y1': Math.ceil(y1),
359
                    'y2': Math.ceil(y2)
360
                });
361
            });
362
        }
363
    };
364

    
365

    
366
    c3_chart_internal_fn.updateSizes = function () {
367
        var $$ = this, config = $$.config;
368
        var legendHeight = $$.legend ? $$.getLegendHeight() : 0,
369
            legendWidth = $$.legend ? $$.getLegendWidth() : 0,
370
            legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight,
371
            hasArc = $$.hasArcType(),
372
            xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'),
373
            subchartHeight = config.subchart_show && !hasArc ? (config.subchart_size_height + xAxisHeight) : 0;
374

    
375
        $$.currentWidth = $$.getCurrentWidth();
376
        $$.currentHeight = $$.getCurrentHeight();
377

    
378
        // for main
379
        $$.margin = config.axis_rotated ? {
380
            top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(),
381
            right: hasArc ? 0 : $$.getCurrentPaddingRight(),
382
            bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(),
383
            left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft())
384
        } : {
385
            top: 4 + $$.getCurrentPaddingTop(), // for top tick text
386
            right: hasArc ? 0 : $$.getCurrentPaddingRight(),
387
            bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(),
388
            left: hasArc ? 0 : $$.getCurrentPaddingLeft()
389
        };
390

    
391
        // for subchart
392
        $$.margin2 = config.axis_rotated ? {
393
            top: $$.margin.top,
394
            right: NaN,
395
            bottom: 20 + legendHeightForBottom,
396
            left: $$.rotated_padding_left
397
        } : {
398
            top: $$.currentHeight - subchartHeight - legendHeightForBottom,
399
            right: NaN,
400
            bottom: xAxisHeight + legendHeightForBottom,
401
            left: $$.margin.left
402
        };
403

    
404
        // for legend
405
        $$.margin3 = {
406
            top: 0,
407
            right: NaN,
408
            bottom: 0,
409
            left: 0
410
        };
411
        if ($$.updateSizeForLegend) { $$.updateSizeForLegend(legendHeight, legendWidth); }
412

    
413
        $$.width = $$.currentWidth - $$.margin.left - $$.margin.right;
414
        $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom;
415
        if ($$.width < 0) { $$.width = 0; }
416
        if ($$.height < 0) { $$.height = 0; }
417

    
418
        $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width;
419
        $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom;
420
        if ($$.width2 < 0) { $$.width2 = 0; }
421
        if ($$.height2 < 0) { $$.height2 = 0; }
422

    
423
        // for arc
424
        $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0);
425
        $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10);
426
        if ($$.hasType('gauge')) {
427
            $$.arcHeight += $$.height - $$.getGaugeLabelHeight();
428
        }
429
        if ($$.updateRadius) { $$.updateRadius(); }
430

    
431
        if ($$.isLegendRight && hasArc) {
432
            $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1;
433
        }
434
    };
435

    
436
    c3_chart_internal_fn.updateTargets = function (targets) {
437
        var $$ = this;
438

    
439
        /*-- Main --*/
440

    
441
        //-- Text --//
442
        $$.updateTargetsForText(targets);
443

    
444
        //-- Bar --//
445
        $$.updateTargetsForBar(targets);
446

    
447
        //-- Line --//
448
        $$.updateTargetsForLine(targets);
449

    
450
        //-- Arc --//
451
        if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets); }
452

    
453
        /*-- Sub --*/
454

    
455
        if ($$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); }
456

    
457
        // Fade-in each chart
458
        $$.showTargets();
459
    };
460
    c3_chart_internal_fn.showTargets = function () {
461
        var $$ = this;
462
        $$.svg.selectAll('.' + CLASS.target).filter(function (d) { return $$.isTargetToShow(d.id); })
463
          .transition().duration($$.config.transition_duration)
464
            .style("opacity", 1);
465
    };
466

    
467
    c3_chart_internal_fn.redraw = function (options, transitions) {
468
        var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config;
469
        var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType);
470
        var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis,
471
            withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend,
472
            withEventRect, withDimension, withUpdateXAxis;
473
        var hideAxis = $$.hasArcType();
474
        var drawArea, drawBar, drawLine, xForText, yForText;
475
        var duration, durationForExit, durationForAxis;
476
        var waitForDraw, flow;
477
        var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom;
478
        var xv = $$.xv.bind($$), cx, cy;
479

    
480
        options = options || {};
481
        withY = getOption(options, "withY", true);
482
        withSubchart = getOption(options, "withSubchart", true);
483
        withTransition = getOption(options, "withTransition", true);
484
        withTransform = getOption(options, "withTransform", false);
485
        withUpdateXDomain = getOption(options, "withUpdateXDomain", false);
486
        withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", false);
487
        withTrimXDomain = getOption(options, "withTrimXDomain", true);
488
        withUpdateXAxis = getOption(options, "withUpdateXAxis", withUpdateXDomain);
489
        withLegend = getOption(options, "withLegend", false);
490
        withEventRect = getOption(options, "withEventRect", true);
491
        withDimension = getOption(options, "withDimension", true);
492
        withTransitionForExit = getOption(options, "withTransitionForExit", withTransition);
493
        withTransitionForAxis = getOption(options, "withTransitionForAxis", withTransition);
494

    
495
        duration = withTransition ? config.transition_duration : 0;
496
        durationForExit = withTransitionForExit ? duration : 0;
497
        durationForAxis = withTransitionForAxis ? duration : 0;
498

    
499
        transitions = transitions || $$.axis.generateTransitions(durationForAxis);
500

    
501
        // update legend and transform each g
502
        if (withLegend && config.legend_show) {
503
            $$.updateLegend($$.mapToIds($$.data.targets), options, transitions);
504
        } else if (withDimension) {
505
            // need to update dimension (e.g. axis.y.tick.values) because y tick values should change
506
            // no need to update axis in it because they will be updated in redraw()
507
            $$.updateDimension(true);
508
        }
509

    
510
        // MEMO: needed for grids calculation
511
        if ($$.isCategorized() && targetsToShow.length === 0) {
512
            $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]);
513
        }
514

    
515
        if (targetsToShow.length) {
516
            $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain);
517
            if (!config.axis_x_tick_values) {
518
                tickValues = $$.axis.updateXAxisTickValues(targetsToShow);
519
            }
520
        } else {
521
            $$.xAxis.tickValues([]);
522
            $$.subXAxis.tickValues([]);
523
        }
524

    
525
        if (config.zoom_rescale && !options.flow) {
526
            xDomainForZoom = $$.x.orgDomain();
527
        }
528

    
529
        $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom));
530
        $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom));
531

    
532
        if (!config.axis_y_tick_values && config.axis_y_tick_count) {
533
            $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count));
534
        }
535
        if (!config.axis_y2_tick_values && config.axis_y2_tick_count) {
536
            $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count));
537
        }
538

    
539
        // axes
540
        $$.axis.redraw(transitions, hideAxis);
541

    
542
        // Update axis label
543
        $$.axis.updateLabels(withTransition);
544

    
545
        // show/hide if manual culling needed
546
        if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) {
547
            if (config.axis_x_tick_culling && tickValues) {
548
                for (i = 1; i < tickValues.length; i++) {
549
                    if (tickValues.length / i < config.axis_x_tick_culling_max) {
550
                        intervalForCulling = i;
551
                        break;
552
                    }
553
                }
554
                $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) {
555
                    var index = tickValues.indexOf(e);
556
                    if (index >= 0) {
557
                        d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block');
558
                    }
559
                });
560
            } else {
561
                $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').style('display', 'block');
562
            }
563
        }
564

    
565
        // setup drawer - MEMO: these must be called after axis updated
566
        drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined;
567
        drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined;
568
        drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined;
569
        xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true);
570
        yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false);
571

    
572
        // Update sub domain
573
        if (withY) {
574
            $$.subY.domain($$.getYDomain(targetsToShow, 'y'));
575
            $$.subY2.domain($$.getYDomain(targetsToShow, 'y2'));
576
        }
577

    
578
        // tooltip
579
        $$.tooltip.style("display", "none");
580

    
581
        // xgrid focus
582
        $$.updateXgridFocus();
583

    
584
        // Data empty label positioning and text.
585
        main.select("text." + CLASS.text + '.' + CLASS.empty)
586
            .attr("x", $$.width / 2)
587
            .attr("y", $$.height / 2)
588
            .text(config.data_empty_label_text)
589
          .transition()
590
            .style('opacity', targetsToShow.length ? 0 : 1);
591

    
592
        // grid
593
        $$.updateGrid(duration);
594

    
595
        // rect for regions
596
        $$.updateRegion(duration);
597

    
598
        // bars
599
        $$.updateBar(durationForExit);
600

    
601
        // lines, areas and cricles
602
        $$.updateLine(durationForExit);
603
        $$.updateArea(durationForExit);
604
        $$.updateCircle();
605

    
606
        // text
607
        if ($$.hasDataLabel()) {
608
            $$.updateText(durationForExit);
609
        }
610

    
611
        // arc
612
        if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); }
613

    
614
        // subchart
615
        if ($$.redrawSubchart) {
616
            $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices);
617
        }
618

    
619
        // circles for select
620
        main.selectAll('.' + CLASS.selectedCircles)
621
            .filter($$.isBarType.bind($$))
622
            .selectAll('circle')
623
            .remove();
624

    
625
        // event rects will redrawn when flow called
626
        if (config.interaction_enabled && !options.flow && withEventRect) {
627
            $$.redrawEventRect();
628
            if ($$.updateZoom) { $$.updateZoom(); }
629
        }
630

    
631
        // update circleY based on updated parameters
632
        $$.updateCircleY();
633

    
634
        // generate circle x/y functions depending on updated params
635
        cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$);
636
        cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$);
637

    
638
        if (options.flow) {
639
            flow = $$.generateFlow({
640
                targets: targetsToShow,
641
                flow: options.flow,
642
                duration: options.flow.duration,
643
                drawBar: drawBar,
644
                drawLine: drawLine,
645
                drawArea: drawArea,
646
                cx: cx,
647
                cy: cy,
648
                xv: xv,
649
                xForText: xForText,
650
                yForText: yForText
651
            });
652
        }
653

    
654
        if ((duration || flow) && $$.isTabVisible()) { // Only use transition if tab visible. See #938.
655
            // transition should be derived from one transition
656
            d3.transition().duration(duration).each(function () {
657
                var transitionsToWait = [];
658

    
659
                // redraw and gather transitions
660
                [
661
                    $$.redrawBar(drawBar, true),
662
                    $$.redrawLine(drawLine, true),
663
                    $$.redrawArea(drawArea, true),
664
                    $$.redrawCircle(cx, cy, true),
665
                    $$.redrawText(xForText, yForText, options.flow, true),
666
                    $$.redrawRegion(true),
667
                    $$.redrawGrid(true),
668
                ].forEach(function (transitions) {
669
                    transitions.forEach(function (transition) {
670
                        transitionsToWait.push(transition);
671
                    });
672
                });
673

    
674
                // Wait for end of transitions to call flow and onrendered callback
675
                waitForDraw = $$.generateWait();
676
                transitionsToWait.forEach(function (t) {
677
                    waitForDraw.add(t);
678
                });
679
            })
680
            .call(waitForDraw, function () {
681
                if (flow) {
682
                    flow();
683
                }
684
                if (config.onrendered) {
685
                    config.onrendered.call($$);
686
                }
687
            });
688
        }
689
        else {
690
            $$.redrawBar(drawBar);
691
            $$.redrawLine(drawLine);
692
            $$.redrawArea(drawArea);
693
            $$.redrawCircle(cx, cy);
694
            $$.redrawText(xForText, yForText, options.flow);
695
            $$.redrawRegion();
696
            $$.redrawGrid();
697
            if (config.onrendered) {
698
                config.onrendered.call($$);
699
            }
700
        }
701

    
702
        // update fadein condition
703
        $$.mapToIds($$.data.targets).forEach(function (id) {
704
            $$.withoutFadeIn[id] = true;
705
        });
706
    };
707

    
708
    c3_chart_internal_fn.updateAndRedraw = function (options) {
709
        var $$ = this, config = $$.config, transitions;
710
        options = options || {};
711
        // same with redraw
712
        options.withTransition = getOption(options, "withTransition", true);
713
        options.withTransform = getOption(options, "withTransform", false);
714
        options.withLegend = getOption(options, "withLegend", false);
715
        // NOT same with redraw
716
        options.withUpdateXDomain = true;
717
        options.withUpdateOrgXDomain = true;
718
        options.withTransitionForExit = false;
719
        options.withTransitionForTransform = getOption(options, "withTransitionForTransform", options.withTransition);
720
        // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called)
721
        $$.updateSizes();
722
        // MEMO: called in updateLegend in redraw if withLegend
723
        if (!(options.withLegend && config.legend_show)) {
724
            transitions = $$.axis.generateTransitions(options.withTransitionForAxis ? config.transition_duration : 0);
725
            // Update scales
726
            $$.updateScales();
727
            $$.updateSvgSize();
728
            // Update g positions
729
            $$.transformAll(options.withTransitionForTransform, transitions);
730
        }
731
        // Draw with new sizes & scales
732
        $$.redraw(options, transitions);
733
    };
734
    c3_chart_internal_fn.redrawWithoutRescale = function () {
735
        this.redraw({
736
            withY: false,
737
            withSubchart: false,
738
            withEventRect: false,
739
            withTransitionForAxis: false
740
        });
741
    };
742

    
743
    c3_chart_internal_fn.isTimeSeries = function () {
744
        return this.config.axis_x_type === 'timeseries';
745
    };
746
    c3_chart_internal_fn.isCategorized = function () {
747
        return this.config.axis_x_type.indexOf('categor') >= 0;
748
    };
749
    c3_chart_internal_fn.isCustomX = function () {
750
        var $$ = this, config = $$.config;
751
        return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs));
752
    };
753

    
754
    c3_chart_internal_fn.isTimeSeriesY = function () {
755
        return this.config.axis_y_type === 'timeseries';
756
    };
757

    
758
    c3_chart_internal_fn.getTranslate = function (target) {
759
        var $$ = this, config = $$.config, x, y;
760
        if (target === 'main') {
761
            x = asHalfPixel($$.margin.left);
762
            y = asHalfPixel($$.margin.top);
763
        } else if (target === 'context') {
764
            x = asHalfPixel($$.margin2.left);
765
            y = asHalfPixel($$.margin2.top);
766
        } else if (target === 'legend') {
767
            x = $$.margin3.left;
768
            y = $$.margin3.top;
769
        } else if (target === 'x') {
770
            x = 0;
771
            y = config.axis_rotated ? 0 : $$.height;
772
        } else if (target === 'y') {
773
            x = 0;
774
            y = config.axis_rotated ? $$.height : 0;
775
        } else if (target === 'y2') {
776
            x = config.axis_rotated ? 0 : $$.width;
777
            y = config.axis_rotated ? 1 : 0;
778
        } else if (target === 'subx') {
779
            x = 0;
780
            y = config.axis_rotated ? 0 : $$.height2;
781
        } else if (target === 'arc') {
782
            x = $$.arcWidth / 2;
783
            y = $$.arcHeight / 2;
784
        }
785
        return "translate(" + x + "," + y + ")";
786
    };
787
    c3_chart_internal_fn.initialOpacity = function (d) {
788
        return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0;
789
    };
790
    c3_chart_internal_fn.initialOpacityForCircle = function (d) {
791
        return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0;
792
    };
793
    c3_chart_internal_fn.opacityForCircle = function (d) {
794
        var opacity = this.config.point_show ? 1 : 0;
795
        return isValue(d.value) ? (this.isScatterType(d) ? 0.5 : opacity) : 0;
796
    };
797
    c3_chart_internal_fn.opacityForText = function () {
798
        return this.hasDataLabel() ? 1 : 0;
799
    };
800
    c3_chart_internal_fn.xx = function (d) {
801
        return d ? this.x(d.x) : null;
802
    };
803
    c3_chart_internal_fn.xv = function (d) {
804
        var $$ = this, value = d.value;
805
        if ($$.isTimeSeries()) {
806
            value = $$.parseDate(d.value);
807
        }
808
        else if ($$.isCategorized() && typeof d.value === 'string') {
809
            value = $$.config.axis_x_categories.indexOf(d.value);
810
        }
811
        return Math.ceil($$.x(value));
812
    };
813
    c3_chart_internal_fn.yv = function (d) {
814
        var $$ = this,
815
            yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y;
816
        return Math.ceil(yScale(d.value));
817
    };
818
    c3_chart_internal_fn.subxx = function (d) {
819
        return d ? this.subX(d.x) : null;
820
    };
821

    
822
    c3_chart_internal_fn.transformMain = function (withTransition, transitions) {
823
        var $$ = this,
824
            xAxis, yAxis, y2Axis;
825
        if (transitions && transitions.axisX) {
826
            xAxis = transitions.axisX;
827
        } else {
828
            xAxis  = $$.main.select('.' + CLASS.axisX);
829
            if (withTransition) { xAxis = xAxis.transition(); }
830
        }
831
        if (transitions && transitions.axisY) {
832
            yAxis = transitions.axisY;
833
        } else {
834
            yAxis = $$.main.select('.' + CLASS.axisY);
835
            if (withTransition) { yAxis = yAxis.transition(); }
836
        }
837
        if (transitions && transitions.axisY2) {
838
            y2Axis = transitions.axisY2;
839
        } else {
840
            y2Axis = $$.main.select('.' + CLASS.axisY2);
841
            if (withTransition) { y2Axis = y2Axis.transition(); }
842
        }
843
        (withTransition ? $$.main.transition() : $$.main).attr("transform", $$.getTranslate('main'));
844
        xAxis.attr("transform", $$.getTranslate('x'));
845
        yAxis.attr("transform", $$.getTranslate('y'));
846
        y2Axis.attr("transform", $$.getTranslate('y2'));
847
        $$.main.select('.' + CLASS.chartArcs).attr("transform", $$.getTranslate('arc'));
848
    };
849
    c3_chart_internal_fn.transformAll = function (withTransition, transitions) {
850
        var $$ = this;
851
        $$.transformMain(withTransition, transitions);
852
        if ($$.config.subchart_show) { $$.transformContext(withTransition, transitions); }
853
        if ($$.legend) { $$.transformLegend(withTransition); }
854
    };
855

    
856
    c3_chart_internal_fn.updateSvgSize = function () {
857
        var $$ = this,
858
            brush = $$.svg.select(".c3-brush .background");
859
        $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight);
860
        $$.svg.selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]).select('rect')
861
            .attr('width', $$.width)
862
            .attr('height', $$.height);
863
        $$.svg.select('#' + $$.clipIdForXAxis).select('rect')
864
            .attr('x', $$.getXAxisClipX.bind($$))
865
            .attr('y', $$.getXAxisClipY.bind($$))
866
            .attr('width', $$.getXAxisClipWidth.bind($$))
867
            .attr('height', $$.getXAxisClipHeight.bind($$));
868
        $$.svg.select('#' + $$.clipIdForYAxis).select('rect')
869
            .attr('x', $$.getYAxisClipX.bind($$))
870
            .attr('y', $$.getYAxisClipY.bind($$))
871
            .attr('width', $$.getYAxisClipWidth.bind($$))
872
            .attr('height', $$.getYAxisClipHeight.bind($$));
873
        $$.svg.select('#' + $$.clipIdForSubchart).select('rect')
874
            .attr('width', $$.width)
875
            .attr('height', brush.size() ? brush.attr('height') : 0);
876
        $$.svg.select('.' + CLASS.zoomRect)
877
            .attr('width', $$.width)
878
            .attr('height', $$.height);
879
        // MEMO: parent div's height will be bigger than svg when <!DOCTYPE html>
880
        $$.selectChart.style('max-height', $$.currentHeight + "px");
881
    };
882

    
883

    
884
    c3_chart_internal_fn.updateDimension = function (withoutAxis) {
885
        var $$ = this;
886
        if (!withoutAxis) {
887
            if ($$.config.axis_rotated) {
888
                $$.axes.x.call($$.xAxis);
889
                $$.axes.subx.call($$.subXAxis);
890
            } else {
891
                $$.axes.y.call($$.yAxis);
892
                $$.axes.y2.call($$.y2Axis);
893
            }
894
        }
895
        $$.updateSizes();
896
        $$.updateScales();
897
        $$.updateSvgSize();
898
        $$.transformAll(false);
899
    };
900

    
901
    c3_chart_internal_fn.observeInserted = function (selection) {
902
        var $$ = this, observer;
903
        if (typeof MutationObserver === 'undefined') {
904
            window.console.error("MutationObserver not defined.");
905
            return;
906
        }
907
        observer= new MutationObserver(function (mutations) {
908
            mutations.forEach(function (mutation) {
909
                if (mutation.type === 'childList' && mutation.previousSibling) {
910
                    observer.disconnect();
911
                    // need to wait for completion of load because size calculation requires the actual sizes determined after that completion
912
                    $$.intervalForObserveInserted = window.setInterval(function () {
913
                        // parentNode will NOT be null when completed
914
                        if (selection.node().parentNode) {
915
                            window.clearInterval($$.intervalForObserveInserted);
916
                            $$.updateDimension();
917
                            $$.config.oninit.call($$);
918
                            $$.redraw({
919
                                withTransform: true,
920
                                withUpdateXDomain: true,
921
                                withUpdateOrgXDomain: true,
922
                                withTransition: false,
923
                                withTransitionForTransform: false,
924
                                withLegend: true
925
                            });
926
                            selection.transition().style('opacity', 1);
927
                        }
928
                    }, 10);
929
                }
930
            });
931
        });
932
        observer.observe(selection.node(), {attributes: true, childList: true, characterData: true});
933
    };
934

    
935

    
936
    c3_chart_internal_fn.generateResize = function () {
937
        var resizeFunctions = [];
938
        function callResizeFunctions() {
939
            resizeFunctions.forEach(function (f) {
940
                f();
941
            });
942
        }
943
        callResizeFunctions.add = function (f) {
944
            resizeFunctions.push(f);
945
        };
946
        return callResizeFunctions;
947
    };
948

    
949
    c3_chart_internal_fn.endall = function (transition, callback) {
950
        var n = 0;
951
        transition
952
            .each(function () { ++n; })
953
            .each("end", function () {
954
                if (!--n) { callback.apply(this, arguments); }
955
            });
956
    };
957
    c3_chart_internal_fn.generateWait = function () {
958
        var transitionsToWait = [],
959
            f = function (transition, callback) {
960
                var timer = setInterval(function () {
961
                    var done = 0;
962
                    transitionsToWait.forEach(function (t) {
963
                        if (t.empty()) {
964
                            done += 1;
965
                            return;
966
                        }
967
                        try {
968
                            t.transition();
969
                        } catch (e) {
970
                            done += 1;
971
                        }
972
                    });
973
                    if (done === transitionsToWait.length) {
974
                        clearInterval(timer);
975
                        if (callback) { callback(); }
976
                    }
977
                }, 10);
978
            };
979
        f.add = function (transition) {
980
            transitionsToWait.push(transition);
981
        };
982
        return f;
983
    };
984

    
985
    c3_chart_internal_fn.parseDate = function (date) {
986
        var $$ = this, parsedDate;
987
        if (date instanceof Date) {
988
            parsedDate = date;
989
        } else if (typeof date === 'string') {
990
            parsedDate = $$.dataTimeFormat($$.config.data_xFormat).parse(date);
991
        } else if (typeof date === 'number' || !isNaN(date)) {
992
            parsedDate = new Date(+date);
993
        }
994
        if (!parsedDate || isNaN(+parsedDate)) {
995
            window.console.error("Failed to parse x '" + date + "' to Date object");
996
        }
997
        return parsedDate;
998
    };
999

    
1000
    c3_chart_internal_fn.isTabVisible = function () {
1001
        var hidden;
1002
        if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
1003
            hidden = "hidden";
1004
        } else if (typeof document.mozHidden !== "undefined") {
1005
            hidden = "mozHidden";
1006
        } else if (typeof document.msHidden !== "undefined") {
1007
            hidden = "msHidden";
1008
        } else if (typeof document.webkitHidden !== "undefined") {
1009
            hidden = "webkitHidden";
1010
        }
1011

    
1012
        return document[hidden] ? false : true;
1013
    };
1014

    
1015
    c3_chart_internal_fn.getDefaultConfig = function () {
1016
        var config = {
1017
            bindto: '#chart',
1018
            size_width: undefined,
1019
            size_height: undefined,
1020
            padding_left: undefined,
1021
            padding_right: undefined,
1022
            padding_top: undefined,
1023
            padding_bottom: undefined,
1024
            zoom_enabled: false,
1025
            zoom_extent: undefined,
1026
            zoom_privileged: false,
1027
            zoom_rescale: false,
1028
            zoom_onzoom: function () {},
1029
            zoom_onzoomstart: function () {},
1030
            zoom_onzoomend: function () {},
1031
            interaction_enabled: true,
1032
            onmouseover: function () {},
1033
            onmouseout: function () {},
1034
            onresize: function () {},
1035
            onresized: function () {},
1036
            oninit: function () {},
1037
            onrendered: function () {},
1038
            transition_duration: 350,
1039
            data_x: undefined,
1040
            data_xs: {},
1041
            data_xFormat: '%Y-%m-%d',
1042
            data_xLocaltime: true,
1043
            data_xSort: true,
1044
            data_idConverter: function (id) { return id; },
1045
            data_names: {},
1046
            data_classes: {},
1047
            data_groups: [],
1048
            data_axes: {},
1049
            data_type: undefined,
1050
            data_types: {},
1051
            data_labels: {},
1052
            data_order: 'desc',
1053
            data_regions: {},
1054
            data_color: undefined,
1055
            data_colors: {},
1056
            data_hide: false,
1057
            data_filter: undefined,
1058
            data_selection_enabled: false,
1059
            data_selection_grouped: false,
1060
            data_selection_isselectable: function () { return true; },
1061
            data_selection_multiple: true,
1062
            data_selection_draggable: false,
1063
            data_onclick: function () {},
1064
            data_onmouseover: function () {},
1065
            data_onmouseout: function () {},
1066
            data_onselected: function () {},
1067
            data_onunselected: function () {},
1068
            data_url: undefined,
1069
            data_json: undefined,
1070
            data_rows: undefined,
1071
            data_columns: undefined,
1072
            data_mimeType: undefined,
1073
            data_keys: undefined,
1074
            // configuration for no plot-able data supplied.
1075
            data_empty_label_text: "",
1076
            // subchart
1077
            subchart_show: false,
1078
            subchart_size_height: 60,
1079
            subchart_onbrush: function () {},
1080
            // color
1081
            color_pattern: [],
1082
            color_threshold: {},
1083
            // legend
1084
            legend_show: true,
1085
            legend_hide: false,
1086
            legend_position: 'bottom',
1087
            legend_inset_anchor: 'top-left',
1088
            legend_inset_x: 10,
1089
            legend_inset_y: 0,
1090
            legend_inset_step: undefined,
1091
            legend_item_onclick: undefined,
1092
            legend_item_onmouseover: undefined,
1093
            legend_item_onmouseout: undefined,
1094
            legend_equally: false,
1095
            // axis
1096
            axis_rotated: false,
1097
            axis_x_show: true,
1098
            axis_x_type: 'indexed',
1099
            axis_x_localtime: true,
1100
            axis_x_categories: [],
1101
            axis_x_tick_centered: false,
1102
            axis_x_tick_format: undefined,
1103
            axis_x_tick_culling: {},
1104
            axis_x_tick_culling_max: 10,
1105
            axis_x_tick_count: undefined,
1106
            axis_x_tick_fit: true,
1107
            axis_x_tick_values: null,
1108
            axis_x_tick_rotate: 0,
1109
            axis_x_tick_outer: true,
1110
            axis_x_tick_multiline: true,
1111
            axis_x_tick_width: null,
1112
            axis_x_max: undefined,
1113
            axis_x_min: undefined,
1114
            axis_x_padding: {},
1115
            axis_x_height: undefined,
1116
            axis_x_extent: undefined,
1117
            axis_x_label: {},
1118
            axis_y_show: true,
1119
            axis_y_type: undefined,
1120
            axis_y_max: undefined,
1121
            axis_y_min: undefined,
1122
            axis_y_inverted: false,
1123
            axis_y_center: undefined,
1124
            axis_y_inner: undefined,
1125
            axis_y_label: {},
1126
            axis_y_tick_format: undefined,
1127
            axis_y_tick_outer: true,
1128
            axis_y_tick_values: null,
1129
            axis_y_tick_count: undefined,
1130
            axis_y_tick_time_value: undefined,
1131
            axis_y_tick_time_interval: undefined,
1132
            axis_y_padding: {},
1133
            axis_y_default: undefined,
1134
            axis_y2_show: false,
1135
            axis_y2_max: undefined,
1136
            axis_y2_min: undefined,
1137
            axis_y2_inverted: false,
1138
            axis_y2_center: undefined,
1139
            axis_y2_inner: undefined,
1140
            axis_y2_label: {},
1141
            axis_y2_tick_format: undefined,
1142
            axis_y2_tick_outer: true,
1143
            axis_y2_tick_values: null,
1144
            axis_y2_tick_count: undefined,
1145
            axis_y2_padding: {},
1146
            axis_y2_default: undefined,
1147
            // grid
1148
            grid_x_show: false,
1149
            grid_x_type: 'tick',
1150
            grid_x_lines: [],
1151
            grid_y_show: false,
1152
            // not used
1153
            // grid_y_type: 'tick',
1154
            grid_y_lines: [],
1155
            grid_y_ticks: 10,
1156
            grid_focus_show: true,
1157
            grid_lines_front: true,
1158
            // point - point of each data
1159
            point_show: true,
1160
            point_r: 2.5,
1161
            point_focus_expand_enabled: true,
1162
            point_focus_expand_r: undefined,
1163
            point_select_r: undefined,
1164
            // line
1165
            line_connectNull: false,
1166
            line_step_type: 'step',
1167
            // bar
1168
            bar_width: undefined,
1169
            bar_width_ratio: 0.6,
1170
            bar_width_max: undefined,
1171
            bar_zerobased: true,
1172
            // area
1173
            area_zerobased: true,
1174
            // pie
1175
            pie_label_show: true,
1176
            pie_label_format: undefined,
1177
            pie_label_threshold: 0.05,
1178
            pie_expand: true,
1179
            // gauge
1180
            gauge_label_show: true,
1181
            gauge_label_format: undefined,
1182
            gauge_expand: true,
1183
            gauge_min: 0,
1184
            gauge_max: 100,
1185
            gauge_units: undefined,
1186
            gauge_width: undefined,
1187
            // donut
1188
            donut_label_show: true,
1189
            donut_label_format: undefined,
1190
            donut_label_threshold: 0.05,
1191
            donut_width: undefined,
1192
            donut_expand: true,
1193
            donut_title: "",
1194
            // region - region to change style
1195
            regions: [],
1196
            // tooltip - show when mouseover on each data
1197
            tooltip_show: true,
1198
            tooltip_grouped: true,
1199
            tooltip_format_title: undefined,
1200
            tooltip_format_name: undefined,
1201
            tooltip_format_value: undefined,
1202
            tooltip_position: undefined,
1203
            tooltip_contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
1204
                return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : '';
1205
            },
1206
            tooltip_init_show: false,
1207
            tooltip_init_x: 0,
1208
            tooltip_init_position: {top: '0px', left: '50px'}
1209
        };
1210

    
1211
        Object.keys(this.additionalConfig).forEach(function (key) {
1212
            config[key] = this.additionalConfig[key];
1213
        }, this);
1214

    
1215
        return config;
1216
    };
1217
    c3_chart_internal_fn.additionalConfig = {};
1218

    
1219
    c3_chart_internal_fn.loadConfig = function (config) {
1220
        var this_config = this.config, target, keys, read;
1221
        function find() {
1222
            var key = keys.shift();
1223
    //        console.log("key =>", key, ", target =>", target);
1224
            if (key && target && typeof target === 'object' && key in target) {
1225
                target = target[key];
1226
                return find();
1227
            }
1228
            else if (!key) {
1229
                return target;
1230
            }
1231
            else {
1232
                return undefined;
1233
            }
1234
        }
1235
        Object.keys(this_config).forEach(function (key) {
1236
            target = config;
1237
            keys = key.split('_');
1238
            read = find();
1239
    //        console.log("CONFIG : ", key, read);
1240
            if (isDefined(read)) {
1241
                this_config[key] = read;
1242
            }
1243
        });
1244
    };
1245

    
1246
    c3_chart_internal_fn.getScale = function (min, max, forTimeseries) {
1247
        return (forTimeseries ? this.d3.time.scale() : this.d3.scale.linear()).range([min, max]);
1248
    };
1249
    c3_chart_internal_fn.getX = function (min, max, domain, offset) {
1250
        var $$ = this,
1251
            scale = $$.getScale(min, max, $$.isTimeSeries()),
1252
            _scale = domain ? scale.domain(domain) : scale, key;
1253
        // Define customized scale if categorized axis
1254
        if ($$.isCategorized()) {
1255
            offset = offset || function () { return 0; };
1256
            scale = function (d, raw) {
1257
                var v = _scale(d) + offset(d);
1258
                return raw ? v : Math.ceil(v);
1259
            };
1260
        } else {
1261
            scale = function (d, raw) {
1262
                var v = _scale(d);
1263
                return raw ? v : Math.ceil(v);
1264
            };
1265
        }
1266
        // define functions
1267
        for (key in _scale) {
1268
            scale[key] = _scale[key];
1269
        }
1270
        scale.orgDomain = function () {
1271
            return _scale.domain();
1272
        };
1273
        // define custom domain() for categorized axis
1274
        if ($$.isCategorized()) {
1275
            scale.domain = function (domain) {
1276
                if (!arguments.length) {
1277
                    domain = this.orgDomain();
1278
                    return [domain[0], domain[1] + 1];
1279
                }
1280
                _scale.domain(domain);
1281
                return scale;
1282
            };
1283
        }
1284
        return scale;
1285
    };
1286
    c3_chart_internal_fn.getY = function (min, max, domain) {
1287
        var scale = this.getScale(min, max, this.isTimeSeriesY());
1288
        if (domain) { scale.domain(domain); }
1289
        return scale;
1290
    };
1291
    c3_chart_internal_fn.getYScale = function (id) {
1292
        return this.axis.getId(id) === 'y2' ? this.y2 : this.y;
1293
    };
1294
    c3_chart_internal_fn.getSubYScale = function (id) {
1295
        return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY;
1296
    };
1297
    c3_chart_internal_fn.updateScales = function () {
1298
        var $$ = this, config = $$.config,
1299
            forInit = !$$.x;
1300
        // update edges
1301
        $$.xMin = config.axis_rotated ? 1 : 0;
1302
        $$.xMax = config.axis_rotated ? $$.height : $$.width;
1303
        $$.yMin = config.axis_rotated ? 0 : $$.height;
1304
        $$.yMax = config.axis_rotated ? $$.width : 1;
1305
        $$.subXMin = $$.xMin;
1306
        $$.subXMax = $$.xMax;
1307
        $$.subYMin = config.axis_rotated ? 0 : $$.height2;
1308
        $$.subYMax = config.axis_rotated ? $$.width2 : 1;
1309
        // update scales
1310
        $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () { return $$.xAxis.tickOffset(); });
1311
        $$.y = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y_default : $$.y.domain());
1312
        $$.y2 = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y2_default : $$.y2.domain());
1313
        $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function (d) { return d % 1 ? 0 : $$.subXAxis.tickOffset(); });
1314
        $$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y_default : $$.subY.domain());
1315
        $$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain());
1316
        // update axes
1317
        $$.xAxisTickFormat = $$.axis.getXAxisTickFormat();
1318
        $$.xAxisTickValues = $$.axis.getXAxisTickValues();
1319
        $$.yAxisTickValues = $$.axis.getYAxisTickValues();
1320
        $$.y2AxisTickValues = $$.axis.getY2AxisTickValues();
1321

    
1322
        $$.xAxis = $$.axis.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
1323
        $$.subXAxis = $$.axis.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
1324
        $$.yAxis = $$.axis.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, config.axis_y_tick_outer);
1325
        $$.y2Axis = $$.axis.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, config.axis_y2_tick_outer);
1326

    
1327
        // Set initialized scales to brush and zoom
1328
        if (!forInit) {
1329
            if ($$.brush) { $$.brush.scale($$.subX); }
1330
            if (config.zoom_enabled) { $$.zoom.scale($$.x); }
1331
        }
1332
        // update for arc
1333
        if ($$.updateArc) { $$.updateArc(); }
1334
    };
1335

    
1336
    c3_chart_internal_fn.getYDomainMin = function (targets) {
1337
        var $$ = this, config = $$.config,
1338
            ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets),
1339
            j, k, baseId, idsInGroup, id, hasNegativeValue;
1340
        if (config.data_groups.length > 0) {
1341
            hasNegativeValue = $$.hasNegativeValueInTargets(targets);
1342
            for (j = 0; j < config.data_groups.length; j++) {
1343
                // Determine baseId
1344
                idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; });
1345
                if (idsInGroup.length === 0) { continue; }
1346
                baseId = idsInGroup[0];
1347
                // Consider negative values
1348
                if (hasNegativeValue && ys[baseId]) {
1349
                    ys[baseId].forEach(function (v, i) {
1350
                        ys[baseId][i] = v < 0 ? v : 0;
1351
                    });
1352
                }
1353
                // Compute min
1354
                for (k = 1; k < idsInGroup.length; k++) {
1355
                    id = idsInGroup[k];
1356
                    if (! ys[id]) { continue; }
1357
                    ys[id].forEach(function (v, i) {
1358
                        if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) {
1359
                            ys[baseId][i] += +v;
1360
                        }
1361
                    });
1362
                }
1363
            }
1364
        }
1365
        return $$.d3.min(Object.keys(ys).map(function (key) { return $$.d3.min(ys[key]); }));
1366
    };
1367
    c3_chart_internal_fn.getYDomainMax = function (targets) {
1368
        var $$ = this, config = $$.config,
1369
            ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets),
1370
            j, k, baseId, idsInGroup, id, hasPositiveValue;
1371
        if (config.data_groups.length > 0) {
1372
            hasPositiveValue = $$.hasPositiveValueInTargets(targets);
1373
            for (j = 0; j < config.data_groups.length; j++) {
1374
                // Determine baseId
1375
                idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; });
1376
                if (idsInGroup.length === 0) { continue; }
1377
                baseId = idsInGroup[0];
1378
                // Consider positive values
1379
                if (hasPositiveValue && ys[baseId]) {
1380
                    ys[baseId].forEach(function (v, i) {
1381
                        ys[baseId][i] = v > 0 ? v : 0;
1382
                    });
1383
                }
1384
                // Compute max
1385
                for (k = 1; k < idsInGroup.length; k++) {
1386
                    id = idsInGroup[k];
1387
                    if (! ys[id]) { continue; }
1388
                    ys[id].forEach(function (v, i) {
1389
                        if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) {
1390
                            ys[baseId][i] += +v;
1391
                        }
1392
                    });
1393
                }
1394
            }
1395
        }
1396
        return $$.d3.max(Object.keys(ys).map(function (key) { return $$.d3.max(ys[key]); }));
1397
    };
1398
    c3_chart_internal_fn.getYDomain = function (targets, axisId, xDomain) {
1399
        var $$ = this, config = $$.config,
1400
            targetsByAxisId = targets.filter(function (t) { return $$.axis.getId(t.id) === axisId; }),
1401
            yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId,
1402
            yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min,
1403
            yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max,
1404
            yDomainMin = $$.getYDomainMin(yTargets),
1405
            yDomainMax = $$.getYDomainMax(yTargets),
1406
            domain, domainLength, padding, padding_top, padding_bottom,
1407
            center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center,
1408
            yDomainAbs, lengths, diff, ratio, isAllPositive, isAllNegative,
1409
            isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased),
1410
            isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted,
1411
            showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated,
1412
            showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated;
1413

    
1414
        // MEMO: avoid inverting domain unexpectedly
1415
        yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? (yDomainMin < yMax ? yDomainMin : yMax - 10) : yDomainMin;
1416
        yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? (yMin < yDomainMax ? yDomainMax : yMin + 10) : yDomainMax;
1417

    
1418
        if (yTargets.length === 0) { // use current domain if target of axisId is none
1419
            return axisId === 'y2' ? $$.y2.domain() : $$.y.domain();
1420
        }
1421
        if (isNaN(yDomainMin)) { // set minimum to zero when not number
1422
            yDomainMin = 0;
1423
        }
1424
        if (isNaN(yDomainMax)) { // set maximum to have same value as yDomainMin
1425
            yDomainMax = yDomainMin;
1426
        }
1427
        if (yDomainMin === yDomainMax) {
1428
            yDomainMin < 0 ? yDomainMax = 0 : yDomainMin = 0;
1429
        }
1430
        isAllPositive = yDomainMin >= 0 && yDomainMax >= 0;
1431
        isAllNegative = yDomainMin <= 0 && yDomainMax <= 0;
1432

    
1433
        // Cancel zerobased if axis_*_min / axis_*_max specified
1434
        if ((isValue(yMin) && isAllPositive) || (isValue(yMax) && isAllNegative)) {
1435
            isZeroBased = false;
1436
        }
1437

    
1438
        // Bar/Area chart should be 0-based if all positive|negative
1439
        if (isZeroBased) {
1440
            if (isAllPositive) { yDomainMin = 0; }
1441
            if (isAllNegative) { yDomainMax = 0; }
1442
        }
1443

    
1444
        domainLength = Math.abs(yDomainMax - yDomainMin);
1445
        padding = padding_top = padding_bottom = domainLength * 0.1;
1446

    
1447
        if (typeof center !== 'undefined') {
1448
            yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax));
1449
            yDomainMax = center + yDomainAbs;
1450
            yDomainMin = center - yDomainAbs;
1451
        }
1452
        // add padding for data label
1453
        if (showHorizontalDataLabel) {
1454
            lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width');
1455
            diff = diffDomain($$.y.range());
1456
            ratio = [lengths[0] / diff, lengths[1] / diff];
1457
            padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1]));
1458
            padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1]));
1459
        } else if (showVerticalDataLabel) {
1460
            lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height');
1461
            padding_top += $$.axis.convertPixelsToAxisPadding(lengths[1], domainLength);
1462
            padding_bottom += $$.axis.convertPixelsToAxisPadding(lengths[0], domainLength);
1463
        }
1464
        if (axisId === 'y' && notEmpty(config.axis_y_padding)) {
1465
            padding_top = $$.axis.getPadding(config.axis_y_padding, 'top', padding_top, domainLength);
1466
            padding_bottom = $$.axis.getPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength);
1467
        }
1468
        if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) {
1469
            padding_top = $$.axis.getPadding(config.axis_y2_padding, 'top', padding_top, domainLength);
1470
            padding_bottom = $$.axis.getPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength);
1471
        }
1472
        // Bar/Area chart should be 0-based if all positive|negative
1473
        if (isZeroBased) {
1474
            if (isAllPositive) { padding_bottom = yDomainMin; }
1475
            if (isAllNegative) { padding_top = -yDomainMax; }
1476
        }
1477
        domain = [yDomainMin - padding_bottom, yDomainMax + padding_top];
1478
        return isInverted ? domain.reverse() : domain;
1479
    };
1480
    c3_chart_internal_fn.getXDomainMin = function (targets) {
1481
        var $$ = this, config = $$.config;
1482
        return isDefined(config.axis_x_min) ?
1483
            ($$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min) :
1484
        $$.d3.min(targets, function (t) { return $$.d3.min(t.values, function (v) { return v.x; }); });
1485
    };
1486
    c3_chart_internal_fn.getXDomainMax = function (targets) {
1487
        var $$ = this, config = $$.config;
1488
        return isDefined(config.axis_x_max) ?
1489
            ($$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max) :
1490
        $$.d3.max(targets, function (t) { return $$.d3.max(t.values, function (v) { return v.x; }); });
1491
    };
1492
    c3_chart_internal_fn.getXDomainPadding = function (domain) {
1493
        var $$ = this, config = $$.config,
1494
            diff = domain[1] - domain[0],
1495
            maxDataCount, padding, paddingLeft, paddingRight;
1496
        if ($$.isCategorized()) {
1497
            padding = 0;
1498
        } else if ($$.hasType('bar')) {
1499
            maxDataCount = $$.getMaxDataCount();
1500
            padding = maxDataCount > 1 ? (diff / (maxDataCount - 1)) / 2 : 0.5;
1501
        } else {
1502
            padding = diff * 0.01;
1503
        }
1504
        if (typeof config.axis_x_padding === 'object' && notEmpty(config.axis_x_padding)) {
1505
            paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding;
1506
            paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding;
1507
        } else if (typeof config.axis_x_padding === 'number') {
1508
            paddingLeft = paddingRight = config.axis_x_padding;
1509
        } else {
1510
            paddingLeft = paddingRight = padding;
1511
        }
1512
        return {left: paddingLeft, right: paddingRight};
1513
    };
1514
    c3_chart_internal_fn.getXDomain = function (targets) {
1515
        var $$ = this,
1516
            xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)],
1517
            firstX = xDomain[0], lastX = xDomain[1],
1518
            padding = $$.getXDomainPadding(xDomain),
1519
            min = 0, max = 0;
1520
        // show center of x domain if min and max are the same
1521
        if ((firstX - lastX) === 0 && !$$.isCategorized()) {
1522
            if ($$.isTimeSeries()) {
1523
                firstX = new Date(firstX.getTime() * 0.5);
1524
                lastX = new Date(lastX.getTime() * 1.5);
1525
            } else {
1526
                firstX = firstX === 0 ? 1 : (firstX * 0.5);
1527
                lastX = lastX === 0 ? -1 : (lastX * 1.5);
1528
            }
1529
        }
1530
        if (firstX || firstX === 0) {
1531
            min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left;
1532
        }
1533
        if (lastX || lastX === 0) {
1534
            max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right;
1535
        }
1536
        return [min, max];
1537
    };
1538
    c3_chart_internal_fn.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain) {
1539
        var $$ = this, config = $$.config;
1540

    
1541
        if (withUpdateOrgXDomain) {
1542
            $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets)));
1543
            $$.orgXDomain = $$.x.domain();
1544
            if (config.zoom_enabled) { $$.zoom.scale($$.x).updateScaleExtent(); }
1545
            $$.subX.domain($$.x.domain());
1546
            if ($$.brush) { $$.brush.scale($$.subX); }
1547
        }
1548
        if (withUpdateXDomain) {
1549
            $$.x.domain(domain ? domain : (!$$.brush || $$.brush.empty()) ? $$.orgXDomain : $$.brush.extent());
1550
            if (config.zoom_enabled) { $$.zoom.scale($$.x).updateScaleExtent(); }
1551
        }
1552

    
1553
        // Trim domain when too big by zoom mousemove event
1554
        if (withTrim) { $$.x.domain($$.trimXDomain($$.x.orgDomain())); }
1555

    
1556
        return $$.x.domain();
1557
    };
1558
    c3_chart_internal_fn.trimXDomain = function (domain) {
1559
        var $$ = this;
1560
        if (domain[0] <= $$.orgXDomain[0]) {
1561
            domain[1] = +domain[1] + ($$.orgXDomain[0] - domain[0]);
1562
            domain[0] = $$.orgXDomain[0];
1563
        }
1564
        if ($$.orgXDomain[1] <= domain[1]) {
1565
            domain[0] = +domain[0] - (domain[1] - $$.orgXDomain[1]);
1566
            domain[1] = $$.orgXDomain[1];
1567
        }
1568
        return domain;
1569
    };
1570

    
1571
    c3_chart_internal_fn.isX = function (key) {
1572
        var $$ = this, config = $$.config;
1573
        return (config.data_x && key === config.data_x) || (notEmpty(config.data_xs) && hasValue(config.data_xs, key));
1574
    };
1575
    c3_chart_internal_fn.isNotX = function (key) {
1576
        return !this.isX(key);
1577
    };
1578
    c3_chart_internal_fn.getXKey = function (id) {
1579
        var $$ = this, config = $$.config;
1580
        return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null;
1581
    };
1582
    c3_chart_internal_fn.getXValuesOfXKey = function (key, targets) {
1583
        var $$ = this,
1584
            xValues, ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : [];
1585
        ids.forEach(function (id) {
1586
            if ($$.getXKey(id) === key) {
1587
                xValues = $$.data.xs[id];
1588
            }
1589
        });
1590
        return xValues;
1591
    };
1592
    c3_chart_internal_fn.getIndexByX = function (x) {
1593
        var $$ = this,
1594
            data = $$.filterByX($$.data.targets, x);
1595
        return data.length ? data[0].index : null;
1596
    };
1597
    c3_chart_internal_fn.getXValue = function (id, i) {
1598
        var $$ = this;
1599
        return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i;
1600
    };
1601
    c3_chart_internal_fn.getOtherTargetXs = function () {
1602
        var $$ = this,
1603
            idsForX = Object.keys($$.data.xs);
1604
        return idsForX.length ? $$.data.xs[idsForX[0]] : null;
1605
    };
1606
    c3_chart_internal_fn.getOtherTargetX = function (index) {
1607
        var xs = this.getOtherTargetXs();
1608
        return xs && index < xs.length ? xs[index] : null;
1609
    };
1610
    c3_chart_internal_fn.addXs = function (xs) {
1611
        var $$ = this;
1612
        Object.keys(xs).forEach(function (id) {
1613
            $$.config.data_xs[id] = xs[id];
1614
        });
1615
    };
1616
    c3_chart_internal_fn.hasMultipleX = function (xs) {
1617
        return this.d3.set(Object.keys(xs).map(function (id) { return xs[id]; })).size() > 1;
1618
    };
1619
    c3_chart_internal_fn.isMultipleX = function () {
1620
        return notEmpty(this.config.data_xs) || !this.config.data_xSort || this.hasType('scatter');
1621
    };
1622
    c3_chart_internal_fn.addName = function (data) {
1623
        var $$ = this, name;
1624
        if (data) {
1625
            name = $$.config.data_names[data.id];
1626
            data.name = name ? name : data.id;
1627
        }
1628
        return data;
1629
    };
1630
    c3_chart_internal_fn.getValueOnIndex = function (values, index) {
1631
        var valueOnIndex = values.filter(function (v) { return v.index === index; });
1632
        return valueOnIndex.length ? valueOnIndex[0] : null;
1633
    };
1634
    c3_chart_internal_fn.updateTargetX = function (targets, x) {
1635
        var $$ = this;
1636
        targets.forEach(function (t) {
1637
            t.values.forEach(function (v, i) {
1638
                v.x = $$.generateTargetX(x[i], t.id, i);
1639
            });
1640
            $$.data.xs[t.id] = x;
1641
        });
1642
    };
1643
    c3_chart_internal_fn.updateTargetXs = function (targets, xs) {
1644
        var $$ = this;
1645
        targets.forEach(function (t) {
1646
            if (xs[t.id]) {
1647
                $$.updateTargetX([t], xs[t.id]);
1648
            }
1649
        });
1650
    };
1651
    c3_chart_internal_fn.generateTargetX = function (rawX, id, index) {
1652
        var $$ = this, x;
1653
        if ($$.isTimeSeries()) {
1654
            x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index));
1655
        }
1656
        else if ($$.isCustomX() && !$$.isCategorized()) {
1657
            x = isValue(rawX) ? +rawX : $$.getXValue(id, index);
1658
        }
1659
        else {
1660
            x = index;
1661
        }
1662
        return x;
1663
    };
1664
    c3_chart_internal_fn.cloneTarget = function (target) {
1665
        return {
1666
            id : target.id,
1667
            id_org : target.id_org,
1668
            values : target.values.map(function (d) {
1669
                return {x: d.x, value: d.value, id: d.id};
1670
            })
1671
        };
1672
    };
1673
    c3_chart_internal_fn.updateXs = function () {
1674
        var $$ = this;
1675
        if ($$.data.targets.length) {
1676
            $$.xs = [];
1677
            $$.data.targets[0].values.forEach(function (v) {
1678
                $$.xs[v.index] = v.x;
1679
            });
1680
        }
1681
    };
1682
    c3_chart_internal_fn.getPrevX = function (i) {
1683
        var x = this.xs[i - 1];
1684
        return typeof x !== 'undefined' ? x : null;
1685
    };
1686
    c3_chart_internal_fn.getNextX = function (i) {
1687
        var x = this.xs[i + 1];
1688
        return typeof x !== 'undefined' ? x : null;
1689
    };
1690
    c3_chart_internal_fn.getMaxDataCount = function () {
1691
        var $$ = this;
1692
        return $$.d3.max($$.data.targets, function (t) { return t.values.length; });
1693
    };
1694
    c3_chart_internal_fn.getMaxDataCountTarget = function (targets) {
1695
        var length = targets.length, max = 0, maxTarget;
1696
        if (length > 1) {
1697
            targets.forEach(function (t) {
1698
                if (t.values.length > max) {
1699
                    maxTarget = t;
1700
                    max = t.values.length;
1701
                }
1702
            });
1703
        } else {
1704
            maxTarget = length ? targets[0] : null;
1705
        }
1706
        return maxTarget;
1707
    };
1708
    c3_chart_internal_fn.getEdgeX = function (targets) {
1709
        var $$ = this;
1710
        return !targets.length ? [0, 0] : [
1711
            $$.d3.min(targets, function (t) { return t.values[0].x; }),
1712
            $$.d3.max(targets, function (t) { return t.values[t.values.length - 1].x; })
1713
        ];
1714
    };
1715
    c3_chart_internal_fn.mapToIds = function (targets) {
1716
        return targets.map(function (d) { return d.id; });
1717
    };
1718
    c3_chart_internal_fn.mapToTargetIds = function (ids) {
1719
        var $$ = this;
1720
        return ids ? (isString(ids) ? [ids] : ids) : $$.mapToIds($$.data.targets);
1721
    };
1722
    c3_chart_internal_fn.hasTarget = function (targets, id) {
1723
        var ids = this.mapToIds(targets), i;
1724
        for (i = 0; i < ids.length; i++) {
1725
            if (ids[i] === id) {
1726
                return true;
1727
            }
1728
        }
1729
        return false;
1730
    };
1731
    c3_chart_internal_fn.isTargetToShow = function (targetId) {
1732
        return this.hiddenTargetIds.indexOf(targetId) < 0;
1733
    };
1734
    c3_chart_internal_fn.isLegendToShow = function (targetId) {
1735
        return this.hiddenLegendIds.indexOf(targetId) < 0;
1736
    };
1737
    c3_chart_internal_fn.filterTargetsToShow = function (targets) {
1738
        var $$ = this;
1739
        return targets.filter(function (t) { return $$.isTargetToShow(t.id); });
1740
    };
1741
    c3_chart_internal_fn.mapTargetsToUniqueXs = function (targets) {
1742
        var $$ = this;
1743
        var xs = $$.d3.set($$.d3.merge(targets.map(function (t) { return t.values.map(function (v) { return +v.x; }); }))).values();
1744
        return $$.isTimeSeries() ? xs.map(function (x) { return new Date(+x); }) : xs.map(function (x) { return +x; });
1745
    };
1746
    c3_chart_internal_fn.addHiddenTargetIds = function (targetIds) {
1747
        this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds);
1748
    };
1749
    c3_chart_internal_fn.removeHiddenTargetIds = function (targetIds) {
1750
        this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; });
1751
    };
1752
    c3_chart_internal_fn.addHiddenLegendIds = function (targetIds) {
1753
        this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds);
1754
    };
1755
    c3_chart_internal_fn.removeHiddenLegendIds = function (targetIds) {
1756
        this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) { return targetIds.indexOf(id) < 0; });
1757
    };
1758
    c3_chart_internal_fn.getValuesAsIdKeyed = function (targets) {
1759
        var ys = {};
1760
        targets.forEach(function (t) {
1761
            ys[t.id] = [];
1762
            t.values.forEach(function (v) {
1763
                ys[t.id].push(v.value);
1764
            });
1765
        });
1766
        return ys;
1767
    };
1768
    c3_chart_internal_fn.checkValueInTargets = function (targets, checker) {
1769
        var ids = Object.keys(targets), i, j, values;
1770
        for (i = 0; i < ids.length; i++) {
1771
            values = targets[ids[i]].values;
1772
            for (j = 0; j < values.length; j++) {
1773
                if (checker(values[j].value)) {
1774
                    return true;
1775
                }
1776
            }
1777
        }
1778
        return false;
1779
    };
1780
    c3_chart_internal_fn.hasNegativeValueInTargets = function (targets) {
1781
        return this.checkValueInTargets(targets, function (v) { return v < 0; });
1782
    };
1783
    c3_chart_internal_fn.hasPositiveValueInTargets = function (targets) {
1784
        return this.checkValueInTargets(targets, function (v) { return v > 0; });
1785
    };
1786
    c3_chart_internal_fn.isOrderDesc = function () {
1787
        var config = this.config;
1788
        return typeof(config.data_order) === 'string' && config.data_order.toLowerCase() === 'desc';
1789
    };
1790
    c3_chart_internal_fn.isOrderAsc = function () {
1791
        var config = this.config;
1792
        return typeof(config.data_order) === 'string' && config.data_order.toLowerCase() === 'asc';
1793
    };
1794
    c3_chart_internal_fn.orderTargets = function (targets) {
1795
        var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc();
1796
        if (orderAsc || orderDesc) {
1797
            targets.sort(function (t1, t2) {
1798
                var reducer = function (p, c) { return p + Math.abs(c.value); };
1799
                var t1Sum = t1.values.reduce(reducer, 0),
1800
                    t2Sum = t2.values.reduce(reducer, 0);
1801
                return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum;
1802
            });
1803
        } else if (isFunction(config.data_order)) {
1804
            targets.sort(config.data_order);
1805
        } // TODO: accept name array for order
1806
        return targets;
1807
    };
1808
    c3_chart_internal_fn.filterByX = function (targets, x) {
1809
        return this.d3.merge(targets.map(function (t) { return t.values; })).filter(function (v) { return v.x - x === 0; });
1810
    };
1811
    c3_chart_internal_fn.filterRemoveNull = function (data) {
1812
        return data.filter(function (d) { return isValue(d.value); });
1813
    };
1814
    c3_chart_internal_fn.filterByXDomain = function (targets, xDomain) {
1815
        return targets.map(function (t) {
1816
            return {
1817
                id: t.id,
1818
                id_org: t.id_org,
1819
                values: t.values.filter(function (v) {
1820
                    return xDomain[0] <= v.x && v.x <= xDomain[1];
1821
                })
1822
            };
1823
        });
1824
    };
1825
    c3_chart_internal_fn.hasDataLabel = function () {
1826
        var config = this.config;
1827
        if (typeof config.data_labels === 'boolean' && config.data_labels) {
1828
            return true;
1829
        } else if (typeof config.data_labels === 'object' && notEmpty(config.data_labels)) {
1830
            return true;
1831
        }
1832
        return false;
1833
    };
1834
    c3_chart_internal_fn.getDataLabelLength = function (min, max, key) {
1835
        var $$ = this,
1836
            lengths = [0, 0], paddingCoef = 1.3;
1837
        $$.selectChart.select('svg').selectAll('.dummy')
1838
            .data([min, max])
1839
            .enter().append('text')
1840
            .text(function (d) { return $$.dataLabelFormat(d.id)(d); })
1841
            .each(function (d, i) {
1842
                lengths[i] = this.getBoundingClientRect()[key] * paddingCoef;
1843
            })
1844
            .remove();
1845
        return lengths;
1846
    };
1847
    c3_chart_internal_fn.isNoneArc = function (d) {
1848
        return this.hasTarget(this.data.targets, d.id);
1849
    },
1850
    c3_chart_internal_fn.isArc = function (d) {
1851
        return 'data' in d && this.hasTarget(this.data.targets, d.data.id);
1852
    };
1853
    c3_chart_internal_fn.findSameXOfValues = function (values, index) {
1854
        var i, targetX = values[index].x, sames = [];
1855
        for (i = index - 1; i >= 0; i--) {
1856
            if (targetX !== values[i].x) { break; }
1857
            sames.push(values[i]);
1858
        }
1859
        for (i = index; i < values.length; i++) {
1860
            if (targetX !== values[i].x) { break; }
1861
            sames.push(values[i]);
1862
        }
1863
        return sames;
1864
    };
1865

    
1866
    c3_chart_internal_fn.findClosestFromTargets = function (targets, pos) {
1867
        var $$ = this, candidates;
1868

    
1869
        // map to array of closest points of each target
1870
        candidates = targets.map(function (target) {
1871
            return $$.findClosest(target.values, pos);
1872
        });
1873

    
1874
        // decide closest point and return
1875
        return $$.findClosest(candidates, pos);
1876
    };
1877
    c3_chart_internal_fn.findClosest = function (values, pos) {
1878
        var $$ = this, minDist = 100, closest;
1879

    
1880
        // find mouseovering bar
1881
        values.filter(function (v) { return v && $$.isBarType(v.id); }).forEach(function (v) {
1882
            var shape = $$.main.select('.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index).node();
1883
            if (!closest && $$.isWithinBar(shape)) {
1884
                closest = v;
1885
            }
1886
        });
1887

    
1888
        // find closest point from non-bar
1889
        values.filter(function (v) { return v && !$$.isBarType(v.id); }).forEach(function (v) {
1890
            var d = $$.dist(v, pos);
1891
            if (d < minDist) {
1892
                minDist = d;
1893
                closest = v;
1894
            }
1895
        });
1896

    
1897
        return closest;
1898
    };
1899
    c3_chart_internal_fn.dist = function (data, pos) {
1900
        var $$ = this, config = $$.config,
1901
            xIndex = config.axis_rotated ? 1 : 0,
1902
            yIndex = config.axis_rotated ? 0 : 1,
1903
            y = $$.circleY(data, data.index),
1904
            x = $$.x(data.x);
1905
        return Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2);
1906
    };
1907
    c3_chart_internal_fn.convertValuesToStep = function (values) {
1908
        var converted = [].concat(values), i;
1909

    
1910
        if (!this.isCategorized()) {
1911
            return values;
1912
        }
1913

    
1914
        for (i = values.length + 1; 0 < i; i--) {
1915
            converted[i] = converted[i - 1];
1916
        }
1917

    
1918
        converted[0] = {
1919
            x: converted[0].x - 1,
1920
            value: converted[0].value,
1921
            id: converted[0].id
1922
        };
1923
        converted[values.length + 1] = {
1924
            x: converted[values.length].x + 1,
1925
            value: converted[values.length].value,
1926
            id: converted[values.length].id
1927
        };
1928

    
1929
        return converted;
1930
    };
1931
    c3_chart_internal_fn.updateDataAttributes = function (name, attrs) {
1932
        var $$ = this, config = $$.config, current = config['data_' + name];
1933
        if (typeof attrs === 'undefined') { return current; }
1934
        Object.keys(attrs).forEach(function (id) {
1935
            current[id] = attrs[id];
1936
        });
1937
        $$.redraw({withLegend: true});
1938
        return current;
1939
    };
1940

    
1941
    c3_chart_internal_fn.convertUrlToData = function (url, mimeType, keys, done) {
1942
        var $$ = this, type = mimeType ? mimeType : 'csv';
1943
        $$.d3.xhr(url, function (error, data) {
1944
            var d;
1945
            if (!data) {
1946
                throw new Error(error.responseURL + ' ' + error.status + ' (' + error.statusText + ')');
1947
            }
1948
            if (type === 'json') {
1949
                d = $$.convertJsonToData(JSON.parse(data.response), keys);
1950
            } else if (type === 'tsv') {
1951
                d = $$.convertTsvToData(data.response);
1952
            } else {
1953
                d = $$.convertCsvToData(data.response);
1954
            }
1955
            done.call($$, d);
1956
        });
1957
    };
1958
    c3_chart_internal_fn.convertXsvToData = function (xsv, parser) {
1959
        var rows = parser.parseRows(xsv), d;
1960
        if (rows.length === 1) {
1961
            d = [{}];
1962
            rows[0].forEach(function (id) {
1963
                d[0][id] = null;
1964
            });
1965
        } else {
1966
            d = parser.parse(xsv);
1967
        }
1968
        return d;
1969
    };
1970
    c3_chart_internal_fn.convertCsvToData = function (csv) {
1971
        return this.convertXsvToData(csv, this.d3.csv);
1972
    };
1973
    c3_chart_internal_fn.convertTsvToData = function (tsv) {
1974
        return this.convertXsvToData(tsv, this.d3.tsv);
1975
    };
1976
    c3_chart_internal_fn.convertJsonToData = function (json, keys) {
1977
        var $$ = this,
1978
            new_rows = [], targetKeys, data;
1979
        if (keys) { // when keys specified, json would be an array that includes objects
1980
            if (keys.x) {
1981
                targetKeys = keys.value.concat(keys.x);
1982
                $$.config.data_x = keys.x;
1983
            } else {
1984
                targetKeys = keys.value;
1985
            }
1986
            new_rows.push(targetKeys);
1987
            json.forEach(function (o) {
1988
                var new_row = [];
1989
                targetKeys.forEach(function (key) {
1990
                    // convert undefined to null because undefined data will be removed in convertDataToTargets()
1991
                    var v = isUndefined(o[key]) ? null : o[key];
1992
                    new_row.push(v);
1993
                });
1994
                new_rows.push(new_row);
1995
            });
1996
            data = $$.convertRowsToData(new_rows);
1997
        } else {
1998
            Object.keys(json).forEach(function (key) {
1999
                new_rows.push([key].concat(json[key]));
2000
            });
2001
            data = $$.convertColumnsToData(new_rows);
2002
        }
2003
        return data;
2004
    };
2005
    c3_chart_internal_fn.convertRowsToData = function (rows) {
2006
        var keys = rows[0], new_row = {}, new_rows = [], i, j;
2007
        for (i = 1; i < rows.length; i++) {
2008
            new_row = {};
2009
            for (j = 0; j < rows[i].length; j++) {
2010
                if (isUndefined(rows[i][j])) {
2011
                    throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
2012
                }
2013
                new_row[keys[j]] = rows[i][j];
2014
            }
2015
            new_rows.push(new_row);
2016
        }
2017
        return new_rows;
2018
    };
2019
    c3_chart_internal_fn.convertColumnsToData = function (columns) {
2020
        var new_rows = [], i, j, key;
2021
        for (i = 0; i < columns.length; i++) {
2022
            key = columns[i][0];
2023
            for (j = 1; j < columns[i].length; j++) {
2024
                if (isUndefined(new_rows[j - 1])) {
2025
                    new_rows[j - 1] = {};
2026
                }
2027
                if (isUndefined(columns[i][j])) {
2028
                    throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
2029
                }
2030
                new_rows[j - 1][key] = columns[i][j];
2031
            }
2032
        }
2033
        return new_rows;
2034
    };
2035
    c3_chart_internal_fn.convertDataToTargets = function (data, appendXs) {
2036
        var $$ = this, config = $$.config,
2037
            ids = $$.d3.keys(data[0]).filter($$.isNotX, $$),
2038
            xs = $$.d3.keys(data[0]).filter($$.isX, $$),
2039
            targets;
2040

    
2041
        // save x for update data by load when custom x and c3.x API
2042
        ids.forEach(function (id) {
2043
            var xKey = $$.getXKey(id);
2044

    
2045
            if ($$.isCustomX() || $$.isTimeSeries()) {
2046
                // if included in input data
2047
                if (xs.indexOf(xKey) >= 0) {
2048
                    $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : []).concat(
2049
                        data.map(function (d) { return d[xKey]; })
2050
                            .filter(isValue)
2051
                            .map(function (rawX, i) { return $$.generateTargetX(rawX, id, i); })
2052
                    );
2053
                }
2054
                // if not included in input data, find from preloaded data of other id's x
2055
                else if (config.data_x) {
2056
                    $$.data.xs[id] = $$.getOtherTargetXs();
2057
                }
2058
                // if not included in input data, find from preloaded data
2059
                else if (notEmpty(config.data_xs)) {
2060
                    $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets);
2061
                }
2062
                // MEMO: if no x included, use same x of current will be used
2063
            } else {
2064
                $$.data.xs[id] = data.map(function (d, i) { return i; });
2065
            }
2066
        });
2067

    
2068

    
2069
        // check x is defined
2070
        ids.forEach(function (id) {
2071
            if (!$$.data.xs[id]) {
2072
                throw new Error('x is not defined for id = "' + id + '".');
2073
            }
2074
        });
2075

    
2076
        // convert to target
2077
        targets = ids.map(function (id, index) {
2078
            var convertedId = config.data_idConverter(id);
2079
            return {
2080
                id: convertedId,
2081
                id_org: id,
2082
                values: data.map(function (d, i) {
2083
                    var xKey = $$.getXKey(id), rawX = d[xKey], x = $$.generateTargetX(rawX, id, i);
2084
                    // use x as categories if custom x and categorized
2085
                    if ($$.isCustomX() && $$.isCategorized() && index === 0 && rawX) {
2086
                        if (i === 0) { config.axis_x_categories = []; }
2087
                        config.axis_x_categories.push(rawX);
2088
                    }
2089
                    // mark as x = undefined if value is undefined and filter to remove after mapped
2090
                    if (isUndefined(d[id]) || $$.data.xs[id].length <= i) {
2091
                        x = undefined;
2092
                    }
2093
                    return {x: x, value: d[id] !== null && !isNaN(d[id]) ? +d[id] : null, id: convertedId};
2094
                }).filter(function (v) { return isDefined(v.x); })
2095
            };
2096
        });
2097

    
2098
        // finish targets
2099
        targets.forEach(function (t) {
2100
            var i;
2101
            // sort values by its x
2102
            if (config.data_xSort) {
2103
                t.values = t.values.sort(function (v1, v2) {
2104
                    var x1 = v1.x || v1.x === 0 ? v1.x : Infinity,
2105
                        x2 = v2.x || v2.x === 0 ? v2.x : Infinity;
2106
                    return x1 - x2;
2107
                });
2108
            }
2109
            // indexing each value
2110
            i = 0;
2111
            t.values.forEach(function (v) {
2112
                v.index = i++;
2113
            });
2114
            // this needs to be sorted because its index and value.index is identical
2115
            $$.data.xs[t.id].sort(function (v1, v2) {
2116
                return v1 - v2;
2117
            });
2118
        });
2119

    
2120
        // set target types
2121
        if (config.data_type) {
2122
            $$.setTargetType($$.mapToIds(targets).filter(function (id) { return ! (id in config.data_types); }), config.data_type);
2123
        }
2124

    
2125
        // cache as original id keyed
2126
        targets.forEach(function (d) {
2127
            $$.addCache(d.id_org, d);
2128
        });
2129

    
2130
        return targets;
2131
    };
2132

    
2133
    c3_chart_internal_fn.load = function (targets, args) {
2134
        var $$ = this;
2135
        if (targets) {
2136
            // filter loading targets if needed
2137
            if (args.filter) {
2138
                targets = targets.filter(args.filter);
2139
            }
2140
            // set type if args.types || args.type specified
2141
            if (args.type || args.types) {
2142
                targets.forEach(function (t) {
2143
                    var type = args.types && args.types[t.id] ? args.types[t.id] : args.type;
2144
                    $$.setTargetType(t.id, type);
2145
                });
2146
            }
2147
            // Update/Add data
2148
            $$.data.targets.forEach(function (d) {
2149
                for (var i = 0; i < targets.length; i++) {
2150
                    if (d.id === targets[i].id) {
2151
                        d.values = targets[i].values;
2152
                        targets.splice(i, 1);
2153
                        break;
2154
                    }
2155
                }
2156
            });
2157
            $$.data.targets = $$.data.targets.concat(targets); // add remained
2158
        }
2159

    
2160
        // Set targets
2161
        $$.updateTargets($$.data.targets);
2162

    
2163
        // Redraw with new targets
2164
        $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
2165

    
2166
        if (args.done) { args.done(); }
2167
    };
2168
    c3_chart_internal_fn.loadFromArgs = function (args) {
2169
        var $$ = this;
2170
        if (args.data) {
2171
            $$.load($$.convertDataToTargets(args.data), args);
2172
        }
2173
        else if (args.url) {
2174
            $$.convertUrlToData(args.url, args.mimeType, args.keys, function (data) {
2175
                $$.load($$.convertDataToTargets(data), args);
2176
            });
2177
        }
2178
        else if (args.json) {
2179
            $$.load($$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args);
2180
        }
2181
        else if (args.rows) {
2182
            $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args);
2183
        }
2184
        else if (args.columns) {
2185
            $$.load($$.convertDataToTargets($$.convertColumnsToData(args.columns)), args);
2186
        }
2187
        else {
2188
            $$.load(null, args);
2189
        }
2190
    };
2191
    c3_chart_internal_fn.unload = function (targetIds, done) {
2192
        var $$ = this;
2193
        if (!done) {
2194
            done = function () {};
2195
        }
2196
        // filter existing target
2197
        targetIds = targetIds.filter(function (id) { return $$.hasTarget($$.data.targets, id); });
2198
        // If no target, call done and return
2199
        if (!targetIds || targetIds.length === 0) {
2200
            done();
2201
            return;
2202
        }
2203
        $$.svg.selectAll(targetIds.map(function (id) { return $$.selectorTarget(id); }))
2204
            .transition()
2205
            .style('opacity', 0)
2206
            .remove()
2207
            .call($$.endall, done);
2208
        targetIds.forEach(function (id) {
2209
            // Reset fadein for future load
2210
            $$.withoutFadeIn[id] = false;
2211
            // Remove target's elements
2212
            if ($$.legend) {
2213
                $$.legend.selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)).remove();
2214
            }
2215
            // Remove target
2216
            $$.data.targets = $$.data.targets.filter(function (t) {
2217
                return t.id !== id;
2218
            });
2219
        });
2220
    };
2221

    
2222
    c3_chart_internal_fn.categoryName = function (i) {
2223
        var config = this.config;
2224
        return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i;
2225
    };
2226

    
2227
    c3_chart_internal_fn.initEventRect = function () {
2228
        var $$ = this;
2229
        $$.main.select('.' + CLASS.chart).append("g")
2230
            .attr("class", CLASS.eventRects)
2231
            .style('fill-opacity', 0);
2232
    };
2233
    c3_chart_internal_fn.redrawEventRect = function () {
2234
        var $$ = this, config = $$.config,
2235
            eventRectUpdate, maxDataCountTarget,
2236
            isMultipleX = $$.isMultipleX();
2237

    
2238
        // rects for mouseover
2239
        var eventRects = $$.main.select('.' + CLASS.eventRects)
2240
                .style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null)
2241
                .classed(CLASS.eventRectsMultiple, isMultipleX)
2242
                .classed(CLASS.eventRectsSingle, !isMultipleX);
2243

    
2244
        // clear old rects
2245
        eventRects.selectAll('.' + CLASS.eventRect).remove();
2246

    
2247
        // open as public variable
2248
        $$.eventRect = eventRects.selectAll('.' + CLASS.eventRect);
2249

    
2250
        if (isMultipleX) {
2251
            eventRectUpdate = $$.eventRect.data([0]);
2252
            // enter : only one rect will be added
2253
            $$.generateEventRectsForMultipleXs(eventRectUpdate.enter());
2254
            // update
2255
            $$.updateEventRect(eventRectUpdate);
2256
            // exit : not needed because always only one rect exists
2257
        }
2258
        else {
2259
            // Set data and update $$.eventRect
2260
            maxDataCountTarget = $$.getMaxDataCountTarget($$.data.targets);
2261
            eventRects.datum(maxDataCountTarget ? maxDataCountTarget.values : []);
2262
            $$.eventRect = eventRects.selectAll('.' + CLASS.eventRect);
2263
            eventRectUpdate = $$.eventRect.data(function (d) { return d; });
2264
            // enter
2265
            $$.generateEventRectsForSingleX(eventRectUpdate.enter());
2266
            // update
2267
            $$.updateEventRect(eventRectUpdate);
2268
            // exit
2269
            eventRectUpdate.exit().remove();
2270
        }
2271
    };
2272
    c3_chart_internal_fn.updateEventRect = function (eventRectUpdate) {
2273
        var $$ = this, config = $$.config,
2274
            x, y, w, h, rectW, rectX;
2275

    
2276
        // set update selection if null
2277
        eventRectUpdate = eventRectUpdate || $$.eventRect.data(function (d) { return d; });
2278

    
2279
        if ($$.isMultipleX()) {
2280
            // TODO: rotated not supported yet
2281
            x = 0;
2282
            y = 0;
2283
            w = $$.width;
2284
            h = $$.height;
2285
        }
2286
        else {
2287
            if (($$.isCustomX() || $$.isTimeSeries()) && !$$.isCategorized()) {
2288

    
2289
                // update index for x that is used by prevX and nextX
2290
                $$.updateXs();
2291

    
2292
                rectW = function (d) {
2293
                    var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index);
2294

    
2295
                    // if there this is a single data point make the eventRect full width (or height)
2296
                    if (prevX === null && nextX === null) {
2297
                        return config.axis_rotated ? $$.height : $$.width;
2298
                    }
2299

    
2300
                    if (prevX === null) { prevX = $$.x.domain()[0]; }
2301
                    if (nextX === null) { nextX = $$.x.domain()[1]; }
2302

    
2303
                    return Math.max(0, ($$.x(nextX) - $$.x(prevX)) / 2);
2304
                };
2305
                rectX = function (d) {
2306
                    var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index),
2307
                        thisX = $$.data.xs[d.id][d.index];
2308

    
2309
                    // if there this is a single data point position the eventRect at 0
2310
                    if (prevX === null && nextX === null) {
2311
                        return 0;
2312
                    }
2313

    
2314
                    if (prevX === null) { prevX = $$.x.domain()[0]; }
2315

    
2316
                    return ($$.x(thisX) + $$.x(prevX)) / 2;
2317
                };
2318
            } else {
2319
                rectW = $$.getEventRectWidth();
2320
                rectX = function (d) {
2321
                    return $$.x(d.x) - (rectW / 2);
2322
                };
2323
            }
2324
            x = config.axis_rotated ? 0 : rectX;
2325
            y = config.axis_rotated ? rectX : 0;
2326
            w = config.axis_rotated ? $$.width : rectW;
2327
            h = config.axis_rotated ? rectW : $$.height;
2328
        }
2329

    
2330
        eventRectUpdate
2331
            .attr('class', $$.classEvent.bind($$))
2332
            .attr("x", x)
2333
            .attr("y", y)
2334
            .attr("width", w)
2335
            .attr("height", h);
2336
    };
2337
    c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) {
2338
        var $$ = this, d3 = $$.d3, config = $$.config;
2339
        eventRectEnter.append("rect")
2340
            .attr("class", $$.classEvent.bind($$))
2341
            .style("cursor", config.data_selection_enabled && config.data_selection_grouped ? "pointer" : null)
2342
            .on('mouseover', function (d) {
2343
                var index = d.index;
2344

    
2345
                if ($$.dragging || $$.flowing) { return; } // do nothing while dragging/flowing
2346
                if ($$.hasArcType()) { return; }
2347

    
2348
                // Expand shapes for selection
2349
                if (config.point_focus_expand_enabled) { $$.expandCircles(index, null, true); }
2350
                $$.expandBars(index, null, true);
2351

    
2352
                // Call event handler
2353
                $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) {
2354
                    config.data_onmouseover.call($$.api, d);
2355
                });
2356
            })
2357
            .on('mouseout', function (d) {
2358
                var index = d.index;
2359
                if (!$$.config) { return; } // chart is destroyed
2360
                if ($$.hasArcType()) { return; }
2361
                $$.hideXGridFocus();
2362
                $$.hideTooltip();
2363
                // Undo expanded shapes
2364
                $$.unexpandCircles();
2365
                $$.unexpandBars();
2366
                // Call event handler
2367
                $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) {
2368
                    config.data_onmouseout.call($$.api, d);
2369
                });
2370
            })
2371
            .on('mousemove', function (d) {
2372
                var selectedData, index = d.index,
2373
                    eventRect = $$.svg.select('.' + CLASS.eventRect + '-' + index);
2374

    
2375
                if ($$.dragging || $$.flowing) { return; } // do nothing while dragging/flowing
2376
                if ($$.hasArcType()) { return; }
2377

    
2378
                if ($$.isStepType(d) && $$.config.line_step_type === 'step-after' && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) {
2379
                    index -= 1;
2380
                }
2381

    
2382
                // Show tooltip
2383
                selectedData = $$.filterTargetsToShow($$.data.targets).map(function (t) {
2384
                    return $$.addName($$.getValueOnIndex(t.values, index));
2385
                });
2386

    
2387
                if (config.tooltip_grouped) {
2388
                    $$.showTooltip(selectedData, this);
2389
                    $$.showXGridFocus(selectedData);
2390
                }
2391

    
2392
                if (config.tooltip_grouped && (!config.data_selection_enabled || config.data_selection_grouped)) {
2393
                    return;
2394
                }
2395

    
2396
                $$.main.selectAll('.' + CLASS.shape + '-' + index)
2397
                    .each(function () {
2398
                        d3.select(this).classed(CLASS.EXPANDED, true);
2399
                        if (config.data_selection_enabled) {
2400
                            eventRect.style('cursor', config.data_selection_grouped ? 'pointer' : null);
2401
                        }
2402
                        if (!config.tooltip_grouped) {
2403
                            $$.hideXGridFocus();
2404
                            $$.hideTooltip();
2405
                            if (!config.data_selection_grouped) {
2406
                                $$.unexpandCircles(index);
2407
                                $$.unexpandBars(index);
2408
                            }
2409
                        }
2410
                    })
2411
                    .filter(function (d) {
2412
                        return $$.isWithinShape(this, d);
2413
                    })
2414
                    .each(function (d) {
2415
                        if (config.data_selection_enabled && (config.data_selection_grouped || config.data_selection_isselectable(d))) {
2416
                            eventRect.style('cursor', 'pointer');
2417
                        }
2418
                        if (!config.tooltip_grouped) {
2419
                            $$.showTooltip([d], this);
2420
                            $$.showXGridFocus([d]);
2421
                            if (config.point_focus_expand_enabled) { $$.expandCircles(index, d.id, true); }
2422
                            $$.expandBars(index, d.id, true);
2423
                        }
2424
                    });
2425
            })
2426
            .on('click', function (d) {
2427
                var index = d.index;
2428
                if ($$.hasArcType() || !$$.toggleShape) { return; }
2429
                if ($$.cancelClick) {
2430
                    $$.cancelClick = false;
2431
                    return;
2432
                }
2433
                if ($$.isStepType(d) && config.line_step_type === 'step-after' && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) {
2434
                    index -= 1;
2435
                }
2436
                $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) {
2437
                    if (config.data_selection_grouped || $$.isWithinShape(this, d)) {
2438
                        $$.toggleShape(this, d, index);
2439
                        $$.config.data_onclick.call($$.api, d, this);
2440
                    }
2441
                });
2442
            })
2443
            .call(
2444
                config.data_selection_draggable && $$.drag ? (
2445
                    d3.behavior.drag().origin(Object)
2446
                        .on('drag', function () { $$.drag(d3.mouse(this)); })
2447
                        .on('dragstart', function () { $$.dragstart(d3.mouse(this)); })
2448
                        .on('dragend', function () { $$.dragend(); })
2449
                ) : function () {}
2450
            );
2451
    };
2452

    
2453
    c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter) {
2454
        var $$ = this, d3 = $$.d3, config = $$.config;
2455

    
2456
        function mouseout() {
2457
            $$.svg.select('.' + CLASS.eventRect).style('cursor', null);
2458
            $$.hideXGridFocus();
2459
            $$.hideTooltip();
2460
            $$.unexpandCircles();
2461
            $$.unexpandBars();
2462
        }
2463

    
2464
        eventRectEnter.append('rect')
2465
            .attr('x', 0)
2466
            .attr('y', 0)
2467
            .attr('width', $$.width)
2468
            .attr('height', $$.height)
2469
            .attr('class', CLASS.eventRect)
2470
            .on('mouseout', function () {
2471
                if (!$$.config) { return; } // chart is destroyed
2472
                if ($$.hasArcType()) { return; }
2473
                mouseout();
2474
            })
2475
            .on('mousemove', function () {
2476
                var targetsToShow = $$.filterTargetsToShow($$.data.targets);
2477
                var mouse, closest, sameXData, selectedData;
2478

    
2479
                if ($$.dragging) { return; } // do nothing when dragging
2480
                if ($$.hasArcType(targetsToShow)) { return; }
2481

    
2482
                mouse = d3.mouse(this);
2483
                closest = $$.findClosestFromTargets(targetsToShow, mouse);
2484

    
2485
                if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id)) {
2486
                    config.data_onmouseout.call($$.api, $$.mouseover);
2487
                    $$.mouseover = undefined;
2488
                }
2489

    
2490
                if (! closest) {
2491
                    mouseout();
2492
                    return;
2493
                }
2494

    
2495
                if ($$.isScatterType(closest) || !config.tooltip_grouped) {
2496
                    sameXData = [closest];
2497
                } else {
2498
                    sameXData = $$.filterByX(targetsToShow, closest.x);
2499
                }
2500

    
2501
                // show tooltip when cursor is close to some point
2502
                selectedData = sameXData.map(function (d) {
2503
                    return $$.addName(d);
2504
                });
2505
                $$.showTooltip(selectedData, this);
2506

    
2507
                // expand points
2508
                if (config.point_focus_expand_enabled) {
2509
                    $$.expandCircles(closest.index, closest.id, true);
2510
                }
2511
                $$.expandBars(closest.index, closest.id, true);
2512

    
2513
                // Show xgrid focus line
2514
                $$.showXGridFocus(selectedData);
2515

    
2516
                // Show cursor as pointer if point is close to mouse position
2517
                if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < 100) {
2518
                    $$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer');
2519
                    if (!$$.mouseover) {
2520
                        config.data_onmouseover.call($$.api, closest);
2521
                        $$.mouseover = closest;
2522
                    }
2523
                }
2524
            })
2525
            .on('click', function () {
2526
                var targetsToShow = $$.filterTargetsToShow($$.data.targets);
2527
                var mouse, closest;
2528

    
2529
                if ($$.hasArcType(targetsToShow)) { return; }
2530

    
2531
                mouse = d3.mouse(this);
2532
                closest = $$.findClosestFromTargets(targetsToShow, mouse);
2533

    
2534
                if (! closest) { return; }
2535

    
2536
                // select if selection enabled
2537
                if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < 100) {
2538
                    $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id)).selectAll('.' + CLASS.shape + '-' + closest.index).each(function () {
2539
                        if (config.data_selection_grouped || $$.isWithinShape(this, closest)) {
2540
                            $$.toggleShape(this, closest, closest.index);
2541
                            $$.config.data_onclick.call($$.api, closest, this);
2542
                        }
2543
                    });
2544
                }
2545
            })
2546
            .call(
2547
                config.data_selection_draggable && $$.drag ? (
2548
                    d3.behavior.drag().origin(Object)
2549
                        .on('drag', function () { $$.drag(d3.mouse(this)); })
2550
                        .on('dragstart', function () { $$.dragstart(d3.mouse(this)); })
2551
                        .on('dragend', function () { $$.dragend(); })
2552
                ) : function () {}
2553
            );
2554
    };
2555
    c3_chart_internal_fn.dispatchEvent = function (type, index, mouse) {
2556
        var $$ = this,
2557
            selector = '.' + CLASS.eventRect + (!$$.isMultipleX() ? '-' + index : ''),
2558
            eventRect = $$.main.select(selector).node(),
2559
            box = eventRect.getBoundingClientRect(),
2560
            x = box.left + (mouse ? mouse[0] : 0),
2561
            y = box.top + (mouse ? mouse[1] : 0),
2562
            event = document.createEvent("MouseEvents");
2563

    
2564
        event.initMouseEvent(type, true, true, window, 0, x, y, x, y,
2565
                             false, false, false, false, 0, null);
2566
        eventRect.dispatchEvent(event);
2567
    };
2568

    
2569
    c3_chart_internal_fn.getCurrentWidth = function () {
2570
        var $$ = this, config = $$.config;
2571
        return config.size_width ? config.size_width : $$.getParentWidth();
2572
    };
2573
    c3_chart_internal_fn.getCurrentHeight = function () {
2574
        var $$ = this, config = $$.config,
2575
            h = config.size_height ? config.size_height : $$.getParentHeight();
2576
        return h > 0 ? h : 320 / ($$.hasType('gauge') ? 2 : 1);
2577
    };
2578
    c3_chart_internal_fn.getCurrentPaddingTop = function () {
2579
        var config = this.config;
2580
        return isValue(config.padding_top) ? config.padding_top : 0;
2581
    };
2582
    c3_chart_internal_fn.getCurrentPaddingBottom = function () {
2583
        var config = this.config;
2584
        return isValue(config.padding_bottom) ? config.padding_bottom : 0;
2585
    };
2586
    c3_chart_internal_fn.getCurrentPaddingLeft = function (withoutRecompute) {
2587
        var $$ = this, config = $$.config;
2588
        if (isValue(config.padding_left)) {
2589
            return config.padding_left;
2590
        } else if (config.axis_rotated) {
2591
            return !config.axis_x_show ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40);
2592
        } else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated
2593
            return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1;
2594
        } else {
2595
            return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute));
2596
        }
2597
    };
2598
    c3_chart_internal_fn.getCurrentPaddingRight = function () {
2599
        var $$ = this, config = $$.config,
2600
            defaultPadding = 10, legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0;
2601
        if (isValue(config.padding_right)) {
2602
            return config.padding_right + 1; // 1 is needed not to hide tick line
2603
        } else if (config.axis_rotated) {
2604
            return defaultPadding + legendWidthOnRight;
2605
        } else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated
2606
            return 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0);
2607
        } else {
2608
            return ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight;
2609
        }
2610
    };
2611

    
2612
    c3_chart_internal_fn.getParentRectValue = function (key) {
2613
        var parent = this.selectChart.node(), v;
2614
        while (parent && parent.tagName !== 'BODY') {
2615
            try {
2616
                v = parent.getBoundingClientRect()[key];
2617
            } catch(e) {
2618
                if (key === 'width') {
2619
                    // In IE in certain cases getBoundingClientRect
2620
                    // will cause an "unspecified error"
2621
                    v = parent.offsetWidth;
2622
                }
2623
            }
2624
            if (v) {
2625
                break;
2626
            }
2627
            parent = parent.parentNode;
2628
        }
2629
        return v;
2630
    };
2631
    c3_chart_internal_fn.getParentWidth = function () {
2632
        return this.getParentRectValue('width');
2633
    };
2634
    c3_chart_internal_fn.getParentHeight = function () {
2635
        var h = this.selectChart.style('height');
2636
        return h.indexOf('px') > 0 ? +h.replace('px', '') : 0;
2637
    };
2638

    
2639

    
2640
    c3_chart_internal_fn.getSvgLeft = function (withoutRecompute) {
2641
        var $$ = this, config = $$.config,
2642
            hasLeftAxisRect = config.axis_rotated || (!config.axis_rotated && !config.axis_y_inner),
2643
            leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY,
2644
            leftAxis = $$.main.select('.' + leftAxisClass).node(),
2645
            svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : {right: 0},
2646
            chartRect = $$.selectChart.node().getBoundingClientRect(),
2647
            hasArc = $$.hasArcType(),
2648
            svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute));
2649
        return svgLeft > 0 ? svgLeft : 0;
2650
    };
2651

    
2652

    
2653
    c3_chart_internal_fn.getAxisWidthByAxisId = function (id, withoutRecompute) {
2654
        var $$ = this, position = $$.axis.getLabelPositionById(id);
2655
        return $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40);
2656
    };
2657
    c3_chart_internal_fn.getHorizontalAxisHeight = function (axisId) {
2658
        var $$ = this, config = $$.config, h = 30;
2659
        if (axisId === 'x' && !config.axis_x_show) { return 8; }
2660
        if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height; }
2661
        if (axisId === 'y' && !config.axis_y_show) { return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1; }
2662
        if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top; }
2663
        // Calculate x axis height when tick rotated
2664
        if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) {
2665
            h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - config.axis_x_tick_rotate) / 180);
2666
        }
2667
        return h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0);
2668
    };
2669

    
2670
    c3_chart_internal_fn.getEventRectWidth = function () {
2671
        return Math.max(0, this.xAxis.tickInterval());
2672
    };
2673

    
2674
    c3_chart_internal_fn.getShapeIndices = function (typeFilter) {
2675
        var $$ = this, config = $$.config,
2676
            indices = {}, i = 0, j, k;
2677
        $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach(function (d) {
2678
            for (j = 0; j < config.data_groups.length; j++) {
2679
                if (config.data_groups[j].indexOf(d.id) < 0) { continue; }
2680
                for (k = 0; k < config.data_groups[j].length; k++) {
2681
                    if (config.data_groups[j][k] in indices) {
2682
                        indices[d.id] = indices[config.data_groups[j][k]];
2683
                        break;
2684
                    }
2685
                }
2686
            }
2687
            if (isUndefined(indices[d.id])) { indices[d.id] = i++; }
2688
        });
2689
        indices.__max__ = i - 1;
2690
        return indices;
2691
    };
2692
    c3_chart_internal_fn.getShapeX = function (offset, targetsNum, indices, isSub) {
2693
        var $$ = this, scale = isSub ? $$.subX : $$.x;
2694
        return function (d) {
2695
            var index = d.id in indices ? indices[d.id] : 0;
2696
            return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0;
2697
        };
2698
    };
2699
    c3_chart_internal_fn.getShapeY = function (isSub) {
2700
        var $$ = this;
2701
        return function (d) {
2702
            var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id);
2703
            return scale(d.value);
2704
        };
2705
    };
2706
    c3_chart_internal_fn.getShapeOffset = function (typeFilter, indices, isSub) {
2707
        var $$ = this,
2708
            targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))),
2709
            targetIds = targets.map(function (t) { return t.id; });
2710
        return function (d, i) {
2711
            var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id),
2712
                y0 = scale(0), offset = y0;
2713
            targets.forEach(function (t) {
2714
                var values = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values;
2715
                if (t.id === d.id || indices[t.id] !== indices[d.id]) { return; }
2716
                if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) {
2717
                    if (values[i].value * d.value >= 0) {
2718
                        offset += scale(values[i].value) - y0;
2719
                    }
2720
                }
2721
            });
2722
            return offset;
2723
        };
2724
    };
2725
    c3_chart_internal_fn.isWithinShape = function (that, d) {
2726
        var $$ = this,
2727
            shape = $$.d3.select(that), isWithin;
2728
        if (!$$.isTargetToShow(d.id)) {
2729
            isWithin = false;
2730
        }
2731
        else if (that.nodeName === 'circle') {
2732
            isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5);
2733
        }
2734
        else if (that.nodeName === 'path') {
2735
            isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar(that) : true;
2736
        }
2737
        return isWithin;
2738
    };
2739

    
2740

    
2741
    c3_chart_internal_fn.getInterpolate = function (d) {
2742
        var $$ = this;
2743
        return $$.isSplineType(d) ? "cardinal" : $$.isStepType(d) ? $$.config.line_step_type : "linear";
2744
    };
2745

    
2746
    c3_chart_internal_fn.initLine = function () {
2747
        var $$ = this;
2748
        $$.main.select('.' + CLASS.chart).append("g")
2749
            .attr("class", CLASS.chartLines);
2750
    };
2751
    c3_chart_internal_fn.updateTargetsForLine = function (targets) {
2752
        var $$ = this, config = $$.config,
2753
            mainLineUpdate, mainLineEnter,
2754
            classChartLine = $$.classChartLine.bind($$),
2755
            classLines = $$.classLines.bind($$),
2756
            classAreas = $$.classAreas.bind($$),
2757
            classCircles = $$.classCircles.bind($$),
2758
            classFocus = $$.classFocus.bind($$);
2759
        mainLineUpdate = $$.main.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine)
2760
            .data(targets)
2761
            .attr('class', function (d) { return classChartLine(d) + classFocus(d); });
2762
        mainLineEnter = mainLineUpdate.enter().append('g')
2763
            .attr('class', classChartLine)
2764
            .style('opacity', 0)
2765
            .style("pointer-events", "none");
2766
        // Lines for each data
2767
        mainLineEnter.append('g')
2768
            .attr("class", classLines);
2769
        // Areas
2770
        mainLineEnter.append('g')
2771
            .attr('class', classAreas);
2772
        // Circles for each data point on lines
2773
        mainLineEnter.append('g')
2774
            .attr("class", function (d) { return $$.generateClass(CLASS.selectedCircles, d.id); });
2775
        mainLineEnter.append('g')
2776
            .attr("class", classCircles)
2777
            .style("cursor", function (d) { return config.data_selection_isselectable(d) ? "pointer" : null; });
2778
        // Update date for selected circles
2779
        targets.forEach(function (t) {
2780
            $$.main.selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)).selectAll('.' + CLASS.selectedCircle).each(function (d) {
2781
                d.value = t.values[d.index].value;
2782
            });
2783
        });
2784
        // MEMO: can not keep same color...
2785
        //mainLineUpdate.exit().remove();
2786
    };
2787
    c3_chart_internal_fn.updateLine = function (durationForExit) {
2788
        var $$ = this;
2789
        $$.mainLine = $$.main.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line)
2790
            .data($$.lineData.bind($$));
2791
        $$.mainLine.enter().append('path')
2792
            .attr('class', $$.classLine.bind($$))
2793
            .style("stroke", $$.color);
2794
        $$.mainLine
2795
            .style("opacity", $$.initialOpacity.bind($$))
2796
            .style('shape-rendering', function (d) { return $$.isStepType(d) ? 'crispEdges' : ''; })
2797
            .attr('transform', null);
2798
        $$.mainLine.exit().transition().duration(durationForExit)
2799
            .style('opacity', 0)
2800
            .remove();
2801
    };
2802
    c3_chart_internal_fn.redrawLine = function (drawLine, withTransition) {
2803
        return [
2804
            (withTransition ? this.mainLine.transition() : this.mainLine)
2805
                .attr("d", drawLine)
2806
                .style("stroke", this.color)
2807
                .style("opacity", 1)
2808
        ];
2809
    };
2810
    c3_chart_internal_fn.generateDrawLine = function (lineIndices, isSub) {
2811
        var $$ = this, config = $$.config,
2812
            line = $$.d3.svg.line(),
2813
            getPoints = $$.generateGetLinePoints(lineIndices, isSub),
2814
            yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale,
2815
            xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); },
2816
            yValue = function (d, i) {
2817
                return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value);
2818
            };
2819

    
2820
        line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue);
2821
        if (!config.line_connectNull) { line = line.defined(function (d) { return d.value != null; }); }
2822
        return function (d) {
2823
            var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
2824
                x = isSub ? $$.x : $$.subX, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path;
2825
            if ($$.isLineType(d)) {
2826
                if (config.data_regions[d.id]) {
2827
                    path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]);
2828
                } else {
2829
                    if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); }
2830
                    path = line.interpolate($$.getInterpolate(d))(values);
2831
                }
2832
            } else {
2833
                if (values[0]) {
2834
                    x0 = x(values[0].x);
2835
                    y0 = y(values[0].value);
2836
                }
2837
                path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
2838
            }
2839
            return path ? path : "M 0 0";
2840
        };
2841
    };
2842
    c3_chart_internal_fn.generateGetLinePoints = function (lineIndices, isSub) { // partial duplication of generateGetBarPoints
2843
        var $$ = this, config = $$.config,
2844
            lineTargetsNum = lineIndices.__max__ + 1,
2845
            x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub),
2846
            y = $$.getShapeY(!!isSub),
2847
            lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub),
2848
            yScale = isSub ? $$.getSubYScale : $$.getYScale;
2849
        return function (d, i) {
2850
            var y0 = yScale.call($$, d.id)(0),
2851
                offset = lineOffset(d, i) || y0, // offset is for stacked area chart
2852
                posX = x(d), posY = y(d);
2853
            // fix posY not to overflow opposite quadrant
2854
            if (config.axis_rotated) {
2855
                if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; }
2856
            }
2857
            // 1 point that marks the line position
2858
            return [
2859
                [posX, posY - (y0 - offset)],
2860
                [posX, posY - (y0 - offset)], // needed for compatibility
2861
                [posX, posY - (y0 - offset)], // needed for compatibility
2862
                [posX, posY - (y0 - offset)]  // needed for compatibility
2863
            ];
2864
        };
2865
    };
2866

    
2867

    
2868
    c3_chart_internal_fn.lineWithRegions = function (d, x, y, _regions) {
2869
        var $$ = this, config = $$.config,
2870
            prev = -1, i, j,
2871
            s = "M", sWithRegion,
2872
            xp, yp, dx, dy, dd, diff, diffx2,
2873
            xOffset = $$.isCategorized() ? 0.5 : 0,
2874
            xValue, yValue,
2875
            regions = [];
2876

    
2877
        function isWithinRegions(x, regions) {
2878
            var i;
2879
            for (i = 0; i < regions.length; i++) {
2880
                if (regions[i].start < x && x <= regions[i].end) { return true; }
2881
            }
2882
            return false;
2883
        }
2884

    
2885
        // Check start/end of regions
2886
        if (isDefined(_regions)) {
2887
            for (i = 0; i < _regions.length; i++) {
2888
                regions[i] = {};
2889
                if (isUndefined(_regions[i].start)) {
2890
                    regions[i].start = d[0].x;
2891
                } else {
2892
                    regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start;
2893
                }
2894
                if (isUndefined(_regions[i].end)) {
2895
                    regions[i].end = d[d.length - 1].x;
2896
                } else {
2897
                    regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end;
2898
                }
2899
            }
2900
        }
2901

    
2902
        // Set scales
2903
        xValue = config.axis_rotated ? function (d) { return y(d.value); } : function (d) { return x(d.x); };
2904
        yValue = config.axis_rotated ? function (d) { return x(d.x); } : function (d) { return y(d.value); };
2905

    
2906
        // Define svg generator function for region
2907
        function generateM(points) {
2908
            return 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1];
2909
        }
2910
        if ($$.isTimeSeries()) {
2911
            sWithRegion = function (d0, d1, j, diff) {
2912
                var x0 = d0.x.getTime(), x_diff = d1.x - d0.x,
2913
                    xv0 = new Date(x0 + x_diff * j),
2914
                    xv1 = new Date(x0 + x_diff * (j + diff)),
2915
                    points;
2916
                if (config.axis_rotated) {
2917
                    points = [[y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)]];
2918
                } else {
2919
                    points = [[x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))]];
2920
                }
2921
                return generateM(points);
2922
            };
2923
        } else {
2924
            sWithRegion = function (d0, d1, j, diff) {
2925
                var points;
2926
                if (config.axis_rotated) {
2927
                    points = [[y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))]];
2928
                } else {
2929
                    points = [[x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))]];
2930
                }
2931
                return generateM(points);
2932
            };
2933
        }
2934

    
2935
        // Generate
2936
        for (i = 0; i < d.length; i++) {
2937

    
2938
            // Draw as normal
2939
            if (isUndefined(regions) || ! isWithinRegions(d[i].x, regions)) {
2940
                s += " " + xValue(d[i]) + " " + yValue(d[i]);
2941
            }
2942
            // Draw with region // TODO: Fix for horizotal charts
2943
            else {
2944
                xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries());
2945
                yp = $$.getScale(d[i - 1].value, d[i].value);
2946

    
2947
                dx = x(d[i].x) - x(d[i - 1].x);
2948
                dy = y(d[i].value) - y(d[i - 1].value);
2949
                dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
2950
                diff = 2 / dd;
2951
                diffx2 = diff * 2;
2952

    
2953
                for (j = diff; j <= 1; j += diffx2) {
2954
                    s += sWithRegion(d[i - 1], d[i], j, diff);
2955
                }
2956
            }
2957
            prev = d[i].x;
2958
        }
2959

    
2960
        return s;
2961
    };
2962

    
2963

    
2964
    c3_chart_internal_fn.updateArea = function (durationForExit) {
2965
        var $$ = this, d3 = $$.d3;
2966
        $$.mainArea = $$.main.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area)
2967
            .data($$.lineData.bind($$));
2968
        $$.mainArea.enter().append('path')
2969
            .attr("class", $$.classArea.bind($$))
2970
            .style("fill", $$.color)
2971
            .style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; });
2972
        $$.mainArea
2973
            .style("opacity", $$.orgAreaOpacity);
2974
        $$.mainArea.exit().transition().duration(durationForExit)
2975
            .style('opacity', 0)
2976
            .remove();
2977
    };
2978
    c3_chart_internal_fn.redrawArea = function (drawArea, withTransition) {
2979
        return [
2980
            (withTransition ? this.mainArea.transition() : this.mainArea)
2981
                .attr("d", drawArea)
2982
                .style("fill", this.color)
2983
                .style("opacity", this.orgAreaOpacity)
2984
        ];
2985
    };
2986
    c3_chart_internal_fn.generateDrawArea = function (areaIndices, isSub) {
2987
        var $$ = this, config = $$.config, area = $$.d3.svg.area(),
2988
            getPoints = $$.generateGetAreaPoints(areaIndices, isSub),
2989
            yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale,
2990
            xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); },
2991
            value0 = function (d, i) {
2992
                return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id));
2993
            },
2994
            value1 = function (d, i) {
2995
                return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value);
2996
            };
2997

    
2998
        area = config.axis_rotated ? area.x0(value0).x1(value1).y(xValue) : area.x(xValue).y0(value0).y1(value1);
2999
        if (!config.line_connectNull) {
3000
            area = area.defined(function (d) { return d.value !== null; });
3001
        }
3002

    
3003
        return function (d) {
3004
            var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
3005
                x0 = 0, y0 = 0, path;
3006
            if ($$.isAreaType(d)) {
3007
                if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); }
3008
                path = area.interpolate($$.getInterpolate(d))(values);
3009
            } else {
3010
                if (values[0]) {
3011
                    x0 = $$.x(values[0].x);
3012
                    y0 = $$.getYScale(d.id)(values[0].value);
3013
                }
3014
                path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
3015
            }
3016
            return path ? path : "M 0 0";
3017
        };
3018
    };
3019
    c3_chart_internal_fn.getAreaBaseValue = function () {
3020
        return 0;
3021
    };
3022
    c3_chart_internal_fn.generateGetAreaPoints = function (areaIndices, isSub) { // partial duplication of generateGetBarPoints
3023
        var $$ = this, config = $$.config,
3024
            areaTargetsNum = areaIndices.__max__ + 1,
3025
            x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub),
3026
            y = $$.getShapeY(!!isSub),
3027
            areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub),
3028
            yScale = isSub ? $$.getSubYScale : $$.getYScale;
3029
        return function (d, i) {
3030
            var y0 = yScale.call($$, d.id)(0),
3031
                offset = areaOffset(d, i) || y0, // offset is for stacked area chart
3032
                posX = x(d), posY = y(d);
3033
            // fix posY not to overflow opposite quadrant
3034
            if (config.axis_rotated) {
3035
                if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; }
3036
            }
3037
            // 1 point that marks the area position
3038
            return [
3039
                [posX, offset],
3040
                [posX, posY - (y0 - offset)],
3041
                [posX, posY - (y0 - offset)], // needed for compatibility
3042
                [posX, offset] // needed for compatibility
3043
            ];
3044
        };
3045
    };
3046

    
3047

    
3048
    c3_chart_internal_fn.updateCircle = function () {
3049
        var $$ = this;
3050
        $$.mainCircle = $$.main.selectAll('.' + CLASS.circles).selectAll('.' + CLASS.circle)
3051
            .data($$.lineOrScatterData.bind($$));
3052
        $$.mainCircle.enter().append("circle")
3053
            .attr("class", $$.classCircle.bind($$))
3054
            .attr("r", $$.pointR.bind($$))
3055
            .style("fill", $$.color);
3056
        $$.mainCircle
3057
            .style("opacity", $$.initialOpacityForCircle.bind($$));
3058
        $$.mainCircle.exit().remove();
3059
    };
3060
    c3_chart_internal_fn.redrawCircle = function (cx, cy, withTransition) {
3061
        var selectedCircles = this.main.selectAll('.' + CLASS.selectedCircle);
3062
        return [
3063
            (withTransition ? this.mainCircle.transition() : this.mainCircle)
3064
                .style('opacity', this.opacityForCircle.bind(this))
3065
                .style("fill", this.color)
3066
                .attr("cx", cx)
3067
                .attr("cy", cy),
3068
            (withTransition ? selectedCircles.transition() : selectedCircles)
3069
                .attr("cx", cx)
3070
                .attr("cy", cy)
3071
        ];
3072
    };
3073
    c3_chart_internal_fn.circleX = function (d) {
3074
        return d.x || d.x === 0 ? this.x(d.x) : null;
3075
    };
3076
    c3_chart_internal_fn.updateCircleY = function () {
3077
        var $$ = this, lineIndices, getPoints;
3078
        if ($$.config.data_groups.length > 0) {
3079
            lineIndices = $$.getShapeIndices($$.isLineType),
3080
            getPoints = $$.generateGetLinePoints(lineIndices);
3081
            $$.circleY = function (d, i) {
3082
                return getPoints(d, i)[0][1];
3083
            };
3084
        } else {
3085
            $$.circleY = function (d) {
3086
                return $$.getYScale(d.id)(d.value);
3087
            };
3088
        }
3089
    };
3090
    c3_chart_internal_fn.getCircles = function (i, id) {
3091
        var $$ = this;
3092
        return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : ''));
3093
    };
3094
    c3_chart_internal_fn.expandCircles = function (i, id, reset) {
3095
        var $$ = this,
3096
            r = $$.pointExpandedR.bind($$);
3097
        if (reset) { $$.unexpandCircles(); }
3098
        $$.getCircles(i, id)
3099
            .classed(CLASS.EXPANDED, true)
3100
            .attr('r', r);
3101
    };
3102
    c3_chart_internal_fn.unexpandCircles = function (i) {
3103
        var $$ = this,
3104
            r = $$.pointR.bind($$);
3105
        $$.getCircles(i)
3106
            .filter(function () { return $$.d3.select(this).classed(CLASS.EXPANDED); })
3107
            .classed(CLASS.EXPANDED, false)
3108
            .attr('r', r);
3109
    };
3110
    c3_chart_internal_fn.pointR = function (d) {
3111
        var $$ = this, config = $$.config;
3112
        return $$.isStepType(d) ? 0 : (isFunction(config.point_r) ? config.point_r(d) : config.point_r);
3113
    };
3114
    c3_chart_internal_fn.pointExpandedR = function (d) {
3115
        var $$ = this, config = $$.config;
3116
        return config.point_focus_expand_enabled ? (config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75) : $$.pointR(d);
3117
    };
3118
    c3_chart_internal_fn.pointSelectR = function (d) {
3119
        var $$ = this, config = $$.config;
3120
        return config.point_select_r ? config.point_select_r : $$.pointR(d) * 4;
3121
    };
3122
    c3_chart_internal_fn.isWithinCircle = function (that, r) {
3123
        var d3 = this.d3,
3124
            mouse = d3.mouse(that), d3_this = d3.select(that),
3125
            cx = +d3_this.attr("cx"), cy = +d3_this.attr("cy");
3126
        return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r;
3127
    };
3128
    c3_chart_internal_fn.isWithinStep = function (that, y) {
3129
        return Math.abs(y - this.d3.mouse(that)[1]) < 30;
3130
    };
3131

    
3132
    c3_chart_internal_fn.initBar = function () {
3133
        var $$ = this;
3134
        $$.main.select('.' + CLASS.chart).append("g")
3135
            .attr("class", CLASS.chartBars);
3136
    };
3137
    c3_chart_internal_fn.updateTargetsForBar = function (targets) {
3138
        var $$ = this, config = $$.config,
3139
            mainBarUpdate, mainBarEnter,
3140
            classChartBar = $$.classChartBar.bind($$),
3141
            classBars = $$.classBars.bind($$),
3142
            classFocus = $$.classFocus.bind($$);
3143
        mainBarUpdate = $$.main.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar)
3144
            .data(targets)
3145
            .attr('class', function (d) { return classChartBar(d) + classFocus(d); });
3146
        mainBarEnter = mainBarUpdate.enter().append('g')
3147
            .attr('class', classChartBar)
3148
            .style('opacity', 0)
3149
            .style("pointer-events", "none");
3150
        // Bars for each data
3151
        mainBarEnter.append('g')
3152
            .attr("class", classBars)
3153
            .style("cursor", function (d) { return config.data_selection_isselectable(d) ? "pointer" : null; });
3154

    
3155
    };
3156
    c3_chart_internal_fn.updateBar = function (durationForExit) {
3157
        var $$ = this,
3158
            barData = $$.barData.bind($$),
3159
            classBar = $$.classBar.bind($$),
3160
            initialOpacity = $$.initialOpacity.bind($$),
3161
            color = function (d) { return $$.color(d.id); };
3162
        $$.mainBar = $$.main.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar)
3163
            .data(barData);
3164
        $$.mainBar.enter().append('path')
3165
            .attr("class", classBar)
3166
            .style("stroke", color)
3167
            .style("fill", color);
3168
        $$.mainBar
3169
            .style("opacity", initialOpacity);
3170
        $$.mainBar.exit().transition().duration(durationForExit)
3171
            .style('opacity', 0)
3172
            .remove();
3173
    };
3174
    c3_chart_internal_fn.redrawBar = function (drawBar, withTransition) {
3175
        return [
3176
            (withTransition ? this.mainBar.transition() : this.mainBar)
3177
                .attr('d', drawBar)
3178
                .style("fill", this.color)
3179
                .style("opacity", 1)
3180
        ];
3181
    };
3182
    c3_chart_internal_fn.getBarW = function (axis, barTargetsNum) {
3183
        var $$ = this, config = $$.config,
3184
            w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickInterval() * config.bar_width_ratio) / barTargetsNum : 0;
3185
        return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w;
3186
    };
3187
    c3_chart_internal_fn.getBars = function (i, id) {
3188
        var $$ = this;
3189
        return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : ''));
3190
    };
3191
    c3_chart_internal_fn.expandBars = function (i, id, reset) {
3192
        var $$ = this;
3193
        if (reset) { $$.unexpandBars(); }
3194
        $$.getBars(i, id).classed(CLASS.EXPANDED, true);
3195
    };
3196
    c3_chart_internal_fn.unexpandBars = function (i) {
3197
        var $$ = this;
3198
        $$.getBars(i).classed(CLASS.EXPANDED, false);
3199
    };
3200
    c3_chart_internal_fn.generateDrawBar = function (barIndices, isSub) {
3201
        var $$ = this, config = $$.config,
3202
            getPoints = $$.generateGetBarPoints(barIndices, isSub);
3203
        return function (d, i) {
3204
            // 4 points that make a bar
3205
            var points = getPoints(d, i);
3206

    
3207
            // switch points if axis is rotated, not applicable for sub chart
3208
            var indexX = config.axis_rotated ? 1 : 0;
3209
            var indexY = config.axis_rotated ? 0 : 1;
3210

    
3211
            var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' +
3212
                    'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' +
3213
                    'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' +
3214
                    'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' +
3215
                    'z';
3216

    
3217
            return path;
3218
        };
3219
    };
3220
    c3_chart_internal_fn.generateGetBarPoints = function (barIndices, isSub) {
3221
        var $$ = this,
3222
            axis = isSub ? $$.subXAxis : $$.xAxis,
3223
            barTargetsNum = barIndices.__max__ + 1,
3224
            barW = $$.getBarW(axis, barTargetsNum),
3225
            barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub),
3226
            barY = $$.getShapeY(!!isSub),
3227
            barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub),
3228
            yScale = isSub ? $$.getSubYScale : $$.getYScale;
3229
        return function (d, i) {
3230
            var y0 = yScale.call($$, d.id)(0),
3231
                offset = barOffset(d, i) || y0, // offset is for stacked bar chart
3232
                posX = barX(d), posY = barY(d);
3233
            // fix posY not to overflow opposite quadrant
3234
            if ($$.config.axis_rotated) {
3235
                if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; }
3236
            }
3237
            // 4 points that make a bar
3238
            return [
3239
                [posX, offset],
3240
                [posX, posY - (y0 - offset)],
3241
                [posX + barW, posY - (y0 - offset)],
3242
                [posX + barW, offset]
3243
            ];
3244
        };
3245
    };
3246
    c3_chart_internal_fn.isWithinBar = function (that) {
3247
        var mouse = this.d3.mouse(that), box = that.getBoundingClientRect(),
3248
            seg0 = that.pathSegList.getItem(0), seg1 = that.pathSegList.getItem(1),
3249
            x = Math.min(seg0.x, seg1.x), y = Math.min(seg0.y, seg1.y),
3250
            w = box.width, h = box.height, offset = 2,
3251
            sx = x - offset, ex = x + w + offset, sy = y + h + offset, ey = y - offset;
3252
        return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
3253
    };
3254

    
3255
    c3_chart_internal_fn.initText = function () {
3256
        var $$ = this;
3257
        $$.main.select('.' + CLASS.chart).append("g")
3258
            .attr("class", CLASS.chartTexts);
3259
        $$.mainText = $$.d3.selectAll([]);
3260
    };
3261
    c3_chart_internal_fn.updateTargetsForText = function (targets) {
3262
        var $$ = this, mainTextUpdate, mainTextEnter,
3263
            classChartText = $$.classChartText.bind($$),
3264
            classTexts = $$.classTexts.bind($$),
3265
            classFocus = $$.classFocus.bind($$);
3266
        mainTextUpdate = $$.main.select('.' + CLASS.chartTexts).selectAll('.' + CLASS.chartText)
3267
            .data(targets)
3268
            .attr('class', function (d) { return classChartText(d) + classFocus(d); });
3269
        mainTextEnter = mainTextUpdate.enter().append('g')
3270
            .attr('class', classChartText)
3271
            .style('opacity', 0)
3272
            .style("pointer-events", "none");
3273
        mainTextEnter.append('g')
3274
            .attr('class', classTexts);
3275
    };
3276
    c3_chart_internal_fn.updateText = function (durationForExit) {
3277
        var $$ = this, config = $$.config,
3278
            barOrLineData = $$.barOrLineData.bind($$),
3279
            classText = $$.classText.bind($$);
3280
        $$.mainText = $$.main.selectAll('.' + CLASS.texts).selectAll('.' + CLASS.text)
3281
            .data(barOrLineData);
3282
        $$.mainText.enter().append('text')
3283
            .attr("class", classText)
3284
            .attr('text-anchor', function (d) { return config.axis_rotated ? (d.value < 0 ? 'end' : 'start') : 'middle'; })
3285
            .style("stroke", 'none')
3286
            .style("fill", function (d) { return $$.color(d); })
3287
            .style("fill-opacity", 0);
3288
        $$.mainText
3289
            .text(function (d, i, j) { return $$.dataLabelFormat(d.id)(d.value, d.id, i, j); });
3290
        $$.mainText.exit()
3291
            .transition().duration(durationForExit)
3292
            .style('fill-opacity', 0)
3293
            .remove();
3294
    };
3295
    c3_chart_internal_fn.redrawText = function (xForText, yForText, forFlow, withTransition) {
3296
        return [
3297
            (withTransition ? this.mainText.transition() : this.mainText)
3298
                .attr('x', xForText)
3299
                .attr('y', yForText)
3300
                .style("fill", this.color)
3301
                .style("fill-opacity", forFlow ? 0 : this.opacityForText.bind(this))
3302
        ];
3303
    };
3304
    c3_chart_internal_fn.getTextRect = function (text, cls) {
3305
        var dummy = this.d3.select('body').append('div').classed('c3', true),
3306
            svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0),
3307
            rect;
3308
        svg.selectAll('.dummy')
3309
            .data([text])
3310
          .enter().append('text')
3311
            .classed(cls ? cls : "", true)
3312
            .text(text)
3313
          .each(function () { rect = this.getBoundingClientRect(); });
3314
        dummy.remove();
3315
        return rect;
3316
    };
3317
    c3_chart_internal_fn.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) {
3318
        var $$ = this,
3319
            getAreaPoints = $$.generateGetAreaPoints(areaIndices, false),
3320
            getBarPoints = $$.generateGetBarPoints(barIndices, false),
3321
            getLinePoints = $$.generateGetLinePoints(lineIndices, false),
3322
            getter = forX ? $$.getXForText : $$.getYForText;
3323
        return function (d, i) {
3324
            var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints;
3325
            return getter.call($$, getPoints(d, i), d, this);
3326
        };
3327
    };
3328
    c3_chart_internal_fn.getXForText = function (points, d, textElement) {
3329
        var $$ = this,
3330
            box = textElement.getBoundingClientRect(), xPos, padding;
3331
        if ($$.config.axis_rotated) {
3332
            padding = $$.isBarType(d) ? 4 : 6;
3333
            xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1);
3334
        } else {
3335
            xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0];
3336
        }
3337
        // show labels regardless of the domain if value is null
3338
        if (d.value === null) {
3339
            if (xPos > $$.width) {
3340
                xPos = $$.width - box.width;
3341
            } else if (xPos < 0) {
3342
                xPos = 4;
3343
            }
3344
        }
3345
        return xPos;
3346
    };
3347
    c3_chart_internal_fn.getYForText = function (points, d, textElement) {
3348
        var $$ = this,
3349
            box = textElement.getBoundingClientRect(),
3350
            yPos;
3351
        if ($$.config.axis_rotated) {
3352
            yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2;
3353
        } else {
3354
            yPos = points[2][1];
3355
            if (d.value < 0) {
3356
                yPos += box.height;
3357
                if ($$.isBarType(d) && $$.isSafari()) {
3358
                    yPos -= 3;
3359
                }
3360
                else if (!$$.isBarType(d) && $$.isChrome()) {
3361
                    yPos += 3;
3362
                }
3363
            } else {
3364
                yPos += $$.isBarType(d) ? -3 : -6;
3365
            }
3366
        }
3367
        // show labels regardless of the domain if value is null
3368
        if (d.value === null && !$$.config.axis_rotated) {
3369
            if (yPos < box.height) {
3370
                yPos = box.height;
3371
            } else if (yPos > this.height) {
3372
                yPos = this.height - 4;
3373
            }
3374
        }
3375
        return yPos;
3376
    };
3377

    
3378
    c3_chart_internal_fn.setTargetType = function (targetIds, type) {
3379
        var $$ = this, config = $$.config;
3380
        $$.mapToTargetIds(targetIds).forEach(function (id) {
3381
            $$.withoutFadeIn[id] = (type === config.data_types[id]);
3382
            config.data_types[id] = type;
3383
        });
3384
        if (!targetIds) {
3385
            config.data_type = type;
3386
        }
3387
    };
3388
    c3_chart_internal_fn.hasType = function (type, targets) {
3389
        var $$ = this, types = $$.config.data_types, has = false;
3390
        targets = targets || $$.data.targets;
3391
        if (targets && targets.length) {
3392
            targets.forEach(function (target) {
3393
                var t = types[target.id];
3394
                if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) {
3395
                    has = true;
3396
                }
3397
            });
3398
        } else if (Object.keys(types).length) {
3399
            Object.keys(types).forEach(function (id) {
3400
                if (types[id] === type) { has = true; }
3401
            });
3402
        } else {
3403
            has = $$.config.data_type === type;
3404
        }
3405
        return has;
3406
    };
3407
    c3_chart_internal_fn.hasArcType = function (targets) {
3408
        return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
3409
    };
3410
    c3_chart_internal_fn.isLineType = function (d) {
3411
        var config = this.config, id = isString(d) ? d : d.id;
3412
        return !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0;
3413
    };
3414
    c3_chart_internal_fn.isStepType = function (d) {
3415
        var id = isString(d) ? d : d.id;
3416
        return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0;
3417
    };
3418
    c3_chart_internal_fn.isSplineType = function (d) {
3419
        var id = isString(d) ? d : d.id;
3420
        return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0;
3421
    };
3422
    c3_chart_internal_fn.isAreaType = function (d) {
3423
        var id = isString(d) ? d : d.id;
3424
        return ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0;
3425
    };
3426
    c3_chart_internal_fn.isBarType = function (d) {
3427
        var id = isString(d) ? d : d.id;
3428
        return this.config.data_types[id] === 'bar';
3429
    };
3430
    c3_chart_internal_fn.isScatterType = function (d) {
3431
        var id = isString(d) ? d : d.id;
3432
        return this.config.data_types[id] === 'scatter';
3433
    };
3434
    c3_chart_internal_fn.isPieType = function (d) {
3435
        var id = isString(d) ? d : d.id;
3436
        return this.config.data_types[id] === 'pie';
3437
    };
3438
    c3_chart_internal_fn.isGaugeType = function (d) {
3439
        var id = isString(d) ? d : d.id;
3440
        return this.config.data_types[id] === 'gauge';
3441
    };
3442
    c3_chart_internal_fn.isDonutType = function (d) {
3443
        var id = isString(d) ? d : d.id;
3444
        return this.config.data_types[id] === 'donut';
3445
    };
3446
    c3_chart_internal_fn.isArcType = function (d) {
3447
        return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
3448
    };
3449
    c3_chart_internal_fn.lineData = function (d) {
3450
        return this.isLineType(d) ? [d] : [];
3451
    };
3452
    c3_chart_internal_fn.arcData = function (d) {
3453
        return this.isArcType(d.data) ? [d] : [];
3454
    };
3455
    /* not used
3456
     function scatterData(d) {
3457
     return isScatterType(d) ? d.values : [];
3458
     }
3459
     */
3460
    c3_chart_internal_fn.barData = function (d) {
3461
        return this.isBarType(d) ? d.values : [];
3462
    };
3463
    c3_chart_internal_fn.lineOrScatterData = function (d) {
3464
        return this.isLineType(d) || this.isScatterType(d) ? d.values : [];
3465
    };
3466
    c3_chart_internal_fn.barOrLineData = function (d) {
3467
        return this.isBarType(d) || this.isLineType(d) ? d.values : [];
3468
    };
3469

    
3470
    c3_chart_internal_fn.initGrid = function () {
3471
        var $$ = this, config = $$.config, d3 = $$.d3;
3472
        $$.grid = $$.main.append('g')
3473
            .attr("clip-path", $$.clipPathForGrid)
3474
            .attr('class', CLASS.grid);
3475
        if (config.grid_x_show) {
3476
            $$.grid.append("g").attr("class", CLASS.xgrids);
3477
        }
3478
        if (config.grid_y_show) {
3479
            $$.grid.append('g').attr('class', CLASS.ygrids);
3480
        }
3481
        if (config.grid_focus_show) {
3482
            $$.grid.append('g')
3483
                .attr("class", CLASS.xgridFocus)
3484
                .append('line')
3485
                .attr('class', CLASS.xgridFocus);
3486
        }
3487
        $$.xgrid = d3.selectAll([]);
3488
        if (!config.grid_lines_front) { $$.initGridLines(); }
3489
    };
3490
    c3_chart_internal_fn.initGridLines = function () {
3491
        var $$ = this, d3 = $$.d3;
3492
        $$.gridLines = $$.main.append('g')
3493
            .attr("clip-path", $$.clipPathForGrid)
3494
            .attr('class', CLASS.grid + ' ' + CLASS.gridLines);
3495
        $$.gridLines.append('g').attr("class", CLASS.xgridLines);
3496
        $$.gridLines.append('g').attr('class', CLASS.ygridLines);
3497
        $$.xgridLines = d3.selectAll([]);
3498
    };
3499
    c3_chart_internal_fn.updateXGrid = function (withoutUpdate) {
3500
        var $$ = this, config = $$.config, d3 = $$.d3,
3501
            xgridData = $$.generateGridData(config.grid_x_type, $$.x),
3502
            tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0;
3503

    
3504
        $$.xgridAttr = config.axis_rotated ? {
3505
            'x1': 0,
3506
            'x2': $$.width,
3507
            'y1': function (d) { return $$.x(d) - tickOffset; },
3508
            'y2': function (d) { return $$.x(d) - tickOffset; }
3509
        } : {
3510
            'x1': function (d) { return $$.x(d) + tickOffset; },
3511
            'x2': function (d) { return $$.x(d) + tickOffset; },
3512
            'y1': 0,
3513
            'y2': $$.height
3514
        };
3515

    
3516
        $$.xgrid = $$.main.select('.' + CLASS.xgrids).selectAll('.' + CLASS.xgrid)
3517
            .data(xgridData);
3518
        $$.xgrid.enter().append('line').attr("class", CLASS.xgrid);
3519
        if (!withoutUpdate) {
3520
            $$.xgrid.attr($$.xgridAttr)
3521
                .style("opacity", function () { return +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1') === (config.axis_rotated ? $$.height : 0) ? 0 : 1; });
3522
        }
3523
        $$.xgrid.exit().remove();
3524
    };
3525

    
3526
    c3_chart_internal_fn.updateYGrid = function () {
3527
        var $$ = this, config = $$.config,
3528
            gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks);
3529
        $$.ygrid = $$.main.select('.' + CLASS.ygrids).selectAll('.' + CLASS.ygrid)
3530
            .data(gridValues);
3531
        $$.ygrid.enter().append('line')
3532
            .attr('class', CLASS.ygrid);
3533
        $$.ygrid.attr("x1", config.axis_rotated ? $$.y : 0)
3534
            .attr("x2", config.axis_rotated ? $$.y : $$.width)
3535
            .attr("y1", config.axis_rotated ? 0 : $$.y)
3536
            .attr("y2", config.axis_rotated ? $$.height : $$.y);
3537
        $$.ygrid.exit().remove();
3538
        $$.smoothLines($$.ygrid, 'grid');
3539
    };
3540

    
3541
    c3_chart_internal_fn.gridTextAnchor = function (d) {
3542
        return d.position ? d.position : "end";
3543
    };
3544
    c3_chart_internal_fn.gridTextDx = function (d) {
3545
        return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4;
3546
    };
3547
    c3_chart_internal_fn.xGridTextX = function (d) {
3548
        return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0;
3549
    };
3550
    c3_chart_internal_fn.yGridTextX = function (d) {
3551
        return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width;
3552
    };
3553
    c3_chart_internal_fn.updateGrid = function (duration) {
3554
        var $$ = this, main = $$.main, config = $$.config,
3555
            xgridLine, ygridLine, yv;
3556

    
3557
        // hide if arc type
3558
        $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible');
3559

    
3560
        main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
3561
        if (config.grid_x_show) {
3562
            $$.updateXGrid();
3563
        }
3564
        $$.xgridLines = main.select('.' + CLASS.xgridLines).selectAll('.' + CLASS.xgridLine)
3565
            .data(config.grid_x_lines);
3566
        // enter
3567
        xgridLine = $$.xgridLines.enter().append('g')
3568
            .attr("class", function (d) { return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : ''); });
3569
        xgridLine.append('line')
3570
            .style("opacity", 0);
3571
        xgridLine.append('text')
3572
            .attr("text-anchor", $$.gridTextAnchor)
3573
            .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
3574
            .attr('dx', $$.gridTextDx)
3575
            .attr('dy', -5)
3576
            .style("opacity", 0);
3577
        // udpate
3578
        // done in d3.transition() of the end of this function
3579
        // exit
3580
        $$.xgridLines.exit().transition().duration(duration)
3581
            .style("opacity", 0)
3582
            .remove();
3583

    
3584
        // Y-Grid
3585
        if (config.grid_y_show) {
3586
            $$.updateYGrid();
3587
        }
3588
        $$.ygridLines = main.select('.' + CLASS.ygridLines).selectAll('.' + CLASS.ygridLine)
3589
            .data(config.grid_y_lines);
3590
        // enter
3591
        ygridLine = $$.ygridLines.enter().append('g')
3592
            .attr("class", function (d) { return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : ''); });
3593
        ygridLine.append('line')
3594
            .style("opacity", 0);
3595
        ygridLine.append('text')
3596
            .attr("text-anchor", $$.gridTextAnchor)
3597
            .attr("transform", config.axis_rotated ? "rotate(-90)" : "")
3598
            .attr('dx', $$.gridTextDx)
3599
            .attr('dy', -5)
3600
            .style("opacity", 0);
3601
        // update
3602
        yv = $$.yv.bind($$);
3603
        $$.ygridLines.select('line')
3604
          .transition().duration(duration)
3605
            .attr("x1", config.axis_rotated ? yv : 0)
3606
            .attr("x2", config.axis_rotated ? yv : $$.width)
3607
            .attr("y1", config.axis_rotated ? 0 : yv)
3608
            .attr("y2", config.axis_rotated ? $$.height : yv)
3609
            .style("opacity", 1);
3610
        $$.ygridLines.select('text')
3611
          .transition().duration(duration)
3612
            .attr("x", config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$))
3613
            .attr("y", yv)
3614
            .text(function (d) { return d.text; })
3615
            .style("opacity", 1);
3616
        // exit
3617
        $$.ygridLines.exit().transition().duration(duration)
3618
            .style("opacity", 0)
3619
            .remove();
3620
    };
3621
    c3_chart_internal_fn.redrawGrid = function (withTransition) {
3622
        var $$ = this, config = $$.config, xv = $$.xv.bind($$),
3623
            lines = $$.xgridLines.select('line'),
3624
            texts = $$.xgridLines.select('text');
3625
        return [
3626
            (withTransition ? lines.transition() : lines)
3627
                .attr("x1", config.axis_rotated ? 0 : xv)
3628
                .attr("x2", config.axis_rotated ? $$.width : xv)
3629
                .attr("y1", config.axis_rotated ? xv : 0)
3630
                .attr("y2", config.axis_rotated ? xv : $$.height)
3631
                .style("opacity", 1),
3632
            (withTransition ? texts.transition() : texts)
3633
                .attr("x", config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$))
3634
                .attr("y", xv)
3635
                .text(function (d) { return d.text; })
3636
                .style("opacity", 1)
3637
        ];
3638
    };
3639
    c3_chart_internal_fn.showXGridFocus = function (selectedData) {
3640
        var $$ = this, config = $$.config,
3641
            dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }),
3642
            focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus),
3643
            xx = $$.xx.bind($$);
3644
        if (! config.tooltip_show) { return; }
3645
        // Hide when scatter plot exists
3646
        if ($$.hasType('scatter') || $$.hasArcType()) { return; }
3647
        focusEl
3648
            .style("visibility", "visible")
3649
            .data([dataToShow[0]])
3650
            .attr(config.axis_rotated ? 'y1' : 'x1', xx)
3651
            .attr(config.axis_rotated ? 'y2' : 'x2', xx);
3652
        $$.smoothLines(focusEl, 'grid');
3653
    };
3654
    c3_chart_internal_fn.hideXGridFocus = function () {
3655
        this.main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
3656
    };
3657
    c3_chart_internal_fn.updateXgridFocus = function () {
3658
        var $$ = this, config = $$.config;
3659
        $$.main.select('line.' + CLASS.xgridFocus)
3660
            .attr("x1", config.axis_rotated ? 0 : -10)
3661
            .attr("x2", config.axis_rotated ? $$.width : -10)
3662
            .attr("y1", config.axis_rotated ? -10 : 0)
3663
            .attr("y2", config.axis_rotated ? -10 : $$.height);
3664
    };
3665
    c3_chart_internal_fn.generateGridData = function (type, scale) {
3666
        var $$ = this,
3667
            gridData = [], xDomain, firstYear, lastYear, i,
3668
            tickNum = $$.main.select("." + CLASS.axisX).selectAll('.tick').size();
3669
        if (type === 'year') {
3670
            xDomain = $$.getXDomain();
3671
            firstYear = xDomain[0].getFullYear();
3672
            lastYear = xDomain[1].getFullYear();
3673
            for (i = firstYear; i <= lastYear; i++) {
3674
                gridData.push(new Date(i + '-01-01 00:00:00'));
3675
            }
3676
        } else {
3677
            gridData = scale.ticks(10);
3678
            if (gridData.length > tickNum) { // use only int
3679
                gridData = gridData.filter(function (d) { return ("" + d).indexOf('.') < 0; });
3680
            }
3681
        }
3682
        return gridData;
3683
    };
3684
    c3_chart_internal_fn.getGridFilterToRemove = function (params) {
3685
        return params ? function (line) {
3686
            var found = false;
3687
            [].concat(params).forEach(function (param) {
3688
                if ((('value' in param && line.value === param.value) || ('class' in param && line['class'] === param['class']))) {
3689
                    found = true;
3690
                }
3691
            });
3692
            return found;
3693
        } : function () { return true; };
3694
    };
3695
    c3_chart_internal_fn.removeGridLines = function (params, forX) {
3696
        var $$ = this, config = $$.config,
3697
            toRemove = $$.getGridFilterToRemove(params),
3698
            toShow = function (line) { return !toRemove(line); },
3699
            classLines = forX ? CLASS.xgridLines : CLASS.ygridLines,
3700
            classLine = forX ? CLASS.xgridLine : CLASS.ygridLine;
3701
        $$.main.select('.' + classLines).selectAll('.' + classLine).filter(toRemove)
3702
            .transition().duration(config.transition_duration)
3703
            .style('opacity', 0).remove();
3704
        if (forX) {
3705
            config.grid_x_lines = config.grid_x_lines.filter(toShow);
3706
        } else {
3707
            config.grid_y_lines = config.grid_y_lines.filter(toShow);
3708
        }
3709
    };
3710

    
3711
    c3_chart_internal_fn.initTooltip = function () {
3712
        var $$ = this, config = $$.config, i;
3713
        $$.tooltip = $$.selectChart
3714
            .style("position", "relative")
3715
          .append("div")
3716
            .attr('class', CLASS.tooltipContainer)
3717
            .style("position", "absolute")
3718
            .style("pointer-events", "none")
3719
            .style("display", "none");
3720
        // Show tooltip if needed
3721
        if (config.tooltip_init_show) {
3722
            if ($$.isTimeSeries() && isString(config.tooltip_init_x)) {
3723
                config.tooltip_init_x = $$.parseDate(config.tooltip_init_x);
3724
                for (i = 0; i < $$.data.targets[0].values.length; i++) {
3725
                    if (($$.data.targets[0].values[i].x - config.tooltip_init_x) === 0) { break; }
3726
                }
3727
                config.tooltip_init_x = i;
3728
            }
3729
            $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) {
3730
                return $$.addName(d.values[config.tooltip_init_x]);
3731
            }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color));
3732
            $$.tooltip.style("top", config.tooltip_init_position.top)
3733
                .style("left", config.tooltip_init_position.left)
3734
                .style("display", "block");
3735
        }
3736
    };
3737
    c3_chart_internal_fn.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) {
3738
        var $$ = this, config = $$.config,
3739
            titleFormat = config.tooltip_format_title || defaultTitleFormat,
3740
            nameFormat = config.tooltip_format_name || function (name) { return name; },
3741
            valueFormat = config.tooltip_format_value || defaultValueFormat,
3742
            text, i, title, value, name, bgcolor;
3743
        for (i = 0; i < d.length; i++) {
3744
            if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
3745

    
3746
            if (! text) {
3747
                title = titleFormat ? titleFormat(d[i].x) : d[i].x;
3748
                text = "<table class='" + CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
3749
            }
3750

    
3751
            value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
3752
            if (value !== undefined) {
3753
                name = nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index);
3754
                bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
3755

    
3756
                text += "<tr class='" + CLASS.tooltipName + "-" + d[i].id + "'>";
3757
                text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
3758
                text += "<td class='value'>" + value + "</td>";
3759
                text += "</tr>";
3760
            }
3761
        }
3762
        return text + "</table>";
3763
    };
3764
    c3_chart_internal_fn.tooltipPosition = function (dataToShow, tWidth, tHeight, element) {
3765
        var $$ = this, config = $$.config, d3 = $$.d3;
3766
        var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight;
3767
        var forArc = $$.hasArcType(),
3768
            mouse = d3.mouse(element);
3769
      // Determin tooltip position
3770
        if (forArc) {
3771
            tooltipLeft = (($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2) + mouse[0];
3772
            tooltipTop = ($$.height / 2) + mouse[1] + 20;
3773
        } else {
3774
            svgLeft = $$.getSvgLeft(true);
3775
            if (config.axis_rotated) {
3776
                tooltipLeft = svgLeft + mouse[0] + 100;
3777
                tooltipRight = tooltipLeft + tWidth;
3778
                chartRight = $$.currentWidth - $$.getCurrentPaddingRight();
3779
                tooltipTop = $$.x(dataToShow[0].x) + 20;
3780
            } else {
3781
                tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20;
3782
                tooltipRight = tooltipLeft + tWidth;
3783
                chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();
3784
                tooltipTop = mouse[1] + 15;
3785
            }
3786

    
3787
            if (tooltipRight > chartRight) {
3788
                // 20 is needed for Firefox to keep tooletip width
3789
                tooltipLeft -= tooltipRight - chartRight + 20;
3790
            }
3791
            if (tooltipTop + tHeight > $$.currentHeight) {
3792
                tooltipTop -= tHeight + 30;
3793
            }
3794
        }
3795
        if (tooltipTop < 0) {
3796
            tooltipTop = 0;
3797
        }
3798
        return {top: tooltipTop, left: tooltipLeft};
3799
    };
3800
    c3_chart_internal_fn.showTooltip = function (selectedData, element) {
3801
        var $$ = this, config = $$.config;
3802
        var tWidth, tHeight, position;
3803
        var forArc = $$.hasArcType(),
3804
            dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }),
3805
            positionFunction = config.tooltip_position || c3_chart_internal_fn.tooltipPosition;
3806
        if (dataToShow.length === 0 || !config.tooltip_show) {
3807
            return;
3808
        }
3809
        $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block");
3810

    
3811
        // Get tooltip dimensions
3812
        tWidth = $$.tooltip.property('offsetWidth');
3813
        tHeight = $$.tooltip.property('offsetHeight');
3814

    
3815
        position = positionFunction.call(this, dataToShow, tWidth, tHeight, element);
3816
        // Set tooltip
3817
        $$.tooltip
3818
            .style("top", position.top + "px")
3819
            .style("left", position.left + 'px');
3820
    };
3821
    c3_chart_internal_fn.hideTooltip = function () {
3822
        this.tooltip.style("display", "none");
3823
    };
3824

    
3825
    c3_chart_internal_fn.initLegend = function () {
3826
        var $$ = this;
3827
        $$.legendItemTextBox = {};
3828
        $$.legendHasRendered = false;
3829
        $$.legend = $$.svg.append("g").attr("transform", $$.getTranslate('legend'));
3830
        if (!$$.config.legend_show) {
3831
            $$.legend.style('visibility', 'hidden');
3832
            $$.hiddenLegendIds = $$.mapToIds($$.data.targets);
3833
            return;
3834
        }
3835
        // MEMO: call here to update legend box and tranlate for all
3836
        // MEMO: translate will be upated by this, so transform not needed in updateLegend()
3837
        $$.updateLegendWithDefaults();
3838
    };
3839
    c3_chart_internal_fn.updateLegendWithDefaults = function () {
3840
        var $$ = this;
3841
        $$.updateLegend($$.mapToIds($$.data.targets), {withTransform: false, withTransitionForTransform: false, withTransition: false});
3842
    };
3843
    c3_chart_internal_fn.updateSizeForLegend = function (legendHeight, legendWidth) {
3844
        var $$ = this, config = $$.config, insetLegendPosition = {
3845
            top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y,
3846
            left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5
3847
        };
3848

    
3849
        $$.margin3 = {
3850
            top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight,
3851
            right: NaN,
3852
            bottom: 0,
3853
            left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0
3854
        };
3855
    };
3856
    c3_chart_internal_fn.transformLegend = function (withTransition) {
3857
        var $$ = this;
3858
        (withTransition ? $$.legend.transition() : $$.legend).attr("transform", $$.getTranslate('legend'));
3859
    };
3860
    c3_chart_internal_fn.updateLegendStep = function (step) {
3861
        this.legendStep = step;
3862
    };
3863
    c3_chart_internal_fn.updateLegendItemWidth = function (w) {
3864
        this.legendItemWidth = w;
3865
    };
3866
    c3_chart_internal_fn.updateLegendItemHeight = function (h) {
3867
        this.legendItemHeight = h;
3868
    };
3869
    c3_chart_internal_fn.getLegendWidth = function () {
3870
        var $$ = this;
3871
        return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0;
3872
    };
3873
    c3_chart_internal_fn.getLegendHeight = function () {
3874
        var $$ = this, h = 0;
3875
        if ($$.config.legend_show) {
3876
            if ($$.isLegendRight) {
3877
                h = $$.currentHeight;
3878
            } else {
3879
                h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1);
3880
            }
3881
        }
3882
        return h;
3883
    };
3884
    c3_chart_internal_fn.opacityForLegend = function (legendItem) {
3885
        return legendItem.classed(CLASS.legendItemHidden) ? null : 1;
3886
    };
3887
    c3_chart_internal_fn.opacityForUnfocusedLegend = function (legendItem) {
3888
        return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3;
3889
    };
3890
    c3_chart_internal_fn.toggleFocusLegend = function (targetIds, focus) {
3891
        var $$ = this;
3892
        targetIds = $$.mapToTargetIds(targetIds);
3893
        $$.legend.selectAll('.' + CLASS.legendItem)
3894
            .filter(function (id) { return targetIds.indexOf(id) >= 0; })
3895
            .classed(CLASS.legendItemFocused, focus)
3896
          .transition().duration(100)
3897
            .style('opacity', function () {
3898
                var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend;
3899
                return opacity.call($$, $$.d3.select(this));
3900
            });
3901
    };
3902
    c3_chart_internal_fn.revertLegend = function () {
3903
        var $$ = this, d3 = $$.d3;
3904
        $$.legend.selectAll('.' + CLASS.legendItem)
3905
            .classed(CLASS.legendItemFocused, false)
3906
            .transition().duration(100)
3907
            .style('opacity', function () { return $$.opacityForLegend(d3.select(this)); });
3908
    };
3909
    c3_chart_internal_fn.showLegend = function (targetIds) {
3910
        var $$ = this, config = $$.config;
3911
        if (!config.legend_show) {
3912
            config.legend_show = true;
3913
            $$.legend.style('visibility', 'visible');
3914
            if (!$$.legendHasRendered) {
3915
                $$.updateLegendWithDefaults();
3916
            }
3917
        }
3918
        $$.removeHiddenLegendIds(targetIds);
3919
        $$.legend.selectAll($$.selectorLegends(targetIds))
3920
            .style('visibility', 'visible')
3921
            .transition()
3922
            .style('opacity', function () { return $$.opacityForLegend($$.d3.select(this)); });
3923
    };
3924
    c3_chart_internal_fn.hideLegend = function (targetIds) {
3925
        var $$ = this, config = $$.config;
3926
        if (config.legend_show && isEmpty(targetIds)) {
3927
            config.legend_show = false;
3928
            $$.legend.style('visibility', 'hidden');
3929
        }
3930
        $$.addHiddenLegendIds(targetIds);
3931
        $$.legend.selectAll($$.selectorLegends(targetIds))
3932
            .style('opacity', 0)
3933
            .style('visibility', 'hidden');
3934
    };
3935
    c3_chart_internal_fn.clearLegendItemTextBoxCache = function () {
3936
        this.legendItemTextBox = {};
3937
    };
3938
    c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) {
3939
        var $$ = this, config = $$.config;
3940
        var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect;
3941
        var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = 15;
3942
        var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0;
3943
        var withTransition, withTransitionForTransform;
3944
        var texts, rects, tiles, background;
3945

    
3946
        options = options || {};
3947
        withTransition = getOption(options, "withTransition", true);
3948
        withTransitionForTransform = getOption(options, "withTransitionForTransform", true);
3949

    
3950
        function getTextBox(textElement, id) {
3951
            if (!$$.legendItemTextBox[id]) {
3952
                $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem);
3953
            }
3954
            return $$.legendItemTextBox[id];
3955
        }
3956

    
3957
        function updatePositions(textElement, id, index) {
3958
            var reset = index === 0, isLast = index === targetIds.length - 1,
3959
                box = getTextBox(textElement, id),
3960
                itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight),
3961
                itemHeight = box.height + paddingTop,
3962
                itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth,
3963
                areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(),
3964
                margin, maxLength;
3965

    
3966
            // MEMO: care about condifion of step, totalLength
3967
            function updateValues(id, withoutStep) {
3968
                if (!withoutStep) {
3969
                    margin = (areaLength - totalLength - itemLength) / 2;
3970
                    if (margin < posMin) {
3971
                        margin = (areaLength - itemLength) / 2;
3972
                        totalLength = 0;
3973
                        step++;
3974
                    }
3975
                }
3976
                steps[id] = step;
3977
                margins[step] = $$.isLegendInset ? 10 : margin;
3978
                offsets[id] = totalLength;
3979
                totalLength += itemLength;
3980
            }
3981

    
3982
            if (reset) {
3983
                totalLength = 0;
3984
                step = 0;
3985
                maxWidth = 0;
3986
                maxHeight = 0;
3987
            }
3988

    
3989
            if (config.legend_show && !$$.isLegendToShow(id)) {
3990
                widths[id] = heights[id] = steps[id] = offsets[id] = 0;
3991
                return;
3992
            }
3993

    
3994
            widths[id] = itemWidth;
3995
            heights[id] = itemHeight;
3996

    
3997
            if (!maxWidth || itemWidth >= maxWidth) { maxWidth = itemWidth; }
3998
            if (!maxHeight || itemHeight >= maxHeight) { maxHeight = itemHeight; }
3999
            maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth;
4000

    
4001
            if (config.legend_equally) {
4002
                Object.keys(widths).forEach(function (id) { widths[id] = maxWidth; });
4003
                Object.keys(heights).forEach(function (id) { heights[id] = maxHeight; });
4004
                margin = (areaLength - maxLength * targetIds.length) / 2;
4005
                if (margin < posMin) {
4006
                    totalLength = 0;
4007
                    step = 0;
4008
                    targetIds.forEach(function (id) { updateValues(id); });
4009
                }
4010
                else {
4011
                    updateValues(id, true);
4012
                }
4013
            } else {
4014
                updateValues(id);
4015
            }
4016
        }
4017

    
4018
        if ($$.isLegendInset) {
4019
            step = config.legend_inset_step ? config.legend_inset_step : targetIds.length;
4020
            $$.updateLegendStep(step);
4021
        }
4022

    
4023
        if ($$.isLegendRight) {
4024
            xForLegend = function (id) { return maxWidth * steps[id]; };
4025
            yForLegend = function (id) { return margins[steps[id]] + offsets[id]; };
4026
        } else if ($$.isLegendInset) {
4027
            xForLegend = function (id) { return maxWidth * steps[id] + 10; };
4028
            yForLegend = function (id) { return margins[steps[id]] + offsets[id]; };
4029
        } else {
4030
            xForLegend = function (id) { return margins[steps[id]] + offsets[id]; };
4031
            yForLegend = function (id) { return maxHeight * steps[id]; };
4032
        }
4033
        xForLegendText = function (id, i) { return xForLegend(id, i) + 14; };
4034
        yForLegendText = function (id, i) { return yForLegend(id, i) + 9; };
4035
        xForLegendRect = function (id, i) { return xForLegend(id, i); };
4036
        yForLegendRect = function (id, i) { return yForLegend(id, i) - 5; };
4037

    
4038
        // Define g for legend area
4039
        l = $$.legend.selectAll('.' + CLASS.legendItem)
4040
            .data(targetIds)
4041
            .enter().append('g')
4042
            .attr('class', function (id) { return $$.generateClass(CLASS.legendItem, id); })
4043
            .style('visibility', function (id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden'; })
4044
            .style('cursor', 'pointer')
4045
            .on('click', function (id) {
4046
                if (config.legend_item_onclick) {
4047
                    config.legend_item_onclick.call($$, id);
4048
                } else {
4049
                    if ($$.d3.event.altKey) {
4050
                        $$.api.hide();
4051
                        $$.api.show(id);
4052
                    } else {
4053
                        $$.api.toggle(id);
4054
                        $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert();
4055
                    }
4056
                }
4057
            })
4058
            .on('mouseover', function (id) {
4059
                $$.d3.select(this).classed(CLASS.legendItemFocused, true);
4060
                if (!$$.transiting && $$.isTargetToShow(id)) {
4061
                    $$.api.focus(id);
4062
                }
4063
                if (config.legend_item_onmouseover) {
4064
                    config.legend_item_onmouseover.call($$, id);
4065
                }
4066
            })
4067
            .on('mouseout', function (id) {
4068
                $$.d3.select(this).classed(CLASS.legendItemFocused, false);
4069
                $$.api.revert();
4070
                if (config.legend_item_onmouseout) {
4071
                    config.legend_item_onmouseout.call($$, id);
4072
                }
4073
            });
4074
        l.append('text')
4075
            .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; })
4076
            .each(function (id, i) { updatePositions(this, id, i); })
4077
            .style("pointer-events", "none")
4078
            .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200)
4079
            .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText);
4080
        l.append('rect')
4081
            .attr("class", CLASS.legendItemEvent)
4082
            .style('fill-opacity', 0)
4083
            .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200)
4084
            .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect);
4085
        l.append('rect')
4086
            .attr("class", CLASS.legendItemTile)
4087
            .style("pointer-events", "none")
4088
            .style('fill', $$.color)
4089
            .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200)
4090
            .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegend)
4091
            .attr('width', 10)
4092
            .attr('height', 10);
4093

    
4094
        // Set background for inset legend
4095
        background = $$.legend.select('.' + CLASS.legendBackground + ' rect');
4096
        if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) {
4097
            background = $$.legend.insert('g', '.' + CLASS.legendItem)
4098
                .attr("class", CLASS.legendBackground)
4099
                .append('rect');
4100
        }
4101

    
4102
        texts = $$.legend.selectAll('text')
4103
            .data(targetIds)
4104
            .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) // MEMO: needed for update
4105
            .each(function (id, i) { updatePositions(this, id, i); });
4106
        (withTransition ? texts.transition() : texts)
4107
            .attr('x', xForLegendText)
4108
            .attr('y', yForLegendText);
4109

    
4110
        rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent)
4111
            .data(targetIds);
4112
        (withTransition ? rects.transition() : rects)
4113
            .attr('width', function (id) { return widths[id]; })
4114
            .attr('height', function (id) { return heights[id]; })
4115
            .attr('x', xForLegendRect)
4116
            .attr('y', yForLegendRect);
4117

    
4118
        tiles = $$.legend.selectAll('rect.' + CLASS.legendItemTile)
4119
            .data(targetIds);
4120
        (withTransition ? tiles.transition() : tiles)
4121
            .style('fill', $$.color)
4122
            .attr('x', xForLegend)
4123
            .attr('y', yForLegend);
4124

    
4125
        if (background) {
4126
            (withTransition ? background.transition() : background)
4127
                .attr('height', $$.getLegendHeight() - 12)
4128
                .attr('width', maxWidth * (step + 1) + 10);
4129
        }
4130

    
4131
        // toggle legend state
4132
        $$.legend.selectAll('.' + CLASS.legendItem)
4133
            .classed(CLASS.legendItemHidden, function (id) { return !$$.isTargetToShow(id); });
4134

    
4135
        // Update all to reflect change of legend
4136
        $$.updateLegendItemWidth(maxWidth);
4137
        $$.updateLegendItemHeight(maxHeight);
4138
        $$.updateLegendStep(step);
4139
        // Update size and scale
4140
        $$.updateSizes();
4141
        $$.updateScales();
4142
        $$.updateSvgSize();
4143
        // Update g positions
4144
        $$.transformAll(withTransitionForTransform, transitions);
4145
        $$.legendHasRendered = true;
4146
    };
4147

    
4148
    function Axis(owner) {
4149
        API.call(this, owner);
4150
    }
4151

    
4152
    inherit(API, Axis);
4153

    
4154
    Axis.prototype.init = function init() {
4155

    
4156
        var $$ = this.owner, config = $$.config, main = $$.main;
4157
        $$.axes.x = main.append("g")
4158
            .attr("class", CLASS.axis + ' ' + CLASS.axisX)
4159
            .attr("clip-path", $$.clipPathForXAxis)
4160
            .attr("transform", $$.getTranslate('x'))
4161
            .style("visibility", config.axis_x_show ? 'visible' : 'hidden');
4162
        $$.axes.x.append("text")
4163
            .attr("class", CLASS.axisXLabel)
4164
            .attr("transform", config.axis_rotated ? "rotate(-90)" : "")
4165
            .style("text-anchor", this.textAnchorForXAxisLabel.bind(this));
4166
        $$.axes.y = main.append("g")
4167
            .attr("class", CLASS.axis + ' ' + CLASS.axisY)
4168
            .attr("clip-path", config.axis_y_inner ? "" : $$.clipPathForYAxis)
4169
            .attr("transform", $$.getTranslate('y'))
4170
            .style("visibility", config.axis_y_show ? 'visible' : 'hidden');
4171
        $$.axes.y.append("text")
4172
            .attr("class", CLASS.axisYLabel)
4173
            .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
4174
            .style("text-anchor", this.textAnchorForYAxisLabel.bind(this));
4175

    
4176
        $$.axes.y2 = main.append("g")
4177
            .attr("class", CLASS.axis + ' ' + CLASS.axisY2)
4178
            // clip-path?
4179
            .attr("transform", $$.getTranslate('y2'))
4180
            .style("visibility", config.axis_y2_show ? 'visible' : 'hidden');
4181
        $$.axes.y2.append("text")
4182
            .attr("class", CLASS.axisY2Label)
4183
            .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
4184
            .style("text-anchor", this.textAnchorForY2AxisLabel.bind(this));
4185
    };
4186
    Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) {
4187
        var $$ = this.owner, config = $$.config,
4188
            axisParams = {
4189
                isCategory: $$.isCategorized(),
4190
                withOuterTick: withOuterTick,
4191
                tickMultiline: config.axis_x_tick_multiline,
4192
                tickWidth: config.axis_x_tick_width,
4193
                tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate,
4194
                withoutTransition: withoutTransition,
4195
            },
4196
            axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient);
4197

    
4198
        if ($$.isTimeSeries() && tickValues) {
4199
            tickValues = tickValues.map(function (v) { return $$.parseDate(v); });
4200
        }
4201

    
4202
        // Set tick
4203
        axis.tickFormat(tickFormat).tickValues(tickValues);
4204
        if ($$.isCategorized()) {
4205
            axis.tickCentered(config.axis_x_tick_centered);
4206
            if (isEmpty(config.axis_x_tick_culling)) {
4207
                config.axis_x_tick_culling = false;
4208
            }
4209
        }
4210

    
4211
        return axis;
4212
    };
4213
    Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) {
4214
        var $$ = this.owner, config = $$.config, tickValues;
4215
        if (config.axis_x_tick_fit || config.axis_x_tick_count) {
4216
            tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries());
4217
        }
4218
        if (axis) {
4219
            axis.tickValues(tickValues);
4220
        } else {
4221
            $$.xAxis.tickValues(tickValues);
4222
            $$.subXAxis.tickValues(tickValues);
4223
        }
4224
        return tickValues;
4225
    };
4226
    Axis.prototype.getYAxis = function getYAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition) {
4227
        var axisParams = {
4228
            withOuterTick: withOuterTick,
4229
            withoutTransition: withoutTransition,
4230
        },
4231
            $$ = this.owner,
4232
            d3 = $$.d3,
4233
            config = $$.config,
4234
            axis = c3_axis(d3, axisParams).scale(scale).orient(orient).tickFormat(tickFormat);
4235
        if ($$.isTimeSeriesY()) {
4236
            axis.ticks(d3.time[config.axis_y_tick_time_value], config.axis_y_tick_time_interval);
4237
        } else {
4238
            axis.tickValues(tickValues);
4239
        }
4240
        return axis;
4241
    };
4242
    Axis.prototype.getId = function getId(id) {
4243
        var config = this.owner.config;
4244
        return id in config.data_axes ? config.data_axes[id] : 'y';
4245
    };
4246
    Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() {
4247
        var $$ = this.owner, config = $$.config,
4248
            format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { return v < 0 ? v.toFixed(0) : v; };
4249
        if (config.axis_x_tick_format) {
4250
            if (isFunction(config.axis_x_tick_format)) {
4251
                format = config.axis_x_tick_format;
4252
            } else if ($$.isTimeSeries()) {
4253
                format = function (date) {
4254
                    return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : "";
4255
                };
4256
            }
4257
        }
4258
        return isFunction(format) ? function (v) { return format.call($$, v); } : format;
4259
    };
4260
    Axis.prototype.getTickValues = function getTickValues(tickValues, axis) {
4261
        return tickValues ? tickValues : axis ? axis.tickValues() : undefined;
4262
    };
4263
    Axis.prototype.getXAxisTickValues = function getXAxisTickValues() {
4264
        return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis);
4265
    };
4266
    Axis.prototype.getYAxisTickValues = function getYAxisTickValues() {
4267
        return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis);
4268
    };
4269
    Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() {
4270
        return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis);
4271
    };
4272
    Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) {
4273
        var $$ = this.owner, config = $$.config, option;
4274
        if (axisId === 'y') {
4275
            option = config.axis_y_label;
4276
        } else if (axisId === 'y2') {
4277
            option = config.axis_y2_label;
4278
        } else if (axisId === 'x') {
4279
            option = config.axis_x_label;
4280
        }
4281
        return option;
4282
    };
4283
    Axis.prototype.getLabelText = function getLabelText(axisId) {
4284
        var option = this.getLabelOptionByAxisId(axisId);
4285
        return isString(option) ? option : option ? option.text : null;
4286
    };
4287
    Axis.prototype.setLabelText = function setLabelText(axisId, text) {
4288
        var $$ = this.owner, config = $$.config,
4289
            option = this.getLabelOptionByAxisId(axisId);
4290
        if (isString(option)) {
4291
            if (axisId === 'y') {
4292
                config.axis_y_label = text;
4293
            } else if (axisId === 'y2') {
4294
                config.axis_y2_label = text;
4295
            } else if (axisId === 'x') {
4296
                config.axis_x_label = text;
4297
            }
4298
        } else if (option) {
4299
            option.text = text;
4300
        }
4301
    };
4302
    Axis.prototype.getLabelPosition = function getLabelPosition(axisId, defaultPosition) {
4303
        var option = this.getLabelOptionByAxisId(axisId),
4304
            position = (option && typeof option === 'object' && option.position) ? option.position : defaultPosition;
4305
        return {
4306
            isInner: position.indexOf('inner') >= 0,
4307
            isOuter: position.indexOf('outer') >= 0,
4308
            isLeft: position.indexOf('left') >= 0,
4309
            isCenter: position.indexOf('center') >= 0,
4310
            isRight: position.indexOf('right') >= 0,
4311
            isTop: position.indexOf('top') >= 0,
4312
            isMiddle: position.indexOf('middle') >= 0,
4313
            isBottom: position.indexOf('bottom') >= 0
4314
        };
4315
    };
4316
    Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() {
4317
        return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right');
4318
    };
4319
    Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() {
4320
        return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
4321
    };
4322
    Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() {
4323
        return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
4324
    };
4325
    Axis.prototype.getLabelPositionById = function getLabelPositionById(id) {
4326
        return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition();
4327
    };
4328
    Axis.prototype.textForXAxisLabel = function textForXAxisLabel() {
4329
        return this.getLabelText('x');
4330
    };
4331
    Axis.prototype.textForYAxisLabel = function textForYAxisLabel() {
4332
        return this.getLabelText('y');
4333
    };
4334
    Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() {
4335
        return this.getLabelText('y2');
4336
    };
4337
    Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) {
4338
        var $$ = this.owner;
4339
        if (forHorizontal) {
4340
            return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width;
4341
        } else {
4342
            return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0;
4343
        }
4344
    };
4345
    Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) {
4346
        if (forHorizontal) {
4347
            return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0";
4348
        } else {
4349
            return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0";
4350
        }
4351
    };
4352
    Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) {
4353
        if (forHorizontal) {
4354
            return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end';
4355
        } else {
4356
            return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end';
4357
        }
4358
    };
4359
    Axis.prototype.xForXAxisLabel = function xForXAxisLabel() {
4360
        return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
4361
    };
4362
    Axis.prototype.xForYAxisLabel = function xForYAxisLabel() {
4363
        return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
4364
    };
4365
    Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() {
4366
        return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
4367
    };
4368
    Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() {
4369
        return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
4370
    };
4371
    Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() {
4372
        return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
4373
    };
4374
    Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() {
4375
        return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
4376
    };
4377
    Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() {
4378
        var $$ = this.owner, config = $$.config,
4379
            position = this.getXAxisLabelPosition();
4380
        if (config.axis_rotated) {
4381
            return position.isInner ? "1.2em" : -25 - this.getMaxTickWidth('x');
4382
        } else {
4383
            return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em";
4384
        }
4385
    };
4386
    Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() {
4387
        var $$ = this.owner,
4388
            position = this.getYAxisLabelPosition();
4389
        if ($$.config.axis_rotated) {
4390
            return position.isInner ? "-0.5em" : "3em";
4391
        } else {
4392
            return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : (this.getMaxTickWidth('y') + 10));
4393
        }
4394
    };
4395
    Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() {
4396
        var $$ = this.owner,
4397
            position = this.getY2AxisLabelPosition();
4398
        if ($$.config.axis_rotated) {
4399
            return position.isInner ? "1.2em" : "-2.2em";
4400
        } else {
4401
            return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : (this.getMaxTickWidth('y2') + 15));
4402
        }
4403
    };
4404
    Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() {
4405
        var $$ = this.owner;
4406
        return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition());
4407
    };
4408
    Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() {
4409
        var $$ = this.owner;
4410
        return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition());
4411
    };
4412
    Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() {
4413
        var $$ = this.owner;
4414
        return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition());
4415
    };
4416
    Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) {
4417
        var $$ = this.owner, config = $$.config,
4418
            maxWidth = 0, targetsToShow, scale, axis, dummy, svg;
4419
        if (withoutRecompute && $$.currentMaxTickWidths[id]) {
4420
            return $$.currentMaxTickWidths[id];
4421
        }
4422
        if ($$.svg) {
4423
            targetsToShow = $$.filterTargetsToShow($$.data.targets);
4424
            if (id === 'y') {
4425
                scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y'));
4426
                axis = this.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, false, true);
4427
            } else if (id === 'y2') {
4428
                scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2'));
4429
                axis = this.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, false, true);
4430
            } else {
4431
                scale = $$.x.copy().domain($$.getXDomain(targetsToShow));
4432
                axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true);
4433
                this.updateXAxisTickValues(targetsToShow, axis);
4434
            }
4435
            dummy = $$.d3.select('body').append('div').classed('c3', true);
4436
            svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0),
4437
            svg.append('g').call(axis).each(function () {
4438
                $$.d3.select(this).selectAll('text').each(function () {
4439
                    var box = this.getBoundingClientRect();
4440
                    if (maxWidth < box.width) { maxWidth = box.width; }
4441
                });
4442
                dummy.remove();
4443
            });
4444
        }
4445
        $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth;
4446
        return $$.currentMaxTickWidths[id];
4447
    };
4448

    
4449
    Axis.prototype.updateLabels = function updateLabels(withTransition) {
4450
        var $$ = this.owner;
4451
        var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel),
4452
            axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel),
4453
            axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label);
4454
        (withTransition ? axisXLabel.transition() : axisXLabel)
4455
            .attr("x", this.xForXAxisLabel.bind(this))
4456
            .attr("dx", this.dxForXAxisLabel.bind(this))
4457
            .attr("dy", this.dyForXAxisLabel.bind(this))
4458
            .text(this.textForXAxisLabel.bind(this));
4459
        (withTransition ? axisYLabel.transition() : axisYLabel)
4460
            .attr("x", this.xForYAxisLabel.bind(this))
4461
            .attr("dx", this.dxForYAxisLabel.bind(this))
4462
            .attr("dy", this.dyForYAxisLabel.bind(this))
4463
            .text(this.textForYAxisLabel.bind(this));
4464
        (withTransition ? axisY2Label.transition() : axisY2Label)
4465
            .attr("x", this.xForY2AxisLabel.bind(this))
4466
            .attr("dx", this.dxForY2AxisLabel.bind(this))
4467
            .attr("dy", this.dyForY2AxisLabel.bind(this))
4468
            .text(this.textForY2AxisLabel.bind(this));
4469
    };
4470
    Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) {
4471
        if (!isValue(padding[key])) {
4472
            return defaultValue;
4473
        }
4474
        if (padding.unit === 'ratio') {
4475
            return padding[key] * domainLength;
4476
        }
4477
        // assume padding is pixels if unit is not specified
4478
        return this.convertPixelsToAxisPadding(padding[key], domainLength);
4479
    };
4480
    Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) {
4481
        var $$ = this.owner,
4482
            length = $$.config.axis_rotated ? $$.width : $$.height;
4483
        return domainLength * (pixels / length);
4484
    };
4485
    Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) {
4486
        var tickValues = values, targetCount, start, end, count, interval, i, tickValue;
4487
        if (tickCount) {
4488
            targetCount = isFunction(tickCount) ? tickCount() : tickCount;
4489
            // compute ticks according to tickCount
4490
            if (targetCount === 1) {
4491
                tickValues = [values[0]];
4492
            } else if (targetCount === 2) {
4493
                tickValues = [values[0], values[values.length - 1]];
4494
            } else if (targetCount > 2) {
4495
                count = targetCount - 2;
4496
                start = values[0];
4497
                end = values[values.length - 1];
4498
                interval = (end - start) / (count + 1);
4499
                // re-construct unique values
4500
                tickValues = [start];
4501
                for (i = 0; i < count; i++) {
4502
                    tickValue = +start + interval * (i + 1);
4503
                    tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue);
4504
                }
4505
                tickValues.push(end);
4506
            }
4507
        }
4508
        if (!forTimeSeries) { tickValues = tickValues.sort(function (a, b) { return a - b; }); }
4509
        return tickValues;
4510
    };
4511
    Axis.prototype.generateTransitions = function generateTransitions(duration) {
4512
        var $$ = this.owner, axes = $$.axes;
4513
        return {
4514
            axisX: duration ? axes.x.transition().duration(duration) : axes.x,
4515
            axisY: duration ? axes.y.transition().duration(duration) : axes.y,
4516
            axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2,
4517
            axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx
4518
        };
4519
    };
4520
    Axis.prototype.redraw = function redraw(transitions, isHidden) {
4521
        var $$ = this.owner;
4522
        $$.axes.x.style("opacity", isHidden ? 0 : 1);
4523
        $$.axes.y.style("opacity", isHidden ? 0 : 1);
4524
        $$.axes.y2.style("opacity", isHidden ? 0 : 1);
4525
        $$.axes.subx.style("opacity", isHidden ? 0 : 1);
4526
        transitions.axisX.call($$.xAxis);
4527
        transitions.axisY.call($$.yAxis);
4528
        transitions.axisY2.call($$.y2Axis);
4529
        transitions.axisSubX.call($$.subXAxis);
4530
    };
4531

    
4532
    c3_chart_internal_fn.getClipPath = function (id) {
4533
        var isIE9 = window.navigator.appVersion.toLowerCase().indexOf("msie 9.") >= 0;
4534
        return "url(" + (isIE9 ? "" : document.URL.split('#')[0]) + "#" + id + ")";
4535
    };
4536
    c3_chart_internal_fn.appendClip = function (parent, id) {
4537
        return parent.append("clipPath").attr("id", id).append("rect");
4538
    };
4539
    c3_chart_internal_fn.getAxisClipX = function (forHorizontal) {
4540
        // axis line width + padding for left
4541
        var left = Math.max(30, this.margin.left);
4542
        return forHorizontal ? -(1 + left) : -(left - 1);
4543
    };
4544
    c3_chart_internal_fn.getAxisClipY = function (forHorizontal) {
4545
        return forHorizontal ? -20 : -this.margin.top;
4546
    };
4547
    c3_chart_internal_fn.getXAxisClipX = function () {
4548
        var $$ = this;
4549
        return $$.getAxisClipX(!$$.config.axis_rotated);
4550
    };
4551
    c3_chart_internal_fn.getXAxisClipY = function () {
4552
        var $$ = this;
4553
        return $$.getAxisClipY(!$$.config.axis_rotated);
4554
    };
4555
    c3_chart_internal_fn.getYAxisClipX = function () {
4556
        var $$ = this;
4557
        return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated);
4558
    };
4559
    c3_chart_internal_fn.getYAxisClipY = function () {
4560
        var $$ = this;
4561
        return $$.getAxisClipY($$.config.axis_rotated);
4562
    };
4563
    c3_chart_internal_fn.getAxisClipWidth = function (forHorizontal) {
4564
        var $$ = this,
4565
            left = Math.max(30, $$.margin.left),
4566
            right = Math.max(30, $$.margin.right);
4567
        // width + axis line width + padding for left/right
4568
        return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20;
4569
    };
4570
    c3_chart_internal_fn.getAxisClipHeight = function (forHorizontal) {
4571
        // less than 20 is not enough to show the axis label 'outer' without legend
4572
        return (forHorizontal ? this.margin.bottom : (this.margin.top + this.height)) + 20;
4573
    };
4574
    c3_chart_internal_fn.getXAxisClipWidth = function () {
4575
        var $$ = this;
4576
        return $$.getAxisClipWidth(!$$.config.axis_rotated);
4577
    };
4578
    c3_chart_internal_fn.getXAxisClipHeight = function () {
4579
        var $$ = this;
4580
        return $$.getAxisClipHeight(!$$.config.axis_rotated);
4581
    };
4582
    c3_chart_internal_fn.getYAxisClipWidth = function () {
4583
        var $$ = this;
4584
        return $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0);
4585
    };
4586
    c3_chart_internal_fn.getYAxisClipHeight = function () {
4587
        var $$ = this;
4588
        return $$.getAxisClipHeight($$.config.axis_rotated);
4589
    };
4590

    
4591
    c3_chart_internal_fn.initPie = function () {
4592
        var $$ = this, d3 = $$.d3, config = $$.config;
4593
        $$.pie = d3.layout.pie().value(function (d) {
4594
            return d.values.reduce(function (a, b) { return a + b.value; }, 0);
4595
        });
4596
        if (!config.data_order) {
4597
            $$.pie.sort(null);
4598
        }
4599
    };
4600

    
4601
    c3_chart_internal_fn.updateRadius = function () {
4602
        var $$ = this, config = $$.config,
4603
            w = config.gauge_width || config.donut_width;
4604
        $$.radiusExpanded = Math.min($$.arcWidth, $$.arcHeight) / 2;
4605
        $$.radius = $$.radiusExpanded * 0.95;
4606
        $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6;
4607
        $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0;
4608
    };
4609

    
4610
    c3_chart_internal_fn.updateArc = function () {
4611
        var $$ = this;
4612
        $$.svgArc = $$.getSvgArc();
4613
        $$.svgArcExpanded = $$.getSvgArcExpanded();
4614
        $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98);
4615
    };
4616

    
4617
    c3_chart_internal_fn.updateAngle = function (d) {
4618
        var $$ = this, config = $$.config,
4619
            found = false, index = 0,
4620
            gMin = config.gauge_min, gMax = config.gauge_max, gTic, gValue;
4621
        $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) {
4622
            if (! found && t.data.id === d.data.id) {
4623
                found = true;
4624
                d = t;
4625
                d.index = index;
4626
            }
4627
            index++;
4628
        });
4629
        if (isNaN(d.startAngle)) {
4630
            d.startAngle = 0;
4631
        }
4632
        if (isNaN(d.endAngle)) {
4633
            d.endAngle = d.startAngle;
4634
        }
4635
        if ($$.isGaugeType(d.data)) {
4636
            gTic = (Math.PI) / (gMax - gMin);
4637
            gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : (gMax - gMin);
4638
            d.startAngle = -1 * (Math.PI / 2);
4639
            d.endAngle = d.startAngle + gTic * gValue;
4640
        }
4641
        return found ? d : null;
4642
    };
4643

    
4644
    c3_chart_internal_fn.getSvgArc = function () {
4645
        var $$ = this,
4646
            arc = $$.d3.svg.arc().outerRadius($$.radius).innerRadius($$.innerRadius),
4647
            newArc = function (d, withoutUpdate) {
4648
                var updated;
4649
                if (withoutUpdate) { return arc(d); } // for interpolate
4650
                updated = $$.updateAngle(d);
4651
                return updated ? arc(updated) : "M 0 0";
4652
            };
4653
        // TODO: extends all function
4654
        newArc.centroid = arc.centroid;
4655
        return newArc;
4656
    };
4657

    
4658
    c3_chart_internal_fn.getSvgArcExpanded = function (rate) {
4659
        var $$ = this,
4660
            arc = $$.d3.svg.arc().outerRadius($$.radiusExpanded * (rate ? rate : 1)).innerRadius($$.innerRadius);
4661
        return function (d) {
4662
            var updated = $$.updateAngle(d);
4663
            return updated ? arc(updated) : "M 0 0";
4664
        };
4665
    };
4666

    
4667
    c3_chart_internal_fn.getArc = function (d, withoutUpdate, force) {
4668
        return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : "M 0 0";
4669
    };
4670

    
4671

    
4672
    c3_chart_internal_fn.transformForArcLabel = function (d) {
4673
        var $$ = this,
4674
            updated = $$.updateAngle(d), c, x, y, h, ratio, translate = "";
4675
        if (updated && !$$.hasType('gauge')) {
4676
            c = this.svgArc.centroid(updated);
4677
            x = isNaN(c[0]) ? 0 : c[0];
4678
            y = isNaN(c[1]) ? 0 : c[1];
4679
            h = Math.sqrt(x * x + y * y);
4680
            // TODO: ratio should be an option?
4681
            ratio = $$.radius && h ? (36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius / h : 0;
4682
            translate = "translate(" + (x * ratio) +  ',' + (y * ratio) +  ")";
4683
        }
4684
        return translate;
4685
    };
4686

    
4687
    c3_chart_internal_fn.getArcRatio = function (d) {
4688
        var $$ = this,
4689
            whole = $$.hasType('gauge') ? Math.PI : (Math.PI * 2);
4690
        return d ? (d.endAngle - d.startAngle) / whole : null;
4691
    };
4692

    
4693
    c3_chart_internal_fn.convertToArcData = function (d) {
4694
        return this.addName({
4695
            id: d.data.id,
4696
            value: d.value,
4697
            ratio: this.getArcRatio(d),
4698
            index: d.index
4699
        });
4700
    };
4701

    
4702
    c3_chart_internal_fn.textForArcLabel = function (d) {
4703
        var $$ = this,
4704
            updated, value, ratio, id, format;
4705
        if (! $$.shouldShowArcLabel()) { return ""; }
4706
        updated = $$.updateAngle(d);
4707
        value = updated ? updated.value : null;
4708
        ratio = $$.getArcRatio(updated);
4709
        id = d.data.id;
4710
        if (! $$.hasType('gauge') && ! $$.meetsArcLabelThreshold(ratio)) { return ""; }
4711
        format = $$.getArcLabelFormat();
4712
        return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio);
4713
    };
4714

    
4715
    c3_chart_internal_fn.expandArc = function (targetIds) {
4716
        var $$ = this, interval;
4717

    
4718
        // MEMO: avoid to cancel transition
4719
        if ($$.transiting) {
4720
            interval = window.setInterval(function () {
4721
                if (!$$.transiting) {
4722
                    window.clearInterval(interval);
4723
                    if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) {
4724
                        $$.expandArc(targetIds);
4725
                    }
4726
                }
4727
            }, 10);
4728
            return;
4729
        }
4730

    
4731
        targetIds = $$.mapToTargetIds(targetIds);
4732

    
4733
        $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) {
4734
            if (! $$.shouldExpand(d.data.id)) { return; }
4735
            $$.d3.select(this).selectAll('path')
4736
                .transition().duration(50)
4737
                .attr("d", $$.svgArcExpanded)
4738
                .transition().duration(100)
4739
                .attr("d", $$.svgArcExpandedSub)
4740
                .each(function (d) {
4741
                    if ($$.isDonutType(d.data)) {
4742
                        // callback here
4743
                    }
4744
                });
4745
        });
4746
    };
4747

    
4748
    c3_chart_internal_fn.unexpandArc = function (targetIds) {
4749
        var $$ = this;
4750

    
4751
        if ($$.transiting) { return; }
4752

    
4753
        targetIds = $$.mapToTargetIds(targetIds);
4754

    
4755
        $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).selectAll('path')
4756
            .transition().duration(50)
4757
            .attr("d", $$.svgArc);
4758
        $$.svg.selectAll('.' + CLASS.arc)
4759
            .style("opacity", 1);
4760
    };
4761

    
4762
    c3_chart_internal_fn.shouldExpand = function (id) {
4763
        var $$ = this, config = $$.config;
4764
        return ($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand);
4765
    };
4766

    
4767
    c3_chart_internal_fn.shouldShowArcLabel = function () {
4768
        var $$ = this, config = $$.config, shouldShow = true;
4769
        if ($$.hasType('donut')) {
4770
            shouldShow = config.donut_label_show;
4771
        } else if ($$.hasType('pie')) {
4772
            shouldShow = config.pie_label_show;
4773
        }
4774
        // when gauge, always true
4775
        return shouldShow;
4776
    };
4777

    
4778
    c3_chart_internal_fn.meetsArcLabelThreshold = function (ratio) {
4779
        var $$ = this, config = $$.config,
4780
            threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold;
4781
        return ratio >= threshold;
4782
    };
4783

    
4784
    c3_chart_internal_fn.getArcLabelFormat = function () {
4785
        var $$ = this, config = $$.config,
4786
            format = config.pie_label_format;
4787
        if ($$.hasType('gauge')) {
4788
            format = config.gauge_label_format;
4789
        } else if ($$.hasType('donut')) {
4790
            format = config.donut_label_format;
4791
        }
4792
        return format;
4793
    };
4794

    
4795
    c3_chart_internal_fn.getArcTitle = function () {
4796
        var $$ = this;
4797
        return $$.hasType('donut') ? $$.config.donut_title : "";
4798
    };
4799

    
4800
    c3_chart_internal_fn.updateTargetsForArc = function (targets) {
4801
        var $$ = this, main = $$.main,
4802
            mainPieUpdate, mainPieEnter,
4803
            classChartArc = $$.classChartArc.bind($$),
4804
            classArcs = $$.classArcs.bind($$),
4805
            classFocus = $$.classFocus.bind($$);
4806
        mainPieUpdate = main.select('.' + CLASS.chartArcs).selectAll('.' + CLASS.chartArc)
4807
            .data($$.pie(targets))
4808
            .attr("class", function (d) { return classChartArc(d) + classFocus(d.data); });
4809
        mainPieEnter = mainPieUpdate.enter().append("g")
4810
            .attr("class", classChartArc);
4811
        mainPieEnter.append('g')
4812
            .attr('class', classArcs);
4813
        mainPieEnter.append("text")
4814
            .attr("dy", $$.hasType('gauge') ? "-.1em" : ".35em")
4815
            .style("opacity", 0)
4816
            .style("text-anchor", "middle")
4817
            .style("pointer-events", "none");
4818
        // MEMO: can not keep same color..., but not bad to update color in redraw
4819
        //mainPieUpdate.exit().remove();
4820
    };
4821

    
4822
    c3_chart_internal_fn.initArc = function () {
4823
        var $$ = this;
4824
        $$.arcs = $$.main.select('.' + CLASS.chart).append("g")
4825
            .attr("class", CLASS.chartArcs)
4826
            .attr("transform", $$.getTranslate('arc'));
4827
        $$.arcs.append('text')
4828
            .attr('class', CLASS.chartArcsTitle)
4829
            .style("text-anchor", "middle")
4830
            .text($$.getArcTitle());
4831
    };
4832

    
4833
    c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransform) {
4834
        var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main,
4835
            mainArc;
4836
        mainArc = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arc)
4837
            .data($$.arcData.bind($$));
4838
        mainArc.enter().append('path')
4839
            .attr("class", $$.classArc.bind($$))
4840
            .style("fill", function (d) { return $$.color(d.data); })
4841
            .style("cursor", function (d) { return config.interaction_enabled && config.data_selection_isselectable(d) ? "pointer" : null; })
4842
            .style("opacity", 0)
4843
            .each(function (d) {
4844
                if ($$.isGaugeType(d.data)) {
4845
                    d.startAngle = d.endAngle = -1 * (Math.PI / 2);
4846
                }
4847
                this._current = d;
4848
            });
4849
        mainArc
4850
            .attr("transform", function (d) { return !$$.isGaugeType(d.data) && withTransform ? "scale(0)" : ""; })
4851
            .style("opacity", function (d) { return d === this._current ? 0 : 1; })
4852
            .on('mouseover', config.interaction_enabled ? function (d) {
4853
                var updated, arcData;
4854
                if ($$.transiting) { // skip while transiting
4855
                    return;
4856
                }
4857
                updated = $$.updateAngle(d);
4858
                arcData = $$.convertToArcData(updated);
4859
                // transitions
4860
                $$.expandArc(updated.data.id);
4861
                $$.api.focus(updated.data.id);
4862
                $$.toggleFocusLegend(updated.data.id, true);
4863
                $$.config.data_onmouseover(arcData, this);
4864
            } : null)
4865
            .on('mousemove', config.interaction_enabled ? function (d) {
4866
                var updated = $$.updateAngle(d),
4867
                    arcData = $$.convertToArcData(updated),
4868
                    selectedData = [arcData];
4869
                $$.showTooltip(selectedData, this);
4870
            } : null)
4871
            .on('mouseout', config.interaction_enabled ? function (d) {
4872
                var updated, arcData;
4873
                if ($$.transiting) { // skip while transiting
4874
                    return;
4875
                }
4876
                updated = $$.updateAngle(d);
4877
                arcData = $$.convertToArcData(updated);
4878
                // transitions
4879
                $$.unexpandArc(updated.data.id);
4880
                $$.api.revert();
4881
                $$.revertLegend();
4882
                $$.hideTooltip();
4883
                $$.config.data_onmouseout(arcData, this);
4884
            } : null)
4885
            .on('click', config.interaction_enabled ? function (d, i) {
4886
                var updated = $$.updateAngle(d),
4887
                    arcData = $$.convertToArcData(updated);
4888
                if ($$.toggleShape) { $$.toggleShape(this, arcData, i); }
4889
                $$.config.data_onclick.call($$.api, arcData, this);
4890
            } : null)
4891
            .each(function () { $$.transiting = true; })
4892
            .transition().duration(duration)
4893
            .attrTween("d", function (d) {
4894
                var updated = $$.updateAngle(d), interpolate;
4895
                if (! updated) {
4896
                    return function () { return "M 0 0"; };
4897
                }
4898
                //                if (this._current === d) {
4899
                //                    this._current = {
4900
                //                        startAngle: Math.PI*2,
4901
                //                        endAngle: Math.PI*2,
4902
                //                    };
4903
                //                }
4904
                if (isNaN(this._current.startAngle)) {
4905
                    this._current.startAngle = 0;
4906
                }
4907
                if (isNaN(this._current.endAngle)) {
4908
                    this._current.endAngle = this._current.startAngle;
4909
                }
4910
                interpolate = d3.interpolate(this._current, updated);
4911
                this._current = interpolate(0);
4912
                return function (t) {
4913
                    var interpolated = interpolate(t);
4914
                    interpolated.data = d.data; // data.id will be updated by interporator
4915
                    return $$.getArc(interpolated, true);
4916
                };
4917
            })
4918
            .attr("transform", withTransform ? "scale(1)" : "")
4919
            .style("fill", function (d) {
4920
                return $$.levelColor ? $$.levelColor(d.data.values[0].value) : $$.color(d.data.id);
4921
            }) // Where gauge reading color would receive customization.
4922
            .style("opacity", 1)
4923
            .call($$.endall, function () {
4924
                $$.transiting = false;
4925
            });
4926
        mainArc.exit().transition().duration(durationForExit)
4927
            .style('opacity', 0)
4928
            .remove();
4929
        main.selectAll('.' + CLASS.chartArc).select('text')
4930
            .style("opacity", 0)
4931
            .attr('class', function (d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : ''; })
4932
            .text($$.textForArcLabel.bind($$))
4933
            .attr("transform", $$.transformForArcLabel.bind($$))
4934
            .style('font-size', function (d) { return $$.isGaugeType(d.data) ? Math.round($$.radius / 5) + 'px' : ''; })
4935
          .transition().duration(duration)
4936
            .style("opacity", function (d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0; });
4937
        main.select('.' + CLASS.chartArcsTitle)
4938
            .style("opacity", $$.hasType('donut') || $$.hasType('gauge') ? 1 : 0);
4939

    
4940
        if ($$.hasType('gauge')) {
4941
            $$.arcs.select('.' + CLASS.chartArcsBackground)
4942
                .attr("d", function () {
4943
                    var d = {
4944
                        data: [{value: config.gauge_max}],
4945
                        startAngle: -1 * (Math.PI / 2),
4946
                        endAngle: Math.PI / 2
4947
                    };
4948
                    return $$.getArc(d, true, true);
4949
                });
4950
            $$.arcs.select('.' + CLASS.chartArcsGaugeUnit)
4951
                .attr("dy", ".75em")
4952
                .text(config.gauge_label_show ? config.gauge_units : '');
4953
            $$.arcs.select('.' + CLASS.chartArcsGaugeMin)
4954
                .attr("dx", -1 * ($$.innerRadius + (($$.radius - $$.innerRadius) / 2)) + "px")
4955
                .attr("dy", "1.2em")
4956
                .text(config.gauge_label_show ? config.gauge_min : '');
4957
            $$.arcs.select('.' + CLASS.chartArcsGaugeMax)
4958
                .attr("dx", $$.innerRadius + (($$.radius - $$.innerRadius) / 2) + "px")
4959
                .attr("dy", "1.2em")
4960
                .text(config.gauge_label_show ? config.gauge_max : '');
4961
        }
4962
    };
4963
    c3_chart_internal_fn.initGauge = function () {
4964
        var arcs = this.arcs;
4965
        if (this.hasType('gauge')) {
4966
            arcs.append('path')
4967
                .attr("class", CLASS.chartArcsBackground);
4968
            arcs.append("text")
4969
                .attr("class", CLASS.chartArcsGaugeUnit)
4970
                .style("text-anchor", "middle")
4971
                .style("pointer-events", "none");
4972
            arcs.append("text")
4973
                .attr("class", CLASS.chartArcsGaugeMin)
4974
                .style("text-anchor", "middle")
4975
                .style("pointer-events", "none");
4976
            arcs.append("text")
4977
                .attr("class", CLASS.chartArcsGaugeMax)
4978
                .style("text-anchor", "middle")
4979
                .style("pointer-events", "none");
4980
        }
4981
    };
4982
    c3_chart_internal_fn.getGaugeLabelHeight = function () {
4983
        return this.config.gauge_label_show ? 20 : 0;
4984
    };
4985

    
4986
    c3_chart_internal_fn.initRegion = function () {
4987
        var $$ = this;
4988
        $$.region = $$.main.append('g')
4989
            .attr("clip-path", $$.clipPath)
4990
            .attr("class", CLASS.regions);
4991
    };
4992
    c3_chart_internal_fn.updateRegion = function (duration) {
4993
        var $$ = this, config = $$.config;
4994

    
4995
        // hide if arc type
4996
        $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible');
4997

    
4998
        $$.mainRegion = $$.main.select('.' + CLASS.regions).selectAll('.' + CLASS.region)
4999
            .data(config.regions);
5000
        $$.mainRegion.enter().append('g')
5001
            .attr('class', $$.classRegion.bind($$))
5002
          .append('rect')
5003
            .style("fill-opacity", 0);
5004
        $$.mainRegion.exit().transition().duration(duration)
5005
            .style("opacity", 0)
5006
            .remove();
5007
    };
5008
    c3_chart_internal_fn.redrawRegion = function (withTransition) {
5009
        var $$ = this,
5010
            regions = $$.mainRegion.selectAll('rect'),
5011
            x = $$.regionX.bind($$),
5012
            y = $$.regionY.bind($$),
5013
            w = $$.regionWidth.bind($$),
5014
            h = $$.regionHeight.bind($$);
5015
        return [
5016
            (withTransition ? regions.transition() : regions)
5017
                .attr("x", x)
5018
                .attr("y", y)
5019
                .attr("width", w)
5020
                .attr("height", h)
5021
                .style("fill-opacity", function (d) { return isValue(d.opacity) ? d.opacity : 0.1; })
5022
        ];
5023
    };
5024
    c3_chart_internal_fn.regionX = function (d) {
5025
        var $$ = this, config = $$.config,
5026
            xPos, yScale = d.axis === 'y' ? $$.y : $$.y2;
5027
        if (d.axis === 'y' || d.axis === 'y2') {
5028
            xPos = config.axis_rotated ? ('start' in d ? yScale(d.start) : 0) : 0;
5029
        } else {
5030
            xPos = config.axis_rotated ? 0 : ('start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0);
5031
        }
5032
        return xPos;
5033
    };
5034
    c3_chart_internal_fn.regionY = function (d) {
5035
        var $$ = this, config = $$.config,
5036
            yPos, yScale = d.axis === 'y' ? $$.y : $$.y2;
5037
        if (d.axis === 'y' || d.axis === 'y2') {
5038
            yPos = config.axis_rotated ? 0 : ('end' in d ? yScale(d.end) : 0);
5039
        } else {
5040
            yPos = config.axis_rotated ? ('start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0) : 0;
5041
        }
5042
        return yPos;
5043
    };
5044
    c3_chart_internal_fn.regionWidth = function (d) {
5045
        var $$ = this, config = $$.config,
5046
            start = $$.regionX(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2;
5047
        if (d.axis === 'y' || d.axis === 'y2') {
5048
            end = config.axis_rotated ? ('end' in d ? yScale(d.end) : $$.width) : $$.width;
5049
        } else {
5050
            end = config.axis_rotated ? $$.width : ('end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width);
5051
        }
5052
        return end < start ? 0 : end - start;
5053
    };
5054
    c3_chart_internal_fn.regionHeight = function (d) {
5055
        var $$ = this, config = $$.config,
5056
            start = this.regionY(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2;
5057
        if (d.axis === 'y' || d.axis === 'y2') {
5058
            end = config.axis_rotated ? $$.height : ('start' in d ? yScale(d.start) : $$.height);
5059
        } else {
5060
            end = config.axis_rotated ? ('end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height) : $$.height;
5061
        }
5062
        return end < start ? 0 : end - start;
5063
    };
5064
    c3_chart_internal_fn.isRegionOnX = function (d) {
5065
        return !d.axis || d.axis === 'x';
5066
    };
5067

    
5068
    c3_chart_internal_fn.drag = function (mouse) {
5069
        var $$ = this, config = $$.config, main = $$.main, d3 = $$.d3;
5070
        var sx, sy, mx, my, minX, maxX, minY, maxY;
5071

    
5072
        if ($$.hasArcType()) { return; }
5073
        if (! config.data_selection_enabled) { return; } // do nothing if not selectable
5074
        if (config.zoom_enabled && ! $$.zoom.altDomain) { return; } // skip if zoomable because of conflict drag dehavior
5075
        if (!config.data_selection_multiple) { return; } // skip when single selection because drag is used for multiple selection
5076

    
5077
        sx = $$.dragStart[0];
5078
        sy = $$.dragStart[1];
5079
        mx = mouse[0];
5080
        my = mouse[1];
5081
        minX = Math.min(sx, mx);
5082
        maxX = Math.max(sx, mx);
5083
        minY = (config.data_selection_grouped) ? $$.margin.top : Math.min(sy, my);
5084
        maxY = (config.data_selection_grouped) ? $$.height : Math.max(sy, my);
5085

    
5086
        main.select('.' + CLASS.dragarea)
5087
            .attr('x', minX)
5088
            .attr('y', minY)
5089
            .attr('width', maxX - minX)
5090
            .attr('height', maxY - minY);
5091
        // TODO: binary search when multiple xs
5092
        main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape)
5093
            .filter(function (d) { return config.data_selection_isselectable(d); })
5094
            .each(function (d, i) {
5095
                var shape = d3.select(this),
5096
                    isSelected = shape.classed(CLASS.SELECTED),
5097
                    isIncluded = shape.classed(CLASS.INCLUDED),
5098
                    _x, _y, _w, _h, toggle, isWithin = false, box;
5099
                if (shape.classed(CLASS.circle)) {
5100
                    _x = shape.attr("cx") * 1;
5101
                    _y = shape.attr("cy") * 1;
5102
                    toggle = $$.togglePoint;
5103
                    isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY;
5104
                }
5105
                else if (shape.classed(CLASS.bar)) {
5106
                    box = getPathBox(this);
5107
                    _x = box.x;
5108
                    _y = box.y;
5109
                    _w = box.width;
5110
                    _h = box.height;
5111
                    toggle = $$.togglePath;
5112
                    isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY);
5113
                } else {
5114
                    // line/area selection not supported yet
5115
                    return;
5116
                }
5117
                if (isWithin ^ isIncluded) {
5118
                    shape.classed(CLASS.INCLUDED, !isIncluded);
5119
                    // TODO: included/unincluded callback here
5120
                    shape.classed(CLASS.SELECTED, !isSelected);
5121
                    toggle.call($$, !isSelected, shape, d, i);
5122
                }
5123
            });
5124
    };
5125

    
5126
    c3_chart_internal_fn.dragstart = function (mouse) {
5127
        var $$ = this, config = $$.config;
5128
        if ($$.hasArcType()) { return; }
5129
        if (! config.data_selection_enabled) { return; } // do nothing if not selectable
5130
        $$.dragStart = mouse;
5131
        $$.main.select('.' + CLASS.chart).append('rect')
5132
            .attr('class', CLASS.dragarea)
5133
            .style('opacity', 0.1);
5134
        $$.dragging = true;
5135
    };
5136

    
5137
    c3_chart_internal_fn.dragend = function () {
5138
        var $$ = this, config = $$.config;
5139
        if ($$.hasArcType()) { return; }
5140
        if (! config.data_selection_enabled) { return; } // do nothing if not selectable
5141
        $$.main.select('.' + CLASS.dragarea)
5142
            .transition().duration(100)
5143
            .style('opacity', 0)
5144
            .remove();
5145
        $$.main.selectAll('.' + CLASS.shape)
5146
            .classed(CLASS.INCLUDED, false);
5147
        $$.dragging = false;
5148
    };
5149

    
5150
    c3_chart_internal_fn.selectPoint = function (target, d, i) {
5151
        var $$ = this, config = $$.config,
5152
            cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$),
5153
            cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$),
5154
            r = $$.pointSelectR.bind($$);
5155
        config.data_onselected.call($$.api, d, target.node());
5156
        // add selected-circle on low layer g
5157
        $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i)
5158
            .data([d])
5159
            .enter().append('circle')
5160
            .attr("class", function () { return $$.generateClass(CLASS.selectedCircle, i); })
5161
            .attr("cx", cx)
5162
            .attr("cy", cy)
5163
            .attr("stroke", function () { return $$.color(d); })
5164
            .attr("r", function (d) { return $$.pointSelectR(d) * 1.4; })
5165
            .transition().duration(100)
5166
            .attr("r", r);
5167
    };
5168
    c3_chart_internal_fn.unselectPoint = function (target, d, i) {
5169
        var $$ = this;
5170
        $$.config.data_onunselected(d, target.node());
5171
        // remove selected-circle from low layer g
5172
        $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i)
5173
            .transition().duration(100).attr('r', 0)
5174
            .remove();
5175
    };
5176
    c3_chart_internal_fn.togglePoint = function (selected, target, d, i) {
5177
        selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i);
5178
    };
5179
    c3_chart_internal_fn.selectPath = function (target, d) {
5180
        var $$ = this;
5181
        $$.config.data_onselected.call($$, d, target.node());
5182
        target.transition().duration(100)
5183
            .style("fill", function () { return $$.d3.rgb($$.color(d)).brighter(0.75); });
5184
    };
5185
    c3_chart_internal_fn.unselectPath = function (target, d) {
5186
        var $$ = this;
5187
        $$.config.data_onunselected.call($$, d, target.node());
5188
        target.transition().duration(100)
5189
            .style("fill", function () { return $$.color(d); });
5190
    };
5191
    c3_chart_internal_fn.togglePath = function (selected, target, d, i) {
5192
        selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i);
5193
    };
5194
    c3_chart_internal_fn.getToggle = function (that, d) {
5195
        var $$ = this, toggle;
5196
        if (that.nodeName === 'circle') {
5197
            if ($$.isStepType(d)) {
5198
                // circle is hidden in step chart, so treat as within the click area
5199
                toggle = function () {}; // TODO: how to select step chart?
5200
            } else {
5201
                toggle = $$.togglePoint;
5202
            }
5203
        }
5204
        else if (that.nodeName === 'path') {
5205
            toggle = $$.togglePath;
5206
        }
5207
        return toggle;
5208
    };
5209
    c3_chart_internal_fn.toggleShape = function (that, d, i) {
5210
        var $$ = this, d3 = $$.d3, config = $$.config,
5211
            shape = d3.select(that), isSelected = shape.classed(CLASS.SELECTED),
5212
            toggle = $$.getToggle(that, d).bind($$);
5213

    
5214
        if (config.data_selection_enabled && config.data_selection_isselectable(d)) {
5215
            if (!config.data_selection_multiple) {
5216
                $$.main.selectAll('.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : "")).selectAll('.' + CLASS.shape).each(function (d, i) {
5217
                    var shape = d3.select(this);
5218
                    if (shape.classed(CLASS.SELECTED)) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); }
5219
                });
5220
            }
5221
            shape.classed(CLASS.SELECTED, !isSelected);
5222
            toggle(!isSelected, shape, d, i);
5223
        }
5224
    };
5225

    
5226
    c3_chart_internal_fn.initBrush = function () {
5227
        var $$ = this, d3 = $$.d3;
5228
        $$.brush = d3.svg.brush().on("brush", function () { $$.redrawForBrush(); });
5229
        $$.brush.update = function () {
5230
            if ($$.context) { $$.context.select('.' + CLASS.brush).call(this); }
5231
            return this;
5232
        };
5233
        $$.brush.scale = function (scale) {
5234
            return $$.config.axis_rotated ? this.y(scale) : this.x(scale);
5235
        };
5236
    };
5237
    c3_chart_internal_fn.initSubchart = function () {
5238
        var $$ = this, config = $$.config,
5239
            context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context'));
5240

    
5241
        context.style('visibility', config.subchart_show ? 'visible' : 'hidden');
5242

    
5243
        // Define g for chart area
5244
        context.append('g')
5245
            .attr("clip-path", $$.clipPathForSubchart)
5246
            .attr('class', CLASS.chart);
5247

    
5248
        // Define g for bar chart area
5249
        context.select('.' + CLASS.chart).append("g")
5250
            .attr("class", CLASS.chartBars);
5251

    
5252
        // Define g for line chart area
5253
        context.select('.' + CLASS.chart).append("g")
5254
            .attr("class", CLASS.chartLines);
5255

    
5256
        // Add extent rect for Brush
5257
        context.append("g")
5258
            .attr("clip-path", $$.clipPath)
5259
            .attr("class", CLASS.brush)
5260
            .call($$.brush);
5261

    
5262
        // ATTENTION: This must be called AFTER chart added
5263
        // Add Axis
5264
        $$.axes.subx = context.append("g")
5265
            .attr("class", CLASS.axisX)
5266
            .attr("transform", $$.getTranslate('subx'))
5267
            .attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis);
5268
    };
5269
    c3_chart_internal_fn.updateTargetsForSubchart = function (targets) {
5270
        var $$ = this, context = $$.context, config = $$.config,
5271
            contextLineEnter, contextLineUpdate, contextBarEnter, contextBarUpdate,
5272
            classChartBar = $$.classChartBar.bind($$),
5273
            classBars = $$.classBars.bind($$),
5274
            classChartLine = $$.classChartLine.bind($$),
5275
            classLines = $$.classLines.bind($$),
5276
            classAreas = $$.classAreas.bind($$);
5277

    
5278
        if (config.subchart_show) {
5279
            //-- Bar --//
5280
            contextBarUpdate = context.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar)
5281
                .data(targets)
5282
                .attr('class', classChartBar);
5283
            contextBarEnter = contextBarUpdate.enter().append('g')
5284
                .style('opacity', 0)
5285
                .attr('class', classChartBar);
5286
            // Bars for each data
5287
            contextBarEnter.append('g')
5288
                .attr("class", classBars);
5289

    
5290
            //-- Line --//
5291
            contextLineUpdate = context.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine)
5292
                .data(targets)
5293
                .attr('class', classChartLine);
5294
            contextLineEnter = contextLineUpdate.enter().append('g')
5295
                .style('opacity', 0)
5296
                .attr('class', classChartLine);
5297
            // Lines for each data
5298
            contextLineEnter.append("g")
5299
                .attr("class", classLines);
5300
            // Area
5301
            contextLineEnter.append("g")
5302
                .attr("class", classAreas);
5303

    
5304
            //-- Brush --//
5305
            context.selectAll('.' + CLASS.brush + ' rect')
5306
                .attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2);
5307
        }
5308
    };
5309
    c3_chart_internal_fn.updateBarForSubchart = function (durationForExit) {
5310
        var $$ = this;
5311
        $$.contextBar = $$.context.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar)
5312
            .data($$.barData.bind($$));
5313
        $$.contextBar.enter().append('path')
5314
            .attr("class", $$.classBar.bind($$))
5315
            .style("stroke", 'none')
5316
            .style("fill", $$.color);
5317
        $$.contextBar
5318
            .style("opacity", $$.initialOpacity.bind($$));
5319
        $$.contextBar.exit().transition().duration(durationForExit)
5320
            .style('opacity', 0)
5321
            .remove();
5322
    };
5323
    c3_chart_internal_fn.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) {
5324
        (withTransition ? this.contextBar.transition().duration(duration) : this.contextBar)
5325
            .attr('d', drawBarOnSub)
5326
            .style('opacity', 1);
5327
    };
5328
    c3_chart_internal_fn.updateLineForSubchart = function (durationForExit) {
5329
        var $$ = this;
5330
        $$.contextLine = $$.context.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line)
5331
            .data($$.lineData.bind($$));
5332
        $$.contextLine.enter().append('path')
5333
            .attr('class', $$.classLine.bind($$))
5334
            .style('stroke', $$.color);
5335
        $$.contextLine
5336
            .style("opacity", $$.initialOpacity.bind($$));
5337
        $$.contextLine.exit().transition().duration(durationForExit)
5338
            .style('opacity', 0)
5339
            .remove();
5340
    };
5341
    c3_chart_internal_fn.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) {
5342
        (withTransition ? this.contextLine.transition().duration(duration) : this.contextLine)
5343
            .attr("d", drawLineOnSub)
5344
            .style('opacity', 1);
5345
    };
5346
    c3_chart_internal_fn.updateAreaForSubchart = function (durationForExit) {
5347
        var $$ = this, d3 = $$.d3;
5348
        $$.contextArea = $$.context.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area)
5349
            .data($$.lineData.bind($$));
5350
        $$.contextArea.enter().append('path')
5351
            .attr("class", $$.classArea.bind($$))
5352
            .style("fill", $$.color)
5353
            .style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; });
5354
        $$.contextArea
5355
            .style("opacity", 0);
5356
        $$.contextArea.exit().transition().duration(durationForExit)
5357
            .style('opacity', 0)
5358
            .remove();
5359
    };
5360
    c3_chart_internal_fn.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) {
5361
        (withTransition ? this.contextArea.transition().duration(duration) : this.contextArea)
5362
            .attr("d", drawAreaOnSub)
5363
            .style("fill", this.color)
5364
            .style("opacity", this.orgAreaOpacity);
5365
    };
5366
    c3_chart_internal_fn.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) {
5367
        var $$ = this, d3 = $$.d3, config = $$.config,
5368
            drawAreaOnSub, drawBarOnSub, drawLineOnSub;
5369

    
5370
        $$.context.style('visibility', config.subchart_show ? 'visible' : 'hidden');
5371

    
5372
        // subchart
5373
        if (config.subchart_show) {
5374
            // reflect main chart to extent on subchart if zoomed
5375
            if (d3.event && d3.event.type === 'zoom') {
5376
                $$.brush.extent($$.x.orgDomain()).update();
5377
            }
5378
            // update subchart elements if needed
5379
            if (withSubchart) {
5380

    
5381
                // extent rect
5382
                if (!$$.brush.empty()) {
5383
                    $$.brush.extent($$.x.orgDomain()).update();
5384
                }
5385
                // setup drawer - MEMO: this must be called after axis updated
5386
                drawAreaOnSub = $$.generateDrawArea(areaIndices, true);
5387
                drawBarOnSub = $$.generateDrawBar(barIndices, true);
5388
                drawLineOnSub = $$.generateDrawLine(lineIndices, true);
5389

    
5390
                $$.updateBarForSubchart(duration);
5391
                $$.updateLineForSubchart(duration);
5392
                $$.updateAreaForSubchart(duration);
5393

    
5394
                $$.redrawBarForSubchart(drawBarOnSub, duration, duration);
5395
                $$.redrawLineForSubchart(drawLineOnSub, duration, duration);
5396
                $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration);
5397
            }
5398
        }
5399
    };
5400
    c3_chart_internal_fn.redrawForBrush = function () {
5401
        var $$ = this, x = $$.x;
5402
        $$.redraw({
5403
            withTransition: false,
5404
            withY: $$.config.zoom_rescale,
5405
            withSubchart: false,
5406
            withUpdateXDomain: true,
5407
            withDimension: false
5408
        });
5409
        $$.config.subchart_onbrush.call($$.api, x.orgDomain());
5410
    };
5411
    c3_chart_internal_fn.transformContext = function (withTransition, transitions) {
5412
        var $$ = this, subXAxis;
5413
        if (transitions && transitions.axisSubX) {
5414
            subXAxis = transitions.axisSubX;
5415
        } else {
5416
            subXAxis = $$.context.select('.' + CLASS.axisX);
5417
            if (withTransition) { subXAxis = subXAxis.transition(); }
5418
        }
5419
        $$.context.attr("transform", $$.getTranslate('context'));
5420
        subXAxis.attr("transform", $$.getTranslate('subx'));
5421
    };
5422
    c3_chart_internal_fn.getDefaultExtent = function () {
5423
        var $$ = this, config = $$.config,
5424
            extent = isFunction(config.axis_x_extent) ? config.axis_x_extent($$.getXDomain($$.data.targets)) : config.axis_x_extent;
5425
        if ($$.isTimeSeries()) {
5426
            extent = [$$.parseDate(extent[0]), $$.parseDate(extent[1])];
5427
        }
5428
        return extent;
5429
    };
5430

    
5431
    c3_chart_internal_fn.initZoom = function () {
5432
        var $$ = this, d3 = $$.d3, config = $$.config, startEvent;
5433

    
5434
        $$.zoom = d3.behavior.zoom()
5435
            .on("zoomstart", function () {
5436
                startEvent = d3.event.sourceEvent;
5437
                $$.zoom.altDomain = d3.event.sourceEvent.altKey ? $$.x.orgDomain() : null;
5438
                config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent);
5439
            })
5440
            .on("zoom", function () {
5441
                $$.redrawForZoom.call($$);
5442
            })
5443
            .on('zoomend', function () {
5444
                var event = d3.event.sourceEvent;
5445
                // if click, do nothing. otherwise, click interaction will be canceled.
5446
                if (event && startEvent.clientX === event.clientX && startEvent.clientY === event.clientY) {
5447
                    return;
5448
                }
5449
                $$.redrawEventRect();
5450
                $$.updateZoom();
5451
                config.zoom_onzoomend.call($$.api, $$.x.orgDomain());
5452
            });
5453
        $$.zoom.scale = function (scale) {
5454
            return config.axis_rotated ? this.y(scale) : this.x(scale);
5455
        };
5456
        $$.zoom.orgScaleExtent = function () {
5457
            var extent = config.zoom_extent ? config.zoom_extent : [1, 10];
5458
            return [extent[0], Math.max($$.getMaxDataCount() / extent[1], extent[1])];
5459
        };
5460
        $$.zoom.updateScaleExtent = function () {
5461
            var ratio = diffDomain($$.x.orgDomain()) / diffDomain($$.orgXDomain),
5462
                extent = this.orgScaleExtent();
5463
            this.scaleExtent([extent[0] * ratio, extent[1] * ratio]);
5464
            return this;
5465
        };
5466
    };
5467
    c3_chart_internal_fn.updateZoom = function () {
5468
        var $$ = this, z = $$.config.zoom_enabled ? $$.zoom : function () {};
5469
        $$.main.select('.' + CLASS.zoomRect).call(z).on("dblclick.zoom", null);
5470
        $$.main.selectAll('.' + CLASS.eventRect).call(z).on("dblclick.zoom", null);
5471
    };
5472
    c3_chart_internal_fn.redrawForZoom = function () {
5473
        var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x;
5474
        if (!config.zoom_enabled) {
5475
            return;
5476
        }
5477
        if ($$.filterTargetsToShow($$.data.targets).length === 0) {
5478
            return;
5479
        }
5480
        if (d3.event.sourceEvent.type === 'mousemove' && zoom.altDomain) {
5481
            x.domain(zoom.altDomain);
5482
            zoom.scale(x).updateScaleExtent();
5483
            return;
5484
        }
5485
        if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) {
5486
            x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]);
5487
        }
5488
        $$.redraw({
5489
            withTransition: false,
5490
            withY: config.zoom_rescale,
5491
            withSubchart: false,
5492
            withEventRect: false,
5493
            withDimension: false
5494
        });
5495
        if (d3.event.sourceEvent.type === 'mousemove') {
5496
            $$.cancelClick = true;
5497
        }
5498
        config.zoom_onzoom.call($$.api, x.orgDomain());
5499
    };
5500

    
5501
    c3_chart_internal_fn.generateColor = function () {
5502
        var $$ = this, config = $$.config, d3 = $$.d3,
5503
            colors = config.data_colors,
5504
            pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.scale.category10().range(),
5505
            callback = config.data_color,
5506
            ids = [];
5507

    
5508
        return function (d) {
5509
            var id = d.id || (d.data && d.data.id) || d, color;
5510

    
5511
            // if callback function is provided
5512
            if (colors[id] instanceof Function) {
5513
                color = colors[id](d);
5514
            }
5515
            // if specified, choose that color
5516
            else if (colors[id]) {
5517
                color = colors[id];
5518
            }
5519
            // if not specified, choose from pattern
5520
            else {
5521
                if (ids.indexOf(id) < 0) { ids.push(id); }
5522
                color = pattern[ids.indexOf(id) % pattern.length];
5523
                colors[id] = color;
5524
            }
5525
            return callback instanceof Function ? callback(color, d) : color;
5526
        };
5527
    };
5528
    c3_chart_internal_fn.generateLevelColor = function () {
5529
        var $$ = this, config = $$.config,
5530
            colors = config.color_pattern,
5531
            threshold = config.color_threshold,
5532
            asValue = threshold.unit === 'value',
5533
            values = threshold.values && threshold.values.length ? threshold.values : [],
5534
            max = threshold.max || 100;
5535
        return notEmpty(config.color_threshold) ? function (value) {
5536
            var i, v, color = colors[colors.length - 1];
5537
            for (i = 0; i < values.length; i++) {
5538
                v = asValue ? value : (value * 100 / max);
5539
                if (v < values[i]) {
5540
                    color = colors[i];
5541
                    break;
5542
                }
5543
            }
5544
            return color;
5545
        } : null;
5546
    };
5547

    
5548
    c3_chart_internal_fn.getYFormat = function (forArc) {
5549
        var $$ = this,
5550
            formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat,
5551
            formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format;
5552
        return function (v, ratio, id) {
5553
            var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY;
5554
            return format.call($$, v, ratio);
5555
        };
5556
    };
5557
    c3_chart_internal_fn.yFormat = function (v) {
5558
        var $$ = this, config = $$.config,
5559
            format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat;
5560
        return format(v);
5561
    };
5562
    c3_chart_internal_fn.y2Format = function (v) {
5563
        var $$ = this, config = $$.config,
5564
            format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat;
5565
        return format(v);
5566
    };
5567
    c3_chart_internal_fn.defaultValueFormat = function (v) {
5568
        return isValue(v) ? +v : "";
5569
    };
5570
    c3_chart_internal_fn.defaultArcValueFormat = function (v, ratio) {
5571
        return (ratio * 100).toFixed(1) + '%';
5572
    };
5573
    c3_chart_internal_fn.dataLabelFormat = function (targetId) {
5574
        var $$ = this, data_labels = $$.config.data_labels,
5575
            format, defaultFormat = function (v) { return isValue(v) ? +v : ""; };
5576
        // find format according to axis id
5577
        if (typeof data_labels.format === 'function') {
5578
            format = data_labels.format;
5579
        } else if (typeof data_labels.format === 'object') {
5580
            if (data_labels.format[targetId]) {
5581
                format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId];
5582
            } else {
5583
                format = function () { return ''; };
5584
            }
5585
        } else {
5586
            format = defaultFormat;
5587
        }
5588
        return format;
5589
    };
5590

    
5591
    c3_chart_internal_fn.hasCaches = function (ids) {
5592
        for (var i = 0; i < ids.length; i++) {
5593
            if (! (ids[i] in this.cache)) { return false; }
5594
        }
5595
        return true;
5596
    };
5597
    c3_chart_internal_fn.addCache = function (id, target) {
5598
        this.cache[id] = this.cloneTarget(target);
5599
    };
5600
    c3_chart_internal_fn.getCaches = function (ids) {
5601
        var targets = [], i;
5602
        for (i = 0; i < ids.length; i++) {
5603
            if (ids[i] in this.cache) { targets.push(this.cloneTarget(this.cache[ids[i]])); }
5604
        }
5605
        return targets;
5606
    };
5607

    
5608
    var CLASS = c3_chart_internal_fn.CLASS = {
5609
        target: 'c3-target',
5610
        chart: 'c3-chart',
5611
        chartLine: 'c3-chart-line',
5612
        chartLines: 'c3-chart-lines',
5613
        chartBar: 'c3-chart-bar',
5614
        chartBars: 'c3-chart-bars',
5615
        chartText: 'c3-chart-text',
5616
        chartTexts: 'c3-chart-texts',
5617
        chartArc: 'c3-chart-arc',
5618
        chartArcs: 'c3-chart-arcs',
5619
        chartArcsTitle: 'c3-chart-arcs-title',
5620
        chartArcsBackground: 'c3-chart-arcs-background',
5621
        chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit',
5622
        chartArcsGaugeMax: 'c3-chart-arcs-gauge-max',
5623
        chartArcsGaugeMin: 'c3-chart-arcs-gauge-min',
5624
        selectedCircle: 'c3-selected-circle',
5625
        selectedCircles: 'c3-selected-circles',
5626
        eventRect: 'c3-event-rect',
5627
        eventRects: 'c3-event-rects',
5628
        eventRectsSingle: 'c3-event-rects-single',
5629
        eventRectsMultiple: 'c3-event-rects-multiple',
5630
        zoomRect: 'c3-zoom-rect',
5631
        brush: 'c3-brush',
5632
        focused: 'c3-focused',
5633
        defocused: 'c3-defocused',
5634
        region: 'c3-region',
5635
        regions: 'c3-regions',
5636
        tooltipContainer: 'c3-tooltip-container',
5637
        tooltip: 'c3-tooltip',
5638
        tooltipName: 'c3-tooltip-name',
5639
        shape: 'c3-shape',
5640
        shapes: 'c3-shapes',
5641
        line: 'c3-line',
5642
        lines: 'c3-lines',
5643
        bar: 'c3-bar',
5644
        bars: 'c3-bars',
5645
        circle: 'c3-circle',
5646
        circles: 'c3-circles',
5647
        arc: 'c3-arc',
5648
        arcs: 'c3-arcs',
5649
        area: 'c3-area',
5650
        areas: 'c3-areas',
5651
        empty: 'c3-empty',
5652
        text: 'c3-text',
5653
        texts: 'c3-texts',
5654
        gaugeValue: 'c3-gauge-value',
5655
        grid: 'c3-grid',
5656
        gridLines: 'c3-grid-lines',
5657
        xgrid: 'c3-xgrid',
5658
        xgrids: 'c3-xgrids',
5659
        xgridLine: 'c3-xgrid-line',
5660
        xgridLines: 'c3-xgrid-lines',
5661
        xgridFocus: 'c3-xgrid-focus',
5662
        ygrid: 'c3-ygrid',
5663
        ygrids: 'c3-ygrids',
5664
        ygridLine: 'c3-ygrid-line',
5665
        ygridLines: 'c3-ygrid-lines',
5666
        axis: 'c3-axis',
5667
        axisX: 'c3-axis-x',
5668
        axisXLabel: 'c3-axis-x-label',
5669
        axisY: 'c3-axis-y',
5670
        axisYLabel: 'c3-axis-y-label',
5671
        axisY2: 'c3-axis-y2',
5672
        axisY2Label: 'c3-axis-y2-label',
5673
        legendBackground: 'c3-legend-background',
5674
        legendItem: 'c3-legend-item',
5675
        legendItemEvent: 'c3-legend-item-event',
5676
        legendItemTile: 'c3-legend-item-tile',
5677
        legendItemHidden: 'c3-legend-item-hidden',
5678
        legendItemFocused: 'c3-legend-item-focused',
5679
        dragarea: 'c3-dragarea',
5680
        EXPANDED: '_expanded_',
5681
        SELECTED: '_selected_',
5682
        INCLUDED: '_included_'
5683
    };
5684
    c3_chart_internal_fn.generateClass = function (prefix, targetId) {
5685
        return " " + prefix + " " + prefix + this.getTargetSelectorSuffix(targetId);
5686
    };
5687
    c3_chart_internal_fn.classText = function (d) {
5688
        return this.generateClass(CLASS.text, d.index);
5689
    };
5690
    c3_chart_internal_fn.classTexts = function (d) {
5691
        return this.generateClass(CLASS.texts, d.id);
5692
    };
5693
    c3_chart_internal_fn.classShape = function (d) {
5694
        return this.generateClass(CLASS.shape, d.index);
5695
    };
5696
    c3_chart_internal_fn.classShapes = function (d) {
5697
        return this.generateClass(CLASS.shapes, d.id);
5698
    };
5699
    c3_chart_internal_fn.classLine = function (d) {
5700
        return this.classShape(d) + this.generateClass(CLASS.line, d.id);
5701
    };
5702
    c3_chart_internal_fn.classLines = function (d) {
5703
        return this.classShapes(d) + this.generateClass(CLASS.lines, d.id);
5704
    };
5705
    c3_chart_internal_fn.classCircle = function (d) {
5706
        return this.classShape(d) + this.generateClass(CLASS.circle, d.index);
5707
    };
5708
    c3_chart_internal_fn.classCircles = function (d) {
5709
        return this.classShapes(d) + this.generateClass(CLASS.circles, d.id);
5710
    };
5711
    c3_chart_internal_fn.classBar = function (d) {
5712
        return this.classShape(d) + this.generateClass(CLASS.bar, d.index);
5713
    };
5714
    c3_chart_internal_fn.classBars = function (d) {
5715
        return this.classShapes(d) + this.generateClass(CLASS.bars, d.id);
5716
    };
5717
    c3_chart_internal_fn.classArc = function (d) {
5718
        return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id);
5719
    };
5720
    c3_chart_internal_fn.classArcs = function (d) {
5721
        return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id);
5722
    };
5723
    c3_chart_internal_fn.classArea = function (d) {
5724
        return this.classShape(d) + this.generateClass(CLASS.area, d.id);
5725
    };
5726
    c3_chart_internal_fn.classAreas = function (d) {
5727
        return this.classShapes(d) + this.generateClass(CLASS.areas, d.id);
5728
    };
5729
    c3_chart_internal_fn.classRegion = function (d, i) {
5730
        return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '');
5731
    };
5732
    c3_chart_internal_fn.classEvent = function (d) {
5733
        return this.generateClass(CLASS.eventRect, d.index);
5734
    };
5735
    c3_chart_internal_fn.classTarget = function (id) {
5736
        var $$ = this;
5737
        var additionalClassSuffix = $$.config.data_classes[id], additionalClass = '';
5738
        if (additionalClassSuffix) {
5739
            additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix;
5740
        }
5741
        return $$.generateClass(CLASS.target, id) + additionalClass;
5742
    };
5743
    c3_chart_internal_fn.classFocus = function (d) {
5744
        return this.classFocused(d) + this.classDefocused(d);
5745
    };
5746
    c3_chart_internal_fn.classFocused = function (d) {
5747
        return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : '');
5748
    };
5749
    c3_chart_internal_fn.classDefocused = function (d) {
5750
        return ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '');
5751
    };
5752
    c3_chart_internal_fn.classChartText = function (d) {
5753
        return CLASS.chartText + this.classTarget(d.id);
5754
    };
5755
    c3_chart_internal_fn.classChartLine = function (d) {
5756
        return CLASS.chartLine + this.classTarget(d.id);
5757
    };
5758
    c3_chart_internal_fn.classChartBar = function (d) {
5759
        return CLASS.chartBar + this.classTarget(d.id);
5760
    };
5761
    c3_chart_internal_fn.classChartArc = function (d) {
5762
        return CLASS.chartArc + this.classTarget(d.data.id);
5763
    };
5764
    c3_chart_internal_fn.getTargetSelectorSuffix = function (targetId) {
5765
        return targetId || targetId === 0 ? ('-' + targetId).replace(/[\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g, '-') : '';
5766
    };
5767
    c3_chart_internal_fn.selectorTarget = function (id, prefix) {
5768
        return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id);
5769
    };
5770
    c3_chart_internal_fn.selectorTargets = function (ids, prefix) {
5771
        var $$ = this;
5772
        ids = ids || [];
5773
        return ids.length ? ids.map(function (id) { return $$.selectorTarget(id, prefix); }) : null;
5774
    };
5775
    c3_chart_internal_fn.selectorLegend = function (id) {
5776
        return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id);
5777
    };
5778
    c3_chart_internal_fn.selectorLegends = function (ids) {
5779
        var $$ = this;
5780
        return ids && ids.length ? ids.map(function (id) { return $$.selectorLegend(id); }) : null;
5781
    };
5782

    
5783
    var isValue = c3_chart_internal_fn.isValue = function (v) {
5784
        return v || v === 0;
5785
    },
5786
        isFunction = c3_chart_internal_fn.isFunction = function (o) {
5787
            return typeof o === 'function';
5788
        },
5789
        isString = c3_chart_internal_fn.isString = function (o) {
5790
            return typeof o === 'string';
5791
        },
5792
        isUndefined = c3_chart_internal_fn.isUndefined = function (v) {
5793
            return typeof v === 'undefined';
5794
        },
5795
        isDefined = c3_chart_internal_fn.isDefined = function (v) {
5796
            return typeof v !== 'undefined';
5797
        },
5798
        ceil10 = c3_chart_internal_fn.ceil10 = function (v) {
5799
            return Math.ceil(v / 10) * 10;
5800
        },
5801
        asHalfPixel = c3_chart_internal_fn.asHalfPixel = function (n) {
5802
            return Math.ceil(n) + 0.5;
5803
        },
5804
        diffDomain = c3_chart_internal_fn.diffDomain = function (d) {
5805
            return d[1] - d[0];
5806
        },
5807
        isEmpty = c3_chart_internal_fn.isEmpty = function (o) {
5808
            return !o || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0);
5809
        },
5810
        notEmpty = c3_chart_internal_fn.notEmpty = function (o) {
5811
            return Object.keys(o).length > 0;
5812
        },
5813
        getOption = c3_chart_internal_fn.getOption = function (options, key, defaultValue) {
5814
            return isDefined(options[key]) ? options[key] : defaultValue;
5815
        },
5816
        hasValue = c3_chart_internal_fn.hasValue = function (dict, value) {
5817
            var found = false;
5818
            Object.keys(dict).forEach(function (key) {
5819
                if (dict[key] === value) { found = true; }
5820
            });
5821
            return found;
5822
        },
5823
        getPathBox = c3_chart_internal_fn.getPathBox = function (path) {
5824
            var box = path.getBoundingClientRect(),
5825
                items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)],
5826
                minX = items[0].x, minY = Math.min(items[0].y, items[1].y);
5827
            return {x: minX, y: minY, width: box.width, height: box.height};
5828
        };
5829

    
5830
    c3_chart_fn.focus = function (targetIds) {
5831
        var $$ = this.internal, candidates;
5832

    
5833
        targetIds = $$.mapToTargetIds(targetIds);
5834
        candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))),
5835

    
5836
        this.revert();
5837
        this.defocus();
5838
        candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false);
5839
        if ($$.hasArcType()) {
5840
            $$.expandArc(targetIds);
5841
        }
5842
        $$.toggleFocusLegend(targetIds, true);
5843

    
5844
        $$.focusedTargetIds = targetIds;
5845
        $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) {
5846
            return targetIds.indexOf(id) < 0;
5847
        });
5848
    };
5849

    
5850
    c3_chart_fn.defocus = function (targetIds) {
5851
        var $$ = this.internal, candidates;
5852

    
5853
        targetIds = $$.mapToTargetIds(targetIds);
5854
        candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))),
5855

    
5856
        candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true);
5857
        if ($$.hasArcType()) {
5858
            $$.unexpandArc(targetIds);
5859
        }
5860
        $$.toggleFocusLegend(targetIds, false);
5861

    
5862
        $$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) {
5863
            return targetIds.indexOf(id) < 0;
5864
        });
5865
        $$.defocusedTargetIds = targetIds;
5866
    };
5867

    
5868
    c3_chart_fn.revert = function (targetIds) {
5869
        var $$ = this.internal, candidates;
5870

    
5871
        targetIds = $$.mapToTargetIds(targetIds);
5872
        candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets
5873

    
5874
        candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false);
5875
        if ($$.hasArcType()) {
5876
            $$.unexpandArc(targetIds);
5877
        }
5878
        if ($$.config.legend_show) {
5879
            $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$)));
5880
            $$.legend.selectAll($$.selectorLegends(targetIds))
5881
                .filter(function () {
5882
                    return $$.d3.select(this).classed(CLASS.legendItemFocused);
5883
                })
5884
                .classed(CLASS.legendItemFocused, false);
5885
        }
5886

    
5887
        $$.focusedTargetIds = [];
5888
        $$.defocusedTargetIds = [];
5889
    };
5890

    
5891
    c3_chart_fn.show = function (targetIds, options) {
5892
        var $$ = this.internal, targets;
5893

    
5894
        targetIds = $$.mapToTargetIds(targetIds);
5895
        options = options || {};
5896

    
5897
        $$.removeHiddenTargetIds(targetIds);
5898
        targets = $$.svg.selectAll($$.selectorTargets(targetIds));
5899

    
5900
        targets.transition()
5901
            .style('opacity', 1, 'important')
5902
            .call($$.endall, function () {
5903
                targets.style('opacity', null).style('opacity', 1);
5904
            });
5905

    
5906
        if (options.withLegend) {
5907
            $$.showLegend(targetIds);
5908
        }
5909

    
5910
        $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
5911
    };
5912

    
5913
    c3_chart_fn.hide = function (targetIds, options) {
5914
        var $$ = this.internal, targets;
5915

    
5916
        targetIds = $$.mapToTargetIds(targetIds);
5917
        options = options || {};
5918

    
5919
        $$.addHiddenTargetIds(targetIds);
5920
        targets = $$.svg.selectAll($$.selectorTargets(targetIds));
5921

    
5922
        targets.transition()
5923
            .style('opacity', 0, 'important')
5924
            .call($$.endall, function () {
5925
                targets.style('opacity', null).style('opacity', 0);
5926
            });
5927

    
5928
        if (options.withLegend) {
5929
            $$.hideLegend(targetIds);
5930
        }
5931

    
5932
        $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
5933
    };
5934

    
5935
    c3_chart_fn.toggle = function (targetIds, options) {
5936
        var that = this, $$ = this.internal;
5937
        $$.mapToTargetIds(targetIds).forEach(function (targetId) {
5938
            $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options);
5939
        });
5940
    };
5941

    
5942
    c3_chart_fn.zoom = function (domain) {
5943
        var $$ = this.internal;
5944
        if (domain) {
5945
            if ($$.isTimeSeries()) {
5946
                domain = domain.map(function (x) { return $$.parseDate(x); });
5947
            }
5948
            $$.brush.extent(domain);
5949
            $$.redraw({withUpdateXDomain: true, withY: $$.config.zoom_rescale});
5950
            $$.config.zoom_onzoom.call(this, $$.x.orgDomain());
5951
        }
5952
        return $$.brush.extent();
5953
    };
5954
    c3_chart_fn.zoom.enable = function (enabled) {
5955
        var $$ = this.internal;
5956
        $$.config.zoom_enabled = enabled;
5957
        $$.updateAndRedraw();
5958
    };
5959
    c3_chart_fn.unzoom = function () {
5960
        var $$ = this.internal;
5961
        $$.brush.clear().update();
5962
        $$.redraw({withUpdateXDomain: true});
5963
    };
5964

    
5965
    c3_chart_fn.load = function (args) {
5966
        var $$ = this.internal, config = $$.config;
5967
        // update xs if specified
5968
        if (args.xs) {
5969
            $$.addXs(args.xs);
5970
        }
5971
        // update classes if exists
5972
        if ('classes' in args) {
5973
            Object.keys(args.classes).forEach(function (id) {
5974
                config.data_classes[id] = args.classes[id];
5975
            });
5976
        }
5977
        // update categories if exists
5978
        if ('categories' in args && $$.isCategorized()) {
5979
            config.axis_x_categories = args.categories;
5980
        }
5981
        // update axes if exists
5982
        if ('axes' in args) {
5983
            Object.keys(args.axes).forEach(function (id) {
5984
                config.data_axes[id] = args.axes[id];
5985
            });
5986
        }
5987
        // update colors if exists
5988
        if ('colors' in args) {
5989
            Object.keys(args.colors).forEach(function (id) {
5990
                config.data_colors[id] = args.colors[id];
5991
            });
5992
        }
5993
        // use cache if exists
5994
        if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) {
5995
            $$.load($$.getCaches(args.cacheIds), args.done);
5996
            return;
5997
        }
5998
        // unload if needed
5999
        if ('unload' in args) {
6000
            // TODO: do not unload if target will load (included in url/rows/columns)
6001
            $$.unload($$.mapToTargetIds((typeof args.unload === 'boolean' && args.unload) ? null : args.unload), function () {
6002
                $$.loadFromArgs(args);
6003
            });
6004
        } else {
6005
            $$.loadFromArgs(args);
6006
        }
6007
    };
6008

    
6009
    c3_chart_fn.unload = function (args) {
6010
        var $$ = this.internal;
6011
        args = args || {};
6012
        if (args instanceof Array) {
6013
            args = {ids: args};
6014
        } else if (typeof args === 'string') {
6015
            args = {ids: [args]};
6016
        }
6017
        $$.unload($$.mapToTargetIds(args.ids), function () {
6018
            $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true});
6019
            if (args.done) { args.done(); }
6020
        });
6021
    };
6022

    
6023
    c3_chart_fn.flow = function (args) {
6024
        var $$ = this.internal,
6025
            targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(),
6026
            dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to;
6027

    
6028
        if (args.json) {
6029
            data = $$.convertJsonToData(args.json, args.keys);
6030
        }
6031
        else if (args.rows) {
6032
            data = $$.convertRowsToData(args.rows);
6033
        }
6034
        else if (args.columns) {
6035
            data = $$.convertColumnsToData(args.columns);
6036
        }
6037
        else {
6038
            return;
6039
        }
6040
        targets = $$.convertDataToTargets(data, true);
6041

    
6042
        // Update/Add data
6043
        $$.data.targets.forEach(function (t) {
6044
            var found = false, i, j;
6045
            for (i = 0; i < targets.length; i++) {
6046
                if (t.id === targets[i].id) {
6047
                    found = true;
6048

    
6049
                    if (t.values[t.values.length - 1]) {
6050
                        tail = t.values[t.values.length - 1].index + 1;
6051
                    }
6052
                    length = targets[i].values.length;
6053

    
6054
                    for (j = 0; j < length; j++) {
6055
                        targets[i].values[j].index = tail + j;
6056
                        if (!$$.isTimeSeries()) {
6057
                            targets[i].values[j].x = tail + j;
6058
                        }
6059
                    }
6060
                    t.values = t.values.concat(targets[i].values);
6061

    
6062
                    targets.splice(i, 1);
6063
                    break;
6064
                }
6065
            }
6066
            if (!found) { notfoundIds.push(t.id); }
6067
        });
6068

    
6069
        // Append null for not found targets
6070
        $$.data.targets.forEach(function (t) {
6071
            var i, j;
6072
            for (i = 0; i < notfoundIds.length; i++) {
6073
                if (t.id === notfoundIds[i]) {
6074
                    tail = t.values[t.values.length - 1].index + 1;
6075
                    for (j = 0; j < length; j++) {
6076
                        t.values.push({
6077
                            id: t.id,
6078
                            index: tail + j,
6079
                            x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j,
6080
                            value: null
6081
                        });
6082
                    }
6083
                }
6084
            }
6085
        });
6086

    
6087
        // Generate null values for new target
6088
        if ($$.data.targets.length) {
6089
            targets.forEach(function (t) {
6090
                var i, missing = [];
6091
                for (i = $$.data.targets[0].values[0].index; i < tail; i++) {
6092
                    missing.push({
6093
                        id: t.id,
6094
                        index: i,
6095
                        x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i,
6096
                        value: null
6097
                    });
6098
                }
6099
                t.values.forEach(function (v) {
6100
                    v.index += tail;
6101
                    if (!$$.isTimeSeries()) {
6102
                        v.x += tail;
6103
                    }
6104
                });
6105
                t.values = missing.concat(t.values);
6106
            });
6107
        }
6108
        $$.data.targets = $$.data.targets.concat(targets); // add remained
6109

    
6110
        // check data count because behavior needs to change when it's only one
6111
        dataCount = $$.getMaxDataCount();
6112
        baseTarget = $$.data.targets[0];
6113
        baseValue = baseTarget.values[0];
6114

    
6115
        // Update length to flow if needed
6116
        if (isDefined(args.to)) {
6117
            length = 0;
6118
            to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to;
6119
            baseTarget.values.forEach(function (v) {
6120
                if (v.x < to) { length++; }
6121
            });
6122
        } else if (isDefined(args.length)) {
6123
            length = args.length;
6124
        }
6125

    
6126
        // If only one data, update the domain to flow from left edge of the chart
6127
        if (!orgDataCount) {
6128
            if ($$.isTimeSeries()) {
6129
                if (baseTarget.values.length > 1) {
6130
                    diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x;
6131
                } else {
6132
                    diff = baseValue.x - $$.getXDomain($$.data.targets)[0];
6133
                }
6134
            } else {
6135
                diff = 1;
6136
            }
6137
            domain = [baseValue.x - diff, baseValue.x];
6138
            $$.updateXDomain(null, true, true, false, domain);
6139
        } else if (orgDataCount === 1) {
6140
            if ($$.isTimeSeries()) {
6141
                diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2;
6142
                domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)];
6143
                $$.updateXDomain(null, true, true, false, domain);
6144
            }
6145
        }
6146

    
6147
        // Set targets
6148
        $$.updateTargets($$.data.targets);
6149

    
6150
        // Redraw with new targets
6151
        $$.redraw({
6152
            flow: {
6153
                index: baseValue.index,
6154
                length: length,
6155
                duration: isValue(args.duration) ? args.duration : $$.config.transition_duration,
6156
                done: args.done,
6157
                orgDataCount: orgDataCount,
6158
            },
6159
            withLegend: true,
6160
            withTransition: orgDataCount > 1,
6161
            withTrimXDomain: false,
6162
            withUpdateXAxis: true,
6163
        });
6164
    };
6165

    
6166
    c3_chart_internal_fn.generateFlow = function (args) {
6167
        var $$ = this, config = $$.config, d3 = $$.d3;
6168

    
6169
        return function () {
6170
            var targets = args.targets,
6171
                flow = args.flow,
6172
                drawBar = args.drawBar,
6173
                drawLine = args.drawLine,
6174
                drawArea = args.drawArea,
6175
                cx = args.cx,
6176
                cy = args.cy,
6177
                xv = args.xv,
6178
                xForText = args.xForText,
6179
                yForText = args.yForText,
6180
                duration = args.duration;
6181

    
6182
            var translateX, scaleX = 1, transform,
6183
                flowIndex = flow.index,
6184
                flowLength = flow.length,
6185
                flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex),
6186
                flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength),
6187
                orgDomain = $$.x.domain(), domain,
6188
                durationForFlow = flow.duration || duration,
6189
                done = flow.done || function () {},
6190
                wait = $$.generateWait();
6191

    
6192
            var xgrid = $$.xgrid || d3.selectAll([]),
6193
                xgridLines = $$.xgridLines || d3.selectAll([]),
6194
                mainRegion = $$.mainRegion || d3.selectAll([]),
6195
                mainText = $$.mainText || d3.selectAll([]),
6196
                mainBar = $$.mainBar || d3.selectAll([]),
6197
                mainLine = $$.mainLine || d3.selectAll([]),
6198
                mainArea = $$.mainArea || d3.selectAll([]),
6199
                mainCircle = $$.mainCircle || d3.selectAll([]);
6200

    
6201
            // set flag
6202
            $$.flowing = true;
6203

    
6204
            // remove head data after rendered
6205
            $$.data.targets.forEach(function (d) {
6206
                d.values.splice(0, flowLength);
6207
            });
6208

    
6209
            // update x domain to generate axis elements for flow
6210
            domain = $$.updateXDomain(targets, true, true);
6211
            // update elements related to x scale
6212
            if ($$.updateXGrid) { $$.updateXGrid(true); }
6213

    
6214
            // generate transform to flow
6215
            if (!flow.orgDataCount) { // if empty
6216
                if ($$.data.targets[0].values.length !== 1) {
6217
                    translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
6218
                } else {
6219
                    if ($$.isTimeSeries()) {
6220
                        flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0);
6221
                        flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1);
6222
                        translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
6223
                    } else {
6224
                        translateX = diffDomain(domain) / 2;
6225
                    }
6226
                }
6227
            } else if (flow.orgDataCount === 1 || flowStart.x === flowEnd.x) {
6228
                translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
6229
            } else {
6230
                if ($$.isTimeSeries()) {
6231
                    translateX = ($$.x(orgDomain[0]) - $$.x(domain[0]));
6232
                } else {
6233
                    translateX = ($$.x(flowStart.x) - $$.x(flowEnd.x));
6234
                }
6235
            }
6236
            scaleX = (diffDomain(orgDomain) / diffDomain(domain));
6237
            transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)';
6238

    
6239
            // hide tooltip
6240
            $$.hideXGridFocus();
6241
            $$.hideTooltip();
6242

    
6243
            d3.transition().ease('linear').duration(durationForFlow).each(function () {
6244
                wait.add($$.axes.x.transition().call($$.xAxis));
6245
                wait.add(mainBar.transition().attr('transform', transform));
6246
                wait.add(mainLine.transition().attr('transform', transform));
6247
                wait.add(mainArea.transition().attr('transform', transform));
6248
                wait.add(mainCircle.transition().attr('transform', transform));
6249
                wait.add(mainText.transition().attr('transform', transform));
6250
                wait.add(mainRegion.filter($$.isRegionOnX).transition().attr('transform', transform));
6251
                wait.add(xgrid.transition().attr('transform', transform));
6252
                wait.add(xgridLines.transition().attr('transform', transform));
6253
            })
6254
            .call(wait, function () {
6255
                var i, shapes = [], texts = [], eventRects = [];
6256

    
6257
                // remove flowed elements
6258
                if (flowLength) {
6259
                    for (i = 0; i < flowLength; i++) {
6260
                        shapes.push('.' + CLASS.shape + '-' + (flowIndex + i));
6261
                        texts.push('.' + CLASS.text + '-' + (flowIndex + i));
6262
                        eventRects.push('.' + CLASS.eventRect + '-' + (flowIndex + i));
6263
                    }
6264
                    $$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove();
6265
                    $$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove();
6266
                    $$.svg.selectAll('.' + CLASS.eventRects).selectAll(eventRects).remove();
6267
                    $$.svg.select('.' + CLASS.xgrid).remove();
6268
                }
6269

    
6270
                // draw again for removing flowed elements and reverting attr
6271
                xgrid
6272
                    .attr('transform', null)
6273
                    .attr($$.xgridAttr);
6274
                xgridLines
6275
                    .attr('transform', null);
6276
                xgridLines.select('line')
6277
                    .attr("x1", config.axis_rotated ? 0 : xv)
6278
                    .attr("x2", config.axis_rotated ? $$.width : xv);
6279
                xgridLines.select('text')
6280
                    .attr("x", config.axis_rotated ? $$.width : 0)
6281
                    .attr("y", xv);
6282
                mainBar
6283
                    .attr('transform', null)
6284
                    .attr("d", drawBar);
6285
                mainLine
6286
                    .attr('transform', null)
6287
                    .attr("d", drawLine);
6288
                mainArea
6289
                    .attr('transform', null)
6290
                    .attr("d", drawArea);
6291
                mainCircle
6292
                    .attr('transform', null)
6293
                    .attr("cx", cx)
6294
                    .attr("cy", cy);
6295
                mainText
6296
                    .attr('transform', null)
6297
                    .attr('x', xForText)
6298
                    .attr('y', yForText)
6299
                    .style('fill-opacity', $$.opacityForText.bind($$));
6300
                mainRegion
6301
                    .attr('transform', null);
6302
                mainRegion.select('rect').filter($$.isRegionOnX)
6303
                    .attr("x", $$.regionX.bind($$))
6304
                    .attr("width", $$.regionWidth.bind($$));
6305

    
6306
                if (config.interaction_enabled) {
6307
                    $$.redrawEventRect();
6308
                }
6309

    
6310
                // callback for end of flow
6311
                done();
6312

    
6313
                $$.flowing = false;
6314
            });
6315
        };
6316
    };
6317

    
6318
    c3_chart_fn.selected = function (targetId) {
6319
        var $$ = this.internal, d3 = $$.d3;
6320
        return d3.merge(
6321
            $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape)
6322
                .filter(function () { return d3.select(this).classed(CLASS.SELECTED); })
6323
                .map(function (d) { return d.map(function (d) { var data = d.__data__; return data.data ? data.data : data; }); })
6324
        );
6325
    };
6326
    c3_chart_fn.select = function (ids, indices, resetOther) {
6327
        var $$ = this.internal, d3 = $$.d3, config = $$.config;
6328
        if (! config.data_selection_enabled) { return; }
6329
        $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) {
6330
            var shape = d3.select(this), id = d.data ? d.data.id : d.id,
6331
                toggle = $$.getToggle(this, d).bind($$),
6332
                isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0,
6333
                isTargetIndex = !indices || indices.indexOf(i) >= 0,
6334
                isSelected = shape.classed(CLASS.SELECTED);
6335
            // line/area selection not supported yet
6336
            if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) {
6337
                return;
6338
            }
6339
            if (isTargetId && isTargetIndex) {
6340
                if (config.data_selection_isselectable(d) && !isSelected) {
6341
                    toggle(true, shape.classed(CLASS.SELECTED, true), d, i);
6342
                }
6343
            } else if (isDefined(resetOther) && resetOther) {
6344
                if (isSelected) {
6345
                    toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
6346
                }
6347
            }
6348
        });
6349
    };
6350
    c3_chart_fn.unselect = function (ids, indices) {
6351
        var $$ = this.internal, d3 = $$.d3, config = $$.config;
6352
        if (! config.data_selection_enabled) { return; }
6353
        $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) {
6354
            var shape = d3.select(this), id = d.data ? d.data.id : d.id,
6355
                toggle = $$.getToggle(this, d).bind($$),
6356
                isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0,
6357
                isTargetIndex = !indices || indices.indexOf(i) >= 0,
6358
                isSelected = shape.classed(CLASS.SELECTED);
6359
            // line/area selection not supported yet
6360
            if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) {
6361
                return;
6362
            }
6363
            if (isTargetId && isTargetIndex) {
6364
                if (config.data_selection_isselectable(d)) {
6365
                    if (isSelected) {
6366
                        toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
6367
                    }
6368
                }
6369
            }
6370
        });
6371
    };
6372

    
6373
    c3_chart_fn.transform = function (type, targetIds) {
6374
        var $$ = this.internal,
6375
            options = ['pie', 'donut'].indexOf(type) >= 0 ? {withTransform: true} : null;
6376
        $$.transformTo(targetIds, type, options);
6377
    };
6378

    
6379
    c3_chart_internal_fn.transformTo = function (targetIds, type, optionsForRedraw) {
6380
        var $$ = this,
6381
            withTransitionForAxis = !$$.hasArcType(),
6382
            options = optionsForRedraw || {withTransitionForAxis: withTransitionForAxis};
6383
        options.withTransitionForTransform = false;
6384
        $$.transiting = false;
6385
        $$.setTargetType(targetIds, type);
6386
        $$.updateTargets($$.data.targets); // this is needed when transforming to arc
6387
        $$.updateAndRedraw(options);
6388
    };
6389

    
6390
    c3_chart_fn.groups = function (groups) {
6391
        var $$ = this.internal, config = $$.config;
6392
        if (isUndefined(groups)) { return config.data_groups; }
6393
        config.data_groups = groups;
6394
        $$.redraw();
6395
        return config.data_groups;
6396
    };
6397

    
6398
    c3_chart_fn.xgrids = function (grids) {
6399
        var $$ = this.internal, config = $$.config;
6400
        if (! grids) { return config.grid_x_lines; }
6401
        config.grid_x_lines = grids;
6402
        $$.redrawWithoutRescale();
6403
        return config.grid_x_lines;
6404
    };
6405
    c3_chart_fn.xgrids.add = function (grids) {
6406
        var $$ = this.internal;
6407
        return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : []));
6408
    };
6409
    c3_chart_fn.xgrids.remove = function (params) { // TODO: multiple
6410
        var $$ = this.internal;
6411
        $$.removeGridLines(params, true);
6412
    };
6413

    
6414
    c3_chart_fn.ygrids = function (grids) {
6415
        var $$ = this.internal, config = $$.config;
6416
        if (! grids) { return config.grid_y_lines; }
6417
        config.grid_y_lines = grids;
6418
        $$.redrawWithoutRescale();
6419
        return config.grid_y_lines;
6420
    };
6421
    c3_chart_fn.ygrids.add = function (grids) {
6422
        var $$ = this.internal;
6423
        return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : []));
6424
    };
6425
    c3_chart_fn.ygrids.remove = function (params) { // TODO: multiple
6426
        var $$ = this.internal;
6427
        $$.removeGridLines(params, false);
6428
    };
6429

    
6430
    c3_chart_fn.regions = function (regions) {
6431
        var $$ = this.internal, config = $$.config;
6432
        if (!regions) { return config.regions; }
6433
        config.regions = regions;
6434
        $$.redrawWithoutRescale();
6435
        return config.regions;
6436
    };
6437
    c3_chart_fn.regions.add = function (regions) {
6438
        var $$ = this.internal, config = $$.config;
6439
        if (!regions) { return config.regions; }
6440
        config.regions = config.regions.concat(regions);
6441
        $$.redrawWithoutRescale();
6442
        return config.regions;
6443
    };
6444
    c3_chart_fn.regions.remove = function (options) {
6445
        var $$ = this.internal, config = $$.config,
6446
            duration, classes, regions;
6447

    
6448
        options = options || {};
6449
        duration = $$.getOption(options, "duration", config.transition_duration);
6450
        classes = $$.getOption(options, "classes", [CLASS.region]);
6451

    
6452
        regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) { return '.' + c; }));
6453
        (duration ? regions.transition().duration(duration) : regions)
6454
            .style('opacity', 0)
6455
            .remove();
6456

    
6457
        config.regions = config.regions.filter(function (region) {
6458
            var found = false;
6459
            if (!region['class']) {
6460
                return true;
6461
            }
6462
            region['class'].split(' ').forEach(function (c) {
6463
                if (classes.indexOf(c) >= 0) { found = true; }
6464
            });
6465
            return !found;
6466
        });
6467

    
6468
        return config.regions;
6469
    };
6470

    
6471
    c3_chart_fn.data = function (targetIds) {
6472
        var targets = this.internal.data.targets;
6473
        return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) {
6474
            return [].concat(targetIds).indexOf(t.id) >= 0;
6475
        });
6476
    };
6477
    c3_chart_fn.data.shown = function (targetIds) {
6478
        return this.internal.filterTargetsToShow(this.data(targetIds));
6479
    };
6480
    c3_chart_fn.data.values = function (targetId) {
6481
        var targets, values = null;
6482
        if (targetId) {
6483
            targets = this.data(targetId);
6484
            values = targets[0] ? targets[0].values.map(function (d) { return d.value; }) : null;
6485
        }
6486
        return values;
6487
    };
6488
    c3_chart_fn.data.names = function (names) {
6489
        this.internal.clearLegendItemTextBoxCache();
6490
        return this.internal.updateDataAttributes('names', names);
6491
    };
6492
    c3_chart_fn.data.colors = function (colors) {
6493
        return this.internal.updateDataAttributes('colors', colors);
6494
    };
6495
    c3_chart_fn.data.axes = function (axes) {
6496
        return this.internal.updateDataAttributes('axes', axes);
6497
    };
6498

    
6499
    c3_chart_fn.category = function (i, category) {
6500
        var $$ = this.internal, config = $$.config;
6501
        if (arguments.length > 1) {
6502
            config.axis_x_categories[i] = category;
6503
            $$.redraw();
6504
        }
6505
        return config.axis_x_categories[i];
6506
    };
6507
    c3_chart_fn.categories = function (categories) {
6508
        var $$ = this.internal, config = $$.config;
6509
        if (!arguments.length) { return config.axis_x_categories; }
6510
        config.axis_x_categories = categories;
6511
        $$.redraw();
6512
        return config.axis_x_categories;
6513
    };
6514

    
6515
    // TODO: fix
6516
    c3_chart_fn.color = function (id) {
6517
        var $$ = this.internal;
6518
        return $$.color(id); // more patterns
6519
    };
6520

    
6521
    c3_chart_fn.x = function (x) {
6522
        var $$ = this.internal;
6523
        if (arguments.length) {
6524
            $$.updateTargetX($$.data.targets, x);
6525
            $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true});
6526
        }
6527
        return $$.data.xs;
6528
    };
6529
    c3_chart_fn.xs = function (xs) {
6530
        var $$ = this.internal;
6531
        if (arguments.length) {
6532
            $$.updateTargetXs($$.data.targets, xs);
6533
            $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true});
6534
        }
6535
        return $$.data.xs;
6536
    };
6537

    
6538
    c3_chart_fn.axis = function () {};
6539
    c3_chart_fn.axis.labels = function (labels) {
6540
        var $$ = this.internal;
6541
        if (arguments.length) {
6542
            Object.keys(labels).forEach(function (axisId) {
6543
                $$.axis.setLabelText(axisId, labels[axisId]);
6544
            });
6545
            $$.axis.updateLabels();
6546
        }
6547
        // TODO: return some values?
6548
    };
6549
    c3_chart_fn.axis.max = function (max) {
6550
        var $$ = this.internal, config = $$.config;
6551
        if (arguments.length) {
6552
            if (typeof max === 'object') {
6553
                if (isValue(max.x)) { config.axis_x_max = max.x; }
6554
                if (isValue(max.y)) { config.axis_y_max = max.y; }
6555
                if (isValue(max.y2)) { config.axis_y2_max = max.y2; }
6556
            } else {
6557
                config.axis_y_max = config.axis_y2_max = max;
6558
            }
6559
            $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true});
6560
        } else {
6561
            return {
6562
                x: config.axis_x_max,
6563
                y: config.axis_y_max,
6564
                y2: config.axis_y2_max
6565
            };
6566
        }
6567
    };
6568
    c3_chart_fn.axis.min = function (min) {
6569
        var $$ = this.internal, config = $$.config;
6570
        if (arguments.length) {
6571
            if (typeof min === 'object') {
6572
                if (isValue(min.x)) { config.axis_x_min = min.x; }
6573
                if (isValue(min.y)) { config.axis_y_min = min.y; }
6574
                if (isValue(min.y2)) { config.axis_y2_min = min.y2; }
6575
            } else {
6576
                config.axis_y_min = config.axis_y2_min = min;
6577
            }
6578
            $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true});
6579
        } else {
6580
            return {
6581
                x: config.axis_x_min,
6582
                y: config.axis_y_min,
6583
                y2: config.axis_y2_min
6584
            };
6585
        }
6586
    };
6587
    c3_chart_fn.axis.range = function (range) {
6588
        if (arguments.length) {
6589
            if (isDefined(range.max)) { this.axis.max(range.max); }
6590
            if (isDefined(range.min)) { this.axis.min(range.min); }
6591
        } else {
6592
            return {
6593
                max: this.axis.max(),
6594
                min: this.axis.min()
6595
            };
6596
        }
6597
    };
6598

    
6599
    c3_chart_fn.legend = function () {};
6600
    c3_chart_fn.legend.show = function (targetIds) {
6601
        var $$ = this.internal;
6602
        $$.showLegend($$.mapToTargetIds(targetIds));
6603
        $$.updateAndRedraw({withLegend: true});
6604
    };
6605
    c3_chart_fn.legend.hide = function (targetIds) {
6606
        var $$ = this.internal;
6607
        $$.hideLegend($$.mapToTargetIds(targetIds));
6608
        $$.updateAndRedraw({withLegend: true});
6609
    };
6610

    
6611
    c3_chart_fn.resize = function (size) {
6612
        var $$ = this.internal, config = $$.config;
6613
        config.size_width = size ? size.width : null;
6614
        config.size_height = size ? size.height : null;
6615
        this.flush();
6616
    };
6617

    
6618
    c3_chart_fn.flush = function () {
6619
        var $$ = this.internal;
6620
        $$.updateAndRedraw({withLegend: true, withTransition: false, withTransitionForTransform: false});
6621
    };
6622

    
6623
    c3_chart_fn.destroy = function () {
6624
        var $$ = this.internal;
6625

    
6626
        window.clearInterval($$.intervalForObserveInserted);
6627
        window.onresize = null;
6628

    
6629
        $$.selectChart.classed('c3', false).html("");
6630

    
6631
        // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen.
6632
        Object.keys($$).forEach(function (key) {
6633
            $$[key] = null;
6634
        });
6635

    
6636
        return null;
6637
    };
6638

    
6639
    c3_chart_fn.tooltip = function () {};
6640
    c3_chart_fn.tooltip.show = function (args) {
6641
        var $$ = this.internal, index, mouse;
6642

    
6643
        // determine mouse position on the chart
6644
        if (args.mouse) {
6645
            mouse = args.mouse;
6646
        }
6647

    
6648
        // determine focus data
6649
        if (args.data) {
6650
            if ($$.isMultipleX()) {
6651
                // if multiple xs, target point will be determined by mouse
6652
                mouse = [$$.x(args.data.x), $$.getYScale(args.data.id)(args.data.value)];
6653
                index = null;
6654
            } else {
6655
                // TODO: when tooltip_grouped = false
6656
                index = isValue(args.data.index) ? args.data.index : $$.getIndexByX(args.data.x);
6657
            }
6658
        }
6659
        else if (typeof args.x !== 'undefined') {
6660
            index = $$.getIndexByX(args.x);
6661
        }
6662
        else if (typeof args.index !== 'undefined') {
6663
            index = args.index;
6664
        }
6665

    
6666
        // emulate mouse events to show
6667
        $$.dispatchEvent('mouseover', index, mouse);
6668
        $$.dispatchEvent('mousemove', index, mouse);
6669
    };
6670
    c3_chart_fn.tooltip.hide = function () {
6671
        // TODO: get target data by checking the state of focus
6672
        this.internal.dispatchEvent('mouseout', 0);
6673
    };
6674

    
6675
    // Features:
6676
    // 1. category axis
6677
    // 2. ceil values of translate/x/y to int for half pixel antialiasing
6678
    // 3. multiline tick text
6679
    var tickTextCharSize;
6680
    function c3_axis(d3, params) {
6681
        var scale = d3.scale.linear(), orient = "bottom", innerTickSize = 6, outerTickSize, tickPadding = 3, tickValues = null, tickFormat, tickArguments;
6682

    
6683
        var tickOffset = 0, tickCulling = true, tickCentered;
6684

    
6685
        params = params || {};
6686
        outerTickSize = params.withOuterTick ? 6 : 0;
6687

    
6688
        function axisX(selection, x) {
6689
            selection.attr("transform", function (d) {
6690
                return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)";
6691
            });
6692
        }
6693
        function axisY(selection, y) {
6694
            selection.attr("transform", function (d) {
6695
                return "translate(0," + Math.ceil(y(d)) + ")";
6696
            });
6697
        }
6698
        function scaleExtent(domain) {
6699
            var start = domain[0], stop = domain[domain.length - 1];
6700
            return start < stop ? [ start, stop ] : [ stop, start ];
6701
        }
6702
        function generateTicks(scale) {
6703
            var i, domain, ticks = [];
6704
            if (scale.ticks) {
6705
                return scale.ticks.apply(scale, tickArguments);
6706
            }
6707
            domain = scale.domain();
6708
            for (i = Math.ceil(domain[0]); i < domain[1]; i++) {
6709
                ticks.push(i);
6710
            }
6711
            if (ticks.length > 0 && ticks[0] > 0) {
6712
                ticks.unshift(ticks[0] - (ticks[1] - ticks[0]));
6713
            }
6714
            return ticks;
6715
        }
6716
        function copyScale() {
6717
            var newScale = scale.copy(), domain;
6718
            if (params.isCategory) {
6719
                domain = scale.domain();
6720
                newScale.domain([domain[0], domain[1] - 1]);
6721
            }
6722
            return newScale;
6723
        }
6724
        function textFormatted(v) {
6725
            var formatted = tickFormat ? tickFormat(v) : v;
6726
            return typeof formatted !== 'undefined' ? formatted : '';
6727
        }
6728
        function getSizeFor1Char(tick) {
6729
            if (tickTextCharSize) {
6730
                return tickTextCharSize;
6731
            }
6732
            var size = {
6733
                h: 11.5,
6734
                w: 5.5
6735
            };
6736
            tick.select('text').text(textFormatted).each(function (d) {
6737
                var box = this.getBoundingClientRect(),
6738
                    text = textFormatted(d),
6739
                    h = box.height,
6740
                    w = text ? (box.width / text.length) : undefined;
6741
                if (h && w) {
6742
                    size.h = h;
6743
                    size.w = w;
6744
                }
6745
            }).text('');
6746
            tickTextCharSize = size;
6747
            return size;
6748
        }
6749
        function transitionise(selection) {
6750
            return params.withoutTransition ? selection : d3.transition(selection);
6751
        }
6752
        function axis(g) {
6753
            g.each(function () {
6754
                var g = axis.g = d3.select(this);
6755

    
6756
                var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = copyScale();
6757

    
6758
                var ticks = tickValues ? tickValues : generateTicks(scale1),
6759
                    tick = g.selectAll(".tick").data(ticks, scale1),
6760
                    tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6),
6761
                    // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks.
6762
                    tickExit = tick.exit().remove(),
6763
                    tickUpdate = transitionise(tick).style("opacity", 1),
6764
                    tickTransform, tickX, tickY;
6765

    
6766
                var range = scale.rangeExtent ? scale.rangeExtent() : scaleExtent(scale.range()),
6767
                    path = g.selectAll(".domain").data([ 0 ]),
6768
                    pathUpdate = (path.enter().append("path").attr("class", "domain"), transitionise(path));
6769
                tickEnter.append("line");
6770
                tickEnter.append("text");
6771

    
6772
                var lineEnter = tickEnter.select("line"),
6773
                    lineUpdate = tickUpdate.select("line"),
6774
                    textEnter = tickEnter.select("text"),
6775
                    textUpdate = tickUpdate.select("text");
6776

    
6777
                if (params.isCategory) {
6778
                    tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2);
6779
                    tickX = tickCentered ? 0 : tickOffset;
6780
                    tickY = tickCentered ? tickOffset : 0;
6781
                } else {
6782
                    tickOffset = tickX = 0;
6783
                }
6784

    
6785
                var text, tspan, sizeFor1Char = getSizeFor1Char(g.select('.tick')), counts = [];
6786
                var tickLength = Math.max(innerTickSize, 0) + tickPadding,
6787
                    isVertical = orient === 'left' || orient === 'right';
6788

    
6789
                // this should be called only when category axis
6790
                function splitTickText(d, maxWidth) {
6791
                    var tickText = textFormatted(d),
6792
                        subtext, spaceIndex, textWidth, splitted = [];
6793

    
6794
                    if (Object.prototype.toString.call(tickText) === "[object Array]") {
6795
                        return tickText;
6796
                    }
6797

    
6798
                    if (!maxWidth || maxWidth <= 0) {
6799
                        maxWidth = isVertical ? 95 : params.isCategory ? (Math.ceil(scale1(ticks[1]) - scale1(ticks[0])) - 12) : 110;
6800
                    }
6801

    
6802
                    function split(splitted, text) {
6803
                        spaceIndex = undefined;
6804
                        for (var i = 1; i < text.length; i++) {
6805
                            if (text.charAt(i) === ' ') {
6806
                                spaceIndex = i;
6807
                            }
6808
                            subtext = text.substr(0, i + 1);
6809
                            textWidth = sizeFor1Char.w * subtext.length;
6810
                            // if text width gets over tick width, split by space index or crrent index
6811
                            if (maxWidth < textWidth) {
6812
                                return split(
6813
                                    splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)),
6814
                                    text.slice(spaceIndex ? spaceIndex + 1 : i)
6815
                                );
6816
                            }
6817
                        }
6818
                        return splitted.concat(text);
6819
                    }
6820

    
6821
                    return split(splitted, tickText + "");
6822
                }
6823

    
6824
                function tspanDy(d, i) {
6825
                    var dy = sizeFor1Char.h;
6826
                    if (i === 0) {
6827
                        if (orient === 'left' || orient === 'right') {
6828
                            dy = -((counts[d.index] - 1) * (sizeFor1Char.h / 2) - 3);
6829
                        } else {
6830
                            dy = ".71em";
6831
                        }
6832
                    }
6833
                    return dy;
6834
                }
6835

    
6836
                function tickSize(d) {
6837
                    var tickPosition = scale(d) + (tickCentered ? 0 : tickOffset);
6838
                    return range[0] < tickPosition && tickPosition < range[1] ? innerTickSize : 0;
6839
                }
6840

    
6841
                text = tick.select("text");
6842
                tspan = text.selectAll('tspan')
6843
                    .data(function (d, i) {
6844
                        var splitted = params.tickMultiline ? splitTickText(d, params.tickWidth) : [].concat(textFormatted(d));
6845
                        counts[i] = splitted.length;
6846
                        return splitted.map(function (s) {
6847
                            return { index: i, splitted: s };
6848
                        });
6849
                    });
6850
                tspan.enter().append('tspan');
6851
                tspan.exit().remove();
6852
                tspan.text(function (d) { return d.splitted; });
6853

    
6854
                var rotate = params.tickTextRotate;
6855

    
6856
                function textAnchorForText(rotate) {
6857
                    if (!rotate) {
6858
                        return 'middle';
6859
                    }
6860
                    return rotate > 0 ? "start" : "end";
6861
                }
6862
                function textTransform(rotate) {
6863
                    if (!rotate) {
6864
                        return '';
6865
                    }
6866
                    return "rotate(" + rotate + ")";
6867
                }
6868
                function dxForText(rotate) {
6869
                    if (!rotate) {
6870
                        return 0;
6871
                    }
6872
                    return 8 * Math.sin(Math.PI * (rotate / 180));
6873
                }
6874
                function yForText(rotate) {
6875
                    if (!rotate) {
6876
                        return tickLength;
6877
                    }
6878
                    return 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1);
6879
                }
6880

    
6881
                switch (orient) {
6882
                case "bottom":
6883
                    {
6884
                        tickTransform = axisX;
6885
                        lineEnter.attr("y2", innerTickSize);
6886
                        textEnter.attr("y", tickLength);
6887
                        lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize);
6888
                        textUpdate.attr("x", 0).attr("y", yForText(rotate))
6889
                            .style("text-anchor", textAnchorForText(rotate))
6890
                            .attr("transform", textTransform(rotate));
6891
                        tspan.attr('x', 0).attr("dy", tspanDy).attr('dx', dxForText(rotate));
6892
                        pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
6893
                        break;
6894
                    }
6895
                case "top":
6896
                    {
6897
                        // TODO: rotated tick text
6898
                        tickTransform = axisX;
6899
                        lineEnter.attr("y2", -innerTickSize);
6900
                        textEnter.attr("y", -tickLength);
6901
                        lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);
6902
                        textUpdate.attr("x", 0).attr("y", -tickLength);
6903
                        text.style("text-anchor", "middle");
6904
                        tspan.attr('x', 0).attr("dy", "0em");
6905
                        pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);
6906
                        break;
6907
                    }
6908
                case "left":
6909
                    {
6910
                        tickTransform = axisY;
6911
                        lineEnter.attr("x2", -innerTickSize);
6912
                        textEnter.attr("x", -tickLength);
6913
                        lineUpdate.attr("x2", -innerTickSize).attr("y1", tickY).attr("y2", tickY);
6914
                        textUpdate.attr("x", -tickLength).attr("y", tickOffset);
6915
                        text.style("text-anchor", "end");
6916
                        tspan.attr('x', -tickLength).attr("dy", tspanDy);
6917
                        pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);
6918
                        break;
6919
                    }
6920
                case "right":
6921
                    {
6922
                        tickTransform = axisY;
6923
                        lineEnter.attr("x2", innerTickSize);
6924
                        textEnter.attr("x", tickLength);
6925
                        lineUpdate.attr("x2", innerTickSize).attr("y2", 0);
6926
                        textUpdate.attr("x", tickLength).attr("y", 0);
6927
                        text.style("text-anchor", "start");
6928
                        tspan.attr('x', tickLength).attr("dy", tspanDy);
6929
                        pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);
6930
                        break;
6931
                    }
6932
                }
6933
                if (scale1.rangeBand) {
6934
                    var x = scale1, dx = x.rangeBand() / 2;
6935
                    scale0 = scale1 = function (d) {
6936
                        return x(d) + dx;
6937
                    };
6938
                } else if (scale0.rangeBand) {
6939
                    scale0 = scale1;
6940
                } else {
6941
                    tickExit.call(tickTransform, scale1);
6942
                }
6943
                tickEnter.call(tickTransform, scale0);
6944
                tickUpdate.call(tickTransform, scale1);
6945
            });
6946
        }
6947
        axis.scale = function (x) {
6948
            if (!arguments.length) { return scale; }
6949
            scale = x;
6950
            return axis;
6951
        };
6952
        axis.orient = function (x) {
6953
            if (!arguments.length) { return orient; }
6954
            orient = x in {top: 1, right: 1, bottom: 1, left: 1} ? x + "" : "bottom";
6955
            return axis;
6956
        };
6957
        axis.tickFormat = function (format) {
6958
            if (!arguments.length) { return tickFormat; }
6959
            tickFormat = format;
6960
            return axis;
6961
        };
6962
        axis.tickCentered = function (isCentered) {
6963
            if (!arguments.length) { return tickCentered; }
6964
            tickCentered = isCentered;
6965
            return axis;
6966
        };
6967
        axis.tickOffset = function () {
6968
            return tickOffset;
6969
        };
6970
        axis.tickInterval = function () {
6971
            var interval, length;
6972
            if (params.isCategory) {
6973
                interval = tickOffset * 2;
6974
            }
6975
            else {
6976
                length = axis.g.select('path.domain').node().getTotalLength() - outerTickSize * 2;
6977
                interval = length / axis.g.selectAll('line').size();
6978
            }
6979
            return interval === Infinity ? 0 : interval;
6980
        };
6981
        axis.ticks = function () {
6982
            if (!arguments.length) { return tickArguments; }
6983
            tickArguments = arguments;
6984
            return axis;
6985
        };
6986
        axis.tickCulling = function (culling) {
6987
            if (!arguments.length) { return tickCulling; }
6988
            tickCulling = culling;
6989
            return axis;
6990
        };
6991
        axis.tickValues = function (x) {
6992
            if (typeof x === 'function') {
6993
                tickValues = function () {
6994
                    return x(scale.domain());
6995
                };
6996
            }
6997
            else {
6998
                if (!arguments.length) { return tickValues; }
6999
                tickValues = x;
7000
            }
7001
            return axis;
7002
        };
7003
        return axis;
7004
    }
7005

    
7006
    c3_chart_internal_fn.isSafari = function () {
7007
        var ua = window.navigator.userAgent;
7008
        return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0;
7009
    };
7010
    c3_chart_internal_fn.isChrome = function () {
7011
        var ua = window.navigator.userAgent;
7012
        return ua.indexOf('Chrome') >= 0;
7013
    };
7014

    
7015
    // PhantomJS doesn't have support for Function.prototype.bind, which has caused confusion. Use
7016
    // this polyfill to avoid the confusion.
7017
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill
7018

    
7019
    if (!Function.prototype.bind) {
7020
      Function.prototype.bind = function(oThis) {
7021
        if (typeof this !== 'function') {
7022
          // closest thing possible to the ECMAScript 5
7023
          // internal IsCallable function
7024
          throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
7025
        }
7026

    
7027
        var aArgs   = Array.prototype.slice.call(arguments, 1),
7028
            fToBind = this,
7029
            fNOP    = function() {},
7030
            fBound  = function() {
7031
              return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
7032
            };
7033

    
7034
        fNOP.prototype = this.prototype;
7035
        fBound.prototype = new fNOP();
7036

    
7037
        return fBound;
7038
      };
7039
    }
7040

    
7041
    if (typeof define === 'function' && define.amd) {
7042
        define("c3", ["d3"], c3);
7043
    } else if ('undefined' !== typeof exports && 'undefined' !== typeof module) {
7044
        module.exports = c3;
7045
    } else {
7046
        window.c3 = c3;
7047
    }
7048

    
7049
})(window);
(6-6/8)